2015-05-04 18:56:16 +00:00
|
|
|
//: Mu scenarios. This will get long, but these are the tests we want to
|
|
|
|
//: support in this layer.
|
2015-04-20 17:14:56 +00:00
|
|
|
|
2015-05-04 22:00:30 +00:00
|
|
|
//: You can use variable names in scenarios, but for the most part we'll use
|
|
|
|
//: raw location numbers, because that lets us make assertions on memory.
|
|
|
|
//: Tests should avoid abstraction as far as possible.
|
2015-05-02 22:52:22 +00:00
|
|
|
:(scenarios run_mu_scenario)
|
|
|
|
:(scenario scenario_block)
|
|
|
|
scenario foo [
|
|
|
|
run [
|
2015-05-13 17:03:26 +00:00
|
|
|
1:number <- copy 13:literal
|
2015-05-02 22:52:22 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
1 <- 13
|
|
|
|
]
|
|
|
|
]
|
|
|
|
# checks are inside scenario
|
2015-04-20 17:14:56 +00:00
|
|
|
|
2015-05-02 22:52:22 +00:00
|
|
|
:(scenario scenario_multiple_blocks)
|
2015-04-20 17:14:56 +00:00
|
|
|
scenario foo [
|
|
|
|
run [
|
2015-05-13 17:03:26 +00:00
|
|
|
1:number <- copy 13:literal
|
2015-05-02 22:52:22 +00:00
|
|
|
]
|
|
|
|
memory-should-contain [
|
|
|
|
1 <- 13
|
|
|
|
]
|
|
|
|
run [
|
2015-05-13 17:03:26 +00:00
|
|
|
2:number <- copy 13:literal
|
2015-04-20 17:14:56 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
|
|
|
1 <- 13
|
|
|
|
2 <- 13
|
2015-04-20 17:14:56 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2015-05-02 22:52:22 +00:00
|
|
|
:(scenario scenario_check_memory_and_trace)
|
2015-04-20 17:19:35 +00:00
|
|
|
scenario foo [
|
|
|
|
run [
|
2015-05-13 17:03:26 +00:00
|
|
|
1:number <- copy 13:literal
|
2015-05-02 22:52:22 +00:00
|
|
|
trace [a], [a b c]
|
2015-04-20 17:19:35 +00:00
|
|
|
]
|
2015-05-02 22:52:22 +00:00
|
|
|
memory-should-contain [
|
|
|
|
1 <- 13
|
|
|
|
]
|
|
|
|
trace-should-contain [
|
|
|
|
a: a b c
|
|
|
|
]
|
|
|
|
trace-should-not-contain [
|
|
|
|
a: x y z
|
2015-04-20 17:19:35 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
2015-05-04 18:56:16 +00:00
|
|
|
//:: Core data structure
|
|
|
|
|
|
|
|
:(before "End Types")
|
|
|
|
struct scenario {
|
|
|
|
string name;
|
|
|
|
string to_run;
|
|
|
|
};
|
|
|
|
|
|
|
|
:(before "End Globals")
|
|
|
|
vector<scenario> Scenarios;
|
|
|
|
|
|
|
|
//:: Parse the 'scenario' form.
|
|
|
|
//: Simply store the text of the scenario.
|
|
|
|
|
|
|
|
:(before "End Command Handlers")
|
|
|
|
else if (command == "scenario") {
|
|
|
|
Scenarios.push_back(parse_scenario(in));
|
2015-04-06 19:02:38 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 18:56:16 +00:00
|
|
|
:(code)
|
2015-04-06 19:02:38 +00:00
|
|
|
scenario parse_scenario(istream& in) {
|
2015-05-02 22:52:22 +00:00
|
|
|
scenario result;
|
|
|
|
result.name = next_word(in);
|
2015-04-08 07:47:04 +00:00
|
|
|
skip_bracket(in, "'scenario' must begin with '['");
|
|
|
|
ostringstream buffer;
|
|
|
|
slurp_until_matching_bracket(in, buffer);
|
2015-05-02 22:52:22 +00:00
|
|
|
result.to_run = buffer.str();
|
|
|
|
return result;
|
2015-04-08 07:47:04 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 18:56:16 +00:00
|
|
|
//:: Run scenarios when we run 'mu test'.
|
|
|
|
//: Treat the text of the scenario as a regular series of instructions.
|
|
|
|
|
|
|
|
:(before "End Tests")
|
|
|
|
time_t mu_time; time(&mu_time);
|
|
|
|
cerr << "\nMu tests: " << ctime(&mu_time);
|
2015-05-17 09:22:41 +00:00
|
|
|
for (long long int i = 0; i < SIZE(Scenarios); ++i) {
|
2015-05-04 18:56:16 +00:00
|
|
|
//? cerr << Passed << '\n'; //? 1
|
2015-05-14 22:40:31 +00:00
|
|
|
//? cerr << i << ": " << Scenarios.at(i).name << '\n'; //? 3
|
2015-05-07 22:06:53 +00:00
|
|
|
run_mu_scenario(Scenarios.at(i));
|
2015-05-04 18:56:16 +00:00
|
|
|
if (Passed) cerr << ".";
|
|
|
|
}
|
|
|
|
|
2015-05-12 16:20:19 +00:00
|
|
|
//: Convenience: run a single named scenario.
|
|
|
|
:(before "Loading Commandline Files")
|
|
|
|
if (argc == 2 && Run_tests) {
|
2015-05-17 09:22:41 +00:00
|
|
|
for (long long int i = 0; i < SIZE(Scenarios); ++i) {
|
2015-05-12 16:20:19 +00:00
|
|
|
if (Scenarios.at(i).name == argv[1]) {
|
|
|
|
run_mu_scenario(Scenarios.at(i));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-04 22:06:46 +00:00
|
|
|
:(before "End Globals")
|
|
|
|
const scenario* Current_scenario = NULL;
|
2015-05-04 18:56:16 +00:00
|
|
|
:(code)
|
2015-05-02 22:52:22 +00:00
|
|
|
void run_mu_scenario(const scenario& s) {
|
2015-05-04 22:06:46 +00:00
|
|
|
Current_scenario = &s;
|
2015-05-04 17:31:52 +00:00
|
|
|
bool not_already_inside_test = !Trace_stream;
|
|
|
|
if (not_already_inside_test) {
|
|
|
|
Trace_file = s.name;
|
|
|
|
Trace_stream = new trace_stream;
|
|
|
|
setup();
|
|
|
|
}
|
2015-05-02 22:52:22 +00:00
|
|
|
run("recipe "+s.name+" [ " + s.to_run + " ]");
|
2015-05-19 01:51:48 +00:00
|
|
|
if (not_already_inside_test && Trace_stream) {
|
2015-05-04 17:31:52 +00:00
|
|
|
teardown();
|
2015-05-02 22:52:22 +00:00
|
|
|
ofstream fout((Trace_dir+Trace_file).c_str());
|
|
|
|
fout << Trace_stream->readable_contents("");
|
|
|
|
fout.close();
|
|
|
|
delete Trace_stream;
|
|
|
|
Trace_stream = NULL;
|
2015-05-04 17:31:52 +00:00
|
|
|
Trace_file = "";
|
2015-05-02 22:52:22 +00:00
|
|
|
}
|
2015-05-04 22:06:46 +00:00
|
|
|
Current_scenario = NULL;
|
2015-04-08 07:47:04 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 18:56:16 +00:00
|
|
|
//:: The special instructions we want to support inside scenarios.
|
|
|
|
//: In a compiler for the mu VM these will require more work.
|
|
|
|
|
|
|
|
//: 'run' interprets a string as a set of instructions
|
|
|
|
|
|
|
|
:(scenarios run)
|
|
|
|
:(scenario run)
|
|
|
|
#? % Trace_stream->dump_layer = "all";
|
|
|
|
recipe main [
|
|
|
|
run [
|
2015-05-13 17:03:26 +00:00
|
|
|
1:number <- copy 13:literal
|
2015-05-04 18:56:16 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
+mem: storing 13 in location 1
|
|
|
|
|
|
|
|
:(before "End Primitive Recipe Declarations")
|
|
|
|
RUN,
|
|
|
|
:(before "End Primitive Recipe Numbers")
|
|
|
|
Recipe_number["run"] = RUN;
|
|
|
|
:(before "End Primitive Recipe Implementations")
|
|
|
|
case RUN: {
|
2015-05-07 22:06:53 +00:00
|
|
|
//? cout << "recipe " << current_instruction().ingredients.at(0).name << '\n'; //? 1
|
2015-05-04 18:56:16 +00:00
|
|
|
ostringstream tmp;
|
2015-05-07 22:06:53 +00:00
|
|
|
tmp << "recipe run" << Next_recipe_number << " [ " << current_instruction().ingredients.at(0).name << " ]";
|
2015-05-04 18:56:16 +00:00
|
|
|
//? Show_rest_of_stream = true; //? 1
|
|
|
|
vector<recipe_number> tmp_recipe = load(tmp.str());
|
2015-05-04 19:19:53 +00:00
|
|
|
// Predefined Scenario Locals In Run.
|
|
|
|
// End Predefined Scenario Locals In Run.
|
2015-05-04 18:56:16 +00:00
|
|
|
transform_all();
|
2015-05-07 22:06:53 +00:00
|
|
|
//? cout << tmp_recipe.at(0) << ' ' << Recipe_number["main"] << '\n'; //? 1
|
2015-05-13 23:33:40 +00:00
|
|
|
Current_routine->calls.push_front(call(tmp_recipe.at(0)));
|
2015-05-04 18:56:16 +00:00
|
|
|
continue; // not done with caller; don't increment current_step_index()
|
2015-04-08 07:47:04 +00:00
|
|
|
}
|
|
|
|
|
2015-05-04 18:56:16 +00:00
|
|
|
:(scenario run_multiple)
|
|
|
|
recipe main [
|
|
|
|
run [
|
2015-05-13 17:03:26 +00:00
|
|
|
1:number <- copy 13:literal
|
2015-05-04 18:56:16 +00:00
|
|
|
]
|
|
|
|
run [
|
2015-05-13 17:03:26 +00:00
|
|
|
2:number <- copy 13:literal
|
2015-05-04 18:56:16 +00:00
|
|
|
]
|
|
|
|
]
|
|
|
|
+mem: storing 13 in location 1
|
|
|
|
+mem: storing 13 in location 2
|
|
|
|
|
|
|
|
//: 'memory-should-contain' raises warnings if specific locations aren't as expected
|
|
|
|
//: Also includes some special support for checking strings.
|
|
|
|
|
|
|
|
:(scenario memory_check)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
|
|
|
memory-should-contain [
|
|
|
|
1 <- 13
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+run: checking location 1
|
|
|
|
+warn: expected location 1 to contain 13 but saw 0
|
|
|
|
|
|
|
|
:(before "End Primitive Recipe Declarations")
|
|
|
|
MEMORY_SHOULD_CONTAIN,
|
|
|
|
:(before "End Primitive Recipe Numbers")
|
|
|
|
Recipe_number["memory-should-contain"] = MEMORY_SHOULD_CONTAIN;
|
|
|
|
:(before "End Primitive Recipe Implementations")
|
|
|
|
case MEMORY_SHOULD_CONTAIN: {
|
2015-05-07 22:06:53 +00:00
|
|
|
//? cout << current_instruction().ingredients.at(0).name << '\n'; //? 1
|
|
|
|
check_memory(current_instruction().ingredients.at(0).name);
|
2015-05-04 18:56:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
:(code)
|
|
|
|
void check_memory(const string& s) {
|
|
|
|
istringstream in(s);
|
|
|
|
in >> std::noskipws;
|
2015-05-17 09:22:41 +00:00
|
|
|
set<long long int> locations_checked;
|
2015-05-04 18:56:16 +00:00
|
|
|
while (true) {
|
|
|
|
skip_whitespace_and_comments(in);
|
|
|
|
if (in.eof()) break;
|
|
|
|
string lhs = next_word(in);
|
2015-05-17 04:24:21 +00:00
|
|
|
if (!is_integer(lhs)) {
|
2015-05-04 18:56:16 +00:00
|
|
|
check_type(lhs, in);
|
|
|
|
continue;
|
|
|
|
}
|
2015-05-17 04:24:21 +00:00
|
|
|
int address = to_integer(lhs);
|
2015-05-04 18:56:16 +00:00
|
|
|
skip_whitespace_and_comments(in);
|
|
|
|
string _assign; in >> _assign; assert(_assign == "<-");
|
|
|
|
skip_whitespace_and_comments(in);
|
2015-05-13 00:00:56 +00:00
|
|
|
int value = 0; in >> value;
|
2015-05-04 18:56:16 +00:00
|
|
|
if (locations_checked.find(address) != locations_checked.end())
|
|
|
|
raise << "duplicate expectation for location " << address << '\n';
|
2015-05-22 01:57:25 +00:00
|
|
|
trace(Primitive_recipe_depth, "run") << "checking location " << address;
|
2015-05-13 00:00:56 +00:00
|
|
|
if (Memory[address] != value) {
|
2015-05-26 23:15:07 +00:00
|
|
|
if (Current_scenario && !Hide_warnings) {
|
|
|
|
// genuine test in a mu file
|
2015-05-13 00:00:56 +00:00
|
|
|
raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain " << value << " but saw " << Memory[address] << '\n';
|
2015-05-26 23:15:07 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// just testing scenario support
|
2015-05-13 00:00:56 +00:00
|
|
|
raise << "expected location " << address << " to contain " << value << " but saw " << Memory[address] << '\n';
|
2015-05-26 23:15:07 +00:00
|
|
|
}
|
|
|
|
if (!Hide_warnings) {
|
|
|
|
Passed = false;
|
|
|
|
++Num_failures;
|
|
|
|
}
|
2015-05-04 22:06:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-05-04 18:56:16 +00:00
|
|
|
locations_checked.insert(address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void check_type(const string& lhs, istream& in) {
|
|
|
|
reagent x(lhs);
|
2015-05-07 22:06:53 +00:00
|
|
|
if (x.properties.at(0).second.at(0) == "string") {
|
2015-05-17 04:24:21 +00:00
|
|
|
x.set_value(to_integer(x.name));
|
2015-05-04 18:56:16 +00:00
|
|
|
skip_whitespace_and_comments(in);
|
|
|
|
string _assign = next_word(in);
|
|
|
|
assert(_assign == "<-");
|
|
|
|
skip_whitespace_and_comments(in);
|
|
|
|
string literal = next_word(in);
|
2015-05-17 09:22:41 +00:00
|
|
|
long long int address = x.value;
|
2015-05-04 18:56:16 +00:00
|
|
|
// exclude quoting brackets
|
2015-05-07 22:49:40 +00:00
|
|
|
assert(*literal.begin() == '['); literal.erase(literal.begin());
|
|
|
|
assert(*--literal.end() == ']'); literal.erase(--literal.end());
|
2015-05-04 18:56:16 +00:00
|
|
|
check_string(address, literal);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
raise << "don't know how to check memory for " << lhs << '\n';
|
|
|
|
}
|
|
|
|
|
2015-05-17 09:22:41 +00:00
|
|
|
void check_string(long long int address, const string& literal) {
|
2015-05-22 01:57:25 +00:00
|
|
|
trace(Primitive_recipe_depth, "run") << "checking string length at " << address;
|
2015-05-23 19:30:58 +00:00
|
|
|
if (Memory[address] != SIZE(literal)) {
|
|
|
|
if (Current_scenario && !Hide_warnings)
|
|
|
|
raise << "\nF - " << Current_scenario->name << ": expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << Memory[address] << '\n';
|
|
|
|
else
|
|
|
|
raise << "expected location " << address << " to contain length " << SIZE(literal) << " of string [" << literal << "] but saw " << Memory[address] << '\n';
|
|
|
|
if (!Hide_warnings) {
|
|
|
|
Passed = false;
|
|
|
|
++Num_failures;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2015-05-04 18:56:16 +00:00
|
|
|
++address; // now skip length
|
2015-05-17 09:22:41 +00:00
|
|
|
for (long long int i = 0; i < SIZE(literal); ++i) {
|
2015-05-22 01:57:25 +00:00
|
|
|
trace(Primitive_recipe_depth, "run") << "checking location " << address+i;
|
2015-05-23 19:30:58 +00:00
|
|
|
if (Memory[address+i] != literal.at(i)) {
|
2015-05-26 23:15:07 +00:00
|
|
|
if (Current_scenario && !Hide_warnings) {
|
|
|
|
// genuine test in a mu file
|
2015-05-23 19:30:58 +00:00
|
|
|
raise << "\nF - " << Current_scenario->name << ": expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << Memory[address+i] << '\n';
|
2015-05-26 23:15:07 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// just testing scenario support
|
2015-05-23 19:30:58 +00:00
|
|
|
raise << "expected location " << (address+i) << " to contain " << literal.at(i) << " but saw " << Memory[address+i] << '\n';
|
2015-05-26 23:15:07 +00:00
|
|
|
}
|
2015-05-23 19:30:58 +00:00
|
|
|
if (!Hide_warnings) {
|
|
|
|
Passed = false;
|
|
|
|
++Num_failures;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2015-05-04 18:56:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
:(scenario memory_check_multiple)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
|
|
|
memory-should-contain [
|
|
|
|
1 <- 0
|
|
|
|
1 <- 0
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+warn: duplicate expectation for location 1
|
|
|
|
|
|
|
|
:(scenario memory_check_string_length)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
2015-05-13 17:03:26 +00:00
|
|
|
1:number <- copy 3:literal
|
|
|
|
2:number <- copy 97:literal # 'a'
|
|
|
|
3:number <- copy 98:literal # 'b'
|
|
|
|
4:number <- copy 99:literal # 'c'
|
2015-05-04 18:56:16 +00:00
|
|
|
memory-should-contain [
|
|
|
|
1:string <- [ab]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+warn: expected location 1 to contain length 2 of string [ab] but saw 3
|
|
|
|
|
|
|
|
:(scenario memory_check_string)
|
|
|
|
recipe main [
|
2015-05-13 17:03:26 +00:00
|
|
|
1:number <- copy 3:literal
|
|
|
|
2:number <- copy 97:literal # 'a'
|
|
|
|
3:number <- copy 98:literal # 'b'
|
|
|
|
4:number <- copy 99:literal # 'c'
|
2015-05-04 18:56:16 +00:00
|
|
|
memory-should-contain [
|
|
|
|
1:string <- [abc]
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+run: checking string length at 1
|
|
|
|
+run: checking location 2
|
|
|
|
+run: checking location 3
|
|
|
|
+run: checking location 4
|
|
|
|
|
|
|
|
:(code)
|
|
|
|
//: 'trace-should-contain' is like the '+' lines in our scenarios so far
|
|
|
|
// Like runs of contiguous '+' lines, order is important. The trace checks
|
|
|
|
// that the lines are present *and* in the specified sequence. (There can be
|
|
|
|
// other lines in between.)
|
2015-05-10 18:38:18 +00:00
|
|
|
//
|
|
|
|
// Be careful not to mix setting Hide_warnings and checking the trace in .mu
|
|
|
|
// files. It'll work in C++ scenarios, but the test failure gets silently
|
|
|
|
// hidden in mu scenarios.
|
2015-05-04 18:56:16 +00:00
|
|
|
|
|
|
|
:(scenario trace_check_warns_on_failure)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
|
|
|
trace-should-contain [
|
|
|
|
a: b
|
|
|
|
a: d
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+warn: missing [b] in trace layer a
|
|
|
|
|
|
|
|
:(before "End Primitive Recipe Declarations")
|
|
|
|
TRACE_SHOULD_CONTAIN,
|
|
|
|
:(before "End Primitive Recipe Numbers")
|
|
|
|
Recipe_number["trace-should-contain"] = TRACE_SHOULD_CONTAIN;
|
|
|
|
:(before "End Primitive Recipe Implementations")
|
|
|
|
case TRACE_SHOULD_CONTAIN: {
|
2015-05-07 22:06:53 +00:00
|
|
|
check_trace(current_instruction().ingredients.at(0).name);
|
2015-05-04 18:56:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
:(code)
|
|
|
|
// simplified version of check_trace_contents() that emits warnings rather
|
|
|
|
// than just printing to stderr
|
|
|
|
bool check_trace(const string& expected) {
|
2015-05-10 18:38:18 +00:00
|
|
|
//? cerr << "AAA " << expected << '\n'; //? 1
|
2015-05-04 18:56:16 +00:00
|
|
|
Trace_stream->newline();
|
2015-05-22 01:10:17 +00:00
|
|
|
vector<trace_line> expected_lines = parse_trace(expected);
|
2015-05-17 09:22:41 +00:00
|
|
|
//? cerr << "BBB " << SIZE(expected_lines) << '\n'; //? 1
|
2015-05-04 18:56:16 +00:00
|
|
|
if (expected_lines.empty()) return true;
|
2015-05-17 09:22:41 +00:00
|
|
|
long long int curr_expected_line = 0;
|
2015-05-22 01:10:17 +00:00
|
|
|
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
|
|
|
|
if (expected_lines.at(curr_expected_line).label != p->label) continue;
|
|
|
|
if (expected_lines.at(curr_expected_line).contents != trim(p->contents)) continue;
|
2015-05-04 18:56:16 +00:00
|
|
|
// match
|
|
|
|
++curr_expected_line;
|
2015-05-17 09:22:41 +00:00
|
|
|
if (curr_expected_line == SIZE(expected_lines)) {
|
2015-05-10 18:38:18 +00:00
|
|
|
//? cerr << "ZZZ\n"; //? 1
|
|
|
|
return true;
|
|
|
|
}
|
2015-05-04 18:56:16 +00:00
|
|
|
}
|
|
|
|
|
2015-05-22 01:10:17 +00:00
|
|
|
raise << "missing [" << expected_lines.at(curr_expected_line).contents << "] "
|
|
|
|
<< "in trace layer " << expected_lines.at(curr_expected_line).label << '\n';
|
2015-05-04 18:56:16 +00:00
|
|
|
Passed = false;
|
|
|
|
return false;
|
2015-04-21 05:20:39 +00:00
|
|
|
}
|
|
|
|
|
2015-05-22 01:10:17 +00:00
|
|
|
vector<trace_line> parse_trace(const string& expected) {
|
2015-05-04 18:56:16 +00:00
|
|
|
vector<string> buf = split(expected, "\n");
|
2015-05-22 01:10:17 +00:00
|
|
|
vector<trace_line> result;
|
2015-05-17 09:22:41 +00:00
|
|
|
for (long long int i = 0; i < SIZE(buf); ++i) {
|
2015-05-07 22:06:53 +00:00
|
|
|
buf.at(i) = trim(buf.at(i));
|
|
|
|
if (buf.at(i).empty()) continue;
|
2015-05-17 09:22:41 +00:00
|
|
|
long long int delim = buf.at(i).find(": ");
|
2015-05-22 01:10:17 +00:00
|
|
|
result.push_back(trace_line(trim(buf.at(i).substr(0, delim)), trim(buf.at(i).substr(delim+2))));
|
2015-05-04 18:56:16 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
:(scenario trace_check_warns_on_failure_in_later_line)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
|
|
|
run [
|
|
|
|
trace [a], [b]
|
|
|
|
]
|
|
|
|
trace-should-contain [
|
|
|
|
a: b
|
|
|
|
a: d
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+warn: missing [d] in trace layer a
|
|
|
|
|
|
|
|
:(scenario trace_check_passes_silently)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
|
|
|
run [
|
|
|
|
trace [a], [b]
|
|
|
|
]
|
|
|
|
trace-should-contain [
|
|
|
|
a: b
|
|
|
|
]
|
|
|
|
]
|
|
|
|
-warn: missing [b] in trace layer a
|
|
|
|
|
|
|
|
//: 'trace-should-not-contain' is like the '-' lines in our scenarios so far
|
|
|
|
//: Each trace line is separately checked for absense. Order is *not*
|
|
|
|
//: important, so you can't say things like "B should not exist after A."
|
|
|
|
|
|
|
|
:(scenario trace_negative_check_warns_on_failure)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
|
|
|
run [
|
|
|
|
trace [a], [b]
|
|
|
|
]
|
|
|
|
trace-should-not-contain [
|
|
|
|
a: b
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+warn: unexpected [b] in trace layer a
|
|
|
|
|
|
|
|
:(before "End Primitive Recipe Declarations")
|
|
|
|
TRACE_SHOULD_NOT_CONTAIN,
|
|
|
|
:(before "End Primitive Recipe Numbers")
|
|
|
|
Recipe_number["trace-should-not-contain"] = TRACE_SHOULD_NOT_CONTAIN;
|
|
|
|
:(before "End Primitive Recipe Implementations")
|
|
|
|
case TRACE_SHOULD_NOT_CONTAIN: {
|
2015-05-07 22:06:53 +00:00
|
|
|
check_trace_missing(current_instruction().ingredients.at(0).name);
|
2015-05-04 18:56:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
:(code)
|
|
|
|
// simplified version of check_trace_contents() that emits warnings rather
|
|
|
|
// than just printing to stderr
|
|
|
|
bool check_trace_missing(const string& in) {
|
|
|
|
Trace_stream->newline();
|
2015-05-22 01:10:17 +00:00
|
|
|
vector<trace_line> lines = parse_trace(in);
|
2015-05-17 09:22:41 +00:00
|
|
|
for (long long int i = 0; i < SIZE(lines); ++i) {
|
2015-05-22 01:10:17 +00:00
|
|
|
if (trace_count(lines.at(i).label, lines.at(i).contents) != 0) {
|
|
|
|
raise << "unexpected [" << lines.at(i).contents << "] in trace layer " << lines.at(i).label << '\n';
|
2015-05-04 18:56:16 +00:00
|
|
|
Passed = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
:(scenario trace_negative_check_passes_silently)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
|
|
|
trace-should-not-contain [
|
|
|
|
a: b
|
|
|
|
]
|
|
|
|
]
|
|
|
|
-warn: unexpected [b] in trace layer a
|
|
|
|
|
|
|
|
:(scenario trace_negative_check_warns_on_any_unexpected_line)
|
|
|
|
% Hide_warnings = true;
|
|
|
|
recipe main [
|
|
|
|
run [
|
|
|
|
trace [a], [d]
|
|
|
|
]
|
|
|
|
trace-should-not-contain [
|
|
|
|
a: b
|
|
|
|
a: d
|
|
|
|
]
|
|
|
|
]
|
|
|
|
+warn: unexpected [d] in trace layer a
|
|
|
|
|
2015-04-22 20:29:02 +00:00
|
|
|
//:: Helpers
|
|
|
|
|
2015-05-02 22:52:22 +00:00
|
|
|
:(code)
|
2015-05-04 18:56:16 +00:00
|
|
|
// just for the scenarios running scenarios in C++ layers
|
|
|
|
void run_mu_scenario(const string& form) {
|
|
|
|
istringstream in(form);
|
|
|
|
in >> std::noskipws;
|
2015-05-27 18:27:50 +00:00
|
|
|
skip_whitespace_and_comments(in);
|
2015-05-04 18:56:16 +00:00
|
|
|
string _scenario = next_word(in);
|
2015-05-27 18:27:50 +00:00
|
|
|
//? cout << _scenario << '\n'; //? 2
|
2015-05-04 18:56:16 +00:00
|
|
|
assert(_scenario == "scenario");
|
|
|
|
scenario s = parse_scenario(in);
|
|
|
|
run_mu_scenario(s);
|
|
|
|
}
|
|
|
|
|
2015-04-08 07:47:04 +00:00
|
|
|
void slurp_until_matching_bracket(istream& in, ostream& out) {
|
|
|
|
int brace_depth = 1; // just scanned '['
|
2015-04-06 19:02:38 +00:00
|
|
|
char c;
|
|
|
|
while (in >> c) {
|
|
|
|
if (c == '[') ++brace_depth;
|
|
|
|
if (c == ']') --brace_depth;
|
2015-04-08 07:47:04 +00:00
|
|
|
if (brace_depth == 0) break; // drop final ']'
|
|
|
|
out << c;
|
|
|
|
}
|
|
|
|
}
|