about summary refs log tree commit diff stats
path: root/cpp/001test
blob: 0d45d07034003c94c6728287145fd173cec419c8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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