Files
go-mxl-pattern-generator/internal/generator/v210a_test.go
T
2026-09-18 10:25:35 +03:00

172 lines
5.1 KiB
Go

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")
}
}