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 :(before "End Types")
71 struct trace_line {
72 int depth;
73 string label;
74 string contents;
75 trace_line(string l, string c) :depth(0), label(l), contents(c) {}
76 trace_line(int d, string l, string c) :depth(d), label(l), contents(c) {}
77 };
78
79 :(before "End Globals")
80 bool Hide_errors = false;
81 bool Dump_trace = false;
82 string Dump_label = "";
83 :(before "End Reset")
84 Hide_errors = false;
85 Dump_trace = false;
86 Dump_label = "";
87
88 :(before "End Types")
89
90
91
92 const int Max_depth = 9999;
93 const int Error_depth = 0;
94 const int App_depth = 2;
95
96 struct trace_stream {
97 vector<trace_line> past_lines;
98
99 ostringstream* curr_stream;
100 string curr_label;
101 int curr_depth;
102 int callstack_depth;
103 int collect_depth;
104 ofstream null_stream;
105 trace_stream() :curr_stream(NULL), curr_depth(Max_depth), callstack_depth(0), collect_depth(Max_depth) {}
106 ~trace_stream() { if (curr_stream) delete curr_stream; }
107
108 ostream& stream(string label) {
109 return stream(Max_depth, label);
110 }
111
112 ostream& stream(int depth, string label) {
113 if (depth > collect_depth) return null_stream;
114 curr_stream = new ostringstream;
115 curr_label = label;
116 curr_depth = depth;
117 return *curr_stream;
118 }
119
120 void dump() {
121 ofstream fout("last_run");
122 fout << readable_contents("");
123 fout.close();
124 }
125
126
127 void newline();
128
129 string readable_contents(string label);
130 };
131
132 :(code)
133 void trace_stream::newline() {
134 if (!curr_stream) return;
135 string curr_contents = curr_stream->str();
136 if (!curr_contents.empty()) {
137 past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents));
138 if ((!Hide_errors && curr_label == "error")
139 || Dump_trace
140 || (!Dump_label.empty() && curr_label == Dump_label))
141 cerr << curr_label << ": " << curr_contents << '\n';
142 }
143 delete curr_stream;
144 curr_stream = NULL;
145 curr_label.clear();
146 curr_depth = Max_depth;
147 }
148
149 string trace_stream::readable_contents(string label) {
150 ostringstream output;
151 label = trim(label);
152 for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
153 if (label.empty() || label == p->label) {
154 output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n';
155 }
156 return output.str();
157 }
158
159 :(before "End Globals")
160 trace_stream* Trace_stream = NULL;
161 int Trace_errors = 0;
162
163 :(before "End Includes")
164 #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream;
165
166
167 #define trace(...) !Trace_stream ? cerr : Trace_stream->stream(__VA_ARGS__)
168
169
170 #define dbg trace(0, "a")
171 #define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label);
172
173
174 #define raise (!Trace_stream ? (scroll_to_bottom_and_close_console(),++Trace_errors,cerr) : Trace_stream->stream(Error_depth, "error"))
175
176
177 #define assert_for_now assert
178
179
180 :(before "End One-time Setup")
181 atexit(scroll_to_bottom_and_close_console);
182 :(code)
183 void scroll_to_bottom_and_close_console() {
184 if (!tb_is_active()) return;
185
186 tb_set_cursor(tb_width()-1, tb_height()-1);
187 cout << "\r\n";
188 tb_shutdown();
189 }
190
191
192
193 :(before "End Test Teardown")
194 if (Passed && !Hide_errors && trace_contains_errors()) {
195 Passed = false;
196 }
197 :(code)
198 bool trace_contains_errors() {
199 return Trace_errors > 0 || trace_count("error") > 0;
200 }
201
202 :(before "End Types")
203 struct end {};
204 :(code)
205 ostream& operator<<(ostream& os, vestigial end) {
206 if (Trace_stream) Trace_stream->newline();
207 return os;
208 }
209
210 :(before "End Globals")
211 bool Save_trace = false;
212
213
214 :(before "End Types")
215 struct lease_tracer {
216 lease_tracer();
217 ~lease_tracer();
218 };
219 :(code)
220 lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
221 lease_tracer::~lease_tracer() {
222 if (!Trace_stream) return;
223 if (Save_trace) Trace_stream->dump();
224 delete Trace_stream, Trace_stream = NULL;
225 }
226 :(before "End Includes")
227 #define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer;
228 :(before "End Test Setup")
229 START_TRACING_UNTIL_END_OF_SCOPE
230
231 :(before "End Includes")
232 #define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
233
234 #define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors())
235 #define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
236 if (Passed && trace_contains_errors()) { \
237 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
238 DUMP("error"); \
239 Passed = false; \
240 return; \
241 }
242
243 #define CHECK_TRACE_COUNT(label, count) \
244 if (Passed && trace_count(label) != (count)) { \
245 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \
246 cerr << " got " << trace_count(label) << '\n'; \
247 DUMP(label); \
248 Passed = false; \
249 return; \
250 }
251
252 #define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__))
253
254 :(code)
255 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) {
256 if (!Passed) return false;
257 if (!Trace_stream) return false;
258 vector<string> expected_lines = split(expected, "^D");
259 int curr_expected_line = 0;
260 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
261 ++curr_expected_line;
262 if (curr_expected_line == SIZE(expected_lines)) return true;
263 string label, contents;
264 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
265 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
266 if (label != p->label) continue;
267 if (contents != trim(p->contents)) continue;
268 ++curr_expected_line;
269 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
270 ++curr_expected_line;
271 if (curr_expected_line == SIZE(expected_lines)) return true;
272 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
273 }
274
275 if (line_exists_anywhere(label, contents)) {
276 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n";
277 DUMP("");
278 }
279 else {
280 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n";
281 DUMP(label);
282 }
283 Passed = false;
284 return false;
285 }
286
287 void split_label_contents(const string& s, string* label, string* contents) {
288 static const string delim(": ");
289 size_t pos = s.find(delim);
290 if (pos == string::npos) {
291 *label = "";
292 *contents = trim(s);
293 }
294 else {
295 *label = trim(s.substr(0, pos));
296 *contents = trim(s.substr(pos+SIZE(delim)));
297 }
298 }
299
300 bool line_exists_anywhere(const string& label, const string& contents) {
301 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
302 if (label != p->label) continue;
303 if (contents == trim(p->contents)) return true;
304 }
305 return false;
306 }
307
308 int trace_count(string label) {
309 return trace_count(label, "");
310 }
311
312 int trace_count(string label, string line) {
313 if (!Trace_stream) return 0;
314 long result = 0;
315 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
316 if (label == p->label) {
317 if (line == "" || trim(line) == trim(p->contents))
318 ++result;
319 }
320 }
321 return result;
322 }
323
324 int trace_count_prefix(string label, string prefix) {
325 if (!Trace_stream) return 0;
326 long result = 0;
327 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
328 if (label == p->label) {
329 if (starts_with(trim(p->contents), trim(prefix)))
330 ++result;
331 }
332 }
333 return result;
334 }
335
336 bool trace_doesnt_contain(string label, string line) {
337 return trace_count(label, line) == 0;
338 }
339
340 bool trace_doesnt_contain(string expected) {
341 vector<string> tmp = split_first(expected, ": ");
342 return trace_doesnt_contain(tmp.at(0), tmp.at(1));
343 }
344
345 vector<string> split(string s, string delim) {
346 vector<string> result;
347 size_t begin=0, end=s.find(delim);
348 while (true) {
349 if (end == string::npos) {
350 result.push_back(string(s, begin, string::npos));
351 break;
352 }
353 result.push_back(string(s, begin, end-begin));
354 begin = end+SIZE(delim);
355 end = s.find(delim, begin);
356 }
357 return result;
358 }
359
360 vector<string> split_first(string s, string delim) {
361 vector<string> result;
362 size_t end=s.find(delim);
363 result.push_back(string(s, 0, end));
364 if (end != string::npos)
365 result.push_back(string(s, end+SIZE(delim), string::npos));
366 return result;
367 }
368
369 string trim(const string& s) {
370 string::const_iterator first = s.begin();
371 while (first != s.end() && isspace(*first))
372 ++first;
373 if (first == s.end()) return "";
374
375 string::const_iterator last = --s.end();
376 while (last != s.begin() && isspace(*last))
377 --last;
378 ++last;
379 return string(first, last);
380 }
381
382 :(before "End Includes")
383 #include <vector>
384 using std::vector;
385 #include <list>
386 using std::list;
387 #include <map>
388 using std::map;
389 #include <set>
390 using std::set;
391
392 #include <sstream>
393 using std::istringstream;
394 using std::ostringstream;
395
396 #include <fstream>
397 using std::ifstream;
398 using std::ofstream;
399
400 #include "termbox/termbox.h"
401
402 :(before "End Globals")
403
404
405
406
407
408 extern const int Initial_callstack_depth = 101;
409 extern const int Max_callstack_depth = 9989;
410
411
412
413
414