package achievements import ( "fmt" "math/rand" "sync" "testing" "eq2emu/internal/database" ) // Global shared master list for benchmarks to avoid repeated setup var ( sharedAchievementMasterList *MasterList sharedAchievements []*Achievement achievementSetupOnce sync.Once ) // setupSharedAchievementMasterList creates the shared master list once func setupSharedAchievementMasterList(b *testing.B) { achievementSetupOnce.Do(func() { // Create test database db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { b.Fatalf("Failed to create test database: %v", err) } sharedAchievementMasterList = NewMasterList() // Pre-populate with achievements for realistic testing const numAchievements = 1000 sharedAchievements = make([]*Achievement, numAchievements) categories := []string{"Combat", "Crafting", "Exploration", "Social", "PvP", "Quests", "Collections", "Dungeons"} expansions := []string{"Classic", "Kingdom of Sky", "Echoes of Faydwer", "Rise of Kunark", "The Shadow Odyssey", "Sentinel's Fate"} for i := range numAchievements { sharedAchievements[i] = New(db) sharedAchievements[i].AchievementID = uint32(i + 1) sharedAchievements[i].Title = fmt.Sprintf("Achievement %d", i+1) sharedAchievements[i].Category = categories[i%len(categories)] sharedAchievements[i].Expansion = expansions[i%len(expansions)] sharedAchievements[i].PointValue = uint32(rand.Intn(50) + 10) sharedAchievements[i].QtyRequired = uint32(rand.Intn(100) + 1) // Add some requirements and rewards sharedAchievements[i].AddRequirement(fmt.Sprintf("task_%d", i%10), uint32(rand.Intn(10)+1)) sharedAchievements[i].AddReward(fmt.Sprintf("reward_%d", i%5)) sharedAchievementMasterList.AddAchievement(sharedAchievements[i]) } }) } // createTestAchievement creates an achievement for benchmarking func createTestAchievement(b *testing.B, id uint32) *Achievement { b.Helper() // Use nil database for benchmarking in-memory operations achievement := New(nil) achievement.AchievementID = id achievement.Title = fmt.Sprintf("Benchmark Achievement %d", id) achievement.Category = []string{"Combat", "Crafting", "Exploration", "Social"}[id%4] achievement.Expansion = []string{"Classic", "Expansion1", "Expansion2"}[id%3] achievement.PointValue = uint32(rand.Intn(50) + 10) achievement.QtyRequired = uint32(rand.Intn(100) + 1) // Add mock requirements and rewards achievement.AddRequirement(fmt.Sprintf("task_%d", id%10), uint32(rand.Intn(10)+1)) achievement.AddReward(fmt.Sprintf("reward_%d", id%5)) return achievement } // BenchmarkAchievementCreation measures achievement creation performance func BenchmarkAchievementCreation(b *testing.B) { db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { b.Fatalf("Failed to create test database: %v", err) } defer db.Close() b.ResetTimer() b.Run("Sequential", func(b *testing.B) { for i := 0; i < b.N; i++ { achievement := New(db) achievement.AchievementID = uint32(i) achievement.Title = fmt.Sprintf("Achievement %d", i) _ = achievement } }) b.Run("Parallel", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { id := uint32(0) for pb.Next() { achievement := New(db) achievement.AchievementID = id achievement.Title = fmt.Sprintf("Achievement %d", id) id++ _ = achievement } }) }) } // BenchmarkAchievementOperations measures individual achievement operations func BenchmarkAchievementOperations(b *testing.B) { achievement := createTestAchievement(b, 1001) b.Run("GetID", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = achievement.GetID() } }) }) b.Run("IsNew", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = achievement.IsNew() } }) }) b.Run("Clone", func(b *testing.B) { for b.Loop() { _ = achievement.Clone() } }) } // BenchmarkMasterListOperations measures master list performance func BenchmarkMasterListOperations(b *testing.B) { setupSharedAchievementMasterList(b) ml := sharedAchievementMasterList b.Run("GetAchievement", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { id := uint32(rand.Intn(1000) + 1) _ = ml.GetAchievement(id) } }) }) b.Run("AddAchievement", func(b *testing.B) { // Create a separate master list for add operations addML := NewMasterList() startID := uint32(10000) // Pre-create achievements to measure just the Add operation achievementsToAdd := make([]*Achievement, b.N) for i := 0; i < b.N; i++ { achievementsToAdd[i] = createTestAchievement(b, startID+uint32(i)) } b.ResetTimer() for i := 0; i < b.N; i++ { addML.AddAchievement(achievementsToAdd[i]) } }) b.Run("GetAchievementsByCategory", func(b *testing.B) { categories := []string{"Combat", "Crafting", "Exploration", "Social", "PvP", "Quests", "Collections", "Dungeons"} b.RunParallel(func(pb *testing.PB) { for pb.Next() { category := categories[rand.Intn(len(categories))] _ = ml.GetAchievementsByCategory(category) } }) }) b.Run("GetAchievementsByExpansion", func(b *testing.B) { expansions := []string{"Classic", "Kingdom of Sky", "Echoes of Faydwer", "Rise of Kunark", "The Shadow Odyssey", "Sentinel's Fate"} b.RunParallel(func(pb *testing.PB) { for pb.Next() { expansion := expansions[rand.Intn(len(expansions))] _ = ml.GetAchievementsByExpansion(expansion) } }) }) b.Run("GetAchievementsByCategoryAndExpansion", func(b *testing.B) { categories := []string{"Combat", "Crafting", "Exploration", "Social"} expansions := []string{"Classic", "Kingdom of Sky", "Echoes of Faydwer"} for b.Loop() { category := categories[rand.Intn(len(categories))] expansion := expansions[rand.Intn(len(expansions))] _ = ml.GetAchievementsByCategoryAndExpansion(category, expansion) } }) b.Run("GetCategories", func(b *testing.B) { for b.Loop() { _ = ml.GetCategories() } }) b.Run("GetExpansions", func(b *testing.B) { for b.Loop() { _ = ml.GetExpansions() } }) b.Run("Size", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = ml.Size() } }) }) } // BenchmarkConcurrentOperations tests mixed workload performance func BenchmarkConcurrentOperations(b *testing.B) { setupSharedAchievementMasterList(b) ml := sharedAchievementMasterList b.Run("MixedOperations", func(b *testing.B) { categories := []string{"Combat", "Crafting", "Exploration", "Social", "PvP", "Quests", "Collections", "Dungeons"} expansions := []string{"Classic", "Kingdom of Sky", "Echoes of Faydwer", "Rise of Kunark", "The Shadow Odyssey", "Sentinel's Fate"} b.RunParallel(func(pb *testing.PB) { for pb.Next() { switch rand.Intn(7) { case 0: id := uint32(rand.Intn(1000) + 1) _ = ml.GetAchievement(id) case 1: category := categories[rand.Intn(len(categories))] _ = ml.GetAchievementsByCategory(category) case 2: expansion := expansions[rand.Intn(len(expansions))] _ = ml.GetAchievementsByExpansion(expansion) case 3: category := categories[rand.Intn(len(categories))] expansion := expansions[rand.Intn(len(expansions))] _ = ml.GetAchievementsByCategoryAndExpansion(category, expansion) case 4: _ = ml.GetCategories() case 5: _ = ml.GetExpansions() case 6: _ = ml.Size() } } }) }) } // BenchmarkMemoryAllocation measures memory allocation patterns func BenchmarkMemoryAllocation(b *testing.B) { db, err := database.NewSQLite("file::memory:?mode=memory&cache=shared") if err != nil { b.Fatalf("Failed to create test database: %v", err) } defer db.Close() b.Run("AchievementAllocation", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { achievement := New(db) achievement.AchievementID = uint32(i) achievement.Requirements = make([]Requirement, 2) achievement.Rewards = make([]Reward, 3) _ = achievement } }) b.Run("MasterListAllocation", func(b *testing.B) { b.ReportAllocs() for b.Loop() { ml := NewMasterList() _ = ml } }) b.Run("AddAchievement_Allocations", func(b *testing.B) { b.ReportAllocs() ml := NewMasterList() for i := 0; i < b.N; i++ { achievement := createTestAchievement(b, uint32(i+1)) ml.AddAchievement(achievement) } }) b.Run("GetAchievementsByCategory_Allocations", func(b *testing.B) { setupSharedAchievementMasterList(b) ml := sharedAchievementMasterList b.ReportAllocs() b.ResetTimer() for b.Loop() { _ = ml.GetAchievementsByCategory("Combat") } }) b.Run("GetCategories_Allocations", func(b *testing.B) { setupSharedAchievementMasterList(b) ml := sharedAchievementMasterList b.ReportAllocs() b.ResetTimer() for b.Loop() { _ = ml.GetCategories() } }) } // BenchmarkUpdateOperations measures update performance func BenchmarkUpdateOperations(b *testing.B) { setupSharedAchievementMasterList(b) ml := sharedAchievementMasterList b.Run("UpdateAchievement", func(b *testing.B) { // Create achievements to update updateAchievements := make([]*Achievement, b.N) for i := 0; i < b.N; i++ { updateAchievements[i] = createTestAchievement(b, uint32((i%1000)+1)) updateAchievements[i].Title = "Updated Title" updateAchievements[i].Category = "Updated" } b.ResetTimer() for i := 0; i < b.N; i++ { _ = ml.UpdateAchievement(updateAchievements[i]) } }) b.Run("RemoveAchievement", func(b *testing.B) { // Create a separate master list for removal testing removeML := NewMasterList() // Add achievements to remove for i := 0; i < b.N; i++ { achievement := createTestAchievement(b, uint32(i+1)) removeML.AddAchievement(achievement) } b.ResetTimer() for i := 0; i < b.N; i++ { removeML.RemoveAchievement(uint32(i + 1)) } }) } // BenchmarkCloneOperations measures cloning performance func BenchmarkCloneOperations(b *testing.B) { setupSharedAchievementMasterList(b) ml := sharedAchievementMasterList b.Run("GetAchievementClone", func(b *testing.B) { for b.Loop() { id := uint32(rand.Intn(1000) + 1) _ = ml.GetAchievementClone(id) } }) b.Run("DirectClone", func(b *testing.B) { achievement := createTestAchievement(b, 1001) for b.Loop() { _ = achievement.Clone() } }) }