aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-03-23 12:48:52 +0100
committerFelix Morgner <felix.morgner@ost.ch>2026-03-23 12:48:52 +0100
commitf7b065d72684526aedd580cb564f6d010653a22e (patch)
tree6993544d049b5d4d38ae859609f62019602e1ec5
parent3b5f694af4c366e6e699800d9b2fbcb977f1612d (diff)
downloadteachos-f7b065d72684526aedd580cb564f6d010653a22e.tar.xz
teachos-f7b065d72684526aedd580cb564f6d010653a22e.zip
kstd/tests: extract test helper types
-rw-r--r--libs/kstd/tests/include/kstd/tests/test_types.hpp251
-rw-r--r--libs/kstd/tests/src/vector.cpp199
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")
{