1//go:build linux
2
3package systemtest
4
5import (
6 "context"
7 "encoding/json"
8 "fmt"
9 "image/color"
10 "os"
11 "path/filepath"
12 "regexp"
13 "sort"
14 "testing"
15 "time"
16
17 "github.com/bnema/nefergui/internal/harness"
18)
19
20func TestListImage(t *testing.T) {
21 requireHarness(t)
22 bin := binary(t)
23 for _, scale := range []int{1, 2} {
24 t.Run(fmt.Sprint(scale), func(t *testing.T) {
25 for _, changing := range []bool{false, true} {
26 name := "static"
27 frames := 24
28 if changing {
29 name = "changing"
30 frames = 120
31 }
32 t.Run(name, func(t *testing.T) {
33 root := filepath.Join(t.TempDir(), "artifacts")
34 args := []string{bin, "--static", "--image", "--scale", fmt.Sprint(scale), "--frames", fmt.Sprint(frames)}
35 if changing {
36 args = append(args, "--changing-image")
37 }
38 res, code := harness.Run(context.Background(), harness.Options{Size: "800x600", Scale: "1", Layout: "us", Background: background, Out: root, Timeout: 60 * time.Second, ReadyTimeout: 10 * time.Second, Client: args})
39 t.Logf("artifacts=%s code=%d status=%s", root, code, res.Status)
40 if code != harness.Pass {
41 data, _ := os.ReadFile(filepath.Join(root, "logs", "client.stderr"))
42 t.Fatalf("harness: %s %s", res.Error, data)
43 }
44 files, _ := filepath.Glob(filepath.Join(res.Artifacts["debug"], "readback-*.png"))
45 sort.Strings(files)
46 if len(files) == 0 {
47 t.Fatal("missing readback")
48 }
49 img, err := harness.LoadPNG(files[len(files)-1])
50 if err != nil {
51 t.Fatal(err)
52 }
53 if !changing {
54 bg := color.NRGBA{17, 17, 17, 255}
55 probes := []struct {
56 name string
57 x, y int
58 want color.NRGBA
59 }{
60 {"red", 108, 108, color.NRGBA{136, 8, 8, 255}},
61 {"green", 148, 108, color.NRGBA{8, 136, 8, 255}},
62 {"blue", 108, 148, color.NRGBA{8, 8, 136, 255}},
63 {"yellow", 148, 148, color.NRGBA{136, 136, 8, 255}},
64 {"round corner", 100, 100, bg},
65 {"between images", 170, 130, bg},
66 {"second image", 220, 120, color.NRGBA{255, 128, 0, 255}},
67 }
68 for _, p := range probes {
69 x, y := p.x*scale, p.y*scale
70 t.Logf("scale=%d %s (%d,%d)=%v want=%v", scale, p.name, x, y, color.NRGBAModel.Convert(img.At(x, y)), p.want)
71 if err := harness.PixelProbe(img, x, y, p.want, 4); err != nil {
72 t.Error(err)
73 }
74 }
75 }
76 if changing {
77 data, err := os.ReadFile(filepath.Join(res.Artifacts["debug"], "resource-summary.json"))
78 if err != nil {
79 t.Fatal(err)
80 }
81 var sum summary
82 if err = json.Unmarshal(data, &sum); err != nil {
83 t.Fatal(err)
84 }
85 t.Logf("resources=%+v", sum)
86 if sum.Frames != 120 || sum.ImagesAfter != 0 || sum.FDPeak-sum.FDStart > 2 || sum.FDAfter > sum.FDStart {
87 t.Errorf("unstable resources: %+v", sum)
88 }
89 rows := jsonLines[timing](t, filepath.Join(res.Artifacts["debug"], "frame-timings.jsonl"))
90 if len(rows) != 120 {
91 t.Errorf("frame timings=%d", len(rows))
92 }
93 for _, row := range rows {
94 if row.Images < 1 || row.Images > 6 || row.FDCount > sum.FDStart+2 {
95 t.Errorf("unstable frame: %+v", row)
96 break
97 }
98 }
99 }
100 if os.Getenv("NEFERGUI_VULKAN_VALIDATION") == "1" {
101 data, err := os.ReadFile(res.Artifacts["client_stderr"])
102 if err != nil {
103 t.Fatal(err)
104 }
105 if regexp.MustCompile(`(?i)(NEFERGUI_VULKAN_VALIDATION_(ERROR|WARNING)|validation (error|warning))`).Match(data) {
106 t.Errorf("validation: %s", data)
107 }
108 }
109 })
110 }
111 })
112 }
113}