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