1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-08 01:54:21 +03:00
inform7/inpolicy/Chapter 2/Problem Coverage.w

250 lines
8.9 KiB
OpenEdge ABL
Raw Normal View History

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.
=
void Coverage::which_problems_have_test_cases(void) {
filename *CAT = Filenames::in(path_to_inpolicy_workspace, I"cases.txt");
2020-06-28 01:18:54 +03:00
TEMPORARY_TEXT(COMMAND)
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, U"(PM_%C+)%c*"))
2019-02-05 02:44:07 +02:00
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:
=
void Coverage::which_problems_are_referenced(void) {
2019-03-16 16:39:36 +02:00
pathname *D = Pathnames::from_text(I"resources");
D = Pathnames::down(D, I"Documentation");
filename *WWI = Filenames::in(D, I"Writing with Inform.md");
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, U"(%c*)%{(PM_%C+?)%}(%c*)")) {
2019-02-05 02:44:07 +02:00
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.
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) {
pathname *tools = Pathnames::up(path_to_inpolicy);
pathname *path_to_inweb = Pathnames::down(Pathnames::up(tools), I"inweb");
pathname *path_to_inform7 = Pathnames::down(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);
}
2019-02-05 02:44:07 +02: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) {
filename *SF = (filename *) state;
2019-02-05 02:44:07 +02:00
match_results mr = Regexp::create_mr();
while (Regexp::match(&mr, text, U"(%c*?)_p_%((%c+?)%)(%c*)")) {
2019-02-05 02:44:07 +02:00
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;
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;
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;
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@> =
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;
}