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); +}