diff --git a/tests/main.cpp b/tests/main.cpp index 4168da5..e680fee 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -7,25 +7,36 @@ #define RED "\033[1;91m" #define GREEN "\033[1;92m" +#define YELLOW "\033[1;93m" #define AQUA "\033[1;36m" #define RESET "\033[;1m" -extern std::map tests; - -void loadTests(); - int main(int argc, char** argv) { auto start = std::chrono::high_resolution_clock::now(); - loadTests(); - + testdef* startit = &__start_testlist, *endit = &__stop_testlist; int failcount = 0; - int testcount = tests.size(); + int skipcount = 0; + int testcount = endit-startit; int testnumber = 0; - for(std::map::iterator current = tests.begin(); current != tests.end(); ++current) { - printf("\033[1mRunning test: %d/%d " AQUA "%s " RESET, ++testnumber, testcount, current->first.c_str()); - if((current->second)()) { + + // go through back -> front (tests are inserted in reverse order) + for(testdef* it = startit + testcount-1; it >= startit; --it) { + printf("\033[1mRunning test: %d/%d " AQUA "%s " RESET, ++testnumber, testcount, it->name); + + // run test + int result = TESTFAILED; + try { + result = (it->testf)(); + } catch(std::exception& e) { + std::cout << "catched exception: \"" << e.what() << "\" " << std::flush; + } catch(...) {} + + if(result == TESTGOOD) { printf(GREEN "succeeded" RESET "!\n"); + } else if(result == TESTSKIPPED) { + printf(YELLOW "skipped" RESET "!\n"); + skipcount++; } else { printf(RED "failed" RESET "\n"); failcount++; @@ -33,7 +44,7 @@ int main(int argc, char** argv) { } const char* color = (failcount > 0 ? RED : GREEN); // red or green - printf("%s%d" RESET "/%d failed\n", color, failcount, testcount); + printf("%s%d" RESET "/%d failed (" YELLOW "%d " RESET "skipped)\n", color, failcount, testcount, skipcount); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration t = end - start; diff --git a/tests/sampletest.cpp b/tests/sampletest.cpp index 926bc2a..8dfed74 100644 --- a/tests/sampletest.cpp +++ b/tests/sampletest.cpp @@ -1,11 +1,29 @@ #include "test.h" +#include + +// tests are executed top to bottom + +// never fail TEST(ABC) { CMPASSERT(1, true); } TESTEND +// always fail TEST(CDE) { CMPASSERT(0, true); -} TESTEND \ No newline at end of file +} TESTEND + +// always skip +TEST(FGH) { + SKIPTEST; + +} TESTEND + +// always throw +TEST(IJK) { + throw std::runtime_error("test exception"); + +} TESTEND diff --git a/tests/test.h b/tests/test.h index 1cad15b..e286566 100644 --- a/tests/test.h +++ b/tests/test.h @@ -1,17 +1,43 @@ #define TESTFAILED 0 #define TESTGOOD 1 +#define TESTSKIPPED -1 #include #define TESTDATA "./tests/data/" -#define TESTNAME(NAME) test_##NAME -#define TESTFUNC(NAME) bool TESTNAME(NAME)() -#define TEST(NAME) TESTFUNC(NAME) { -#define TESTEND return TESTGOOD; } +// very helpfull: https://mgalgs.io/2013/05/10/hacking-your-ELF-for-fun-and-profit.html -#define ASSERT(BED, ERR) if(!(BED)) { std::cout << __FILE__ << ":" << __LINE__ << " " << ERR << std::endl; return TESTFAILED; } -#define CMPASSERTE(A, B, ERR) if( !((A) == (B))) { std::cout << __FILE__ << ":" << __LINE__ << " is: \"" << (A) << "\" should: \"" << (B) << "\""<< std::endl; return TESTFAILED; } +#define TESTNAME(NAME) test_##NAME +#define TESTFUNC(NAME) int TESTNAME(NAME)() + +#define REGISTERTEST(NAME) static const testdef __test_ ## NAME \ + __attribute((__section__("testlist"))) \ + __attribute((__used__)) = { \ + TESTNAME(NAME), \ + #NAME, \ + } + +#define TEST(NAME) static TESTFUNC(NAME); \ + REGISTERTEST(NAME); \ + TESTFUNC(NAME) { + +#define TESTEND return TESTGOOD; } \ + + +#define ASSERT(BED, ERR) if(!(BED)) { std::cout << __FILE__ << ":" << __LINE__ << " " << ERR << ' ' << std::flush; return TESTFAILED; } +#define CMPASSERTE(A, B, ERR) if( !((A) == (B))) { std::cout << __FILE__ << ":" << __LINE__ << " is: \"" << (A) << "\" should: \"" << (B) << "\" "<< std::flush; return TESTFAILED; } #define CMPASSERT(A, B) CMPASSERTE(A, B, "") -typedef bool (*test_t)(); +#define SKIPTEST return TESTSKIPPED + +typedef int (*test_t)(); + +struct testdef { + test_t testf; + const char* name; +}; + +// linker generates this <3 +extern struct testdef __start_testlist; +extern struct testdef __stop_testlist; \ No newline at end of file diff --git a/tests/tests.cpp b/tests/tests.cpp deleted file mode 100644 index 64419ee..0000000 --- a/tests/tests.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "test.h" - -#include -#include - -#define REGISTERTEST(NAME) tests.insert({#NAME, TESTNAME(NAME)}) - -TESTFUNC(ABC); -TESTFUNC(CDE); - -std::map tests; - -void loadTests() { - REGISTERTEST(ABC); - REGISTERTEST(CDE); -} \ No newline at end of file