diff --git a/2023/src/test00_ds.cpp b/2023/src/test00_ds.cpp index 5bc0192..eb8bc0d 100644 --- a/2023/src/test00_ds.cpp +++ b/2023/src/test00_ds.cpp @@ -6,12 +6,17 @@ * Description: Unit tests for data structures. *****************************************************************************/ -#include "unit_test/unit_test.hpp" +#include "data_structures.hpp" // IWYU pragma: associated +#include "unit_test/pretty_print.hpp" // for repr +#include "unit_test/unit_test.hpp" // for ManualTest, fix_exit_code +#include "util/util.hpp" // for demangle -#include "data_structures.hpp" +#include // for array #include // for size_t #include // for greater -#include // for string, to_string, string_literals +#include // for cerr +#include // for string +#include // for type_info namespace aoc::ds::test { @@ -81,11 +86,124 @@ std::size_t test_pairing_heap_min() { return test.done(), test.num_failed(); } +template +std::size_t test_grid() { + const std::string type_name = util::demangle(typeid(T).name()); + unit_test::ManualTest test("aoc::ds::Grid<" + type_name + ">"); + std::array arr; + { + int i = 0; + for (auto &x : arr) { + x = i++; + } + } + Grid grid(5, 10, arr); + DO_TEST(grid.in_bounds(4, 9)); + DO_TEST(!grid.in_bounds(5, 3)); + DO_TEST(!grid.in_bounds(0, 10)); + DO_TEST(!grid.in_bounds(-2, 4)); + test( + [&grid]() { + using pretty_print::repr; + auto col_it = grid.begin(); + for (int y = 0; y < grid.height; ++y, ++col_it) { + assert(col_it >= grid.begin()); + assert(col_it == grid.begin() + y); + auto row_it = col_it->begin(); + for (int x = 0; x < grid.width; ++x, ++row_it) { + if (grid.at(x, y) != *row_it) { + std::cerr + << "mismatch at " << x << ", " << y << ": expected " + << pretty_print::repr(grid.at(x, y), true) + << ", got " << pretty_print::repr(*row_it, true) + << "\n"; + return false; + } + } + if (row_it != col_it->end()) { + std::cerr << "iterator for row " << y << " not at end\n"; + return false; + } + } + if (col_it != grid.end()) { + std::cerr << "grid iterator not at end\n"; + return false; + } + return true; + }, + "input iterator"); + test( + [&grid]() { + using pretty_print::repr; + auto col_it = grid.cbegin(); + for (int y = 0; y < grid.height; ++y, ++col_it) { + assert(col_it >= grid.begin()); + assert(col_it == grid.begin() + y); + assert(col_it >= grid.cbegin()); + assert(col_it == grid.cbegin() + y); + auto row_it = col_it->begin(); + for (int x = 0; x < grid.width; ++x, ++row_it) { + if (grid.at(x, y) != *row_it) { + std::cerr + << "mismatch at " << x << ", " << y << ": expected " + << pretty_print::repr(grid.at(x, y), true) + << ", got " << pretty_print::repr(*row_it, true) + << "\n"; + return false; + } + } + if (row_it != col_it->end()) { + std::cerr << "iterator for row " << y << " not at end\n"; + return false; + } + } + if (col_it != grid.cend()) { + std::cerr << "grid iterator not at end\n"; + return false; + } + return true; + }, + "input const_iterator"); + test( + [&grid]() { + using pretty_print::repr; + auto col_it = grid.begin(); + for (int y = 0; y < grid.height; ++y, ++col_it) { + assert(col_it >= grid.begin()); + assert(col_it == grid.begin() + y); + auto row_it = col_it->begin(); + for (int x = 0; x < grid.width; ++x, ++row_it) { + auto new_val = 1 - *row_it; + *row_it = new_val; + if (grid.at(x, y) != new_val) { + std::cerr << "writing failed at " << x << ", " << y + << "\n"; + return false; + } + } + if (row_it != col_it->end()) { + std::cerr << "iterator for row " << y << " not at end\n"; + return false; + } + } + if (col_it != grid.end()) { + std::cerr << "grid iterator not at end\n"; + return false; + } + return true; + }, + "output iterator"); + + return test.done(), test.num_failed(); +} + } // namespace aoc::ds::test int main() { std::size_t failed_count = 0; failed_count += aoc::ds::test::test_pairing_heap_max(); failed_count += aoc::ds::test::test_pairing_heap_min(); + failed_count += aoc::ds::test::test_grid(); + failed_count += aoc::ds::test::test_grid(); return unit_test::fix_exit_code(failed_count); } diff --git a/2023/src/unit_test/unit_test.hpp b/2023/src/unit_test/unit_test.hpp index 038d769..d34e395 100644 --- a/2023/src/unit_test/unit_test.hpp +++ b/2023/src/unit_test/unit_test.hpp @@ -4,6 +4,7 @@ #include // for transform, min #include // for assert #include // for strong_ordering +#include // for predicate #include // for size_t #include // for abs #include // for path @@ -641,11 +642,23 @@ PureTest(const std::string &, std::function, int) -> PureTest; struct ManualTest : public BaseTest { + using runner_type = TestRunner; + explicit ManualTest(const std::string &name) : BaseTest(name) {} template bool operator()(T &&passed, const std::string ¬e = "") { - return (*this)(bool(std::forward(passed)), note); + if constexpr (std::predicate) { + // passed is a function that takes no arguments and returns a + // boolean-testable result + ++test_num; + TestRunner runner(passed, 0); + TestResult result = runner.run_on_args(true, note); + record_result(result); + return result.passed; + } else { + return (*this)(bool(std::forward(passed)), note); + } } bool operator()(bool passed, const std::string ¬e = "") { ++test_num; @@ -658,14 +671,32 @@ struct ManualTest : public BaseTest { namespace { [[maybe_unused]] void _lint_helper() { - unit_test::PureTest test( - "foobar", - +[](const std::vector &vec1, const std::vector &vec2) - -> std::strong_ordering { return vec1 <=> vec2; }); - test({1, 2}, {1, 2}, std::strong_ordering::equal); - test({1, 2, 3}, {1, 2}, std::strong_ordering::greater); - test({2, 2, 3}, {1, 2}, std::strong_ordering::less, "note"); - test.done(); + { + unit_test::PureTest test( + "foobar", + +[](const std::vector &vec1, const std::vector &vec2) + -> std::strong_ordering { return vec1 <=> vec2; }); + test({1, 2}, {1, 2}, std::strong_ordering::equal); + test({1, 1}, {1, 2}, std::strong_ordering::less); + test({2, 2}, {1, 2}, std::strong_ordering::greater); + test({1, 2, 3}, {1, 2}, std::strong_ordering::greater); + test({1, 1, 3}, {1, 2}, std::strong_ordering::less, "note"); + test.done(); + } + { + const auto count_chars = + +[](const std::string &s) -> std::unordered_map { + std::unordered_map counts; + for (char ch : s) { + ++counts[ch]; + } + return counts; + }; + unit_test::PureTest test("count_chars", count_chars); + test("abc", {{'a', 1}, {'b', 1}, {'c', 1}}); + test("banana", {{'a', 3}, {'b', 1}, {'n', 2}}); + test.done(); + } } } // namespace