package ringbuffer import ( "sync" "testing" ) func TestRingBufferBasic(t *testing.T) { rb := New[int](16) // Test Push and Pop if !rb.Push(42) { t.Fatal("Push failed") } val, ok := rb.Pop() if !ok { t.Fatal("Pop failed") } if val != 42 { t.Errorf("Expected 42, got %d", val) } } func TestRingBufferFillAndEmpty(t *testing.T) { rb := New[int](4) // Fill the buffer for i := 0; i < 4; i++ { if !rb.Push(i) { t.Fatalf("Push failed at index %d", i) } } // Buffer should be full if !rb.IsFull() { t.Error("Buffer should be full") } // Push should fail if rb.Push(99) { t.Error("Push should fail on full buffer") } // Empty the buffer for i := 0; i < 4; i++ { val, ok := rb.Pop() if !ok { t.Fatalf("Pop failed at index %d", i) } if val != i { t.Errorf("Expected %d, got %d", i, val) } } // Buffer should be empty if !rb.IsEmpty() { t.Error("Buffer should be empty") } } func TestRingBufferLen(t *testing.T) { rb := New[int](8) if rb.Len() != 0 { t.Errorf("New buffer should have len 0, got %d", rb.Len()) } rb.Push(1) rb.Push(2) if rb.Len() != 2 { t.Errorf("Expected len 2, got %d", rb.Len()) } rb.Pop() if rb.Len() != 1 { t.Errorf("Expected len 1 after pop, got %d", rb.Len()) } } func TestRingBufferReset(t *testing.T) { rb := New[int](8) for i := 0; i < 5; i++ { rb.Push(i) } rb.Reset() if !rb.IsEmpty() { t.Error("Buffer should be empty after reset") } // Should be able to push again if !rb.Push(100) { t.Error("Push should work after reset") } } func TestRingBufferString(t *testing.T) { rb := New[string](4) rb.Push("hello") rb.Push("world") val, ok := rb.Pop() if !ok || val != "hello" { t.Errorf("Expected 'hello', got '%s'", val) } val, ok = rb.Pop() if !ok || val != "world" { t.Errorf("Expected 'world', got '%s'", val) } } func TestMPMCQueueBasic(t *testing.T) { q := NewMPMC[int](16) if !q.Enqueue(42) { t.Fatal("Enqueue failed") } val, ok := q.Dequeue() if !ok { t.Fatal("Dequeue failed") } if val != 42 { t.Errorf("Expected 42, got %d", val) } } func TestMPMCQueueConcurrent(t *testing.T) { q := NewMPMC[int](1024) const numProducers = 4 const numConsumers = 4 const itemsPerProducer = 1000 var wg sync.WaitGroup // Start consumers results := make(chan int, numProducers*itemsPerProducer) for i := 0; i < numConsumers; i++ { wg.Add(1) go func() { defer wg.Done() for { val, ok := q.Dequeue() if !ok { // Check if producers are done if q.Len() == 0 { return } continue } results <- val } }() } // Start producers for i := 0; i < numProducers; i++ { wg.Add(1) go func(id int) { defer wg.Done() for j := 0; j < itemsPerProducer; j++ { item := id*itemsPerProducer + j for !q.Enqueue(item) { // Retry if full } } }(i) } // Wait for producers wg.Wait() // Give consumers time to finish close(results) // Count results count := 0 for range results { count++ } expected := numProducers * itemsPerProducer if count != expected { t.Errorf("Expected %d items, got %d", expected, count) } } func TestRoundUpPowerOf2(t *testing.T) { tests := []struct { input int expected int }{ {0, 1}, {1, 1}, {2, 2}, {3, 4}, {4, 4}, {5, 8}, {7, 8}, {8, 8}, {9, 16}, {100, 128}, {1023, 1024}, {1024, 1024}, } for _, tt := range tests { result := roundUpPowerOf2(tt.input) if result != tt.expected { t.Errorf("roundUpPowerOf2(%d) = %d, expected %d", tt.input, result, tt.expected) } } } func BenchmarkRingBufferPushPop(b *testing.B) { rb := New[int](1024) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { rb.Push(i) rb.Pop() } } func BenchmarkMPMCQueueEnqueueDequeue(b *testing.B) { q := NewMPMC[int](1024) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { q.Enqueue(i) q.Dequeue() } } func BenchmarkRingBufferConcurrent(b *testing.B) { rb := New[int](1024) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { i := 0 for pb.Next() { if i%2 == 0 { rb.Push(i) } else { rb.Pop() } i++ } }) }