517 lines
11 KiB
Go
517 lines
11 KiB
Go
package engine
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/kevin/go-jdenticon/internal/util"
|
|
)
|
|
|
|
func TestNewGenerator(t *testing.T) {
|
|
config := DefaultColorConfig()
|
|
generator := NewGenerator(config)
|
|
|
|
if generator == nil {
|
|
t.Fatal("NewGenerator returned nil")
|
|
}
|
|
|
|
if generator.config.IconPadding != config.IconPadding {
|
|
t.Errorf("Expected icon padding %f, got %f", config.IconPadding, generator.config.IconPadding)
|
|
}
|
|
|
|
if generator.cache == nil {
|
|
t.Error("Generator cache was not initialized")
|
|
}
|
|
}
|
|
|
|
func TestNewDefaultGenerator(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
|
|
if generator == nil {
|
|
t.Fatal("NewDefaultGenerator returned nil")
|
|
}
|
|
|
|
expectedConfig := DefaultColorConfig()
|
|
if generator.config.IconPadding != expectedConfig.IconPadding {
|
|
t.Errorf("Expected icon padding %f, got %f", expectedConfig.IconPadding, generator.config.IconPadding)
|
|
}
|
|
}
|
|
|
|
func TestGenerateValidHash(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
hash := "abcdef123456789"
|
|
size := 64.0
|
|
|
|
icon, err := generator.Generate(hash, size)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Generate failed with error: %v", err)
|
|
}
|
|
|
|
if icon == nil {
|
|
t.Fatal("Generate returned nil icon")
|
|
}
|
|
|
|
if icon.Hash != hash {
|
|
t.Errorf("Expected hash %s, got %s", hash, icon.Hash)
|
|
}
|
|
|
|
if icon.Size != size {
|
|
t.Errorf("Expected size %f, got %f", size, icon.Size)
|
|
}
|
|
|
|
if len(icon.Shapes) == 0 {
|
|
t.Error("Generated icon has no shapes")
|
|
}
|
|
}
|
|
|
|
func TestGenerateInvalidInputs(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
|
|
tests := []struct {
|
|
name string
|
|
hash string
|
|
size float64
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "empty hash",
|
|
hash: "",
|
|
size: 64.0,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "zero size",
|
|
hash: "abcdef123456789",
|
|
size: 0.0,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "negative size",
|
|
hash: "abcdef123456789",
|
|
size: -10.0,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "short hash",
|
|
hash: "abc",
|
|
size: 64.0,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid hex characters",
|
|
hash: "xyz123456789abc",
|
|
size: 64.0,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := generator.Generate(tt.hash, tt.size)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Generate() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateCaching(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
hash := "abcdef123456789"
|
|
size := 64.0
|
|
|
|
// Generate icon first time
|
|
icon1, err := generator.Generate(hash, size)
|
|
if err != nil {
|
|
t.Fatalf("First generate failed: %v", err)
|
|
}
|
|
|
|
// Check cache size
|
|
if generator.GetCacheSize() != 1 {
|
|
t.Errorf("Expected cache size 1, got %d", generator.GetCacheSize())
|
|
}
|
|
|
|
// Generate same icon again
|
|
icon2, err := generator.Generate(hash, size)
|
|
if err != nil {
|
|
t.Fatalf("Second generate failed: %v", err)
|
|
}
|
|
|
|
// Should be the same instance from cache
|
|
if icon1 != icon2 {
|
|
t.Error("Second generate did not return cached instance")
|
|
}
|
|
|
|
// Cache size should still be 1
|
|
if generator.GetCacheSize() != 1 {
|
|
t.Errorf("Expected cache size 1 after second generate, got %d", generator.GetCacheSize())
|
|
}
|
|
}
|
|
|
|
func TestClearCache(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
hash := "abcdef123456789"
|
|
size := 64.0
|
|
|
|
// Generate an icon to populate cache
|
|
_, err := generator.Generate(hash, size)
|
|
if err != nil {
|
|
t.Fatalf("Generate failed: %v", err)
|
|
}
|
|
|
|
// Verify cache has content
|
|
if generator.GetCacheSize() == 0 {
|
|
t.Error("Cache should not be empty after generate")
|
|
}
|
|
|
|
// Clear cache
|
|
generator.ClearCache()
|
|
|
|
// Verify cache is empty
|
|
if generator.GetCacheSize() != 0 {
|
|
t.Errorf("Expected cache size 0 after clear, got %d", generator.GetCacheSize())
|
|
}
|
|
}
|
|
|
|
func TestSetConfig(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
hash := "abcdef123456789"
|
|
size := 64.0
|
|
|
|
// Generate an icon to populate cache
|
|
_, err := generator.Generate(hash, size)
|
|
if err != nil {
|
|
t.Fatalf("Generate failed: %v", err)
|
|
}
|
|
|
|
// Verify cache has content
|
|
if generator.GetCacheSize() == 0 {
|
|
t.Error("Cache should not be empty after generate")
|
|
}
|
|
|
|
// Set new config
|
|
newConfig := DefaultColorConfig()
|
|
newConfig.IconPadding = 0.1
|
|
generator.SetConfig(newConfig)
|
|
|
|
// Verify config was updated
|
|
if generator.GetConfig().IconPadding != 0.1 {
|
|
t.Errorf("Expected icon padding 0.1, got %f", generator.GetConfig().IconPadding)
|
|
}
|
|
|
|
// Verify cache was cleared
|
|
if generator.GetCacheSize() != 0 {
|
|
t.Errorf("Expected cache size 0 after config change, got %d", generator.GetCacheSize())
|
|
}
|
|
}
|
|
|
|
func TestExtractHue(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
|
|
tests := []struct {
|
|
name string
|
|
hash string
|
|
expected float64
|
|
tolerance float64
|
|
}{
|
|
{
|
|
name: "all zeros",
|
|
hash: "0000000000000000000",
|
|
expected: 0.0,
|
|
tolerance: 0.0001,
|
|
},
|
|
{
|
|
name: "all fs",
|
|
hash: "ffffffffffffffffff",
|
|
expected: 1.0,
|
|
tolerance: 0.0001,
|
|
},
|
|
{
|
|
name: "half value",
|
|
hash: "000000000007ffffff",
|
|
expected: 0.5,
|
|
tolerance: 0.001, // Allow small floating point variance
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result, err := generator.extractHue(tt.hash)
|
|
if err != nil {
|
|
t.Fatalf("extractHue failed: %v", err)
|
|
}
|
|
diff := result - tt.expected
|
|
if diff < 0 {
|
|
diff = -diff
|
|
}
|
|
if diff > tt.tolerance {
|
|
t.Errorf("Expected hue %f, got %f (tolerance %f)", tt.expected, result, tt.tolerance)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSelectColors(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
hash := "123456789abcdef"
|
|
|
|
// Create test color palette
|
|
availableColors := []Color{
|
|
NewColorRGB(50, 50, 50), // 0: Dark gray
|
|
NewColorRGB(100, 100, 200), // 1: Mid color
|
|
NewColorRGB(200, 200, 200), // 2: Light gray
|
|
NewColorRGB(150, 150, 255), // 3: Light color
|
|
NewColorRGB(25, 25, 100), // 4: Dark color
|
|
}
|
|
|
|
selectedIndexes, err := generator.selectColors(hash, availableColors)
|
|
|
|
if err != nil {
|
|
t.Fatalf("selectColors failed: %v", err)
|
|
}
|
|
|
|
if len(selectedIndexes) != 3 {
|
|
t.Fatalf("Expected 3 selected colors, got %d", len(selectedIndexes))
|
|
}
|
|
|
|
for i, index := range selectedIndexes {
|
|
if index < 0 || index >= len(availableColors) {
|
|
t.Errorf("Color index %d at position %d is out of range [0, %d)", index, i, len(availableColors))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSelectColorsEmptyPalette(t *testing.T) {
|
|
generator := NewDefaultGenerator()
|
|
hash := "123456789abcdef"
|
|
|
|
_, err := generator.selectColors(hash, []Color{})
|
|
|
|
if err == nil {
|
|
t.Error("Expected error for empty color palette")
|
|
}
|
|
}
|
|
|
|
func TestIsValidHash(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
hash string
|
|
valid bool
|
|
}{
|
|
{
|
|
name: "valid hash",
|
|
hash: "abcdef123456789",
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "too short",
|
|
hash: "abc",
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "invalid characters",
|
|
hash: "xyz123456789abc",
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "uppercase valid",
|
|
hash: "ABCDEF123456789",
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "mixed case valid",
|
|
hash: "AbCdEf123456789",
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "empty",
|
|
hash: "",
|
|
valid: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := util.IsValidHash(tt.hash)
|
|
if result != tt.valid {
|
|
t.Errorf("Expected isValidHash(%s) = %v, got %v", tt.hash, tt.valid, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseHex(t *testing.T) {
|
|
hash := "123456789abcdef"
|
|
|
|
tests := []struct {
|
|
name string
|
|
start int
|
|
octets int
|
|
expected int
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "single character",
|
|
start: 0,
|
|
octets: 1,
|
|
expected: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "two characters",
|
|
start: 1,
|
|
octets: 2,
|
|
expected: 0x23,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "negative index",
|
|
start: -1,
|
|
octets: 1,
|
|
expected: 0xf,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "out of bounds",
|
|
start: 100,
|
|
octets: 1,
|
|
expected: 0,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result, err := util.ParseHex(hash, tt.start, tt.octets)
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Errorf("Expected an error, but got nil")
|
|
}
|
|
return // Test is done for error cases
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("parseHex failed unexpectedly: %v", err)
|
|
}
|
|
if result != tt.expected {
|
|
t.Errorf("Expected %d, got %d", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestShapeCollector(t *testing.T) {
|
|
collector := &shapeCollector{}
|
|
|
|
// Test AddPolygon
|
|
points := []Point{{X: 0, Y: 0}, {X: 1, Y: 0}, {X: 1, Y: 1}, {X: 0, Y: 1}}
|
|
collector.AddPolygon(points)
|
|
|
|
if len(collector.shapes) != 1 {
|
|
t.Fatalf("Expected 1 shape after AddPolygon, got %d", len(collector.shapes))
|
|
}
|
|
|
|
shape := collector.shapes[0]
|
|
if shape.Type != "polygon" {
|
|
t.Errorf("Expected shape type 'polygon', got '%s'", shape.Type)
|
|
}
|
|
|
|
if len(shape.Points) != len(points) {
|
|
t.Errorf("Expected %d points, got %d", len(points), len(shape.Points))
|
|
}
|
|
|
|
// Test AddCircle
|
|
center := Point{X: 5, Y: 5}
|
|
radius := 2.5
|
|
collector.AddCircle(center, radius, false)
|
|
|
|
if len(collector.shapes) != 2 {
|
|
t.Fatalf("Expected 2 shapes after AddCircle, got %d", len(collector.shapes))
|
|
}
|
|
|
|
circleShape := collector.shapes[1]
|
|
if circleShape.Type != "circle" {
|
|
t.Errorf("Expected shape type 'circle', got '%s'", circleShape.Type)
|
|
}
|
|
|
|
// Verify circle fields are set correctly
|
|
if circleShape.CircleX != center.X {
|
|
t.Errorf("Expected CircleX %f, got %f", center.X, circleShape.CircleX)
|
|
}
|
|
if circleShape.CircleY != center.Y {
|
|
t.Errorf("Expected CircleY %f, got %f", center.Y, circleShape.CircleY)
|
|
}
|
|
if circleShape.CircleSize != radius {
|
|
t.Errorf("Expected CircleSize %f, got %f", radius, circleShape.CircleSize)
|
|
}
|
|
if circleShape.Invert != false {
|
|
t.Errorf("Expected Invert false, got %t", circleShape.Invert)
|
|
}
|
|
}
|
|
|
|
func BenchmarkGenerate(b *testing.B) {
|
|
generator := NewDefaultGenerator()
|
|
hash := "abcdef123456789"
|
|
size := 64.0
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := generator.Generate(hash, size)
|
|
if err != nil {
|
|
b.Fatalf("Generate failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkGenerateWithCache(b *testing.B) {
|
|
generator := NewDefaultGenerator()
|
|
hash := "abcdef123456789"
|
|
size := 64.0
|
|
|
|
// Pre-populate cache
|
|
_, err := generator.Generate(hash, size)
|
|
if err != nil {
|
|
b.Fatalf("Initial generate failed: %v", err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := generator.Generate(hash, size)
|
|
if err != nil {
|
|
b.Fatalf("Generate failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsistentGeneration(t *testing.T) {
|
|
generator1 := NewDefaultGenerator()
|
|
generator2 := NewDefaultGenerator()
|
|
|
|
hash := "abcdef123456789"
|
|
size := 64.0
|
|
|
|
icon1, err := generator1.Generate(hash, size)
|
|
if err != nil {
|
|
t.Fatalf("Generator1 failed: %v", err)
|
|
}
|
|
|
|
icon2, err := generator2.Generate(hash, size)
|
|
if err != nil {
|
|
t.Fatalf("Generator2 failed: %v", err)
|
|
}
|
|
|
|
// Icons should have same number of shape groups
|
|
if len(icon1.Shapes) != len(icon2.Shapes) {
|
|
t.Errorf("Different number of shape groups: %d vs %d", len(icon1.Shapes), len(icon2.Shapes))
|
|
}
|
|
|
|
// Colors should be the same
|
|
for i := range icon1.Shapes {
|
|
if i >= len(icon2.Shapes) {
|
|
break
|
|
}
|
|
if !icon1.Shapes[i].Color.Equals(icon2.Shapes[i].Color) {
|
|
t.Errorf("Different colors at group %d", i)
|
|
}
|
|
}
|
|
} |