From fd7d8138a4ff5515f9b79c584a98d5c26d8ddb8a Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Thu, 2 Mar 2017 05:48:01 -0800 Subject: 3750 --- html/003trace.cc.html | 738 +++++++++++++++++++++++++------------------------- 1 file changed, 365 insertions(+), 373 deletions(-) (limited to 'html/003trace.cc.html') diff --git a/html/003trace.cc.html b/html/003trace.cc.html index 069cc057..b1999eb4 100644 --- a/html/003trace.cc.html +++ b/html/003trace.cc.html @@ -72,400 +72,392 @@ if ('onhashchange' in window) { 13 //: longer valid. In both cases you end up having to reorganize code as well as 14 //: tests, an error-prone activity. 15 //: - 16 //: This file tries to fix this problem by supporting domain-driven testing. - 17 //: We try to focus on the domain of inputs the program should work on. All - 18 //: tests invoke the program in a single way: by calling run() with different - 19 //: inputs. The program operates on the input and logs _facts_ it deduces to a - 20 //: trace: + 16 //: In response, this layer introduces the notion of *domain-driven* testing. + 17 //: We focus on the domain of inputs the whole program needs to handle rather + 18 //: than the correctness of individual functions. All tests invoke the program + 19 //: in a single way: by calling run() with some input. As the program operates + 20 //: on the input, it traces out a list of _facts_ deduced about the domain: 21 //: trace("label") << "fact 1: " << val; 22 //: - 23 //: The tests check for facts: + 23 //: Tests can now check these facts: 24 //: :(scenario foo) 25 //: 34 # call run() with this input - 26 //: +label: fact 1: 34 # trace should have logged this at the end - 27 //: -label: fact 1: 35 # trace should never contain such a line + 26 //: +label: fact 1: 34 # 'run' should have deduced this fact + 27 //: -label: fact 1: 35 # the trace should not contain such a fact 28 //: 29 //: Since we never call anything but the run() function directly, we never have 30 //: to rewrite the tests when we reorganize the internals of the program. We 31 //: just have to make sure our rewrite deduces the same facts about the domain, 32 //: and that's something we're going to have to do anyway. 33 //: - 34 //: To avoid the combinatorial explosion of integration tests, each layer logs - 35 //: facts to the trace with a common label. Tests in that layer focus on the - 36 //: same label. In essence, validating the facts logged with a specific label - 37 //: is identical to calling some internal subsystem directly. + 34 //: To avoid the combinatorial explosion of integration tests, each layer + 35 //: mainly logs facts to the trace with a common *label*. All tests in a layer + 36 //: tend to check facts with this label. Validating the facts logged with a + 37 //: specific label is like calling functions of that layer directly. 38 //: - 39 //: Traces interact salubriously with layers. Thanks to our ordering - 40 //: directives, each layer can contain its own tests. They may rely on other - 41 //: layers, but when a test fails it's usually due to breakage in the same - 42 //: layer. When multiple tests fail, it's usually useful to debug the very - 43 //: first test to fail. This is in contrast with the traditional organization - 44 //: of code, where changes can cause breakages in faraway subsystems, and - 45 //: picking the right test to debug can be an important skill to pick up. - 46 //: - 47 //: To build robust tests, trace facts about your domain rather than details of - 48 //: how you computed them. - 49 //: - 50 //: More details: http://akkartik.name/blog/tracing-tests - 51 //: - 52 //: --- - 53 //: - 54 //: Between layers and domain-driven testing, programming starts to look like a - 55 //: fundamentally different activity. Instead of a) superficial, b) local rules - 56 //: on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet], - 57 //: we allow programmers to engage with the a) deep, b) global structure of the - 58 //: c) domain. If you can systematically track discontinuities in the domain, - 59 //: you don't care if the code used gotos as long as it passed the tests. If - 60 //: tests become more robust to run it becomes easier to try out radically - 61 //: different implementations for the same program. If code is super-easy to - 62 //: rewrite, it becomes less important what indentation style it uses, or that - 63 //: the objects are appropriately encapsulated, or that the functions are - 64 //: referentially transparent. + 39 //: To build robust tests, trace facts about your domain rather than details of + 40 //: how you computed them. + 41 //: + 42 //: More details: http://akkartik.name/blog/tracing-tests + 43 //: + 44 //: --- + 45 //: + 46 //: Between layers and domain-driven testing, programming starts to look like a + 47 //: fundamentally different activity. Instead of a) superficial, b) local rules + 48 //: on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet], + 49 //: we allow programmers to engage with the a) deep, b) global structure of the + 50 //: c) domain. If you can systematically track discontinuities in the domain, + 51 //: you don't care if the code used gotos as long as it passed the tests. If + 52 //: tests become more robust to run it becomes easier to try out radically + 53 //: different implementations for the same program. If code is super-easy to + 54 //: rewrite, it becomes less important what indentation style it uses, or that + 55 //: the objects are appropriately encapsulated, or that the functions are + 56 //: referentially transparent. + 57 //: + 58 //: Instead of plumbing, programming becomes building and gradually refining a + 59 //: map of the environment the program must operate under. Whether a program is + 60 //: 'correct' at a given point in time is a red herring; what matters is + 61 //: avoiding regression by monotonically nailing down the more 'eventful' parts + 62 //: of the terrain. It helps readers new and old, and rewards curiosity, to + 63 //: organize large programs in self-similar hierarchies of example scenarios + 64 //: colocated with the code that makes them work. 65 //: - 66 //: Instead of plumbing, programming becomes building and gradually refining a - 67 //: map of the environment the program must operate under. Whether a program is - 68 //: 'correct' at a given point in time is a red herring; what matters is - 69 //: avoiding regression by monotonically nailing down the more 'eventful' parts - 70 //: of the terrain. It helps readers new and old, and rewards curiosity, to - 71 //: organize large programs in self-similar hierarchies of example scenarios - 72 //: colocated with the code that makes them work. - 73 //: - 74 //: "Programming properly should be regarded as an activity by which - 75 //: programmers form a mental model, rather than as production of a program." - 76 //: -- Peter Naur (http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22) - 77 - 78 :(before "End Types") - 79 struct trace_line { - 80 int depth; // optional field just to help browse traces later - 81 string label; - 82 string contents; - 83 trace_line(string l, string c) :depth(0), label(l), contents(c) {} - 84 trace_line(int d, string l, string c) :depth(d), label(l), contents(c) {} - 85 }; - 86 - 87 :(before "End Globals") - 88 bool Hide_errors = false; - 89 bool Dump_trace = false; - 90 string Dump_label = ""; - 91 :(before "End Setup") - 92 Hide_errors = false; - 93 Dump_trace = false; - 94 Dump_label = ""; + 66 //: "Programming properly should be regarded as an activity by which + 67 //: programmers form a mental model, rather than as production of a program." + 68 //: -- Peter Naur (http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22) + 69 + 70 :(before "End Types") + 71 struct trace_line { + 72 int depth; // optional field just to help browse traces later + 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 // Pre-define some global constants that trace_stream needs to know about. + 90 // Since they're in the Types section, they'll be included in any cleaved + 91 // compilation units. So no extern linkage. + 92 const int Max_depth = 9999; + 93 const int Error_depth = 0; // definitely always print errors + 94 const int App_depth = 2; // temporarily where all Mu code will trace to 95 - 96 :(before "End Types") - 97 // Pre-define some global constants that trace_stream needs to know about. - 98 // Since they're in the Types section, they'll be included in any cleaved - 99 // compilation units. So no extern linkage. -100 const int Max_depth = 9999; -101 const int Error_depth = 0; // definitely always print errors -102 const int App_depth = 2; // temporarily where all Mu code will trace to -103 -104 struct trace_stream { -105 vector<trace_line> past_lines; -106 // accumulator for current line -107 ostringstream* curr_stream; -108 string curr_label; -109 int curr_depth; -110 int callstack_depth; -111 int collect_depth; -112 ofstream null_stream; // never opens a file, so writes silently fail -113 trace_stream() :curr_stream(NULL), curr_depth(Max_depth), callstack_depth(0), collect_depth(Max_depth) {} -114 ~trace_stream() { if (curr_stream) delete curr_stream; } -115 -116 ostream& stream(string label) { -117 return stream(Max_depth, label); + 96 struct trace_stream { + 97 vector<trace_line> past_lines; + 98 // accumulator for current line + 99 ostringstream* curr_stream; +100 string curr_label; +101 int curr_depth; +102 int callstack_depth; +103 int collect_depth; +104 ofstream null_stream; // never opens a file, so writes silently fail +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 ostream& stream(int depth, string label) { -121 if (depth > collect_depth) return null_stream; -122 curr_stream = new ostringstream; -123 curr_label = label; -124 curr_depth = depth; -125 return *curr_stream; -126 } -127 -128 // be sure to call this before messing with curr_stream or curr_label -129 void newline(); -130 // useful for debugging -131 string readable_contents(string label); // empty label = show everything -132 }; -133 -134 :(code) -135 void trace_stream::newline() { -136 if (!curr_stream) return; -137 string curr_contents = curr_stream->str(); -138 if (!curr_contents.empty()) { -139 past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents)); // preserve indent in contents -140 if ((!Hide_errors && curr_label == "error") -141 || Dump_trace -142 || (!Dump_label.empty() && curr_label == Dump_label)) -143 cerr << curr_label << ": " << curr_contents << '\n'; -144 } -145 delete curr_stream; -146 curr_stream = NULL; -147 curr_label.clear(); -148 curr_depth = Max_depth; -149 } -150 -151 string trace_stream::readable_contents(string label) { -152 ostringstream output; -153 label = trim(label); -154 for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p) -155 if (label.empty() || label == p->label) { -156 output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n'; -157 } -158 return output.str(); -159 } -160 -161 :(before "End Globals") -162 trace_stream* Trace_stream = NULL; -163 int Trace_errors = 0; // used only when Trace_stream is NULL -164 -165 :(before "End Includes") -166 #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream; -167 -168 // Top-level helper. IMPORTANT: can't nest -169 #define trace(...) !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(__VA_ARGS__) -170 -171 // Just for debugging; 'git log' should never show any calls to 'dbg'. -172 #define dbg trace(0, "a") -173 #define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label); -174 -175 // Errors are a special layer. -176 #define raise (!Trace_stream ? (tb_shutdown(),++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error")) -177 // If we aren't yet sure how to deal with some corner case, use assert_for_now -178 // to indicate that it isn't an inviolable invariant. -179 #define assert_for_now assert -180 -181 // Inside tests, fail any tests that displayed (unexpected) errors. -182 // Expected errors in tests should always be hidden and silently checked for. -183 :(before "End Test Teardown") -184 if (Passed && !Hide_errors && trace_contains_errors()) { -185 Passed = false; -186 } -187 :(code) -188 bool trace_contains_errors() { -189 return Trace_errors > 0 || trace_count("error") > 0; +120 // be sure to call this before messing with curr_stream or curr_label +121 void newline(); +122 // useful for debugging +123 string readable_contents(string label); // empty label = show everything +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)); // preserve indent in 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; // used only when Trace_stream is NULL +156 +157 :(before "End Includes") +158 #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream; +159 +160 // Top-level helper. IMPORTANT: can't nest +161 #define trace(...) !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(__VA_ARGS__) +162 +163 // Just for debugging; 'git log' should never show any calls to 'dbg'. +164 #define dbg trace(0, "a") +165 #define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label); +166 +167 // Errors are a special layer. +168 #define raise (!Trace_stream ? (tb_shutdown(),++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error")) +169 // If we aren't yet sure how to deal with some corner case, use assert_for_now +170 // to indicate that it isn't an inviolable invariant. +171 #define assert_for_now assert +172 +173 // Inside tests, fail any tests that displayed (unexpected) errors. +174 // Expected errors in tests should always be hidden and silently checked for. +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 Types") -193 struct end {}; -194 :(code) -195 ostream& operator<<(ostream& os, unused end) { -196 if (Trace_stream) Trace_stream->newline(); -197 return os; -198 } -199 -200 :(before "End Globals") -201 bool Save_trace = false; -202 -203 // Trace_stream is a resource, lease_tracer uses RAII to manage it. -204 :(before "End Types") -205 struct lease_tracer { -206 lease_tracer(); -207 ~lease_tracer(); -208 }; -209 :(code) -210 lease_tracer::lease_tracer() { Trace_stream = new trace_stream; } -211 lease_tracer::~lease_tracer() { -212 if (!Trace_stream) return; // in case tests close Trace_stream -213 if (Save_trace) { -214 ofstream fout("last_trace"); -215 fout << Trace_stream->readable_contents(""); -216 fout.close(); -217 } -218 delete Trace_stream, Trace_stream = NULL; -219 } -220 :(before "End Includes") -221 #define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer; -222 :(before "End Test Setup") -223 START_TRACING_UNTIL_END_OF_SCOPE -224 -225 :(before "End Includes") -226 #define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__) -227 -228 #define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors()) -229 #define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \ -230 if (Passed && trace_contains_errors()) { \ -231 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \ -232 DUMP("error"); \ -233 Passed = false; \ -234 return; \ -235 } -236 -237 #define CHECK_TRACE_COUNT(label, count) \ -238 if (Passed && trace_count(label) != (count)) { \ -239 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \ -240 cerr << " got " << trace_count(label) << '\n'; /* multiple eval */ \ -241 DUMP(label); \ -242 Passed = false; \ -243 return; /* Currently we stop at the very first failure. */ \ -244 } -245 -246 #define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__)) -247 -248 :(code) -249 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) { -250 if (!Passed) return false; -251 if (!Trace_stream) return false; -252 vector<string> expected_lines = split(expected, "^D"); -253 int curr_expected_line = 0; -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 string label, contents; -258 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); -259 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -260 if (label != p->label) continue; -261 if (contents != trim(p->contents)) continue; -262 ++curr_expected_line; -263 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) -264 ++curr_expected_line; -265 if (curr_expected_line == SIZE(expected_lines)) return true; -266 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); -267 } -268 -269 if (line_exists_anywhere(label, contents)) { -270 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n"; -271 DUMP(""); -272 } -273 else { -274 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n"; -275 DUMP(label); -276 } -277 Passed = false; -278 return false; -279 } -280 -281 void split_label_contents(const string& s, string* label, string* contents) { -282 static const string delim(": "); -283 size_t pos = s.find(delim); -284 if (pos == string::npos) { -285 *label = ""; -286 *contents = trim(s); -287 } -288 else { -289 *label = trim(s.substr(0, pos)); -290 *contents = trim(s.substr(pos+SIZE(delim))); -291 } +192 :(before "End Globals") +193 bool Save_trace = false; +194 +195 // Trace_stream is a resource, lease_tracer uses RAII to manage it. +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; // in case tests close Trace_stream +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'; /* multiple eval */ \ +233 DUMP(label); \ +234 Passed = false; \ +235 return; /* Currently we stop at the very first failure. */ \ +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 bool line_exists_anywhere(const string& label, const string& contents) { -295 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -296 if (label != p->label) continue; -297 if (contents == trim(p->contents)) return true; -298 } -299 return false; -300 } -301 -302 int trace_count(string label) { -303 return trace_count(label, ""); -304 } -305 -306 int trace_count(string label, string line) { -307 if (!Trace_stream) return 0; -308 long result = 0; -309 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -310 if (label == p->label) { -311 if (line == "" || trim(line) == trim(p->contents)) -312 ++result; -313 } -314 } -315 return result; -316 } -317 -318 int trace_count_prefix(string label, string prefix) { -319 if (!Trace_stream) return 0; -320 long result = 0; -321 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { -322 if (label == p->label) { -323 if (starts_with(trim(p->contents), trim(prefix))) -324 ++result; -325 } -326 } -327 return result; -328 } -329 -330 bool trace_doesnt_contain(string label, string line) { -331 return trace_count(label, line) == 0; -332 } -333 -334 bool trace_doesnt_contain(string expected) { -335 vector<string> tmp = split_first(expected, ": "); -336 return trace_doesnt_contain(tmp.at(0), tmp.at(1)); -337 } -338 -339 vector<string> split(string s, string delim) { -340 vector<string> result; -341 size_t begin=0, end=s.find(delim); -342 while (true) { -343 if (end == string::npos) { -344 result.push_back(string(s, begin, string::npos)); -345 break; -346 } -347 result.push_back(string(s, begin, end-begin)); -348 begin = end+SIZE(delim); -349 end = s.find(delim, begin); -350 } -351 return result; -352 } -353 -354 vector<string> split_first(string s, string delim) { -355 vector<string> result; -356 size_t end=s.find(delim); -357 result.push_back(string(s, 0, end)); -358 if (end != string::npos) -359 result.push_back(string(s, end+SIZE(delim), string::npos)); -360 return result; -361 } -362 -363 string trim(const string& s) { -364 string::const_iterator first = s.begin(); -365 while (first != s.end() && isspace(*first)) -366 ++first; -367 if (first == s.end()) return ""; -368 -369 string::const_iterator last = --s.end(); -370 while (last != s.begin() && isspace(*last)) -371 --last; -372 ++last; -373 return string(first, last); -374 } -375 -376 :(before "End Includes") -377 #include <vector> -378 using std::vector; -379 #include <list> -380 using std::list; -381 #include <map> -382 using std::map; -383 #include <set> -384 using std::set; -385 #include <algorithm> +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 <sstream> -388 using std::istringstream; -389 using std::ostringstream; -390 -391 #include <fstream> -392 using std::ifstream; -393 using std::ofstream; -394 -395 #include "termbox/termbox.h" -396 -397 :(before "End Globals") -398 //: In future layers we'll use the depth field as follows: +387 #include "termbox/termbox.h" +388 +389 :(before "End Globals") +390 //: In future layers we'll use the depth field as follows: +391 //: +392 //: Errors will be depth 0. +393 //: Mu 'applications' will be able to use depths 1-100 as they like. +394 //: Primitive statements will occupy 101-9989 +395 extern const int Initial_callstack_depth = 101; +396 extern const int Max_callstack_depth = 9989; +397 //: Finally, details of primitive Mu statements will occupy depth 9990-9999 +398 //: (more on that later as well) 399 //: -400 //: Errors will be depth 0. -401 //: Mu 'applications' will be able to use depths 1-100 as they like. -402 //: Primitive statements will occupy 101-9989 -403 extern const int Initial_callstack_depth = 101; -404 extern const int Max_callstack_depth = 9989; -405 //: Finally, details of primitive Mu statements will occupy depth 9990-9999 -406 //: (more on that later as well) -407 //: -408 //: This framework should help us hide some details at each level, mixing -409 //: static ideas like layers with the dynamic notion of call-stack depth. +400 //: This framework should help us hide some details at each level, mixing +401 //: static ideas like layers with the dynamic notion of call-stack depth. -- cgit 1.4.1-2-gfad0