To see which problem messages have test cases and which are linked to the documentation.


§1. Observation. Problem messages are identified by their code-names, e.g., PM_MisplacedFrom; those names should be unique, but any number of problems can instead be marked with one of three special names.

Problems can be mentioned in the code, in the documentation, or in the set of Inform test cases.

    define CASE_EXISTS_PCON    0x00000001 mentioned in test cases
    define DOC_MENTIONS_PCON   0x00000002 mentioned in documentation
    define CODE_MENTIONS_PCON  0x00000004 mentioned in source code
    define IMPOSSIBLE_PCON     0x00000008 this is BelievedImpossible
    define UNTESTABLE_PCON     0x00000010 this is Untestable
    define NAMELESS_PCON       0x00000020 this is ...
    typedef struct known_problem {
        struct text_stream *name;
        int contexts_observed; bitmap of the above bits
        int contexts_observed_multiple_times; bitmap of the above bits
        MEMORY_MANAGEMENT
    } known_problem;

The structure known_problem is private to this section.

§2. When a problem is observed, we create a dictionary entry for it, if necessary, and augment its bitmap of known contexts:

    dictionary *problems_dictionary = NULL;

    void Coverage::observe_problem(text_stream *name, int context) {
        if (problems_dictionary == NULL)
            problems_dictionary = Dictionaries::new(1000, FALSE);
        known_problem *KP = NULL;
        if (Dictionaries::find(problems_dictionary, name)) {
            KP = (known_problem *) Dictionaries::read_value(problems_dictionary, name);
        } else {
            KP = CREATE(known_problem);
            Dictionaries::create(problems_dictionary, name);
            Dictionaries::write_value(problems_dictionary, name, (void *) KP);
            KP->name = Str::duplicate(name);
            KP->contexts_observed = 0;
            KP->contexts_observed_multiple_times = 0;
        }
        if (KP->contexts_observed & context)
            KP->contexts_observed_multiple_times |= context;
        KP->contexts_observed |= context;
    }

The function Coverage::observe_problem is used in §3, §4, §6.

§3. Problems which have test cases. Here we ask Intest to produce a roster of all known test cases, then parse this back to look for cases whose names have the PM_... format. Those are the problem message test cases, so we observe them.

    void Coverage::which_problems_have_test_cases(void) {
        filename *CAT = Filenames::in_folder(path_to_inpolicy_workspace, I"cases.txt");
        TEMPORARY_TEXT(COMMAND);
        WRITE_TO(COMMAND, "../intest/Tangled/intest inform7 -catalogue ");
        Shell::redirect(COMMAND, CAT);
        if (Shell::run(COMMAND)) Errors::fatal("can't run intest to harvest cases");
        DISCARD_TEXT(COMMAND);
        TextFiles::read(CAT, FALSE, "unable to read roster of test cases", TRUE,
            &Coverage::test_case_harvester, NULL, NULL);
    }

    void Coverage::test_case_harvester(text_stream *text, text_file_position *tfp, void *state) {
        match_results mr = Regexp::create_mr();
        if (Regexp::match(&mr, text, L"(PM_%C+)%c*"))
            Coverage::observe_problem(mr.exp[0], CASE_EXISTS_PCON);
        Regexp::dispose_of(&mr);
    }

The function Coverage::which_problems_have_test_cases is used in §7.1.

The function Coverage::test_case_harvester appears nowhere else.

§4. Problems mentioned in documentation. Here we look through the "Writing with Inform" source text for cross-references to problem messages:

    void Coverage::which_problems_are_referenced(void) {
        pathname *D = Pathnames::from_text(I"resources");
        D = Pathnames::subfolder(D, I"Documentation");
        filename *WWI = Filenames::in_folder(D, I"Writing with Inform.txt");
        TextFiles::read(WWI, FALSE, "unable to read 'Writing with Inform' source text", TRUE,
            &Coverage::xref_harvester, NULL, NULL);
    }

    void Coverage::xref_harvester(text_stream *text, text_file_position *tfp, void *state) {
        match_results mr = Regexp::create_mr();
        while (Regexp::match(&mr, text, L"(%c*)%{(PM_%C+?)%}(%c*)")) {
            Coverage::observe_problem(mr.exp[1], DOC_MENTIONS_PCON);
            Str::clear(text);
            WRITE_TO(text, "%S%S", mr.exp[0], mr.exp[2]);
        }
        Regexp::dispose_of(&mr);
    }

The function Coverage::which_problems_are_referenced is used in §7.1.

The function Coverage::xref_harvester appears nowhere else.

§5. Problems generated in the I7 source. Which is to say, actually existing problem messages.

    void Coverage::which_problems_exist(void) {
        pathname *tools = Pathnames::up(path_to_inpolicy);
        pathname *path_to_inweb = Pathnames::subfolder(Pathnames::up(tools), I"inweb");
        pathname *path_to_inform7 = Pathnames::subfolder(tools, I"inform7");
        web_md *Wm = WebMetadata::get(path_to_inform7, NULL, V2_SYNTAX,
                        NULL, FALSE, TRUE, path_to_inweb);
        chapter_md *Cm;
        LOOP_OVER_LINKED_LIST(Cm, chapter_md, Wm->chapters_md) {
            section_md *Sm;
            LOOP_OVER_LINKED_LIST(Sm, section_md, Cm->sections_md) {
                filename *SF = Sm->source_file_for_section;
                TextFiles::read(SF, FALSE, "unable to read section page from 'inform7'",
                    TRUE, &Coverage::existence_harvester, NULL, (void *) SF);
            }
        }
    }

The function Coverage::which_problems_exist is used in §7.1.

§6. So now we have to read a section, looking for the existence of problem messages:

    void Coverage::existence_harvester(text_stream *text, text_file_position *tfp, void *state) {
        filename *SF = (filename *) state;
        match_results mr = Regexp::create_mr();
        while (Regexp::match(&mr, text, L"(%c*?)_p_%((%c+?)%)(%c*)")) {
            Str::clear(text);
            WRITE_TO(text, "%S%S", mr.exp[0], mr.exp[2]);
            TEMPORARY_TEXT(name);
            Str::copy(name, mr.exp[1]);
            if (Str::eq(name, I"sigil")) break;
            int context = CODE_MENTIONS_PCON;
            if (Str::eq(name, I"BelievedImpossible")) {
                context = IMPOSSIBLE_PCON;
                WRITE_TO(name, "_%f_line%d", SF, tfp->line_count);
            } else if (Str::eq(name, I"Untestable")) {
                context = UNTESTABLE_PCON;
                WRITE_TO(name, "_%f_line%d", SF, tfp->line_count);
            } else if (Str::eq(name, I"...")) {
                context = NAMELESS_PCON;
                WRITE_TO(name, "_%f_line%d", SF, tfp->line_count);
            }
            Coverage::observe_problem(name, context);
            DISCARD_TEXT(name);
        }
        Regexp::dispose_of(&mr);
    }

The function Coverage::existence_harvester is used in §5.

§7. Checking. So the actual policy-enforcement routine is here:

    int observations_made = FALSE;
    int Coverage::check(OUTPUT_STREAM) {
        if (observations_made == FALSE) {
            <Perform the observations 7.1>;
            observations_made = TRUE;
        }

        int all_is_well = TRUE;
        <Report and decide how grave the situation is 7.2>;
        if (all_is_well) WRITE("All is well.\n");
        else WRITE("This needs attention.\n");
        WRITE("\n");
        return all_is_well;
    }

The function Coverage::check is used in 1/mn (§2).

§7.1. <Perform the observations 7.1> =

        Coverage::which_problems_have_test_cases();
        Coverage::which_problems_are_referenced();
        Coverage::which_problems_exist();

This code is used in §7.

§7.2. Okay, so that's all of the scanning done; now to report on it.

<Report and decide how grave the situation is 7.2> =

        WRITE("%d problem name(s) have been observed:\n", NUMBER_CREATED(known_problem)); INDENT;

        WRITE("Problems actually existing (the source code refers to them):\n"); INDENT;
        Coverage::cite(OUT, CODE_MENTIONS_PCON, 0, CODE_MENTIONS_PCON,
            I"are named and in principle testable");
        if (Coverage::cite(OUT, 0, CODE_MENTIONS_PCON, CODE_MENTIONS_PCON,
            I"are named more than once:") > 0) {
            all_is_well = FALSE;
            Coverage::list(OUT, 0, CODE_MENTIONS_PCON, CODE_MENTIONS_PCON);
        }
        Coverage::cite(OUT, IMPOSSIBLE_PCON, 0, IMPOSSIBLE_PCON,
            I"are 'BelievedImpossible', that is, no known source text causes them");
        Coverage::cite(OUT, UNTESTABLE_PCON, 0, UNTESTABLE_PCON,
            I"are 'Untestable', that is, not mechanically testable");
        Coverage::cite(OUT, NAMELESS_PCON, 0, NAMELESS_PCON,
            I"are '...', that is, they need to be give a name and a test case");
        OUTDENT;

        WRITE("Problems which should have test cases:\n"); INDENT;
        Coverage::cite(OUT, CASE_EXISTS_PCON+CODE_MENTIONS_PCON, 0, CASE_EXISTS_PCON+CODE_MENTIONS_PCON,
            I"have test cases");
        if (Coverage::cite(OUT, CASE_EXISTS_PCON+CODE_MENTIONS_PCON, 0, CODE_MENTIONS_PCON,
            I"have no test case yet:") > 0) {
            Coverage::list(OUT, CASE_EXISTS_PCON+CODE_MENTIONS_PCON, 0, CODE_MENTIONS_PCON);
        }
        if (Coverage::cite(OUT, CASE_EXISTS_PCON+CODE_MENTIONS_PCON, 0, CASE_EXISTS_PCON,
            I"are spurious test cases, since no such problems exist:") > 0) {
            all_is_well = FALSE;
            Coverage::list(OUT, CASE_EXISTS_PCON+CODE_MENTIONS_PCON, 0, CASE_EXISTS_PCON);
        }
        OUTDENT;

        WRITE("Problems which are cross-referenced in 'Writing with Inform':\n"); INDENT;
        Coverage::cite(OUT, CODE_MENTIONS_PCON+DOC_MENTIONS_PCON, 0, CODE_MENTIONS_PCON+DOC_MENTIONS_PCON,
            I"are cross-referenced");
        if (Coverage::cite(OUT, 0, DOC_MENTIONS_PCON, DOC_MENTIONS_PCON,
            I"are cross-referenced more than once:") > 0) {
            all_is_well = FALSE;
            Coverage::list(OUT, 0, DOC_MENTIONS_PCON, DOC_MENTIONS_PCON);
        }
        if (Coverage::cite(OUT, CODE_MENTIONS_PCON+DOC_MENTIONS_PCON, 0, DOC_MENTIONS_PCON,
            I"are spurious references, since no such problems exist:") > 0) {
            all_is_well = FALSE;
            Coverage::list(OUT, CODE_MENTIONS_PCON+DOC_MENTIONS_PCON, 0, DOC_MENTIONS_PCON);
        }
        OUTDENT;
        OUTDENT;

This code is used in §7.

§8.

    int Coverage::cite(OUTPUT_STREAM, int mask, int mask2, int val, text_stream *message) {
        int N = 0;
        known_problem *KP;
        LOOP_OVER(KP, known_problem) {
            if ((KP->contexts_observed & mask) == val) N++;
            if ((KP->contexts_observed_multiple_times & mask2) == val) N++;
        }
        if ((N>0) && (message)) WRITE("%d problem(s) %S\n", N, message);
        return N;
    }

    void Coverage::list(OUTPUT_STREAM, int mask, int mask2, int val) {
        INDENT;
        known_problem *KP;
        LOOP_OVER(KP, known_problem)
            if (((KP->contexts_observed & mask) == val) ||
                ((KP->contexts_observed_multiple_times & mask2) == val)) {
                WRITE("%S\n", KP->name);
            }
        OUTDENT;
    }

The function Coverage::cite is used in §7.2.

The function Coverage::list is used in §7.2.