-
-
Notifications
You must be signed in to change notification settings - Fork 175
/
RunExecution.ts
135 lines (127 loc) · 4.33 KB
/
RunExecution.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { VerbosityLevel } from '../configuration/VerbosityLevel';
import { ExecutionStatus } from './ExecutionStatus';
import { ExecutionTree } from './ExecutionTree';
import { RunDetails } from './RunDetails';
/**
* @hidden
*
* Report the status of a run
*
* It receives notification from the runner in case of failures
*/
export class RunExecution<Ts> {
readonly rootExecutionTrees: ExecutionTree<Ts>[];
currentLevelExecutionTrees: ExecutionTree<Ts>[];
pathToFailure?: string;
value?: Ts;
failure: string;
numSkips: number;
numSuccesses: number;
constructor(readonly verbosity: VerbosityLevel) {
this.rootExecutionTrees = [];
this.currentLevelExecutionTrees = this.rootExecutionTrees;
this.numSkips = 0;
this.numSuccesses = 0;
}
private appendExecutionTree(status: ExecutionStatus, value: Ts) {
const currentTree: ExecutionTree<Ts> = { status, value, children: [] };
this.currentLevelExecutionTrees.push(currentTree);
return currentTree;
}
fail(value: Ts, id: number, message: string) {
if (this.verbosity >= VerbosityLevel.Verbose) {
const currentTree = this.appendExecutionTree(ExecutionStatus.Failure, value);
this.currentLevelExecutionTrees = currentTree.children;
}
if (this.pathToFailure == null) this.pathToFailure = `${id}`;
else this.pathToFailure += `:${id}`;
this.value = value;
this.failure = message;
}
skip(value: Ts) {
if (this.verbosity >= VerbosityLevel.VeryVerbose) {
this.appendExecutionTree(ExecutionStatus.Skipped, value);
}
if (this.pathToFailure == null) {
++this.numSkips;
}
}
success(value: Ts) {
if (this.verbosity >= VerbosityLevel.VeryVerbose) {
this.appendExecutionTree(ExecutionStatus.Success, value);
}
if (this.pathToFailure == null) {
++this.numSuccesses;
}
}
private isSuccess = (): boolean => this.pathToFailure == null;
private firstFailure = (): number => (this.pathToFailure ? +this.pathToFailure.split(':')[0] : -1);
private numShrinks = (): number => (this.pathToFailure ? this.pathToFailure.split(':').length - 1 : 0);
private extractFailures() {
if (this.isSuccess()) {
return [];
}
const failures: Ts[] = [];
let cursor = this.rootExecutionTrees;
while (cursor.length > 0 && cursor[cursor.length - 1].status === ExecutionStatus.Failure) {
const failureTree = cursor[cursor.length - 1];
failures.push(failureTree.value);
cursor = failureTree.children;
}
return failures;
}
private static mergePaths = (offsetPath: string, path: string) => {
if (offsetPath.length === 0) return path;
const offsetItems = offsetPath.split(':');
const remainingItems = path.split(':');
const middle = +offsetItems[offsetItems.length - 1] + +remainingItems[0];
return [...offsetItems.slice(0, offsetItems.length - 1), `${middle}`, ...remainingItems.slice(1)].join(':');
};
toRunDetails(seed: number, basePath: string, numRuns: number, maxSkips: number): RunDetails<Ts> {
if (!this.isSuccess()) {
// encountered a property failure
return {
failed: true,
numRuns: this.firstFailure() + 1 - this.numSkips,
numSkips: this.numSkips,
numShrinks: this.numShrinks(),
seed,
counterexample: this.value!,
counterexamplePath: RunExecution.mergePaths(basePath, this.pathToFailure!),
error: this.failure,
failures: this.extractFailures(),
executionSummary: this.rootExecutionTrees,
verbose: this.verbosity
};
}
if (this.numSkips > maxSkips) {
// too many skips
return {
failed: true,
numRuns: this.numSuccesses,
numSkips: this.numSkips,
numShrinks: 0,
seed,
counterexample: null,
counterexamplePath: null,
error: null,
failures: [],
executionSummary: this.rootExecutionTrees,
verbose: this.verbosity
};
}
return {
failed: false,
numRuns,
numSkips: this.numSkips,
numShrinks: 0,
seed,
counterexample: null,
counterexamplePath: null,
error: null,
failures: [],
executionSummary: this.rootExecutionTrees,
verbose: this.verbosity
};
}
}