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