/**
Copyright (C) 2023 Claudius "keldu" Holeksa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
#include "test.h"
#include
#include
#include
#include
namespace keltest {
template
constexpr bool always_false = false;
test_case* test_case_head = nullptr;
test_case** test_case_tail = &test_case_head;
test_case::test_case(std::string file_, uint32_t line_, std::string description_):
file{std::move(file_)},
line{line_},
description{std::move(description_)},
next{nullptr},
prev{test_case_tail}
{
/**
* Since I always forget how this works.
*/
/**
* If the list is empty, then this command sets the head to this
* If the list is not empty it sets the previous test_case "next" to this location
*/
*prev = this;
/**
* The tail is set this->next location which is used in a later test_case's constructor
*/
test_case_tail = &next;
}
test_case::~test_case(){
*prev = next;
if( next == nullptr ){
test_case_tail = prev;
}else {
next->prev = prev;
}
}
namespace colour {
struct red {};
struct green {};
struct blue {};
struct white {};
using variant = std::variant;
}
class test_runner {
private:
void write(const colour::variant& col, const std::string& front, const std::string& message){
std::string_view start_col, end_col;
start_col = std::visit([](auto& col) -> std::string_view {
using T = std::decay_t;
if constexpr ( std::is_same_v ){
return "\033[0;1;31m";
}
else if constexpr ( std::is_same_v ){
return "\033[0;1;32m";
}
else if constexpr ( std::is_same_v ){
return "\033[0;1;34m";
}
else if constexpr ( std::is_same_v ){
return "\033[0m";
}
else {
static_assert(always_false, "Case exhausted");
}
return "\033[0m";
}, col);
end_col = "\033[0m";
std::cout<next){
std::string name = test->file + std::string{":"} + std::to_string(test->line) + std::string{":"} + test->description;
write(colour::blue{}, "[ TEST ] ", name);
bool failed = true;
std::string fail_message;
auto start_clock = std::chrono::steady_clock::now();
try {
test->run();
failed = false;
}catch(std::exception& e){
fail_message = e.what();
failed = true;
}
auto stop_clock = std::chrono::steady_clock::now();
auto runtime_duration_intern = stop_clock - start_clock;
auto runtime_duration = std::chrono::duration_cast(runtime_duration_intern);
std::string message = name + std::string{" ("} + std::to_string(runtime_duration.count()) + std::string{" µs) "};
if( failed ){
write(colour::red{}, "[ FAIL ] ", message + std::string{" "} + fail_message);
++failed_count;
}else{
write(colour::green{}, "[ PASS ] ", message);
++passed_count;
}
}
if( passed_count > 0 ) write(colour::green{}, std::to_string(passed_count) + std::string{" test(s) passed"}, "");
if( failed_count > 0 ) write(colour::red{}, std::to_string(failed_count) + std::string{" test(s) failed"}, "");
return failed_count > 0 ? -1 : 0;
}
};
}
#if KELTEST_COMPILE_TEST_BINARY
int main() {
::keltest::test_runner runner;
int rv = runner.run();
return rv != 0 ? -1 : 0;
}
#endif