2019-02-05 02:44:07 +02:00
|
|
|
[Coverage::] Problem Coverage.
|
|
|
|
|
|
|
|
To see which problem messages have test cases and which are linked
|
|
|
|
to the documentation.
|
|
|
|
|
|
|
|
@h Observation.
|
2019-02-27 12:22:00 +02:00
|
|
|
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.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-27 12:22:00 +02:00
|
|
|
Problems can be mentioned in the code, in the documentation, or in the
|
|
|
|
set of Inform test cases.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-27 12:22:00 +02:00
|
|
|
@d CASE_EXISTS_PCON 0x00000001 /* mentioned in test cases */
|
|
|
|
@d DOC_MENTIONS_PCON 0x00000002 /* mentioned in documentation */
|
|
|
|
@d CODE_MENTIONS_PCON 0x00000004 /* mentioned in source code */
|
|
|
|
@d IMPOSSIBLE_PCON 0x00000008 /* this is |BelievedImpossible| */
|
|
|
|
@d UNTESTABLE_PCON 0x00000010 /* this is |Untestable| */
|
|
|
|
@d NAMELESS_PCON 0x00000020 /* this is |...| */
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-27 12:22:00 +02:00
|
|
|
=
|
2019-02-05 02:44:07 +02:00
|
|
|
typedef struct known_problem {
|
|
|
|
struct text_stream *name;
|
2019-02-27 12:22:00 +02:00
|
|
|
int contexts_observed; /* bitmap of the above bits */
|
|
|
|
int contexts_observed_multiple_times; /* bitmap of the above bits */
|
2020-05-09 15:07:39 +03:00
|
|
|
CLASS_DEFINITION
|
2019-02-05 02:44:07 +02:00
|
|
|
} known_problem;
|
|
|
|
|
|
|
|
@ When a problem is observed, we create a dictionary entry for it, if necessary,
|
|
|
|
and augment its bitmap of known contexts:
|
|
|
|
|
|
|
|
=
|
2019-02-27 12:22:00 +02:00
|
|
|
dictionary *problems_dictionary = NULL;
|
|
|
|
|
2019-02-05 02:44:07 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
@h 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.
|
|
|
|
|
|
|
|
=
|
2019-03-11 01:45:32 +02:00
|
|
|
void Coverage::which_problems_have_test_cases(void) {
|
2020-04-16 01:49:59 +03:00
|
|
|
filename *CAT = Filenames::in(path_to_inpolicy_workspace, I"cases.txt");
|
2020-06-28 01:18:54 +03:00
|
|
|
TEMPORARY_TEXT(COMMAND)
|
2022-04-14 16:39:12 +03:00
|
|
|
WRITE_TO(COMMAND, "..%cintest%cTangled%cintest inform7 -catalogue ", FOLDER_SEPARATOR, FOLDER_SEPARATOR, FOLDER_SEPARATOR);
|
2019-02-05 02:44:07 +02:00
|
|
|
Shell::redirect(COMMAND, CAT);
|
|
|
|
if (Shell::run(COMMAND)) Errors::fatal("can't run intest to harvest cases");
|
2020-06-28 01:18:54 +03:00
|
|
|
DISCARD_TEXT(COMMAND)
|
2019-02-05 02:44:07 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
@h Problems mentioned in documentation.
|
|
|
|
Here we look through the "Writing with Inform" source text for cross-references
|
|
|
|
to problem messages:
|
|
|
|
|
|
|
|
=
|
2019-03-11 01:45:32 +02:00
|
|
|
void Coverage::which_problems_are_referenced(void) {
|
2019-03-16 16:39:36 +02:00
|
|
|
pathname *D = Pathnames::from_text(I"resources");
|
2020-04-16 01:49:59 +03:00
|
|
|
D = Pathnames::down(D, I"Documentation");
|
|
|
|
filename *WWI = Filenames::in(D, I"Writing with Inform.txt");
|
2019-02-05 02:44:07 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
@h Problems generated in the I7 source.
|
2020-04-02 15:31:21 +03:00
|
|
|
Which is to say, actually existing problem messages.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
2019-02-27 12:22:00 +02:00
|
|
|
void Coverage::which_problems_exist(void) {
|
2020-04-02 15:31:21 +03:00
|
|
|
pathname *tools = Pathnames::up(path_to_inpolicy);
|
2020-04-16 01:49:59 +03:00
|
|
|
pathname *path_to_inweb = Pathnames::down(Pathnames::up(tools), I"inweb");
|
|
|
|
pathname *path_to_inform7 = Pathnames::down(tools, I"inform7");
|
2020-04-02 15:31:21 +03:00
|
|
|
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);
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-02 15:31:21 +03:00
|
|
|
@ So now we have to read a section, looking for the existence of problem
|
|
|
|
messages:
|
2019-02-27 12:22:00 +02:00
|
|
|
|
|
|
|
=
|
2019-02-05 02:44:07 +02:00
|
|
|
void Coverage::existence_harvester(text_stream *text, text_file_position *tfp, void *state) {
|
2020-04-02 15:31:21 +03:00
|
|
|
filename *SF = (filename *) state;
|
2019-02-05 02:44:07 +02:00
|
|
|
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]);
|
2020-06-28 01:18:54 +03:00
|
|
|
TEMPORARY_TEXT(name)
|
2019-02-05 02:44:07 +02:00
|
|
|
Str::copy(name, mr.exp[1]);
|
2019-02-27 12:22:00 +02:00
|
|
|
if (Str::eq(name, I"sigil")) break;
|
2019-02-05 02:44:07 +02:00
|
|
|
int context = CODE_MENTIONS_PCON;
|
2019-02-27 12:22:00 +02:00
|
|
|
if (Str::eq(name, I"BelievedImpossible")) {
|
|
|
|
context = IMPOSSIBLE_PCON;
|
2020-04-02 15:31:21 +03:00
|
|
|
WRITE_TO(name, "_%f_line%d", SF, tfp->line_count);
|
2019-02-27 12:22:00 +02:00
|
|
|
} else if (Str::eq(name, I"Untestable")) {
|
|
|
|
context = UNTESTABLE_PCON;
|
2020-04-02 15:31:21 +03:00
|
|
|
WRITE_TO(name, "_%f_line%d", SF, tfp->line_count);
|
2019-02-27 12:22:00 +02:00
|
|
|
} else if (Str::eq(name, I"...")) {
|
|
|
|
context = NAMELESS_PCON;
|
2020-04-02 15:31:21 +03:00
|
|
|
WRITE_TO(name, "_%f_line%d", SF, tfp->line_count);
|
2019-02-27 12:22:00 +02:00
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
Coverage::observe_problem(name, context);
|
2020-06-28 01:18:54 +03:00
|
|
|
DISCARD_TEXT(name)
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
Regexp::dispose_of(&mr);
|
|
|
|
}
|
|
|
|
|
|
|
|
@h Checking.
|
2019-02-27 12:22:00 +02:00
|
|
|
So the actual policy-enforcement routine is here:
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
|
|
|
int observations_made = FALSE;
|
|
|
|
int Coverage::check(OUTPUT_STREAM) {
|
|
|
|
if (observations_made == FALSE) {
|
2019-02-27 12:22:00 +02:00
|
|
|
@<Perform the observations@>;
|
2019-02-05 02:44:07 +02:00
|
|
|
observations_made = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int all_is_well = TRUE;
|
2019-02-27 12:22:00 +02:00
|
|
|
@<Report and decide how grave the situation is@>;
|
|
|
|
if (all_is_well) WRITE("All is well.\n");
|
|
|
|
else WRITE("This needs attention.\n");
|
|
|
|
WRITE("\n");
|
|
|
|
return all_is_well;
|
|
|
|
}
|
|
|
|
|
|
|
|
@<Perform the observations@> =
|
2019-03-11 01:45:32 +02:00
|
|
|
Coverage::which_problems_have_test_cases();
|
|
|
|
Coverage::which_problems_are_referenced();
|
2019-02-27 12:22:00 +02:00
|
|
|
Coverage::which_problems_exist();
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2019-02-27 12:22:00 +02:00
|
|
|
@ Okay, so that's all of the scanning done; now to report on it.
|
|
|
|
|
|
|
|
@<Report and decide how grave the situation is@> =
|
2019-02-05 02:44:07 +02:00
|
|
|
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,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are named and in principle testable");
|
2019-02-05 02:44:07 +02:00
|
|
|
if (Coverage::cite(OUT, 0, CODE_MENTIONS_PCON, CODE_MENTIONS_PCON,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are named more than once:") > 0) {
|
2019-02-05 02:44:07 +02:00
|
|
|
all_is_well = FALSE;
|
|
|
|
Coverage::list(OUT, 0, CODE_MENTIONS_PCON, CODE_MENTIONS_PCON);
|
|
|
|
}
|
|
|
|
Coverage::cite(OUT, IMPOSSIBLE_PCON, 0, IMPOSSIBLE_PCON,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are 'BelievedImpossible', that is, no known source text causes them");
|
2019-02-05 02:44:07 +02:00
|
|
|
Coverage::cite(OUT, UNTESTABLE_PCON, 0, UNTESTABLE_PCON,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are 'Untestable', that is, not mechanically testable");
|
2019-02-05 02:44:07 +02:00
|
|
|
Coverage::cite(OUT, NAMELESS_PCON, 0, NAMELESS_PCON,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are '...', that is, they need to be give a name and a test case");
|
2019-02-05 02:44:07 +02:00
|
|
|
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,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"have test cases");
|
2019-02-05 02:44:07 +02:00
|
|
|
if (Coverage::cite(OUT, CASE_EXISTS_PCON+CODE_MENTIONS_PCON, 0, CODE_MENTIONS_PCON,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"have no test case yet:") > 0) {
|
2019-02-05 02:44:07 +02:00
|
|
|
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,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are spurious test cases, since no such problems exist:") > 0) {
|
2019-02-05 02:44:07 +02:00
|
|
|
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,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are cross-referenced");
|
2019-02-05 02:44:07 +02:00
|
|
|
if (Coverage::cite(OUT, 0, DOC_MENTIONS_PCON, DOC_MENTIONS_PCON,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are cross-referenced more than once:") > 0) {
|
2019-02-05 02:44:07 +02:00
|
|
|
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,
|
2019-02-27 12:22:00 +02:00
|
|
|
I"are spurious references, since no such problems exist:") > 0) {
|
2019-02-05 02:44:07 +02:00
|
|
|
all_is_well = FALSE;
|
|
|
|
Coverage::list(OUT, CODE_MENTIONS_PCON+DOC_MENTIONS_PCON, 0, DOC_MENTIONS_PCON);
|
|
|
|
}
|
|
|
|
OUTDENT;
|
|
|
|
OUTDENT;
|
|
|
|
|
2019-02-27 12:22:00 +02:00
|
|
|
@ =
|
|
|
|
int Coverage::cite(OUTPUT_STREAM, int mask, int mask2, int val, text_stream *message) {
|
2019-02-05 02:44:07 +02:00
|
|
|
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++;
|
|
|
|
}
|
2019-02-27 12:22:00 +02:00
|
|
|
if ((N>0) && (message)) WRITE("%d problem(s) %S\n", N, message);
|
2019-02-05 02:44:07 +02:00
|
|
|
return N;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Coverage::list(OUTPUT_STREAM, int mask, int mask2, int val) {
|
|
|
|
INDENT;
|
|
|
|
known_problem *KP;
|
2019-02-27 12:22:00 +02:00
|
|
|
LOOP_OVER(KP, known_problem)
|
2019-02-05 02:44:07 +02:00
|
|
|
if (((KP->contexts_observed & mask) == val) ||
|
|
|
|
((KP->contexts_observed_multiple_times & mask2) == val)) {
|
|
|
|
WRITE("%S\n", KP->name);
|
|
|
|
}
|
|
|
|
OUTDENT;
|
|
|
|
}
|