Files
go-jdenticon/internal/engine/layout_test.go
Kevin McIntyre f84b511895 init
2025-06-18 01:00:00 -04:00

380 lines
8.1 KiB
Go

package engine
import (
"math"
"testing"
)
func TestNewGrid(t *testing.T) {
tests := []struct {
name string
iconSize float64
paddingRatio float64
wantPadding int
wantCell int
}{
{
name: "standard 64px icon with 8% padding",
iconSize: 64.0,
paddingRatio: 0.08,
wantPadding: 5, // 0.5 + 64 * 0.08 = 5.62, rounded to 5
wantCell: 13, // (64 - 5*2) / 4 = 54/4 = 13.5, truncated to 13
},
{
name: "large 256px icon with 10% padding",
iconSize: 256.0,
paddingRatio: 0.10,
wantPadding: 26, // 0.5 + 256 * 0.10 = 26.1, rounded to 26
wantCell: 51, // (256 - 26*2) / 4 = 204/4 = 51
},
{
name: "small 32px icon with 5% padding",
iconSize: 32.0,
paddingRatio: 0.05,
wantPadding: 2, // 0.5 + 32 * 0.05 = 2.1, rounded to 2
wantCell: 7, // (32 - 2*2) / 4 = 28/4 = 7
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
grid := NewGrid(tt.iconSize, tt.paddingRatio)
if grid.Padding != tt.wantPadding {
t.Errorf("NewGrid() padding = %v, want %v", grid.Padding, tt.wantPadding)
}
if grid.Cell != tt.wantCell {
t.Errorf("NewGrid() cell = %v, want %v", grid.Cell, tt.wantCell)
}
// Verify that the grid is centered
expectedSize := tt.iconSize - float64(tt.wantPadding*2)
if math.Abs(grid.Size-expectedSize) > 0.1 {
t.Errorf("NewGrid() size = %v, want %v", grid.Size, expectedSize)
}
})
}
}
func TestGridCellToCoordinate(t *testing.T) {
grid := NewGrid(64.0, 0.08)
tests := []struct {
name string
cellX int
cellY int
wantX float64
wantY float64
}{
{
name: "origin cell (0,0)",
cellX: 0,
cellY: 0,
wantX: float64(grid.X),
wantY: float64(grid.Y),
},
{
name: "center cell (1,1)",
cellX: 1,
cellY: 1,
wantX: float64(grid.X + grid.Cell),
wantY: float64(grid.Y + grid.Cell),
},
{
name: "corner cell (3,3)",
cellX: 3,
cellY: 3,
wantX: float64(grid.X + 3*grid.Cell),
wantY: float64(grid.Y + 3*grid.Cell),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotX, gotY := grid.CellToCoordinate(tt.cellX, tt.cellY)
if gotX != tt.wantX {
t.Errorf("CellToCoordinate() x = %v, want %v", gotX, tt.wantX)
}
if gotY != tt.wantY {
t.Errorf("CellToCoordinate() y = %v, want %v", gotY, tt.wantY)
}
})
}
}
func TestLayoutEngineGetShapePositions(t *testing.T) {
le := NewLayoutEngine(64.0, 0.08)
tests := []struct {
name string
shapeType string
wantLen int
wantFirst Position
wantLast Position
}{
{
name: "sides positions",
shapeType: "sides",
wantLen: 8,
wantFirst: Position{1, 0},
wantLast: Position{0, 2},
},
{
name: "corners positions",
shapeType: "corners",
wantLen: 4,
wantFirst: Position{0, 0},
wantLast: Position{0, 3},
},
{
name: "center positions",
shapeType: "center",
wantLen: 4,
wantFirst: Position{1, 1},
wantLast: Position{1, 2},
},
{
name: "invalid shape type",
shapeType: "invalid",
wantLen: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
positions := le.GetShapePositions(tt.shapeType)
if len(positions) != tt.wantLen {
t.Errorf("GetShapePositions() len = %v, want %v", len(positions), tt.wantLen)
}
if tt.wantLen > 0 {
if positions[0] != tt.wantFirst {
t.Errorf("GetShapePositions() first = %v, want %v", positions[0], tt.wantFirst)
}
if positions[len(positions)-1] != tt.wantLast {
t.Errorf("GetShapePositions() last = %v, want %v", positions[len(positions)-1], tt.wantLast)
}
}
})
}
}
func TestLayoutEngineGetTransformedPosition(t *testing.T) {
le := NewLayoutEngine(64.0, 0.08)
tests := []struct {
name string
cellX int
cellY int
rotation int
wantX int // Expected cell X after rotation
wantY int // Expected cell Y after rotation
}{
{
name: "no rotation",
cellX: 1,
cellY: 0,
rotation: 0,
wantX: 1,
wantY: 0,
},
{
name: "90 degree rotation",
cellX: 1,
cellY: 0,
rotation: 1,
wantX: 0,
wantY: 2, // 3-1 = 2
},
{
name: "180 degree rotation",
cellX: 1,
cellY: 0,
rotation: 2,
wantX: 2, // 3-1 = 2
wantY: 3, // 3-0 = 3
},
{
name: "270 degree rotation",
cellX: 1,
cellY: 0,
rotation: 3,
wantX: 3, // 3-0 = 3
wantY: 1,
},
{
name: "rotation overflow (4 = 0)",
cellX: 1,
cellY: 0,
rotation: 4,
wantX: 1,
wantY: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotX, gotY, gotCellSize := le.GetTransformedPosition(tt.cellX, tt.cellY, tt.rotation)
// Convert back to cell coordinates to verify rotation
expectedX, expectedY := le.grid.CellToCoordinate(tt.wantX, tt.wantY)
if gotX != expectedX {
t.Errorf("GetTransformedPosition() x = %v, want %v", gotX, expectedX)
}
if gotY != expectedY {
t.Errorf("GetTransformedPosition() y = %v, want %v", gotY, expectedY)
}
if gotCellSize != float64(le.grid.Cell) {
t.Errorf("GetTransformedPosition() cellSize = %v, want %v", gotCellSize, float64(le.grid.Cell))
}
})
}
}
func TestApplySymmetry(t *testing.T) {
positions := []Position{{0, 0}, {1, 0}, {2, 0}, {3, 0}}
tests := []struct {
name string
index int
want int // expected length
}{
{
name: "valid index",
index: 1,
want: 4,
},
{
name: "index out of bounds",
index: 10,
want: 4,
},
{
name: "negative index",
index: -1,
want: 4,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := ApplySymmetry(positions, tt.index)
if len(result) != tt.want {
t.Errorf("ApplySymmetry() len = %v, want %v", len(result), tt.want)
}
// Verify that the positions are unchanged (current implementation)
for i, pos := range result {
if pos != positions[i] {
t.Errorf("ApplySymmetry() changed position at index %d: got %v, want %v", i, pos, positions[i])
}
}
})
}
}
func TestGridValidateGrid(t *testing.T) {
tests := []struct {
name string
grid *Grid
want bool
}{
{
name: "valid grid",
grid: &Grid{Size: 64, Cell: 16, Padding: 4},
want: true,
},
{
name: "zero cell size",
grid: &Grid{Size: 64, Cell: 0, Padding: 4},
want: false,
},
{
name: "zero size",
grid: &Grid{Size: 0, Cell: 16, Padding: 4},
want: false,
},
{
name: "negative padding",
grid: &Grid{Size: 64, Cell: 16, Padding: -1},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.grid.ValidateGrid(); got != tt.want {
t.Errorf("ValidateGrid() = %v, want %v", got, tt.want)
}
})
}
}
func TestGridGetIconBounds(t *testing.T) {
grid := NewGrid(64.0, 0.08)
x, y, width, height := grid.GetIconBounds()
expectedX := float64(grid.X)
expectedY := float64(grid.Y)
expectedWidth := float64(grid.Cell * 4)
expectedHeight := float64(grid.Cell * 4)
if x != expectedX {
t.Errorf("GetIconBounds() x = %v, want %v", x, expectedX)
}
if y != expectedY {
t.Errorf("GetIconBounds() y = %v, want %v", y, expectedY)
}
if width != expectedWidth {
t.Errorf("GetIconBounds() width = %v, want %v", width, expectedWidth)
}
if height != expectedHeight {
t.Errorf("GetIconBounds() height = %v, want %v", height, expectedHeight)
}
}
func TestGridGetCenterOffset(t *testing.T) {
grid := NewGrid(64.0, 0.08)
dx, dy := grid.GetCenterOffset()
expected := float64(grid.Cell) / 2
if dx != expected {
t.Errorf("GetCenterOffset() dx = %v, want %v", dx, expected)
}
if dy != expected {
t.Errorf("GetCenterOffset() dy = %v, want %v", dy, expected)
}
}
func TestNewLayoutEngine(t *testing.T) {
le := NewLayoutEngine(64.0, 0.08)
if le.grid == nil {
t.Error("NewLayoutEngine() grid is nil")
}
if le.Grid() != le.grid {
t.Error("NewLayoutEngine() Grid() does not return internal grid")
}
// Verify grid configuration
if !le.grid.ValidateGrid() {
t.Error("NewLayoutEngine() created invalid grid")
}
}