diff options
Diffstat (limited to 'cpp/002test')
-rw-r--r-- | cpp/002test | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/cpp/002test b/cpp/002test new file mode 100644 index 00000000..f704f7f0 --- /dev/null +++ b/cpp/002test @@ -0,0 +1,135 @@ +//: A simple test harness. To create new tests define functions starting with +//: 'test_'. To run all tests so defined, run: +//: $ wart test +//: +//: So far it seems tasteful for layers to never ever reach back to modify +//: previously-defined tests. Every test is a contract once written, and should +//: pass as-is if it is included, regardless of how much later layers change +//: the program. Avoid writing 'temporary' tests that only work with some +//: subsets of the program. + +:(before "End Types") +typedef void (*test_fn)(void); + +:(before "End Globals") +const test_fn Tests[] = { + #include "test_list" // auto-generated; see makefile +}; + +bool Passed = true; // set this to false inside any test to indicate failure +long Num_failures = 0; + +#define CHECK(X) \ + if (!(X)) { \ + ++Num_failures; \ + cerr << "\nF " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << '\n'; \ + Passed = false; \ + return; /* Currently we stop at the very first failure. */ \ + } + +#define CHECK_EQ(X, Y) \ + if ((X) != (Y)) { \ + ++Num_failures; \ + cerr << "\nF " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << " == " << #Y << '\n'; \ + cerr << " got " << (X) << '\n'; /* BEWARE: multiple eval */ \ + Passed = false; \ + return; /* Currently we stop at the very first failure. */ \ + } + +:(before "End Main") +if (argc <= 1 || is_equal(argv[1], "--help")) { + // need some way to distribute this across layers + cerr << "To load files and run 'main': mu file1.mu file2.mu ...\n" + << "To run all core tests: mu test\n" + << "To load files and run tests defined in them: mu test file1.mu file2.mu ...\n" + << "To run just some C++ tests in `test_list` file: mu test n1 n2 ...\n" + << " (look up n1 n2 ... by running `grep -n` on test_list`)\n" + ; + return 0; +} +if (argc > 1 && is_equal(argv[1], "test")) { + // End Test Run Initialization + if (argc == 2) { + run_tests(); + cerr << '\n'; + if (Num_failures > 0) + cerr << Num_failures << " failure" + << (Num_failures > 1 ? "s" : "") + << '\n'; + return 0; + } + if (is_number(argv[2])) { + // all args are line numbers in test_file specifying tests to run + run_tests(argc, argv); + cerr << '\n'; + if (Num_failures > 0) + cerr << Num_failures << " failure" + << (Num_failures > 1 ? "s" : "") + << '\n'; + return 0; + } + + // End Test Runs +} + +:(code) +void run_tests() { + time_t t; time(&t); + cerr << "C tests: " << ctime(&t); + for (size_t i=0; i < sizeof(Tests)/sizeof(Tests[0]); ++i) { + run_test(i); + } + // End Tests +} + +void run_tests(int argc, char* argv[]) { + for (int i = 2; i < argc; ++i) { + run_test(to_int(argv[i])-1); + } +} + +void run_test(size_t i) { + if (i >= sizeof(Tests)/sizeof(Tests[0])) { + cerr << "no test " << i << '\n'; + return; + } + setup(); + Passed = true; + // End Test Setup + (*Tests[i])(); + if (Passed) cerr << "."; + // Test Teardown + // End Test Teardown +} + +bool is_equal(char* s, const char* lit) { + return strncmp(s, lit, strlen(lit)) == 0; +} + +bool is_number(const string& s) { + return s.find_first_not_of("0123456789-.") == string::npos; +} + +int to_int(string n) { + char* end = NULL; + int result = strtol(n.c_str(), &end, /*any base*/0); + assert(*end == '\0'); + return result; +} + +:(before "End Includes") +#include<assert.h> +#include<cstdlib> + +#include<iostream> +using std::istream; +using std::ostream; +using std::iostream; +using std::cin; +using std::cout; +using std::cerr; + +#include<cstring> +#include<string> +using std::string; +#define NOT_FOUND string::npos // macro doesn't complain about redef |