mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2026-06-03 10:02:21 +08:00
342 lines
7.2 KiB
Go
342 lines
7.2 KiB
Go
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 TestMetrics(t *testing.T) {
|
|
pm := NewMetrics()
|
|
|
|
// 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")
|
|
}
|
|
}
|