package engine import ( "context" "fmt" "testing" "gitea.dockr.co/kev/go-jdenticon/internal/util" ) var benchmarkHashes = []string{ "7c4a8d09ca3762af61e59520943dc26494f8941b", // test-hash "b36d9b6a07d0b5bfb7e0e77a7f8d1e5e6f7a8b9c", // example1@gmail.com "a9d8e7f6c5b4a3d2e1f0e9d8c7b6a5d4e3f2a1b0", // example2@yahoo.com "1234567890abcdef1234567890abcdef12345678", "fedcba0987654321fedcba0987654321fedcba09", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "0000000000000000000000000000000000000000", "ffffffffffffffffffffffffffffffffffffffffffff", } var benchmarkSizesFloat = []float64{ 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, } // Benchmark core generator creation func BenchmarkNewGeneratorWithConfig(b *testing.B) { config := GeneratorConfig{ ColorConfig: DefaultColorConfig(), CacheSize: 1000, } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { generator, err := NewGeneratorWithConfig(config) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } _ = generator } } // Benchmark icon generation without cache (per size) func BenchmarkGenerateWithoutCachePerSize(b *testing.B) { config := GeneratorConfig{ ColorConfig: DefaultColorConfig(), CacheSize: 1000, } generator, err := NewGeneratorWithConfig(config) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } for _, size := range benchmarkSizesFloat { b.Run(fmt.Sprintf("size-%.0f", size), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { hash := benchmarkHashes[i%len(benchmarkHashes)] _, err := generator.GenerateWithoutCache(context.Background(), hash, size) if err != nil { b.Fatalf("GenerateWithoutCache failed: %v", err) } } }) } } // Benchmark icon generation with cache (different from generator_test.go) func BenchmarkGenerateWithCacheHeavy(b *testing.B) { config := GeneratorConfig{ ColorConfig: DefaultColorConfig(), CacheSize: 100, } generator, err := NewGeneratorWithConfig(config) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { // Use limited set of hashes to test cache hits hash := benchmarkHashes[i%3] // Only use first 3 hashes size := 64.0 _, err := generator.Generate(context.Background(), hash, size) if err != nil { b.Fatalf("Generate failed: %v", err) } } } // Benchmark hash parsing functions func BenchmarkParseHex(b *testing.B) { hash := "7c4a8d09ca3762af61e59520943dc26494f8941b" b.Run("offset2_len1", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _, _ = util.ParseHex(hash, 2, 1) } }) b.Run("offset4_len1", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _, _ = util.ParseHex(hash, 4, 1) } }) b.Run("offset1_len1", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _, _ = util.ParseHex(hash, 1, 1) } }) b.Run("offset8_len3", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _, _ = util.ParseHex(hash, 8, 3) } }) } // Benchmark hue extraction func BenchmarkExtractHue(b *testing.B) { config := GeneratorConfig{ ColorConfig: DefaultColorConfig(), CacheSize: 1, } generator, err := NewGeneratorWithConfig(config) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { hash := benchmarkHashes[i%len(benchmarkHashes)] _, _ = generator.extractHue(hash) } } // Benchmark shape selection func BenchmarkShapeSelection(b *testing.B) { hash := "7c4a8d09ca3762af61e59520943dc26494f8941b" b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { // Simulate shape selection process using util.ParseHex sideShapeIndex, _ := util.ParseHex(hash, hashPosSideShape, 1) cornerShapeIndex, _ := util.ParseHex(hash, hashPosCornerShape, 1) centerShapeIndex, _ := util.ParseHex(hash, hashPosCenterShape, 1) // Use modulo with arbitrary shape counts (simulating actual shape arrays) sideShapeIndex = sideShapeIndex % 16 // Assume 16 outer shapes cornerShapeIndex = cornerShapeIndex % 16 centerShapeIndex = centerShapeIndex % 8 // Assume 8 center shapes _, _, _ = sideShapeIndex, cornerShapeIndex, centerShapeIndex } } // Benchmark color theme generation func BenchmarkGenerateColorTheme(b *testing.B) { config := DefaultColorConfig() generator, err := NewGeneratorWithConfig(GeneratorConfig{ ColorConfig: config, CacheSize: 1, }) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { hash := benchmarkHashes[i%len(benchmarkHashes)] hue, _ := generator.extractHue(hash) _ = GenerateColorTheme(hue, config) } } // Benchmark position computation func BenchmarkComputePositions(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { // Test both side and corner positions _ = getSidePositions() _ = getCornerPositions() } } // Benchmark transform applications func BenchmarkTransformApplication(b *testing.B) { transform := Transform{ x: 1.0, y: 2.0, size: 64.0, rotation: 1, } b.Run("center_point", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _ = transform.TransformIconPoint(0.5, 0.5, 0, 0) } }) b.Run("corner_point", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _ = transform.TransformIconPoint(1.0, 1.0, 0, 0) } }) b.Run("origin_point", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _ = transform.TransformIconPoint(0.0, 0.0, 0, 0) } }) } // Benchmark icon size calculations func BenchmarkIconSizeCalculations(b *testing.B) { sizes := benchmarkSizesFloat padding := 0.1 b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { size := sizes[i%len(sizes)] // Simulate size calculations from generator paddingPixels := size * padding * paddingMultiple iconSize := size - paddingPixels cellSize := iconSize / gridSize _, _, _ = paddingPixels, iconSize, cellSize } } // Benchmark cache key generation func BenchmarkCacheKeyGeneration(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { hash := benchmarkHashes[i%len(benchmarkHashes)] size := benchmarkSizesFloat[i%len(benchmarkSizesFloat)] _ = benchmarkCacheKey(hash, size) } } // Helper function to simulate cache key generation func benchmarkCacheKey(hash string, size float64) string { return hash + ":" + fmt.Sprintf("%.0f", size) } // Benchmark full icon generation pipeline func BenchmarkFullGenerationPipeline(b *testing.B) { config := GeneratorConfig{ ColorConfig: DefaultColorConfig(), CacheSize: 1, // Minimal cache to avoid cache hits } generator, err := NewGeneratorWithConfig(config) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { hash := benchmarkHashes[i%len(benchmarkHashes)] size := 64.0 // This tests the full pipeline: hash parsing, color generation, // shape selection, positioning, and rendering preparation _, err := generator.GenerateWithoutCache(context.Background(), hash, size) if err != nil { b.Fatalf("GenerateWithoutCache failed: %v", err) } } } // Benchmark different grid sizes (theoretical) func BenchmarkGridSizeCalculations(b *testing.B) { sizes := benchmarkSizesFloat b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { size := sizes[i%len(sizes)] padding := 0.1 // Test calculations for different theoretical grid sizes for gridSizeTest := 3; gridSizeTest <= 6; gridSizeTest++ { paddingPixels := size * padding * paddingMultiple iconSize := size - paddingPixels cellSize := iconSize / float64(gridSizeTest) _ = cellSize } } } // Benchmark color conflict resolution func BenchmarkColorConflictResolution(b *testing.B) { config := DefaultColorConfig() generator, err := NewGeneratorWithConfig(GeneratorConfig{ ColorConfig: config, CacheSize: 1, }) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { hash := benchmarkHashes[i%len(benchmarkHashes)] hue, _ := generator.extractHue(hash) colorTheme := GenerateColorTheme(hue, config) // Simulate color conflict resolution for j := 0; j < 5; j++ { colorHash, _ := util.ParseHex(hash, hashPosColorStart+j%3, 1) selectedColor := colorTheme[colorHash%len(colorTheme)] _ = selectedColor } } } // Helper function to get side positions (matching generator logic) func getSidePositions() [][]int { return [][]int{{1, 0}, {2, 0}, {2, 3}, {1, 3}, {0, 1}, {3, 1}, {3, 2}, {0, 2}} } // Helper function to get corner positions (matching generator logic) func getCornerPositions() [][]int { return [][]int{{0, 0}, {3, 0}, {3, 3}, {0, 3}} } // Benchmark concurrent icon generation for high-traffic scenarios func BenchmarkGenerateWithoutCacheParallel(b *testing.B) { config := GeneratorConfig{ ColorConfig: DefaultColorConfig(), CacheSize: 1, // Minimal cache to avoid cache effects } generator, err := NewGeneratorWithConfig(config) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } for _, size := range []float64{64.0, 128.0, 256.0} { b.Run(fmt.Sprintf("size-%.0f", size), func(b *testing.B) { b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { hash := benchmarkHashes[i%len(benchmarkHashes)] _, err := generator.GenerateWithoutCache(context.Background(), hash, size) if err != nil { b.Errorf("GenerateWithoutCache failed: %v", err) } i++ } }) }) } } // Benchmark concurrent cached generation func BenchmarkGenerateWithCacheParallel(b *testing.B) { config := GeneratorConfig{ ColorConfig: DefaultColorConfig(), CacheSize: 100, // Shared cache for concurrent access } generator, err := NewGeneratorWithConfig(config) if err != nil { b.Fatalf("NewGeneratorWithConfig failed: %v", err) } b.ReportAllocs() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { // Use limited set of hashes to test cache hits under concurrency hash := benchmarkHashes[i%3] // Only use first 3 hashes size := 64.0 _, err := generator.Generate(context.Background(), hash, size) if err != nil { b.Errorf("Generate failed: %v", err) } i++ } }) }