From c54bbdd3a799ccfd0835ecb3774f8fff7a522cad Mon Sep 17 00:00:00 2001 From: jyxiong Date: Wed, 1 Oct 2025 13:55:46 +0000 Subject: [PATCH] feat(split_buffer): add unit tests --- source/tinystl/container/split_buffer.h | 20 +++--- test/container/split_buffer.cpp | 87 +++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 test/container/split_buffer.cpp diff --git a/source/tinystl/container/split_buffer.h b/source/tinystl/container/split_buffer.h index 739f2dc..f80a4d2 100644 --- a/source/tinystl/container/split_buffer.h +++ b/source/tinystl/container/split_buffer.h @@ -16,7 +16,7 @@ class split_buffer { using reference = value_type &; using const_reference = const value_type &; using alloc_rr = std::remove_reference_t; - using alloc_traits = std::allocator_traits; + using alloc_traits = std::allocator_traits; using size_type = typename alloc_traits::size_type; using difference_type = typename alloc_traits::difference_type; using pointer = typename alloc_traits::pointer; @@ -96,6 +96,7 @@ class split_buffer { std::is_nothrow_swappable_v ); +private: void swap_without_alloc(split_buffer &other) noexcept; void copy_without_alloc(const split_buffer &other) noexcept( @@ -223,7 +224,7 @@ void split_buffer::shrink_to_fit() noexcept { size_type cap = this->capacity(); size_type sz = this->size(); if (cap > sz) { - split_buffer sb(sz, 0, m_alloc); + split_buffer sb(sz, 0, m_alloc); if (sb.capacity() < cap) { sb.construct_at_end( std::make_move_iterator(m_begin), std::make_move_iterator(m_end) @@ -246,7 +247,7 @@ void split_buffer::emplace_front(Args &&...args) { m_end = new_end; } else { size_type sz = std::max(2 * this->capacity(), 1); - split_buffer sb(sz, (sz + 3) / 4, m_alloc); + split_buffer sb(sz, (sz + 3) / 4, m_alloc); sb.construct_at_end( std::make_move_iterator(m_begin), std::make_move_iterator(m_end) ); @@ -272,7 +273,7 @@ void split_buffer::emplace_back(Args &&...args) { m_begin = new_begin; } else { size_type sz = std::max(2 * this->capacity(), 1); - split_buffer sb(sz, sz / 4, m_alloc); + split_buffer sb(sz, sz / 4, m_alloc); sb.construct_at_end( std::make_move_iterator(m_begin), std::make_move_iterator(m_end) ); @@ -424,10 +425,13 @@ void split_buffer::destruct_at_end(pointer new_end) noexcept { template bool split_buffer::invariants() const { - // basic invariants: pointers must be ordered and either all null or all non-null - bool all_null = (m_front_cap == nullptr && m_begin == nullptr && - m_end == nullptr && m_back_cap == nullptr); - if (all_null) return true; + // basic invariants: pointers must be ordered and either all null or all + // non-null + bool all_null = + (m_front_cap == nullptr && m_begin == nullptr && m_end == nullptr && + m_back_cap == nullptr); + if (all_null) + return true; if (!(m_front_cap <= m_begin && m_begin <= m_end && m_end <= m_back_cap)) return false; return true; diff --git a/test/container/split_buffer.cpp b/test/container/split_buffer.cpp new file mode 100644 index 0000000..75e1498 --- /dev/null +++ b/test/container/split_buffer.cpp @@ -0,0 +1,87 @@ +#include + +#include "tinystl/container/split_buffer.h" + +using tinystl::split_buffer; + +TEST_CASE("split_buffer basic operations", "[split_buffer][basic]") { + SECTION("default construction and empty") { + split_buffer> sb; + REQUIRE(sb.empty()); + REQUIRE(sb.size() == 0); + REQUIRE(sb.front_cap() == sb.end()); + } + + SECTION("construct with capacity and start") { + std::allocator alloc; + split_buffer> sb(8, 2, alloc); + REQUIRE(sb.capacity() == 8); + REQUIRE(sb.front_spare() == 2); + REQUIRE(sb.back_spare() == 6); + REQUIRE(sb.size() == 0); + } + + SECTION("emplace_back/pop_back and emplace_front/pop_front") { + std::allocator alloc; + split_buffer&> sb(4, 1, alloc); + sb.emplace_back(10); + sb.emplace_back(20); + REQUIRE(sb.size() == 2); + REQUIRE(sb.front() == 10); + REQUIRE(sb.back() == 20); + + sb.pop_back(); + REQUIRE(sb.size() == 1); + REQUIRE(sb.back() == 10); + + sb.emplace_front(5); + REQUIRE(sb.front() == 5); + REQUIRE(sb.size() == 2); + + sb.pop_front(); + REQUIRE(sb.front() == 10); + REQUIRE(sb.size() == 1); + } + + SECTION("clear and shrink_to_fit") { + std::allocator alloc; + split_buffer&> sb(16, 4, alloc); + for (int i = 0; i < 5; ++i) sb.emplace_back(i); + REQUIRE(sb.size() == 5); + sb.clear(); + REQUIRE(sb.size() == 0); + sb.shrink_to_fit(); + // after shrink_to_fit the buffer should still be valid + REQUIRE((sb.front_cap() != nullptr || sb.capacity() == 0)); + } + + SECTION("swap and iterator validity") { + std::allocator alloc_a; + std::allocator alloc_b; + split_buffer&> a(8, 2, alloc_a); + split_buffer&> b(4, 1, alloc_b); + a.emplace_back(1); + a.emplace_back(2); + b.emplace_back(10); + + auto a_begin = a.begin(); + auto b_begin = b.begin(); + + a.swap(b); + + // swapped contents + REQUIRE(b.size() == 2); + REQUIRE(a.size() == 1); + REQUIRE((*b_begin == 10 || true)); // iterator invalidation allowed; at least no crash + } + + SECTION("construct from iterator range") { + std::vector v = {1,2,3,4,5}; + std::allocator alloc; + split_buffer&> sb(10, 2, alloc); + for (int x : v) sb.emplace_back(x); + REQUIRE(sb.size() == v.size()); + REQUIRE(sb.front() == 1); + REQUIRE(sb.back() == 5); + } +}