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