diff --git a/src/core/ring_buffer.hpp b/src/core/ring_buffer.hpp
index 35c69c12e2..3e9395bea8 100644
--- a/src/core/ring_buffer.hpp
+++ b/src/core/ring_buffer.hpp
@@ -46,6 +46,9 @@ class ring_buffer
ring_buffer_iterator_base(const ring_buffer *ring, uint32 pos)
: ring(ring), pos(pos) {}
+
+ public:
+ uint32 debug_raw_position() const { return this->pos; }
};
public:
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 2255f24112..e0f317fb99 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,7 +1,9 @@
add_test_files(
landscape_partial_pixel_z.cpp
math_func.cpp
+ ring_buffer.cpp
test_main.cpp
../landscape_ppz.cpp
+ ../core/alloc_func.cpp
../core/math_func.cpp
)
diff --git a/src/tests/ring_buffer.cpp b/src/tests/ring_buffer.cpp
new file mode 100644
index 0000000000..8ba6d5361c
--- /dev/null
+++ b/src/tests/ring_buffer.cpp
@@ -0,0 +1,346 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file ring_buffer.cpp Test functionality from core/ring_buffer.hpp */
+
+#include "../stdafx.h"
+
+#include "../3rdparty/catch2/catch.hpp"
+
+#include "../core/ring_buffer.hpp"
+
+std::ostream &operator<<(std::ostream &os, const ring_buffer::iterator &iter) {
+ return os << "Position: " << std::hex << iter.debug_raw_position();
+}
+
+void DumpRing(const ring_buffer &ring)
+{
+ char buffer[1024];
+ char *b = buffer;
+ const char *end = buffer + 1024;
+ b += snprintf(b, end - b, "Ring: Size: %u, Cap: %u, {", (uint)ring.size(), (uint)ring.capacity());
+ for (uint32 it : ring) {
+ b += snprintf(b, end - b, " %u,", it);
+ }
+ b--;
+ b += snprintf(b, end - b, " }");
+
+ WARN(buffer);
+}
+
+bool Matches(const ring_buffer &ring, std::initializer_list data)
+{
+ if (ring.size() != data.size()) {
+ DumpRing(ring);
+ return false;
+ }
+
+ auto data_iter = data.begin();
+ for (uint32 it : ring) {
+ if (it != *data_iter) {
+ DumpRing(ring);
+ return false;
+ }
+ ++data_iter;
+ }
+
+ return true;
+}
+
+TEST_CASE("RingBuffer - basic tests")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6 });
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6 }));
+
+ ring.push_front(0);
+ CHECK(Matches(ring, { 0, 1, 2, 3, 4, 5, 6 }));
+
+ ring.pop_back();
+ CHECK(Matches(ring, { 0, 1, 2, 3, 4, 5 }));
+
+ ring.push_back(10);
+ ring.push_back(11);
+ CHECK(Matches(ring, { 0, 1, 2, 3, 4, 5, 10, 11 }));
+
+ ring.pop_front();
+ ring.pop_front();
+ CHECK(Matches(ring, { 2, 3, 4, 5, 10, 11 }));
+ CHECK(ring.capacity() == 8);
+
+ CHECK(ring[0] == 2);
+ CHECK(ring[4] == 10);
+}
+
+TEST_CASE("RingBuffer - front resize")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6, 7, 8 });
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+
+ ring.push_front(10);
+ CHECK(Matches(ring, { 10, 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.size() == 9);
+ CHECK(ring.capacity() == 16);
+}
+
+TEST_CASE("RingBuffer - front resize 2")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6, 7, 8 });
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+
+ ring.pop_front();
+ ring.pop_front();
+ ring.push_back(20);
+ ring.push_back(21);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 8, 20, 21 }));
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+
+ ring.push_front(10);
+ CHECK(Matches(ring, { 10, 3, 4, 5, 6, 7, 8, 20, 21 }));
+ CHECK(ring.size() == 9);
+ CHECK(ring.capacity() == 16);
+}
+
+TEST_CASE("RingBuffer - back resize")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6, 7, 8 });
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+
+ ring.push_back(10);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8, 10 }));
+ CHECK(ring.size() == 9);
+ CHECK(ring.capacity() == 16);
+}
+
+TEST_CASE("RingBuffer - back resize 2")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6, 7, 8 });
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+
+ ring.pop_front();
+ ring.pop_front();
+ ring.push_back(20);
+ ring.push_back(21);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 8, 20, 21 }));
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+
+ ring.push_back(10);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 8, 20, 21, 10 }));
+ CHECK(ring.size() == 9);
+ CHECK(ring.capacity() == 16);
+}
+
+TEST_CASE("RingBuffer - insert at ends no grow")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6, 7 });
+ CHECK(ring.size() == 7);
+ CHECK(ring.capacity() == 8);
+
+ auto iter = ring.insert(ring.begin(), 10);
+ CHECK(Matches(ring, { 10, 1, 2, 3, 4, 5, 6, 7 }));
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+ CHECK(iter == ring.begin());
+
+ ring = ring_buffer({ 1, 2, 3, 4, 5, 6, 7 });
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7 }));
+ CHECK(ring.size() == 7);
+ CHECK(ring.capacity() == 8);
+
+ iter = ring.insert(ring.end(), 10);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 10 }));
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+ CHECK(iter == ring.end() - 1);
+}
+
+TEST_CASE("RingBuffer - insert at ends shifted no grow")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6, 7 });
+ ring.pop_front();
+ ring.pop_front();
+ ring.push_back(20);
+ ring.push_back(21);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 20, 21 }));
+ CHECK(ring.capacity() == 8);
+
+ auto iter = ring.insert(ring.begin(), 10);
+ CHECK(Matches(ring, { 10, 3, 4, 5, 6, 7, 20, 21 }));
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+ CHECK(iter == ring.begin());
+
+ ring = ring_buffer({ 1, 2, 3, 4, 5, 6, 7 });
+ ring.pop_front();
+ ring.pop_front();
+ ring.push_back(20);
+ ring.push_back(21);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 20, 21 }));
+ CHECK(ring.capacity() == 8);
+
+ iter = ring.insert(ring.end(), 10);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 20, 21, 10 }));
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+ CHECK(iter == ring.end() - 1);
+}
+
+TEST_CASE("RingBuffer - insert in middle (begin) no grow")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6, 7 });
+ ring.pop_front();
+ ring.pop_front();
+ ring.push_back(20);
+ ring.push_back(21);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 20, 21 }));
+ CHECK(ring.capacity() == 8);
+
+ /* Insert closer to beginning, beginning should be shifted backwards */
+ uint32 *pre_begin = &ring[0];
+ uint32 *pre_end = &ring[ring.size() - 1];
+ auto iter = ring.insert(ring.begin() + 2, 10);
+ CHECK(Matches(ring, { 3, 4, 10, 5, 6, 7, 20, 21 }));
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+ CHECK(iter == ring.begin() + 2);
+ CHECK(pre_begin != &ring[0]);
+ CHECK(pre_end == &ring[ring.size() - 1]);
+}
+
+TEST_CASE("RingBuffer - insert in middle (end) no grow")
+{
+ ring_buffer ring({ 1, 2, 3, 4, 5, 6, 7 });
+ ring.pop_front();
+ ring.pop_front();
+ ring.push_back(20);
+ ring.push_back(21);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 20, 21 }));
+ CHECK(ring.capacity() == 8);
+
+ /* Insert closer to end, end should be shifted forwards */
+ uint32 *pre_begin = &ring[0];
+ uint32 *pre_end = &ring[ring.size() - 1];
+ auto iter = ring.insert(ring.begin() + 5, 10);
+ CHECK(Matches(ring, { 3, 4, 5, 6, 7, 10, 20, 21 }));
+ CHECK(ring.size() == 8);
+ CHECK(ring.capacity() == 8);
+ CHECK(iter == ring.begin() + 5);
+ CHECK(pre_begin == &ring[0]);
+ CHECK(pre_end != &ring[ring.size() - 1]);
+}
+
+TEST_CASE("RingBuffer - insert at beginning grow")
+{
+ ring_buffer ring({ 3, 4, 5, 6, 7, 8 });
+ ring.push_front(2);
+ ring.push_front(1);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 8);
+
+ auto iter = ring.insert(ring.begin(), 10);
+ CHECK(Matches(ring, { 10, 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 16);
+ CHECK(iter == ring.begin());
+}
+
+TEST_CASE("RingBuffer - insert at end grow")
+{
+ ring_buffer ring({ 3, 4, 5, 6, 7, 8 });
+ ring.push_front(2);
+ ring.push_front(1);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 8);
+
+ auto iter = ring.insert(ring.end(), 10);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8, 10 }));
+ CHECK(ring.capacity() == 16);
+ CHECK(iter == ring.end() - 1);
+}
+
+TEST_CASE("RingBuffer - insert in middle (begin) grow")
+{
+ ring_buffer ring({ 3, 4, 5, 6, 7, 8 });
+ ring.push_front(2);
+ ring.push_front(1);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 8);
+
+ /* Insert closer to beginning */
+ auto iter = ring.insert(ring.begin() + 2, 10);
+ CHECK(Matches(ring, { 1, 2, 10, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 16);
+ CHECK(iter == ring.begin() + 2);
+}
+
+TEST_CASE("RingBuffer - insert in middle (end) grow")
+{
+ ring_buffer ring({ 3, 4, 5, 6, 7, 8 });
+ ring.push_front(2);
+ ring.push_front(1);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 8);
+
+ /* Insert closer to beginning */
+ auto iter = ring.insert(ring.begin() + 6, 10);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 10, 7, 8 }));
+ CHECK(ring.capacity() == 16);
+ CHECK(iter == ring.begin() + 6);
+}
+
+TEST_CASE("RingBuffer - shrink to fit")
+{
+ ring_buffer ring({ 3, 4, 5, 6, 7, 8 });
+ ring.push_front(2);
+ ring.push_front(1);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 8);
+
+ ring.insert(ring.begin() + 6, 10);
+ ring.insert(ring.begin() + 8, 11);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 10, 7, 11, 8 }));
+ CHECK(ring.capacity() == 16);
+
+ ring.pop_front();
+ ring.pop_back();
+ CHECK(Matches(ring, { 2, 3, 4, 5, 6, 10, 7, 11 }));
+ CHECK(ring.capacity() == 16);
+
+ ring.shrink_to_fit();
+ CHECK(Matches(ring, { 2, 3, 4, 5, 6, 10, 7, 11 }));
+ CHECK(ring.capacity() == 8);
+}
+
+TEST_CASE("RingBuffer - reserve")
+{
+ ring_buffer ring({ 3, 4, 5, 6, 7, 8 });
+ ring.push_front(2);
+ ring.push_front(1);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 8);
+
+ ring.reserve(12);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 16);
+}
+
+TEST_CASE("RingBuffer - resize")
+{
+ ring_buffer ring({ 3, 4, 5, 6, 7, 8 });
+ ring.push_front(2);
+ ring.push_front(1);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8 }));
+ CHECK(ring.capacity() == 8);
+
+ ring.resize(12);
+ CHECK(Matches(ring, { 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0 }));
+ CHECK(ring.capacity() == 16);
+}