package collections import ( "fmt" "testing" "eq2emu/internal/database" ) // Setup creates a master list with test data for benchmarking func benchmarkSetup() *MasterList { db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared") masterList := NewMasterList() // Add collections across different categories and levels categories := []string{ "Heritage", "Treasured", "Legendary", "Fabled", "Mythical", "Handcrafted", "Mastercrafted", "Rare", "Uncommon", "Common", } for i := 0; i < 100; i++ { category := categories[i%len(categories)] level := int8((i % 50) + 1) // Levels 1-50 collection := NewWithData(int32(i+1), fmt.Sprintf("Collection %d", i+1), category, level, db) // Add collection items (some found, some not) numItems := (i % 5) + 1 // 1-5 items per collection for j := 0; j < numItems; j++ { found := ItemNotFound if (i+j)%3 == 0 { // About 1/3 of items are found found = ItemFound } collection.CollectionItems = append(collection.CollectionItems, CollectionItem{ ItemID: int32((i+1)*1000 + j + 1), Index: int8(j), Found: int8(found), }) } // Add rewards if i%4 == 0 { collection.RewardCoin = int64((i + 1) * 100) } if i%5 == 0 { collection.RewardXP = int64((i + 1) * 50) } if i%6 == 0 { collection.RewardItems = append(collection.RewardItems, CollectionRewardItem{ ItemID: int32(i + 10000), Quantity: 1, }) } if i%7 == 0 { collection.SelectableRewardItems = append(collection.SelectableRewardItems, CollectionRewardItem{ ItemID: int32(i + 20000), Quantity: 1, }) } // Some collections are completed if i%10 == 0 { collection.Completed = true } masterList.AddCollection(collection) } return masterList } func BenchmarkMasterList_AddCollection(b *testing.B) { db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared") defer db.Close() masterList := NewMasterList() b.ResetTimer() for i := 0; i < b.N; i++ { collection := NewWithData(int32(i+10000), fmt.Sprintf("Collection%d", i), "Heritage", 20, db) collection.CollectionItems = []CollectionItem{ {ItemID: int32(i + 50000), Index: 0, Found: ItemNotFound}, } masterList.AddCollection(collection) } } func BenchmarkMasterList_GetCollection(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetCollection(int32(i%100 + 1)) } } func BenchmarkMasterList_GetCollectionSafe(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetCollectionSafe(int32(i%100 + 1)) } } func BenchmarkMasterList_HasCollection(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.HasCollection(int32(i%100 + 1)) } } func BenchmarkMasterList_FindCollectionsByCategory(b *testing.B) { masterList := benchmarkSetup() categories := []string{"Heritage", "Treasured", "Legendary", "Fabled", "Mythical"} b.ResetTimer() for i := 0; i < b.N; i++ { masterList.FindCollectionsByCategory(categories[i%len(categories)]) } } func BenchmarkMasterList_GetCollectionsByExactLevel(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { level := int8(i%50 + 1) masterList.GetCollectionsByExactLevel(level) } } func BenchmarkMasterList_FindCollectionsByLevel(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { minLevel := int8(i%45 + 1) maxLevel := minLevel + 5 masterList.FindCollectionsByLevel(minLevel, maxLevel) } } func BenchmarkMasterList_GetCollectionByName(b *testing.B) { masterList := benchmarkSetup() names := []string{"collection 1", "collection 25", "collection 50", "collection 75", "collection 100"} b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetCollectionByName(names[i%len(names)]) } } func BenchmarkMasterList_NeedsItem(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { itemID := int32(i%100*1000 + 1001) // Various item IDs from the collections masterList.NeedsItem(itemID) } } func BenchmarkMasterList_GetCollectionsNeedingItem(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { itemID := int32(i%100*1000 + 1001) // Various item IDs from the collections masterList.GetCollectionsNeedingItem(itemID) } } func BenchmarkMasterList_GetCompletedCollections(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetCompletedCollections() } } func BenchmarkMasterList_GetIncompleteCollections(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetIncompleteCollections() } } func BenchmarkMasterList_GetReadyToTurnInCollections(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetReadyToTurnInCollections() } } func BenchmarkMasterList_GetCategories(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetCategories() } } func BenchmarkMasterList_GetLevels(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetLevels() } } func BenchmarkMasterList_GetItemsNeeded(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetItemsNeeded() } } func BenchmarkMasterList_GetAllCollections(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetAllCollections() } } func BenchmarkMasterList_GetAllCollectionsList(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetAllCollectionsList() } } func BenchmarkMasterList_GetStatistics(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetStatistics() } } func BenchmarkMasterList_ValidateCollections(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.ValidateCollections() } } func BenchmarkMasterList_RemoveCollection(b *testing.B) { b.StopTimer() masterList := benchmarkSetup() initialCount := masterList.GetCollectionCount() // Pre-populate with collections we'll remove db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared") for i := 0; i < b.N; i++ { collection := NewWithData(int32(20000+i), fmt.Sprintf("ToRemove%d", i), "Temporary", 1, db) collection.CollectionItems = []CollectionItem{ {ItemID: int32(60000 + i), Index: 0, Found: ItemNotFound}, } masterList.AddCollection(collection) } b.StartTimer() for i := 0; i < b.N; i++ { masterList.RemoveCollection(int32(20000 + i)) } b.StopTimer() if masterList.GetCollectionCount() != initialCount { b.Errorf("Expected %d collections after removal, got %d", initialCount, masterList.GetCollectionCount()) } } func BenchmarkMasterList_ForEach(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { count := 0 masterList.ForEach(func(id int32, collection *Collection) { count++ }) } } func BenchmarkMasterList_UpdateCollection(b *testing.B) { masterList := benchmarkSetup() db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared") b.ResetTimer() for i := 0; i < b.N; i++ { collectionID := int32(i%100 + 1) updatedCollection := &Collection{ ID: collectionID, Name: fmt.Sprintf("Updated%d", i), Category: "Updated", Level: 25, db: db, isNew: false, CollectionItems: []CollectionItem{ {ItemID: int32(i + 70000), Index: 0, Found: ItemNotFound}, }, } masterList.UpdateCollection(updatedCollection) } } func BenchmarkMasterList_RefreshCollectionIndices(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { collection := masterList.GetCollection(int32(i%100 + 1)) if collection != nil { masterList.RefreshCollectionIndices(collection) } } } func BenchmarkMasterList_GetCollectionClone(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() for i := 0; i < b.N; i++ { masterList.GetCollectionClone(int32(i%100 + 1)) } } // Memory allocation benchmarks func BenchmarkMasterList_GetCollection_Allocs(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { masterList.GetCollection(int32(i%100 + 1)) } } func BenchmarkMasterList_FindCollectionsByCategory_Allocs(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { masterList.FindCollectionsByCategory("Heritage") } } func BenchmarkMasterList_GetCollectionByName_Allocs(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { masterList.GetCollectionByName("collection 1") } } func BenchmarkMasterList_NeedsItem_Allocs(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { masterList.NeedsItem(1001) } } func BenchmarkMasterList_GetCollectionsNeedingItem_Allocs(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { masterList.GetCollectionsNeedingItem(1001) } } // Concurrent benchmarks func BenchmarkMasterList_ConcurrentReads(b *testing.B) { masterList := benchmarkSetup() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { // Mix of read operations switch b.N % 6 { case 0: masterList.GetCollection(int32(b.N%100 + 1)) case 1: masterList.FindCollectionsByCategory("Heritage") case 2: masterList.GetCollectionByName("collection 1") case 3: masterList.NeedsItem(1001) case 4: masterList.GetCompletedCollections() case 5: masterList.GetCollectionsByExactLevel(10) } } }) } func BenchmarkMasterList_ConcurrentMixed(b *testing.B) { masterList := benchmarkSetup() db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared") b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { // Mix of read and write operations (mostly reads) switch b.N % 10 { case 0: // 10% writes collection := NewWithData(int32(b.N+50000), fmt.Sprintf("Concurrent%d", b.N), "Concurrent", 15, db) collection.CollectionItems = []CollectionItem{ {ItemID: int32(b.N + 80000), Index: 0, Found: ItemNotFound}, } masterList.AddCollection(collection) default: // 90% reads switch b.N % 5 { case 0: masterList.GetCollection(int32(b.N%100 + 1)) case 1: masterList.FindCollectionsByCategory("Heritage") case 2: masterList.GetCollectionByName("collection 1") case 3: masterList.NeedsItem(1001) case 4: masterList.GetCompletedCollections() } } } }) }