fox32-hw/simulation/tester.cpp
2024-01-17 14:46:35 +01:00

193 lines
5.9 KiB
C++

#include <iostream>
#include <optional>
#include "tester.hpp"
const std::string ANSI_RESET = "\033[0m";
const std::string ANSI_DARK = "\033[38;5;8m"; // dark gray
const std::string ANSI_LISTING = "\033[38;5;208m"; // orange
const std::string ANSI_INFO = "\033[38;5;6m"; // blue
const std::string ANSI_SUCCESS = "\033[38;5;2m"; // green
const std::string ANSI_FAIL = "\033[38;5;1m"; // red
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<Tester*> at = this; at.has_value(); at = (*at)->parent) {
depth++;
}
return depth;
}
std::string Tester::full_name() {
std::string full_name(name);
std::optional<Tester*> 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_LISTING);
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_LISTING);
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_LISTING);
line.append(ANSI_RESET);
line.append(" ");
return line;
}
}
void Tester::intro() {
if (shown())
std::cout << prefix() << "=== " << ANSI_INFO << "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<Tester*> at = this; at.has_value(); at = (*at)->parent) {
if (name_show_overwritten((*at)->name))
return true;
}
for (std::optional<Tester*> 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_SUCCESS << "" << ANSI_RESET << test_name << ANSI_RESET << std::endl;
}
for (std::optional<Tester*> at = this; at.has_value(); at = (*at)->parent) {
(*at)->succeeded++;
}
} else {
std::cout << prefix() << ANSI_FAIL << "" << ANSI_RESET << test_name << " - " << ANSI_FAIL << "got " << got_s << ", expected " << expected_s << ANSI_RESET << std::endl;
for (std::optional<Tester*> at = this; at.has_value(); at = (*at)->parent) {
(*at)->failed++;
}
}
}
Tester::~Tester() {
finish();
}
void Tester::finish() {
std::chrono::duration<double> 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_SUCCESS << "all succeeded" << ANSI_RESET << ", out of " << n_cases_s << " total";
printed = true;
}
} else {
std::cout << prefix() << "=== " << ANSI_FAIL << n_failed_s << " failed" << ANSI_RESET << ", " << ANSI_SUCCESS << n_succeeded_s << " succeeded" << ANSI_RESET << " out of " << n_cases_s << " total";
printed = true;
}
if (printed)
std::cout << " (" << ANSI_INFO << time_s << ANSI_RESET << " total, " << ANSI_INFO << time_per_s << ANSI_RESET << " per test)" << std::endl;
}