364 lines
10 KiB
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()
|
|
}
|
|
})
|
|
} |