#include #include #include "tester.hpp" const std::string ANSI_RESET = "\033[0m"; const std::string ANSI_DARK = "\033[38;5;8m"; const std::string ANSI_BLUE = "\033[38;5;6m"; const std::string ANSI_GREEN = "\033[38;5;2m"; const std::string ANSI_RED = "\033[38;5;1m"; const std::string ANSI_YELLOW = "\033[38;5;11m"; Tester::Tester(std::string test_name) : parent(), name(test_name), succeeded(0), failed(0), detailed(true), started(std::chrono::steady_clock::now()) { intro(); } Tester::Tester(Tester* tester_parent, std::string test_name) : parent(tester_parent), name(test_name), succeeded(0), failed(0), detailed(false), started(std::chrono::steady_clock::now()) { intro(); } Tester::Tester(Tester* tester_parent, std::string test_name, bool show_details) : parent(tester_parent), name(test_name), succeeded(0), failed(0), detailed(show_details), started(std::chrono::steady_clock::now()) { intro(); } int Tester::depth() { int depth = 0; for (std::optional at = this; at.has_value(); at = (*at)->parent) { depth++; } return depth; } std::string Tester::full_name() { std::string full_name(name); std::optional at = parent; while (at.has_value()) { full_name.insert(0, " "); full_name.insert(0, (*at)->name); at = (*at)->parent; } return full_name; } std::string Tester::prefix() { std::string line = full_name(); line.insert(0, "[ "); line.append(" ]"); int wanted_len = 10 * depth(); if (line.length() == wanted_len) { line.insert(0, ANSI_BLUE); line.append(ANSI_RESET); line.append(" "); return line; } else if (line.length() > wanted_len) { line.append("\n"); line.append(ANSI_DARK); for (int i = 0; i < wanted_len; i++) line.append("—"); line.insert(0, ANSI_BLUE); line.append(ANSI_RESET); line.append(" "); return line; } else { int extra = wanted_len - line.length(); line.append(" "); line.append(ANSI_DARK); for (int i = 1; i < extra; i++) line.append("—"); line.insert(0, ANSI_BLUE); line.append(ANSI_RESET); line.append(" "); return line; } } void Tester::intro() { if (shown()) std::cout << prefix() << "=== " << ANSI_YELLOW << "start" << ANSI_RESET << std::endl; } bool name_show_overwritten(std::string name) { char *shown_c = std::getenv("SHOW_CASE"); if (shown_c == NULL) return false; std::string shown(shown_c); shown.push_back(' '); shown.insert(0, " "); name.push_back(' '); name.insert(0, " "); return shown.find(name) != std::string::npos; } bool Tester::shown() { for (std::optional at = this; at.has_value(); at = (*at)->parent) { if (name_show_overwritten((*at)->name)) return true; } for (std::optional at = this; at.has_value(); at = (*at)->parent) { if (!(*at)->detailed) return false; } return true; } void Tester::assert_eq_str(std::string test_name, bool correct, std::string got_s, std::string expected_s) { if (correct) { if (shown()) { std::cout << prefix() << ANSI_GREEN << "✓ " << ANSI_RESET << test_name << ANSI_RESET << std::endl; } for (std::optional at = this; at.has_value(); at = (*at)->parent) { (*at)->succeeded++; } } else { std::cout << prefix() << ANSI_RED << "✗ " << ANSI_RESET << test_name << " - " << ANSI_RED << "got " << got_s << ", expected " << expected_s << ANSI_RESET << std::endl; for (std::optional at = this; at.has_value(); at = (*at)->parent) { (*at)->failed++; } } } Tester::~Tester() { finish(); } void Tester::finish() { std::chrono::duration time_taken = std::chrono::steady_clock::now() - started; double time_secs = time_taken.count(); double time_per_test_secs = time_secs / (succeeded + failed); char time_s[20], time_per_s[20]; if (time_secs < 1e-6) { snprintf(time_s, sizeof time_s, "%.1fns", time_secs * 1e9); } else if (time_secs < 1e-3) { snprintf(time_s, sizeof time_s, "%.1fµs", time_secs * 1e6); } else if (time_secs < 1) { snprintf(time_s, sizeof time_s, "%.1fms", time_secs * 1e3); } else { snprintf(time_s, sizeof time_s, "%.1fs", time_secs); } if (time_per_test_secs < 1e-6) { snprintf(time_per_s, sizeof time_per_s, "%.1fns", time_per_test_secs * 1e9); } else if (time_per_test_secs < 1e-3) { snprintf(time_per_s, sizeof time_per_s, "%.1fµs", time_per_test_secs * 1e6); } else if (time_per_test_secs < 1) { snprintf(time_per_s, sizeof time_per_s, "%.1fms", time_per_test_secs * 1e3); } else { snprintf(time_per_s, sizeof time_per_s, "%.1fs", time_per_test_secs); } char n_cases_s[10], n_succeeded_s[10], n_failed_s[10]; snprintf(n_cases_s, sizeof n_cases_s, "%d", succeeded + failed); snprintf(n_succeeded_s, sizeof n_succeeded_s, "%d", succeeded); snprintf(n_failed_s, sizeof n_failed_s, "%d", failed); bool printed = false; if (failed == 0) { if (shown()) { std::cout << prefix() << "=== " << ANSI_GREEN << "all succeeded" << ANSI_RESET << ", out of " << n_cases_s << " total"; printed = true; } } else { std::cout << prefix() << "=== " << ANSI_RED << n_failed_s << " failed" << ANSI_RESET << ", " << ANSI_GREEN << n_succeeded_s << " succeeded" << ANSI_RESET << " out of " << n_cases_s << " total"; printed = true; } if (printed) std::cout << " (" << ANSI_YELLOW << time_s << ANSI_RESET << " total, " << ANSI_YELLOW << time_per_s << ANSI_RESET << " per test)" << std::endl; }