1 //: Automatically aggregating functions into test suites.
 2 
 3 //: We don't rely on any transforms running in previous layers, but this layer
 4 //: knows about labels and will emit labels for previous layers to transform.
 5 :(after "Begin Transforms")
 6 // Begin Level-4 Transforms
 7 Transform.push_back(create_test_function);
 8 // End Level-4 Transforms
 9 
10 :(scenario run_test)
11 % Reg[ESP].u = 0x100;
12 == 0x1
13 main:
14   e8/call run_tests/disp32  # 5 bytes
15   f4/halt                   # 1 byte
16 
17 test_foo:  # offset 7
18   01 d8  # just some unique instruction: add EBX to EAX
19   c3/return
20 
21 # check that code in test_foo ran (implicitly called by run_tests)
22 +run: inst: 0x00000007
23 
24 :(code)
25 void create_test_function(program& p) {
26   if (p.segments.empty()) return;
27   segment& code = p.segments.at(0);
28   trace(99, "transform") << "-- create 'run_tests'" << end();
29   vector<line> new_insts;
30   for (int i = 0;  i < SIZE(code.lines);  ++i) {
31     line& inst = code.lines.at(i);
32     for (int j = 0;  j < SIZE(inst.words);  ++j) {
33       const word& curr = inst.words.at(j);
34       if (*curr.data.rbegin() != ':') continue;  // not a label
35       if (!starts_with(curr.data, "test_")) continue;
36       string fn = drop_last(curr.data);
37       new_insts.push_back(call(fn));
38     }
39   }
40   if (new_insts.empty()) return;  // no tests found
41   code.lines.push_back(label("run_tests"));
42   code.lines.insert(code.lines.end(), new_insts.begin(), new_insts.end());
43   code.lines.push_back(ret());
44 }
45 
46 string to_string(const segment& s) {
47   ostringstream out;
48   for (int i = 0;  i < SIZE(s.lines);  ++i) {
49     const line& l = s.lines.at(i);
50     for (int j = 0;  j < SIZE(l.words);  ++j) {
51       if (j > 0) out << ' ';
52       out << to_string(l.words.at(j));
53     }
54     out << '\n';
55   }
56   return out.str();
57 }
58 
59 line call(string s) {
60   line result;
61   result.words.push_back(call());
62   result.words.push_back(disp32(s));
63   return result;
64 }
65 
66 word call() {
67   word result;
68   result.data = "e8";
69   result.metadata.push_back("call");
70   return result;
71 }
72 
73 word disp32(string s) {
74   word result;
75   result.data = s;
76   result.metadata.push_back("disp32");
77   return result;
78 }
79 
80 line ret() {
81   line result;
82   result.words.push_back(word());
83   result.words.back().data = "c3";
84   result.words.back().metadata.push_back("return");
85   return result;
86 }