:(before "End Types")
struct trace_line {
int depth;
string label;
string contents;
trace_line(string l, string c) :depth(0), label(l), contents(c) {}
trace_line(int d, string l, string c) :depth(d), label(l), contents(c) {}
};
:(before "End Globals")
bool Hide_errors = false;
bool Dump_trace = false;
:(before "End Setup")
Hide_errors = false;
Dump_trace = false;
:(before "End Types")
const int Max_depth = 9999;
const int Error_depth = 0;
const int App_depth = 2;
struct trace_stream {
vector<trace_line> past_lines;
ostringstream* curr_stream;
string curr_label;
int curr_depth;
int callstack_depth;
int collect_depth;
ofstream null_stream;
trace_stream() :curr_stream(NULL), curr_depth(Max_depth), callstack_depth(0), collect_depth(Max_depth) {}
~trace_stream() { if (curr_stream) delete curr_stream; }
ostream& stream(string label) {
return stream(Max_depth, label);
}
ostream& stream(int depth, string label) {
if (depth > collect_depth) return null_stream;
curr_stream = new ostringstream;
curr_label = label;
curr_depth = depth;
return *curr_stream;
}
void newline();
string readable_contents(string label);
};
:(code)
void trace_stream::newline() {
if (!curr_stream) return;
string curr_contents = curr_stream->str();
if (!curr_contents.empty()) {
past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents));
if (Dump_trace || (!Hide_errors && curr_label == "error"))
cerr << curr_label << ": " << curr_contents << '\n';
}
delete curr_stream;
curr_stream = NULL;
curr_label.clear();
curr_depth = Max_depth;
}
string trace_stream::readable_contents(string label) {
ostringstream output;
label = trim(label);
for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
if (label.empty() || label == p->label) {
output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n';
}
return output.str();
}
:(before "End Globals")
trace_stream* Trace_stream = NULL;
int Trace_errors = 0;
:(before "End Includes")
#define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream;
#define trace(...) !Trace_stream ? cerr : Trace_stream->stream(__VA_ARGS__)
#define dbg trace(0, "a")
#define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label);
#define raise (!Trace_stream ? (tb_shutdown(),++Trace_errors,cerr) : Trace_stream->stream(Error_depth, "error"))
:(before "End Test Teardown")
if (Passed && !Hide_errors && trace_count("error") > 0) {
Passed = false;
}
:(before "End Types")
struct end {};
:(code)
ostream& operator<<(ostream& os, unused end) {
if (Trace_stream) Trace_stream->newline();
return os;
}
:(before "End Globals")
bool Save_trace = false;
:(before "End Types")
struct lease_tracer {
lease_tracer();
~lease_tracer();
};
:(code)
lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
lease_tracer::~lease_tracer() {
if (!Trace_stream) return;
if (Save_trace) {
ofstream fout("last_trace");
fout << Trace_stream->readable_contents("");
fout.close();
}
delete Trace_stream, Trace_stream = NULL;
}
:(before "End Includes")
#define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer;
:(before "End Test Setup")
START_TRACING_UNTIL_END_OF_SCOPE
:(before "End Includes")
#define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
#define CHECK_TRACE_CONTAINS_ERROR() CHECK(trace_count("error") > 0)
#define CHECK_TRACE_DOESNT_CONTAIN_ERROR() \
if (Passed && trace_count("error") > 0) { \
cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
DUMP("error"); \
Passed = false; \
return; \
}
#define CHECK_TRACE_COUNT(label, count) \
if (Passed && trace_count(label) != (count)) { \
cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \
cerr << " got " << trace_count(label) << '\n'; \
DUMP(label); \
Passed = false; \
return; \
}
#define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__))
:(code)
bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) {
if (!Passed) return false;
if (!Trace_stream) return false;
vector<string> expected_lines = split(expected, "^D");
int curr_expected_line = 0;
while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
++curr_expected_line;
if (curr_expected_line == SIZE(expected_lines)) return true;
string label, contents;
split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
if (label != p->label) continue;
if (contents != trim(p->contents)) continue;
++curr_expected_line;
while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
++curr_expected_line;
if (curr_expected_line == SIZE(expected_lines)) return true;
split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
}
if (line_exists_anywhere(label, contents)) {
cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n";
DUMP("");
}
else {
cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n";
DUMP(label);
}
Passed = false;
return false;
}
void split_label_contents(const string& s, string* label, string* contents) {
static const string delim(": ");
size_t pos = s.find(delim);
if (pos == string::npos) {
*label = "";
*contents = trim(s);
}
else {
*label = trim(s.substr(0, pos));
*contents = trim(s.substr(pos+SIZE(delim)));
}
}
bool line_exists_anywhere(const string& label, const string& contents) {
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
if (label != p->label) continue;
if (contents == trim(p->contents)) return true;
}
return false;
}
int trace_count(string label) {
return trace_count(label, "");
}
int trace_count(string label, string line) {
if (!Trace_stream) return 0;
long result = 0;
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
if (label == p->label) {
if (line == "" || trim(line) == trim(p->contents))
++result;
}
}
return result;
}
int trace_count_prefix(string label, string prefix) {
if (!Trace_stream) return 0;
long result = 0;
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
if (label == p->label) {
if (starts_with(trim(p->contents), trim(prefix)))
++result;
}
}
return result;
}
bool trace_doesnt_contain(string label, string line) {
return trace_count(label, line) == 0;
}
bool trace_doesnt_contain(string expected) {
vector<string> tmp = split_first(expected, ": ");
return trace_doesnt_contain(tmp.at(0), tmp.at(1));
}
vector<string> split(string s, string delim) {
vector<string> result;
size_t begin=0, end=s.find(delim);
while (true) {
if (end == string::npos) {
result.push_back(string(s, begin, string::npos));
break;
}
result.push_back(string(s, begin, end-begin));
begin = end+SIZE(delim);
end = s.find(delim, begin);
}
return result;
}
vector<string> split_first(string s, string delim) {
vector<string> result;
size_t end=s.find(delim);
result.push_back(string(s, 0, end));
if (end != string::npos)
result.push_back(string(s, end+SIZE(delim), string::npos));
return result;
}
string trim(const string& s) {
string::const_iterator first = s.begin();
while (first != s.end() && isspace(*first))
++first;
if (first == s.end()) return "";
string::const_iterator last = --s.end();
while (last != s.begin() && isspace(*last))
--last;
++last;
return string(first, last);
}
:(before "End Includes")
#include <vector>
using std::vector;
#include <list>
using std::list;
#include <map>
using std::map;
#include <set>
using std::set;
#include <algorithm>
#include <sstream>
using std::istringstream;
using std::ostringstream;
#include <fstream>
using std::ifstream;
using std::ofstream;
#include "termbox/termbox.h"
:(before "End Globals")
extern const int Initial_callstack_depth = 101;
extern const int Max_callstack_depth = 9989;