https://github.com/akkartik/mu/blob/master/040---tests.cc
 1 //: Automatically aggregate functions starting with 'test-' into a test suite
 2 //: called 'run-tests'. Running this function will run all tests.
 3 //:
 4 //: This is actually SubX's first (trivial) compiler. We generate all the code
 5 //: needed for the 'run-tests' function.
 6 //:
 7 //: By convention, temporary functions needed by tests will start with
 8 //: '_test-'.
 9 
10 //: We don't rely on any transforms running in previous layers, but this layer
11 //: knows about labels and will emit labels for previous layers to transform.
12 :(after "Begin Transforms")
13 Transform.push_back(create_test_function);
14 
15 :(code)
16 void test_run_test() {
17   Mem.push_back(vma(0xbd000000));  // manually allocate memory
18   Reg[ESP].u = 0xbd000100;
19   run(
20       "== code 0x1\n"  // code segment
21       "main:\n"
22       "  e8/call run-tests/disp32\n"  // 5 bytes
23       "  f4/halt\n"                   // 1 byte
24       "test-foo:\n"  // offset 7
25       "  01 d8\n"  // just some unique instruction: add EBX to EAX
26       "  c3/return\n"
27   );
28   // check that code in test-foo ran (implicitly called by run-tests)
29   CHECK_TRACE_CONTENTS(
30       "run: 0x00000007 opcode: 01\n"
31   );
32 }
33 
34 void create_test_function(program& p) {
35   if (p.segments.empty()) return;
36   segment& code = *find(p, "code");
37   trace(3, "transform") << "-- create 'run-tests'" << end();
38   vector<line> new_insts;
39   for (int i = 0;  i < SIZE(code.lines);  ++i) {
40     line& inst = code.lines.at(i);
41     for (int j = 0;  j < SIZE(inst.words);  ++j) {
42       const word& curr = inst.words.at(j);
43       if (*curr.data.rbegin() != ':') continue;  // not a label
44       if (!starts_with(curr.data, "test-")) continue;
45       string fn = drop_last(curr.data);
46       new_insts.push_back(call(fn));
47     }
48   }
49   if (new_insts.empty()) return;  // no tests found
50   code.lines.push_back(label("run-tests"));
51   code.lines.insert(code.lines.end(), new_insts.begin(), new_insts.end());
52   code.lines.push_back(ret());
53 }
54 
55 string to_string(const segment& s) {
56   ostrpan class="na">name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="none">
<meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #aaaaaa; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #aaaaaa; background-color: #080808; }
a { color:#eeeeee; text-decoration: none; }
a:hover { text-decoration: underline; }
* { font-size: 12pt; font-size: 1em; }
.muRecipe { color: #ff8700; }
.Special { color: #c00000; }
.Comment { color: #9090ff; }
.Comment a { color:#0000ee; text-decoration:underline; }
.Constant { color: #00a0a0; }
.LineNr { color: #444444; }
-->
</style>

<script type='text/javascript'>
<!--

/* function to open any folds containing a jumped-to line before jumping to it */
function