/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include #include "Data/Containers/RingBuffer.hpp" #include #include using namespace OpenVulkano; // Helper to track construction/destruction struct Tracked { static inline int ctorCount = 0; static inline int dtorCount = 0; int value; Tracked(int v = 0) : value(v) { ++ctorCount; } Tracked(const Tracked& other) : value(other.value) { ++ctorCount; } Tracked(Tracked&& other) noexcept : value(other.value) { ++ctorCount; } ~Tracked() { ++dtorCount; } Tracked& operator=(const Tracked&) = default; Tracked& operator=(Tracked&&) = default; friend bool operator==(const Tracked& lhs, const Tracked& rhs) { return lhs.value == rhs.value; } }; void reset_tracking() { Tracked::ctorCount = 0; Tracked::dtorCount = 0; } TEST_CASE("RingBuffer Tracking Destruction", "[RingBuffer][Tracked]") { reset_tracking(); { RingBuffer buf; buf.Push(Tracked(1)); buf.Push(Tracked(2)); buf.PopFront(); buf.Clear(); } REQUIRE(Tracked::dtorCount == Tracked::ctorCount); } TEST_CASE("RingBuffer Push and Pop Operations", "[RingBuffer]") { RingBuffer buf; buf.Push(1); buf.Push(2); buf.Push(3); REQUIRE(buf.Count() == 3); REQUIRE_FALSE(buf.HasFree()); SECTION("PopFront/Back correctness") { REQUIRE(buf.PopBack() == 1); REQUIRE(buf.PopFront() == 3); REQUIRE(buf.Count() == 1); } SECTION("Clear empties the buffer") { buf.Clear(); REQUIRE(buf.IsEmpty()); REQUIRE(buf.Count() == 0); } } TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") { RingBuffer buf; buf.Push(1); buf.Push(2); auto overwrittenFront = buf.PushFront(3); REQUIRE(overwrittenFront.has_value()); REQUIRE(overwrittenFront.value() == 1); auto overwrittenBack = buf.PushBack(4); REQUIRE(overwrittenBack.has_value()); REQUIRE(overwrittenBack.value() == 2); REQUIRE(buf.Front() == 3); REQUIRE(buf.Back() == 4); } TEST_CASE("at() bounds checking", "[RingBuffer]") { RingBuffer buf; buf.Push(100); buf.Push(200); REQUIRE(buf.at(0) == 100); REQUIRE(buf.at(1) == 200); REQUIRE_THROWS_AS(buf.at(2), std::range_error); } TEST_CASE("Index-based Access and Wraparound", "[RingBuffer]") { RingBuffer buf; buf.Push(1); buf.Push(2); buf.Push(3); REQUIRE(buf[0] == 1); REQUIRE(buf[1] == 2); REQUIRE(buf[2] == 3); buf.PopBack(); buf.Push(4); REQUIRE(buf[0] == 2); REQUIRE(buf[1] == 3); REQUIRE(buf[2] == 4); // Test full wraparound buf.Push(5); buf.Push(6); REQUIRE(buf[0] == 4); REQUIRE(buf[1] == 5); REQUIRE(buf[2] == 6); } TEST_CASE("Emplace and EmplaceBack/Front work correctly", "[RingBuffer]") { RingBuffer buf; buf.Emplace("first"); buf.EmplaceBack("second"); REQUIRE(buf.Count() == 2); REQUIRE(buf[1] == "first"); REQUIRE(buf[0] == "second"); auto overwritten = buf.EmplaceFront("new"); REQUIRE(overwritten.has_value()); REQUIRE(overwritten.value() == "second"); REQUIRE(buf.Front() == "new"); } TEST_CASE("Iterators forward and reverse", "[RingBuffer]") { RingBuffer buf; buf.Push(10); buf.Push(20); buf.Push(30); std::vector forward; for (int val : buf) forward.push_back(val); REQUIRE(forward == std::vector{10, 20, 30}); std::vector reverse; for (auto it = buf.rbegin(); it != buf.rend(); ++it) reverse.push_back(*it); REQUIRE(reverse == std::vector{30, 20, 10}); } TEST_CASE("Const correctness in iterators", "[RingBuffer][Const]") { RingBuffer buf; buf.Push(5); buf.Push(6); const auto& constBuf = buf; std::ostringstream oss; for (auto it = constBuf.cbegin(); it != constBuf.cend(); ++it) { oss << *it << " "; } REQUIRE(oss.str() == "5 6 "); } TEST_CASE("Dynamic RingBuffer behaves like static", "[RingBuffer][Dynamic]") { RingBuffer buf(5); for (int i = 0; i < 5; ++i) buf.Push(i * 10); REQUIRE(buf.Count() == 5); REQUIRE_FALSE(buf.HasFree()); REQUIRE(buf.Front() == 40); REQUIRE(buf.Back() == 0); auto val = buf.PopBack(); REQUIRE(val == 0); REQUIRE(buf.Count() == 4); }