From d359e46818536156e62dac859265b144952ea530 Mon Sep 17 00:00:00 2001 From: q66 Date: Sun, 23 Apr 2017 17:54:51 +0200 Subject: [PATCH] add some basic unit test infra to allow testing together with impl --- ostd/range.hh | 13 ++++++ ostd/unit_test.hh | 113 ++++++++++++++++++++++++++++++++++++++++++++++ test_runner.cc | 25 +++++----- tests/range.cc | 9 ++++ 4 files changed, 147 insertions(+), 13 deletions(-) create mode 100644 ostd/unit_test.hh create mode 100644 tests/range.cc diff --git a/ostd/range.hh b/ostd/range.hh index e8fc731..43ebe06 100644 --- a/ostd/range.hh +++ b/ostd/range.hh @@ -37,6 +37,8 @@ #ifndef OSTD_RANGE_HH #define OSTD_RANGE_HH +#include "ostd/unit_test.hh" + #include #include #include @@ -1328,6 +1330,17 @@ inline auto range(T v) { return detail::number_range(v); } +OSTD_UNIT_TEST(range, { + auto r = range(5, 10, 2); + fail_if(r.empty() || (r.front() != 5)); + r.pop_front(); + fail_if(r.empty() || (r.front() != 7)); + r.pop_front(); + fail_if(r.empty() || (r.front() != 9)); + r.pop_front(); + fail_if(!r.empty()); +}); + namespace detail { template struct reverse_range: input_range> { diff --git a/ostd/unit_test.hh b/ostd/unit_test.hh new file mode 100644 index 0000000..5360857 --- /dev/null +++ b/ostd/unit_test.hh @@ -0,0 +1,113 @@ +/** @defgroup Testing + * + * @brief Reusable unit test infrastructure. + * + * This module defines header-only infra for unit tests that can be used to + * integrate tests directly where the implementation is. All you really need + * to do then is enable a macro, include the file you're testing and compile + * into an executable. + * + * @{ + */ + +/** @file unit_test.hh + * + * @brief The unit test infrastructure implementation. + * + * Include this at the very beginning in the files you want to define unit + * tests in. It has no dependencies within libostd. + * + * @copyright See COPYING.md in the project tree for further information. + */ + +#ifndef OSTD_UNIT_TEST_HH +#define OSTD_UNIT_TEST_HH + +#ifdef OSTD_BUILD_TESTS +#include +#include +#include +#include +#include +#include +#endif + +namespace ostd { +namespace test { + +/** @addtogroup Testing + * @{ + */ + +#ifdef OSTD_BUILD_TESTS + +#define OSTD_TEST_MODULE_STRINGIFY(x) #x +#define OSTD_TEST_MODULE_STR(x) OSTD_TEST_MODULE_STRINGIFY(x) +#define OSTD_TEST_MODULE OSTD_TEST_MODULE_STR(OSTD_BUILD_TESTS) + +static std::vector test_cases; + +static bool add_test(std::string testn, void (*func)()) { + if (testn == OSTD_TEST_MODULE) { + test_cases.push_back(func); + } + return true; +} + +#define OSTD_UNIT_TEST(module, body) \ +static bool test_case_##module##_##__LINE__ = \ + ostd::test::add_test(#module, []() { using namespace ostd::test; body }); + +struct test_error {}; + +void fail_if(bool b) { + if (b) { + throw test_error{}; + } +} + +void fail_if_not(bool b) { + if (!b) { + throw test_error{}; + } +} + +void run() { + int succ = 0, fail = 0; + for (auto &f: test_cases) { + try { + f(); + } catch (test_error) { + ++fail; + continue; + } + ++succ; + } +#if defined(_WIN32) || defined(WIN32) + printf( + "%s...\t%d out of %d (%d failures)\n", + OSTD_TEST_MODULE, succ, succ + fail, fail + ); +#else + printf( + "%s...\t%s\033[1m%d out of %d\033[0m (%d failures)\n", + OSTD_TEST_MODULE, fail ? "\033[91m" : "\033[92m", + succ, succ + fail, fail + ); +#endif +} + +#else /* OSTD_BUILD_TESTS */ + +#define OSTD_UNIT_TEST(module, body) + +#endif /* OSTD_BUILD_TESTS */ + +/** @} */ + +} /* namespace test */ +} /* namespace ostd */ + +#endif + +/** @} */ diff --git a/test_runner.cc b/test_runner.cc index b900706..f8a2fc1 100644 --- a/test_runner.cc +++ b/test_runner.cc @@ -36,17 +36,12 @@ int main() { /* do not change past this point */ int nsuccess = 0, nfailed = 0; - std::string modname = nullptr; + std::string modname; - auto print_result = [&](string_range fmsg = nullptr) { + auto print_error = [&](string_range fmsg) { write(modname, "...\t"); - if (!fmsg.empty()) { - writeln(COLOR_RED, COLOR_BOLD, '(', fmsg, ')', COLOR_END); - ++nfailed; - } else { - writeln(COLOR_GREEN, COLOR_BOLD, "(success)", COLOR_END); - ++nsuccess; - } + writeln(COLOR_RED, COLOR_BOLD, '(', fmsg, ')', COLOR_END); + ++nfailed; }; filesystem::directory_iterator ds{testdir}; @@ -70,7 +65,7 @@ int main() { auto cxxcmd = appender(); format( - cxxcmd, "%s %s%s%s -o %s %s", compiler, testdir, + cxxcmd, "%s %s%s%s -o %s %s ", compiler, testdir, char(filesystem::path::preferred_separator), p.filename(), exepath, cxxflags ); @@ -78,20 +73,24 @@ int main() { cxxcmd.get() += ' '; cxxcmd.get() += userflags; } + cxxcmd.get() += ' '; + cxxcmd.get() += "-DOSTD_BUILD_TESTS="; + cxxcmd.get() += modname; + int ret = system(cxxcmd.get().data()); if (ret) { - print_result("compile errror"); + print_error("compile errror"); continue; } ret = system(exepath.data()); if (ret) { - print_result("runtime error"); + print_error("runtime error"); continue; } remove(exepath.data()); - print_result(); + ++nsuccess; } writeln("\n", COLOR_BLUE, COLOR_BOLD, "testing done:", COLOR_END); diff --git a/tests/range.cc b/tests/range.cc new file mode 100644 index 0000000..ae7e59c --- /dev/null +++ b/tests/range.cc @@ -0,0 +1,9 @@ +#define OSTD_BUILD_TESTS range + +#include +#include + +int main() { + ostd::test::run(); + return 0; +}