package renderer import ( "testing" "github.com/kevin/go-jdenticon/internal/engine" ) func TestNewBaseRenderer(t *testing.T) { iconSize := 100 r := NewBaseRenderer(iconSize) if r.GetSize() != iconSize { t.Errorf("Expected icon size %d, got %d", iconSize, r.GetSize()) } if len(r.GetCurrentPath()) != 0 { t.Errorf("Expected empty path, got %d commands", len(r.GetCurrentPath())) } if r.GetCurrentColor() != "" { t.Errorf("Expected empty current color, got %s", r.GetCurrentColor()) } bg, bgOp := r.GetBackground() if bg != "" || bgOp != 0 { t.Errorf("Expected empty background, got %s with opacity %f", bg, bgOp) } } func TestBaseRendererSetBackground(t *testing.T) { r := NewBaseRenderer(100) color := "#ff0000" opacity := 0.5 r.SetBackground(color, opacity) bg, bgOp := r.GetBackground() if bg != color { t.Errorf("Expected background color %s, got %s", color, bg) } if bgOp != opacity { t.Errorf("Expected background opacity %f, got %f", opacity, bgOp) } } func TestBaseRendererBeginShape(t *testing.T) { r := NewBaseRenderer(100) color := "#00ff00" r.BeginShape(color) if r.GetCurrentColor() != color { t.Errorf("Expected current color %s, got %s", color, r.GetCurrentColor()) } // Path should be reset when beginning a shape if len(r.GetCurrentPath()) != 0 { t.Errorf("Expected empty path after BeginShape, got %d commands", len(r.GetCurrentPath())) } } func TestBaseRendererMoveTo(t *testing.T) { r := NewBaseRenderer(100) x, y := 10.5, 20.3 r.MoveTo(x, y) path := r.GetCurrentPath() if len(path) != 1 { t.Fatalf("Expected 1 path command, got %d", len(path)) } cmd := path[0] if cmd.Type != MoveToCommand { t.Errorf("Expected MoveToCommand, got %v", cmd.Type) } if len(cmd.Points) != 1 { t.Fatalf("Expected 1 point, got %d", len(cmd.Points)) } point := cmd.Points[0] if point.X != x || point.Y != y { t.Errorf("Expected point (%f, %f), got (%f, %f)", x, y, point.X, point.Y) } } func TestBaseRendererLineTo(t *testing.T) { r := NewBaseRenderer(100) // Move to start point first r.MoveTo(0, 0) x, y := 15.7, 25.9 r.LineTo(x, y) path := r.GetCurrentPath() if len(path) != 2 { t.Fatalf("Expected 2 path commands, got %d", len(path)) } cmd := path[1] // Second command should be LineTo if cmd.Type != LineToCommand { t.Errorf("Expected LineToCommand, got %v", cmd.Type) } if len(cmd.Points) != 1 { t.Fatalf("Expected 1 point, got %d", len(cmd.Points)) } point := cmd.Points[0] if point.X != x || point.Y != y { t.Errorf("Expected point (%f, %f), got (%f, %f)", x, y, point.X, point.Y) } } func TestBaseRendererCurveTo(t *testing.T) { r := NewBaseRenderer(100) // Move to start point first r.MoveTo(0, 0) x1, y1 := 10.0, 5.0 x2, y2 := 20.0, 15.0 x, y := 30.0, 25.0 r.CurveTo(x1, y1, x2, y2, x, y) path := r.GetCurrentPath() if len(path) != 2 { t.Fatalf("Expected 2 path commands, got %d", len(path)) } cmd := path[1] // Second command should be CurveTo if cmd.Type != CurveToCommand { t.Errorf("Expected CurveToCommand, got %v", cmd.Type) } if len(cmd.Points) != 3 { t.Fatalf("Expected 3 points, got %d", len(cmd.Points)) } // Check control points and end point if cmd.Points[0].X != x1 || cmd.Points[0].Y != y1 { t.Errorf("Expected first control point (%f, %f), got (%f, %f)", x1, y1, cmd.Points[0].X, cmd.Points[0].Y) } if cmd.Points[1].X != x2 || cmd.Points[1].Y != y2 { t.Errorf("Expected second control point (%f, %f), got (%f, %f)", x2, y2, cmd.Points[1].X, cmd.Points[1].Y) } if cmd.Points[2].X != x || cmd.Points[2].Y != y { t.Errorf("Expected end point (%f, %f), got (%f, %f)", x, y, cmd.Points[2].X, cmd.Points[2].Y) } } func TestBaseRendererClosePath(t *testing.T) { r := NewBaseRenderer(100) // Move to start point first r.MoveTo(0, 0) r.LineTo(10, 10) r.ClosePath() path := r.GetCurrentPath() if len(path) != 3 { t.Fatalf("Expected 3 path commands, got %d", len(path)) } cmd := path[2] // Third command should be ClosePath if cmd.Type != ClosePathCommand { t.Errorf("Expected ClosePathCommand, got %v", cmd.Type) } if len(cmd.Points) != 0 { t.Errorf("Expected 0 points for ClosePath, got %d", len(cmd.Points)) } } func TestBaseRendererAddPolygon(t *testing.T) { r := NewBaseRenderer(100) r.BeginShape("#ff0000") points := []engine.Point{ {X: 0, Y: 0}, {X: 10, Y: 0}, {X: 10, Y: 10}, {X: 0, Y: 10}, } r.AddPolygon(points) path := r.GetCurrentPath() // Should have MoveTo + 3 LineTo + ClosePath = 5 commands expectedCommands := len(points) + 1 // +1 for ClosePath if len(path) != expectedCommands { t.Fatalf("Expected %d path commands, got %d", expectedCommands, len(path)) } // Check first command is MoveTo if path[0].Type != MoveToCommand { t.Errorf("Expected first command to be MoveTo, got %v", path[0].Type) } // Check last command is ClosePath if path[len(path)-1].Type != ClosePathCommand { t.Errorf("Expected last command to be ClosePath, got %v", path[len(path)-1].Type) } } func TestBaseRendererAddRectangle(t *testing.T) { r := NewBaseRenderer(100) r.BeginShape("#0000ff") x, y, width, height := 5.0, 10.0, 20.0, 15.0 r.AddRectangle(x, y, width, height) path := r.GetCurrentPath() // Should have MoveTo + 3 LineTo + ClosePath = 5 commands if len(path) != 5 { t.Fatalf("Expected 5 path commands, got %d", len(path)) } // Verify the rectangle points expectedPoints := []engine.Point{ {X: x, Y: y}, // bottom-left {X: x + width, Y: y}, // bottom-right {X: x + width, Y: y + height}, // top-right {X: x, Y: y + height}, // top-left } // Check MoveTo point if path[0].Points[0] != expectedPoints[0] { t.Errorf("Expected first point %v, got %v", expectedPoints[0], path[0].Points[0]) } // Check LineTo points for i := 1; i < 4; i++ { if path[i].Type != LineToCommand { t.Errorf("Expected LineTo command at index %d, got %v", i, path[i].Type) } if path[i].Points[0] != expectedPoints[i] { t.Errorf("Expected point %v at index %d, got %v", expectedPoints[i], i, path[i].Points[0]) } } } func TestBaseRendererAddTriangle(t *testing.T) { r := NewBaseRenderer(100) r.BeginShape("#00ffff") p1 := engine.Point{X: 0, Y: 0} p2 := engine.Point{X: 10, Y: 0} p3 := engine.Point{X: 5, Y: 10} r.AddTriangle(p1, p2, p3) path := r.GetCurrentPath() // Should have MoveTo + 2 LineTo + ClosePath = 4 commands if len(path) != 4 { t.Fatalf("Expected 4 path commands, got %d", len(path)) } // Check the triangle points if path[0].Points[0] != p1 { t.Errorf("Expected first point %v, got %v", p1, path[0].Points[0]) } if path[1].Points[0] != p2 { t.Errorf("Expected second point %v, got %v", p2, path[1].Points[0]) } if path[2].Points[0] != p3 { t.Errorf("Expected third point %v, got %v", p3, path[2].Points[0]) } } func TestBaseRendererAddCircle(t *testing.T) { r := NewBaseRenderer(100) r.BeginShape("#ffff00") center := engine.Point{X: 50, Y: 50} radius := 25.0 r.AddCircle(center, radius, false) path := r.GetCurrentPath() // Should have MoveTo + 4 CurveTo + ClosePath = 6 commands if len(path) != 6 { t.Fatalf("Expected 6 path commands for circle, got %d", len(path)) } // Check first command is MoveTo if path[0].Type != MoveToCommand { t.Errorf("Expected first command to be MoveTo, got %v", path[0].Type) } // Check that we have 4 CurveTo commands curveCount := 0 for i := 1; i < len(path)-1; i++ { if path[i].Type == CurveToCommand { curveCount++ } } if curveCount != 4 { t.Errorf("Expected 4 CurveTo commands for circle, got %d", curveCount) } // Check last command is ClosePath if path[len(path)-1].Type != ClosePathCommand { t.Errorf("Expected last command to be ClosePath, got %v", path[len(path)-1].Type) } } func TestBaseRendererClear(t *testing.T) { r := NewBaseRenderer(100) // Set some state r.BeginShape("#ff0000") r.SetBackground("#ffffff", 0.8) r.MoveTo(10, 20) r.LineTo(30, 40) // Verify state is set if r.GetCurrentColor() == "" { t.Error("Expected current color to be set before clear") } if len(r.GetCurrentPath()) == 0 { t.Error("Expected path commands before clear") } // Clear the renderer r.Clear() // Verify state is cleared if r.GetCurrentColor() != "" { t.Errorf("Expected empty current color after clear, got %s", r.GetCurrentColor()) } if len(r.GetCurrentPath()) != 0 { t.Errorf("Expected empty path after clear, got %d commands", len(r.GetCurrentPath())) } bg, bgOp := r.GetBackground() if bg != "" || bgOp != 0 { t.Errorf("Expected empty background after clear, got %s with opacity %f", bg, bgOp) } } func TestBaseRendererEmptyPolygon(t *testing.T) { r := NewBaseRenderer(100) r.BeginShape("#ff0000") // Test with empty points slice r.AddPolygon([]engine.Point{}) path := r.GetCurrentPath() if len(path) != 0 { t.Errorf("Expected no path commands for empty polygon, got %d", len(path)) } }