Special sentences for requesting unit tests or providing test scripts.

§1. The verb "Test ... with ..." has two different uses, one public, letting users set up test dialogues called "scenarios", and the other undocumented, for performing unit tests of parts of the compiler. These exist because the test suite for Inform is made up of end-to-end tests, and sometimes these make it hard to see what any given component has done.

All sentences in the shape "test ... with ..." are accepted:

test_scenario *ts_being_created = NULL;

int TestRequests::test_with_SMF(int task, parse_node *V, wording *NPs) {
    wording OW = (NPs)?(NPs[1]):EMPTY_WORDING;
    wording O2W = (NPs)?(NPs[2]):EMPTY_WORDING;
    switch (task) {  "Test me with..."
        case ACCEPT_SMFT:
            <np-unparsed>(O2W);
            V->next = <<rp>>;
            <np-unparsed>(OW);
            V->next->next = <<rp>>;
            return TRUE;
        case PASS_2_SMFT:
            Create the new test request1.1;
            break;
        case ALLOW_IN_OPTIONS_FILE_SMFT:
            return TRUE;
    }
    return FALSE;
}

§2. The subject phrase is often just "me", as in, "Test me with...", but in fact it can generate a range of possibilities.

enum NO_INTT from 0  used for error recovery
enum HEADLINE_INTT
enum SCENARIO_INTT
enum TEST_INTT
<test-sentence-subject> ::=
    <internal-test-case-name> ( internal ) |  ==> { pass 1 }
    headline ( internal ) |                   ==> { HEADLINE_INTT, - }
    ### ( internal ) |                        ==> Issue PM_UnknownInternalTest problem2.1
    <quoted-text> |                           ==> Issue PM_TestQuoted problem2.2
    ### |                                     ==> { SCENARIO_INTT, - }
    ...                                       ==> Issue PM_TestMultiWord problem2.3

<internal-test-case-name> internal {
    internal_test *it = InternalTests::by_name(W);
    if (it) {
        ==> { TEST_INTT, it };
        return TRUE;
    }
    return FALSE;
}

§2.1. Issue PM_UnknownInternalTest problem2.1 =

    StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_UnknownInternalTest),
        "that's an internal test case which I don't know",
        "so I am taking no action.");
    ==> { NO_INTT, - };

§2.2. Issue PM_TestQuoted problem2.2 =

    StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_TestQuoted),
        "test scenarios must have unquoted names",
        "so 'test garden with ...' is allowed but not 'test \"garden\" with...'");
    ==> { NO_INTT, - };

§2.3. Issue PM_TestMultiWord problem2.3 =

    StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_TestMultiWord),
        "test scenarios must have single-word names",
        "so 'test garden with ...' is allowed but not 'test garden gate with...'");
    ==> { NO_INTT, - };

§1.1. Create the new test request1.1 =

    wording SP = Node::get_text(V->next);
    wording OP = Node::get_text(V->next->next);
    <test-sentence-subject>(SP);  always passes
    switch (<<r>>) {
        case NO_INTT: break;  recover from errors
        case SCENARIO_INTT: Create a test scenario1.1.1; break;
        case HEADLINE_INTT: InternalTests::new(NULL, OP); break;
        default: InternalTests::new(<<rp>>, OP); break;
    }

§1.1.1. Create a test scenario1.1.1 =

    wording XW = GET_RW(<test-sentence-subject>, 1);
    ts_being_created = TestCommand::new_scenario(XW);
    <test-sentence-object>(OP);

§3. The object NP for a scenario is usually just a quoted script, but it can be more elaborate:

Test me with "x egg" in Timbuktu holding the egg.

<test-sentence-object> ::=
    <quoted-text> |                                ==> { TRUE, - }; Add script3.1
    <quoted-text> <test-case-circumstance-list> |  ==> { TRUE, - }; Add script3.1
    ...                                            ==> Issue PM_TestBadRequirements problem3.5

<test-case-circumstance-list> ::=
    ... |                                                     ==> { lookahead }
    <test-case-circumstance-list> <test-case-circumstance> |  ==> { 0, - }
    <test-case-circumstance> |                                ==> { 0, - }
    ...                                                       ==> Issue PM_TestBadRequirements problem3.5

<test-case-circumstance> ::=
    in <instance-of-object> |             ==> Add in-test requirement3.2
    holding/and/, <instance-of-object> |  ==> Add holding requirement3.3
    with ...                              ==> Issue PM_TestDoubleWith problem3.4

§3.1. Add script3.1 =

    Word::dequote(Wordings::first_wn(W));
    inchar32_t *p = Lexer::word_text(Wordings::first_wn(W));
    TestCommand::add_script_to_scenario(ts_being_created, p);

§3.2. Add in-test requirement3.2 =

    TestCommand::add_location_to_scenario(ts_being_created, RP[1]);

§3.3. Add holding requirement3.3 =

    TestCommand::add_possession_to_scenario(ts_being_created, RP[1]);

§3.4. Issue PM_TestDoubleWith problem3.4 =

    StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_TestDoubleWith),
        "the second 'with' should be 'holding'",
        "as in 'test frogs with \"get frogs\" holding net' rather than "
        "'test frogs with \"get frogs\" with net'.");

§3.5. Issue PM_TestBadRequirements problem3.5 =

    StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_TestBadRequirements),
        "I didn't recognise the requirements for this test scenario",
        "which should be 'test ... with ... in ...' or '... "
        "holding ...'");