about summary refs log blame commit diff stats
path: root/cpp/050scenario.cc
blob: e67bb1720773c86ae3f3dd5b4f14f3f5cbdbd35e (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                                             
 


                     
                    

                                    
                        




                           
                            
 
                     

                                          


                                               

       

















                                                                                                                                                

     
                        
             
                          

 
                             

                             
                                             









                                 
                                                       
                       










                                                   









                                          













                                                               



                                                 



                                          

                                    
                                                                             








                                                                   


                                                                




                                                                       
                                                                                  

                                   
                                                                                          
   
                                                                                                     




                                                 





                                






                                                             

                                                                                                                                       






                                                                            
























                                                                                      

            

                                                              

                   

                                



                                                   
 
       























                                                                                                          







                                              
//: Support a scenario [ ... ] form at the top level so we can start creating
//: scenarios in mu files just like we do in C++.

:(before "End Types")
struct scenario {
  string name;
  string dump_layer;
  string to_run;
  map<int, int> memory_expectations;
  // End scenario Fields
};

:(before "End Globals")
vector<scenario> 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<int, int>::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 &ltrim(string &s) {
  s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(isspace))));
  return s;
}

string &rtrim(string &s) {
  s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(isspace))).base(), s.end());
  return s;
}

:(before "End Includes")
#include <sys/stat.h>
:(code)
bool file_exists(const string& filename) {
  struct stat buffer;
  return stat(filename.c_str(), &buffer) == 0;
}