diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | tests/src/arithmetic.cpp | 297 | ||||
| -rw-r--r-- | tests/src/constructors.cpp | 102 | ||||
| -rw-r--r-- | tests/src/conversion.cpp | 133 | ||||
| -rw-r--r-- | tests/src/derivation_clause.cpp | 63 | ||||
| -rw-r--r-- | tests/src/equality_comparison.cpp | 102 | ||||
| -rw-r--r-- | tests/src/hash.cpp | 59 | ||||
| -rw-r--r-- | tests/src/io_operators.cpp | 110 | ||||
| -rw-r--r-- | tests/src/iterable.cpp | 1060 | ||||
| -rw-r--r-- | tests/src/relational_operators.cpp | 249 | ||||
| -rw-r--r-- | tests/src/threeway_comparison.cpp | 52 |
11 files changed, 2260 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..47b8331 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,33 @@ +find_package("Catch2" "3.1" + COMPONENTS "Catch2WithMain" + REQUIRED +) + +include("CTest") +include("Catch") + +file(GLOB SOURCES + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + CONFIGURE_DEPENDS + "src/*.cpp" +) + + +add_executable("${PROJECT_NAME}_tests" + ${SOURCES} +) + +target_link_libraries("${PROJECT_NAME}_tests" + "${PROJECT_NAME}::${PROJECT_NAME}" + "Catch2::Catch2WithMain" +) + +target_compile_options("${PROJECT_NAME}_tests" PRIVATE + "$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall>" + "$<$<CXX_COMPILER_ID:GNU,Clang>:-Wextra>" + "$<$<CXX_COMPILER_ID:GNU,Clang>:-Werror>" + "$<$<CXX_COMPILER_ID:GNU,Clang>:-pedantic-errors>" + "$<$<CXX_COMPILER_ID:GNU>:-fconcepts-diagnostics-depth=5>" +) + +catch_discover_tests("${PROJECT_NAME}_tests") diff --git a/tests/src/arithmetic.cpp b/tests/src/arithmetic.cpp new file mode 100644 index 0000000..30c243f --- /dev/null +++ b/tests/src/arithmetic.cpp @@ -0,0 +1,297 @@ +#include "newtype/newtype.hpp" + +#include <catch2/catch_test_macros.hpp> + +#include <type_traits> + +SCENARIO("Addition", "[arithmetic]") +{ + struct addable_type + { + auto constexpr operator+(addable_type const &) const -> addable_type + { + return {}; + }; + }; + + GIVEN("A new_type instance not deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag>; + + THEN("it is not addable") + { + STATIC_REQUIRE(!nt::concepts::addable<type_alias>); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is addable") + { + STATIC_REQUIRE(nt::concepts::addable<type_alias>); + } + } + + GIVEN("A new_type over a non-addable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<void *, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is not addable") + { + STATIC_REQUIRE(!nt::concepts::addable<addable_type> == nt::concepts::addable<type_alias>); + } + } + + GIVEN("A new_type over an addable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<addable_type, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is addable") + { + STATIC_REQUIRE(nt::concepts::addable<addable_type> == nt::concepts::addable<type_alias>); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + + THEN("addition produces the same type") + { + STATIC_REQUIRE(std::is_same_v<type_alias, decltype(std::declval<type_alias const &>() + std::declval<type_alias const &>())>); + } + } + + GIVEN("Two objects of a new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + auto lhs = type_alias{24}; + auto rhs = type_alias{18}; + + THEN("addition produces the correct result with respect to the base type") + { + REQUIRE((lhs + rhs).decay() == 24 + 18); + } + } +} + +SCENARIO("Subtraction", "[arithmetic]") +{ + struct subtractable_type + { + auto constexpr operator-(subtractable_type const &) const -> subtractable_type + { + return {}; + }; + }; + + GIVEN("A new_type not deriving nt::arithmetic") + { + using type_alias = nt::new_type<int, struct tag>; + + THEN("it is not subtractable") + { + STATIC_REQUIRE(!nt::concepts::subtractable<type_alias>); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is subtractable") + { + STATIC_REQUIRE(nt::concepts::subtractable<type_alias>); + } + } + + GIVEN("A new_type over a non-subtractable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<void *, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is not addable") + { + STATIC_REQUIRE(!nt::concepts::subtractable<type_alias>); + } + } + + GIVEN("A new_type over a subtractable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<subtractable_type, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is subtractable") + { + STATIC_REQUIRE(nt::concepts::subtractable<type_alias>); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + + THEN("subtraction produces the same type") + { + STATIC_REQUIRE(std::is_same_v<type_alias, decltype(std::declval<type_alias const &>() - std::declval<type_alias const &>())>); + } + } + + GIVEN("Two objects of a new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + auto lhs = type_alias{24}; + auto rhs = type_alias{18}; + + THEN("subtraction produces the correct result with respect to the base type") + { + REQUIRE((lhs - rhs).decay() == 24 - 18); + } + } +} + +SCENARIO("Multiplication", "[arithmetic]") +{ + struct multipliable_type + { + auto constexpr operator*(multipliable_type const &) const -> multipliable_type + { + return {}; + }; + }; + + GIVEN("A new_type not deriving nt::arithmetic") + { + using type_alias = nt::new_type<int, struct tag>; + + THEN("it is not multipliable") + { + STATIC_REQUIRE(!nt::concepts::multipliable<type_alias>); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is multipliable") + { + STATIC_REQUIRE(nt::concepts::multipliable<type_alias>); + } + } + + GIVEN("A new_type over a non-multipliable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<void *, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is not multipliable") + { + STATIC_REQUIRE(!nt::concepts::multipliable<type_alias>); + } + } + + GIVEN("A new_type over a multipliable class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<multipliable_type, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is multipliable") + { + STATIC_REQUIRE(nt::concepts::multipliable<type_alias>); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + + THEN("multiplication produces the same type") + { + STATIC_REQUIRE(std::is_same_v<type_alias, decltype(std::declval<type_alias const &>() * std::declval<type_alias const &>())>); + } + } + + GIVEN("Two objects of a new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + auto lhs = type_alias{24}; + auto rhs = type_alias{18}; + + THEN("multiplication produces the correct result with respect to the base type") + { + REQUIRE((lhs * rhs).decay() == 24 * 18); + } + } +} + +SCENARIO("Division", "[arithmetic]") +{ + struct dividable_type + { + auto constexpr operator/(dividable_type const &) const -> dividable_type + { + return {}; + }; + }; + + GIVEN("A new_type not deriving nt::arithmetic") + { + using type_alias = nt::new_type<int, struct tag>; + + THEN("it is not divisible") + { + STATIC_REQUIRE(!nt::concepts::divisible<type_alias>); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is divisible") + { + STATIC_REQUIRE(nt::concepts::divisible<type_alias>); + } + } + + GIVEN("A new_type over a non-divisible class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<void *, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is not divisible") + { + STATIC_REQUIRE(!nt::concepts::divisible<type_alias>); + } + } + + GIVEN("A new_type over a divisible class type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<dividable_type, struct tag, deriving(nt::Arithmetic)>; + + THEN("it is divisible") + { + STATIC_REQUIRE(nt::concepts::divisible<type_alias>); + } + } + + GIVEN("A new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + + THEN("division produces the same type") + { + STATIC_REQUIRE(std::is_same_v<type_alias, decltype(std::declval<type_alias const &>() / std::declval<type_alias const &>())>); + } + } + + GIVEN("Two objects of a new_type deriving nt::Arithmetic") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Arithmetic)>; + auto lhs = type_alias{30}; + auto rhs = type_alias{15}; + + THEN("division produces the correct result with respect to the base type") + { + REQUIRE((lhs / rhs).decay() == 30 / 15); + } + } +} diff --git a/tests/src/constructors.cpp b/tests/src/constructors.cpp new file mode 100644 index 0000000..b866f2e --- /dev/null +++ b/tests/src/constructors.cpp @@ -0,0 +1,102 @@ +#include "newtype/newtype.hpp" + +#include <catch2/catch_template_test_macros.hpp> +#include <catch2/catch_test_macros.hpp> + +#include <type_traits> + +using fundamental_types = std::tuple<bool, + char, + unsigned char, + signed char, + wchar_t, + char16_t, + char32_t, + short, + unsigned short, + int, + unsigned int, + long, + unsigned long, + long long, + unsigned long long, + float, + double, + long double>; + +TEMPLATE_LIST_TEST_CASE("Scenario: Construction from Fundamental Types", "[construction]", fundamental_types) +{ + GIVEN("A new_type over a fundamental type") + { + using type_alias = nt::new_type<TestType, struct tag>; + + THEN("objects of it can be constructed from the fundamental type") + { + STATIC_REQUIRE(std::is_constructible_v<type_alias, TestType>); + } + } +} + +SCENARIO("Default Construction", "[construction]") +{ + struct not_default_constructible + { + not_default_constructible() = delete; + }; + + GIVEN("A new_type over a default-constructible type") + { + using type_alias = nt::new_type<int, struct tag>; + static_assert(std::is_default_constructible_v<type_alias::base_type>); + + THEN("it is default-constructible") + { + STATIC_REQUIRE(std::is_default_constructible_v<type_alias>); + } + } + + GIVEN("A new_type over a type that is not default-constructible") + { + using type_alias = nt::new_type<not_default_constructible, struct tag>; + static_assert(!std::is_default_constructible_v<type_alias::base_type>); + + THEN("it is not default-constructible") + { + STATIC_REQUIRE_FALSE(std::is_default_constructible_v<type_alias>); + } + } +} + +SCENARIO("Copy Construction", "[construction]") +{ + struct not_copy_constructible + { + not_copy_constructible() = default; + not_copy_constructible(not_copy_constructible const &) = delete; + not_copy_constructible(not_copy_constructible &&) = default; + auto operator=(not_copy_constructible const &) -> not_copy_constructible & = default; + auto operator=(not_copy_constructible &&) -> not_copy_constructible & = default; + }; + + GIVEN("A new_type over a copy-constructible type") + { + using type_alias = nt::new_type<int, struct tag>; + static_assert(std::is_copy_constructible_v<type_alias::base_type>); + + THEN("it is copy-constructible") + { + STATIC_REQUIRE(std::is_copy_constructible_v<type_alias>); + } + } + + GIVEN("A new_type over a type that is not copy-constructible") + { + using type_alias = nt::new_type<not_copy_constructible, struct tag>; + static_assert(!std::is_copy_constructible_v<type_alias::base_type>); + + THEN("it is not copy-constructible") + { + STATIC_REQUIRE_FALSE(std::is_copy_constructible_v<type_alias>); + } + } +} diff --git a/tests/src/conversion.cpp b/tests/src/conversion.cpp new file mode 100644 index 0000000..e7ce51c --- /dev/null +++ b/tests/src/conversion.cpp @@ -0,0 +1,133 @@ +#include "newtype/newtype.hpp" + +#include <catch2/catch_template_test_macros.hpp> +#include <catch2/catch_test_macros.hpp> +#include <catch2/generators/catch_generators_all.hpp> + +#include <string> +#include <type_traits> +#include <vector> + +using test_types = std::tuple<bool, char, int, double, std::string>; + +TEMPLATE_LIST_TEST_CASE("Scenario: Implicit Conversions", "[conversion]", test_types) +{ + GIVEN("A new_type not deriving nt::ImplicitConversion") + { + using type_alias = nt::new_type<TestType, struct conversion_test>; + + THEN("it is not implicitly convertible to the base type") + { + STATIC_REQUIRE(!std::is_convertible_v<type_alias, TestType>); + } + } + + GIVEN("A new_type deriving nt::ImplicitConversion") + { + using type_alias = nt::new_type<TestType, struct conversion_test, deriving(nt::ImplicitConversion)>; + + THEN("it is implicitly convertible to the base type") + { + STATIC_REQUIRE(std::is_convertible_v<type_alias, TestType>); + } + } +} + +TEMPLATE_LIST_TEST_CASE("Scenario: Decay", "[conversion]", test_types) +{ + GIVEN("Any new_type") + { + using type_alias = nt::new_type<TestType, struct conversion_test>; + + THEN("it's decay() member function returns a value of the base type") + { + STATIC_REQUIRE(std::is_same_v<TestType, decltype(std::declval<type_alias>().decay())>); + } + } + + GIVEN("Any new_type") + { + using type_alias = nt::new_type<TestType, struct conversion_test>; + + WHEN("an object of that type is constructed") + { + auto integral_value = GENERATE(take(64, random(0, 127))); + auto value = [integral_value] { + if constexpr (std::is_same_v<std::string, TestType>) + { + return std::to_string(integral_value); + } + else + { + return static_cast<TestType>(integral_value); + } + }(); + auto obj = type_alias{value}; + + THEN("it's decay() member function return the underlying value") + { + REQUIRE(obj.decay() == value); + } + } + } +} + +SCENARIO("Nothrow Decay") +{ + struct strange_type + { + strange_type(strange_type const &) noexcept(false) + { + } + }; + + GIVEN("A new_type over a nothrow-copyable type") + { + using type_alias = nt::new_type<int, struct conversion_test>; + + THEN("the decay member function is nothrow-invokable") + { + STATIC_REQUIRE(noexcept(std::declval<type_alias>().decay())); + } + } + + GIVEN("A new_type over a non-nothrow-copyable type") + { + using type_alias = nt::new_type<strange_type, struct conversion_test>; + + THEN("the decay member function is not nothrow-invokable") + { + STATIC_REQUIRE(!noexcept(std::declval<type_alias>().decay())); + } + } +} + +SCENARIO("Nothrow Conversion") +{ + struct strange_type + { + strange_type(strange_type const &) noexcept(false) + { + } + }; + + GIVEN("A new_type over a nothrow-copy-constructible type") + { + using type_alias = nt::new_type<int, struct conversion_test>; + + THEN("the decay member function is nothrow-invokable") + { + STATIC_REQUIRE(noexcept(std::declval<type_alias>().operator int())); + } + } + + GIVEN("A new_type over a non-nothrow-copy-constructible type") + { + using type_alias = nt::new_type<strange_type, struct conversion_test>; + + THEN("the decay member function is not nothrow-invokable") + { + STATIC_REQUIRE(!noexcept(std::declval<type_alias>().operator strange_type())); + } + } +} diff --git a/tests/src/derivation_clause.cpp b/tests/src/derivation_clause.cpp new file mode 100644 index 0000000..78bd3d4 --- /dev/null +++ b/tests/src/derivation_clause.cpp @@ -0,0 +1,63 @@ +#include "newtype/newtype.hpp" + +#include <catch2/catch_test_macros.hpp> + +#include <string> + +SCENARIO("Derivation Clause", "[infrastructure]") +{ + GIVEN("An empty derivation clause") + { + auto clause = nt::deriving(); + + THEN("it doesn't contain any derivable") + { + STATIC_REQUIRE_FALSE(nt::derives<decltype(clause), nt::Show>); + } + } + + GIVEN("A derivation clause containing only nt::Show") + { + auto clause = deriving(nt::Show); + + THEN("it doesn't contain nt::EqBase") + { + STATIC_REQUIRE_FALSE(nt::derives<decltype(clause), nt::EqBase>); + } + + THEN("it contains nt::Show") + { + STATIC_REQUIRE(nt::derives<decltype(clause), nt::Show>); + } + } + + GIVEN("A derivation clause containing only nt::Show and nt::EqBase") + { + auto clause = deriving(nt::Show, nt::EqBase); + + THEN("it contains nt::EqBase") + { + STATIC_REQUIRE(nt::derives<decltype(clause), nt::EqBase>); + } + + THEN("it contains nt::Show") + { + STATIC_REQUIRE(nt::derives<decltype(clause), nt::Show>); + } + + THEN("it contains both nt::Show and nt::EqBase") + { + STATIC_REQUIRE(nt::derives<decltype(clause), nt::Show, nt::EqBase>); + } + + THEN("it does not contain nt::Arithmetic") + { + STATIC_REQUIRE_FALSE(nt::derives<decltype(clause), nt::Arithmetic>); + } + + THEN("it does not contain both nt::Arithmetic and nt::Show") + { + STATIC_REQUIRE_FALSE(nt::derives<decltype(clause), nt::Arithmetic, nt::Show>); + } + } +} diff --git a/tests/src/equality_comparison.cpp b/tests/src/equality_comparison.cpp new file mode 100644 index 0000000..e0be7f9 --- /dev/null +++ b/tests/src/equality_comparison.cpp @@ -0,0 +1,102 @@ +#include "newtype/newtype.hpp" + +#include <catch2/catch_test_macros.hpp> + +#include <string> +#include <type_traits> +#include <utility> + +SCENARIO("Equality Comparison", "[compare]") +{ + GIVEN("A new_type over an equality comparable type") + { + using type_alias = nt::new_type<int, struct tag>; + + THEN("two objects of it with the same value compare equal") + { + REQUIRE(type_alias{42} == type_alias{42}); + } + + THEN("two objects of it with the same value do not compare not-equal") + { + REQUIRE_FALSE(type_alias{42} != type_alias{42}); + } + + THEN("two object of it with different values do not compare equal") + { + REQUIRE_FALSE(type_alias{42} == type_alias{43}); + } + + THEN("two object of it with different values compare not-equal") + { + REQUIRE(type_alias{42} != type_alias{43}); + } + + THEN("equality comparison returns bool") + { + STATIC_REQUIRE(std::is_same_v<bool, decltype(std::declval<type_alias>() == std::declval<type_alias>())>); + } + + THEN("inequality comparison returns bool") + { + STATIC_REQUIRE(std::is_same_v<bool, decltype(std::declval<type_alias>() != std::declval<type_alias>())>); + } + } + + GIVEN("A new_type deriving nt::EqBase") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::EqBase)>; + + THEN("an instance of it compares equal to the equivalent base type value") + { + REQUIRE(type_alias{42} == 42); + } + + THEN("an instance of it comapres not-equal to a different base type value") + { + REQUIRE(type_alias{42} != 43); + } + } + + GIVEN("A new_type over a nothrow-comparable type") + { + using type_alias = nt::new_type<int, struct tag>; + + static_assert(nt::concepts::nothrow_equality_comparable<type_alias::base_type>); + static_assert(nt::concepts::nothrow_inequality_comparable<type_alias::base_type>); + + THEN("it is nothrow-equality-comparable") + { + STATIC_REQUIRE(nt::concepts::nothrow_equality_comparable<type_alias>); + } + + THEN("it is nothrow-inequality-comparable") + { + STATIC_REQUIRE(nt::concepts::nothrow_inequality_comparable<type_alias>); + } + } + + GIVEN("A new_type over a non-nothrow-comparable type") + { + struct not_nothrow_comparable + { + auto operator==(not_nothrow_comparable) const noexcept(false) -> bool; + auto operator!=(not_nothrow_comparable) const noexcept(false) -> bool; + }; + + using type_alias = nt::new_type<not_nothrow_comparable, struct tag>; + + static_assert(!nt::concepts::nothrow_equality_comparable<type_alias::base_type>); + static_assert(!nt::concepts::nothrow_inequality_comparable<type_alias::base_type>); + + THEN("it is not nothrow-equality-comparable") + { + STATIC_REQUIRE_FALSE(nt::concepts::nothrow_equality_comparable<type_alias>); + } + + THEN("it is not nothrow-inequality-comparable") + { + STATIC_REQUIRE_FALSE(noexcept(std::declval<type_alias>() != std::declval<type_alias>())); + } + } +} diff --git a/tests/src/hash.cpp b/tests/src/hash.cpp new file mode 100644 index 0000000..94f252f --- /dev/null +++ b/tests/src/hash.cpp @@ -0,0 +1,59 @@ +#include "newtype/newtype.hpp" + +#include <catch2/catch_template_test_macros.hpp> +#include <catch2/catch_test_macros.hpp> + +#include <string> +#include <unordered_map> + +TEMPLATE_TEST_CASE("Hash", "[hash]", std::string, int) +{ + GIVEN("A new_type not deriving nt::Hash") + { + using type_alias = nt::new_type<TestType, struct tag>; + static_assert(nt::concepts::hashable<typename type_alias::base_type>); + + THEN("it is not hashable") + { + STATIC_REQUIRE_FALSE(nt::concepts::hashable<type_alias>); + } + } + + GIVEN("A new_type over a hashable type deriving nt::Hash") + { + using type_alias = nt::new_type<TestType, struct tag, deriving(nt::Hash)>; + static_assert(nt::concepts::hashable<typename type_alias::base_type>); + + THEN("it is hashable") + { + STATIC_REQUIRE(nt::concepts::hashable<type_alias>); + } + } + + GIVEN("A new_type over a non-hashable type deriving nt::Hash") + { + struct non_hashable + { + }; + using type_alias = nt::new_type<non_hashable, struct tag, deriving(nt::Hash)>; + static_assert(!nt::concepts::hashable<typename type_alias::base_type>); + + THEN("it is not hashable") + { + STATIC_REQUIRE_FALSE(nt::concepts::hashable<type_alias>); + } + } + + GIVEN("A hashable new_type") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Hash)>; + static_assert(nt::concepts::hashable<typename type_alias::base_type>); + + THEN("it can be used a the key in an unordered_map") + { + auto map = std::unordered_map<type_alias, int>{}; + map[type_alias{42}] = 43; + REQUIRE(map[type_alias{42}] == 43); + } + } +} diff --git a/tests/src/io_operators.cpp b/tests/src/io_operators.cpp new file mode 100644 index 0000000..f7f8f29 --- /dev/null +++ b/tests/src/io_operators.cpp @@ -0,0 +1,110 @@ +#include "newtype/newtype.hpp" + +#include <catch2/catch_test_macros.hpp> + +#include <iosfwd> +#include <sstream> +#include <string> +#include <type_traits> +#include <utility> + +inline namespace traits_extensions +{ + + template<typename T, typename = void> + struct has_stream_input : std::false_type + { + }; + + template<typename T> + struct has_stream_input<T, std::void_t<decltype(std::declval<std::istream &>() >> std::declval<T &>())>> : std::true_type + { + }; + + template<typename T> + auto constexpr has_stream_input_v = has_stream_input<T>::value; + + template<typename T, typename = void> + struct has_stream_output : std::false_type + { + }; + + template<typename T> + struct has_stream_output<T, std::void_t<decltype(std::declval<std::ostream &>() << std::declval<T &>())>> : std::true_type + { + }; + + template<typename T> + auto constexpr has_stream_output_v = has_stream_output<T>::value; + +} // namespace traits_extensions + +SCENARIO("Stream Input") +{ + GIVEN("A new_type over a stream-inputtable type deriving nt::Read") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Read)>; + static_assert(has_stream_input_v<type_alias::base_type>); + + THEN("it has the stream input operator") + { + STATIC_REQUIRE(has_stream_input_v<type_alias>); + } + + THEN("an instance of it can be read from an std::istream") + { + auto obj = type_alias{}; + auto str = std::istringstream{"42"}; + + str >> obj; + + REQUIRE(obj.decay() == 42); + } + } + + GIVEN("A new_type over a non-stream-inputtable type deriving nt::Read") + { + using type_alias = nt::new_type<std::istream, struct tag, deriving(nt::Read)>; + static_assert(!has_stream_input_v<type_alias::base_type>); + + THEN("it does not have the input operator") + { + STATIC_REQUIRE(!has_stream_input_v<type_alias>); + } + } +} + +SCENARIO("Stream Output") +{ + GIVEN("A new_type over a stream-outputtable type deriving nt::Show") + { + using type_alias = nt::new_type<int, struct tag, deriving(nt::Show)>; + static_assert(has_stream_output_v<type_alias::base_type>); + + THEN("it has the stream output operator") + { + STATIC_REQUIRE(has_stream_output_v<type_alias>); + } + + THEN("an instance of it can be written to an std::ostream") + { + auto obj = type_alias{42}; + auto str = std::ostringstream{}; + + str << obj; + + REQUIRE(str.str() == "42"); + } + } + + GIVEN("A new_type over a non-stream-outputtable type deriving nt::Show") + { + using type_alias = nt::new_type<std::istream, struct tag, deriving(nt::Show)>; + static_assert(!has_stream_output_v<type_alias::base_type>); + + THEN("it does not have the output operator") + { + STATIC_REQUIRE(!has_stream_output_v<type_alias>); + } + } +} diff --git a/tests/src/iterable.cpp b/tests/src/iterable.cpp new file mode 100644 index 0000000..85b7edc --- /dev/null +++ b/tests/src/iterable.cpp @@ -0,0 +1,1060 @@ +#include "newtype/newtype.hpp" + +#include <catch2/catch_test_macros.hpp> + +#include <algorithm> +#include <array> +#include <iterator> +#include <numeric> + +namespace iterable_types +{ + + struct with_member + { + using iterator = char *; + using const_iterator = char const *; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + auto constexpr begin() -> iterator; + auto constexpr begin() const -> const_iterator; + auto constexpr cbegin() const -> const_iterator; + auto constexpr rbegin() -> reverse_iterator; + auto constexpr rbegin() const -> const_reverse_iterator; + auto constexpr crbegin() const -> const_reverse_iterator; + + auto constexpr end() -> iterator; + auto constexpr end() const -> const_iterator; + auto constexpr cend() const -> const_iterator; + auto constexpr rend() -> reverse_iterator; + auto constexpr rend() const -> const_reverse_iterator; + auto constexpr crend() const -> const_reverse_iterator; + }; + + struct with_free + { + using iterator = char *; + using const_iterator = char const *; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + }; + + auto constexpr begin(with_free &) -> with_free::iterator; + auto constexpr begin(with_free const &) -> with_free::const_iterator; + auto constexpr cbegin(with_free const &) -> with_free::const_iterator; + auto constexpr rbegin(with_free &) -> with_free::reverse_iterator; + auto constexpr rbegin(with_free const &) -> with_free::const_reverse_iterator; + auto constexpr crbegin(with_free const &) -> with_free::const_reverse_iterator; + auto constexpr end(with_free &) -> with_free::iterator; + auto constexpr end(with_free const &) -> with_free::const_iterator; + auto constexpr cend(with_free const &) -> with_free::const_iterator; + auto constexpr rend(with_free &) -> with_free::reverse_iterator; + auto constexpr rend(with_free const &) -> with_free::const_reverse_iterator; + auto constexpr crend(with_free const &) -> with_free::const_reverse_iterator; + +} // namespace iterable_types + +SCENARIO("Iterators", "[iterators]") +{ + GIVEN("A new_type over a non-iterable base type not deriving nt::Iterable") + { + using type_alias = nt::new_type<int, struct tag>; + static_assert(!nt::concepts::beginnable<type_alias::base_type>); + static_assert(!nt::concepts::beginnable<type_alias::base_type const>); + static_assert(!nt::concepts::cbeginnable<type_alias::base_type>); + static_assert(!nt::concepts::rbeginnable<type_alias::base_type>); + static_assert(!nt::concepts::rbeginnable<type_alias::base_type const>); + static_assert(!nt::concepts::crbeginnable<type_alias::base_type>); + static_assert(!nt::concepts::endable<type_alias::base_type>); + static_assert(!nt::concepts::endable<type_alias::base_type const>); + static_assert(!nt::concepts::cendable<type_alias::base_type>); + static_assert(!nt::concepts::rendable<type_alias::base_type>); + static_assert(!nt::concepts::rendable<type_alias::base_type const>); + static_assert(!nt::concepts::crendable<type_alias::base_type>); + + THEN("it has no begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::beginnable<type_alias>); + } + + THEN("it has no constant begin") + { + STATIC_REQUIRE_FALSE(nt::concepts::beginnable<type_alias const>); + } + + THEN("it has no cbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::cbeginnable<type_alias>); + } + + THEN("it has no rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable<type_alias>); + } + + THEN("it has no constant rbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::rbeginnable<type_alias const>); + } + + THEN("it has no crbegin") + { + STATIC_REQUIRE_FALSE(nt::concepts::crbeginnable<type_alias>); + } + + THEN("it has no end") + { + STATIC_REQUIRE_FALSE(nt::concepts::endable<type_alias>); + } + + THEN("it has no constant end") + { + STATIC_REQUIRE_FALSE(nt::concepts::endable<type_alias const>); + } + + THEN("it has no cend") + { + STAT |
