Add more tests
This commit is contained in:
@@ -382,6 +382,37 @@ namespace OpenVulkano
|
|||||||
new (&data()[tail]) T(std::forward<Args>(args)...);
|
new (&data()[tail]) T(std::forward<Args>(args)...);
|
||||||
return oldData;
|
return oldData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T PushAndOverwrite(const T& value)
|
||||||
|
{
|
||||||
|
if (HasFree()) [[unlikely]] throw std::runtime_error("Can't overwrite data because there are still empty slots.");
|
||||||
|
Impl().IncrementHead();
|
||||||
|
T oldData(std::move(Front()));
|
||||||
|
data()[head].~T();
|
||||||
|
new (&data()[head]) T(value);
|
||||||
|
return oldData;
|
||||||
|
}
|
||||||
|
|
||||||
|
T PushAndOverwrite(T&& value)
|
||||||
|
{
|
||||||
|
if (HasFree()) [[unlikely]] throw std::runtime_error("Can't overwrite data because there are still empty slots.");
|
||||||
|
Impl().IncrementHead();
|
||||||
|
T oldData(std::move(Front()));
|
||||||
|
data()[head].~T();
|
||||||
|
new (&data()[head]) T(std::move(value));
|
||||||
|
return oldData;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
T PushAndOverwrite(Args&&... args)
|
||||||
|
{
|
||||||
|
if (HasFree()) [[unlikely]] throw std::runtime_error("Can't overwrite data because there are still empty slots.");
|
||||||
|
Impl().IncrementHead();
|
||||||
|
T oldData(std::move(Front()));
|
||||||
|
data()[head].~T();
|
||||||
|
new (&data()[head]) T(std::forward<Args>(args)...);
|
||||||
|
return oldData;
|
||||||
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
[[nodiscard]] T& at(size_t idx) { if (idx >= Size()) throw std::range_error("Out of bounds"); return (*this)[idx]; }
|
[[nodiscard]] T& at(size_t idx) { if (idx >= Size()) throw std::range_error("Out of bounds"); return (*this)[idx]; }
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
#include "Data/Containers/RingBuffer.hpp"
|
#include "Data/Containers/RingBuffer.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace OpenVulkano;
|
using namespace OpenVulkano;
|
||||||
|
|
||||||
@@ -15,15 +18,26 @@ using namespace OpenVulkano;
|
|||||||
struct Tracked {
|
struct Tracked {
|
||||||
static inline int ctorCount = 0;
|
static inline int ctorCount = 0;
|
||||||
static inline int dtorCount = 0;
|
static inline int dtorCount = 0;
|
||||||
|
static inline int moveCount = 0;
|
||||||
|
static inline int copyCount = 0;
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
Tracked(int v = 0) : value(v) { ++ctorCount; }
|
Tracked(int v = 0) : value(v) { ++ctorCount; }
|
||||||
Tracked(const Tracked& other) : value(other.value) { ++ctorCount; }
|
Tracked(const Tracked& other) : value(other.value) { ++ctorCount; ++copyCount; }
|
||||||
Tracked(Tracked&& other) noexcept : value(other.value) { ++ctorCount; }
|
Tracked(Tracked&& other) noexcept : value(other.value) { ++ctorCount; ++moveCount; }
|
||||||
~Tracked() { ++dtorCount; }
|
~Tracked() { ++dtorCount; }
|
||||||
|
|
||||||
Tracked& operator=(const Tracked&) = default;
|
Tracked& operator=(const Tracked& other) {
|
||||||
Tracked& operator=(Tracked&&) = default;
|
value = other.value;
|
||||||
|
++copyCount;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return lhs.value == rhs.value;
|
||||||
@@ -33,6 +47,26 @@ struct Tracked {
|
|||||||
void reset_tracking() {
|
void reset_tracking() {
|
||||||
Tracked::ctorCount = 0;
|
Tracked::ctorCount = 0;
|
||||||
Tracked::dtorCount = 0;
|
Tracked::dtorCount = 0;
|
||||||
|
Tracked::moveCount = 0;
|
||||||
|
Tracked::copyCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("RingBuffer Basic Construction", "[RingBuffer]") {
|
||||||
|
SECTION("Static buffer construction") {
|
||||||
|
RingBuffer<int, 5> buf;
|
||||||
|
REQUIRE(buf.Capacity() == 5);
|
||||||
|
REQUIRE(buf.IsEmpty());
|
||||||
|
REQUIRE(buf.Count() == 0);
|
||||||
|
REQUIRE(buf.HasFree());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Dynamic buffer construction") {
|
||||||
|
RingBuffer<int> buf(10);
|
||||||
|
REQUIRE(buf.Capacity() == 10);
|
||||||
|
REQUIRE(buf.IsEmpty());
|
||||||
|
REQUIRE(buf.Count() == 0);
|
||||||
|
REQUIRE(buf.HasFree());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("RingBuffer Tracking Destruction", "[RingBuffer][Tracked]") {
|
TEST_CASE("RingBuffer Tracking Destruction", "[RingBuffer][Tracked]") {
|
||||||
@@ -49,6 +83,37 @@ TEST_CASE("RingBuffer Tracking Destruction", "[RingBuffer][Tracked]") {
|
|||||||
REQUIRE(Tracked::dtorCount == Tracked::ctorCount);
|
REQUIRE(Tracked::dtorCount == Tracked::ctorCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("RingBuffer Move and Copy Semantics", "[RingBuffer][Tracked]") {
|
||||||
|
reset_tracking();
|
||||||
|
|
||||||
|
{
|
||||||
|
RingBuffer<Tracked, 3> buf;
|
||||||
|
|
||||||
|
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") {
|
||||||
|
Tracked t(42);
|
||||||
|
buf.Push(t);
|
||||||
|
REQUIRE(Tracked::copyCount >= 1);
|
||||||
|
REQUIRE(buf.Front().value == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Emplace constructs in-place") {
|
||||||
|
int initialCtors = Tracked::ctorCount;
|
||||||
|
buf.Emplace(99);
|
||||||
|
REQUIRE(Tracked::ctorCount == initialCtors + 1);
|
||||||
|
REQUIRE(buf.Front().value == 99);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
RingBuffer<int, 3> buf;
|
||||||
|
|
||||||
@@ -63,6 +128,10 @@ TEST_CASE("RingBuffer Push and Pop Operations", "[RingBuffer]") {
|
|||||||
REQUIRE(buf.PopBack() == 1);
|
REQUIRE(buf.PopBack() == 1);
|
||||||
REQUIRE(buf.PopFront() == 3);
|
REQUIRE(buf.PopFront() == 3);
|
||||||
REQUIRE(buf.Count() == 1);
|
REQUIRE(buf.Count() == 1);
|
||||||
|
REQUIRE(buf.PopFront() == 2);
|
||||||
|
REQUIRE(buf.IsEmpty());
|
||||||
|
REQUIRE_THROWS_AS(buf.PopFront(), std::underflow_error);
|
||||||
|
REQUIRE_THROWS_AS(buf.PopBack(), std::underflow_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Clear empties the buffer") {
|
SECTION("Clear empties the buffer") {
|
||||||
@@ -70,6 +139,53 @@ TEST_CASE("RingBuffer Push and Pop Operations", "[RingBuffer]") {
|
|||||||
REQUIRE(buf.IsEmpty());
|
REQUIRE(buf.IsEmpty());
|
||||||
REQUIRE(buf.Count() == 0);
|
REQUIRE(buf.Count() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Front and Back access") {
|
||||||
|
REQUIRE(buf.Front() == 3);
|
||||||
|
REQUIRE(buf.Back() == 1);
|
||||||
|
|
||||||
|
buf.PopFront();
|
||||||
|
REQUIRE(buf.Front() == 2);
|
||||||
|
REQUIRE(buf.Back() == 1);
|
||||||
|
|
||||||
|
buf.PopBack();
|
||||||
|
REQUIRE(buf.Front() == 2);
|
||||||
|
REQUIRE(buf.Back() == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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") {
|
||||||
|
RingBuffer<std::string, 2> strBuf;
|
||||||
|
strBuf.EmplaceNoOverwrite("first");
|
||||||
|
strBuf.EmplaceNoOverwrite("second");
|
||||||
|
REQUIRE_THROWS_AS(strBuf.EmplaceNoOverwrite("third"), std::overflow_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("RingBuffer PushAndOverwrite operation", "[RingBuffer]") {
|
||||||
|
RingBuffer<int, 2> buf;
|
||||||
|
|
||||||
|
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") {
|
||||||
|
buf.Fill(42);
|
||||||
|
REQUIRE(buf.Count() == 2);
|
||||||
|
|
||||||
|
int overwritten = buf.PushAndOverwrite(99);
|
||||||
|
REQUIRE(overwritten == 42);
|
||||||
|
REQUIRE(buf.Front() == 99);
|
||||||
|
REQUIRE(buf.Back() == 42);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") {
|
TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") {
|
||||||
@@ -77,16 +193,43 @@ TEST_CASE("PushBack and PushFront Overwrite Logic", "[RingBuffer]") {
|
|||||||
buf.Push(1);
|
buf.Push(1);
|
||||||
buf.Push(2);
|
buf.Push(2);
|
||||||
|
|
||||||
|
SECTION("PushFront overwrites correctly") {
|
||||||
auto overwrittenFront = buf.PushFront(3);
|
auto overwrittenFront = buf.PushFront(3);
|
||||||
REQUIRE(overwrittenFront.has_value());
|
REQUIRE(overwrittenFront.has_value());
|
||||||
REQUIRE(overwrittenFront.value() == 1);
|
REQUIRE(overwrittenFront.value() == 1);
|
||||||
|
REQUIRE(buf.Front() == 3);
|
||||||
|
REQUIRE(buf.Back() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("PushBack overwrites correctly") {
|
||||||
auto overwrittenBack = buf.PushBack(4);
|
auto overwrittenBack = buf.PushBack(4);
|
||||||
REQUIRE(overwrittenBack.has_value());
|
REQUIRE(overwrittenBack.has_value());
|
||||||
REQUIRE(overwrittenBack.value() == 2);
|
REQUIRE(overwrittenBack.value() == 1);
|
||||||
|
REQUIRE(buf.Front() == 2);
|
||||||
REQUIRE(buf.Front() == 3);
|
|
||||||
REQUIRE(buf.Back() == 4);
|
REQUIRE(buf.Back() == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("PushFront with available space doesn't overwrite") {
|
||||||
|
RingBuffer<int, 3> buf2;
|
||||||
|
buf2.Push(10);
|
||||||
|
|
||||||
|
auto result = buf2.PushFront(20);
|
||||||
|
REQUIRE_FALSE(result.has_value());
|
||||||
|
REQUIRE(buf2.Count() == 2);
|
||||||
|
REQUIRE(buf2.Front() == 20);
|
||||||
|
REQUIRE(buf2.Back() == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("PushBack with available space doesn't overwrite") {
|
||||||
|
RingBuffer<int, 3> buf2;
|
||||||
|
buf2.Push(10);
|
||||||
|
|
||||||
|
auto result = buf2.PushBack(20);
|
||||||
|
REQUIRE_FALSE(result.has_value());
|
||||||
|
REQUIRE(buf2.Count() == 2);
|
||||||
|
REQUIRE(buf2.Front() == 10);
|
||||||
|
REQUIRE(buf2.Back() == 20);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("at() bounds checking", "[RingBuffer]") {
|
TEST_CASE("at() bounds checking", "[RingBuffer]") {
|
||||||
@@ -97,6 +240,13 @@ TEST_CASE("at() bounds checking", "[RingBuffer]") {
|
|||||||
REQUIRE(buf.at(0) == 100);
|
REQUIRE(buf.at(0) == 100);
|
||||||
REQUIRE(buf.at(1) == 200);
|
REQUIRE(buf.at(1) == 200);
|
||||||
REQUIRE_THROWS_AS(buf.at(2), std::range_error);
|
REQUIRE_THROWS_AS(buf.at(2), std::range_error);
|
||||||
|
|
||||||
|
SECTION("const at() works correctly") {
|
||||||
|
const auto& constBuf = buf;
|
||||||
|
REQUIRE(constBuf.at(0) == 100);
|
||||||
|
REQUIRE(constBuf.at(1) == 200);
|
||||||
|
REQUIRE_THROWS_AS(constBuf.at(2), std::range_error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Index-based Access and Wraparound", "[RingBuffer]") {
|
TEST_CASE("Index-based Access and Wraparound", "[RingBuffer]") {
|
||||||
@@ -123,6 +273,52 @@ TEST_CASE("Index-based Access and Wraparound", "[RingBuffer]") {
|
|||||||
REQUIRE(buf[0] == 4);
|
REQUIRE(buf[0] == 4);
|
||||||
REQUIRE(buf[1] == 5);
|
REQUIRE(buf[1] == 5);
|
||||||
REQUIRE(buf[2] == 6);
|
REQUIRE(buf[2] == 6);
|
||||||
|
|
||||||
|
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++) {
|
||||||
|
powBuf.Push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(powBuf[0] == 1);
|
||||||
|
REQUIRE(powBuf[3] == 4);
|
||||||
|
|
||||||
|
powBuf.PopBack();
|
||||||
|
powBuf.Push(5);
|
||||||
|
|
||||||
|
REQUIRE(powBuf[0] == 2);
|
||||||
|
REQUIRE(powBuf[3] == 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Fill operation", "[RingBuffer]") {
|
||||||
|
RingBuffer<int, 5> buf;
|
||||||
|
|
||||||
|
SECTION("Fill empty buffer") {
|
||||||
|
buf.Fill(42);
|
||||||
|
REQUIRE(buf.Count() == 5);
|
||||||
|
REQUIRE_FALSE(buf.HasFree());
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
REQUIRE(buf[i] == 42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Fill partially full buffer") {
|
||||||
|
buf.Push(1);
|
||||||
|
buf.Push(2);
|
||||||
|
|
||||||
|
buf.Fill(99);
|
||||||
|
REQUIRE(buf.Count() == 5);
|
||||||
|
REQUIRE_FALSE(buf.HasFree());
|
||||||
|
|
||||||
|
REQUIRE(buf[0] == 1);
|
||||||
|
REQUIRE(buf[1] == 2);
|
||||||
|
REQUIRE(buf[2] == 99);
|
||||||
|
REQUIRE(buf[3] == 99);
|
||||||
|
REQUIRE(buf[4] == 99);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Emplace and EmplaceBack/Front work correctly", "[RingBuffer]") {
|
TEST_CASE("Emplace and EmplaceBack/Front work correctly", "[RingBuffer]") {
|
||||||
@@ -139,6 +335,13 @@ TEST_CASE("Emplace and EmplaceBack/Front work correctly", "[RingBuffer]") {
|
|||||||
REQUIRE(overwritten.has_value());
|
REQUIRE(overwritten.has_value());
|
||||||
REQUIRE(overwritten.value() == "second");
|
REQUIRE(overwritten.value() == "second");
|
||||||
REQUIRE(buf.Front() == "new");
|
REQUIRE(buf.Front() == "new");
|
||||||
|
|
||||||
|
SECTION("EmplaceNoOverwrite throws when full") {
|
||||||
|
RingBuffer<std::string, 2> buf2;
|
||||||
|
buf2.EmplaceNoOverwrite("one");
|
||||||
|
buf2.EmplaceNoOverwrite("two");
|
||||||
|
REQUIRE_THROWS_AS(buf2.EmplaceNoOverwrite("three"), std::overflow_error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Iterators forward and reverse", "[RingBuffer]") {
|
TEST_CASE("Iterators forward and reverse", "[RingBuffer]") {
|
||||||
@@ -147,14 +350,54 @@ TEST_CASE("Iterators forward and reverse", "[RingBuffer]") {
|
|||||||
buf.Push(20);
|
buf.Push(20);
|
||||||
buf.Push(30);
|
buf.Push(30);
|
||||||
|
|
||||||
|
SECTION("Forward iteration") {
|
||||||
std::vector<int> forward;
|
std::vector<int> forward;
|
||||||
for (int val : buf) forward.push_back(val);
|
for (int val : buf) forward.push_back(val);
|
||||||
REQUIRE(forward == std::vector<int>{10, 20, 30});
|
REQUIRE(forward == std::vector<int>{10, 20, 30});
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Reverse iteration") {
|
||||||
std::vector<int> reverse;
|
std::vector<int> reverse;
|
||||||
for (auto it = buf.rbegin(); it != buf.rend(); ++it)
|
for (auto it = buf.rbegin(); it != buf.rend(); ++it)
|
||||||
reverse.push_back(*it);
|
reverse.push_back(*it);
|
||||||
REQUIRE(reverse == std::vector<int>{30, 20, 10});
|
REQUIRE(reverse == std::vector<int>{30, 20, 10});
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Iterator decrement") {
|
||||||
|
auto it = buf.end();
|
||||||
|
--it;
|
||||||
|
REQUIRE(*it == 30);
|
||||||
|
--it;
|
||||||
|
REQUIRE(*it == 20);
|
||||||
|
--it;
|
||||||
|
REQUIRE(*it == 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Reverse iterator decrement") {
|
||||||
|
auto it = buf.rend();
|
||||||
|
--it;
|
||||||
|
REQUIRE(*it == 10);
|
||||||
|
--it;
|
||||||
|
REQUIRE(*it == 20);
|
||||||
|
--it;
|
||||||
|
REQUIRE(*it == 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Iterator post-increment/decrement") {
|
||||||
|
auto it = buf.begin();
|
||||||
|
REQUIRE(*(it++) == 10);
|
||||||
|
REQUIRE(*it == 20);
|
||||||
|
|
||||||
|
auto it2 = --buf.end();
|
||||||
|
REQUIRE(*(it2--) == 30);
|
||||||
|
REQUIRE(*it2 == 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]") {
|
||||||
@@ -164,12 +407,26 @@ TEST_CASE("Const correctness in iterators", "[RingBuffer][Const]") {
|
|||||||
|
|
||||||
const auto& constBuf = buf;
|
const auto& constBuf = buf;
|
||||||
|
|
||||||
|
SECTION("const_iterator usage") {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
for (auto it = constBuf.cbegin(); it != constBuf.cend(); ++it) {
|
for (auto it = constBuf.cbegin(); it != constBuf.cend(); ++it) {
|
||||||
oss << *it << " ";
|
oss << *it << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
REQUIRE(oss.str() == "5 6 ");
|
REQUIRE(oss.str() == "5 6 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("const rbegin/rend usage") {
|
||||||
|
std::vector<int> values;
|
||||||
|
for (auto it = constBuf.crbegin(); it != constBuf.crend(); ++it) {
|
||||||
|
values.push_back(*it);
|
||||||
|
}
|
||||||
|
REQUIRE(values == std::vector<int>{6, 5});
|
||||||
|
}
|
||||||
|
|
||||||
|
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]") {
|
||||||
@@ -186,4 +443,126 @@ TEST_CASE("Dynamic RingBuffer behaves like static", "[RingBuffer][Dynamic]") {
|
|||||||
auto val = buf.PopBack();
|
auto val = buf.PopBack();
|
||||||
REQUIRE(val == 0);
|
REQUIRE(val == 0);
|
||||||
REQUIRE(buf.Count() == 4);
|
REQUIRE(buf.Count() == 4);
|
||||||
|
|
||||||
|
SECTION("Dynamic buffer wraparound") {
|
||||||
|
buf.Push(50);
|
||||||
|
REQUIRE(buf.Front() == 50);
|
||||||
|
REQUIRE(buf.Back() == 10);
|
||||||
|
|
||||||
|
buf.Push(60);
|
||||||
|
REQUIRE(buf[0] == 20);
|
||||||
|
REQUIRE(buf[1] == 30);
|
||||||
|
REQUIRE(buf[2] == 40);
|
||||||
|
REQUIRE(buf[3] == 50);
|
||||||
|
REQUIRE(buf[4] == 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("RingBuffer with complex types", "[RingBuffer][Complex]") {
|
||||||
|
RingBuffer<std::vector<int>, 3> buf;
|
||||||
|
|
||||||
|
buf.Push(std::vector<int>{1, 2, 3});
|
||||||
|
buf.Emplace(std::initializer_list<int>{4, 5, 6});
|
||||||
|
|
||||||
|
REQUIRE(buf.Count() == 2);
|
||||||
|
REQUIRE(buf.Front()[0] == 4);
|
||||||
|
REQUIRE(buf.Front()[2] == 6);
|
||||||
|
REQUIRE(buf.Back()[0] == 1);
|
||||||
|
REQUIRE(buf.Back()[2] == 3);
|
||||||
|
|
||||||
|
buf.PushFront(std::vector<int>{7, 8, 9});
|
||||||
|
REQUIRE(buf.Front()[0] == 7);
|
||||||
|
|
||||||
|
buf.Clear();
|
||||||
|
REQUIRE(buf.IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Iterator operations with wrapped buffer", "[RingBuffer][Iterators]") {
|
||||||
|
RingBuffer<int, 5> buf;
|
||||||
|
|
||||||
|
// Fill buffer
|
||||||
|
for (int i = 1; i <= 5; i++) {
|
||||||
|
buf.Push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force wraparound
|
||||||
|
buf.PopBack();
|
||||||
|
buf.PopBack();
|
||||||
|
buf.Push(6);
|
||||||
|
buf.Push(7);
|
||||||
|
|
||||||
|
// Buffer should now be: 3, 4, 5, 6, 7 with internal wraparound
|
||||||
|
|
||||||
|
std::vector<int> values;
|
||||||
|
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) {
|
||||||
|
reverseValues.push_back(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(reverseValues == std::vector<int>{7, 6, 5, 4, 3});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Edge cases", "[RingBuffer][EdgeCases]") {
|
||||||
|
SECTION("Capacity of 1") {
|
||||||
|
RingBuffer<int, 1> buf;
|
||||||
|
REQUIRE(buf.Capacity() == 1);
|
||||||
|
REQUIRE(buf.IsEmpty());
|
||||||
|
|
||||||
|
buf.Push(42);
|
||||||
|
REQUIRE(buf.Front() == 42);
|
||||||
|
REQUIRE(buf.Back() == 42);
|
||||||
|
REQUIRE_FALSE(buf.HasFree());
|
||||||
|
|
||||||
|
buf.Push(99);
|
||||||
|
REQUIRE(buf.Front() == 99);
|
||||||
|
REQUIRE(buf.Back() == 99);
|
||||||
|
|
||||||
|
buf.PopFront();
|
||||||
|
REQUIRE(buf.IsEmpty());
|
||||||
|
REQUIRE_THROWS_AS(buf.PopFront(), std::underflow_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Dynamic buffer with size 1") {
|
||||||
|
RingBuffer<int> buf(1);
|
||||||
|
buf.Push(42);
|
||||||
|
REQUIRE(buf.Front() == 42);
|
||||||
|
REQUIRE_FALSE(buf.HasFree());
|
||||||
|
|
||||||
|
buf.Push(99);
|
||||||
|
REQUIRE(buf.Front() == 99);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Algorithm compatibility", "[RingBuffer][Algorithms]") {
|
||||||
|
RingBuffer<int, 5> buf;
|
||||||
|
for (int i = 1; i <= 5; i++) {
|
||||||
|
buf.Push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::find") {
|
||||||
|
auto it = std::find(buf.begin(), buf.end(), 3);
|
||||||
|
REQUIRE(it != buf.end());
|
||||||
|
REQUIRE(*it == 3);
|
||||||
|
|
||||||
|
auto notFound = std::find(buf.begin(), buf.end(), 99);
|
||||||
|
REQUIRE(notFound == buf.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::count") {
|
||||||
|
buf.Push(3); // Overwrites the 1
|
||||||
|
int count = std::count(buf.begin(), buf.end(), 3);
|
||||||
|
REQUIRE(count == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user