#include "../src/dc_spsc_queue.h"
#include <cassert>
#include <cstdio>
#include <string>
#include <thread>
#include <vector>
// ---- Test 1: Single-threaded push/pop ----
static void test_basic_push_pop() {
SPSCQueue<int, 16> q;
assert(q.empty());
assert(q.try_push(42));
assert(!q.empty());
int val = 0;
assert(q.try_pop(val));
assert(val == 42);
assert(q.empty());
// Pop from empty should fail
assert(!q.try_pop(val));
// Push several, pop in order
for (int i = 0; i < 10; i++) {
assert(q.try_push(i));
}
for (int i = 0; i < 10; i++) {
assert(q.try_pop(val));
assert(val == i);
}
assert(q.empty());
printf("PASS: basic push/pop\n");
}
// ---- Test 2: Fill to capacity ----
static void test_fill_capacity() {
// N=8, usable capacity = 7
SPSCQueue<int, 8> q;
for (int i = 0; i < 7; i++) {
assert(q.try_push(i));
}
// 8th push should fail (full at N-1)
assert(!q.try_push(999));
// Pop one, then push should succeed again
int val = 0;
assert(q.try_pop(val));
assert(val == 0);
assert(q.try_push(999));
// Should be full again
assert(!q.try_push(1000));
// Drain and verify order
for (int i = 1; i <= 7; i++) {
assert(q.try_pop(val));
int expected = (i < 7) ? i : 999;
assert(val == expected);
}
assert(q.empty());
printf("PASS: fill to capacity (N-1 usable slots)\n");
}
// ---- Test 3: Multi-threaded producer/consumer ----
static void test_multithreaded() {
static constexpr int COUNT = 10000;
SPSCQueue<int, 1024> q;
std::thread producer([&]() {
for (int i = 0; i < COUNT; i++) {
while (!q.try_push(i)) {
// spin
}
}
});
std::vector<int> received;
received.reserve(COUNT);
std::thread consumer([&]() {
int val;
for (int i = 0; i < COUNT; i++) {
while (!q.try_pop(val)) {
// spin
}
received.push_back(val);
}
});
producer.join();
consumer.join();
assert((int)received.size() == COUNT);
for (int i = 0; i < COUNT; i++) {
assert(received[i] == i);
}
printf("PASS: multi-threaded producer/consumer (%d items, ordering verified)\n", COUNT);
}
// ---- Test 4: Struct payload ----
struct Payload {
int id;
float x, y, z;
std::string name;
Payload() : id(0), x(0), y(0), z(0) {}
Payload(int id_, float x_, float y_, float z_, const char *name_)
: id(id_), x(x_), y(y_), z(z_), name(name_) {}
};
static void test_struct_payload() {
SPSCQueue<Payload, 32> q;
assert(q.try_push(Payload(1, 1.0f, 2.0f, 3.0f, "alpha")));
assert(q.try_push(Payload(2, 4.0f, 5.0f, 6.0f, "beta")));
Payload p;
assert(q.try_pop(p));
assert(p.id == 1);
assert(p.x == 1.0f && p.y == 2.0f && p.z == 3.0f);
assert(p.name == "alpha");
assert(q.try_pop(p));
assert(p.id == 2);
assert(p.name == "beta");
assert(q.empty());
// Test move semantics with string
Payload big(3, 0, 0, 0, "a rather long string to avoid SSO");
assert(q.try_push(std::move(big)));
assert(q.try_pop(p));
assert(p.id == 3);
assert(p.name == "a rather long string to avoid SSO");
printf("PASS: struct payload (non-trivial type with move)\n");
}
// ---- Test 5: Wrap-around ----
static void test_wraparound() {
// Small queue (N=4, usable=3) to force frequent wrapping
SPSCQueue<int, 4> q;
int val;
// Push/pop 20 items through a 3-slot queue => many wrap-arounds
for (int i = 0; i < 20; i++) {
assert(q.try_push(i));
assert(q.try_pop(val));
assert(val == i);
}
// Batch: fill, drain, repeat across boundary
for (int round = 0; round < 10; round++) {
for (int i = 0; i < 3; i++) {
assert(q.try_push(round * 3 + i));
}
assert(!q.try_push(-1)); // full
for (int i = 0; i < 3; i++) {
assert(q.try_pop(val));
assert(val == round * 3 + i);
}
assert(q.empty());
}
printf("PASS: wrap-around (push/pop past capacity boundary)\n");
}
int main() {
test_basic_push_pop();
test_fill_capacity();
test_multithreaded();
test_struct_payload();
test_wraparound();
printf("\nAll SPSC queue tests passed.\n");
return 0;
}