package renderer import ( "bytes" "image/png" "testing" "gitea.dockr.co/kev/go-jdenticon/internal/engine" ) func TestNewPNGRenderer(t *testing.T) { renderer := NewPNGRenderer(100) if renderer.GetSize() != 100 { t.Errorf("NewPNGRenderer(100).GetSize() = %v, want 100", renderer.GetSize()) } if renderer == nil { t.Error("PNGRenderer should be initialized") } } func TestPNGRenderer_SetBackground(t *testing.T) { renderer := NewPNGRenderer(50) renderer.SetBackground("#ff0000", 1.0) // Check that background was set on base renderer bg, op := renderer.GetBackground() if bg != "#ff0000" { t.Errorf("background color = %v, want #ff0000", bg) } if op != 1.0 { t.Errorf("background opacity = %v, want 1.0", op) } } func TestPNGRenderer_SetBackgroundWithOpacity(t *testing.T) { renderer := NewPNGRenderer(50) renderer.SetBackground("#00ff00", 0.5) bg, op := renderer.GetBackground() if bg != "#00ff00" { t.Errorf("background color = %v, want #00ff00", bg) } if op != 0.5 { t.Errorf("background opacity = %v, want 0.5", op) } } func TestPNGRenderer_BeginEndShape(t *testing.T) { renderer := NewPNGRenderer(100) renderer.BeginShape("#0000ff") // Check that current color was set if renderer.GetCurrentColor() != "#0000ff" { t.Errorf("currentColor = %v, want #0000ff", renderer.GetCurrentColor()) } renderer.EndShape() // EndShape is a no-op for PNG, just verify it doesn't panic } func TestPNGRenderer_AddPolygon(t *testing.T) { renderer := NewPNGRenderer(100) renderer.BeginShape("#ff0000") // Create a simple triangle points := []engine.Point{ {X: 10, Y: 10}, {X: 30, Y: 10}, {X: 20, Y: 30}, } // Should not panic renderer.AddPolygon(points) } func TestPNGRenderer_AddPolygonEmpty(t *testing.T) { renderer := NewPNGRenderer(100) renderer.BeginShape("#ff0000") // Empty polygon should not panic renderer.AddPolygon([]engine.Point{}) // Polygon with < 3 points should not panic renderer.AddPolygon([]engine.Point{{X: 10, Y: 10}}) renderer.AddPolygon([]engine.Point{{X: 10, Y: 10}, {X: 20, Y: 20}}) } func TestPNGRenderer_AddCircle(t *testing.T) { renderer := NewPNGRenderer(100) renderer.BeginShape("#00ff00") // Circle with center at (50, 50) and radius 20 means topLeft at (30, 30) and size 40 topLeft := engine.Point{X: 30, Y: 30} size := 40.0 // Should not panic renderer.AddCircle(topLeft, size, false) } func TestPNGRenderer_AddCircleInvert(t *testing.T) { renderer := NewPNGRenderer(100) // First fill with background renderer.SetBackground("#ffffff", 1.0) renderer.BeginShape("#ff0000") // Add inverted circle (should not panic) topLeft := engine.Point{X: 30, Y: 30} size := 40.0 renderer.AddCircle(topLeft, size, true) } func TestPNGRenderer_ToPNG(t *testing.T) { renderer := NewPNGRenderer(50) renderer.SetBackground("#ffffff", 1.0) renderer.BeginShape("#ff0000") points := []engine.Point{ {X: 10, Y: 10}, {X: 40, Y: 10}, {X: 40, Y: 40}, {X: 10, Y: 40}, } renderer.AddPolygon(points) pngData, err := renderer.ToPNG() if err != nil { t.Fatalf("Failed to generate PNG: %v", err) } if len(pngData) == 0 { t.Error("ToPNG() should return non-empty data") } // Verify it's valid PNG data by decoding it reader := bytes.NewReader(pngData) decodedImg, err := png.Decode(reader) if err != nil { t.Errorf("ToPNG() returned invalid PNG data: %v", err) } // Check dimensions bounds := decodedImg.Bounds() if bounds.Max.X != 50 || bounds.Max.Y != 50 { t.Errorf("decoded image bounds = %v, want 50x50", bounds) } } func TestPNGRenderer_ToPNGWithSize(t *testing.T) { renderer := NewPNGRenderer(50) renderer.SetBackground("#ffffff", 1.0) renderer.BeginShape("#ff0000") points := []engine.Point{ {X: 10, Y: 10}, {X: 40, Y: 10}, {X: 40, Y: 40}, {X: 10, Y: 40}, } renderer.AddPolygon(points) // Test generating at different size pngData, err := renderer.ToPNGWithSize(100) if err != nil { t.Fatalf("Failed to generate PNG with size: %v", err) } if len(pngData) == 0 { t.Error("ToPNGWithSize() should return non-empty data") } // Verify it's valid PNG data by decoding it reader := bytes.NewReader(pngData) decodedImg, err := png.Decode(reader) if err != nil { t.Errorf("ToPNGWithSize() returned invalid PNG data: %v", err) } // Check dimensions - should be 100x100 instead of 50x50 bounds := decodedImg.Bounds() if bounds.Max.X != 100 || bounds.Max.Y != 100 { t.Errorf("decoded image bounds = %v, want 100x100", bounds) } } func TestPNGRenderer_ToPNGEmpty(t *testing.T) { renderer := NewPNGRenderer(10) pngData, err := renderer.ToPNG() if err != nil { t.Fatalf("Failed to generate PNG: %v", err) } if len(pngData) == 0 { t.Error("ToPNG() should return data even for empty image") } // Should be valid PNG reader := bytes.NewReader(pngData) decodedImg, err := png.Decode(reader) if err != nil { t.Errorf("ToPNG() returned invalid PNG data: %v", err) } // Check dimensions bounds := decodedImg.Bounds() if bounds.Max.X != 10 || bounds.Max.Y != 10 { t.Errorf("decoded image bounds = %v, want 10x10", bounds) } } func BenchmarkPNGRenderer_ToPNG(b *testing.B) { renderer := NewPNGRenderer(200) renderer.SetBackground("#ffffff", 1.0) // Add some shapes for a realistic benchmark renderer.BeginShape("#ff0000") renderer.AddPolygon([]engine.Point{ {X: 20, Y: 20}, {X: 80, Y: 20}, {X: 80, Y: 80}, {X: 20, Y: 80}, }) renderer.BeginShape("#00ff00") renderer.AddCircle(engine.Point{X: 100, Y: 100}, 30, false) b.ResetTimer() for i := 0; i < b.N; i++ { pngData, err := renderer.ToPNG() if err != nil { b.Fatalf("Failed to generate PNG: %v", err) } if len(pngData) == 0 { b.Fatal("ToPNG returned empty data") } } }