123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- package utils
- import (
- "fmt"
- "sync"
- "testing"
- "time"
- )
- func TestStringPool(t *testing.T) {
- pool := NewStringPool()
- // Test basic functionality
- buf := pool.Get()
- if len(buf) != 0 {
- t.Errorf("Expected empty buffer, got length %d", len(buf))
- }
- buf = append(buf, []byte("test")...)
- pool.Put(buf)
- // Test string interning
- s1 := "test_string"
- s2 := "test_string"
- interned1 := pool.Intern(s1)
- interned2 := pool.Intern(s2)
- if interned1 != interned2 {
- t.Error("Expected same interned strings")
- }
- // Test size and clear
- if pool.Size() == 0 {
- t.Error("Expected non-zero pool size")
- }
- pool.Clear()
- if pool.Size() != 0 {
- t.Error("Expected zero pool size after clear")
- }
- }
- func TestMemoryPool(t *testing.T) {
- pool := NewMemoryPool()
- // Test getting different sizes
- buf1 := pool.Get(100)
- if cap(buf1) < 100 {
- t.Errorf("Expected capacity >= 100, got %d", cap(buf1))
- }
- buf2 := pool.Get(1000)
- if cap(buf2) < 1000 {
- t.Errorf("Expected capacity >= 1000, got %d", cap(buf2))
- }
- // Test putting back
- pool.Put(buf1)
- pool.Put(buf2)
- // Test very large buffer (should allocate directly)
- largeBuf := pool.Get(100000)
- if cap(largeBuf) < 100000 {
- t.Errorf("Expected capacity >= 100000, got %d", cap(largeBuf))
- }
- }
- func TestWorkerPool(t *testing.T) {
- numWorkers := 3
- queueSize := 10
- pool := NewWorkerPool(numWorkers, queueSize)
- defer pool.Close()
- // Test job submission and execution
- var counter int64
- var mu sync.Mutex
- for i := 0; i < 5; i++ {
- success := pool.Submit(func() {
- mu.Lock()
- counter++
- mu.Unlock()
- })
- if !success {
- t.Error("Failed to submit job")
- }
- }
- // Wait for jobs to complete
- time.Sleep(100 * time.Millisecond)
- mu.Lock()
- if counter != 5 {
- t.Errorf("Expected counter = 5, got %d", counter)
- }
- mu.Unlock()
- }
- func TestBatchProcessor(t *testing.T) {
- capacity := 3
- bp := NewBatchProcessor(capacity)
- // Test adding items
- if !bp.Add("item1") {
- t.Error("Failed to add item1")
- }
- if !bp.Add("item2") {
- t.Error("Failed to add item2")
- }
- if !bp.Add("item3") {
- t.Error("Failed to add item3")
- }
- // Should fail to add more than capacity
- if bp.Add("item4") {
- t.Error("Should have failed to add item4")
- }
- // Test size
- if bp.Size() != 3 {
- t.Errorf("Expected size 3, got %d", bp.Size())
- }
- // Test getting batch
- batch := bp.GetBatch()
- if len(batch) != 3 {
- t.Errorf("Expected batch size 3, got %d", len(batch))
- }
- // Should be empty after getting batch
- if bp.Size() != 0 {
- t.Errorf("Expected size 0 after GetBatch, got %d", bp.Size())
- }
- }
- func TestMemoryOptimizer(t *testing.T) {
- mo := NewMemoryOptimizer(1024 * 1024) // 1MB threshold
- // Test stats retrieval
- stats := mo.GetMemoryStats()
- if stats == nil {
- t.Fatal("Expected non-nil memory stats")
- }
- if stats.AllocMB < 0 {
- t.Error("Expected non-negative allocated memory")
- }
- // Test check memory usage (should not panic)
- mo.CheckMemoryUsage()
- }
- func TestPerformanceMetrics(t *testing.T) {
- pm := NewPerformanceMetrics()
- // Record some operations
- pm.RecordOperation(10, time.Millisecond*100, true)
- pm.RecordOperation(20, time.Millisecond*200, false) // failure
- pm.RecordCacheHit()
- pm.RecordCacheHit()
- pm.RecordCacheMiss()
- pm.RecordAllocation(1024)
- metrics := pm.GetMetrics()
- if metrics["operation_count"] != int64(2) {
- t.Errorf("Expected 2 operations, got %v", metrics["operation_count"])
- }
- if metrics["processed_items"] != int64(30) {
- t.Errorf("Expected 30 processed items, got %v", metrics["processed_items"])
- }
- if metrics["cache_hits"] != int64(2) {
- t.Errorf("Expected 2 cache hits, got %v", metrics["cache_hits"])
- }
- if metrics["cache_misses"] != int64(1) {
- t.Errorf("Expected 1 cache miss, got %v", metrics["cache_misses"])
- }
- cacheHitRate, ok := metrics["cache_hit_rate"].(float64)
- if !ok || cacheHitRate < 0.6 || cacheHitRate > 0.7 {
- t.Errorf("Expected cache hit rate around 0.67, got %v", cacheHitRate)
- }
- // Test reset
- pm.Reset()
- resetMetrics := pm.GetMetrics()
- if resetMetrics["operation_count"] != int64(0) {
- t.Errorf("Expected 0 operations after reset, got %v", resetMetrics["operation_count"])
- }
- }
- func TestUnsafeConversions(t *testing.T) {
- // Test bytes to string conversion
- original := []byte("test string")
- str := BytesToStringUnsafe(original)
- if str != "test string" {
- t.Errorf("Expected 'test string', got '%s'", str)
- }
- // Test string to bytes conversion
- originalStr := "test string"
- bytes := StringToBytesUnsafe(originalStr)
- if string(bytes) != originalStr {
- t.Errorf("Expected '%s', got '%s'", originalStr, string(bytes))
- }
- // Test empty cases
- emptyStr := BytesToStringUnsafe(nil)
- if emptyStr != "" {
- t.Errorf("Expected empty string, got '%s'", emptyStr)
- }
- emptyBytes := StringToBytesUnsafe("")
- if len(emptyBytes) != 0 {
- t.Errorf("Expected empty bytes, got length %d", len(emptyBytes))
- }
- }
- func TestAppendInt(t *testing.T) {
- testCases := []struct {
- input int
- expected string
- }{
- {0, "0"},
- {123, "123"},
- {-456, "-456"},
- {7890, "7890"},
- }
- for _, tc := range testCases {
- buf := make([]byte, 0, 10)
- result := AppendInt(buf, tc.input)
- if string(result) != tc.expected {
- t.Errorf("AppendInt(%d) = '%s', expected '%s'", tc.input, string(result), tc.expected)
- }
- }
- // Test appending to existing buffer
- buf := []byte("prefix:")
- result := AppendInt(buf, 42)
- if string(result) != "prefix:42" {
- t.Errorf("Expected 'prefix:42', got '%s'", string(result))
- }
- }
- func BenchmarkStringPool(b *testing.B) {
- pool := NewStringPool()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- buf := pool.Get()
- buf = append(buf, []byte("benchmark test")...)
- pool.Put(buf)
- }
- }
- func BenchmarkStringIntern(b *testing.B) {
- pool := NewStringPool()
- testStrings := []string{
- "common_string_1",
- "common_string_2",
- "common_string_3",
- "common_string_1", // duplicate
- "common_string_2", // duplicate
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- s := testStrings[i%len(testStrings)]
- pool.Intern(s)
- }
- }
- func BenchmarkMemoryPool(b *testing.B) {
- pool := NewMemoryPool()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- buf := pool.Get(1024)
- pool.Put(buf)
- }
- }
- func BenchmarkUnsafeConversions(b *testing.B) {
- testBytes := []byte("benchmark test string for conversion")
- testString := "benchmark test string for conversion"
- b.Run("BytesToStringUnsafe", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _ = BytesToStringUnsafe(testBytes)
- }
- })
- b.Run("StringToBytesUnsafe", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _ = StringToBytesUnsafe(testString)
- }
- })
- b.Run("StandardConversion", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- _ = string(testBytes)
- }
- })
- }
- func TestStringPoolConcurrency(t *testing.T) {
- pool := NewStringPool()
- const numGoroutines = 10
- const numOperations = 100
- var wg sync.WaitGroup
- wg.Add(numGoroutines)
- for i := 0; i < numGoroutines; i++ {
- go func(id int) {
- defer wg.Done()
- for j := 0; j < numOperations; j++ {
- // Test buffer operations
- buf := pool.Get()
- buf = append(buf, byte(id), byte(j))
- pool.Put(buf)
- // Test string interning
- s := fmt.Sprintf("test_%d_%d", id, j%10) // Limited unique strings
- pool.Intern(s)
- }
- }(i)
- }
- wg.Wait()
- // Pool should have some interned strings
- if pool.Size() == 0 {
- t.Error("Expected some interned strings after concurrent operations")
- }
- }
|