aboutsummaryrefslogtreecommitdiff
path: root/libs/kstd/tests
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-05-02 17:12:21 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-05-02 17:12:21 +0200
commit3ab0a15fb6aba0ad9516da69589b9da8dbd63a8e (patch)
treee79c1c0911c3b712a1b02c79d84f0ce5f643bc6c /libs/kstd/tests
parente8904a65e49dea8ce2e1d58eaa3fb60c7cd3443e (diff)
downloadkernel-3ab0a15fb6aba0ad9516da69589b9da8dbd63a8e.tar.xz
kernel-3ab0a15fb6aba0ad9516da69589b9da8dbd63a8e.zip
libs: adopt p1204 layout for kstd
Diffstat (limited to 'libs/kstd/tests')
-rw-r--r--libs/kstd/tests/include/kstd/tests/os_panic.hpp23
-rw-r--r--libs/kstd/tests/include/kstd/tests/test_types.hpp313
-rw-r--r--libs/kstd/tests/src/flat_map.cpp314
-rw-r--r--libs/kstd/tests/src/format.cpp114
-rw-r--r--libs/kstd/tests/src/observer_ptr.cpp360
-rw-r--r--libs/kstd/tests/src/os_panic.cpp15
-rw-r--r--libs/kstd/tests/src/string.cpp445
-rw-r--r--libs/kstd/tests/src/vector.cpp2003
8 files changed, 0 insertions, 3587 deletions
diff --git a/libs/kstd/tests/include/kstd/tests/os_panic.hpp b/libs/kstd/tests/include/kstd/tests/os_panic.hpp
deleted file mode 100644
index 4396a9f..0000000
--- a/libs/kstd/tests/include/kstd/tests/os_panic.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef KSTD_TESTS_OS_PANIC_HPP
-#define KSTD_TESTS_OS_PANIC_HPP
-
-#include <source_location>
-#include <stdexcept>
-#include <string>
-
-namespace kstd::tests
-{
-
- struct os_panic : std::runtime_error
- {
- os_panic(std::string message, std::source_location location)
- : std::runtime_error{message}
- , location(location)
- {}
-
- std::source_location location;
- };
-
-} // namespace kstd::tests
-
-#endif \ No newline at end of file
diff --git a/libs/kstd/tests/include/kstd/tests/test_types.hpp b/libs/kstd/tests/include/kstd/tests/test_types.hpp
deleted file mode 100644
index 8231fd3..0000000
--- a/libs/kstd/tests/include/kstd/tests/test_types.hpp
+++ /dev/null
@@ -1,313 +0,0 @@
-#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 special_member_tracker
- {
- //! A value indicating that the object was moved from.
- constexpr auto static moved_from_v = -1;
-
- constexpr special_member_tracker()
- : default_constructed_count{1}
- {}
-
- //! Construct a new move tracker with the given value, if any.
- constexpr special_member_tracker(int v = 0)
- : value{v}
- , value_constructed_count{1}
- {}
-
- //! Construct a new move tracker by copying an existing one.
- constexpr special_member_tracker(special_member_tracker const & other)
- : value{other.value}
- , copy_constructed_count{1}
- {}
-
- //! Construct a new move tracker by moving from an existing one.
- constexpr special_member_tracker(special_member_tracker && other) noexcept
- : value{other.value}
- , move_constructed_count{1}
- {
- other.value = moved_from_v;
- }
-
- //! Copy assign a new move tracker from an existing one.
- constexpr auto operator=(special_member_tracker const & other) -> special_member_tracker &
- {
- if (this != &other)
- {
- value = other.value;
- ++copy_assigned_count;
- ++other.copied_from_count;
- }
- 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=(special_member_tracker && other) noexcept -> special_member_tracker &
- {
- if (this != &other)
- {
- value = other.value;
- ++move_assigned_count;
- other.value = moved_from_v;
- ++other.moved_from_count;
- }
- return *this;
- }
-
- ~special_member_tracker()
- {
- ++destroyed_count;
- }
-
- auto reset_counts() -> void
- {
- default_constructed_count = 0;
- copy_constructed_count = 0;
- move_constructed_count = 0;
- value_constructed_count = 0;
- copy_assigned_count = 0;
- move_assigned_count = 0;
- destroyed_count = 0;
- copied_from_count = 0;
- moved_from_count = 0;
- }
-
- //! A simple value to be able to track the move-from state.
- int value{};
- //! A counter to track how many times an instance of this type was default constructed.
- std::size_t default_constructed_count{0};
- //! A counter to track how many times an instance of this type was copy constructed.
- std::size_t copy_constructed_count{0};
- //! A counter to track how many times an instance of this type was move constructed.
- std::size_t move_constructed_count{0};
- //! A counter to track how many times an instance of this type was value constructed.
- std::size_t value_constructed_count{0};
- //! A counter to track how many times an instance of this type was copy assigned.
- std::size_t copy_assigned_count{0};
- //! A counter to track how many times an instance of this type was move assigned.
- std::size_t move_assigned_count{0};
- //! A counter to track how many times an instance of this type was destroyed.
- std::size_t destroyed_count{0};
- //! A counter to track how many times an instance of this type was copied from another instance.
- mutable std::size_t copied_from_count{0};
- //! A counter to track how many times an instance of this type was moved from another instance.
- std::size_t moved_from_count{0};
- };
-
- //! 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 iterator_category = std::input_iterator_tag;
- using difference_type = std::ptrdiff_t;
- using value_type = int;
- using reference = int const &;
- using pointer = int const *;
-
- //! The current element pointed to by the iterator.
- int const * current;
- //! The number of elements in the range.
- std::size_t count;
-
- explicit test_input_iterator()
- : current{nullptr}
- , count{0}
- {}
-
- //! Construct a new test input iterator.
- //!
- //! @param current The current element pointed to by the iterator.
- //! @param count The number of elements in the range.
- explicit test_input_iterator(int const * current, std::size_t count)
- : current{current}
- , count{count}
- {}
-
- //! 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;
- --count;
- 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
- {
- if (current == nullptr && other.current == nullptr)
- {
- return true;
- }
-
- if (current == nullptr || other.current == nullptr)
- {
- return count == other.count;
- }
-
- return current == other.current && count == other.count;
- }
- };
-
-} // namespace kstd::tests
-
-#endif
diff --git a/libs/kstd/tests/src/flat_map.cpp b/libs/kstd/tests/src/flat_map.cpp
deleted file mode 100644
index 2b793d9..0000000
--- a/libs/kstd/tests/src/flat_map.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-#include <kstd/flat_map>
-
-#include <kstd/tests/os_panic.hpp>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <functional>
-#include <type_traits>
-#include <utility>
-
-SCENARIO("Flat Map initialization and construction", "[flat_map]")
-{
- GIVEN("An empty context")
- {
- WHEN("constructing by default")
- {
- auto map = kstd::flat_map<int, int>{};
-
- THEN("the Flat Map does not contain elements")
- {
- REQUIRE_FALSE(map.contains(1));
- }
- }
- }
-}
-
-SCENARIO("Flat Map modifiers", "[flat_map]")
-{
- GIVEN("An empty Flat Map")
- {
- auto map = kstd::flat_map<int, int>{};
-
- WHEN("emplacing a new element")
- {
- auto [it, inserted] = map.emplace(1, 100);
-
- THEN("the map contains the new element")
- {
- REQUIRE(inserted);
- REQUIRE(map.contains(1));
- }
- }
-
- WHEN("emplacing an existing element")
- {
- map.emplace(1, 100);
- auto [it, inserted] = map.emplace(1, 200);
-
- THEN("the map does not insert the duplicate")
- {
- REQUIRE_FALSE(inserted);
- REQUIRE(map.contains(1));
- }
- }
- }
-}
-
-SCENARIO("Flat Map element access", "[flat_map]")
-{
- GIVEN("A populated Flat Map")
- {
- auto map = kstd::flat_map<int, int>{};
- map.emplace(1, 10);
- map.emplace(2, 20);
- map.emplace(3, 30);
-
- WHEN("accessing an existing element with at()")
- {
- auto & val = map.at(2);
-
- THEN("it returns a reference to the mapped value")
- {
- REQUIRE(val == 20);
- }
-
- THEN("the mapped value can be modified")
- {
- val = 200;
- REQUIRE(map.at(2) == 200);
- }
- }
-
- WHEN("accessing a non-existent element with at()")
- {
- THEN("it panics")
- {
- REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic);
- }
- }
- }
-
- GIVEN("A const populated Flat Map")
- {
- auto map_builder = kstd::flat_map<int, int>{};
- map_builder.emplace(1, 10);
- map_builder.emplace(2, 20);
- auto const map = map_builder;
-
- WHEN("accessing an existing element with const at()")
- {
- auto const & val = map.at(2);
-
- THEN("it returns a const reference to the mapped value")
- {
- REQUIRE(val == 20);
- }
- }
-
- WHEN("accessing a non-existent element with const at()")
- {
- THEN("it panics")
- {
- REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic);
- }
- }
- }
-}
-
-SCENARIO("Flat Map iterators", "[flat_map]")
-{
- GIVEN("A populated Flat Map")
- {
- auto map = kstd::flat_map<int, int>{};
- map.emplace(1, 10);
- map.emplace(2, 20);
- map.emplace(3, 30);
-
- WHEN("using forward iterators")
- {
- THEN("they navigate the elements in the correct forward order")
- {
- auto it = map.begin();
- REQUIRE(it != map.end());
- REQUIRE((*it).first == 1);
-
- ++it;
- REQUIRE(it != map.end());
- REQUIRE((*it).first == 2);
-
- ++it;
- REQUIRE(it != map.end());
- REQUIRE((*it).first == 3);
-
- ++it;
- REQUIRE(it == map.end());
- }
-
- THEN("const forward iterators provide correct access")
- {
- auto it = map.cbegin();
- REQUIRE(it != map.cend());
- REQUIRE((*it).first == 1);
-
- ++it;
- REQUIRE(it != map.cend());
- REQUIRE((*it).first == 2);
-
- ++it;
- REQUIRE(it != map.cend());
- REQUIRE((*it).first == 3);
-
- ++it;
- REQUIRE(it == map.cend());
- }
-
- THEN("assignment through the proxy modifies the mapped value")
- {
- auto it = map.begin();
-
- *it = std::pair{1, 100};
-
- REQUIRE(it->second == 100);
- REQUIRE(map.at(1) == 100);
- }
-
- THEN("structured bindings evaluate correctly")
- {
- auto it = map.cbegin();
-
- auto [key, value] = *it;
-
- REQUIRE(key == 1);
- REQUIRE(value == 10);
-
- STATIC_REQUIRE(std::is_same_v<decltype(key), int const &>);
- STATIC_REQUIRE(std::is_same_v<decltype(value), int const &>);
- }
- }
-
- WHEN("using reverse iterators")
- {
- THEN("they navigate the elements in the correct reverse order")
- {
- auto it = map.rbegin();
- REQUIRE(it != map.rend());
- REQUIRE((*it).first == 3);
-
- ++it;
- REQUIRE(it != map.rend());
- REQUIRE((*it).first == 2);
-
- ++it;
- REQUIRE(it != map.rend());
- REQUIRE((*it).first == 1);
-
- ++it;
- REQUIRE(it == map.rend());
- }
-
- THEN("const reverse iterators provide correct access")
- {
- auto it = map.crbegin();
- REQUIRE(it != map.crend());
- REQUIRE((*it).first == 3);
-
- ++it;
- REQUIRE(it != map.crend());
- REQUIRE((*it).first == 2);
-
- ++it;
- REQUIRE(it != map.crend());
- REQUIRE((*it).first == 1);
-
- ++it;
- REQUIRE(it == map.crend());
- }
- }
- }
-
- GIVEN("an empty Flat Map")
- {
- auto map = kstd::flat_map<int, int>{};
-
- WHEN("getting iterators")
- {
- THEN("begin() equals end() and cbegin() equals cend()")
- {
- REQUIRE(map.begin() == map.end());
- REQUIRE(map.cbegin() == map.cend());
- }
-
- THEN("rbegin() equals rend() and crbegin() equals crend()")
- {
- REQUIRE(map.rbegin() == map.rend());
- REQUIRE(map.crbegin() == map.crend());
- }
- }
- }
-}
-
-SCENARIO("Flat Map heterogeneous element access", "[flat_map]")
-{
- GIVEN("A populated Flat Map with a transparent comparator")
- {
- auto map = kstd::flat_map<int, int, std::less<void>>{};
- map.emplace(1, 10);
- map.emplace(2, 20);
- map.emplace(3, 30);
-
- WHEN("accessing an existing element with a different key type via at()")
- {
- long const key = 2L;
- auto & val = map.at(key);
-
- THEN("it returns a reference to the mapped value")
- {
- REQUIRE(val == 20);
- }
-
- THEN("the mapped value can be modified")
- {
- val = 200;
- REQUIRE(map.at(2L) == 200);
- }
- }
-
- WHEN("accessing a non-existent element with a different key type via at()")
- {
- long const key = 4L;
- THEN("it panics")
- {
- REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic);
- }
- }
- }
-
- GIVEN("A const populated Flat Map with a transparent comparator")
- {
- auto map_builder = kstd::flat_map<int, int, std::less<void>>{};
- map_builder.emplace(1, 10);
- map_builder.emplace(2, 20);
- auto const map = map_builder;
-
- WHEN("accessing an existing element with a different key type via const at()")
- {
- long const key = 2L;
- auto const & val = map.at(key);
-
- THEN("it returns a const reference to the mapped value")
- {
- REQUIRE(val == 20);
- }
- }
-
- WHEN("accessing a non-existent element with a different key type via const at()")
- {
- long const key = 4L;
- THEN("it panics")
- {
- REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic);
- }
- }
- }
-}
diff --git a/libs/kstd/tests/src/format.cpp b/libs/kstd/tests/src/format.cpp
deleted file mode 100644
index 624779a..0000000
--- a/libs/kstd/tests/src/format.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-#include <kstd/format>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <iterator>
-#include <sstream>
-
-SCENARIO("Formatting to a new string", "[format]")
-{
- GIVEN("a format string without any placeholders")
- {
- auto const & fmt = "This is a test";
-
- WHEN("calling format with without any arguments.")
- {
- auto result = kstd::format(fmt);
-
- THEN("the result is the unmodified string")
- {
- REQUIRE(result == "This is a test");
- }
- }
-
- WHEN("calling format with additional arguments")
- {
- auto result = kstd::format(fmt, 1, 2, 3);
-
- THEN("the result is the unmodified string")
- {
- REQUIRE(result == "This is a test");
- }
- }
- }
-
- GIVEN("a format string with placeholders")
- {
- auto const & fmt = "Here are some placeholders: {} {} {}";
-
- WHEN("calling format with the same number of arguments as there are placeholders")
- {
- auto result = kstd::format(fmt, 1, true, 'a');
-
- THEN("the result is the formatted string")
- {
- REQUIRE(result == "Here are some placeholders: 1 true a");
- }
- }
-
- WHEN("calling format with too many arguments")
- {
- auto result = kstd::format(fmt, 2, false, 'b', 4, 5, 6);
-
- THEN("the result is the formatted string")
- {
- REQUIRE(result == "Here are some placeholders: 2 false b");
- }
- }
- }
-}
-
-SCENARIO("Formatting to an output iterator", "[format]")
-{
- auto buffer = std::ostringstream{};
-
- GIVEN("a format string without any placeholders")
- {
- auto const & fmt = "This is a test";
-
- WHEN("calling format with without any arguments.")
- {
- kstd::format_to(std::ostream_iterator<char>{buffer}, fmt);
-
- THEN("the unmodified string is written to the iterator")
- {
- REQUIRE(buffer.str() == "This is a test");
- }
- }
-
- WHEN("calling format with additional arguments")
- {
- kstd::format_to(std::ostream_iterator<char>{buffer}, fmt, 1, 2, 3);
-
- THEN("the unmodified string is written to the iterator")
- {
- REQUIRE(buffer.str() == "This is a test");
- }
- }
- }
-
- GIVEN("a format string with placeholders")
- {
- auto const & fmt = "Here are some placeholders: {} {} {}";
-
- WHEN("calling format with the same number of arguments as there are placeholders")
- {
- kstd::format_to(std::ostream_iterator<char>{buffer}, fmt, 1, true, -100);
-
- THEN("the formatted string is written to the iterator")
- {
- REQUIRE(buffer.str() == "Here are some placeholders: 1 true -100");
- }
- }
-
- WHEN("calling format with too many arguments")
- {
- kstd::format_to(std::ostream_iterator<char>{buffer}, fmt, 2, false, -200, 4, 5, 6);
-
- THEN("the formatted string is written to the iterator")
- {
- REQUIRE(buffer.str() == "Here are some placeholders: 2 false -200");
- }
- }
- }
-} \ No newline at end of file
diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp
deleted file mode 100644
index 0c94892..0000000
--- a/libs/kstd/tests/src/observer_ptr.cpp
+++ /dev/null
@@ -1,360 +0,0 @@
-#include <kstd/memory>
-#include <kstd/tests/os_panic.hpp>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <array>
-#include <compare>
-#include <type_traits>
-#include <utility>
-
-namespace
-{
- struct Base
- {
- };
-
- struct Derived : Base
- {
- };
-
- struct Element
- {
- int value{};
-
- constexpr auto operator<=>(Element const &) const noexcept = default;
- };
-} // namespace
-
-SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]")
-{
- GIVEN("An empty context")
- {
- WHEN("constructing by default")
- {
- auto ptr = kstd::observer_ptr<int>{};
-
- THEN("the observer pointer is null")
- {
- REQUIRE_FALSE(ptr);
- }
- }
-
- WHEN("constructing from a nullptr")
- {
- auto ptr = kstd::observer_ptr<int>{nullptr};
-
- THEN("the observer pointer is null")
- {
- REQUIRE_FALSE(ptr);
- }
- }
-
- WHEN("constructing from a raw pointer")
- {
- auto value = 1;
- auto ptr = kstd::observer_ptr{&value};
-
- THEN("the observer pointer is not null")
- {
- REQUIRE(ptr);
- }
-
- THEN("the observer pointer points to the correct object")
- {
- REQUIRE(&*ptr == &value);
- }
- }
-
- WHEN("copy constructing from an existing observer pointer")
- {
- auto value = 1;
- auto ptr = kstd::observer_ptr{&value};
- auto copy = ptr;
-
- THEN("the new observer pointer points to the same object as the other observer pointer")
- {
- REQUIRE(&*copy == &value);
- }
- }
-
- WHEN("copy constructing from an existing observer pointer with a compatible type")
- {
- auto value = Derived{};
- auto ptr = kstd::observer_ptr<Derived>(&value);
- kstd::observer_ptr<Base> copy = ptr;
-
- THEN("the new observer pointer points to the same object as the other observer pointer")
- {
- REQUIRE(&*copy == &value);
- }
- }
-
- WHEN("copy assigning from an existing observer pointer")
- {
- auto value = 1;
- auto ptr = kstd::observer_ptr{&value};
- auto copy = ptr;
-
- THEN("the new observer pointer points to the same object as the other observer pointer")
- {
- REQUIRE(&*copy == &value);
- }
- }
-
- WHEN("move constructing from an existing observer pointer")
- {
- auto value = 1;
- auto ptr = kstd::observer_ptr{&value};
- auto copy = std::move(ptr);
-
- THEN("the new observer pointer points to the same object as the other observer pointer")
- {
- REQUIRE(&*copy == &value);
- }
- }
-
- WHEN("move assigning from an existing observer pointer")
- {
- auto value = 1;
- auto ptr = kstd::observer_ptr{&value};
- auto copy = std::move(ptr);
-
- THEN("the new observer pointer points to the same object as the other observer pointer")
- {
- REQUIRE(&*copy == &value);
- }
- }
-
- WHEN("constructing an observer pointer using make_observer")
- {
- auto value = 1;
- auto ptr = kstd::make_observer(&value);
-
- THEN("the observer pointer points to the correct object")
- {
- REQUIRE(&*ptr == &value);
- }
-
- THEN("the observe pointer has the correct element type")
- {
- STATIC_REQUIRE(std::is_same_v<decltype(ptr), kstd::observer_ptr<int>>);
- }
- }
- }
-}
-
-SCENARIO("Observer pointer modifiers", "[observer_ptr]")
-{
- GIVEN("A non-null observer pointer")
- {
- auto value = 1;
- auto ptr = kstd::observer_ptr{&value};
-
- WHEN("releasing the observer pointer")
- {
- auto raw_ptr = ptr.release();
-
- THEN("the observer pointer is null")
- {
- REQUIRE_FALSE(ptr);
- }
-
- THEN("the returned pointer points to the correct object")
- {
- REQUIRE(raw_ptr == &value);
- }
- }
-
- WHEN("resetting the observer pointer to nullptr")
- {
- ptr.reset();
-
- THEN("the observer pointer is null")
- {
- REQUIRE_FALSE(ptr);
- }
- }
-
- WHEN("resetting the observer pointer to a new object")
- {
- auto other_value = 2;
- ptr.reset(&other_value);
-
- THEN("the observer pointer points to the new object")
- {
- REQUIRE(&*ptr == &other_value);
- }
- }
-
- WHEN("swapping it with another observer pointer")
- {
- auto other_value = 2;
- auto other_ptr = kstd::observer_ptr{&other_value};
- ptr.swap(other_ptr);
-
- THEN("the observer pointer points to the other object")
- {
- REQUIRE(&*ptr == &other_value);
- }
-
- THEN("the other observer pointer points to the original object")
- {
- REQUIRE(&*other_ptr == &value);
- }
- }
-
- WHEN("using namespace-level swap to swap it with another observer pointer")
- {
- using std::swap;
- auto other_value = 2;
- auto other_ptr = kstd::observer_ptr{&other_value};
- swap(ptr, other_ptr);
-
- THEN("the observer pointer points to the other object")
- {
- REQUIRE(&*ptr == &other_value);
- }
-
- THEN("the other observer pointer points to the original object")
- {
- REQUIRE(&*other_ptr == &value);
- }
- }
- }
-}
-
-SCENARIO("Observer pointer observers", "[observer_ptr]")
-{
- GIVEN("A non-null observer pointer")
- {
- auto value = Element{1};
- auto ptr = kstd::observer_ptr{&value};
-
- WHEN("getting the raw pointer")
- {
- auto raw_ptr = ptr.get();
-
- THEN("the raw pointer points to the correct object")
- {
- REQUIRE(raw_ptr == &value);
- }
- }
-
- WHEN("dereferencing the observer pointer")
- {
- auto dereferenced = *ptr;
-
- THEN("the dereferenced value is the correct value")
- {
- REQUIRE(dereferenced == value);
- }
- }
-
- WHEN("writing through the observer pointer with the arrow operator")
- {
- ptr->value = 2;
-
- THEN("the value is updated")
- {
- REQUIRE(value.value == 2);
- }
- }
-
- WHEN("converting the observer pointer to a raw pointer")
- {
- auto raw_ptr = static_cast<Element *>(ptr);
-
- THEN("the raw pointer points to the correct object")
- {
- REQUIRE(raw_ptr == &value);
- }
- }
-
- WHEN("checking the observer pointer as a boolean")
- {
- THEN("it returns true")
- {
- REQUIRE(static_cast<bool>(ptr));
- }
- }
- }
-
- GIVEN("A null observer pointer")
- {
- auto ptr = kstd::observer_ptr<Element>{};
-
- WHEN("checking the observer pointer as a boolean")
- {
- THEN("it returns false")
- {
- REQUIRE_FALSE(static_cast<bool>(ptr));
- }
- }
-
- WHEN("dereferencing the observer pointer")
- {
- THEN("the observer pointer panics")
- {
- REQUIRE_THROWS_AS(*ptr, kstd::tests::os_panic);
- }
- }
-
- WHEN("writing through the observer pointer with the arrow operator")
- {
- THEN("the observer pointer panics")
- {
- REQUIRE_THROWS_AS(ptr->value = 2, kstd::tests::os_panic);
- }
- }
- }
-}
-
-SCENARIO("Observer pointer comparisons", "[observer_ptr]")
-{
- GIVEN("Observer pointers to elements of an array")
- {
- auto arr = std::array{1, 2};
- auto ptr1 = kstd::observer_ptr{&arr[0]};
- auto ptr2 = kstd::observer_ptr{&arr[1]};
-
- WHEN("comparing the same observer pointer")
- {
- THEN("they are equal")
- {
- REQUIRE(ptr1 == ptr1);
- REQUIRE((ptr1 <=> ptr1) == std::strong_ordering::equal);
- }
- }
-
- WHEN("comparing different observer pointers")
- {
- THEN("they are ordered correctly")
- {
- REQUIRE(ptr1 != ptr2);
- REQUIRE(ptr1 < ptr2);
- REQUIRE(ptr1 <= ptr2);
- REQUIRE(ptr2 > ptr1);
- REQUIRE(ptr2 >= ptr1);
- REQUIRE((ptr1 <=> ptr2) == std::strong_ordering::less);
- REQUIRE((ptr2 <=> ptr1) == std::strong_ordering::greater);
- }
- }
- }
-
- GIVEN("A null observer pointer")
- {
- auto ptr = kstd::observer_ptr<int>{};
-
- WHEN("comparing with another null observer pointer")
- {
- auto other_ptr = kstd::observer_ptr<int>{};
-
- THEN("they are equal")
- {
- REQUIRE(ptr == other_ptr);
- REQUIRE((ptr <=> other_ptr) == std::strong_ordering::equal);
- }
- }
- }
-} \ No newline at end of file
diff --git a/libs/kstd/tests/src/os_panic.cpp b/libs/kstd/tests/src/os_panic.cpp
deleted file mode 100644
index 0759763..0000000
--- a/libs/kstd/tests/src/os_panic.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <kstd/tests/os_panic.hpp>
-
-#include <source_location>
-#include <string>
-#include <string_view>
-
-namespace kstd::os
-{
-
- auto panic(std::string_view message, std::source_location location)
- {
- throw kstd::tests::os_panic{std::string{message}, location};
- }
-
-} // namespace kstd::os \ No newline at end of file
diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp
deleted file mode 100644
index 53d7c9a..0000000
--- a/libs/kstd/tests/src/string.cpp
+++ /dev/null
@@ -1,445 +0,0 @@
-#include <kstd/string>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <algorithm>
-#include <cstring>
-#include <string_view>
-
-SCENARIO("String initialization and construction", "[string]")
-{
- GIVEN("Nothing")
- {
- WHEN("constructing an empty string")
- {
- auto str = kstd::string{};
-
- THEN("the size is zero and therefore the string is empty")
- {
- REQUIRE(str.empty());
- REQUIRE(str.size() == 0);
- }
-
- THEN("the string is empty")
- {
- REQUIRE(str.view() == std::string_view{});
- }
- }
- }
-
- GIVEN("A string view")
- {
- auto view = std::string_view{"Blub Blub"};
-
- WHEN("constructing a string from string_view")
- {
- auto str = kstd::string{view};
-
- THEN("the string is not empty and has the same size as the view")
- {
- REQUIRE(!str.empty());
- REQUIRE(str.size() == view.size());
- }
-
- THEN("the string contains the same characters as the view")
- {
- REQUIRE(str.view() == view);
- }
- }
- }
-
- GIVEN("A C-style string")
- {
- auto c_str = "Blub Blub";
-
- WHEN("constructing a string from the C-style string")
- {
- auto str = kstd::string{c_str};
-
- THEN("the string is not empty and has the same size as the C-style string")
- {
- REQUIRE(!str.empty());
- REQUIRE(str.size() == std::strlen(c_str));
- }
-
- THEN("the string contains the same characters as the C-style string")
- {
- REQUIRE(str.view() == c_str);
- }
- }
- }
-
- GIVEN("A character")
- {
- auto ch = 'x';
-
- WHEN("constructing a string from the character")
- {
- auto str = kstd::string{ch};
-
- THEN("the string is not empty and has size 1")
- {
- REQUIRE(!str.empty());
- REQUIRE(str.size() == 1);
- }
-
- THEN("the string contains the same character as the given character")
- {
- REQUIRE(str.view() == std::string_view{&ch, 1});
- }
- }
- }
-
- GIVEN("Another string")
- {
- auto other = kstd::string{"Blub Blub"};
-
- WHEN("copy constructing a new string")
- {
- auto str = kstd::string{other};
-
- THEN("the new string contains the same characters as the original")
- {
- REQUIRE(str.view() == other.view());
- }
- }
-
- auto str = kstd::string{"Blub"};
-
- WHEN("copy assigning another string")
- {
- auto other = kstd::string{"Blub Blub"};
- str = other;
-
- THEN("the string contains the same characters as the assigned string")
- {
- REQUIRE(str.view() == other.view());
- }
- }
- }
-
- GIVEN("A string")
- {
- auto str = kstd::string{"Hello"};
-
- WHEN("copy assigning a string view")
- {
- auto view = std::string_view{"Hello, world!"};
- str = view;
-
- THEN("the string contains the same characters as the assigned view")
- {
- REQUIRE(str.view() == view);
- }
- }
- }
-
- GIVEN("A string")
- {
- auto str = kstd::string{"Hello"};
-
- WHEN("copy assigning a C-style string")
- {
- auto c_str = "Hello, world!";
- str = c_str;
-
- THEN("the string contains the same characters as the assigned C-style string")
- {
- REQUIRE(str.view() == c_str);
- }
- }
- }
-}
-
-SCENARIO("String concatenation", "[string]")
-{
- GIVEN("Two strings")
- {
- auto str1 = kstd::string{"Blub"};
- auto str2 = kstd::string{" Blub"};
-
- WHEN("appending the second string to the first string")
- {
- str1.append(str2);
-
- THEN("the first string contains the characters of both strings concatenated")
- {
- REQUIRE(str1.view() == "Blub Blub");
- }
-
- THEN("the size of the first string is the sum of the sizes of both strings")
- {
- REQUIRE(str1.size() == str2.size() + 4);
- }
- }
-
- WHEN("using operator+= to append the second string to the first string")
- {
- str1 += str2;
-
- THEN("the first string contains the characters of both strings concatenated")
- {
- REQUIRE(str1.view() == "Blub Blub");
- }
-
- THEN("the size of the first string is the sum of the sizes of both strings")
- {
- REQUIRE(str1.size() == str2.size() + 4);
- }
- }
-
- WHEN("using operator+ to concatenate the two strings into a new string")
- {
- auto str3 = str1 + str2;
-
- THEN("the new string contains the characters of both strings concatenated")
- {
- REQUIRE(str3.view() == "Blub Blub");
- }
-
- THEN("the size of the new string is the sum of the sizes of both strings")
- {
- REQUIRE(str3.size() == str1.size() + str2.size());
- }
- }
- }
-
- GIVEN("A string and a string view")
- {
- auto str = kstd::string{"Blub"};
- auto view = std::string_view{" Blub"};
-
- WHEN("appending the string view to the string")
- {
- str.append(view);
-
- THEN("the string contains the characters of both the original string and the appended view concatenated")
- {
- REQUIRE(str.view() == "Blub Blub");
- }
-
- THEN("the size of the string is the sum of the sizes of the original string and the appended view")
- {
- REQUIRE(str.size() == view.size() + 4);
- }
- }
- }
-
- GIVEN("A string and a character")
- {
- auto str = kstd::string{"Blub"};
- auto ch = '!';
-
- WHEN("appending the character to the string")
- {
- str.push_back(ch);
-
- THEN("the string contains the original characters followed by the appended character")
- {
- REQUIRE(str.view() == "Blub!");
- }
-
- THEN("the size of the string is one more than the original size")
- {
- REQUIRE(str.size() == 5);
- }
- }
-
- WHEN("using operator+= to append the character to the string")
- {
- str += ch;
-
- THEN("the string contains the original characters followed by the appended character")
- {
- REQUIRE(str.view() == "Blub!");
- }
-
- THEN("the size of the string is one more than the original size")
- {
- REQUIRE(str.size() == 5);
- }
- }
- }
-}
-
-SCENARIO("String conversion and comparison", "[string]")
-{
- GIVEN("An unsigned integer")
- {
- constexpr auto value1 = 12345u;
- constexpr auto value2 = 0u;
-
- WHEN("converting the unsigned integer to a string")
- {
- auto str1 = kstd::to_string(value1);
- auto str2 = kstd::to_string(value2);
-
- THEN("the string contains the decimal representation of the unsigned integer")
- {
- REQUIRE(str1.view() == "12345");
- REQUIRE(str2.view() == "0");
- }
- }
- }
-
- GIVEN("Two strings with the same characters")
- {
- auto str1 = kstd::string{"Blub Blub"};
- auto str2 = kstd::string{"Blub Blub"};
-
- THEN("the strings are equal")
- {
- REQUIRE(str1 == str2);
- }
-
- THEN("the strings are not unequal")
- {
- REQUIRE(!(str1 != str2));
- }
- }
-
- GIVEN("A string and a string view with the same characters")
- {
- auto str = kstd::string{"Blub Blub"};
- auto view = std::string_view{"Blub Blub"};
-
- THEN("the string and the string view are equal")
- {
- REQUIRE(str == view);
- REQUIRE(view == str);
- }
-
- THEN("the string and the string view are not unequal")
- {
- REQUIRE(!(str != view));
- REQUIRE(!(view != str));
- }
- }
-}
-
-SCENARIO("String clearing", "[string]")
-{
- GIVEN("A non-empty string")
- {
- auto str = kstd::string{"Blub Blub"};
-
- WHEN("clearing the string")
- {
- str.clear();
-
- THEN("the string is empty and has size zero")
- {
- REQUIRE(str.empty());
- REQUIRE(str.size() == 0);
- }
-
- THEN("the string contains no characters")
- {
- REQUIRE(str.view() == std::string_view{});
- }
- }
- }
-}
-
-SCENARIO("String iteration", "[string]")
-{
- GIVEN("A string")
- {
- auto str = kstd::string{"Blub"};
-
- WHEN("iterating over the characters of the string as string_view using a range-based for loop")
- {
- kstd::string result;
-
- for (auto ch : str.view())
- {
- result.push_back(ch);
- }
-
- THEN("the iterated characters are the same as the characters in the string")
- {
- REQUIRE(result == str);
- }
- }
-
- WHEN("using std::ranges::for_each to iterate over the characters of the string")
- {
- kstd::string result;
-
- std::ranges::for_each(str, [&result](auto ch) { result.push_back(ch); });
-
- THEN("the iterated characters are the same as the characters in the string")
- {
- REQUIRE(result == str);
- }
- }
-
- WHEN("using front and back to access the first and last characters of the string")
- {
- THEN("front returns the first character of the string")
- {
- REQUIRE(str.front() == 'B');
- }
-
- THEN("back returns the last character of the string")
- {
- REQUIRE(str.back() == 'b');
- }
- }
- }
-
- GIVEN("A const string")
- {
- auto const str = kstd::string{"Blub"};
-
- WHEN("iterating over the characters of the string as string_view using a range-based for loop")
- {
- kstd::string result;
-
- for (auto ch : str.view())
- {
- result.push_back(ch);
- }
-
- THEN("the iterated characters are the same as the characters in the string")
- {
- REQUIRE(result == str.view());
- }
- }
-
- WHEN("using front and back to access the first and last characters of the string")
- {
- THEN("front returns the first character of the string")
- {
- REQUIRE(str.front() == 'B');
- }
-
- THEN("back returns the last character of the string")
- {
- REQUIRE(str.back() == 'b');
- }
- }
- }
-
- GIVEN("An empty string")
- {
- auto str = kstd::string{};
-
- WHEN("iterating over the characters of an empty string")
- {
- kstd::string result;
-
- for (auto ch : str.view())
- {
- result.push_back(ch);
- }
-
- THEN("no characters are iterated and the result is an empty string")
- {
- REQUIRE(result.empty());
- REQUIRE(result.size() == 0);
- REQUIRE(result.view() == std::string_view{});
- }
- }
- }
-}
diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp
deleted file mode 100644
index 4fb3559..0000000
--- a/libs/kstd/tests/src/vector.cpp
+++ /dev/null
@@ -1,2003 +0,0 @@
-#include <kstd/vector>
-
-#include <kstd/ranges>
-#include <kstd/tests/os_panic.hpp>
-#include <kstd/tests/test_types.hpp>
-
-#include <catch2/catch_test_macros.hpp>
-
-#include <array>
-#include <iterator>
-#include <ranges>
-#include <utility>
-
-SCENARIO("Vector initialization and construction", "[vector]")
-{
- GIVEN("An empty context")
- {
- WHEN("constructing by default")
- {
- auto v = kstd::vector<int>{};
-
- THEN("the vector is empty")
- {
- REQUIRE(v.empty());
- }
-
- THEN("the size and capacity are zero")
- {
- REQUIRE(v.size() == 0);
- REQUIRE(v.capacity() == 0);
- }
- }
-
- WHEN("constructing with a specific size")
- {
- auto v = kstd::vector<int>(10);
-
- THEN("the vector is not empty")
- {
- REQUIRE_FALSE(v.empty());
- }
-
- THEN("the size is and capacity match the specified value")
- {
- REQUIRE(v.size() == 10);
- REQUIRE(v.capacity() == 10);
- }
- }
-
- WHEN("constructing from an initializer list")
- {
- auto v = kstd::vector<int>{1, 2, 3, 4, 5};
-
- THEN("the vector is not empty")
- {
- REQUIRE_FALSE(v.empty());
- }
-
- THEN("the size is and capacity match the specified value")
- {
- REQUIRE(v.size() == 5);
- REQUIRE(v.capacity() == 5);
- }
-
- THEN("the elements are correctly initialized")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- REQUIRE(v[3] == 4);
- REQUIRE(v[4] == 5);
- }
- }
- }
-
- GIVEN("A non-empty range")
- {
- auto range = std::array<int, 3>{1, 2, 3};
-
- WHEN("constructing from a random-access iterator range")
- {
- auto v = kstd::vector<int>{std::begin(range), std::end(range)};
-
- THEN("the vector is not empty")
- {
- REQUIRE_FALSE(v.empty());
- }
-
- THEN("the size and capacity match the range size")
- {
- REQUIRE(v.size() == std::size(range));
- REQUIRE(v.capacity() == std::size(range));
- }
-
- THEN("the elements are correctly initialized")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- }
- }
-
- WHEN("constructing from a range")
- {
- auto v = kstd::vector<int>{kstd::from_range, range};
-
- THEN("the vector is not empty")
- {
- REQUIRE_FALSE(v.empty());
- }
-
- THEN("the size and capacity match the range size")
- {
- REQUIRE(v.size() == std::ranges::size(range));
- REQUIRE(v.capacity() == std::ranges::size(range));
- }
-
- THEN("the elements are correctly initialized")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- }
- }
- }
-
- GIVEN("A populated vector")
- {
- auto source = kstd::vector<int>{1, 2, 3, 4, 5};
-
- WHEN("copy constructing a new vector")
- {
- auto copy = kstd::vector<int>(source);
-
- THEN("the copy matches the original")
- {
- REQUIRE(copy.size() == source.size());
- REQUIRE(copy.capacity() == source.capacity());
- REQUIRE(copy[0] == 1);
- REQUIRE(copy[1] == 2);
- REQUIRE(copy[2] == 3);
- REQUIRE(copy[3] == 4);
- REQUIRE(copy[4] == 5);
- }
-
- THEN("the original is left unchanged")
- {
- REQUIRE(source.size() == 5);
- REQUIRE(source.capacity() == 5);
- REQUIRE(source[0] == 1);
- REQUIRE(source[1] == 2);
- REQUIRE(source[2] == 3);
- REQUIRE(source[3] == 4);
- REQUIRE(source[4] == 5);
- }
- }
-
- WHEN("move constructing a new vector")
- {
- auto moved = kstd::vector<int>(std::move(source));
-
- THEN("The new vector has the original elements")
- {
- REQUIRE(moved.size() == 5);
- REQUIRE(moved.capacity() == 5);
- REQUIRE(moved[0] == 1);
- REQUIRE(moved[1] == 2);
- REQUIRE(moved[2] == 3);
- REQUIRE(moved[3] == 4);
- REQUIRE(moved[4] == 5);
- }
-
- THEN("The original vector is left in a valid but unspecified state")
- {
- REQUIRE(source.empty());
- REQUIRE(source.size() == 0);
- REQUIRE(source.capacity() == 0);
- }
- }
- }
-}
-
-SCENARIO("Vector element access", "[vector]")
-{
- GIVEN("A populated vector")
- {
- auto v = kstd::vector<int>{10, 20, 30};
-
- WHEN("accessing elements for reading")
- {
- THEN("operator[] and at() return the correct elements")
- {
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(v[2] == 30);
-
- REQUIRE(v.at(0) == 10);
- REQUIRE(v.at(1) == 20);
- REQUIRE(v.at(2) == 30);
- }
-
- THEN("front() and back() return the first and last elements")
- {
- REQUIRE(v.front() == 10);
- REQUIRE(v.back() == 30);
- }
-
- THEN("data() return a pointer to the contiguous storage")
- {
- auto ptr = v.data();
- REQUIRE(ptr);
- REQUIRE(ptr[0] == 10);
- REQUIRE(ptr[1] == 20);
- REQUIRE(ptr[2] == 30);
- }
-
- THEN("accessing out of bounds elements panics")
- {
- REQUIRE_THROWS_AS(v.at(3), kstd::tests::os_panic);
- }
- }
-
- WHEN("accessing elements for writing")
- {
- v[0] = 100;
- v.at(1) = 200;
- v.back() = 300;
-
- THEN("the elements are correctly modified")
- {
- REQUIRE(v[0] == 100);
- REQUIRE(v[1] == 200);
- REQUIRE(v[2] == 300);
- }
- }
- }
-}
-
-SCENARIO("Vector iterators", "[vector]")
-{
- GIVEN("A populated vector")
- {
- auto v = kstd::vector<int>{1, 2, 3};
-
- WHEN("using forward iterators")
- {
- THEN("they navigate the elements in the correct forward order")
- {
- auto it = v.begin();
- REQUIRE(it != v.end());
- REQUIRE(*it == 1);
-
- ++it;
- REQUIRE(it != v.end());
- REQUIRE(*it == 2);
-
- ++it;
- REQUIRE(it != v.end());
- REQUIRE(*it == 3);
-
- ++it;
- REQUIRE(it == v.end());
- }
-
- THEN("const forward iterators provide correct access")
- {
- auto it = v.cbegin();
- REQUIRE(it != v.cend());
- REQUIRE(*it == 1);
-
- ++it;
- REQUIRE(it != v.cend());
- REQUIRE(*it == 2);
-
- ++it;
- REQUIRE(it != v.cend());
- REQUIRE(*it == 3);
-
- ++it;
- REQUIRE(it == v.cend());
- }
- }
-
- WHEN("using reverse iterators")
- {
- THEN("they navigate the elements in the correct reverse order")
- {
- auto it = v.rbegin();
- REQUIRE(it != v.rend());
- REQUIRE(*it == 3);
-
- ++it;
- REQUIRE(it != v.rend());
- REQUIRE(*it == 2);
-
- ++it;
- REQUIRE(it != v.rend());
- REQUIRE(*it == 1);
-
- ++it;
- REQUIRE(it == v.rend());
- }
-
- THEN("const reverse iterators provide correct access")
- {
- auto it = v.crbegin();
- REQUIRE(it != v.crend());
- REQUIRE(*it == 3);
-
- ++it;
- REQUIRE(it != v.crend());
- REQUIRE(*it == 2);
-
- ++it;
- REQUIRE(it != v.crend());
- REQUIRE(*it == 1);
-
- ++it;
- REQUIRE(it == v.crend());
- }
- }
- }
-
- GIVEN("an empty vector")
- {
- auto v = kstd::vector<int>{};
-
- WHEN("getting iterators")
- {
- THEN("begin() equals end() and cbegin() equals cend()")
- {
- REQUIRE(v.begin() == v.end());
- REQUIRE(v.cbegin() == v.cend());
- }
-
- THEN("rbegin() equals rend() and crbegin() equals crend()")
- {
- REQUIRE(v.rbegin() == v.rend());
- REQUIRE(v.crbegin() == v.crend());
- }
- }
- }
-}
-
-SCENARIO("Vector capacity management", "[vector]")
-{
- GIVEN("An empty vector")
- {
- auto v = kstd::vector<int>{};
-
- WHEN("reserving space")
- {
- v.reserve(10);
-
- THEN("the capacity is at least the reserved amount")
- {
- REQUIRE(v.capacity() >= 10);
- }
-
- THEN("the size is still zero")
- {
- REQUIRE(v.size() == 0);
- }
-
- THEN("the vector is still empty")
- {
- REQUIRE(v.empty());
- }
- }
-
- WHEN("reserving space less than or equal to current capacity")
- {
- v.reserve(10);
- auto const current_capacity = v.capacity();
- v.reserve(5);
-
- THEN("the capacity remains unchanged")
- {
- REQUIRE(v.capacity() == current_capacity);
- }
- }
-
- WHEN("reserving space greater than max_size")
- {
- THEN("a panic is triggered")
- {
- REQUIRE_THROWS_AS(v.reserve(v.max_size() + 1), kstd::tests::os_panic);
- }
- }
- }
-
- GIVEN("A populated vector with excess capacity")
- {
- auto v = kstd::vector<int>{1, 2, 3};
- v.reserve(10);
-
- REQUIRE(v.capacity() == 10);
-
- WHEN("calling shrink_to_fit")
- {
- v.shrink_to_fit();
-
- THEN("the capacity is reduced to match the size")
- {
- REQUIRE(v.capacity() == 3);
- REQUIRE(v.size() == 3);
- }
-
- THEN("the elements remain unchanged")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- }
- }
- }
-}
-
-SCENARIO("Vector modifiers", "[vector]")
-{
- GIVEN("An empty vector")
- {
- auto v = kstd::vector<int>{};
-
- WHEN("push_back is called with a value")
- {
- v.push_back(10);
-
- THEN("the element is added and the size and capacity increase")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.capacity() >= 1);
- REQUIRE(v.back() == 10);
- }
- }
-
- WHEN("emplace_back is called with constructor arguments")
- {
- v.emplace_back(20);
-
- THEN("the element is added and the size and capacity increase")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.capacity() >= 1);
- REQUIRE(v.back() == 20);
- }
- }
-
- WHEN("elements are added while capacity is sufficient")
- {
- v.reserve(10);
- auto const capacity = v.capacity();
-
- v.push_back(10);
- v.emplace_back(20);
-
- THEN("the elements are added without reallocation")
- {
- REQUIRE(v.size() == 2);
- REQUIRE(v.capacity() == capacity);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- }
- }
-
- WHEN("emplace is called with the end iterator and constructor arguments")
- {
- v.emplace(v.end(), 20);
-
- THEN("the element is appended and the size and capacity increase")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.capacity() >= 1);
- REQUIRE(v.back() == 20);
- }
- }
-
- WHEN("emplace is called while capacity is sufficient")
- {
- v.reserve(10);
- auto const capacity = v.capacity();
-
- v.emplace(v.end(), 20);
-
- THEN("the element is appended without reallocation")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.capacity() == capacity);
- REQUIRE(v.back() == 20);
- }
- }
-
- WHEN("inserting an element")
- {
- auto it = v.insert(v.cbegin(), 40);
-
- THEN("the size and capacity increase and the element is inserted")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.capacity() >= 1);
- REQUIRE(v[0] == 40);
- REQUIRE(it == v.begin());
- }
- }
-
- WHEN("inserting an lvalue element")
- {
- auto const value = 40;
- auto it = v.insert(v.cbegin(), value);
-
- THEN("the size and capacity increase and the element is inserted")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.capacity() >= 1);
- REQUIRE(v[0] == 40);
- REQUIRE(it == v.begin());
- }
- }
-
- WHEN("appending a range")
- {
- auto const range = std::views::iota(0, 3);
- v.append_range(range);
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == 3);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= 3);
- }
-
- THEN("the elements are appended")
- {
- REQUIRE(v[0] == 0);
- REQUIRE(v[1] == 1);
- REQUIRE(v[2] == 2);
- }
- }
-
- WHEN("appending from an input range")
- {
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()};
- auto const last = kstd::tests::test_input_iterator{};
-
- v.append_range(std::ranges::subrange{first, last});
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == 3);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= 3);
- }
-
- THEN("the elements are appended")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- }
- }
-
- WHEN("appending from an input range with sufficient capacity")
- {
- v.reserve(3);
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()};
- auto const last = kstd::tests::test_input_iterator{};
-
- v.append_range(std::ranges::subrange{first, last});
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == 3);
- }
-
- THEN("the capacity stays the same")
- {
- REQUIRE(v.capacity() == 3);
- }
-
- THEN("the elements are appended")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- }
- }
-
- WHEN("resizing the vector to a greater size")
- {
- v.resize(3);
-
- THEN("the size and capacity increase and the elements are value initialized")
- {
- REQUIRE(v.size() == 3);
- REQUIRE(v.capacity() >= 3);
- REQUIRE(v[0] == 0);
- REQUIRE(v[1] == 0);
- REQUIRE(v[2] == 0);
- }
- }
-
- WHEN("resizing the vector to a greater size with initial value")
- {
- v.resize(3, 2);
-
- THEN("the size and capacity increase and the elements are initialized to the given value")
- {
- REQUIRE(v.size() == 3);
- REQUIRE(v.capacity() >= 3);
- REQUIRE(v[0] == 2);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 2);
- }
- }
-
- WHEN("inserting a range at the beginning")
- {
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto it = v.insert_range(v.begin(), arr);
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == 3);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= 3);
- }
-
- THEN("the elements are inserted")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- }
-
- THEN("the returned iterator points to the beginning of the inserted range")
- {
- REQUIRE(it == v.begin());
- }
- }
-
- WHEN("inserting a range at the end")
- {
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto it = v.insert_range(v.end(), arr);
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == 3);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= 3);
- }
-
- THEN("the elements are inserted")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- }
-
- THEN("the returned iterator points to the beginning of the inserted range")
- {
- REQUIRE(it == v.begin());
- }
- }
-
- WHEN("inserting from an input range without sufficient capacity")
- {
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()};
- auto const last = kstd::tests::test_input_iterator{};
- auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last});
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == 3);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= 3);
- }
-
- THEN("the elements are inserted")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- }
-
- THEN("the returned iterator points to the beginning of the inserted range")
- {
- REQUIRE(it == v.begin());
- }
- }
- }
-
- GIVEN("A populated vector")
- {
- auto v = kstd::vector<int>{10, 20, 30};
- auto initial_capacity = v.capacity();
-
- WHEN("push_back is called")
- {
- v.push_back(40);
-
- THEN("the element is added and the size and capacity increase")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v.capacity() >= initial_capacity);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(v[2] == 30);
- REQUIRE(v[3] == 40);
- }
- }
-
- WHEN("emplace_back is called with constructor arguments")
- {
- v.emplace_back(40);
-
- THEN("the element is added and the size and capacity increase")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v.capacity() >= initial_capacity);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(v[2] == 30);
- REQUIRE(v[3] == 40);
- }
- }
-
- WHEN("emplace is called with an iterator and constructor arguments")
- {
- auto it = v.emplace(v.begin() + 2, 25);
-
- THEN("the element is inserted and the size and capacity increase")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v.capacity() >= initial_capacity);
- REQUIRE(v.at(2) == 25);
- }
-
- THEN("the returned iterator points to the inserted element")
- {
- REQUIRE(it == v.cbegin() + 2);
- REQUIRE(*it == 25);
- }
- }
-
- WHEN("emplace is called with an iterator and sufficient capacity")
- {
- v.reserve(v.size() + 1);
-
- auto it = v.emplace(v.begin() + 2, 25);
-
- THEN("the element is inserted and the size and capacity increase")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v.capacity() >= initial_capacity);
- REQUIRE(v.at(2) == 25);
- }
-
- THEN("the returned iterator points to the inserted element")
- {
- REQUIRE(it == v.cbegin() + 2);
- REQUIRE(*it == 25);
- }
- }
-
- WHEN("push_back is called with a reference to an internal element")
- {
- v.shrink_to_fit();
- auto const original_value = v[0];
-
- v.push_back(v[0]);
-
- THEN("reallocation handles the internal reference safely without dangling")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == original_value);
- REQUIRE(v.back() == original_value);
- }
- }
-
- WHEN("pop_back is called")
- {
- v.pop_back();
-
- THEN("the last element is removed and the size decreases")
- {
- REQUIRE(v.size() == 2);
- REQUIRE(v.capacity() == initial_capacity);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- }
- }
-
- WHEN("clear is called")
- {
- v.clear();
-
- THEN("the vector is empty")
- {
- REQUIRE(v.empty());
- REQUIRE(v.size() == 0);
- REQUIRE(v.capacity() == initial_capacity);
- }
- }
-
- WHEN("inserting at the beginning")
- {
- auto it = v.insert(v.cbegin(), 5);
-
- THEN("the element is inserted at the front")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 5);
- REQUIRE(v[1] == 10);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- REQUIRE(it == v.begin());
- }
- }
-
- WHEN("inserting in the middle")
- {
- auto it = v.insert(v.cbegin() + 1, 15);
-
- THEN("the element is inserted in the middle")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 15);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("inserting at the end")
- {
- auto it = v.insert(v.cend(), 40);
-
- THEN("the element is inserted at the back")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(v[2] == 30);
- REQUIRE(v[3] == 40);
- REQUIRE(it == v.begin() + 3);
- }
- }
-
- WHEN("inserting an lvalue at the end")
- {
- auto const value = 40;
- auto it = v.insert(v.cend(), value);
-
- THEN("the element is inserted at the back")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(v[2] == 30);
- REQUIRE(v[3] == 40);
- REQUIRE(it == v.begin() + 3);
- }
- }
-
- WHEN("inserting when capacity is sufficient")
- {
- v.reserve(10);
- auto const capacity = v.capacity();
-
- auto it = v.insert(v.cbegin() + 1, 15);
-
- THEN("the element is added without reallocation")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v.capacity() == capacity);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 15);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("inserting a reference to an existing element with reallocation")
- {
- v.shrink_to_fit();
- REQUIRE(v.capacity() == v.size());
- auto it = v.insert(v.cbegin() + 1, v[2]);
-
- THEN("the element is correctly copied and inserted")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 30);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("inserting a reference to an existing element without reallocation")
- {
- v.reserve(10);
- REQUIRE(v.capacity() > v.size());
- auto it = v.insert(v.cbegin() + 1, v[2]);
-
- THEN("the element is correctly copied and inserted")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 30);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("inserting an rvalue reference to an existing element with reallocation")
- {
- v.shrink_to_fit();
- REQUIRE(v.capacity() == v.size());
- auto it = v.insert(v.cbegin() + 1, std::move(v[2]));
-
- THEN("the element is correctly moved and inserted")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 30);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("inserting an rvalue reference to an existing element without reallocation")
- {
- v.reserve(10);
- REQUIRE(v.capacity() > v.size());
- auto it = v.insert(v.cbegin() + 1, std::move(v[2]));
-
- THEN("the element is correctly moved and inserted")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 30);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("erasing the first element")
- {
- auto it = v.erase(v.cbegin());
-
- THEN("the first element is removed and the size decreases")
- {
- REQUIRE(v.size() == 2);
- REQUIRE(v[0] == 20);
- REQUIRE(v[1] == 30);
- REQUIRE(it == v.begin());
- }
- }
-
- WHEN("erasing a middle element")
- {
- auto it = v.erase(v.cbegin() + 1);
-
- THEN("the middle element is removed and the size decreases")
- {
- REQUIRE(v.size() == 2);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("erasing the last element")
- {
- auto it = v.erase(v.cend() - 1);
-
- THEN("the last element is removed and the size decreases")
- {
- REQUIRE(v.size() == 2);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(it == v.end());
- }
- }
-
- WHEN("erasing the end() iterator")
- {
- THEN("a panic is triggered")
- {
- REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic);
- }
- }
-
- WHEN("erasing a range of elements")
- {
- auto it = v.erase(v.cbegin() + 1, v.cend() - 1);
-
- THEN("the specified range is removed and the size decreases")
- {
- REQUIRE(v.size() == 2);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("erasing an empty range")
- {
- auto it = v.erase(v.cbegin() + 1, v.cbegin() + 1);
-
- THEN("the vector is unchanged")
- {
- REQUIRE(v.size() == 3);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(v[2] == 30);
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("appending a range")
- {
- auto initial_size = v.size();
- v.append_range(std::views::iota(0, 3));
-
- THEN("capacity is increased")
- {
- REQUIRE(v.capacity() >= initial_capacity);
- }
-
- THEN("size is increased")
- {
- REQUIRE(v.size() == initial_size + 3);
- }
-
- THEN("the elements are appended")
- {
- REQUIRE(v[initial_size + 0] == 0);
- REQUIRE(v[initial_size + 1] == 1);
- REQUIRE(v[initial_size + 2] == 2);
- }
- }
-
- WHEN("resizing the vector to a greater size")
- {
- auto initial_size = v.size();
- v.resize(initial_size + 3);
-
- THEN("the size and capacity increase and the elements are value initialized")
- {
- REQUIRE(v.size() == initial_size + 3);
- REQUIRE(v.capacity() >= initial_size + 3);
- REQUIRE(v[initial_size + 0] == 0);
- REQUIRE(v[initial_size + 1] == 0);
- REQUIRE(v[initial_size + 2] == 0);
- }
- }
-
- WHEN("resizing the vector to a greater size with initial value")
- {
- auto initial_size = v.size();
- v.resize(initial_size + 3, 2);
-
- THEN("the size and capacity increase and the elements are initialized to the given value")
- {
- REQUIRE(v.size() == initial_size + 3);
- REQUIRE(v.capacity() >= initial_size + 3);
- REQUIRE(v[initial_size + 0] == 2);
- REQUIRE(v[initial_size + 1] == 2);
- REQUIRE(v[initial_size + 2] == 2);
- }
- }
-
- WHEN("resizing the vector to a smaller size")
- {
- v.resize(1);
-
- THEN("the size decreases and the elements are destroyed")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v[0] == 10);
- }
- }
-
- WHEN("inserting an empty range")
- {
- auto initial_size = v.size();
- auto it = v.insert_range(v.begin(), std::views::empty<int>);
-
- THEN("the size does not change")
- {
- REQUIRE(v.size() == initial_size);
- }
-
- THEN("the capacity does not change")
- {
- REQUIRE(v.capacity() == initial_capacity);
- }
-
- THEN("the content is unchanged")
- {
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(v[2] == 30);
- }
-
- THEN("the returned iterator points to the position of insertion")
- {
- REQUIRE(it == v.begin());
- }
- }
-
- WHEN("inserting a range at the beginning")
- {
- auto initial_size = v.size();
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto it = v.insert_range(v.begin(), arr);
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == initial_size + 3);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= initial_size + 3);
- }
-
- THEN("the elements are inserted")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- REQUIRE(v[3] == 10);
- REQUIRE(v[4] == 20);
- REQUIRE(v[5] == 30);
- }
-
- THEN("the returned iterator points to the beginning of the inserted range")
- {
- REQUIRE(it == v.begin());
- }
- }
-
- WHEN("inserting a range at the end")
- {
- auto initial_size = v.size();
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto it = v.insert_range(v.end(), arr);
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == initial_size + 3);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= initial_size + 3);
- }
-
- THEN("the elements are inserted")
- {
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 20);
- REQUIRE(v[2] == 30);
- REQUIRE(v[3] == 1);
- REQUIRE(v[4] == 2);
- REQUIRE(v[5] == 3);
- }
-
- THEN("the returned iterator points to the beginning of the inserted range")
- {
- REQUIRE(it == v.begin() + initial_size);
- }
- }
-
- WHEN("inserting a range that causes reallocation")
- {
- auto initial_size = v.size();
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto it = v.insert_range(v.begin() + 1, arr);
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == initial_size + 3);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= initial_size + 3);
- }
-
- THEN("the elements are inserted")
- {
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 1);
- REQUIRE(v[2] == 2);
- REQUIRE(v[3] == 3);
- REQUIRE(v[4] == 20);
- REQUIRE(v[5] == 30);
- }
-
- THEN("the returned iterator points to the beginning of the inserted range")
- {
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("inserting fewer elements than the suffix without reallocation")
- {
- v.reserve(10);
- auto const capacity = v.capacity();
- auto const arr = std::array<int, 1>{1};
- auto it = v.insert_range(v.begin() + 1, arr);
-
- THEN("the elements are correctly placed")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 1);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- }
-
- THEN("no reallocation occurs")
- {
- REQUIRE(v.capacity() == capacity);
- }
-
- THEN("the returned iterator points to the inserted element")
- {
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("inserting fewer elements than the suffix without sufficient capacity")
- {
- v.shrink_to_fit();
- auto const arr = std::array<int, 1>{1};
- auto it = v.insert_range(v.begin() + 1, arr);
-
- THEN("the elements are correctly placed")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0] == 10);
- REQUIRE(v[1] == 1);
- REQUIRE(v[2] == 20);
- REQUIRE(v[3] == 30);
- }
-
- THEN("the returned iterator points to the inserted element")
- {
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("inserting from an input range without sufficient capacity")
- {
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()};
- auto const last = kstd::tests::test_input_iterator{};
- auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last});
-
- THEN("the size increases")
- {
- REQUIRE(v.size() == 6);
- }
-
- THEN("the capacity increases")
- {
- REQUIRE(v.capacity() >= 6);
- }
-
- THEN("the elements are inserted")
- {
- REQUIRE(v[0] == 1);
- REQUIRE(v[1] == 2);
- REQUIRE(v[2] == 3);
- REQUIRE(v[3] == 10);
- REQUIRE(v[4] == 20);
- REQUIRE(v[5] == 30);
- }
-
- THEN("the returned iterator points to the beginning of the inserted range")
- {
- REQUIRE(it == v.begin());
- }
- }
- }
-}
-
-SCENARIO("Vector comparison", "[vector]")
-{
- GIVEN("Two identical vectors")
- {
- auto v1 = kstd::vector<int>{1, 2, 3};
- auto v2 = kstd::vector<int>{1, 2, 3};
-
- WHEN("comparing for equality")
- {
- THEN("the vectors are equal")
- {
- REQUIRE(v1 == v2);
- }
-
- THEN("the vectors and not not-equal")
- {
- REQUIRE_FALSE(v1 != v2);
- }
- }
-
- WHEN("comparing using the spaceship operator")
- {
- THEN("the vectors are equivalent")
- {
- REQUIRE((v1 <=> v2) == 0);
- REQUIRE(v1 <= v2);
- REQUIRE(v1 >= v2);
- }
- }
- }
-
- GIVEN("Two vectors of different sizes")
- {
- auto v1 = kstd::vector<int>{1, 2, 3};
- auto v2 = kstd::vector<int>{1, 2, 3, 4};
-
- WHEN("comparing for equality")
- {
- THEN("the vectors are not equal")
- {
- REQUIRE_FALSE(v1 == v2);
- }
-
- THEN("the vectors are not-equal")
- {
- REQUIRE(v1 != v2);
- }
- }
-
- WHEN("comparing for ordering")
- {
- THEN("the shorter vector evaluates as less than the longer vector")
- {
- REQUIRE(v1 < v2);
- REQUIRE(v2 > v1);
- }
- }
- }
-
- GIVEN("Two vectors of the same size but different elements")
- {
- auto v1 = kstd::vector<int>{1, 2, 3};
- auto v2 = kstd::vector<int>{1, 2, 4};
-
- WHEN("comparing for ordering")
- {
- THEN("they are ordered lexicographically")
- {
- REQUIRE(v1 < v2);
- REQUIRE(v2 > v1);
- }
- }
- }
-}
-
-SCENARIO("Vector with non-default-constructible types", "[vector]")
-{
- GIVEN("A type without a default constructor")
- {
- WHEN("constructing an empty vector")
- {
- auto v = kstd::vector<kstd::tests::non_default_constructible>{};
-
- THEN("the vector is empty")
- {
- REQUIRE(v.empty());
- }
- }
-
- WHEN("using emplace_back")
- {
- auto v = kstd::vector<kstd::tests::non_default_constructible>{};
- v.emplace_back(40);
-
- THEN("the element is added and the size and capacity increase")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.back() == kstd::tests::non_default_constructible{40});
- }
- }
- }
-}
-
-SCENARIO("Vector with custom allocator", "[vector]")
-{
- GIVEN("a tracking allocator acting as the vector's memory manager")
- {
- auto allocations = 0;
- auto allocator = kstd::tests::tracking_allocator<int>{&allocations};
-
- WHEN("a vector uses this allocator to allocate memory")
- {
- auto v = kstd::vector<int, kstd::tests::tracking_allocator<int>>(allocator);
- REQUIRE(allocations == 0);
-
- v.reserve(10);
-
- THEN("the allocator was used to allocate memory")
- {
- REQUIRE(allocations > 0);
- }
- }
- }
-}
-
-SCENARIO("Vector modifier move semantics", "[vector]")
-{
- GIVEN("An empty vector and a move tracker element")
- {
- auto v = kstd::vector<kstd::tests::special_member_tracker>{};
- auto tracker = kstd::tests::special_member_tracker{40};
-
- WHEN("push_back is called with the move tracker")
- {
- v.push_back(std::move(tracker));
-
- THEN("the element is added and the size and capacity increase")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.back().move_constructed_count == 1);
- REQUIRE(v.back().copy_constructed_count == 0);
- REQUIRE(v.back().value == 40);
- }
-
- THEN("the original tracker is left in a valid but unspecified state")
- {
- REQUIRE(tracker.value == -1);
- }
- }
- }
-
- GIVEN("An empty vector")
- {
- auto v = kstd::vector<kstd::tests::special_member_tracker>{};
-
- WHEN("emplace_back is called with constructor arguments")
- {
- v.emplace_back(40);
-
- THEN("the element is constructed directly, without moves or copies")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.back().move_constructed_count == 0);
- REQUIRE(v.back().copy_constructed_count == 0);
- REQUIRE(v.back().value == 40);
- }
- }
- }
-
- GIVEN("A populated vector of move trackers")
- {
- auto v = kstd::vector<kstd::tests::special_member_tracker>{};
- v.reserve(10);
- v.emplace_back(10);
- v.emplace_back(20);
- v.emplace_back(30);
-
- WHEN("inserting an element in the middle with sufficient capacity")
- {
- auto const tracker = kstd::tests::special_member_tracker{15};
- v.insert(v.cbegin() + 1, tracker);
-
- THEN("the shifted elements are move-assigned and the new element is copy-assigned")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0].value == 10);
- REQUIRE(v[0].move_assigned_count == 0);
- REQUIRE(v[0].copy_assigned_count == 0);
- REQUIRE(v[1].value == 15);
- REQUIRE(v[1].move_assigned_count == 0);
- REQUIRE(v[1].copy_assigned_count == 1);
- REQUIRE(tracker.copied_from_count == 1);
- REQUIRE(v[2].value == 20);
- REQUIRE(v[2].move_assigned_count == 1);
- REQUIRE(v[2].copy_assigned_count == 0);
- REQUIRE(v[3].value == 30);
- REQUIRE(v[3].move_constructed_count == 1);
- }
- }
-
- WHEN("inserting an rvalue element in the middle with sufficient capacity")
- {
- auto tracker = kstd::tests::special_member_tracker{15};
- v.insert(v.cbegin() + 1, std::move(tracker));
-
- THEN("the shifted elements are move-assigned and the new element is move-assigned")
- {
- REQUIRE(v.size() == 4);
- REQUIRE(v[0].value == 10);
- REQUIRE(v[0].move_assigned_count == 0);
- REQUIRE(v[0].copy_assigned_count == 0);
- REQUIRE(v[1].value == 15);
- REQUIRE(v[1].move_assigned_count == 1);
- REQUIRE(v[1].copy_assigned_count == 0);
- REQUIRE(tracker.moved_from_count == 1);
- REQUIRE(v[2].value == 20);
- REQUIRE(v[2].move_assigned_count == 1);
- REQUIRE(v[3].value == 30);
- REQUIRE(v[3].move_constructed_count == 1);
- }
- }
-
- WHEN("erasing an element in the middle")
- {
- for (auto & elem : v)
- {
- elem.reset_counts();
- }
-
- auto it = v.erase(v.cbegin() + 1);
-
- THEN("the subsequent elements are move-assigned leftwards")
- {
- REQUIRE(v.size() == 2);
-
- REQUIRE(v[0].value == 10);
- REQUIRE(v[0].move_assigned_count == 0);
- REQUIRE(v[0].copy_assigned_count == 0);
-
- REQUIRE(v[1].value == 30);
- REQUIRE(v[1].move_assigned_count == 1);
- REQUIRE(v[1].copy_assigned_count == 0);
-
- REQUIRE(v.data()[2].destroyed_count == 1);
- REQUIRE(v.data()[2].moved_from_count == 1);
-
- REQUIRE(it == v.begin() + 1);
- }
- }
-
- WHEN("erasing the last element")
- {
- for (auto & elem : v)
- {
- elem.reset_counts();
- }
-
- auto it = v.erase(v.cend() - 1);
-
- THEN("no elements are moved, just the last element destroyed")
- {
- REQUIRE(v.size() == 2);
-
- REQUIRE(v[0].value == 10);
- REQUIRE(v[0].move_constructed_count == 0);
- REQUIRE(v[0].copy_constructed_count == 0);
- REQUIRE(v[0].move_assigned_count == 0);
- REQUIRE(v[0].copy_assigned_count == 0);
-
- REQUIRE(v[1].value == 20);
- REQUIRE(v[1].move_constructed_count == 0);
- REQUIRE(v[1].copy_constructed_count == 0);
- REQUIRE(v[1].move_assigned_count == 0);
- REQUIRE(v[1].copy_assigned_count == 0);
-
- REQUIRE(v.data()[2].destroyed_count == 1);
- REQUIRE(v.data()[2].moved_from_count == 0);
- REQUIRE(v.data()[2].copied_from_count == 0);
-
- REQUIRE(it == v.end());
- }
- }
-
- WHEN("erasing a range of elements in the middle")
- {
- v.emplace_back(40);
- v.emplace_back(50);
-
- for (auto & elem : v)
- {
- elem.reset_counts();
- }
-
- auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3);
-
- THEN("the specified elements are destroyed and subsequent elements are move-assigned leftwards")
- {
- REQUIRE(v.size() == 3);
-
- REQUIRE(v[0].value == 10);
- REQUIRE(v[0].move_constructed_count == 0);
- REQUIRE(v[0].copy_constructed_count == 0);
- REQUIRE(v[0].move_assigned_count == 0);
- REQUIRE(v[0].copy_assigned_count == 0);
-
- REQUIRE(v[1].value == 40);
- REQUIRE(v[1].move_constructed_count == 0);
- REQUIRE(v[1].copy_constructed_count == 0);
- REQUIRE(v[1].move_assigned_count == 1);
- REQUIRE(v[1].copy_assigned_count == 0);
-
- REQUIRE(v[2].value == 50);
- REQUIRE(v[2].move_constructed_count == 0);
- REQUIRE(v[2].copy_constructed_count == 0);
- REQUIRE(v[2].move_assigned_count == 1);
- REQUIRE(v[2].copy_assigned_count == 0);
-
- REQUIRE(v.data()[3].destroyed_count == 1);
- REQUIRE(v.data()[3].moved_from_count == 1);
- REQUIRE(v.data()[3].copied_from_count == 0);
-
- REQUIRE(v.data()[4].destroyed_count == 1);
- REQUIRE(v.data()[4].moved_from_count == 1);
- REQUIRE(v.data()[4].copied_from_count == 0);
-
- REQUIRE(it == v.begin() + 1);
- }
- }
- }
-}
-
-SCENARIO("Vector advanced construction", "[vector]")
-{
- GIVEN("A count and a default value")
- {
- WHEN("constructing with count and default argument")
- {
- auto const count = 5uz;
- auto const value = 42;
- auto v = kstd::vector<int>(count, value);
-
- THEN("the vector is initialized with count copies of value")
- {
- REQUIRE(v.size() == 5);
- REQUIRE(v.capacity() == 5);
- REQUIRE(v.front() == 42);
- REQUIRE(v.back() == 42);
- }
- }
- }
-
- GIVEN("A pure input iterator range")
- {
- WHEN("constructing from input iterators")
- {
- auto const arr = std::array<int, 3>{1, 2, 3};
- auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()};
- auto const last = kstd::tests::test_input_iterator{};
-
- auto v = kstd::vector<int>(first, last);
-
- THEN("the vector is generated dynamically and initialized correctly")
- {
- REQUIRE(v.size() == 3);
- REQUIRE(v[0] == 1);
- REQUIRE(v[2] == 3);
- }
- }
- }
-
- GIVEN("A tracking allocator and a source vector")
- {
- auto allocs = 0;
- 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);
-
- allocs = 0;
-
- WHEN("copy constructing with an allocator")
- {
- auto copy = kstd::vector<int, kstd::tests::tracking_allocator<int>>(source, allocator);
-
- THEN("the copy succeeds and the allocator is used")
- {
- REQUIRE(copy.size() == 3);
- REQUIRE(allocs > 0);
- REQUIRE(copy[0] == 1);
- REQUIRE(copy[2] == 3);
- }
- }
-
- WHEN("move constructing with an identically comparing 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)")
- {
- REQUIRE(moved.size() == 3);
- REQUIRE(allocs == 0);
- REQUIRE(moved[0] == 1);
- REQUIRE(moved[2] == 3);
- }
- }
-
- WHEN("move constructing with a non-equal allocator")
- {
- auto allocs2 = 0;
- auto allocator2 = kstd::tests::tracking_allocator<int>{&allocs2};
-
- auto moved = kstd::vector<int, kstd::tests::tracking_allocator<int>>(std::move(source), allocator2);
-
- THEN("the move allocates new memory and moves elements")
- {
- REQUIRE(allocs2 > 0);
- REQUIRE(moved.size() == 3);
- REQUIRE(source.empty());
- }
- }
- }
-}
-
-SCENARIO("Vector assignment operators", "[vector]")
-{
- GIVEN("A source vector and an empty target vector")
- {
- auto source = kstd::vector<int>{1, 2, 3};
- auto target = kstd::vector<int>{};
-
- WHEN("copy assigning")
- {
- target = source;
-
- THEN("the target matches the source")
- {
- REQUIRE(target.size() == 3);
- REQUIRE(target[0] == 1);
- REQUIRE(target[2] == 3);
- REQUIRE(source.size() == 3);
- }
- }
-
- WHEN("move assigning")
- {
- target = std::move(source);
-
- THEN("the target assumes the source's data")
- {
- REQUIRE(target.size() == 3);
- REQUIRE(target[0] == 1);
- REQUIRE(target[2] == 3);
- REQUIRE(source.empty());
- }
- }
- }
-
- GIVEN("Vectors with propagating copy allocator")
- {
- auto allocs1 = 0;
- auto allocs2 = 0;
- auto alloc1 = kstd::tests::propagating_allocator<int>{&allocs1};
- auto alloc2 = kstd::tests::propagating_allocator<int>{&allocs2};
-
- auto v1 = kstd::vector<int, kstd::tests::propagating_allocator<int>>{alloc1};
- v1.push_back(1);
- v1.push_back(2);
- auto v2 = kstd::vector<int, kstd::tests::propagating_allocator<int>>{alloc2};
-
- WHEN("copy assigning")
- {
- v2 = v1;
- THEN("the allocator propagates")
- {
- REQUIRE(v2.get_allocator() == v1.get_allocator());
- }
- }
- }
-
- GIVEN("Vectors for copy assignment overlap")
- {
- auto v1 = kstd::vector<int>{1, 2, 3};
- auto v2 = kstd::vector<int>{4, 5};
- v2.reserve(10);
-
- WHEN("copy assigning a larger vector to a smaller one with enough capacity")
- {
- v2 = v1;
- THEN("elements are copied and size is updated")
- {
- REQUIRE(v2.size() == 3);
- REQUIRE(v2.capacity() >= 10);
- REQUIRE(v2[2] == 3);
- }
- }
-
- auto v3 = kstd::vector<int>{1, 2, 3, 4};
- v3.reserve(10);
- WHEN("copy assigning a smaller vector to a larger one")
- {
- v3 = v1;
- THEN("excess elements are destroyed")
- {
- REQUIRE(v3.size() == 3);
- REQUIRE(v3[0] == 1);
- }
- }
- }
-
- GIVEN("Vectors with the same tracking allocator")
- {
- auto allocs = 0;
- 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, kstd::tests::tracking_allocator<int>>{alloc};
-
- WHEN("move assigning")
- {
- v2 = std::move(v1);
- THEN("memory is stolen without allocation")
- {
- REQUIRE(v2.size() == 1);
- REQUIRE(allocs == 1);
- }
- }
- }
-
- GIVEN("Vectors with different non-propagating tracking allocators")
- {
- auto allocs1 = 0;
- auto allocs2 = 0;
- auto alloc1 = kstd::tests::tracking_allocator<int>{&allocs1};
- auto alloc2 = kstd::tests::tracking_allocator<int>{&allocs2};
-
- 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, kstd::tests::tracking_allocator<int>>{alloc2};
- v2.push_back(4);
- v2.push_back(5);
-
- WHEN("move assigning a larger vector to a smaller one without enough capacity")
- {
- v2.shrink_to_fit();
- v2 = std::move(v1);
- THEN("memory is reallocated and elements are moved")
- {
- REQUIRE(v2.size() == 3);
- REQUIRE(allocs2 > 2);
- }
- }
-
- auto v3 = kstd::vector<int, kstd::tests::tracking_allocator<int>>{alloc2};
- v3.reserve(10);
- v3.push_back(4);
- v3.push_back(5);
- WHEN("move assigning a larger vector to a smaller one with enough capacity")
- {
- v3 = std::move(v1);
- THEN("elements are move-assigned over overlap and move-constructed over remainder")
- {
- REQUIRE(v3.size() == 3);
- }
- }
-
- auto v4 = kstd::vector<int, kstd::tests::tracking_allocator<int>>{alloc2};
- v4.reserve(10);
- v4.push_back(4);
- v4.push_back(5);
- v4.push_back(6);
- v4.push_back(7);
- WHEN("move assigning a smaller vector to a larger one with enough capacity")
- {
- v4 = std::move(v1);
- THEN("overlap is moved and excess is destroyed")
- {
- REQUIRE(v4.size() == 3);
- }
- }
- }
-}
-
-SCENARIO("Vector self-assignment operators", "[vector]")
-{
- GIVEN("A populated vector")
- {
- auto v = kstd::vector<int>{1, 2, 3};
- auto const initial_capacity = v.capacity();
- auto const * initial_data = v.data();
-
- WHEN("copy assigning to itself")
- {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpragmas"
-#pragma GCC diagnostic ignored "-Wunknown-warning-option"
-#pragma GCC diagnostic ignored "-Wself-assign-overloaded"
- v = v;
-#pragma GCC diagnostic pop
-
- THEN("the vector remains unchanged")
- {
- REQUIRE(v.size() == 3);
- REQUIRE(v.capacity() == initial_capacity);
- REQUIRE(v.data() == initial_data);
- REQUIRE(v[0] == 1);
- REQUIRE(v[2] == 3);
- }
- }
-
- WHEN("move assigning to itself")
- {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpragmas"
-#pragma GCC diagnostic ignored "-Wunknown-warning-option"
-#pragma GCC diagnostic ignored "-Wself-move"
- v = std::move(v);
-#pragma GCC diagnostic pop
-
- THEN("the vector remains unchanged")
- {
- REQUIRE(v.size() == 3);
- REQUIRE(v.capacity() == initial_capacity);
- REQUIRE(v.data() == initial_data);
- REQUIRE(v[0] == 1);
- REQUIRE(v[2] == 3);
- }
- }
- }
-}
-
-SCENARIO("Vector const accessors and copy insertion", "[vector]")
-{
- GIVEN("A const populated vector")
- {
- auto const v = kstd::vector<int>{10, 20, 30};
-
- WHEN("calling const accessors")
- {
- THEN("elements are read correctly as const references")
- {
- REQUIRE(v.front() == 10);
- REQUIRE(v.back() == 30);
- REQUIRE(v[1] == 20);
- REQUIRE(v.at(1) == 20);
- }
- }
- }
-
- GIVEN("An empty vector and a const lvalue tracker")
- {
- auto v = kstd::vector<kstd::tests::special_member_tracker>{};
- auto const tracker = kstd::tests::special_member_tracker{42};
-
- WHEN("push_back is called with the const lvalue")
- {
- v.push_back(tracker);
-
- THEN("the element is gracefully copy-constructed")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.back().value == 42);
- REQUIRE(v.back().copy_constructed_count == 1);
- REQUIRE(v.back().move_constructed_count == 0);
- }
- }
-
- WHEN("push_back is called with a const lvalue when capacity is sufficient")
- {
- v.reserve(10);
- auto const current_capacity = v.capacity();
- v.push_back(tracker);
-
- THEN("the element is copy-constructed without reallocation")
- {
- REQUIRE(v.size() == 1);
- REQUIRE(v.capacity() == current_capacity);
- REQUIRE(v.back().value == 42);
- REQUIRE(v.back().copy_constructed_count == 1);
- REQUIRE(v.back().move_constructed_count == 0);
- }
- }
- }
-}