| package ringbuffer |
|
|
| import ( |
| "sync" |
| "testing" |
| ) |
|
|
| func TestRingBufferBasic(t *testing.T) { |
| rb := New[int](16) |
|
|
| |
| 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) |
|
|
| |
| for i := 0; i < 4; i++ { |
| if !rb.Push(i) { |
| t.Fatalf("Push failed at index %d", i) |
| } |
| } |
|
|
| |
| if !rb.IsFull() { |
| t.Error("Buffer should be full") |
| } |
|
|
| |
| if rb.Push(99) { |
| t.Error("Push should fail on full 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) |
| } |
| } |
|
|
| |
| 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") |
| } |
|
|
| |
| 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 |
|
|
| |
| 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 { |
| |
| if q.Len() == 0 { |
| return |
| } |
| continue |
| } |
| results <- val |
| } |
| }() |
| } |
|
|
| |
| 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) { |
| |
| } |
| } |
| }(i) |
| } |
|
|
| |
| wg.Wait() |
|
|
| |
| close(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++ |
| } |
| }) |
| } |
|
|