package generator import ( "errors" "strings" "testing" ) type fakeFrameGenerator struct { generateErr error closeErr error closed bool calls int } func (g *fakeFrameGenerator) GenerateFrame(dst []byte, frameIndex int) error { g.calls++ if g.generateErr != nil { return g.generateErr } for i := range dst { dst[i] = byte(frameIndex) } return nil } func (g *fakeFrameGenerator) Close() error { g.closed = true return g.closeErr } func TestV210AGeneratorLayoutAndAlpha(t *testing.T) { const width, height = 304, 200 fill := &fakeFrameGenerator{} g, err := NewV210AGenerator(fill, width, height, true) if err != nil { t.Fatalf("NewV210AGenerator: %v", err) } dst := make([]byte, V210AFrameSize(width, height)) if err := g.GenerateFrame(dst, 7); err != nil { t.Fatalf("GenerateFrame: %v", err) } fillSize := V210FrameSize(width, height) for i, b := range dst[:fillSize] { if b != 7 { t.Fatalf("fill byte %d = %#x, want 0x07", i, b) } } alpha := dst[fillSize:] if got := sampleAlpha(alpha, width, width/2, height/2); got != alphaTransparent { t.Errorf("square center = %d, want transparent %d", got, alphaTransparent) } if got := sampleAlpha(alpha, width, 10, height/2); got != alphaOpaque { t.Errorf("outside square = %d, want opaque %d", got, alphaOpaque) } } func TestV210AGeneratorRestoresAlphaBase(t *testing.T) { const width, height = 304, 200 g, err := NewV210AGenerator(&fakeFrameGenerator{}, width, height, true) if err != nil { t.Fatalf("NewV210AGenerator: %v", err) } dst := make([]byte, V210AFrameSize(width, height)) if err := g.GenerateFrame(dst, 0); err != nil { t.Fatalf("GenerateFrame(0): %v", err) } alpha := dst[V210FrameSize(width, height):] if got := sampleAlpha(alpha, width, width/2, height/2); got != alphaTransparent { t.Fatalf("frame 0 center = %d, want transparent %d", got, alphaTransparent) } if err := g.GenerateFrame(dst, 79); err != nil { t.Fatalf("GenerateFrame(79): %v", err) } if got := sampleAlpha(alpha, width, width/2, height/2); got != alphaOpaque { t.Errorf("old square position = %d, want restored opaque %d", got, alphaOpaque) } if got := sampleAlpha(alpha, width, 250, height/2); got != alphaTransparent { t.Errorf("new square position = %d, want transparent %d", got, alphaTransparent) } } func TestV210AGeneratorCachesStaticFill(t *testing.T) { const width, height = 100, 20 fill := &fakeFrameGenerator{} g, err := NewV210AGenerator(fill, width, height, false) if err != nil { t.Fatalf("NewV210AGenerator: %v", err) } if fill.calls != 1 { t.Fatalf("fill calls after construction = %d, want 1", fill.calls) } dst := make([]byte, V210AFrameSize(width, height)) for _, frameIndex := range []int{7, 19} { if err := g.GenerateFrame(dst, frameIndex); err != nil { t.Fatalf("GenerateFrame(%d): %v", frameIndex, err) } } if fill.calls != 1 { t.Errorf("fill calls after two frames = %d, want 1", fill.calls) } for i, b := range dst[:V210FrameSize(width, height)] { if b != 0 { t.Fatalf("cached fill byte %d = %#x, want frame-zero value 0", i, b) } } } func TestV210AGeneratorRegeneratesDynamicFill(t *testing.T) { const width, height = 100, 20 fill := &fakeFrameGenerator{} g, err := NewV210AGenerator(fill, width, height, true) if err != nil { t.Fatalf("NewV210AGenerator: %v", err) } if fill.calls != 0 { t.Fatalf("fill calls after construction = %d, want 0", fill.calls) } dst := make([]byte, V210AFrameSize(width, height)) for _, frameIndex := range []int{7, 19} { if err := g.GenerateFrame(dst, frameIndex); err != nil { t.Fatalf("GenerateFrame(%d): %v", frameIndex, err) } } if fill.calls != 2 { t.Errorf("fill calls after two frames = %d, want 2", fill.calls) } if got := dst[0]; got != 19 { t.Errorf("dynamic fill byte = %#x, want frame-index value 0x13", got) } } func TestV210AGeneratorErrors(t *testing.T) { if _, err := NewV210AGenerator(nil, 1920, 1080, false); err == nil || !strings.Contains(err.Error(), "nil") { t.Fatalf("nil fill error = %v", err) } fillErr := errors.New("fill failed") if _, err := NewV210AGenerator(&fakeFrameGenerator{generateErr: fillErr}, 100, 20, false); !errors.Is(err, fillErr) { t.Fatalf("static fill initialization error = %v, want wrapped %v", err, fillErr) } g, err := NewV210AGenerator(&fakeFrameGenerator{generateErr: fillErr}, 100, 20, true) if err != nil { t.Fatalf("NewV210AGenerator: %v", err) } if err := g.GenerateFrame(make([]byte, V210AFrameSize(100, 20)-1), 0); err == nil || !strings.Contains(err.Error(), "too small") { t.Fatalf("small destination error = %v", err) } if err := g.GenerateFrame(make([]byte, V210AFrameSize(100, 20)), 3); !errors.Is(err, fillErr) { t.Fatalf("fill error = %v, want wrapped %v", err, fillErr) } } func TestV210AGeneratorClosesFill(t *testing.T) { closeErr := errors.New("close failed") fill := &fakeFrameGenerator{closeErr: closeErr} g, err := NewV210AGenerator(fill, 100, 20, false) if err != nil { t.Fatalf("NewV210AGenerator: %v", err) } if err := g.Close(); !errors.Is(err, closeErr) { t.Fatalf("Close = %v, want %v", err, closeErr) } if !fill.closed { t.Fatal("wrapped fill generator was not closed") } }