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