:(scenarios run_mu_scenario)
:(scenario screen_in_scenario)
scenario screen-in-scenario [
assume-screen 5:literal/width, 3:literal/height
run [
screen:address <- print-character screen:address, 97:literal
]
screen-should-contain [
.a .
. .
. .
]
]
:(scenario screen_in_scenario_unicode)
scenario screen-in-scenario-unicode-color [
assume-screen 5:literal/width, 3:literal/height
run [
screen:address <- print-character screen:address, 955:literal/greek-small-lambda, 1:literal/red
screen:address <- print-character screen:address, 97:literal/a
]
screen-should-contain [
.λa .
. .
. .
]
]
:(scenario screen_in_scenario_color)
scenario screen-in-scenario-color [
assume-screen 5:literal/width, 3:literal/height
run [
screen:address <- print-character screen:address, 955:literal/greek-small-lambda, 1:literal/red
screen:address <- print-character screen:address, 97:literal/a, 7:literal/white
]
screen-should-contain [
.λa .
. .
. .
]
screen-should-contain-in-color 7:literal/white, [
. a .
. .
. .
]
screen-should-contain-in-color 1:literal/red, [
.λ .
. .
. .
]
]
:(scenario screen_in_scenario_error)
% Scenario_testing_scenario = true;
% Hide_warnings = true;
scenario screen-in-scenario-error [
assume-screen 5:literal/width, 3:literal/height
run [
screen:address <- print-character screen:address, 97:literal
]
screen-should-contain [
.b .
. .
. .
]
]
+warn: expected screen location (0, 0) to contain 98 ('b') instead of 97 ('a')
:(scenario screen_in_scenario_color_error)
% Scenario_testing_scenario = true;
% Hide_warnings = true;
scenario screen-in-scenario-color [
assume-screen 5:literal/width, 3:literal/height
run [
screen:address <- print-character screen:address, 97:literal/a, 1:literal/red
]
screen-should-contain-in-color 2:literal/green, [
.a .
. .
. .
]
]
+warn: expected screen location (0, 0) to be in color 2 instead of 1
:(before "End is_special_name Cases")
if (s == "screen") return true;
:(scenarios run)
:(scenario convert_names_does_not_warn_when_mixing_special_names_and_numeric_locations)
% Scenario_testing_scenario = true;
% Hide_warnings = true;
recipe main [
screen:number <- copy 1:number
]
-warn: mixing variable names and numeric addresses in main
$warn: 0
:(scenarios run_mu_scenario)
:(before "End Globals")
const long long int Max_variables_in_scenarios = Reserved_for_tests-100;
long long int Next_predefined_global_for_scenarios = Max_variables_in_scenarios;
:(before "End Setup")
assert(Next_predefined_global_for_scenarios < Reserved_for_tests);
:(after "transform_all()" following "case RUN:")
assert(Name[tmp_recipe.at(0)][""] < Max_variables_in_scenarios);
:(before "End Globals")
const long long int SCREEN = Next_predefined_global_for_scenarios++;
:(before "End Special Scenario Variable Names(r)")
Name[r]["screen"] = SCREEN;
:(before "End Rewrite Instruction(curr)")
if (curr.name == "assume-screen") {
curr.operation = Recipe_ordinal["new-fake-screen"];
curr.name = "new-fake-screen";
assert(curr.operation);
assert(curr.products.empty());
curr.products.push_back(reagent("screen:address"));
curr.products.at(0).set_value(SCREEN);
}
:(before "End Primitive Recipe Declarations")
SCREEN_SHOULD_CONTAIN,
:(before "End Primitive Recipe Numbers")
Recipe_ordinal["screen-should-contain"] = SCREEN_SHOULD_CONTAIN;
:(before "End Primitive Recipe Implementations")
case SCREEN_SHOULD_CONTAIN: {
if (!Passed) break;
check_screen(current_instruction().ingredients.at(0).name, -1);
break;
}
:(before "End Primitive Recipe Declarations")
SCREEN_SHOULD_CONTAIN_IN_COLOR,
:(before "End Primitive Recipe Numbers")
Recipe_ordinal["screen-should-contain-in-color"] = SCREEN_SHOULD_CONTAIN_IN_COLOR;
:(before "End Primitive Recipe Implementations")
case SCREEN_SHOULD_CONTAIN_IN_COLOR: {
if (!Passed) break;
assert(scalar(ingredients.at(0)));
check_screen(current_instruction().ingredients.at(1).name, ingredients.at(0).at(0));
break;
}
:(before "End Types")
struct raw_string_stream {
long long int index;
const long long int max;
const char* buf;
raw_string_stream(const string&);
uint32_t get();
uint32_t peek();
bool at_end() const;
void skip_whitespace_and_comments();
};
:(code)
void check_screen(const string& expected_contents, const int color) {
assert(!Current_routine->calls.front().default_space);
long long int screen_location = Memory[SCREEN];
int data_offset = find_element_name(Type_ordinal["screen"], "data");
assert(data_offset >= 0);
long long int screen_data_location = screen_location+data_offset;
long long int screen_data_start = Memory[screen_data_location];
int width_offset = find_element_name(Type_ordinal["screen"], "num-columns");
long long int screen_width = Memory[screen_location+width_offset];
int height_offset = find_element_name(Type_ordinal["screen"], "num-rows");
long long int screen_height = Memory[screen_location+height_offset];
raw_string_stream cursor(expected_contents);
long long int addr = screen_data_start+1;
for (long long int row = 0; row < screen_height; ++row) {
cursor.skip_whitespace_and_comments();
if (cursor.at_end()) break;
assert(cursor.get() == '.');
for (long long int column = 0; column < screen_width; ++column, addr+= 2) {
const int cell_color_offset = 1;
uint32_t curr = cursor.get();
if (Memory[addr] == 0 && isspace(curr)) continue;
if (curr == ' ' && color != -1 && color != Memory[addr+cell_color_offset]) {
continue;
}
if (Memory[addr] != 0 && Memory[addr] == curr) {
if (color == -1 || color == Memory[addr+cell_color_offset]) continue;
if (Current_scenario && !Scenario_testing_scenario) {
raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ", address " << addr << ", value " << Memory[addr] << ") to be in color " << color << " instead of " << Memory[addr+cell_color_offset] << "\n";
}
else {
raise << "expected screen location (" << row << ", " << column << ") to be in color " << color << " instead of " << Memory[addr+cell_color_offset] << '\n';
}
if (!Scenario_testing_scenario) {
Passed = false;
++Num_failures;
}
return;
}
char expected_pretty[10] = {0};
if (curr < 256 && !iscntrl(curr)) {
expected_pretty[0] = ' ', expected_pretty[1] = '(', expected_pretty[2] = '\'', expected_pretty[3] = static_cast<unsigned char>(curr), expected_pretty[4] = '\'', expected_pretty[5] = ')', expected_pretty[6] = '\0';
}
char actual_pretty[10] = {0};
if (Memory[addr] < 256 && !iscntrl(Memory[addr])) {
actual_pretty[0] = ' ', actual_pretty[1] = '(', actual_pretty[2] = '\'', actual_pretty[3] = static_cast<unsigned char>(Memory[addr]), actual_pretty[4] = '\'', actual_pretty[5] = ')', actual_pretty[6] = '\0';
}
if (Current_scenario && !Scenario_testing_scenario) {
raise << "\nF - " << Current_scenario->name << ": expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << " instead of " << Memory[addr] << actual_pretty << '\n';
dump_screen();
}
else {
raise << "expected screen location (" << row << ", " << column << ") to contain " << curr << expected_pretty << " instead of " << Memory[addr] << actual_pretty << '\n';
}
if (!Scenario_testing_scenario) {
Passed = false;
++Num_failures;
}
return;
}
assert(cursor.get() == '.');
}
cursor.skip_whitespace_and_comments();
assert(cursor.at_end());
}
raw_string_stream::raw_string_stream(const string& backing) :index(0), max(backing.size()), buf(backing.c_str()) {}
bool raw_string_stream::at_end() const {
if (index >= max) return true;
if (tb_utf8_char_length(buf[index]) > max-index) {
raise << "unicode string seems corrupted at index "<< index << " character " << static_cast<int>(buf[index]) << '\n';
return true;
}
return false;
}
uint32_t raw_string_stream::get() {
assert(index < max);
uint32_t result = 0;
int length = tb_utf8_char_to_unicode(&result, &buf[index]);
assert(length != TB_EOF);
index += length;
return result;
}
uint32_t raw_string_stream::peek() {
assert(index < max);
uint32_t result = 0;
int length = tb_utf8_char_to_unicode(&result, &buf[index]);
assert(length != TB_EOF);
return result;
}
void raw_string_stream::skip_whitespace_and_comments() {
while (!at_end()) {
if (isspace(peek())) get();
else if (peek() == '#') {
get();
while (peek() != '\n') get();
}
else break;
}
}
:(before "End Primitive Recipe Declarations")
_DUMP_SCREEN,
:(before "End Primitive Recipe Numbers")
Recipe_ordinal["$dump-screen"] = _DUMP_SCREEN;
:(before "End Primitive Recipe Implementations")
case _DUMP_SCREEN: {
dump_screen();
break;
}
:(code)
void dump_screen() {
assert(!Current_routine->calls.front().default_space);
long long int screen_location = Memory[SCREEN];
int width_offset = find_element_name(Type_ordinal["screen"], "num-columns");
long long int screen_width = Memory[screen_location+width_offset];
int height_offset = find_element_name(Type_ordinal["screen"], "num-rows");
long long int screen_height = Memory[screen_location+height_offset];
int data_offset = find_element_name(Type_ordinal["screen"], "data");
assert(data_offset >= 0);
long long int screen_data_location = screen_location+data_offset;
long long int screen_data_start = Memory[screen_data_location];
assert(Memory[screen_data_start] == screen_width*screen_height);
long long int curr = screen_data_start+1;
for (long long int row = 0; row < screen_height; ++row) {
cerr << '.';
for (long long int col = 0; col < screen_width; ++col) {
if (Memory[curr])
cerr << to_unicode(Memory[curr]);
else
cerr << ' ';
curr += 2;
}
cerr << ".\n";
}
}