#ifndef HEADER_TEST_U_HPP #define HEADER_TEST_U_HPP #include #include #include #include #include #include #include #include #include template struct TestDefaultEps {}; template<> struct TestDefaultEps { static constexpr float value = 1e-6; }; template<> struct TestDefaultEps { static constexpr double value = 1e-9; }; template<> struct TestDefaultEps { static constexpr long double value = 1e-12; }; class TestCase { public: TestCase(int id, const char* title): ok(0), fail(0), n(0), ended(false), time_used(0) { std::printf("[%02d]Testing %s:\n", id, title); } void expectTrue(std::function func) noexcept { expectEq(func, true); } void expectFalse(std::function func) noexcept { expectEq(func, false); } template> typename std::enable_if::value>::type expectEq( std::function func, T&& answer) noexcept { auto res = runTask(func); if (!res.has_value()) { return onError(); } if (std::abs(res.value() - answer) < EPS::value) { onPass(); } else { onFail(); } } template typename std::enable_if::value>::type expectEq( std::function func, T&& answer) noexcept { auto res = runTask(func); if (!res.has_value()) { return onError(); } if (res.value() == answer) { onPass(); } else { onFail(); } } void end() noexcept { if (ended) { return; } ended = true; std::putchar('\n'); for (int i = 0; i < 20; ++i) { std::putchar('-'); } std::putchar('\n'); if (fail) { if (fail == 1) { std::puts("1 test failed."); } else { std::printf("%d tests failed.\n", fail); } } else { if (ok == 1) { std::printf("OK. 1 test passed"); } else { std::printf("OK. %d tests passed", ok); } std::printf(" in %lldms.\n", static_cast(time_used.count())); } } bool hasFail() const noexcept { return fail > 0; } private: void putchar(char c) noexcept { n += 1; if (n > 1 && n % 15 == 1) { std::putchar('\n'); } std::putchar(c); } void onPass() noexcept { ok += 1; putchar('.'); } void onFail() noexcept { fail += 1; putchar('F'); } void onError() noexcept { fail += 1; putchar('E'); } template std::optional runTask(std::function func) noexcept { auto start = std::chrono::high_resolution_clock::now(); try { T res = func(); auto end = std::chrono::high_resolution_clock::now(); auto dt = end - start; time_used += std::chrono::duration_cast(dt); return {res}; } catch (...) { return std::nullopt; } } int ok, fail, n; bool ended; std::chrono::milliseconds time_used; }; class TestSuit { TestSuit(const TestSuit&) = delete; TestSuit(TestSuit&&) = delete; public: static TestSuit* getInstance() { if (!instance) { instance = new TestSuit(); } return instance; } void addTestCase(int id, const char* title, std::function f) { tc.emplace_back(id, title, f); } int evalTestCases() { std::sort(tc.begin(), tc.end(), [](const auto &x, const auto &y) { return std::get<0>(x) < std::get<0>(y); }); for (auto [id, title, func] : tc) { TestCase t(id, title); func(t); t.end(); std::putchar('\n'); if (t.hasFail()) { return 1; } } tc.clear(); return 0; } private: static inline TestSuit* instance = nullptr; TestSuit() {} std::vector>> tc; }; class AddTestCase { AddTestCase() = delete; AddTestCase(const AddTestCase&) = delete; AddTestCase(AddTestCase&&) = delete; public: AddTestCase(int id, const char* tilte, std::function func) { TestSuit::getInstance()->addTestCase(id, tilte, func); } }; #endif // HEADER_TEST_U_HPP