Files
2026-08-16 18:33:03 +07:00

135 lines
4.0 KiB
Go

package main
import (
"net/http/httptest"
"testing"
"unsafe"
)
func testEngine(pipeline uintptr, freed *int) *engine {
return &engine{
pipeline: pipeline,
backend: "test GPU",
caps: capCoarse | cap512 | capTexture,
textured: true,
pipelineFree: func(uintptr) {
*freed++
},
}
}
func TestEngineUnloadRetainsRuntimeInfoAndReloads(t *testing.T) {
freed, loads := 0, 0
e := testEngine(41, &freed)
e.models.dino = "dino.gguf"
e.pipelineLoad = func(dino, flow, dec, slat, slatHR, shapeDec, shapeEnc, texDec, texFlow, texFlowHR string,
flags int32, err unsafe.Pointer, errLen int32) uintptr {
loads++
if dino != "dino.gguf" {
t.Fatalf("reload used dino path %q", dino)
}
return 42
}
e.pipelineBackend = func(uintptr) string { return "test GPU" }
e.pipelineCaps = func(uintptr) int32 { return capCoarse | cap512 | capTexture }
if !e.Unload() || freed != 1 {
t.Fatalf("Unload() = true with one free wanted; freed=%d", freed)
}
backend, caps, textured, loaded := e.Info()
if loaded || backend != "test GPU" || caps != capCoarse|cap512|capTexture || !textured {
t.Fatalf("Info() after unload = %q, %d, %v, %v", backend, caps, textured, loaded)
}
e.mu.Lock()
err := e.loadLocked()
e.mu.Unlock()
if err != nil || loads != 1 {
t.Fatalf("reload: err=%v loads=%d", err, loads)
}
_, _, _, loaded = e.Info()
if !loaded {
t.Fatal("pipeline is not marked loaded after reload")
}
}
func TestConfiguredCapsForLazyStartup(t *testing.T) {
tests := []struct {
name string
m engineModels
want int
}{
{"coarse", engineModels{}, capCoarse},
{"512", engineModels{slat: "slat", shapeDec: "shape"}, capCoarse | cap512},
{"1024 textured", engineModels{
slat: "slat", slatHR: "hr", shapeDec: "shape", shapeEnc: "shapeenc",
texDec: "texdec", texFlow: "texflow",
}, capCoarse | cap512 | cap1024 | capTexture},
{"texture missing encoder", engineModels{
slat: "slat", shapeDec: "shape", texDec: "texdec", texFlow: "texflow",
}, capCoarse | cap512},
{"incomplete fine", engineModels{slat: "slat", texDec: "texdec", texFlow: "texflow"}, capCoarse},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := configuredCaps(tt.m); got != tt.want {
t.Fatalf("configuredCaps() = %d, want %d", got, tt.want)
}
})
}
}
func TestIdlePolicyWaitsForQueuedWork(t *testing.T) {
freed := 0
e := testEngine(41, &freed)
s := &server{eng: e, jobs: map[string]*job{}, q: make(chan *job, 1), queued: 1}
if s.setUnloadIdle(true) {
t.Fatal("enabled idle policy unloaded with queued work")
}
if freed != 0 {
t.Fatalf("pipeline freed with queued work: %d", freed)
}
s.mu.Lock()
s.queued = 0
s.mu.Unlock()
if !s.unloadModelsIfIdle(nil) || freed != 1 {
t.Fatalf("idle unload failed; freed=%d", freed)
}
}
func TestParseExportOptions(t *testing.T) {
def := parseExportOptions(httptest.NewRequest("GET", "/api/glb/job", nil))
if def.componentFilter != 2 || def.printWrap || def.prepareKey() != "2" {
t.Fatalf("default export should preserve all components: %+v", def)
}
r := httptest.NewRequest("GET", "/api/glb/job?tex=1024&components=largest", nil)
o := parseExportOptions(r)
if o.textureSize != 1024 || o.componentFilter != 1 {
t.Fatalf("parseExportOptions() = %+v", o)
}
if o.prepareKey() != "1" || o.glbKey() != "1024-1" {
t.Fatalf("unexpected export cache keys: %q %q", o.prepareKey(), o.glbKey())
}
wrapped := parseExportOptions(httptest.NewRequest("GET",
"/api/glb/job?print=1&alpha=2.5&offset=0.1", nil))
if !wrapped.printWrap || wrapped.alphaRatio != 0.025 || wrapped.offsetRatio != 0.001 ||
wrapped.prepareKey() == def.prepareKey() {
t.Fatalf("print-wrap options were not parsed/cached independently: %+v", wrapped)
}
}
func TestKeepAllExportPreviewUsesOriginalMesh(t *testing.T) {
original := &meshData{NVerts: 3, NTris: 1}
j := &job{mesh: original}
s := &server{}
got, err := s.preparedExportMesh(j, exportOptions{textureSize: 2048, componentFilter: 2})
if err != nil {
t.Fatal(err)
}
if got != original {
t.Fatal("keep-all export preview did not return the exact source mesh")
}
}