szdytom f0445542ce
Add procedure call impl
Signed-off-by: szdytom <szdytom@qq.com>
2024-02-04 13:47:53 +08:00

206 lines
3.9 KiB
C++

#ifndef HEADER_TEST_U_HPP
#define HEADER_TEST_U_HPP
#include <chrono>
#include <cstdio>
#include <exception>
#include <functional>
#include <optional>
#include <type_traits>
#include <tuple>
#include <vector>
#include <algorithm>
template<typename T>
struct TestDefaultEps {};
template<>
struct TestDefaultEps<float> {
static constexpr float value = 1e-6;
};
template<>
struct TestDefaultEps<double> {
static constexpr double value = 1e-9;
};
template<>
struct TestDefaultEps<long double> {
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<bool()> func) noexcept {
expectEq<bool>(func, true);
}
void expectFalse(std::function<bool()> func) noexcept {
expectEq<bool>(func, false);
}
template<typename T, typename EPS = TestDefaultEps<T>>
typename std::enable_if<std::is_floating_point<T>::value>::type expectEq(
std::function<T()> 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 T>
typename std::enable_if<!std::is_floating_point<T>::value>::type expectEq(
std::function<T()> 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<long long>(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<typename T>
std::optional<T> runTask(std::function<T()> 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<std::chrono::milliseconds>(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<void(TestCase&)> 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<std::tuple<int, const char*, std::function<void(TestCase&)>>> tc;
};
class AddTestCase {
AddTestCase() = delete;
AddTestCase(const AddTestCase&) = delete;
AddTestCase(AddTestCase&&) = delete;
public:
AddTestCase(int id, const char* tilte, std::function<void(TestCase&)> func) {
TestSuit::getInstance()->addTestCase(id, tilte, func);
}
};
#endif // HEADER_TEST_U_HPP