testilo 25.1.2 → 26.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -32
- package/call.js +34 -1
- package/package.json +1 -1
- package/procs/difgest/tfp40/index.js +1 -1
- package/procs/util.js +8 -2
- package/summarize.js +35 -0
package/README.md
CHANGED
|
@@ -244,7 +244,7 @@ There are two ways to use the `script` module.
|
|
|
244
244
|
|
|
245
245
|
##### By a module
|
|
246
246
|
|
|
247
|
-
A module can invoke `script` in this way:
|
|
247
|
+
A module can invoke `script()` in this way:
|
|
248
248
|
|
|
249
249
|
```javaScript
|
|
250
250
|
const {script} = require('testilo/script');
|
|
@@ -293,7 +293,7 @@ There are two ways to use the `merge` module.
|
|
|
293
293
|
|
|
294
294
|
##### By a module
|
|
295
295
|
|
|
296
|
-
A module can invoke `merge` in this way:
|
|
296
|
+
A module can invoke `merge()` in this way:
|
|
297
297
|
|
|
298
298
|
```javaScript
|
|
299
299
|
const {merge} = require('testilo/merge');
|
|
@@ -426,7 +426,7 @@ Testilo can enhance such a report by:
|
|
|
426
426
|
- creating difgests
|
|
427
427
|
- creating comparisons
|
|
428
428
|
|
|
429
|
-
|
|
429
|
+
### Scoring
|
|
430
430
|
|
|
431
431
|
To add scores to reports, the `score` module of Testilo performs computations on the test results and adds a `score` property to each report.
|
|
432
432
|
|
|
@@ -436,7 +436,7 @@ The `score()` function of the `score` module takes two arguments:
|
|
|
436
436
|
|
|
437
437
|
A scoring function defines scoring rules. The Testilo package contains a `procs/score` directory, in which there are modules that export scoring functions. You can use one of those scoring functions, or you can create your own.
|
|
438
438
|
|
|
439
|
-
|
|
439
|
+
#### Scorers
|
|
440
440
|
|
|
441
441
|
The built-in scoring functions are named `scorer()` and are exported by files whose names begin with `tsp` (for Testilo scoring proc). Those functions make use of `issues` objects defined in files whose names begin with `tic`. An `issues` object defines an issue classification: a body of data about rules of tools and the tool-agnostic issues that those rules are deemed to belong to.
|
|
442
442
|
|
|
@@ -482,11 +482,11 @@ The `quality` property is usually 1, but if the test of the rule is known to be
|
|
|
482
482
|
|
|
483
483
|
Some issue objects (such as `flash` in `tic40.js`) have a `max` property, equal to the maximum possible count of instances. That property allows a scorer to ascribe a greater weight to an instance of that issue.
|
|
484
484
|
|
|
485
|
-
|
|
485
|
+
#### Invocation
|
|
486
486
|
|
|
487
487
|
There are two ways to invoke the `score` module.
|
|
488
488
|
|
|
489
|
-
|
|
489
|
+
##### By a module
|
|
490
490
|
|
|
491
491
|
A module can invoke `score()` in this way:
|
|
492
492
|
|
|
@@ -503,7 +503,7 @@ The second argument to `score()` is an array of report objects. They may have be
|
|
|
503
503
|
|
|
504
504
|
The invoking module can further dispose of the scored reports as needed.
|
|
505
505
|
|
|
506
|
-
|
|
506
|
+
##### By a user
|
|
507
507
|
|
|
508
508
|
A user can invoke `score()` in this way:
|
|
509
509
|
|
|
@@ -519,13 +519,13 @@ When a user invokes `score()` in this example, the `call` module:
|
|
|
519
519
|
|
|
520
520
|
The optional third argument to `call()` (`75m` in this example) is a report selector. Without the argument, `call()` gets all the reports in the `raw` subdirectory. With the argument, `call()` gets only those reports whose names begin with the argument string.
|
|
521
521
|
|
|
522
|
-
|
|
522
|
+
#### Validation
|
|
523
523
|
|
|
524
524
|
To test the `score` module, in the project directory you can execute the statement `node validation/score/validate`. If `score` is valid, all logging statements will begin with “Success” and none will begin with “ERROR”.
|
|
525
525
|
|
|
526
|
-
|
|
526
|
+
### Report digesting
|
|
527
527
|
|
|
528
|
-
|
|
528
|
+
#### Introduction
|
|
529
529
|
|
|
530
530
|
Reports from Testaro are JavaScript objects. When represented as JSON, they are human-readable, but not human-friendly. They are basically designed for machine tractability. This is equally true for reports that have been scored by Testilo. But Testilo can _digest_ a scored report, converting it to a human-oriented HTML document, or _digest_.
|
|
531
531
|
|
|
@@ -538,11 +538,11 @@ The digesting function populates an HTML digest template. A copy of the template
|
|
|
538
538
|
|
|
539
539
|
The included templates format placeholders with leading and trailing underscore pairs (such as `__issueCount__`).
|
|
540
540
|
|
|
541
|
-
|
|
541
|
+
#### Invocation
|
|
542
542
|
|
|
543
543
|
There are two ways to use the `digest` module.
|
|
544
544
|
|
|
545
|
-
|
|
545
|
+
##### By a module
|
|
546
546
|
|
|
547
547
|
A module can invoke `digest()` in this way:
|
|
548
548
|
|
|
@@ -564,7 +564,7 @@ The third argument is the absolute or relative URL of a directory where the repo
|
|
|
564
564
|
|
|
565
565
|
The `digest()` function returns an array of digested reports. The invoking module can further dispose of the digested reports as needed.
|
|
566
566
|
|
|
567
|
-
|
|
567
|
+
##### By a user
|
|
568
568
|
|
|
569
569
|
A user can invoke `digest()` in this way:
|
|
570
570
|
|
|
@@ -585,9 +585,9 @@ The optional fourth argument to `call()` (`75m` in this example) is a report sel
|
|
|
585
585
|
|
|
586
586
|
The digests created by `digest()` are HTML files, and they expect a `style.css` file to exist in their directory. The `reports/digested/style.css` file in Testilo is an appropriate stylesheet to be copied into the directory where digested reports are written.
|
|
587
587
|
|
|
588
|
-
|
|
588
|
+
### Report difgesting
|
|
589
589
|
|
|
590
|
-
|
|
590
|
+
#### Introduction
|
|
591
591
|
|
|
592
592
|
A _difgest_ is a digest that compares two reports. They can be reports of different targets, or reports of the same target from two different times or under two different conditions.
|
|
593
593
|
|
|
@@ -601,11 +601,11 @@ The `difgest` module difgests two scored reports. Its `difgest()` function takes
|
|
|
601
601
|
|
|
602
602
|
The difgest template and module operate like the digest ones.
|
|
603
603
|
|
|
604
|
-
|
|
604
|
+
#### Invocation
|
|
605
605
|
|
|
606
606
|
There are two ways to use the `difgest` module.
|
|
607
607
|
|
|
608
|
-
|
|
608
|
+
##### By a module
|
|
609
609
|
|
|
610
610
|
A module can invoke `difgest()` in this way:
|
|
611
611
|
|
|
@@ -625,9 +625,9 @@ The difgest will include links to the two digests, which, in turn, contain links
|
|
|
625
625
|
|
|
626
626
|
`difgest()` returns a difgest. The invoking module can further dispose of the difgest as needed.
|
|
627
627
|
|
|
628
|
-
|
|
628
|
+
##### By a user
|
|
629
629
|
|
|
630
|
-
A user can invoke `difgest` in this way:
|
|
630
|
+
A user can invoke `difgest()` in this way:
|
|
631
631
|
|
|
632
632
|
```bash
|
|
633
633
|
node call difgest tfp99 20141215T1200-x7-3 20141215T1200-x7-4
|
|
@@ -642,7 +642,7 @@ Difgests include links to the digests of the two reports. The destinations of th
|
|
|
642
642
|
|
|
643
643
|
Difgests expect a `style.css` file to exist in their directory, as digests do.
|
|
644
644
|
|
|
645
|
-
|
|
645
|
+
#### Validation
|
|
646
646
|
|
|
647
647
|
To test the `digest` module, in the project directory you can execute the statement `node validation/digest/validate`. If `digest` is valid, all logging statements will begin with “Success” and none will begin with “ERROR”.
|
|
648
648
|
|
|
@@ -656,13 +656,13 @@ The `compare` module compares the scores in a collection of scored reports. Its
|
|
|
656
656
|
|
|
657
657
|
The comparison function defines the rules for generating an HTML file comparing the scored reports. The Testilo package contains a `procs/compare` directory, in which there are subdirectories containing modules that export comparison functions. You can use one of those functions, or you can create your own.
|
|
658
658
|
|
|
659
|
-
|
|
659
|
+
#### Invocation
|
|
660
660
|
|
|
661
661
|
There are two ways to use the `compare` module.
|
|
662
662
|
|
|
663
|
-
|
|
663
|
+
##### By a module
|
|
664
664
|
|
|
665
|
-
A module can invoke `compare` in this way:
|
|
665
|
+
A module can invoke `compare()` in this way:
|
|
666
666
|
|
|
667
667
|
```javaScript
|
|
668
668
|
const {compare} = require('testilo/compare');
|
|
@@ -674,9 +674,9 @@ compare(comparer, scoredReports)
|
|
|
674
674
|
|
|
675
675
|
The first argument to `compare()` is a comparison function. In this example, it been obtained from a file in the Testilo package, but it could be custom-made. The second argument to `compare()` is an array of report objects. The `compare()` function returns a comparative report. The invoking module can further dispose of the comparative report as needed.
|
|
676
676
|
|
|
677
|
-
|
|
677
|
+
##### By a user
|
|
678
678
|
|
|
679
|
-
A user can invoke `compare` in this way:
|
|
679
|
+
A user can invoke `compare()` in this way:
|
|
680
680
|
|
|
681
681
|
```bash
|
|
682
682
|
node call compare tcp99 legislators
|
|
@@ -690,6 +690,10 @@ When a user invokes `compare` in this example, the `call` module:
|
|
|
690
690
|
|
|
691
691
|
The comparative report created by `compare` is an HTML file, and it expects a `style.css` file to exist in its directory. The `reports/comparative/style.css` file in Testilo is an appropriate stylesheet to be copied into the directory where comparative reports are written.
|
|
692
692
|
|
|
693
|
+
#### Validation
|
|
694
|
+
|
|
695
|
+
To test the `compare` module, in the project directory you can execute the statement `node validation/compare/validate`. If `compare` is valid, all logging statements will begin with “Success” and none will begin with “ERROR”.
|
|
696
|
+
|
|
693
697
|
### Tool crediting
|
|
694
698
|
|
|
695
699
|
If you use Testaro to perform all the tests of all the tools on multiple targets and score the reports with a score proc that maps tool rules onto tool-agnostic issues, you may want to tabulate the comparative efficacy of each tool in discovering instances of issues. Testilo can help you do this by producing a _credit report_.
|
|
@@ -704,13 +708,13 @@ The credit report contains four sections:
|
|
|
704
708
|
- `onlies`: a list of the issues that only the tool reported instances of
|
|
705
709
|
- `mosts`: a list of the issues for which the instance count of the tool was not surpassed by that of any other tool
|
|
706
710
|
|
|
707
|
-
|
|
711
|
+
#### Invocation
|
|
708
712
|
|
|
709
713
|
There are two ways to use the `credit` module.
|
|
710
714
|
|
|
711
|
-
|
|
715
|
+
##### By a module
|
|
712
716
|
|
|
713
|
-
A module can invoke `credit` in this way:
|
|
717
|
+
A module can invoke `credit()` in this way:
|
|
714
718
|
|
|
715
719
|
```javaScript
|
|
716
720
|
const {credit} = require('testilo/credit');
|
|
@@ -720,9 +724,9 @@ credit(scoredReports)
|
|
|
720
724
|
|
|
721
725
|
The argument to `credit()` is an array of scored report objects. The `credit()` function returns a credit report. The invoking module can further dispose of the credit report as needed.
|
|
722
726
|
|
|
723
|
-
|
|
727
|
+
##### By a user
|
|
724
728
|
|
|
725
|
-
A user can invoke `credit` in this way:
|
|
729
|
+
A user can invoke `credit()` in this way:
|
|
726
730
|
|
|
727
731
|
```bash
|
|
728
732
|
node call credit legislators 23pl
|
|
@@ -734,9 +738,37 @@ When a user invokes `credit` in this example, the `call` module:
|
|
|
734
738
|
|
|
735
739
|
The third argument to `call` (`23pl` in this example) is optional. If it is omitted, `call` will get and `credit()` will tabulate all the reports in the `scored` directory.
|
|
736
740
|
|
|
737
|
-
###
|
|
741
|
+
### Summarization
|
|
738
742
|
|
|
739
|
-
|
|
743
|
+
The `summarize` module of Testilo can summarize a collection of scored reports. The summary of each report contains, insofar as they exist in the report, its ID, end time, order ID, target data, and total score.
|
|
744
|
+
|
|
745
|
+
#### Invocation
|
|
746
|
+
|
|
747
|
+
##### By a module
|
|
748
|
+
|
|
749
|
+
A module can invoke `summarize()` in this way:
|
|
750
|
+
|
|
751
|
+
```javaScript
|
|
752
|
+
const {summarize} = require('testilo/summarize');
|
|
753
|
+
const reports = […];
|
|
754
|
+
const summary = summarize(reports);
|
|
755
|
+
…
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
The `reports` argument is an array of scored reports. The `summary` constant is an object. The module can further dispose of `summary` as needed.
|
|
759
|
+
|
|
760
|
+
##### By a user
|
|
761
|
+
|
|
762
|
+
A user can invoke `summarize()` in either of these two ways:
|
|
763
|
+
|
|
764
|
+
```javaScript
|
|
765
|
+
node call summarize divisions
|
|
766
|
+
node call summarize divisions 2411
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
When a user invokes `summarize` in this example, the `call` module:
|
|
770
|
+
- gets all the reports in the `scored` subdirectory of the `REPORTDIR` directory, or (if the third argument is present) all those whose file names begin with `2411`.
|
|
771
|
+
- writes the summary as a JSON file named `divisions.json` to the `summarized` subdirectory of the `REPORTDIR` directory.
|
|
740
772
|
|
|
741
773
|
## Origin
|
|
742
774
|
|
package/call.js
CHANGED
|
@@ -34,6 +34,8 @@ const {digest} = require('./digest');
|
|
|
34
34
|
const {difgest} = require('./difgest');
|
|
35
35
|
// Function to compare scores.
|
|
36
36
|
const {compare} = require('./compare');
|
|
37
|
+
// Function to summarize reports.
|
|
38
|
+
const {summarize} = require('./summarize');
|
|
37
39
|
|
|
38
40
|
// ########## CONSTANTS
|
|
39
41
|
|
|
@@ -230,7 +232,7 @@ const callCredit = async (tallyID, selector = '') => {
|
|
|
230
232
|
const creditDir = `${reportDir}/credit`;
|
|
231
233
|
await fs.mkdir(creditDir, {recursive: true});
|
|
232
234
|
await fs.writeFile(`${creditDir}/${tallyID}.json`, JSON.stringify(tally, null, 2));
|
|
233
|
-
console.log(`Reports tallied and credit report saved in ${creditDir}`);
|
|
235
|
+
console.log(`Reports tallied and credit report ${tallyID} saved in ${creditDir}`);
|
|
234
236
|
}
|
|
235
237
|
// Otherwise, i.e. if no scored reports are to be tallied:
|
|
236
238
|
else {
|
|
@@ -238,6 +240,31 @@ const callCredit = async (tallyID, selector = '') => {
|
|
|
238
240
|
console.log('ERROR: No scored reports to be tallied');
|
|
239
241
|
}
|
|
240
242
|
};
|
|
243
|
+
// Fulfills a summarize request.
|
|
244
|
+
const callSummarize = async (what, selector = '') => {
|
|
245
|
+
// Get the scored reports to be summarized.
|
|
246
|
+
const reports = await getReports('scored', selector);
|
|
247
|
+
// If any exist:
|
|
248
|
+
if (reports.length) {
|
|
249
|
+
// Summarize them.
|
|
250
|
+
const summary = summarize(what, reports);
|
|
251
|
+
// Add the selector, if any, to the summary.
|
|
252
|
+
if (selector) {
|
|
253
|
+
summary.selector = selector;
|
|
254
|
+
}
|
|
255
|
+
// Save the summary.
|
|
256
|
+
const summaryDir = `${reportDir}/summarized`;
|
|
257
|
+
await fs.mkdir(summaryDir, {recursive: true});
|
|
258
|
+
const filePath = `${summaryDir}/${summary.timeStamp}-${getRandomString(2)}-0.json`;
|
|
259
|
+
await fs.writeFile(filePath, `${JSON.stringify(summary, null, 2)}\n`);
|
|
260
|
+
console.log(`Reports summarized and summary saved as ${filePath}`);
|
|
261
|
+
}
|
|
262
|
+
// Otherwise, i.e. if no scored reports are to be summarized:
|
|
263
|
+
else {
|
|
264
|
+
// Report this.
|
|
265
|
+
console.log('ERROR: No scored reports to be summarized');
|
|
266
|
+
}
|
|
267
|
+
};
|
|
241
268
|
|
|
242
269
|
// ########## OPERATION
|
|
243
270
|
|
|
@@ -296,6 +323,12 @@ else if (fn === 'credit' && fnArgs.length > 0 && fnArgs.length < 3) {
|
|
|
296
323
|
console.log('Execution completed');
|
|
297
324
|
});
|
|
298
325
|
}
|
|
326
|
+
else if (fn === 'summarize' && fnArgs.length > 0 && fnArgs.length < 3) {
|
|
327
|
+
callSummarize(... fnArgs)
|
|
328
|
+
.then(() => {
|
|
329
|
+
console.log('Execution completed');
|
|
330
|
+
});
|
|
331
|
+
}
|
|
299
332
|
else {
|
|
300
333
|
console.log('ERROR: Invalid statement');
|
|
301
334
|
}
|
package/package.json
CHANGED
|
@@ -58,7 +58,7 @@ const populateQuery = (reportA, reportB, query) => {
|
|
|
58
58
|
const {summary, details} = score;
|
|
59
59
|
query[`org${suffix}`] = target.what;
|
|
60
60
|
query[`url${suffix}`] = target.which;
|
|
61
|
-
query[`dateTime${suffix}`] = jobData.replace(/-/g, '/').replace('T', ', ');
|
|
61
|
+
query[`dateTime${suffix}`] = jobData.endTime.replace(/-/g, '/').replace('T', ', ');
|
|
62
62
|
query[`total${suffix}`] = summary.total;
|
|
63
63
|
query[`digest${suffix}URL`] = process.env.DIGEST_URL.replace('__id__', id);
|
|
64
64
|
// Get the union of the issues in the reports.
|
package/procs/util.js
CHANGED
|
@@ -24,7 +24,7 @@ const getTimeStamp
|
|
|
24
24
|
= exports.getTimeStamp
|
|
25
25
|
= date => getTimeString(date).replace(/[-:]/g, '').slice(2, 13);
|
|
26
26
|
// Returns a time stamp representing the date and time.
|
|
27
|
-
exports.getNowStamp = () => getTimeStamp(new Date());
|
|
27
|
+
const getNowStamp = exports.getNowStamp = () => getTimeStamp(new Date());
|
|
28
28
|
// Inserts a character periodically in a string.
|
|
29
29
|
const punctuate = (string, insertion, chunkSize) => {
|
|
30
30
|
const segments = [];
|
|
@@ -56,13 +56,19 @@ exports.alphaNumOf = num => {
|
|
|
56
56
|
return resultDigits.join('');
|
|
57
57
|
};
|
|
58
58
|
// Returns a random string.
|
|
59
|
-
exports.getRandomString = length => {
|
|
59
|
+
const getRandomString = exports.getRandomString = length => {
|
|
60
60
|
const chars = [];
|
|
61
61
|
for (let i = 0; i < length; i++) {
|
|
62
62
|
chars.push(alphaNumChars[Math.floor(62 * Math.random())]);
|
|
63
63
|
}
|
|
64
64
|
return chars.join('');
|
|
65
65
|
};
|
|
66
|
+
// Returns a file ID.
|
|
67
|
+
exports.getFileID = randomLength => {
|
|
68
|
+
const timePart = getNowStamp();
|
|
69
|
+
const randomPart = getRandomString(randomLength);
|
|
70
|
+
return `${timePart}-${randomPart}-0`;
|
|
71
|
+
};
|
|
66
72
|
// Returns a horizontal SVG graph bar.
|
|
67
73
|
const getSVGBar = (num, max, isRight) => {
|
|
68
74
|
const widthFrac = 100 * num / max;
|
package/summarize.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
summarize.js
|
|
3
|
+
Returns a summary of reports.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ########## IMPORTS
|
|
7
|
+
|
|
8
|
+
// Module to keep secrets.
|
|
9
|
+
require('dotenv').config();
|
|
10
|
+
// Module to perform common operations.
|
|
11
|
+
const {getNowStamp} = require('./procs/util');
|
|
12
|
+
|
|
13
|
+
// ########## FUNCTIONS
|
|
14
|
+
|
|
15
|
+
// Returns a summary.
|
|
16
|
+
exports.summarize = (what, reports) => {
|
|
17
|
+
const data = reports.map(report => {
|
|
18
|
+
const {id, jobData, score, sources} = report;
|
|
19
|
+
const order = sources && sources.order || '';
|
|
20
|
+
const target = sources && sources.target || '';
|
|
21
|
+
return {
|
|
22
|
+
id: id || '',
|
|
23
|
+
endTime: jobData && jobData.endTime || '',
|
|
24
|
+
order: order || '',
|
|
25
|
+
target,
|
|
26
|
+
score: score && score.summary && score.summary.total || null
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
const summary = {
|
|
30
|
+
what,
|
|
31
|
+
timeStamp: getNowStamp(),
|
|
32
|
+
data
|
|
33
|
+
};
|
|
34
|
+
return summary;
|
|
35
|
+
};
|