#include #include #include #include #include #include #include #include #include #include namespace psemek::test { static std::map tests; void add_test_case(char const * name, void(*f)()) { std::string pname(name); std::replace(pname.begin(), pname.end(), '_', '/'); if (tests.count(pname)) { std::cerr << "Test " << pname << " already registered" << std::endl; std::exit(EXIT_FAILURE); } tests[pname] = f; } } int main(int argc, char ** argv) { std::set tests; if (argc == 1) { for (auto const & p : psemek::test::tests) tests.insert(p.first); } else { for (int a = 1; a < argc; ++a) { std::string pattern = std::string(argv[a]); if (pattern.empty()) { std::cerr << "Empty test case pattern in argument list" << std::endl; std::exit(EXIT_FAILURE); } if (pattern.back() != '/') pattern += "/"; for (auto const & p : psemek::test::tests) { if (p.first == argv[a] || p.first.starts_with(pattern)) tests.insert(p.first); } } } std::size_t test_count = tests.size(); std::size_t const test_index_len = std::ceil(std::log10(test_count + 1)); std::string const indent = std::string(2 * test_index_len + 6, ' '); std::size_t max_name_length = 0; for (auto const & name : tests) max_name_length = std::max(max_name_length, name.size()); std::size_t success = 0; using clock = std::chrono::high_resolution_clock; auto all_start = clock::now(); std::size_t i = 0; for (auto const & name : tests) { ++i; std::cout << '[' << std::setfill(' ') << std::setw(test_index_len) << std::right << i << '/' << test_count << "] " << std::left << std::setfill('.') << std::setw(max_name_length + 5) << name; try { auto start = clock::now(); psemek::test::tests[name](); auto end = clock::now(); std::cout << "ok (" << psemek::util::pretty(end - start, std::chrono::milliseconds{1}) << ")" << std::endl; ++success; } catch (psemek::test::failure const & e) { std::cout << "failure" << std::endl; std::cout << indent << "Reason: " << e.message() << std::endl; std::cout << indent << "Location: " << e.location() << std::endl; } catch (std::exception const & e) { std::cout << "failure: " << e.what() << std::endl; } catch (...) { std::cout << "failure: (unknown exception)" << std::endl; } } auto all_end = clock::now(); std::cout << std::endl; std::cout << success << '/' << test_count << " passed (" << psemek::util::pretty(all_end - all_start, std::chrono::milliseconds{1}) << ")" << std::endl; if (success <= test_count) return EXIT_FAILURE; }