diff options
| -rw-r--r-- | libs/kstd/tests/include/kstd/tests/test_types.hpp | 251 | ||||
| -rw-r--r-- | libs/kstd/tests/src/vector.cpp | 199 |
2 files changed, 283 insertions, 167 deletions
diff --git a/libs/kstd/tests/include/kstd/tests/test_types.hpp b/libs/kstd/tests/include/kstd/tests/test_types.hpp new file mode 100644 index 0000000..6a06311 --- /dev/null +++ b/libs/kstd/tests/include/kstd/tests/test_types.hpp @@ -0,0 +1,251 @@ +#ifndef KSTD_TESTS_TEST_TYPES_HPP +#define KSTD_TESTS_TEST_TYPES_HPP + +#include <cstddef> +#include <iterator> +#include <type_traits> + +namespace kstd::tests +{ + + //! A type tracking copy and move operations + //! + //! This type is designed to test move and copy semantics of standard library containers implemented in kstd. + struct move_tracker + { + //! A value indicating that the object was moved from. + constexpr auto static moved_from_v = -1; + + //! A simple value to be able to track the move-from state. + int value{}; + //! A flag to track if an instance of this type was either copy constructed or copy assigned. + bool was_copied{false}; + //! A flag to track if an instance of this type was either move constructed or move assigned. + bool was_moved{false}; + + //! Construct a new move tracker with the given value, if any. + constexpr move_tracker(int v = 0) + : value{v} + {} + + //! Construct a new move tracker by copying an existing one. + constexpr move_tracker(move_tracker const & other) + : value{other.value} + , was_copied{true} + {} + + //! Construct a new move tracker by moving from an existing one. + constexpr move_tracker(move_tracker && other) noexcept + : value{other.value} + , was_moved{true} + { + other.value = moved_from_v; + } + + //! Copy assign a new move tracker from an existing one. + constexpr auto operator=(move_tracker const & other) -> move_tracker & + { + if (this != &other) + { + value = other.value; + was_copied = true; + } + return *this; + } + + //! Move assign a new move tracker from an existing one. + //! + //! This function ensures that the moved-from state is marked. + constexpr auto operator=(move_tracker && other) noexcept -> move_tracker & + { + if (this != &other) + { + value = other.value; + was_moved = true; + other.value = moved_from_v; + } + return *this; + } + }; + + //! A type that is not default constructible. + //! + //! This type is designed to test default construction semantics of standard library containers implemented in kstd. + struct non_default_constructible + { + //! A simple placeholder value. + int value{}; + + //! Construct a new non-default-constructible object with the given value. + constexpr explicit non_default_constructible(int v) + : value{v} + {} + + //! Compare two non-default-constructible objects for equality. + [[nodiscard]] constexpr auto operator==(non_default_constructible const & other) const -> bool + { + return value == other.value; + } + }; + + //! An allocator that tracks the number of allocations. + //! + //! This allocator is designed to test allocation semantics of standard library containers implemented in kstd. + //! + //! @tparam T The type of the elements to be allocated. + template<typename T> + struct tracking_allocator + { + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + //! A pointer to a counter that is incremented on allocation. + //! + //! A pointer is used so that multiple allocators can share the same counter. + int * allocation_count{}; + + //! Construct a new tracking allocator referencing the given counter. + tracking_allocator(int * counter) + : allocation_count{counter} + {} + + //! Construct a new tracking allocator by copying from another tracking allocator. + //! + //! This constructor is templated to allow for conversion from allocators of different types. + //! + //! @tparam U The type of the elements to be allocated by the other allocator. + template<typename U> + tracking_allocator(tracking_allocator<U> const & other) noexcept + : allocation_count{other.allocation_count} + {} + + //! Allocate memory for n elements of type T. + //! + //! @param n The number of elements to allocate. + //! @return A pointer to the allocated memory. + [[nodiscard]] auto allocate(std::size_t n) -> T * + { + if (allocation_count != nullptr) + { + ++(*allocation_count); + } + return static_cast<T *>(::operator new(n * sizeof(T))); + } + + //! Deallocate memory for n elements of type T. + //! + //! @param p A pointer to the memory to deallocate. + //! @param n The number of elements to deallocate. + auto deallocate(T * p, std::size_t n) noexcept -> void + { + ::operator delete(p, n * sizeof(T)); + } + + //! Compare two tracking allocators for equality. + //! + //! Two allocators are considered equal if they reference the same allocation counter. + //! + //! @param other The other tracking allocator to compare to. + //! @return True if the two tracking allocators are equal, false otherwise. + [[nodiscard]] auto operator==(tracking_allocator const & other) const -> bool + { + return allocation_count == other.allocation_count; + } + + //! Compare two tracking_allocators for inequality. + //! + //! @param other The other tracking_allocator to compare to. + //! @return True if the two tracking_allocators are not equal, false otherwise. + [[nodiscard]] auto operator!=(tracking_allocator const & other) const -> bool + { + return allocation_count != other.allocation_count; + } + }; + + //! An allocator that propagates copy assignment. + //! + //! This allocator is designed to test copy assignment semantics of standard library containers implemented in kstd. + //! + //! @tparam T The type of the elements to be allocated. + template<typename T> + struct propagating_allocator : tracking_allocator<T> + { + //! A flag to indicate that the allocator propagates copy assignment. + using propagate_on_container_copy_assignment = std::true_type; + + //! Construct a new propagating allocator referencing the given counter. + //! + //! @param counter A pointer to a counter that is incremented on allocation. + //! @see tracking_allocator::tracking_allocator(int*) + propagating_allocator(int * counter) + : tracking_allocator<T>{counter} + {} + + //! Construct a new propagating allocator by copying from another propagating allocator. + //! + //! This constructor is templated to allow for conversion from allocators of different types. + //! + //! @tparam U The type of the elements to be allocated by the other allocator. + //! @see tracking_allocator::tracking_allocator(tracking_allocator<U> const&) + template<typename U> + propagating_allocator(propagating_allocator<U> const & other) noexcept + : tracking_allocator<T>{other.allocation_count} + {} + }; + + //! A test input iterator. + //! + //! This iterator is designed to test input iterator semantics of standard library containers implemented in kstd. + struct test_input_iterator + { + using iterator_concept = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = int; + + //! The current element pointed to by the iterator. + int const * current; + + //! Construct a new test input iterator. + //! + //! @param current The current element pointed to by the iterator. + constexpr explicit test_input_iterator(int const * current) + : current{current} + {} + + //! Dereference the iterator to get the current element. + //! + //! @return The current element pointed to by the iterator. + [[nodiscard]] auto operator*() const -> int + { + return *current; + } + + //! Increment the iterator to point to the next element. + //! + //! @return A reference to the incremented iterator. + auto operator++() -> test_input_iterator & + { + ++current; + return *this; + } + + //! Increment the iterator to point to the next element. + auto operator++(int) -> void + { + ++*this; + } + + //! Compare two test input iterators for equality. + //! + //! @param other The other test input iterator to compare to. + //! @return True if the two test input iterators are equal, false otherwise. + [[nodiscard]] auto operator==(test_input_iterator const & other) const -> bool + { + return current == other.current; + } + }; + +} // namespace kstd::tests + +#endif diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 713f6ab..d4c5e4f 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -1,12 +1,12 @@ #include "kstd/tests/os_panic.hpp" #include <kstd/ranges> +#include <kstd/tests/test_types.hpp> #include <kstd/vector> #include <catch2/catch_test_macros.hpp> #include <array> -#include <cstddef> #include <iterator> #include <ranges> #include <utility> @@ -616,24 +616,13 @@ SCENARIO("Vector comparison", "[vector]") } } -struct non_default_constructible -{ - int value; - non_default_constructible() = delete; - explicit non_default_constructible(int v) - : value{v} - {} - - bool operator==(non_default_constructible const & other) const noexcept = default; -}; - SCENARIO("Vector with non-default-constructible types", "[vector]") { GIVEN("A type without a default constructor") { WHEN("constructing an empty vector") { - auto v = kstd::vector<non_default_constructible>{}; + auto v = kstd::vector<kstd::tests::non_default_constructible>{}; THEN("the vector is empty") { @@ -643,83 +632,28 @@ SCENARIO("Vector with non-default-constructible types", "[vector]") WHEN("using emplace_back") { - auto v = kstd::vector<non_default_constructible>{}; + auto v = kstd::vector<kstd::tests::non_default_constructible>{}; v.emplace_back(42); THEN("the element is added and the size and capacity increase") { REQUIRE(v.size() == 1); - REQUIRE(v.back() == non_default_constructible{42}); + REQUIRE(v.back() == kstd::tests::non_default_constructible{42}); } } } } -template<typename T> -struct tracking_allocator -{ - using value_type = T; - - int * allocation_count; - - tracking_allocator(int * counter) - : allocation_count{counter} - {} - - template<typename U> - tracking_allocator(tracking_allocator<U> const & other) noexcept - : allocation_count{other.allocation_count} - {} - - T * allocate(std::size_t n) - { - if (allocation_count) - { - (*allocation_count)++; - } - return static_cast<T *>(::operator new(n * sizeof(T))); - } - - void deallocate(T * p, std::size_t n) noexcept - { - ::operator delete(p, n * sizeof(T)); - } - - bool operator==(tracking_allocator const & other) const - { - return allocation_count == other.allocation_count; - } - - bool operator!=(tracking_allocator const & other) const - { - return allocation_count != other.allocation_count; - } -}; - -template<typename T> -struct propagating_allocator : tracking_allocator<T> -{ - using propagate_on_container_copy_assignment = std::true_type; - propagating_allocator(int * counter) - : tracking_allocator<T>{counter} - {} - - template<typename U> - propagating_allocator(propagating_allocator<U> const & other) noexcept - : tracking_allocator<T>{other.allocation_count} - {} -}; - SCENARIO("Vector with custom allocator", "[vector]") { GIVEN("a tracking allocator acting as the vector's memory manager") { auto allocations = 0; - auto allocator = tracking_allocator<int>{&allocations}; + auto allocator = kstd::tests::tracking_allocator<int>{&allocations}; WHEN("a vector uses this allocator to allocate memory") { - auto v = kstd::vector<int, tracking_allocator<int>>(allocator); + auto v = kstd::vector<int, kstd::tests::tracking_allocator<int>>(allocator); REQUIRE(allocations == 0); v.reserve(10); @@ -732,55 +666,12 @@ SCENARIO("Vector with custom allocator", "[vector]") } } -struct move_tracker -{ - int value; - bool was_copied{}; - bool was_moved{}; - - move_tracker() = default; - explicit move_tracker(int v) - : value{v} - {} - - move_tracker(move_tracker const & other) - : value{other.value} - , was_copied{true} - , was_moved{false} - {} - - move_tracker(move_tracker && other) noexcept - : value{other.value} - , was_copied{false} - , was_moved{true} - { - other.value = -1; - } - - move_tracker & operator=(move_tracker const & other) - { - value = other.value; - was_copied = true; - was_moved = false; - return *this; - } - - move_tracker & operator=(move_tracker && other) noexcept - { - value = other.value; - was_moved = true; - was_copied = false; - other.value = -1; - return *this; - } -}; - SCENARIO("Vector modifier move semantics", "[vector]") { GIVEN("An empty vector and a move tracker element") { - auto v = kstd::vector<move_tracker>{}; - auto tracker = move_tracker{42}; + auto v = kstd::vector<kstd::tests::move_tracker>{}; + auto tracker = kstd::tests::move_tracker{42}; WHEN("push_back is called with the move tracker") { @@ -803,7 +694,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") GIVEN("An empty vector") { - auto v = kstd::vector<move_tracker>{}; + auto v = kstd::vector<kstd::tests::move_tracker>{}; WHEN("emplace_back is called with constructor arguments") { @@ -820,32 +711,6 @@ SCENARIO("Vector modifier move semantics", "[vector]") } } -struct test_input_iterator -{ - using iterator_concept = std::input_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = int; - - int const * current; - int operator*() const - { - return *current; - } - test_input_iterator & operator++() - { - ++current; - return *this; - } - void operator++(int) - { - ++current; - } - bool operator==(test_input_iterator const & other) const - { - return current == other.current; - } -}; - SCENARIO("Vector advanced construction", "[vector]") { GIVEN("A count and a default value") @@ -871,8 +736,8 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("constructing from input iterators") { auto const arr = std::array<int, 3>{1, 2, 3}; - auto const first = test_input_iterator{arr.data()}; - auto const last = test_input_iterator{arr.data() + arr.size()}; + auto const first = kstd::tests::test_input_iterator{arr.data()}; + auto const last = kstd::tests::test_input_iterator{arr.data() + arr.size()}; auto v = kstd::vector<int>(first, last); @@ -888,8 +753,8 @@ SCENARIO("Vector advanced construction", "[vector]") GIVEN("A tracking allocator and a source vector") { auto allocs = 0; - auto allocator = tracking_allocator<int>{&allocs}; - auto source = kstd::vector<int, tracking_allocator<int>>{allocator}; + auto allocator = kstd::tests::tracking_allocator<int>{&allocs}; + auto source = kstd::vector<int, kstd::tests::tracking_allocator<int>>{allocator}; source.push_back(1); source.push_back(2); source.push_back(3); @@ -898,7 +763,7 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("copy constructing with an allocator") { - auto copy = kstd::vector<int, tracking_allocator<int>>(source, allocator); + auto copy = kstd::vector<int, kstd::tests::tracking_allocator<int>>(source, allocator); THEN("the copy succeeds and the allocator is used") { @@ -911,7 +776,7 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("move constructing with an identically comparing allocator") { - auto moved = kstd::vector<int, tracking_allocator<int>>(std::move(source), allocator); + auto moved = kstd::vector<int, kstd::tests::tracking_allocator<int>>(std::move(source), allocator); THEN("the move succeeds and no new allocations are made (memory is stolen)") { @@ -925,9 +790,9 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("move constructing with a non-equal allocator") { auto allocs2 = 0; - auto allocator2 = tracking_allocator<int>{&allocs2}; + auto allocator2 = kstd::tests::tracking_allocator<int>{&allocs2}; - auto moved = kstd::vector<int, tracking_allocator<int>>(std::move(source), allocator2); + auto moved = kstd::vector<int, kstd::tests::tracking_allocator<int>>(std::move(source), allocator2); THEN("the move allocates new memory and moves elements") { @@ -977,13 +842,13 @@ SCENARIO("Vector assignment operators", "[vector]") { auto allocs1 = 0; auto allocs2 = 0; - auto alloc1 = propagating_allocator<int>{&allocs1}; - auto alloc2 = propagating_allocator<int>{&allocs2}; + auto alloc1 = kstd::tests::propagating_allocator<int>{&allocs1}; + auto alloc2 = kstd::tests::propagating_allocator<int>{&allocs2}; - auto v1 = kstd::vector<int, propagating_allocator<int>>{alloc1}; + auto v1 = kstd::vector<int, kstd::tests::propagating_allocator<int>>{alloc1}; v1.push_back(1); v1.push_back(2); - auto v2 = kstd::vector<int, propagating_allocator<int>>{alloc2}; + auto v2 = kstd::vector<int, kstd::tests::propagating_allocator<int>>{alloc2}; WHEN("copy assigning") { @@ -1028,10 +893,10 @@ SCENARIO("Vector assignment operators", "[vector]") GIVEN("Vectors with the same tracking allocator") { auto allocs = 0; - auto alloc = tracking_allocator<int>{&allocs}; - auto v1 = kstd::vector<int, tracking_allocator<int>>{alloc}; + auto alloc = kstd::tests::tracking_allocator<int>{&allocs}; + auto v1 = kstd::vector<int, kstd::tests::tracking_allocator<int>>{alloc}; v1.push_back(1); - auto v2 = kstd::vector<int, tracking_allocator<int>>{alloc}; + auto v2 = kstd::vector<int, kstd::tests::tracking_allocator<int>>{alloc}; WHEN("move assigning") { @@ -1048,15 +913,15 @@ SCENARIO("Vector assignment operators", "[vector]") { auto allocs1 = 0; auto allocs2 = 0; - auto alloc1 = tracking_allocator<int>{&allocs1}; - auto alloc2 = tracking_allocator<int>{&allocs2}; + auto alloc1 = kstd::tests::tracking_allocator<int>{&allocs1}; + auto alloc2 = kstd::tests::tracking_allocator<int>{&allocs2}; - auto v1 = kstd::vector<int, tracking_allocator<int>>{alloc1}; + auto v1 = kstd::vector<int, kstd::tests::tracking_allocator<int>>{alloc1}; v1.push_back(1); v1.push_back(2); v1.push_back(3); - auto v2 = kstd::vector<int, tracking_allocator<int>>{alloc2}; + auto v2 = kstd::vector<int, kstd::tests::tracking_allocator<int>>{alloc2}; v2.push_back(4); v2.push_back(5); @@ -1071,7 +936,7 @@ SCENARIO("Vector assignment operators", "[vector]") } } - auto v3 = kstd::vector<int, tracking_allocator<int>>{alloc2}; + auto v3 = kstd::vector<int, kstd::tests::tracking_allocator<int>>{alloc2}; v3.reserve(10); v3.push_back(4); v3.push_back(5); @@ -1084,7 +949,7 @@ SCENARIO("Vector assignment operators", "[vector]") } } - auto v4 = kstd::vector<int, tracking_allocator<int>>{alloc2}; + auto v4 = kstd::vector<int, kstd::tests::tracking_allocator<int>>{alloc2}; v4.reserve(10); v4.push_back(4); v4.push_back(5); @@ -1169,8 +1034,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]") GIVEN("An empty vector and a const lvalue tracker") { - auto v = kstd::vector<move_tracker>{}; - auto const tracker = move_tracker{42}; + auto v = kstd::vector<kstd::tests::move_tracker>{}; + auto const tracker = kstd::tests::move_tracker{42}; WHEN("push_back is called with the const lvalue") { |
