Add tests and reformat

This commit is contained in:
Georg Hagen
2025-05-13 20:15:13 +02:00
parent 69ecaf7725
commit 3fd31f92ae
2 changed files with 362 additions and 276 deletions

View File

@@ -15,7 +15,8 @@
using namespace OpenVulkano;
// Helper to track construction/destruction
struct Tracked {
struct Tracked
{
static inline int ctorCount = 0;
static inline int dtorCount = 0;
static inline int moveCount = 0;
@@ -23,36 +24,53 @@ struct Tracked {
int value;
Tracked(int v = 0) : value(v) { ++ctorCount; }
Tracked(const Tracked& other) : value(other.value) { ++ctorCount; ++copyCount; }
Tracked(Tracked&& other) noexcept : value(other.value) { ++ctorCount; ++moveCount; }
Tracked(const Tracked& other) : value(other.value)
{
++ctorCount;
++copyCount;
}
Tracked(Tracked&& other) noexcept: value(other.value)
{
++ctorCount;
++moveCount;
}
~Tracked() { ++dtorCount; }
Tracked& operator=(const Tracked& other) {
Tracked& operator=(const Tracked& other)
{
value = other.value;
++copyCount;
return *this;
}
Tracked& operator=(Tracked&& other) noexcept {
Tracked& operator=(Tracked&& other) noexcept
{
value = other.value;
++moveCount;
return *this;
}
friend bool operator==(const Tracked& lhs, const Tracked& rhs) {
friend bool operator==(const Tracked& lhs, const Tracked& rhs)
{
return lhs.value == rhs.value;
}
};
void reset_tracking() {
void reset_tracking()
{
Tracked::ctorCount = 0;
Tracked::dtorCount = 0;
Tracked::moveCount = 0;
Tracked::copyCount = 0;
}
TEST_CASE("RingBuffer Basic Construction", "[RingBuffer]") {
SECTION("Static buffer construction") {
TEST_CASE("RingBuffer Basic Construction", "[RingBuffer]")
{
SECTION("Static buffer construction")
{
RingBuffer<int, 5> buf;
REQUIRE(buf.Capacity() == 5);
REQUIRE(buf.IsEmpty());
@@ -60,7 +78,8 @@ TEST_CASE("RingBuffer Basic Construction", "[RingBuffer]") {
REQUIRE(buf.HasFree());
}
SECTION("Dynamic buffer construction") {
SECTION("Dynamic buffer construction")
{
RingBuffer<int> buf(10);
REQUIRE(buf.Capacity() == 10);
REQUIRE(buf.IsEmpty());
@@ -69,7 +88,8 @@ TEST_CASE("RingBuffer Basic Construction", "[RingBuffer]") {
}
}
TEST_CASE("RingBuffer Tracking Destruction", "[RingBuffer][Tracked]") {
TEST_CASE("RingBuffer Tracking Destruction", "[RingBuffer][Tracked]")
{
reset_tracking();
{
@@ -83,27 +103,31 @@ TEST_CASE("RingBuffer Tracking Destruction", "[RingBuffer][Tracked]") {
REQUIRE(Tracked::dtorCount == Tracked::ctorCount);
}
TEST_CASE("RingBuffer Move and Copy Semantics", "[RingBuffer][Tracked]") {
TEST_CASE("RingBuffer Move and Copy Semantics", "[RingBuffer][Tracked]")
{
reset_tracking();
{
RingBuffer<Tracked, 3> buf;
SECTION("Push with move") {
SECTION("Push with move")
{
Tracked t(42);
buf.Push(std::move(t));
REQUIRE(Tracked::moveCount >= 1);
REQUIRE(buf.Front().value == 42);
}
SECTION("Push with copy") {
SECTION("Push with copy")
{
Tracked t(42);
buf.Push(t);
REQUIRE(Tracked::copyCount >= 1);
REQUIRE(buf.Front().value == 42);
}
SECTION("Emplace constructs in-place") {
SECTION("Emplace constructs in-place")
{
int initialCtors = Tracked::ctorCount;
buf.Emplace(99);
REQUIRE(Tracked::ctorCount == initialCtors + 1);
@@ -114,7 +138,8 @@ TEST_CASE("RingBuffer Move and Copy Semantics", "[RingBuffer][Tracked]") {
REQUIRE(Tracked::dtorCount == Tracked::ctorCount);
}
TEST_CASE("RingBuffer Push and Pop Operations", "[RingBuffer]") {
TEST_CASE("RingBuffer Push and Pop Operations", "[RingBuffer]")
{
RingBuffer<int, 3> buf;
buf.Push(1);
@@ -124,7 +149,8 @@ TEST_CASE("RingBuffer Push and Pop Operations", "[RingBuffer]") {
REQUIRE(buf.Count() == 3);
REQUIRE_FALSE(buf.HasFree());
SECTION("PopFront/Back correctness") {
SECTION("PopFront/Back correctness")
{
REQUIRE(buf.PopBack() == 1);
REQUIRE(buf.PopFront() == 3);
REQUIRE(buf.Count() == 1);
@@ -134,13 +160,15 @@ TEST_CASE("RingBuffer Push and Pop Operations", "[RingBuffer]") {
REQUIRE_THROWS_AS(buf.PopBack(), std::underflow_error);
}
SECTION("Clear empties the buffer") {
SECTION("Clear empties the buffer")
{
buf.Clear();
REQUIRE(buf.IsEmpty());
REQUIRE(buf.Count() == 0);
}
SECTION("Front and Back access") {
SECTION("Front and Back access")
{
REQUIRE(buf.Front() == 3);
REQUIRE(buf.Back() == 1);
@@ -154,14 +182,16 @@ TEST_CASE("RingBuffer Push and Pop Operations", "[RingBuffer]") {
}
}
TEST_CASE("RingBuffer PushNoOverwrite operation", "[RingBuffer]") {
TEST_CASE("RingBuffer PushNoOverwrite operation", "[RingBuffer]")
{
RingBuffer<int, 2> buf;
buf.PushNoOverwrite(1);
buf.PushNoOverwrite(2);
REQUIRE_THROWS_AS(buf.PushNoOverwrite(3), std::overflow_error);
SECTION("EmplaceNoOverwrite") {
SECTION("EmplaceNoOverwrite")
{
RingBuffer<std::string, 2> strBuf;
strBuf.EmplaceNoOverwrite("first");
strBuf.EmplaceNoOverwrite("second");
@@ -169,15 +199,18 @@ TEST_CASE("RingBuffer PushNoOverwrite operation", "[RingBuffer]") {
}
}
TEST_CASE("RingBuffer PushAndOverwrite operation", "[RingBuffer]") {
TEST_CASE("RingBuffer PushAndOverwrite operation", "[RingBuffer]")
{
RingBuffer<int, 2> buf;
SECTION("Cannot use PushAndOverwrite when buffer not full") {
SECTION("Cannot use PushAndOverwrite when buffer not full")
{
buf.Push(1);
REQUIRE_THROWS_AS(buf.PushAndOverwrite(5), std::runtime_error);
}
SECTION("PushAndOverwrite returns overwritten value") {
SECTION("PushAndOverwrite returns overwritten value")
{
buf.Fill(42);
REQUIRE(buf.Count() == 2);
@@ -188,12 +221,14 @@ TEST_CASE("RingBuffer PushAndOverwrite operation", "[RingBuffer]") {
}
}
TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") {
TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]")
{
RingBuffer<int, 2> buf;
buf.Push(1);
buf.Push(2);
SECTION("PushFront overwrites correctly") {
SECTION("PushFront overwrites correctly")
{
auto overwrittenFront = buf.PushFront(3);
REQUIRE(overwrittenFront.has_value());
REQUIRE(overwrittenFront.value() == 1);
@@ -201,7 +236,8 @@ TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") {
REQUIRE(buf.Back() == 2);
}
SECTION("PushBack overwrites correctly") {
SECTION("PushBack overwrites correctly")
{
auto overwrittenBack = buf.PushBack(4);
REQUIRE(overwrittenBack.has_value());
REQUIRE(overwrittenBack.value() == 1);
@@ -209,7 +245,8 @@ TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") {
REQUIRE(buf.Back() == 4);
}
SECTION("PushFront with available space doesn't overwrite") {
SECTION("PushFront with available space doesn't overwrite")
{
RingBuffer<int, 3> buf2;
buf2.Push(10);
@@ -220,7 +257,8 @@ TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") {
REQUIRE(buf2.Back() == 10);
}
SECTION("PushBack with available space doesn't overwrite") {
SECTION("PushBack with available space doesn't overwrite")
{
RingBuffer<int, 3> buf2;
buf2.Push(10);
@@ -232,7 +270,8 @@ TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") {
}
}
TEST_CASE("at() bounds checking", "[RingBuffer]") {
TEST_CASE("at() bounds checking", "[RingBuffer]")
{
RingBuffer<int, 3> buf;
buf.Push(100);
buf.Push(200);
@@ -241,7 +280,8 @@ TEST_CASE("at() bounds checking", "[RingBuffer]") {
REQUIRE(buf.at(1) == 200);
REQUIRE_THROWS_AS(buf.at(2), std::range_error);
SECTION("const at() works correctly") {
SECTION("const at() works correctly")
{
const auto& constBuf = buf;
REQUIRE(constBuf.at(0) == 100);
REQUIRE(constBuf.at(1) == 200);
@@ -249,7 +289,8 @@ TEST_CASE("at() bounds checking", "[RingBuffer]") {
}
}
TEST_CASE("Index-based Access and Wraparound", "[RingBuffer]") {
TEST_CASE("Index-based Access and Wraparound", "[RingBuffer]")
{
RingBuffer<int, 3> buf;
buf.Push(1);
@@ -274,10 +315,12 @@ TEST_CASE("Index-based Access and Wraparound", "[RingBuffer]") {
REQUIRE(buf[1] == 5);
REQUIRE(buf[2] == 6);
SECTION("Wraparound with power-of-2 size") {
SECTION("Wraparound with power-of-2 size")
{
RingBuffer<int, 4> powBuf; // Power of 2 size for different code path
for (int i = 1; i <= 4; i++) {
for (int i = 1; i <= 4; i++)
{
powBuf.Push(i);
}
@@ -292,20 +335,24 @@ TEST_CASE("Index-based Access and Wraparound", "[RingBuffer]") {
}
}
TEST_CASE("Fill operation", "[RingBuffer]") {
TEST_CASE("Fill operation", "[RingBuffer]")
{
RingBuffer<int, 5> buf;
SECTION("Fill empty buffer") {
SECTION("Fill empty buffer")
{
buf.Fill(42);
REQUIRE(buf.Count() == 5);
REQUIRE_FALSE(buf.HasFree());
for (int i = 0; i < 5; i++) {
for (int i = 0; i < 5; i++)
{
REQUIRE(buf[i] == 42);
}
}
SECTION("Fill partially full buffer") {
SECTION("Fill partially full buffer")
{
buf.Push(1);
buf.Push(2);
@@ -321,7 +368,8 @@ TEST_CASE("Fill operation", "[RingBuffer]") {
}
}
TEST_CASE("Emplace and EmplaceBack/Front work correctly", "[RingBuffer]") {
TEST_CASE("Emplace and EmplaceBack/Front work correctly", "[RingBuffer]")
{
RingBuffer<std::string, 2> buf;
buf.Emplace("first");
@@ -336,7 +384,8 @@ TEST_CASE("Emplace and EmplaceBack/Front work correctly", "[RingBuffer]") {
REQUIRE(overwritten.value() == "second");
REQUIRE(buf.Front() == "new");
SECTION("EmplaceNoOverwrite throws when full") {
SECTION("EmplaceNoOverwrite throws when full")
{
RingBuffer<std::string, 2> buf2;
buf2.EmplaceNoOverwrite("one");
buf2.EmplaceNoOverwrite("two");
@@ -344,26 +393,30 @@ TEST_CASE("Emplace and EmplaceBack/Front work correctly", "[RingBuffer]") {
}
}
TEST_CASE("Iterators forward and reverse", "[RingBuffer]") {
TEST_CASE("Iterators forward and reverse", "[RingBuffer]")
{
RingBuffer<int, 4> buf;
buf.Push(10);
buf.Push(20);
buf.Push(30);
SECTION("Forward iteration") {
SECTION("Forward iteration")
{
std::vector<int> forward;
for (int val: buf) forward.push_back(val);
REQUIRE(forward == std::vector<int> {10, 20, 30});
}
SECTION("Reverse iteration") {
SECTION("Reverse iteration")
{
std::vector<int> reverse;
for (auto it = buf.rbegin(); it != buf.rend(); ++it)
reverse.push_back(*it);
REQUIRE(reverse == std::vector<int> {30, 20, 10});
}
SECTION("Iterator decrement") {
SECTION("Iterator decrement")
{
auto it = buf.end();
--it;
REQUIRE(*it == 30);
@@ -373,7 +426,8 @@ TEST_CASE("Iterators forward and reverse", "[RingBuffer]") {
REQUIRE(*it == 10);
}
SECTION("Reverse iterator decrement") {
SECTION("Reverse iterator decrement")
{
auto it = buf.rend();
--it;
REQUIRE(*it == 10);
@@ -383,7 +437,8 @@ TEST_CASE("Iterators forward and reverse", "[RingBuffer]") {
REQUIRE(*it == 30);
}
SECTION("Iterator post-increment/decrement") {
SECTION("Iterator post-increment/decrement")
{
auto it = buf.begin();
REQUIRE(*(it++) == 10);
REQUIRE(*it == 20);
@@ -393,43 +448,51 @@ TEST_CASE("Iterators forward and reverse", "[RingBuffer]") {
REQUIRE(*it2 == 20);
}
SECTION("Empty buffer iteration") {
SECTION("Empty buffer iteration")
{
RingBuffer<int, 3> emptyBuf;
REQUIRE(emptyBuf.begin() == emptyBuf.end());
REQUIRE(emptyBuf.rbegin() == emptyBuf.rend());
}
}
TEST_CASE("Const correctness in iterators", "[RingBuffer][Const]") {
TEST_CASE("Const correctness in iterators", "[RingBuffer][Const]")
{
RingBuffer<int, 3> buf;
buf.Push(5);
buf.Push(6);
const auto& constBuf = buf;
SECTION("const_iterator usage") {
SECTION("const_iterator usage")
{
std::ostringstream oss;
for (auto it = constBuf.cbegin(); it != constBuf.cend(); ++it) {
for (auto it = constBuf.cbegin(); it != constBuf.cend(); ++it)
{
oss << *it << " ";
}
REQUIRE(oss.str() == "5 6 ");
}
SECTION("const rbegin/rend usage") {
SECTION("const rbegin/rend usage")
{
std::vector<int> values;
for (auto it = constBuf.crbegin(); it != constBuf.crend(); ++it) {
for (auto it = constBuf.crbegin(); it != constBuf.crend(); ++it)
{
values.push_back(*it);
}
REQUIRE(values == std::vector<int> {6, 5});
}
SECTION("const operator[]") {
SECTION("const operator[]")
{
REQUIRE(constBuf[0] == 5);
REQUIRE(constBuf[1] == 6);
}
}
TEST_CASE("Dynamic RingBuffer behaves like static", "[RingBuffer][Dynamic]") {
TEST_CASE("Dynamic RingBuffer behaves like static", "[RingBuffer][Dynamic]")
{
RingBuffer<int> buf(5);
for (int i = 0; i < 5; ++i)
@@ -444,7 +507,8 @@ TEST_CASE("Dynamic RingBuffer behaves like static", "[RingBuffer][Dynamic]") {
REQUIRE(val == 0);
REQUIRE(buf.Count() == 4);
SECTION("Dynamic buffer wraparound") {
SECTION("Dynamic buffer wraparound")
{
buf.Push(50);
REQUIRE(buf.Front() == 50);
REQUIRE(buf.Back() == 10);
@@ -458,7 +522,8 @@ TEST_CASE("Dynamic RingBuffer behaves like static", "[RingBuffer][Dynamic]") {
}
}
TEST_CASE("RingBuffer with complex types", "[RingBuffer][Complex]") {
TEST_CASE("RingBuffer with complex types", "[RingBuffer][Complex]")
{
RingBuffer<std::vector<int>, 3> buf;
buf.Push(std::vector<int> {1, 2, 3});
@@ -478,11 +543,13 @@ TEST_CASE("RingBuffer with complex types", "[RingBuffer][Complex]") {
REQUIRE(buf.IsEmpty());
}
TEST_CASE("Iterator operations with wrapped buffer", "[RingBuffer][Iterators]") {
TEST_CASE("Iterator operations with wrapped buffer", "[RingBuffer][Iterators]")
{
RingBuffer<int, 5> buf;
// Fill buffer
for (int i = 1; i <= 5; i++) {
for (int i = 1; i <= 5; i++)
{
buf.Push(i);
}
@@ -495,22 +562,26 @@ TEST_CASE("Iterator operations with wrapped buffer", "[RingBuffer][Iterators]")
// Buffer should now be: 3, 4, 5, 6, 7 with internal wraparound
std::vector<int> values;
for (auto val : buf) {
for (auto val: buf)
{
values.push_back(val);
}
REQUIRE(values == std::vector<int> {3, 4, 5, 6, 7});
std::vector<int> reverseValues;
for (auto it = buf.rbegin(); it != buf.rend(); ++it) {
for (auto it = buf.rbegin(); it != buf.rend(); ++it)
{
reverseValues.push_back(*it);
}
REQUIRE(reverseValues == std::vector<int> {7, 6, 5, 4, 3});
}
TEST_CASE("Edge cases", "[RingBuffer][EdgeCases]") {
SECTION("Capacity of 1") {
TEST_CASE("Edge cases", "[RingBuffer][EdgeCases]")
{
SECTION("Capacity of 1")
{
RingBuffer<int, 1> buf;
REQUIRE(buf.Capacity() == 1);
REQUIRE(buf.IsEmpty());
@@ -529,7 +600,8 @@ TEST_CASE("Edge cases", "[RingBuffer][EdgeCases]") {
REQUIRE_THROWS_AS(buf.PopFront(), std::underflow_error);
}
SECTION("Dynamic buffer with size 1") {
SECTION("Dynamic buffer with size 1")
{
RingBuffer<int> buf(1);
buf.Push(42);
REQUIRE(buf.Front() == 42);
@@ -540,13 +612,16 @@ TEST_CASE("Edge cases", "[RingBuffer][EdgeCases]") {
}
}
TEST_CASE("Algorithm compatibility", "[RingBuffer][Algorithms]") {
TEST_CASE("Algorithm compatibility", "[RingBuffer][Algorithms]")
{
RingBuffer<int, 5> buf;
for (int i = 1; i <= 5; i++) {
for (int i = 1; i <= 5; i++)
{
buf.Push(i);
}
SECTION("std::find") {
SECTION("std::find")
{
auto it = std::find(buf.begin(), buf.end(), 3);
REQUIRE(it != buf.end());
REQUIRE(*it == 3);
@@ -555,15 +630,25 @@ TEST_CASE("Algorithm compatibility", "[RingBuffer][Algorithms]") {
REQUIRE(notFound == buf.end());
}
SECTION("std::count") {
SECTION("std::count")
{
buf.Push(3); // Overwrites the 1
int count = std::count(buf.begin(), buf.end(), 3);
REQUIRE(count == 2);
}
SECTION("std::copy") {
SECTION("std::copy")
{
std::vector<int> target(5);
std::copy(buf.begin(), buf.end(), target.begin());
REQUIRE(target == std::vector<int> {1, 2, 3, 4, 5});
}
}
TEST_CASE("Class size", "[RingBuffer]")
{
REQUIRE(sizeof(RingBuffer<int>) == 32);
REQUIRE(sizeof(RingBuffer<int, 1>) == 24);
REQUIRE(sizeof(RingBuffer<int, 2>) == 24);
REQUIRE(sizeof(RingBuffer<int, 4>) == 32);
}

View File

@@ -161,7 +161,8 @@ TEST_CASE("Precision with Floating Point Types", "[RunningAverage]")
RunningAverage<double, std::numeric_limits<size_t>::max(), true, 20> avg(5, 0.0);
// Push values that would cause floating point rounding errors
for (int i = 0; i < 100; i++) {
for (int i = 0; i < 100; i++)
{
avg.Push(0.1); // 0.1 cannot be represented exactly in binary floating point
}