package generator import ( "strings" "testing" ) func TestCPUEBU75Static(t *testing.T) { const width, height = 1920, 1080 g, err := NewCPUPatternGenerator(width, height, "ebu75") if err != nil { t.Fatalf("NewCPUPatternGenerator: %v", err) } frame := make([]byte, width*height*8/3) if err := g.GenerateFrame(frame, 99); err != nil { t.Fatalf("GenerateFrame: %v", err) } for _, tc := range []struct { x int wantY, wantCb, wantCr uint32 }{ {x: 0, wantY: 721, wantCb: 512, wantCr: 512}, {x: 300, wantY: 674, wantCb: 176, wantCr: 543}, {x: 960, wantY: 251, wantCb: 771, wantCr: 817}, {x: 1800, wantY: 64, wantCb: 512, wantCr: 512}, } { y, cb, cr := sampleV210(frame, width, tc.x, 100) if y != tc.wantY || cb != tc.wantCb || cr != tc.wantCr { t.Fatalf("x=%d: got %d/%d/%d, want %d/%d/%d", tc.x, y, cb, cr, tc.wantY, tc.wantCb, tc.wantCr) } } } func TestCPUEBU75MovingSquare(t *testing.T) { const width, height = 1920, 1080 g, err := NewCPUPatternGenerator(width, height, "ebu75-move") if err != nil { t.Fatalf("NewCPUPatternGenerator: %v", err) } frame := make([]byte, width*height*8/3) if err := g.GenerateFrame(frame, 0); err != nil { t.Fatalf("GenerateFrame(0): %v", err) } if y, cb, cr := sampleV210(frame, width, 960, 540); y != 753 || cb != 253 || cr != 207 { t.Fatalf("tick 0 center = %d/%d/%d, want inverted magenta 753/253/207", y, cb, cr) } if err := g.GenerateFrame(frame, 79); err != nil { t.Fatalf("GenerateFrame(79): %v", err) } if y, cb, cr := sampleV210(frame, width, 960, 540); y != 251 || cb != 771 || cr != 817 { t.Fatalf("old square position = %d/%d/%d, want restored magenta 251/771/817", y, cb, cr) } if y, cb, cr := sampleV210(frame, width, 1840, 540); y != 940 || cb != 512 || cr != 512 { t.Fatalf("shifted square = %d/%d/%d, want inverted black 940/512/512", y, cb, cr) } } func TestCPUStaticPatterns(t *testing.T) { const width, height = 1920, 1080 tests := []struct { name string x int wantY, wantCb, wantCr uint32 }{ {name: "ebu100", x: 0, wantY: 940, wantCb: 512, wantCr: 512}, {name: "ebu100", x: 300, wantY: 877, wantCb: 64, wantCr: 553}, {name: "ebu100", x: 960, wantY: 313, wantCb: 857, wantCr: 919}, {name: "gray-bars", x: 0, wantY: 64, wantCb: 512, wantCr: 512}, {name: "gray-bars", x: 960, wantY: 502, wantCb: 512, wantCr: 512}, {name: "gray-bars", x: 1800, wantY: 940, wantCb: 512, wantCr: 512}, {name: "gray-ramp", x: 0, wantY: 64, wantCb: 512, wantCr: 512}, {name: "gray-ramp", x: 6, wantY: 66, wantCb: 512, wantCr: 512}, {name: "gray-ramp", x: 960, wantY: 502, wantCb: 512, wantCr: 512}, {name: "gray-ramp", x: 1918, wantY: 939, wantCb: 512, wantCr: 512}, } frames := make(map[string][]byte) for _, tc := range tests { frame, ok := frames[tc.name] if !ok { g, err := NewCPUPatternGenerator(width, height, tc.name) if err != nil { t.Fatalf("NewCPUPatternGenerator(%q): %v", tc.name, err) } frame = make([]byte, width*height*8/3) if err := g.GenerateFrame(frame, 0); err != nil { t.Fatalf("GenerateFrame(%q): %v", tc.name, err) } frames[tc.name] = frame } y, cb, cr := sampleV210(frame, width, tc.x, 100) if y != tc.wantY || cb != tc.wantCb || cr != tc.wantCr { t.Errorf("%s x=%d: got %d/%d/%d, want %d/%d/%d", tc.name, tc.x, y, cb, cr, tc.wantY, tc.wantCb, tc.wantCr) } } } func TestCPUMovingPatternUsesOwnBaseColor(t *testing.T) { const width, height = 1920, 1080 g, err := NewCPUPatternGenerator(width, height, "ebu100-move") if err != nil { t.Fatalf("NewCPUPatternGenerator: %v", err) } frame := make([]byte, width*height*8/3) if err := g.GenerateFrame(frame, 0); err != nil { t.Fatalf("GenerateFrame: %v", err) } if y, cb, cr := sampleV210(frame, width, 960, 540); y != 691 || cb != 167 || cr != 105 { t.Fatalf("center = %d/%d/%d, want inverted EBU100 magenta 691/167/105", y, cb, cr) } } func TestCPURP219Pattern(t *testing.T) { const width, height = 1920, 1080 g, err := NewCPUPatternGenerator(width, height, "smpte") if err != nil { t.Fatalf("NewCPUPatternGenerator: %v", err) } frame := make([]byte, width*height*8/3) if err := g.GenerateFrame(frame, 0); err != nil { t.Fatalf("GenerateFrame: %v", err) } tests := []struct { name string x, y int wantY, wantCb, wantCr uint32 }{ {"top left gray flank", 100, 100, 414, 512, 512}, {"top white bar", 300, 100, 721, 512, 512}, {"top green bar", 960, 100, 534, 253, 207}, {"top right gray flank", 1800, 100, 414, 512, 512}, {"section 2 cyan flank", 100, 650, 754, 615, 64}, {"section 2 minus I", 300, 650, 244, 612, 395}, {"section 2 white", 600, 650, 721, 512, 512}, {"section 2 blue flank", 1800, 650, 127, 960, 471}, {"section 3 yellow flank", 100, 750, 877, 64, 553}, {"section 3 plus Q", 300, 750, 141, 697, 606}, {"section 3 red flank", 1800, 750, 250, 409, 960}, {"bottom gray flank", 100, 900, 195, 512, 512}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { y, cb, cr := sampleV210(frame, width, tc.x, tc.y) if y != tc.wantY || cb != tc.wantCb || cr != tc.wantCr { t.Fatalf("pixel (%d,%d): got %d/%d/%d, want %d/%d/%d", tc.x, tc.y, y, cb, cr, tc.wantY, tc.wantCb, tc.wantCr) } }) } } func TestCPURP219MovingSquare(t *testing.T) { const width, height = 1920, 1080 g, err := NewCPUPatternGenerator(width, height, "smpte-move") if err != nil { t.Fatalf("NewCPUPatternGenerator: %v", err) } frame := make([]byte, width*height*8/3) if err := g.GenerateFrame(frame, 0); err != nil { t.Fatalf("GenerateFrame(0): %v", err) } if y, cb, cr := sampleV210(frame, width, 960, 540); y != 470 || cb != 771 || cr != 817 { t.Fatalf("tick 0 center = %d/%d/%d, want inverted green 470/771/817", y, cb, cr) } if err := g.GenerateFrame(frame, 79); err != nil { t.Fatalf("GenerateFrame(79): %v", err) } if y, cb, cr := sampleV210(frame, width, 1840, 540); y != 590 || cb != 512 || cr != 512 { t.Fatalf("tick 79 shifted square = %d/%d/%d, want inverted gray 590/512/512", y, cb, cr) } } func TestCPURP219RejectsInvalidGeometry(t *testing.T) { for _, tc := range []struct { name string width, height uint }{ {name: "height too small", width: 1920, height: 11}, {name: "canvas too narrow", width: 600, height: 1080}, } { t.Run(tc.name, func(t *testing.T) { _, err := NewCPUPatternGenerator(tc.width, tc.height, "smpte") if err == nil || !strings.Contains(err.Error(), "RP 219") { t.Fatalf("error = %v, want RP 219 geometry error", err) } }) } } func TestNewCPUPatternGeneratorRejectsUnknownPattern(t *testing.T) { _, err := NewCPUPatternGenerator(1920, 1080, "unknown") if err == nil || !strings.Contains(err.Error(), "not implemented") { t.Fatalf("error = %v, want unsupported pattern error", err) } }