package alt_advancement import ( "fmt" "math/rand" "sync" "testing" "eq2emu/internal/database" ) // Global shared master list for benchmarks to avoid repeated setup var ( sharedAltAdvancementMasterList *MasterList sharedAltAdvancements []*AltAdvancement altAdvancementSetupOnce sync.Once ) // setupSharedAltAdvancementMasterList creates the shared master list once func setupSharedAltAdvancementMasterList(b *testing.B) { altAdvancementSetupOnce.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) } sharedAltAdvancementMasterList = NewMasterList() // Pre-populate with alternate advancements for realistic testing const numAltAdvancements = 1000 sharedAltAdvancements = make([]*AltAdvancement, numAltAdvancements) groups := []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC, AA_TRADESKILL, AA_PRESTIGE} classes := []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 0 = universal, 1-10 = specific classes for i := range numAltAdvancements { sharedAltAdvancements[i] = New(db) sharedAltAdvancements[i].NodeID = int32(i + 1) sharedAltAdvancements[i].SpellID = int32(i + 1) sharedAltAdvancements[i].Name = fmt.Sprintf("Alt Advancement %d", i+1) sharedAltAdvancements[i].Group = groups[i%len(groups)] sharedAltAdvancements[i].ClassReq = classes[i%len(classes)] sharedAltAdvancements[i].MinLevel = int8(rand.Intn(50) + 1) sharedAltAdvancements[i].RankCost = int8(rand.Intn(5) + 1) sharedAltAdvancements[i].MaxRank = int8(rand.Intn(10) + 1) sharedAltAdvancements[i].Col = int8(rand.Intn(11)) sharedAltAdvancements[i].Row = int8(rand.Intn(16)) sharedAltAdvancementMasterList.AddAltAdvancement(sharedAltAdvancements[i]) } }) } // createTestAltAdvancement creates an alternate advancement for benchmarking func createTestAltAdvancement(b *testing.B, id int32) *AltAdvancement { b.Helper() // Use nil database for benchmarking in-memory operations aa := New(nil) aa.NodeID = id aa.SpellID = id aa.Name = fmt.Sprintf("Benchmark AA %d", id) aa.Group = []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC}[id%4] aa.ClassReq = int8((id % 10) + 1) aa.MinLevel = int8((id % 50) + 1) aa.RankCost = int8(rand.Intn(5) + 1) aa.MaxRank = int8(rand.Intn(10) + 1) aa.Col = int8(rand.Intn(11)) aa.Row = int8(rand.Intn(16)) return aa } // BenchmarkAltAdvancementCreation measures alternate advancement creation performance func BenchmarkAltAdvancementCreation(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++ { aa := New(db) aa.NodeID = int32(i) aa.SpellID = int32(i) aa.Name = fmt.Sprintf("AA %d", i) _ = aa } }) b.Run("Parallel", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { id := int32(0) for pb.Next() { aa := New(db) aa.NodeID = id aa.SpellID = id aa.Name = fmt.Sprintf("AA %d", id) id++ _ = aa } }) }) } // BenchmarkAltAdvancementOperations measures individual alternate advancement operations func BenchmarkAltAdvancementOperations(b *testing.B) { aa := createTestAltAdvancement(b, 1001) b.Run("GetID", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = aa.GetID() } }) }) b.Run("IsNew", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = aa.IsNew() } }) }) b.Run("Clone", func(b *testing.B) { for b.Loop() { _ = aa.Clone() } }) b.Run("IsValid", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = aa.IsValid() } }) }) } // BenchmarkMasterListOperations measures master list performance func BenchmarkMasterListOperations(b *testing.B) { setupSharedAltAdvancementMasterList(b) ml := sharedAltAdvancementMasterList b.Run("GetAltAdvancement", func(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { id := int32(rand.Intn(1000) + 1) _ = ml.GetAltAdvancement(id) } }) }) b.Run("AddAltAdvancement", func(b *testing.B) { // Create a separate master list for add operations addML := NewMasterList() startID := int32(10000) // Pre-create AAs to measure just the Add operation aasToAdd := make([]*AltAdvancement, b.N) for i := 0; i < b.N; i++ { aasToAdd[i] = createTestAltAdvancement(b, startID+int32(i)) } b.ResetTimer() for i := 0; i < b.N; i++ { addML.AddAltAdvancement(aasToAdd[i]) } }) b.Run("GetAltAdvancementsByGroup", func(b *testing.B) { groups := []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC, AA_TRADESKILL, AA_PRESTIGE} b.RunParallel(func(pb *testing.PB) { for pb.Next() { group := groups[rand.Intn(len(groups))] _ = ml.GetAltAdvancementsByGroup(group) } }) }) b.Run("GetAltAdvancementsByClass", func(b *testing.B) { classes := []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} b.RunParallel(func(pb *testing.PB) { for pb.Next() { class := classes[rand.Intn(len(classes))] _ = ml.GetAltAdvancementsByClass(class) } }) }) b.Run("GetAltAdvancementsByLevel", func(b *testing.B) { for b.Loop() { level := int8(rand.Intn(50) + 1) _ = ml.GetAltAdvancementsByLevel(level) } }) b.Run("GetAltAdvancementsByGroupAndClass", func(b *testing.B) { groups := []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC} classes := []int8{1, 2, 3, 4, 5} for b.Loop() { group := groups[rand.Intn(len(groups))] class := classes[rand.Intn(len(classes))] _ = ml.GetAltAdvancementsByGroupAndClass(group, class) } }) b.Run("GetGroups", func(b *testing.B) { for b.Loop() { _ = ml.GetGroups() } }) b.Run("GetClasses", func(b *testing.B) { for b.Loop() { _ = ml.GetClasses() } }) 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) { setupSharedAltAdvancementMasterList(b) ml := sharedAltAdvancementMasterList b.Run("MixedOperations", func(b *testing.B) { groups := []int8{AA_CLASS, AA_SUBCLASS, AA_SHADOW, AA_HEROIC, AA_TRADESKILL, AA_PRESTIGE} classes := []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} b.RunParallel(func(pb *testing.PB) { for pb.Next() { switch rand.Intn(8) { case 0: id := int32(rand.Intn(1000) + 1) _ = ml.GetAltAdvancement(id) case 1: group := groups[rand.Intn(len(groups))] _ = ml.GetAltAdvancementsByGroup(group) case 2: class := classes[rand.Intn(len(classes))] _ = ml.GetAltAdvancementsByClass(class) case 3: level := int8(rand.Intn(50) + 1) _ = ml.GetAltAdvancementsByLevel(level) case 4: group := groups[rand.Intn(len(groups))] class := classes[rand.Intn(len(classes))] _ = ml.GetAltAdvancementsByGroupAndClass(group, class) case 5: _ = ml.GetGroups() case 6: _ = ml.GetClasses() case 7: _ = 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("AltAdvancementAllocation", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { aa := New(db) aa.NodeID = int32(i) aa.SpellID = int32(i) aa.Name = fmt.Sprintf("AA %d", i) _ = aa } }) b.Run("MasterListAllocation", func(b *testing.B) { b.ReportAllocs() for b.Loop() { ml := NewMasterList() _ = ml } }) b.Run("AddAltAdvancement_Allocations", func(b *testing.B) { b.ReportAllocs() ml := NewMasterList() for i := 0; i < b.N; i++ { aa := createTestAltAdvancement(b, int32(i+1)) ml.AddAltAdvancement(aa) } }) b.Run("GetAltAdvancementsByGroup_Allocations", func(b *testing.B) { setupSharedAltAdvancementMasterList(b) ml := sharedAltAdvancementMasterList b.ReportAllocs() b.ResetTimer() for b.Loop() { _ = ml.GetAltAdvancementsByGroup(AA_CLASS) } }) b.Run("GetGroups_Allocations", func(b *testing.B) { setupSharedAltAdvancementMasterList(b) ml := sharedAltAdvancementMasterList b.ReportAllocs() b.ResetTimer() for b.Loop() { _ = ml.GetGroups() } }) } // BenchmarkUpdateOperations measures update performance func BenchmarkUpdateOperations(b *testing.B) { setupSharedAltAdvancementMasterList(b) ml := sharedAltAdvancementMasterList b.Run("UpdateAltAdvancement", func(b *testing.B) { // Create AAs to update updateAAs := make([]*AltAdvancement, b.N) for i := 0; i < b.N; i++ { updateAAs[i] = createTestAltAdvancement(b, int32((i%1000)+1)) updateAAs[i].Name = "Updated Name" updateAAs[i].Group = AA_SUBCLASS } b.ResetTimer() for i := 0; i < b.N; i++ { _ = ml.UpdateAltAdvancement(updateAAs[i]) } }) b.Run("RemoveAltAdvancement", func(b *testing.B) { // Create a separate master list for removal testing removeML := NewMasterList() // Add AAs to remove for i := 0; i < b.N; i++ { aa := createTestAltAdvancement(b, int32(i+1)) removeML.AddAltAdvancement(aa) } b.ResetTimer() for i := 0; i < b.N; i++ { removeML.RemoveAltAdvancement(int32(i + 1)) } }) } // BenchmarkValidation measures validation performance func BenchmarkValidation(b *testing.B) { setupSharedAltAdvancementMasterList(b) ml := sharedAltAdvancementMasterList b.Run("ValidateAll", func(b *testing.B) { for b.Loop() { _ = ml.ValidateAll() } }) b.Run("IndividualValidation", func(b *testing.B) { aa := createTestAltAdvancement(b, 1001) b.RunParallel(func(pb *testing.PB) { for pb.Next() { _ = aa.IsValid() } }) }) } // BenchmarkCloneOperations measures cloning performance func BenchmarkCloneOperations(b *testing.B) { setupSharedAltAdvancementMasterList(b) ml := sharedAltAdvancementMasterList b.Run("GetAltAdvancementClone", func(b *testing.B) { for b.Loop() { id := int32(rand.Intn(1000) + 1) _ = ml.GetAltAdvancementClone(id) } }) b.Run("DirectClone", func(b *testing.B) { aa := createTestAltAdvancement(b, 1001) for b.Loop() { _ = aa.Clone() } }) }