mirror of
https://github.com/steredbits/pelagia.git
synced 2026-10-18 01:02:17 +02:00
150 lines
4.0 KiB
C++
150 lines
4.0 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (C) 2026 steredbits and Pelagia contributors
|
|
// Tests of the bounded queue: limits, serial, abort (no blocking), flush.
|
|
|
|
#include "player/bounded_queue.h"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
#include "test_framework.h"
|
|
|
|
using player::BoundedQueue;
|
|
|
|
static void test_limits() {
|
|
BoundedQueue<int> q(2, 1000);
|
|
CHECK(q.try_push(1, 10, 0));
|
|
CHECK(q.try_push(2, 10, 0));
|
|
CHECK(!q.try_push(3, 10, 0)); // limit on the count
|
|
|
|
BoundedQueue<int> qb(100, 10);
|
|
CHECK(qb.try_push(1, 6, 0));
|
|
CHECK(!qb.try_push(2, 6, 0)); // limit in bytes
|
|
BoundedQueue<int>::Entry e;
|
|
CHECK(qb.pop(&e));
|
|
CHECK_EQ(e.item, 1);
|
|
CHECK_EQ(e.bytes, 6u);
|
|
CHECK(qb.try_push(2, 6, 0));
|
|
|
|
// An entry bigger than the limit passes if the queue is empty
|
|
// (otherwise the pipeline would block forever).
|
|
BoundedQueue<int> qo(4, 10);
|
|
CHECK(qo.try_push(1, 100, 0));
|
|
CHECK(!qo.try_push(2, 1, 0));
|
|
}
|
|
|
|
static void test_serial_passthrough() {
|
|
BoundedQueue<int> q(4, 1000);
|
|
q.try_push(42, 1, 7);
|
|
BoundedQueue<int>::Entry e;
|
|
CHECK(q.pop(&e));
|
|
CHECK_EQ(e.item, 42);
|
|
CHECK_EQ(e.serial, 7);
|
|
}
|
|
|
|
static void test_abort_unblocks_push() {
|
|
BoundedQueue<int> q(1, 1000);
|
|
CHECK(q.try_push(1, 1, 0));
|
|
std::atomic<bool> returned{false};
|
|
std::atomic<bool> push_result{true};
|
|
std::thread t([&] {
|
|
push_result = q.push(2, 1, 0); // file pleine : bloque
|
|
returned = true;
|
|
});
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
CHECK(!returned.load());
|
|
q.abort();
|
|
t.join();
|
|
CHECK(returned.load());
|
|
CHECK(!push_result.load());
|
|
}
|
|
|
|
static void test_abort_unblocks_pop() {
|
|
BoundedQueue<int> q(4, 1000);
|
|
std::atomic<bool> returned{false};
|
|
std::atomic<bool> pop_result{true};
|
|
std::thread t([&] {
|
|
BoundedQueue<int>::Entry e;
|
|
pop_result = q.pop(&e); // file vide : bloque
|
|
returned = true;
|
|
});
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
CHECK(!returned.load());
|
|
q.abort();
|
|
t.join();
|
|
CHECK(returned.load());
|
|
CHECK(!pop_result.load());
|
|
}
|
|
|
|
static void test_flush_releases_and_unblocks() {
|
|
BoundedQueue<int> q(2, 1000);
|
|
q.try_push(1, 1, 0);
|
|
q.try_push(2, 1, 0);
|
|
std::atomic<bool> pushed{false};
|
|
std::thread t([&] {
|
|
q.push(3, 1, 1); // blocks until the flush
|
|
pushed = true;
|
|
});
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
int released = 0;
|
|
q.flush([&](BoundedQueue<int>::Entry*) { ++released; });
|
|
t.join();
|
|
CHECK(pushed.load());
|
|
CHECK_EQ(released, 2);
|
|
CHECK_EQ(q.size(), 1u); // the entry pushed after the flush
|
|
}
|
|
|
|
static void test_stress_producer_consumer() {
|
|
BoundedQueue<int> q(8, 1u << 20);
|
|
const int kCount = 20000;
|
|
long long sum = 0;
|
|
std::thread producer([&] {
|
|
for (int i = 0; i < kCount; ++i) {
|
|
q.push(i, 4, 0);
|
|
}
|
|
q.push(-1, 0, 0); // fin
|
|
});
|
|
std::thread consumer([&] {
|
|
BoundedQueue<int>::Entry e;
|
|
while (q.pop(&e) && e.item != -1) {
|
|
sum += e.item;
|
|
}
|
|
});
|
|
producer.join();
|
|
consumer.join();
|
|
CHECK_EQ(sum, static_cast<long long>(kCount) * (kCount - 1) / 2);
|
|
CHECK_EQ(q.size(), 0u);
|
|
}
|
|
|
|
// Duration totals (reserve) and adjustable limits.
|
|
static void test_duration_and_limits() {
|
|
BoundedQueue<int> q(2, 1000);
|
|
CHECK(q.try_push(1, 1, 0, 40));
|
|
CHECK(q.try_push(2, 1, 0, 21));
|
|
CHECK_EQ(q.duration_ms(), 61);
|
|
CHECK(!q.try_push(3, 1, 0, 40)); // limit on the count
|
|
q.set_limits(10, 1000); // deeper queue (network)
|
|
CHECK(q.try_push(3, 1, 0, 40));
|
|
CHECK_EQ(q.duration_ms(), 101);
|
|
BoundedQueue<int>::Entry e;
|
|
CHECK(q.pop(&e));
|
|
CHECK_EQ(e.duration_ms, 40);
|
|
CHECK_EQ(q.duration_ms(), 61);
|
|
q.flush([](BoundedQueue<int>::Entry*) {});
|
|
CHECK_EQ(q.duration_ms(), 0);
|
|
CHECK(q.try_push(4, 1, 1)); // default duration: 0
|
|
CHECK_EQ(q.duration_ms(), 0);
|
|
}
|
|
|
|
int main() {
|
|
test_limits();
|
|
test_duration_and_limits();
|
|
test_serial_passthrough();
|
|
test_abort_unblocks_push();
|
|
test_abort_unblocks_pop();
|
|
test_flush_releases_and_unblocks();
|
|
test_stress_producer_consumer();
|
|
return testfw::test_failures();
|
|
}
|