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