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