eq2go/internal/chat/benchmark_test.go
2025-08-08 11:00:00 -05:00

371 lines
8.3 KiB
Go

package chat
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 world channels
worldChannels := []string{
"Auction", "Trade", "General", "OOC", "LFG", "Crafting",
"Roleplay", "Newbie", "Antonica", "Commonlands",
"Freeport", "Qeynos", "Kelethin", "Neriak",
}
for i, name := range worldChannels {
ch := NewWithData(int32(i+1), name, ChannelTypeWorld, db)
if i%3 == 0 {
ch.SetLevelRestriction(10) // Some have level restrictions
}
if i%4 == 0 {
ch.SetRacesAllowed(1 << 1) // Some have race restrictions
}
masterList.AddChannel(ch)
// Add some members to channels
if i%2 == 0 {
ch.JoinChannel(int32(1000 + i))
}
if i%3 == 0 {
ch.JoinChannel(int32(2000 + i))
}
}
// Add custom channels
for i := 0; i < 50; i++ {
ch := NewWithData(int32(100+i), fmt.Sprintf("CustomChannel%d", i), ChannelTypeCustom, db)
if i%5 == 0 {
ch.SetLevelRestriction(20)
}
masterList.AddChannel(ch)
// Add members to some custom channels
if i%4 == 0 {
ch.JoinChannel(int32(3000 + i))
}
}
return masterList
}
func BenchmarkMasterList_AddChannel(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++ {
ch := NewWithData(int32(i+10000), fmt.Sprintf("Channel%d", i), ChannelTypeWorld, db)
masterList.AddChannel(ch)
}
}
func BenchmarkMasterList_GetChannel(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetChannel(int32(i%64 + 1))
}
}
func BenchmarkMasterList_GetChannelSafe(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetChannelSafe(int32(i%64 + 1))
}
}
func BenchmarkMasterList_HasChannel(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.HasChannel(int32(i%64 + 1))
}
}
func BenchmarkMasterList_FindChannelsByType(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i%2 == 0 {
masterList.FindChannelsByType(ChannelTypeWorld)
} else {
masterList.FindChannelsByType(ChannelTypeCustom)
}
}
}
func BenchmarkMasterList_GetWorldChannels(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetWorldChannels()
}
}
func BenchmarkMasterList_GetCustomChannels(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetCustomChannels()
}
}
func BenchmarkMasterList_GetChannelByName(b *testing.B) {
masterList := benchmarkSetup()
names := []string{"auction", "trade", "general", "ooc", "customchannel5", "customchannel15"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetChannelByName(names[i%len(names)])
}
}
func BenchmarkMasterList_FindChannelsByName(b *testing.B) {
masterList := benchmarkSetup()
searchTerms := []string{"Auction", "Custom", "Channel", "Trade", "General"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.FindChannelsByName(searchTerms[i%len(searchTerms)])
}
}
func BenchmarkMasterList_GetActiveChannels(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetActiveChannels()
}
}
func BenchmarkMasterList_GetEmptyChannels(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetEmptyChannels()
}
}
func BenchmarkMasterList_GetCompatibleChannels(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
level := int32(i%50 + 1)
race := int32(i%10 + 1)
class := int32(i%20 + 1)
masterList.GetCompatibleChannels(level, race, class)
}
}
func BenchmarkMasterList_GetChannelsByMemberCount(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
memberCount := i % 5 // 0-4 members
masterList.GetChannelsByMemberCount(memberCount)
}
}
func BenchmarkMasterList_GetChannelsByLevelRestriction(b *testing.B) {
masterList := benchmarkSetup()
levels := []int32{0, 10, 20, 30, 50}
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetChannelsByLevelRestriction(levels[i%len(levels)])
}
}
func BenchmarkMasterList_GetAllChannels(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetAllChannels()
}
}
func BenchmarkMasterList_GetAllChannelsList(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetAllChannelsList()
}
}
func BenchmarkMasterList_GetStatistics(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.GetStatistics()
}
}
func BenchmarkMasterList_ValidateChannels(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
masterList.ValidateChannels()
}
}
func BenchmarkMasterList_RemoveChannel(b *testing.B) {
b.StopTimer()
masterList := benchmarkSetup()
initialCount := masterList.GetChannelCount()
// Pre-populate with channels we'll remove
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
for i := 0; i < b.N; i++ {
ch := NewWithData(int32(20000+i), fmt.Sprintf("ToRemove%d", i), ChannelTypeCustom, db)
masterList.AddChannel(ch)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
masterList.RemoveChannel(int32(20000 + i))
}
b.StopTimer()
if masterList.GetChannelCount() != initialCount {
b.Errorf("Expected %d channels after removal, got %d", initialCount, masterList.GetChannelCount())
}
}
func BenchmarkMasterList_ForEach(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
for i := 0; i < b.N; i++ {
count := 0
masterList.ForEach(func(id int32, channel *Channel) {
count++
})
}
}
func BenchmarkMasterList_UpdateChannel(b *testing.B) {
masterList := benchmarkSetup()
db, _ := database.NewSQLite("file::memory:?mode=memory&cache=shared")
b.ResetTimer()
for i := 0; i < b.N; i++ {
channelID := int32(i%64 + 1)
updatedChannel := &Channel{
ID: channelID,
Name: fmt.Sprintf("Updated%d", i),
ChannelType: ChannelTypeCustom,
db: db,
isNew: false,
members: make([]int32, 0),
}
masterList.UpdateChannel(updatedChannel)
}
}
// Memory allocation benchmarks
func BenchmarkMasterList_GetChannel_Allocs(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
masterList.GetChannel(int32(i%64 + 1))
}
}
func BenchmarkMasterList_FindChannelsByType_Allocs(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
masterList.FindChannelsByType(ChannelTypeWorld)
}
}
func BenchmarkMasterList_GetChannelByName_Allocs(b *testing.B) {
masterList := benchmarkSetup()
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
masterList.GetChannelByName("auction")
}
}
// Concurrent benchmark
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 % 5 {
case 0:
masterList.GetChannel(int32(b.N%64 + 1))
case 1:
masterList.FindChannelsByType(ChannelTypeWorld)
case 2:
masterList.GetChannelByName("auction")
case 3:
masterList.GetActiveChannels()
case 4:
masterList.GetCompatibleChannels(25, 1, 1)
}
}
})
}
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
ch := NewWithData(int32(b.N+50000), fmt.Sprintf("Concurrent%d", b.N), ChannelTypeCustom, db)
masterList.AddChannel(ch)
default: // 90% reads
switch b.N % 4 {
case 0:
masterList.GetChannel(int32(b.N%64 + 1))
case 1:
masterList.FindChannelsByType(ChannelTypeWorld)
case 2:
masterList.GetChannelByName("auction")
case 3:
masterList.GetActiveChannels()
}
}
}
})
}