305 lines
6.8 KiB
Go
305 lines
6.8 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
)
|
|
|
|
// TestItem implements Identifiable for testing
|
|
type TestItem struct {
|
|
ID int32 `json:"id"`
|
|
Name string `json:"name"`
|
|
Category string `json:"category"`
|
|
}
|
|
|
|
func (t *TestItem) GetID() int32 {
|
|
return t.ID
|
|
}
|
|
|
|
// TestMasterList tests the basic functionality of the generic master list
|
|
func TestMasterList(t *testing.T) {
|
|
ml := NewMasterList[int32, *TestItem]()
|
|
|
|
// Test initial state
|
|
if !ml.IsEmpty() {
|
|
t.Error("New master list should be empty")
|
|
}
|
|
|
|
if ml.Size() != 0 {
|
|
t.Error("New master list should have size 0")
|
|
}
|
|
|
|
// Test adding items
|
|
item1 := &TestItem{ID: 1, Name: "Item One", Category: "A"}
|
|
item2 := &TestItem{ID: 2, Name: "Item Two", Category: "B"}
|
|
item3 := &TestItem{ID: 3, Name: "Item Three", Category: "A"}
|
|
|
|
if !ml.Add(item1) {
|
|
t.Error("Should successfully add item1")
|
|
}
|
|
|
|
if !ml.Add(item2) {
|
|
t.Error("Should successfully add item2")
|
|
}
|
|
|
|
if !ml.Add(item3) {
|
|
t.Error("Should successfully add item3")
|
|
}
|
|
|
|
// Test duplicate addition
|
|
if ml.Add(item1) {
|
|
t.Error("Should not add duplicate item")
|
|
}
|
|
|
|
// Test size
|
|
if ml.Size() != 3 {
|
|
t.Errorf("Expected size 3, got %d", ml.Size())
|
|
}
|
|
|
|
if ml.IsEmpty() {
|
|
t.Error("List should not be empty")
|
|
}
|
|
|
|
// Test retrieval
|
|
retrieved := ml.Get(1)
|
|
if retrieved == nil || retrieved.Name != "Item One" {
|
|
t.Error("Failed to retrieve item1")
|
|
}
|
|
|
|
// Test safe retrieval
|
|
retrievedSafe, exists := ml.GetSafe(1)
|
|
if !exists || retrievedSafe.Name != "Item One" {
|
|
t.Error("Failed to safely retrieve item1")
|
|
}
|
|
|
|
_, exists = ml.GetSafe(999)
|
|
if exists {
|
|
t.Error("Should not find non-existent item")
|
|
}
|
|
|
|
// Test existence
|
|
if !ml.Exists(1) {
|
|
t.Error("Item 1 should exist")
|
|
}
|
|
|
|
if ml.Exists(999) {
|
|
t.Error("Item 999 should not exist")
|
|
}
|
|
|
|
// Test update
|
|
updatedItem := &TestItem{ID: 1, Name: "Updated Item One", Category: "A"}
|
|
if err := ml.Update(updatedItem); err != nil {
|
|
t.Errorf("Should successfully update item: %v", err)
|
|
}
|
|
|
|
retrieved = ml.Get(1)
|
|
if retrieved.Name != "Updated Item One" {
|
|
t.Error("Item was not updated correctly")
|
|
}
|
|
|
|
// Test update non-existent item
|
|
nonExistent := &TestItem{ID: 999, Name: "Non Existent", Category: "Z"}
|
|
if err := ml.Update(nonExistent); err == nil {
|
|
t.Error("Should fail to update non-existent item")
|
|
}
|
|
|
|
// Test AddOrUpdate
|
|
newItem := &TestItem{ID: 4, Name: "Item Four", Category: "C"}
|
|
if !ml.AddOrUpdate(newItem) {
|
|
t.Error("Should successfully add new item with AddOrUpdate")
|
|
}
|
|
|
|
updateExisting := &TestItem{ID: 1, Name: "Double Updated Item One", Category: "A"}
|
|
if !ml.AddOrUpdate(updateExisting) {
|
|
t.Error("Should successfully update existing item with AddOrUpdate")
|
|
}
|
|
|
|
retrieved = ml.Get(1)
|
|
if retrieved.Name != "Double Updated Item One" {
|
|
t.Error("Item was not updated correctly with AddOrUpdate")
|
|
}
|
|
|
|
if ml.Size() != 4 {
|
|
t.Errorf("Expected size 4 after AddOrUpdate, got %d", ml.Size())
|
|
}
|
|
|
|
// Test removal
|
|
if !ml.Remove(2) {
|
|
t.Error("Should successfully remove item2")
|
|
}
|
|
|
|
if ml.Remove(2) {
|
|
t.Error("Should not remove already removed item")
|
|
}
|
|
|
|
if ml.Size() != 3 {
|
|
t.Errorf("Expected size 3 after removal, got %d", ml.Size())
|
|
}
|
|
|
|
// Test GetAll
|
|
all := ml.GetAll()
|
|
if len(all) != 3 {
|
|
t.Errorf("Expected 3 items in GetAll, got %d", len(all))
|
|
}
|
|
|
|
// Verify we can modify the returned map without affecting the original
|
|
all[999] = &TestItem{ID: 999, Name: "Should not affect original", Category: "Z"}
|
|
if ml.Exists(999) {
|
|
t.Error("Modifying returned map should not affect original list")
|
|
}
|
|
|
|
// Test GetAllSlice
|
|
slice := ml.GetAllSlice()
|
|
if len(slice) != 3 {
|
|
t.Errorf("Expected 3 items in GetAllSlice, got %d", len(slice))
|
|
}
|
|
|
|
// Test GetAllIDs
|
|
ids := ml.GetAllIDs()
|
|
if len(ids) != 3 {
|
|
t.Errorf("Expected 3 IDs in GetAllIDs, got %d", len(ids))
|
|
}
|
|
|
|
// Test Clear
|
|
ml.Clear()
|
|
if !ml.IsEmpty() {
|
|
t.Error("List should be empty after Clear")
|
|
}
|
|
|
|
if ml.Size() != 0 {
|
|
t.Error("List should have size 0 after Clear")
|
|
}
|
|
}
|
|
|
|
// TestMasterListSearch tests search functionality
|
|
func TestMasterListSearch(t *testing.T) {
|
|
ml := NewMasterList[int32, *TestItem]()
|
|
|
|
// Add test items
|
|
items := []*TestItem{
|
|
{ID: 1, Name: "Alpha", Category: "A"},
|
|
{ID: 2, Name: "Beta", Category: "B"},
|
|
{ID: 3, Name: "Gamma", Category: "A"},
|
|
{ID: 4, Name: "Delta", Category: "C"},
|
|
{ID: 5, Name: "Alpha Two", Category: "A"},
|
|
}
|
|
|
|
for _, item := range items {
|
|
ml.Add(item)
|
|
}
|
|
|
|
// Test Filter
|
|
categoryA := ml.Filter(func(item *TestItem) bool {
|
|
return item.Category == "A"
|
|
})
|
|
|
|
if len(categoryA) != 3 {
|
|
t.Errorf("Expected 3 items in category A, got %d", len(categoryA))
|
|
}
|
|
|
|
// Test Find
|
|
found, exists := ml.Find(func(item *TestItem) bool {
|
|
return item.Name == "Beta"
|
|
})
|
|
|
|
if !exists || found.ID != 2 {
|
|
t.Error("Should find Beta with ID 2")
|
|
}
|
|
|
|
notFound, exists := ml.Find(func(item *TestItem) bool {
|
|
return item.Name == "Nonexistent"
|
|
})
|
|
|
|
if exists || notFound != nil {
|
|
t.Error("Should not find nonexistent item")
|
|
}
|
|
|
|
// Test Count
|
|
count := ml.Count(func(item *TestItem) bool {
|
|
return item.Category == "A"
|
|
})
|
|
|
|
if count != 3 {
|
|
t.Errorf("Expected count of 3 for category A, got %d", count)
|
|
}
|
|
|
|
// Test ForEach
|
|
var visitedIDs []int32
|
|
ml.ForEach(func(id int32, item *TestItem) {
|
|
visitedIDs = append(visitedIDs, id)
|
|
})
|
|
|
|
if len(visitedIDs) != 5 {
|
|
t.Errorf("Expected to visit 5 items, visited %d", len(visitedIDs))
|
|
}
|
|
}
|
|
|
|
// TestMasterListConcurrency tests thread safety (basic test)
|
|
func TestMasterListConcurrency(t *testing.T) {
|
|
ml := NewMasterList[int32, *TestItem]()
|
|
|
|
// Test WithReadLock
|
|
ml.Add(&TestItem{ID: 1, Name: "Test", Category: "A"})
|
|
|
|
var foundItem *TestItem
|
|
ml.WithReadLock(func(items map[int32]*TestItem) {
|
|
foundItem = items[1]
|
|
})
|
|
|
|
if foundItem == nil || foundItem.Name != "Test" {
|
|
t.Error("WithReadLock should provide access to internal map")
|
|
}
|
|
|
|
// Test WithWriteLock
|
|
ml.WithWriteLock(func(items map[int32]*TestItem) {
|
|
items[2] = &TestItem{ID: 2, Name: "Added via WriteLock", Category: "B"}
|
|
})
|
|
|
|
if !ml.Exists(2) {
|
|
t.Error("Item added via WithWriteLock should exist")
|
|
}
|
|
|
|
retrieved := ml.Get(2)
|
|
if retrieved.Name != "Added via WriteLock" {
|
|
t.Error("Item added via WithWriteLock not found correctly")
|
|
}
|
|
}
|
|
|
|
|
|
// BenchmarkMasterList tests performance of basic operations
|
|
func BenchmarkMasterList(b *testing.B) {
|
|
ml := NewMasterList[int32, *TestItem]()
|
|
|
|
// Pre-populate for benchmarks
|
|
for i := int32(0); i < 1000; i++ {
|
|
ml.Add(&TestItem{
|
|
ID: i,
|
|
Name: fmt.Sprintf("Item %d", i),
|
|
Category: fmt.Sprintf("Category %d", i%10),
|
|
})
|
|
}
|
|
|
|
b.Run("Get", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
ml.Get(int32(i % 1000))
|
|
}
|
|
})
|
|
|
|
b.Run("Add", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
ml.AddOrUpdate(&TestItem{
|
|
ID: int32(1000 + i),
|
|
Name: fmt.Sprintf("Bench Item %d", i),
|
|
Category: "Bench",
|
|
})
|
|
}
|
|
})
|
|
|
|
b.Run("Filter", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
ml.Filter(func(item *TestItem) bool {
|
|
return item.Category == "Category 5"
|
|
})
|
|
}
|
|
})
|
|
} |