From dbf7c77917cdeb3c55eb4de377202c8a6fa04ae0 Mon Sep 17 00:00:00 2001 From: Yggdrasill Date: Thu, 3 Apr 2025 10:16:38 +0100 Subject: [PATCH] Left shift in StaticVector::erase(), add tests (#7879) --- Source/utils/static_vector.hpp | 18 +++- test/CMakeLists.txt | 1 + test/staticvector_test.cpp | 168 +++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 test/staticvector_test.cpp diff --git a/Source/utils/static_vector.hpp b/Source/utils/static_vector.hpp index b57fcf8af..12262819f 100644 --- a/Source/utils/static_vector.hpp +++ b/Source/utils/static_vector.hpp @@ -71,12 +71,20 @@ public: const T &operator[](std::size_t pos) const { return *data_[pos].ptr(); } T &operator[](std::size_t pos) { return *data_[pos].ptr(); } - void erase(const T *begin, const T *end) + void erase(const T *first, const T *last) { - for (const T *it = begin; it < end; ++it) { - std::destroy_at(it); - } - size_ -= end - begin; + if (last == first) return; + assert(first >= begin() && last <= end() && first <= last); + const auto count = last - first; + auto tail = std::move(const_cast(last), end(), const_cast(first)); + std::destroy(tail, end()); + size_ -= count; + } + + void erase(const T *element) + { + assert(element >= begin() && element < end()); + erase(element, element + 1); } void pop_back() // NOLINT(readability-identifier-naming) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 35d003fc2..8ece9fa17 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -36,6 +36,7 @@ set(tests tile_properties_test timedemo_test writehero_test + staticvector_test ) set(standalone_tests codec_test diff --git a/test/staticvector_test.cpp b/test/staticvector_test.cpp new file mode 100644 index 000000000..a7394b8c4 --- /dev/null +++ b/test/staticvector_test.cpp @@ -0,0 +1,168 @@ +#include +#include + +#include "engine/random.hpp" +#include "utils/static_vector.hpp" + +using namespace devilution; + +namespace { + +constexpr int MAX_SIZE = 32; + +TEST(StaticVector, StaticVector_push_back) +{ + StaticVector container; + + SetRndSeed(testing::UnitTest::GetInstance()->random_seed()); + size_t size = RandomIntBetween(10, MAX_SIZE); + + for (size_t i = 0; i < size; i++) { + container.push_back(i); + } + + EXPECT_EQ(container.size(), size); + for (size_t i = 0; i < size; i++) { + EXPECT_EQ(container[i], i); + } +} + +TEST(StaticVector, StaticVector_push_back_full) +{ + StaticVector container; + + size_t size = MAX_SIZE; + for (size_t i = 0; i < size; i++) { + container.push_back(i); + } + + EXPECT_EQ(container.size(), MAX_SIZE); + for (size_t i = 0; i < size; i++) { + EXPECT_EQ(container[i], i); + } + +#ifdef _DEBUG + ASSERT_DEATH(container.push_back(size + 1), ""); +#endif +} + +TEST(StaticVector, StaticVector_erase) +{ + StaticVector container; + std::vector expected; + + SetRndSeed(testing::UnitTest::GetInstance()->random_seed()); + +#ifdef _DEBUG + ASSERT_DEATH(container.erase(container.begin()), ""); +#endif + + container = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + expected = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + container.erase(container.begin()); + EXPECT_EQ(container.size(), expected.size()); + for (size_t i = 0; i < container.size(); i++) { + EXPECT_EQ(container[i], expected[i]); + } + + expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + container.erase(container.end() - 1, container.end()); + EXPECT_EQ(container.size(), expected.size()); + for (size_t i = 0; i < container.size(); i++) { + EXPECT_EQ(container[i], expected[i]); + } + + while (!container.empty()) { + size_t idx = RandomIntLessThan(static_cast(container.size())); + container.erase(container.begin() + idx); + expected.erase(expected.begin() + idx); + if (container.size() > 0) { + EXPECT_EQ(container.size(), expected.size()); + idx = idx <= 1 ? 0 : idx - 1; + EXPECT_EQ(container[idx], expected[idx]); + idx = (idx + 1) >= container.size() ? container.size() - 1 : idx + 1; + EXPECT_EQ(container[idx], expected[idx]); + } + } + EXPECT_EQ(container.size(), 0); + +#ifdef _DEBUG + ASSERT_DEATH(container.erase(container.begin(), container.end() + 1), ""); + ASSERT_DEATH(container.erase(container.begin() - 1, container.end()), ""); +#endif +} + +TEST(StaticVector, StaticVector_erase_random) +{ + StaticVector container; + std::vector expected; + + SetRndSeed(testing::UnitTest::GetInstance()->random_seed()); + size_t size = RandomIntBetween(10, MAX_SIZE); + size_t erasures = RandomIntBetween(1, static_cast(size) - 1, true); + + for (size_t i = 0; i < size; i++) { + container.push_back(i); + expected.push_back(i); + } + + while (erasures-- > 0) { + size_t idx = RandomIntLessThan(static_cast(container.size())); + container.erase(container.begin() + idx); + expected.erase(expected.begin() + idx); + } + + EXPECT_EQ(container.size(), expected.size()); + for (size_t i = 0; i < expected.size(); i++) { + EXPECT_EQ(container[i], expected[i]); + } +} + +TEST(StaticVector, StaticVector_erase_range) +{ + StaticVector container; + std::vector erase_idx; + std::vector expected; + + SetRndSeed(testing::UnitTest::GetInstance()->random_seed()); + + container = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + expected = { 3, 4, 5, 6, 7, 8, 9 }; + container.erase(container.begin(), container.begin() + 3); + EXPECT_EQ(container.size(), expected.size()); + for (size_t i = 0; i < container.size(); i++) { + EXPECT_EQ(container[i], expected[i]); + } + + container.erase(container.begin() + 1, container.begin() + 1); + EXPECT_EQ(container.size(), expected.size()); + for (size_t i = 0; i < container.size(); i++) { + EXPECT_EQ(container[i], expected[i]); + } + + int32_t from = RandomIntBetween(0, static_cast(container.size()) - 1); + int32_t to = RandomIntBetween(from + 1, static_cast(container.size())); + container.erase(container.begin() + from, container.begin() + to); + for (int32_t i = to - from; i > 0; i--) { + expected.erase(expected.begin() + from); + } + + EXPECT_EQ(container.size(), expected.size()); + for (size_t i = 0; i < container.size(); i++) { + EXPECT_EQ(container[i], expected[i]); + } +} + +TEST(StaticVector, StaticVector_clear) +{ + StaticVector container; + + container.clear(); + EXPECT_EQ(container.size(), 0); + + container = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + container.clear(); + EXPECT_EQ(container.size(), 0); +} + +} // namespace