//: Allow tests to be written in mu files. :(before "End Types") struct scenario { string name; string dump_layer; string to_run; map memory_expectations; // End scenario Fields }; :(before "End Globals") vector Scenarios; //:: How we check Scenarios. :(before "End Tests") time_t mu_time; time(&mu_time); cerr << "\nMu tests: " << ctime(&mu_time); for (size_t i = 0; i < Scenarios.size(); ++i) { run_mu_test(i); } :(code) void run_mu_test(size_t i) { setup(); Trace_file = Scenarios[i].name; START_TRACING_UNTIL_END_OF_SCOPE if (!Scenarios[i].dump_layer.empty()) Trace_stream->dump_layer = Scenarios[i].dump_layer; //? cerr << "AAA " << Scenarios[i].name << '\n'; //? 1 //? cout << Scenarios[i].to_run; //? 2 run(Scenarios[i].to_run); //? cout << "after: " << Memory[1] << '\n'; //? 1 //? cout << "after:\n"; dump_memory(); //? 1 for (map::iterator p = Scenarios[i].memory_expectations.begin(); p != Scenarios[i].memory_expectations.end(); ++p) { if (Memory[p->first] != p->second) { // todo: unit tests for the test parsing infrastructure; use raise? cerr << Scenarios[i].name << ": Expected location " << p->first << " to contain " << p->second << " but saw " << Memory[p->first] << '\n'; Passed = false; } } // End Scenario Checks teardown(); if (Passed) cerr << "."; } //:: How we create Scenarios. :(scenarios "parse_scenario") :(scenario parse_scenario_memory_expectation) scenario foo [ run [ a <- b ] memory should contain [ 1 <- 0 ] ] +parse: scenario will run: a <- b :(scenario parse_scenario_memory_expectation_duplicate) % Hide_warnings = true; scenario foo [ run [ a <- b ] memory should contain [ 1 <- 0 1 <- 1 ] ] +warn: duplicate expectation for location 1: 0 -> 1 :(before "End Command Handlers") else if (command == "scenario") { //? cout << "AAA scenario\n"; //? 1 Scenarios.push_back(parse_scenario(in)); } :(code) scenario parse_scenario(istream& in) { scenario x; x.name = next_word(in); trace("parse") << "reading scenario " << x.name; skip_bracket(in, "'scenario' must begin with '['"); ostringstream buffer; slurp_until_matching_bracket(in, buffer); //? cout << "inner buffer: ^" << buffer.str() << "$\n"; //? 1 istringstream inner(buffer.str()); inner >> std::noskipws; while (!inner.eof()) { skip_whitespace_and_comments(inner); string scenario_command = next_word(inner); if (scenario_command.empty() && inner.eof()) break; // Scenario Command Handlers if (scenario_command == "run") { handle_scenario_run_directive(inner, x); } else if (scenario_command == "memory") { handle_scenario_memory_directive(inner, x); } else if (scenario_command == "dump") { skip_whitespace_and_comments(inner); x.dump_layer = next_word(inner); } // End Scenario Command Handlers else { raise << "unknown command in scenario: ^" << scenario_command << "$\n"; } } return x; } void handle_scenario_run_directive(istream& in, scenario& result) { skip_bracket(in, "'run' inside scenario must begin with '['"); ostringstream buffer; slurp_until_matching_bracket(in, buffer); string trace_result = buffer.str(); // temporary copy trace("parse") << "scenario will run: " << trim(trace_result); //? cout << buffer.str() << '\n'; //? 1 result.to_run = "recipe test-"+result.name+" [" + buffer.str() + "]"; } void handle_scenario_memory_directive(istream& in, scenario& out) { if (next_word(in) != "should") { raise << "'memory' directive inside scenario must continue 'memory should'\n"; } if (next_word(in) != "contain") { raise << "'memory' directive inside scenario must continue 'memory should contain'\n"; } skip_bracket(in, "'memory' directive inside scenario must begin with 'memory should contain ['\n"); while (true) { skip_whitespace_and_comments(in); if (in.eof()) break; //? cout << "a: " << in.peek() << '\n'; //? 1 if (in.peek() == ']') break; string lhs = next_word(in); if (!is_number(lhs)) { handle_type(lhs, in, out); continue; } int address = to_int(lhs); //? cout << "address: " << address << '\n'; //? 2 //? cout << "b: " << in.peek() << '\n'; //? 1 skip_whitespace_and_comments(in); //? cout << "c: " << in.peek() << '\n'; //? 1 string _assign; in >> _assign; assert(_assign == "<-"); skip_whitespace_and_comments(in); int value = 0; in >> value; if (out.memory_expectations.find(address) != out.memory_expectations.end()) raise << "duplicate expectation for location " << address << ": " << out.memory_expectations[address] << " -> " << value << '\n'; out.memory_expectations[address] = value; trace("parse") << "memory expectation: *" << address << " == " << value; } skip_whitespace(in); assert(in.get() == ']'); } void handle_type(const string& lhs, istream& in, scenario& out) { reagent x(lhs); if (x.properties[0].second[0] == "string") { x.set_value(to_int(x.name)); //? cerr << x.name << ' ' << x.value << '\n'; //? 1 skip_whitespace_and_comments(in); string _assign = next_word(in); //? cerr << _assign << '\n'; //? 1 assert(_assign == "<-"); skip_whitespace_and_comments(in); string literal = next_word(in); //? cerr << literal << '\n'; //? 1 size_t address = x.value; out.memory_expectations[address] = literal.size()-2; // exclude quoting brackets ++address; for (size_t i = 1; i < literal.size()-1; ++i) { //? cerr << "checking " << address << ": " << literal[i] << '\n'; //? 1 out.memory_expectations[address] = literal[i]; ++address; } return; } raise << "scenario doesn't know how to parse memory expectation on " << lhs << '\n'; } //:: Helpers void slurp_until_matching_bracket(istream& in, ostream& out) { int brace_depth = 1; // just scanned '[' char c; while (in >> c) { if (c == '[') ++brace_depth; if (c == ']') --brace_depth; if (brace_depth == 0) break; // drop final ']' out << c; } } :(code) // for tests void parse_scenario(const string& s) { istringstream in(s); in >> std::noskipws; skip_whitespace_and_comments(in); string _scenario = next_word(in); //? cout << _scenario << '\n'; //? 1 assert(_scenario == "scenario"); parse_scenario(in); } string &trim(string &s) { return ltrim(rtrim(s)); } string <rim(string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(isspace)))); return s; } string &rtrim(string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(isspace))).base(), s.end()); return s; } :(before "End Includes") #include :(code) bool file_exists(const string& filename) { struct stat buffer; return stat(filename.c_str(), &buffer) == 0; }