From 357936b639eb809052593085ffa3b37d55bca578 Mon Sep 17 00:00:00 2001 From: Max Luebke Date: Fri, 3 Mar 2023 14:16:26 +0100 Subject: [PATCH] test: add test cases for Field class --- CMakeLists.txt | 10 +++ test/CMakeLists.txt | 11 +++ test/setup.cpp | 2 + test/testDataStructures.cpp | 175 ++++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+) create mode 100644 test/CMakeLists.txt create mode 100644 test/setup.cpp create mode 100644 test/testDataStructures.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a277ed9c..01946e0b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,9 +24,19 @@ add_subdirectory(data) add_subdirectory(app) add_subdirectory(bench/dolo_diffu_inner) +# as tug will also pull in doctest as a dependency +set(TUG_ENABLE_TESTING OFF CACHE BOOL "" FORCE) + add_subdirectory(ext/tug EXCLUDE_FROM_ALL) add_subdirectory(ext/phreeqcrm EXCLUDE_FROM_ALL) +option(POET_ENABLE_TESTING "Build test suite for POET" OFF) + +if (POET_ENABLE_TESTING) + add_subdirectory(ext/doctest EXCLUDE_FROM_ALL) + add_subdirectory(test) +endif() + option(BUILD_DOC "Build documentation with doxygen" OFF) if(BUILD_DOC) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 000000000..9d3a5ac43 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB test_SRC + CONFIGURE_DEPENDS + "*.cpp" "*.c") + +add_executable(testPOET ${test_SRC}) +target_link_libraries(testPOET doctest poet_lib) + +add_custom_target(check + COMMAND $ + DEPENDS testPOET +) diff --git a/test/setup.cpp b/test/setup.cpp new file mode 100644 index 000000000..0a3f254ea --- /dev/null +++ b/test/setup.cpp @@ -0,0 +1,2 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include diff --git a/test/testDataStructures.cpp b/test/testDataStructures.cpp new file mode 100644 index 000000000..c3046f275 --- /dev/null +++ b/test/testDataStructures.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace poet; + +#define CHECK_AND_FAIL_LOOP(val1, val2) \ + if (val1 != val2) { \ + FAIL_CHECK("Value differs: ", val1, " != ", val2); \ + } + +TEST_CASE("Field") { + constexpr uint32_t VEC_SIZE = 5; + constexpr uint32_t VEC_COUNT = 3; + constexpr double INIT_VAL = 1; + + Field dut(VEC_SIZE); + + std::vector names = {"C", "Ca", "Na"}; + std::vector init_values(names.size(), + FieldColumn(VEC_SIZE, INIT_VAL)); + + SUBCASE("Initialize field with 2D vector") { + dut.InitFromVec(init_values, names); + + auto props = dut.GetProps(); + + CHECK_EQ(names.size(), props.size()); + + const auto res = dut["C"]; + + CHECK_EQ(res.size(), VEC_SIZE); + + for (const auto &elem : res) { + CHECK_AND_FAIL_LOOP(elem, INIT_VAL); + } + } + + SUBCASE("Initialize field with 2D vector") { + std::vector init_values(VEC_SIZE * VEC_COUNT, 1); + dut.InitFromVec(init_values, names); + + auto props = dut.GetProps(); + + CHECK_EQ(names.size(), props.size()); + + const auto res = dut["C"]; + + CHECK_EQ(res.size(), VEC_SIZE); + + for (const auto &elem : res) { + CHECK_AND_FAIL_LOOP(elem, INIT_VAL); + } + } + + dut.InitFromVec(init_values, names); + + SUBCASE("Return vector") { + std::vector output = dut.AsVector(); + + CHECK(output.size() == VEC_SIZE * VEC_COUNT); + } + + constexpr double NEW_VAL = 2.; + std::vector new_val_vec(VEC_SIZE, NEW_VAL); + + dut["C"] = new_val_vec; + + SUBCASE("Check manipulation of column") { + + auto test_it = new_val_vec.begin(); + + for (const auto &val : dut["C"]) { + CHECK_EQ(val, *test_it); + test_it++; + } + } + + SUBCASE("Check correctly manipulated values of 1D vector") { + auto out_res = dut.AsVector(); + auto out_it = out_res.begin(); + + for (uint32_t i = 0; i < VEC_SIZE; i++, out_it++) { + CHECK_AND_FAIL_LOOP(NEW_VAL, *out_it); + } + + for (; out_it != out_res.end(); out_it++) { + CHECK_AND_FAIL_LOOP(INIT_VAL, *out_it); + } + } + + std::vector new_field(VEC_SIZE * VEC_COUNT); + + for (uint32_t i = 0; i < VEC_COUNT; i++) { + for (uint32_t j = 0; j < VEC_SIZE; j++) { + new_field[j + (i * VEC_SIZE)] = (double)(i + 1) / (double)(j + 1); + } + } + + dut.SetFromVector(new_field); + + SUBCASE("SetFromVector return correct field vector") { + auto ret_vec = dut.AsVector(); + + auto ret_it = ret_vec.begin(); + auto new_it = new_field.begin(); + + for (; ret_it != ret_vec.end(); ret_it++, new_it++) { + CHECK_AND_FAIL_LOOP(*ret_it, *new_it); + } + } + + SUBCASE("Get single column with new values") { + auto new_it = new_field.begin() + 2 * VEC_SIZE; + auto ret_vec = dut["Na"]; + auto ret_it = ret_vec.begin(); + + CHECK_EQ(ret_vec.size(), VEC_SIZE); + + for (; ret_it != ret_vec.end(); ret_it++, new_it++) { + CHECK_AND_FAIL_LOOP(*ret_it, *new_it); + } + } + + std::vector mg_vec = {1, 2, 3, 4, 5}; + dut["Mg"] = mg_vec; + + SUBCASE("Operator creates new element and places it at the end") { + + auto new_props = dut.GetProps(); + + CHECK_EQ(new_props.size(), 4); + CHECK_EQ(new_props[3], "Mg"); + + auto ret_vec = dut.As2DVector(); + auto mg_vec_it = mg_vec.begin(); + + for (const auto &val : ret_vec[3]) { + CHECK_AND_FAIL_LOOP(val, *(mg_vec_it++)); + } + } + + // reset field + names = dut.GetProps(); + dut.SetFromVector( + std::vector(names.size(), FieldColumn(VEC_SIZE, INIT_VAL))); + + constexpr double SOME_OTHER_VAL = -0.5; + Field some_other_field(VEC_SIZE); + std::vector some_other_props = {"Na", "Cl"}; + std::vector some_other_values(VEC_SIZE * some_other_props.size(), + SOME_OTHER_VAL); + + some_other_field.InitFromVec(some_other_values, some_other_props); + + SUBCASE("Update existing field from another field") { + dut.UpdateFromField(some_other_field); + + auto ret_vec = dut.As2DVector(); + auto ret_it = ret_vec.begin(); + + for (const auto &prop : names) { + const auto &curr_vec = *(ret_it++); + + for (const auto &val : curr_vec) { + CHECK_AND_FAIL_LOOP((prop == "Na" ? SOME_OTHER_VAL : INIT_VAL), val); + } + } + } +}