eq2go/internal/achievements/benchmark_test.go

364 lines
10 KiB
Go

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()
}
})
}