From 0eb9b1e367ca3bc978deb18ca9b7ac3dd6bfb637 Mon Sep 17 00:00:00 2001 From: Ricardo Antunes Date: Sat, 16 Sep 2023 19:32:22 +0100 Subject: [PATCH] test(core): cover ArrayTrait --- core/tests/CMakeLists.txt | 1 + core/tests/reflection/external/vector.cpp | 22 ++++++ core/tests/reflection/traits/array.cpp | 87 +++++++++++++++++++++++ core/tests/reflection/traits/array.hpp | 73 +++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 core/tests/reflection/external/vector.cpp create mode 100644 core/tests/reflection/traits/array.cpp create mode 100644 core/tests/reflection/traits/array.hpp diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index 321174a29f..ca39ff6824 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable( reflection/type.cpp reflection/traits/constructible.cpp reflection/external/primitives.cpp + reflection/external/vector.cpp data/fs/embedded_archive.cpp data/fs/standard_archive.cpp diff --git a/core/tests/reflection/external/vector.cpp b/core/tests/reflection/external/vector.cpp new file mode 100644 index 0000000000..c841c29941 --- /dev/null +++ b/core/tests/reflection/external/vector.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include + +#include "../traits/array.hpp" +#include "../traits/constructible.hpp" + +template +static void test(const char* name, std::vector vec, T inserted) +{ + CHECK(reflect>().name() == name); + testArray, T>(vec, vec.size(), &inserted); + testConstructible>(&vec); +} + +TEST_CASE("reflection::reflect>()") +{ + test("std::vector", {5, 4, 1}, 1); + test>>("std::vector>>", {{{'H'}, {'e'}}, {{'y', '!'}}}, + {{'o'}}); +} diff --git a/core/tests/reflection/traits/array.cpp b/core/tests/reflection/traits/array.cpp new file mode 100644 index 0000000000..38ff33621f --- /dev/null +++ b/core/tests/reflection/traits/array.cpp @@ -0,0 +1,87 @@ +#include + +#include +#include +#include + +#include "../../utils.hpp" + +using cubos::core::reflection::ArrayTrait; +using cubos::core::reflection::reflect; + +TEST_CASE("reflection::ArrayTrait") +{ + auto trait = ArrayTrait( + reflect(), [](const void*) -> std::size_t { return 2; }, + [](const void* array, std::size_t index) { return reinterpret_cast(array) + sizeof(int) * index; }); + + SUBCASE("without any resizing operations") + { + int instance[2]; + CHECK(trait.elementType().is()); + CHECK(trait.length(&instance) == 2); + CHECK(trait.get(&instance, 0) == &instance[0]); + CHECK(trait.get(static_cast(&instance), 1) == &instance[1]); + + // No insert or erase operations were provided + CHECK_FALSE(trait.hasInsertDefault()); + CHECK_FALSE(trait.hasInsertCopy()); + CHECK_FALSE(trait.hasInsertMove()); + CHECK_FALSE(trait.hasErase()); + CHECK_FALSE(trait.insertDefault(&instance, 0)); + CHECK_FALSE(trait.insertCopy(&instance, 0, &instance[1])); + CHECK_FALSE(trait.insertMove(&instance, 0, &instance[1])); + CHECK_FALSE(trait.erase(&instance, 0)); + } + + // These subcases below are not really inserting or erasing anything, but they are testing that + // the insert and erase methods are called correctly. + + SUBCASE("with default-constructing inserts") + { + trait.insertDefault([](void* array, std::size_t index) { static_cast(array)[index] = 42; }); + + int instance[2] = {1, 2}; + CHECK(trait.hasInsertDefault()); + CHECK(trait.insertDefault(&instance, 0)); + CHECK(instance[0] == 42); + CHECK(instance[1] == 2); + } + + SUBCASE("with copy-constructing inserts") + { + trait.insertCopy([](void* array, std::size_t index, const void* value) { + static_cast(array)[index] = *static_cast(value); + }); + + int instance[2] = {1, 2}; + CHECK(trait.hasInsertCopy()); + CHECK(trait.insertCopy(&instance, 0, &instance[1])); + CHECK(instance[0] == 2); + CHECK(instance[1] == 2); + } + + SUBCASE("with move-constructing inserts") + { + trait.insertMove([](void* array, std::size_t index, void* value) { + static_cast(array)[index] = *static_cast(value); + }); + + int instance[2] = {1, 2}; + CHECK(trait.hasInsertMove()); + CHECK(trait.insertMove(&instance, 1, &instance[0])); + CHECK(instance[0] == 1); + CHECK(instance[1] == 1); + } + + SUBCASE("with erases") + { + trait.erase([](void* array, std::size_t index) { static_cast(array)[index] = 0; }); + + int instance[2] = {1, 2}; + CHECK(trait.hasErase()); + CHECK(trait.erase(&instance, 1)); + CHECK(instance[0] == 1); + CHECK(instance[1] == 0); + } +} diff --git a/core/tests/reflection/traits/array.hpp b/core/tests/reflection/traits/array.hpp new file mode 100644 index 0000000000..173ac51997 --- /dev/null +++ b/core/tests/reflection/traits/array.hpp @@ -0,0 +1,73 @@ +#include + +#include + +#include +#include + +using cubos::core::reflection::ArrayTrait; +using cubos::core::reflection::reflect; +using cubos::core::reflection::Type; + +/// @brief Checks if a type's ArrayTrait works as expected. +/// @tparam T Type. +/// @tparam E Element type. +/// @param value Value to test. +/// @param length Initial length. +/// @param inserted Optional value to insert. +template +void testArray(T& value, std::size_t length, E* inserted = nullptr) +{ + const Type& type = reflect(); + REQUIRE(type.has()); + const ArrayTrait& trait = type.get(); + + REQUIRE(trait.length(&value) == length); + REQUIRE(trait.elementType().is()); + + if (trait.hasInsertDefault()) + { + trait.insertDefault(&value, length); + CHECK(trait.length(&value) == length + 1); + trait.insertDefault(&value, 0); + CHECK(trait.length(&value) == length + 2); + length += 2; + + if constexpr (std::equality_comparable && std::default_initializable) + { + CHECK(*reinterpret_cast(trait.get(&value, 0)) == E{}); + CHECK(*reinterpret_cast(trait.get(static_cast(&value), length - 1)) == E{}); + } + } + + if (trait.hasErase()) + { + for (; length > 0; --length) + { + trait.erase(&value, length - 1); + CHECK(trait.length(&value) == length - 1); + } + } + + if (inserted) + { + if (trait.hasInsertCopy()) + { + trait.insertCopy(&value, 0, inserted); + CHECK(trait.length(&value) == length + 1); + length += 1; + + if constexpr (std::equality_comparable) + { + CHECK(*reinterpret_cast(trait.get(&value, 0)) == *inserted); + } + } + + if (trait.hasInsertMove()) + { + trait.insertMove(&value, (length + 1) / 2, inserted); + CHECK(trait.length(&value) == length + 1); + length += 1; + } + } +}