To parse and carry out requests to do something.
§1. Reading the command line. Suppose the tester invoked Intest as
$ intest/Tangled/intest inweb -bless plain twinprimes
The following routine will be called to take care of the actual command:
-bless plain twinprimes
The tokens -bless, plain and twinprimes will be in the array argv, at indexes from_arg_n onwards. to_arg_n is the index after the last one. The token list can contain multiple actions, one after the other.
void Actions::read_do_instructions(intest_instructions *args, int from_arg_n, int to_arg_n, text_stream **argv) { Log the do instructions1.1; for (int index=from_arg_n; index<to_arg_n; index++) { text_stream *opt = argv[index]; int action = TEST_ACTION; which command is used text_stream *action_details = Str::new(); int ops_from = index+1, ops_to = index; operands run from ops_from to ops_to filename *redirect_output = NULL; optional file to redirect output to Scan the command and advance index to the end of it1.2; Translate the command into action item structures1.3; } }
§1.1. Log the do instructions1.1 =
if (Log::aspect_switched_on(INSTRUCTIONS_DA)) { LOG("doing:"); for (int i=from_arg_n; i<to_arg_n; i++) LOG(" %S", argv[i]); LOG("\n"); }
- This code is used in §1.
§1.2. The arity is the expected number of operands which will follow the command, or is -1 to mean "any positive number of operands".
Scan the command and advance index to the end of it1.2 =
int pos = index + 1, arity = -1; Parse the command name1.2.1; Scan for the operands1.2.2; Parse an optional redirection filename1.2.3; index = pos - 1;
- This code is used in §1.
§1.2.1. A command begins with one of the following tokens: or, if it doesn't, we pretend that it begins with test.
The following enumerates the "do commands", or as we'll call them in this section, "actions":
enum BBDIFF_ACTION from 1 enum BLESS_ACTION enum CATALOGUE_ACTION enum CENSUS_ACTION enum COMBINE_REPORTS_ACTION enum CONCORDANCE_ACTION enum CURSE_ACTION enum DEBUGGER_ACTION enum DIFF_ACTION enum SOURCE_ACTION enum FIND_ACTION enum OPEN_ACTION enum REBLESS_ACTION enum REPORT_ACTION enum SCRIPT_ACTION enum SHOW_ACTION enum SKEIN_ACTION enum TEST_ACTION enum LIST_ACTION define SCHEDULED_TEST_ACTION 100 must be more than all of the above
Parse the command name1.2.1 =
if (Str::eq(opt, I"-catalogue")) { action = CATALOGUE_ACTION; arity = 0; } else if (Str::eq(opt, I"-find")) { action = FIND_ACTION; arity = 1; } else if (Str::eq(opt, I"-test-skein")) { action = SKEIN_ACTION; arity = 1; } else if (Str::eq(opt, I"-script")) { action = SCRIPT_ACTION; } else if (Str::eq(opt, I"-source")) { action = SOURCE_ACTION; } else if (Str::eq(opt, I"-concordance")) { action = CONCORDANCE_ACTION; } else if (Str::eq(opt, I"-report")) { action = REPORT_ACTION; arity = 5; } else if (Str::eq(opt, I"-combine")) { action = COMBINE_REPORTS_ACTION; arity = 2; } else if (Str::eq(opt, I"-open")) { action = OPEN_ACTION; } else if (Str::eq(opt, I"-show")) { action = SHOW_ACTION; Str::clear(action_details); } else if (Str::prefix_eq(opt, I"-show-", 6)) { action = SHOW_ACTION; Str::substr(action_details, Str::at(opt, 6), Str::end(opt)); } else if (Str::eq(opt, I"-bbdiff")) { action = BBDIFF_ACTION; } else if (Str::eq(opt, I"-diff")) { action = DIFF_ACTION; } else if (Str::eq(opt, I"-test")) { action = TEST_ACTION; } else if (Str::eq(opt, I"-list")) { action = LIST_ACTION; } else if (Str::eq(opt, I"-debug")) { action = DEBUGGER_ACTION; } else if (Str::eq(opt, I"-bless")) { action = BLESS_ACTION; } else if (Str::eq(opt, I"-curse")) { action = CURSE_ACTION; } else if (Str::eq(opt, I"-rebless")) { action = REBLESS_ACTION; } else if (Str::get_at(opt, 0) == '-') Errors::fatal_with_text("no such action as: %S", opt); else { opt = I"-test"; action = TEST_ACTION; pos = index; }
- This code is used in §1.2.
§1.2.2. After the command, operands: e.g., in the token list -bless A B -catalogue, the command -bless has two operands A and B. We know that -catalogue is not an operand because it starts with a dash but is not a negative number. (Thus, -12 would be allowed as an operand, but -minustwelve would not.)
At this point pos is the index of the first operand. We must find the range ops_from to ops_to.
Scan for the operands1.2.2 =
ops_from = pos; while (pos < to_arg_n) { if ((Str::get_at(argv[pos], 0) == '-') && (!Characters::isdigit(Str::get_at(argv[pos], 1)))) break; pos++; } ops_to = pos-1; int count = ops_to - ops_from + 1; if (arity >= 0) { if (count != arity) { if (arity == 0) Errors::fatal_with_text("this action takes no case name(s): %S", opt); else { TEMPORARY_TEXT(M) WRITE_TO(M, "the action '%S' takes %d parameters", opt, arity); Errors::fatal_with_text("%S", M); DISCARD_TEXT(M) } } } else { if (count == 0) Errors::fatal_with_text("this action must be followed by case name(s): %S", opt); }
- This code is used in §1.2.
§1.2.3. After the operands, there can optionally be -to F, where F is a filename.
Parse an optional redirection filename1.2.3 =
if ((pos+1 < to_arg_n) && (Str::eq(argv[pos], I"-to"))) { redirect_output = Filenames::from_text(argv[pos+1]); pos += 2; }
- This code is used in §1.2.
§1.3. Translate the command into action item structures1.3 =
TEMPORARY_TEXT(assoc_text) int assoc_number = 0, assoc_number2 = 0; filename *assoc_file1 = NULL, *assoc_file2 = NULL; switch (action) { case REPORT_ACTION: Create action item for REPORT1.3.1; break; case COMBINE_REPORTS_ACTION: Create action item for COMBINE REPORTS1.3.2; break; case FIND_ACTION: Create action item for FIND1.3.3; break; case SKEIN_ACTION: Create action item for SKEIN1.3.4; break; case SHOW_ACTION: Create action item for SHOW1.3.5; break; default: Create more typical action items1.3.6; break; } DISCARD_TEXT(assoc_text)
- This code is used in §1.
§1.3.1. Create action item for REPORT1.3.1 =
if (Str::eq(argv[ops_from+1], I"i7")) assoc_number = I7_FAILED_OUTCOME; else if (Str::eq(argv[ops_from+1], I"i6")) assoc_number = I6_FAILED_OUTCOME; else if (Str::eq(argv[ops_from+1], I"cursed")) assoc_number = CURSED_OUTCOME; else if (Str::eq(argv[ops_from+1], I"wrong")) assoc_number = WRONG_TRANSCRIPT_OUTCOME; else if (Str::eq(argv[ops_from+1], I"right")) assoc_number = PERFECT_OUTCOME; else Errors::fatal_with_text( "expected 'i7', 'i6', 'wrong' or 'right' but found: %S", argv[ops_from+1]); assoc_file1 = Filenames::from_text(argv[ops_from+2]); assoc_text = argv[ops_from+3]; if (Str::get_at(assoc_text, 0) == 'n') Str::delete_first_character(assoc_text); text_stream *p = argv[ops_from+4]; if (Str::get_at(p, 0) == 't') assoc_number2 = Str::atoi(p, 1); Actions::create(action, redirect_output, argv[ops_from], args, assoc_number, assoc_number2, assoc_file1, assoc_file2, assoc_text);
- This code is used in §1.3.
§1.3.2. Create action item for COMBINE REPORTS1.3.2 =
assoc_file1 = Filenames::from_text(argv[ops_from]); text_stream *p = argv[ops_from+1]; if (Str::get_at(p, 0) == '-') assoc_number = Str::atoi(p, 1); if (assoc_number <= 0) Errors::fatal_with_text( "expected dash then positive integer, e.g., '-5', but found: '%S'", p); Actions::create(action, redirect_output, NULL, args, assoc_number, assoc_number2, assoc_file1, assoc_file2, assoc_text);
- This code is used in §1.3.
§1.3.3. Create action item for FIND1.3.3 =
assoc_text = argv[ops_from]; Actions::create(action, redirect_output, NULL, args, assoc_number, assoc_number2, assoc_file1, assoc_file2, assoc_text);
- This code is used in §1.3.
§1.3.4. Create action item for SKEIN1.3.4 =
assoc_file1 = Filenames::from_text(argv[ops_from]); assoc_text = argv[ops_from+1]; Actions::create(action, redirect_output, NULL, args, assoc_number, assoc_number2, assoc_file1, assoc_file2, assoc_text);
- This code is used in §1.3.
§1.3.5. Create action item for SHOW1.3.5 =
assoc_file1 = Filenames::from_text(argv[ops_from]); assoc_text = Str::duplicate(action_details); if (ops_to >= ops_from) for (int j = ops_from; j <= ops_to; j++) Actions::create(action, redirect_output, argv[j], args, assoc_number, assoc_number2, assoc_file1, assoc_file2, assoc_text);
- This code is used in §1.3.
§1.3.6. Create more typical action items1.3.6 =
if (ops_to >= ops_from) for (int j = ops_from; j <= ops_to; j++) Actions::create(action, redirect_output, argv[j], args, assoc_number, assoc_number2, assoc_file1, assoc_file2, assoc_text); else Actions::create(action, redirect_output, NULL, args, assoc_number, assoc_number2, assoc_file1, assoc_file2, assoc_text);
- This code is used in §1.3.
§2. So, then, a command such as -test alpha beta gamma will cause three instances of the "action item" structure to be created, of type TEST_ACTION on alpha, beta and gamma respectively.
typedef struct action_item { int action_type; one of the _ACTION cases above int test_form; struct case_specifier operand; struct filename *redirection_filename; int assoc_number; int assoc_number2; struct filename *assoc_file1; struct filename *assoc_file2; struct text_stream *assoc_text; CLASS_DEFINITION } action_item;
- The structure action_item is accessed in 3/ts, 3/tr and here.
§3. As each action item is created, it is added to the "to-do list" for this run of Intest. (There is just one global to-do list.)
void Actions::create(int action, filename *redirect_output, text_stream *op, intest_instructions *args, int assoc_number, int assoc_number2, filename *assoc_file1, filename *assoc_file2, text_stream *text) { action_item *ai = CREATE(action_item); ai->action_type = action; ai->redirection_filename = redirect_output; ai->assoc_number = assoc_number; ai->assoc_number2 = assoc_number2; ai->assoc_file1 = assoc_file1; ai->assoc_file2 = assoc_file2; ai->assoc_text = Str::duplicate(text); ai->operand = Actions::parse_specifier(op, args); ADD_TO_LINKED_LIST(ai, action_item, args->to_do_list); }
§4. A "case specifier" says what case(s) an action should apply to, and this can involve a wildcard such as all:
typedef struct case_specifier { struct test_case *specific_case; a specific test to apply to... int wild_card; ...or a wildcard struct text_stream *regexp_wild_card; } case_specifier;
- The structure case_specifier is private to this section.
case_specifier Actions::parse_specifier(text_stream *token, intest_instructions *args) { case_specifier cs; cs.wild_card = Actions::identify_wildcard(token); cs.specific_case = NULL; cs.regexp_wild_card = NULL; if ((token) && (cs.wild_card == TAMECARD)) { cs.specific_case = RecipeFiles::find_case(args, token); if (cs.specific_case == NULL) Errors::fatal_with_text("no such test case as %S", token); } if ((token) && (cs.wild_card == REGEXP_WILDCARD)) cs.regexp_wild_card = Str::duplicate(token); if ((token) && (cs.wild_card == GROUP_WILDCARD)) cs.regexp_wild_card = Str::duplicate(token); return cs; }
define COUNT_WILDCARD_BASE 1001 1001 is ^1, 1002 is ^2, ... define EXTENSION_WILDCARD_BASE 101 101 is A, 102 is B, ... define GROUP_WILDCARD 2 define REGEXP_WILDCARD 1 define TAMECARD 0 define ALL_WILDCARD -1 define EXAMPLES_WILDCARD -2 define CASES_WILDCARD -3 define PROBLEMS_WILDCARD -4 define EXTENSIONS_WILDCARD -5
int Actions::identify_wildcard(text_stream *token) { if (token == NULL) return TAMECARD; LOOP_THROUGH_TEXT(pos, token) if (Str::get(pos) == '%') return REGEXP_WILDCARD; inchar32_t c = Str::get_first_char(token); if (c == ':') return GROUP_WILDCARD; if (Str::len(token) == 1) { if ((c >= 'A') && (c <= 'Z')) return (int) c - 'A' + EXTENSION_WILDCARD_BASE; if ((c >= 'a') && (c <= 'z')) return (int) c - 'a' + EXTENSION_WILDCARD_BASE; } if (Str::eq(token, I"all")) return ALL_WILDCARD; if (Str::eq(token, I"examples")) return EXAMPLES_WILDCARD; if (Str::eq(token, I"cases")) return CASES_WILDCARD; if (Str::eq(token, I"problems")) return PROBLEMS_WILDCARD; if (Str::eq(token, I"extensions")) return EXTENSIONS_WILDCARD; if (Str::get_first_char(token) == '^') { int n = Str::atoi(token, 1); if (n > 0) return n - 1 + COUNT_WILDCARD_BASE; } return TAMECARD; } int Actions::matches_wildcard(test_case *tc, int w, dictionary *exemptions) { if (Dictionaries::find(exemptions, tc->test_case_name)) return FALSE; if (w == ALL_WILDCARD) return TRUE; if (w == Actions::which_wildcard(tc)) return TRUE; return FALSE; } int Actions::which_wildcard(test_case *tc) { if (tc->format_reference == EXAMPLE_FORMAT) return EXAMPLES_WILDCARD; if (tc->test_type == PROBLEM_SPT) return PROBLEMS_WILDCARD; if (tc->format_reference == EXTENSION_FORMAT) return EXTENSIONS_WILDCARD; return CASES_WILDCARD; } char *Actions::name_of_wildcard(int w) { switch (w) { case EXAMPLES_WILDCARD: return "examples"; break; case EXTENSIONS_WILDCARD: return "extensions"; break; case PROBLEMS_WILDCARD: return "problems"; break; case CASES_WILDCARD: return "cases"; break; } return "?"; }
§7. Performance. At this point, parsing is long over. We have to perform the actions in the to-do list:
void Actions::perform(OUTPUT_STREAM, intest_instructions *args) { Hasher::read_hashes(args); Scheduler::start(args->threads_available); int count = 1; linked_list *report_on = NEW_LINKED_LIST(test_case); action_item *ai; LOOP_OVER_LINKED_LIST(ai, action_item, args->to_do_list) Perform this action item7.1; Scheduler::test(OUT); Hasher::write_hashes(); if (args->results_stream) { action_item *ai; LOOP_OVER_LINKED_LIST(ai, action_item, args->to_do_list) Report back on this action item7.2; } }
§7.1.1. Close friends get to call him tc7.1.1 =
if (args->results_stream) { ADD_TO_LINKED_LIST(tc, test_case, report_on); tc->HTML_report = Str::new(); }
§7.1. Perform this action item7.1 =
if (ai->operand.wild_card >= COUNT_WILDCARD_BASE) Perform this counted case7.1.2 else if (ai->operand.wild_card >= EXTENSION_WILDCARD_BASE) Perform this lettered case7.1.3 else if (ai->operand.wild_card == TAMECARD) { test_case *tc = ai->operand.specific_case; Close friends get to call him tc7.1.1; Actions::perform_inner(OUT, args, ai, tc, count++); } else if (ai->operand.wild_card == REGEXP_WILDCARD) Perform this regular expressed case7.1.4 else if (ai->operand.wild_card == GROUP_WILDCARD) Perform this grouped case7.1.5 else Perform this matched case7.1.6;
- This code is used in §7.
§7.2. Report back on this action item7.2 =
text_stream *OUT = args->results_stream; HTML_OPEN_WITH("div", "class=\"markdowncontent\""); HTML_OPEN("p"); switch(ai->action_type) { case BLESS_ACTION: WRITE("Blessing the current output as correct for the following:"); break; case REBLESS_ACTION: WRITE("Replacing the previously blessed output with the current behaviour of:"); break; case CURSE_ACTION: WRITE("Throwing away the previously blessed output from:"); break; case TEST_ACTION: WRITE("Testing to see if the following compile and produce the correct, or 'blessed', output:"); break; } HTML_CLOSE("p"); HTML_OPEN("table"); test_case *tc; LOOP_OVER_LINKED_LIST(tc, test_case, report_on) { int I = Streams::get_indentation(OUT); Streams::set_indentation(OUT, 0); WRITE("%S", tc->HTML_report); Streams::set_indentation(OUT, I); } HTML_CLOSE("table"); HTML_CLOSE("div");
- This code is used in §7.
§7.1.2. Perform this counted case7.1.2 =
int find_count = 0; test_source *spi; test_case *tc; LOOP_OVER_LINKED_LIST(spi, test_source, args->search_path) LOOP_OVER_LINKED_LIST(tc, test_case, spi->contents) if (find_count++ == ai->operand.wild_card - COUNT_WILDCARD_BASE) { Close friends get to call him tc7.1.1; Actions::perform_inner(OUT, args, ai, tc, count++); goto ExitCountSearch; } TEMPORARY_TEXT(M) if (find_count == 0) WRITE_TO(M, "there were no cases here at all"); else WRITE_TO(M, "cases only run from ^1 to ^%d", find_count); Errors::fatal_with_text("%S", M); DISCARD_TEXT(M) ExitCountSearch: ;
- This code is used in §7.1.
§7.1.3. Perform this lettered case7.1.3 =
test_source *spi; test_case *tc; LOOP_OVER_LINKED_LIST(spi, test_source, args->search_path) LOOP_OVER_LINKED_LIST(tc, test_case, spi->contents) if ((tc->format_reference == EXTENSION_FORMAT) && (tc->letter_reference == ai->operand.wild_card - EXTENSION_WILDCARD_BASE + 1)) { Close friends get to call him tc7.1.1; Actions::perform_inner(OUT, args, ai, tc, count++); goto ExitLetterSearch; } Errors::fatal("unable to find any such extension example"); ExitLetterSearch: ;
- This code is used in §7.1.
§7.1.4. Perform this regular expressed case7.1.4 =
linked_list *matches = NEW_LINKED_LIST(test_case); RecipeFiles::find_cases_matching(matches, args->search_path, NULL, ai->operand.regexp_wild_card, FALSE); test_case *tc; LOOP_OVER_LINKED_LIST(tc, test_case, matches) { Close friends get to call him tc7.1.1; Actions::perform_inner(OUT, args, ai, tc, count++); }
- This code is used in §7.1.
§7.1.5. Perform this grouped case7.1.5 =
int scheduled = TRUE; TEMPORARY_TEXT(leafname) WRITE_TO(leafname, "%S.testgroup", ai->operand.regexp_wild_card); Str::delete_first_character(leafname); if (Str::get_first_char(leafname) == ':') { Str::delete_first_character(leafname); scheduled = FALSE; } filename *F = Filenames::in(args->groups_folder, leafname); linked_list *names_in_group = NEW_LINKED_LIST(text_stream); TextFiles::read(F, FALSE, "can't open test group file", TRUE, &Actions::read_group, NULL, names_in_group); DISCARD_TEXT(leafname) linked_list *matches = NEW_LINKED_LIST(test_case); text_stream *name; LOOP_OVER_LINKED_LIST(name, text_stream, names_in_group) { match_results mr = Regexp::create_mr(); text_stream *key = NULL, *match = name; int exactly = TRUE; if (Regexp::match(&mr, name, U"$*(%C+) is (%c*)")) { key = mr.exp[0]; match = mr.exp[1]; } else if (Regexp::match(&mr, name, U"$*(%C+) includes (%c*)")) { key = mr.exp[0]; match = mr.exp[1]; exactly = FALSE; } RecipeFiles::find_cases_matching(matches, args->search_path, key, match, exactly); } test_case *tc; LOOP_OVER_LINKED_LIST(tc, test_case, matches) { ai->test_form = ai->action_type; if ((ai->action_type != LIST_ACTION) && (scheduled)) ai->action_type += SCHEDULED_TEST_ACTION; Close friends get to call him tc7.1.1; Actions::perform_inner(OUT, args, ai, tc, count++); if ((ai->action_type != LIST_ACTION) && (scheduled)) ai->action_type -= SCHEDULED_TEST_ACTION; }
- This code is used in §7.1.
§7.1.6. Perform this matched case7.1.6 =
test_source *spi; test_case *tc; LOOP_OVER_LINKED_LIST(spi, test_source, args->search_path) LOOP_OVER_LINKED_LIST(tc, test_case, spi->contents) if (Actions::matches_wildcard(tc, ai->operand.wild_card, args->singular_case_names)) { ai->test_form = ai->action_type; ai->action_type += SCHEDULED_TEST_ACTION; Close friends get to call him tc7.1.1; Actions::perform_inner(OUT, args, ai, tc, count++); ai->action_type -= SCHEDULED_TEST_ACTION; }
- This code is used in §7.1.
void Actions::read_group(text_stream *text, text_file_position *tfp, void *vm) { linked_list *matches = (linked_list *) vm; Str::trim_white_space(text); inchar32_t c = Str::get_first_char(text); if ((c == 0) || (c == '#') || (c == '!')) return; ADD_TO_LINKED_LIST(Str::duplicate(text), text_stream, matches); }
§9. And now we can forget everything about wild cards: we know for definite which test case to work on.
void Actions::perform_inner(OUTPUT_STREAM, intest_instructions *args, action_item *ai, test_case *itc, int count) { text_stream *TO = OUT; text_stream TO_struct; int file_opened = FALSE; if ((ai->action_type < SCHEDULED_TEST_ACTION) && (ai->redirection_filename)) { filename *F = ai->redirection_filename; if (itc) Expand NAME and NUMBER in the redirection filename9.1; TO = &TO_struct; if (STREAM_OPEN_TO_FILE(TO, F, UTF8_ENC) == FALSE) Errors::fatal_with_file("unable to write file", F); file_opened = TRUE; } Finally do something, or at leask ask somebody else to9.2; if (file_opened) STREAM_CLOSE(TO); }
§9.1. If the leafname of the redirection file is something_[NUMBER] then we substitute in the case number for [NUMBER], and similarly for [NAME].
Expand NAME and NUMBER in the redirection filename9.1 =
TEMPORARY_TEXT(leaf) leaf = Str::duplicate(Filenames::get_leafname(F)); match_results mr = Regexp::create_mr(); while (Regexp::match(&mr, leaf, U"(%c*?)%[NAME%](%c*)")) { Str::clear(leaf); WRITE_TO(leaf, "%S%s%S", mr.exp[0], itc->test_case_name, mr.exp[1]); } while (Regexp::match(&mr, leaf, U"(%c*?)%[NUMBER%](%c*)")) { Str::clear(leaf); WRITE_TO(leaf, "%S%d%S", mr.exp[0], count, mr.exp[1]); } F = Filenames::in(Filenames::up(F), leaf); DISCARD_TEXT(leaf) Regexp::dispose_of(&mr);
- This code is used in §9.
§9.2. Finally do something, or at leask ask somebody else to9.2 =
switch (ai->action_type) { case CATALOGUE_ACTION: RecipeFiles::perform_catalogue(TO, args->search_path, NULL); break; case FIND_ACTION: RecipeFiles::perform_catalogue(TO, args->search_path, ai->assoc_text); break; case LIST_ACTION: if (itc) { if (Str::eq(itc->test_case_name, itc->test_case_title)) { WRITE_TO(TO, "%S\n", itc->test_case_name); } else { WRITE_TO(TO, "%S = '%S'\n", itc->test_case_name, itc->test_case_title); } } break; case SOURCE_ACTION: case CONCORDANCE_ACTION: if (itc) Extractor::run(NULL, TO, itc, itc->test_location, itc->format_reference, itc->letter_reference, ai->action_type, NULL); break; case SCRIPT_ACTION: if (itc) { if (TextFiles::exists(itc->commands_location)) Extractor::run(NULL, TO, itc, itc->commands_location, PLAIN_FORMAT, 0, SOURCE_ACTION, NULL); else Extractor::run(NULL, TO, itc, itc->test_location, itc->format_reference, itc->letter_reference, ai->action_type, NULL); } break; case OPEN_ACTION: if (itc) Shell::apply("open", itc->test_location); break; case BBDIFF_ACTION: case DIFF_ACTION: case DEBUGGER_ACTION: case BLESS_ACTION: case CURSE_ACTION: case SHOW_ACTION: case REBLESS_ACTION: case TEST_ACTION: if (itc) Tester::test(TO, itc, count, -1, ai->action_type, ai->assoc_text); break; case REPORT_ACTION: Reporter::report_single(TO, itc, ai); break; case COMBINE_REPORTS_ACTION: Reporter::combine(TO, ai->assoc_number, ai->assoc_file1); break; case SKEIN_ACTION: Skeins::test_i7_skein(TO, ai->assoc_file1, ai->assoc_text); break; default: if (ai->action_type >= SCHEDULED_TEST_ACTION) { ai->action_type -= SCHEDULED_TEST_ACTION; Scheduler::schedule(itc, ai->redirection_filename, ai->test_form); ai->action_type += SCHEDULED_TEST_ACTION; break; } WRITE_TO(STDERR, "Action is %d\n", ai->action_type); internal_error("unimplemented action"); }
- This code is used in §9.