about summary refs log tree commit diff stats
path: root/cpp/002test
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-04-22 13:14:00 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-04-22 13:15:55 -0700
commit8c9ffa039f2f623cf61f263d487c1f4a919bea65 (patch)
tree817ee0dbd58a90fb6d4338dd2d44b82e64dfccc3 /cpp/002test
parentb8757483df26f3baa75cdbd4b4a09f642bf9542c (diff)
downloadmu-8c9ffa039f2f623cf61f263d487c1f4a919bea65.tar.gz
1132
Diffstat (limited to 'cpp/002test')
-rw-r--r--cpp/002test135
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