@flemist/test-variants 2.0.5 → 3.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/dist/bundle/browser.js +598 -523
- package/dist/lib/index.cjs +2 -1
- package/dist/lib/index.d.ts +2 -1
- package/dist/lib/index.mjs +1 -1
- package/dist/lib/test-variants/createTestVariants.cjs +5 -2
- package/dist/lib/test-variants/createTestVariants.mjs +5 -2
- package/dist/lib/test-variants/createTestVariants.perf.cjs +1 -1
- package/dist/lib/test-variants/createTestVariants.perf.mjs +1 -1
- package/dist/lib/test-variants/testVariantsIterable.cjs +3 -0
- package/dist/lib/test-variants/testVariantsIterable.d.ts +3 -0
- package/dist/lib/test-variants/testVariantsIterable.mjs +3 -0
- package/dist/lib/test-variants/testVariantsIterator.cjs +357 -0
- package/dist/lib/test-variants/testVariantsIterator.d.ts +67 -0
- package/dist/lib/test-variants/testVariantsIterator.mjs +353 -0
- package/dist/lib/test-variants/testVariantsRun.cjs +145 -191
- package/dist/lib/test-variants/testVariantsRun.d.ts +6 -16
- package/dist/lib/test-variants/testVariantsRun.mjs +145 -191
- package/dist/lib/test-variants/types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -24,23 +24,11 @@ function testVariantsRun(testRun, variants, options = {}) {
|
|
|
24
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
25
25
|
const saveErrorVariants = options.saveErrorVariants;
|
|
26
26
|
const retriesPerVariant = (_a = saveErrorVariants === null || saveErrorVariants === void 0 ? void 0 : saveErrorVariants.retriesPerVariant) !== null && _a !== void 0 ? _a : 1;
|
|
27
|
+
const useToFindBestError = saveErrorVariants === null || saveErrorVariants === void 0 ? void 0 : saveErrorVariants.useToFindBestError;
|
|
27
28
|
const sessionDate = new Date();
|
|
28
29
|
const errorVariantFilePath = saveErrorVariants
|
|
29
30
|
? path.resolve(saveErrorVariants.dir, (_c = (_b = saveErrorVariants.getFilePath) === null || _b === void 0 ? void 0 : _b.call(saveErrorVariants, { sessionDate })) !== null && _c !== void 0 ? _c : generateErrorVariantFilePath({ sessionDate }))
|
|
30
31
|
: null;
|
|
31
|
-
// Replay phase: run previously saved error variants before normal iteration
|
|
32
|
-
if (saveErrorVariants) {
|
|
33
|
-
const files = yield readErrorVariantFiles(saveErrorVariants.dir);
|
|
34
|
-
for (const filePath of files) {
|
|
35
|
-
const args = yield parseErrorVariantFile(filePath, saveErrorVariants.jsonToArgs);
|
|
36
|
-
for (let retry = 0; retry < retriesPerVariant; retry++) {
|
|
37
|
-
const promiseOrResult = testRun(args, -1, null);
|
|
38
|
-
if (isPromiseLike(promiseOrResult)) {
|
|
39
|
-
yield promiseOrResult;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
32
|
const GC_Iterations = (_d = options.GC_Iterations) !== null && _d !== void 0 ? _d : 1000000;
|
|
45
33
|
const GC_IterationsAsync = (_e = options.GC_IterationsAsync) !== null && _e !== void 0 ? _e : 10000;
|
|
46
34
|
const GC_Interval = (_f = options.GC_Interval) !== null && _f !== void 0 ? _f : 1000;
|
|
@@ -48,85 +36,47 @@ function testVariantsRun(testRun, variants, options = {}) {
|
|
|
48
36
|
const logCompleted = (_h = options.logCompleted) !== null && _h !== void 0 ? _h : true;
|
|
49
37
|
const abortSignalExternal = options.abortSignal;
|
|
50
38
|
const findBestError = options.findBestError;
|
|
39
|
+
const cycles = (_j = findBestError === null || findBestError === void 0 ? void 0 : findBestError.cycles) !== null && _j !== void 0 ? _j : 1;
|
|
51
40
|
const parallel = options.parallel === true
|
|
52
41
|
? Math.pow(2, 31)
|
|
53
42
|
: !options.parallel || options.parallel <= 0
|
|
54
43
|
? 1
|
|
55
44
|
: options.parallel;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
let cycleIndex = 0;
|
|
60
|
-
let repeatIndex = 0;
|
|
61
|
-
const startTime = Date.now();
|
|
62
|
-
let cycleStartTime = startTime;
|
|
63
|
-
let seed = void 0;
|
|
64
|
-
let bestError = null;
|
|
65
|
-
let index = -1;
|
|
66
|
-
let args = {};
|
|
67
|
-
let variantsIterator = variants[Symbol.iterator]();
|
|
68
|
-
function getLimitVariantsCount() {
|
|
69
|
-
if (limitVariantsCount != null && bestError != null) {
|
|
70
|
-
return Math.min(limitVariantsCount, bestError.index);
|
|
71
|
-
}
|
|
72
|
-
if (limitVariantsCount != null) {
|
|
73
|
-
return limitVariantsCount;
|
|
74
|
-
}
|
|
75
|
-
if (bestError != null) {
|
|
76
|
-
return bestError.index;
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
45
|
+
// Apply initial limits
|
|
46
|
+
if (options.limitVariantsCount != null) {
|
|
47
|
+
variants.addLimit({ index: options.limitVariantsCount });
|
|
79
48
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
return true;
|
|
49
|
+
// Replay phase: run previously saved error variants before normal iteration
|
|
50
|
+
if (saveErrorVariants) {
|
|
51
|
+
const files = yield readErrorVariantFiles(saveErrorVariants.dir);
|
|
52
|
+
for (const filePath of files) {
|
|
53
|
+
const args = yield parseErrorVariantFile(filePath, saveErrorVariants.jsonToArgs);
|
|
54
|
+
for (let retry = 0; retry < retriesPerVariant; retry++) {
|
|
55
|
+
try {
|
|
56
|
+
const promiseOrResult = testRun(args, -1, null);
|
|
57
|
+
if (isPromiseLike(promiseOrResult)) {
|
|
58
|
+
yield promiseOrResult;
|
|
59
|
+
}
|
|
93
60
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const result = variantsIterator.next();
|
|
103
|
-
if (!result.done) {
|
|
104
|
-
args = result.value;
|
|
105
|
-
if (findBestError) {
|
|
106
|
-
seed = findBestError.getSeed({
|
|
107
|
-
variantIndex: index,
|
|
108
|
-
cycleIndex,
|
|
109
|
-
repeatIndex,
|
|
110
|
-
totalIndex: cycleIndex * findBestError.repeatsPerVariant + repeatIndex,
|
|
111
|
-
});
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (useToFindBestError && findBestError) {
|
|
63
|
+
// Store as pending limit for findBestError cycle
|
|
64
|
+
variants.addLimit({ args, error });
|
|
65
|
+
break; // Exit retry loop, continue to next file
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
throw error;
|
|
112
69
|
}
|
|
113
|
-
return true;
|
|
114
70
|
}
|
|
115
71
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
prevCycleVariantsCount = index;
|
|
120
|
-
prevCycleDuration = Date.now() - cycleStartTime;
|
|
121
|
-
cycleIndex++;
|
|
122
|
-
cycleStartTime = Date.now();
|
|
123
|
-
if (cycleIndex >= findBestError.cycles) {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
index = -1;
|
|
127
|
-
variantsIterator = variants[Symbol.iterator]();
|
|
72
|
+
// If no error occurred during replays, the saved variant is no longer reproducible
|
|
73
|
+
// (templates may have changed) - silently skip
|
|
128
74
|
}
|
|
129
75
|
}
|
|
76
|
+
let prevCycleVariantsCount = null;
|
|
77
|
+
let prevCycleDuration = null;
|
|
78
|
+
const startTime = Date.now();
|
|
79
|
+
let cycleStartTime = startTime;
|
|
130
80
|
const abortControllerParallel = new AbortControllerFast();
|
|
131
81
|
const abortSignalParallel = combineAbortSignals(abortSignalExternal, abortControllerParallel.signal);
|
|
132
82
|
const abortSignalAll = abortSignalParallel;
|
|
@@ -142,67 +92,105 @@ function testVariantsRun(testRun, variants, options = {}) {
|
|
|
142
92
|
: new Pool(parallel);
|
|
143
93
|
function onCompleted() {
|
|
144
94
|
if (logCompleted) {
|
|
145
|
-
console.log(`[test-variants] variants: ${index}, iterations: ${iterations}, async: ${iterationsAsync}`);
|
|
95
|
+
console.log(`[test-variants] variants: ${variants.index}, iterations: ${iterations}, async: ${iterationsAsync}`);
|
|
146
96
|
}
|
|
147
97
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
98
|
+
// Main iteration using iterator
|
|
99
|
+
variants.start();
|
|
100
|
+
while (variants.cycleIndex < cycles) {
|
|
101
|
+
let args;
|
|
102
|
+
while (!(abortSignalExternal === null || abortSignalExternal === void 0 ? void 0 : abortSignalExternal.aborted) && (debug || (args = variants.next()) != null)) {
|
|
103
|
+
const _index = variants.index;
|
|
104
|
+
const _args = args;
|
|
105
|
+
const now = (logInterval || GC_Interval) && Date.now();
|
|
106
|
+
if (logInterval && now - prevLogTime >= logInterval) {
|
|
107
|
+
// the log is required to prevent the karma browserNoActivityTimeout
|
|
108
|
+
let log = '';
|
|
109
|
+
const cycleElapsed = now - cycleStartTime;
|
|
110
|
+
const totalElapsed = now - startTime;
|
|
111
|
+
if (findBestError) {
|
|
112
|
+
log += `cycle: ${variants.cycleIndex}, variant: ${variants.index}`;
|
|
113
|
+
let max = variants.count;
|
|
114
|
+
if (max != null) {
|
|
115
|
+
if (prevCycleVariantsCount != null && prevCycleVariantsCount < max) {
|
|
116
|
+
max = prevCycleVariantsCount;
|
|
166
117
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
else {
|
|
178
|
-
estimatedCycleTime = cycleElapsed * max / index;
|
|
179
|
-
}
|
|
180
|
-
log += `/${max} (${formatDuration(cycleElapsed)}/${formatDuration(estimatedCycleTime)})`;
|
|
118
|
+
}
|
|
119
|
+
if (max != null && variants.index > 0) {
|
|
120
|
+
let estimatedCycleTime;
|
|
121
|
+
if (prevCycleDuration != null && prevCycleVariantsCount != null
|
|
122
|
+
&& variants.index < prevCycleVariantsCount && cycleElapsed < prevCycleDuration) {
|
|
123
|
+
const adjustedDuration = prevCycleDuration - cycleElapsed;
|
|
124
|
+
const adjustedCount = prevCycleVariantsCount - variants.index;
|
|
125
|
+
const speedForRemaining = adjustedDuration / adjustedCount;
|
|
126
|
+
const remainingTime = (max - variants.index) * speedForRemaining;
|
|
127
|
+
estimatedCycleTime = cycleElapsed + remainingTime;
|
|
181
128
|
}
|
|
182
129
|
else {
|
|
183
|
-
|
|
130
|
+
estimatedCycleTime = cycleElapsed * max / variants.index;
|
|
184
131
|
}
|
|
132
|
+
log += `/${max} (${formatDuration(cycleElapsed)}/${formatDuration(estimatedCycleTime)})`;
|
|
185
133
|
}
|
|
186
134
|
else {
|
|
187
|
-
log += `
|
|
135
|
+
log += ` (${formatDuration(cycleElapsed)})`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
log += `variant: ${variants.index} (${formatDuration(cycleElapsed)})`;
|
|
140
|
+
}
|
|
141
|
+
log += `, total: ${iterations} (${formatDuration(totalElapsed)})`;
|
|
142
|
+
console.log(log);
|
|
143
|
+
prevLogTime = now;
|
|
144
|
+
}
|
|
145
|
+
if (GC_Iterations && iterations - prevGC_Iterations >= GC_Iterations
|
|
146
|
+
|| GC_IterationsAsync && iterationsAsync - prevGC_IterationsAsync >= GC_IterationsAsync
|
|
147
|
+
|| GC_Interval && now - prevGC_Time >= GC_Interval) {
|
|
148
|
+
prevGC_Iterations = iterations;
|
|
149
|
+
prevGC_IterationsAsync = iterationsAsync;
|
|
150
|
+
prevGC_Time = now;
|
|
151
|
+
yield garbageCollect(1);
|
|
152
|
+
}
|
|
153
|
+
if (abortSignalExternal === null || abortSignalExternal === void 0 ? void 0 : abortSignalExternal.aborted) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (!pool || abortSignalParallel.aborted) {
|
|
157
|
+
try {
|
|
158
|
+
let promiseOrIterations = testRun(_args, _index, abortSignalParallel);
|
|
159
|
+
if (isPromiseLike(promiseOrIterations)) {
|
|
160
|
+
promiseOrIterations = yield promiseOrIterations;
|
|
161
|
+
}
|
|
162
|
+
if (!promiseOrIterations) {
|
|
163
|
+
debug = true;
|
|
164
|
+
abortControllerParallel.abort();
|
|
165
|
+
continue;
|
|
188
166
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
167
|
+
const { iterationsAsync: _iterationsAsync, iterationsSync: _iterationsSync } = promiseOrIterations;
|
|
168
|
+
iterationsAsync += _iterationsAsync;
|
|
169
|
+
iterations += _iterationsSync + _iterationsAsync;
|
|
192
170
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
171
|
+
catch (err) {
|
|
172
|
+
if (errorVariantFilePath) {
|
|
173
|
+
yield saveErrorVariantFile(_args, errorVariantFilePath, saveErrorVariants.argsToJson);
|
|
174
|
+
}
|
|
175
|
+
if (findBestError) {
|
|
176
|
+
variants.addLimit({ error: err });
|
|
177
|
+
debug = false;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
throw err;
|
|
181
|
+
}
|
|
200
182
|
}
|
|
201
|
-
|
|
202
|
-
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
if (!pool.hold(1)) {
|
|
186
|
+
yield pool.holdWait(1);
|
|
203
187
|
}
|
|
204
|
-
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
|
189
|
+
void (() => __awaiter(this, void 0, void 0, function* () {
|
|
205
190
|
try {
|
|
191
|
+
if (abortSignalParallel === null || abortSignalParallel === void 0 ? void 0 : abortSignalParallel.aborted) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
206
194
|
let promiseOrIterations = testRun(_args, _index, abortSignalParallel);
|
|
207
195
|
if (isPromiseLike(promiseOrIterations)) {
|
|
208
196
|
promiseOrIterations = yield promiseOrIterations;
|
|
@@ -210,7 +198,7 @@ function testVariantsRun(testRun, variants, options = {}) {
|
|
|
210
198
|
if (!promiseOrIterations) {
|
|
211
199
|
debug = true;
|
|
212
200
|
abortControllerParallel.abort();
|
|
213
|
-
|
|
201
|
+
return;
|
|
214
202
|
}
|
|
215
203
|
const { iterationsAsync: _iterationsAsync, iterationsSync: _iterationsSync } = promiseOrIterations;
|
|
216
204
|
iterationsAsync += _iterationsAsync;
|
|
@@ -221,78 +209,44 @@ function testVariantsRun(testRun, variants, options = {}) {
|
|
|
221
209
|
yield saveErrorVariantFile(_args, errorVariantFilePath, saveErrorVariants.argsToJson);
|
|
222
210
|
}
|
|
223
211
|
if (findBestError) {
|
|
224
|
-
|
|
225
|
-
error: err,
|
|
226
|
-
args: _args,
|
|
227
|
-
index: _index,
|
|
228
|
-
};
|
|
212
|
+
variants.addLimit({ error: err });
|
|
229
213
|
debug = false;
|
|
230
214
|
}
|
|
231
215
|
else {
|
|
232
216
|
throw err;
|
|
233
217
|
}
|
|
234
218
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (!pool.hold(1)) {
|
|
238
|
-
yield pool.holdWait(1);
|
|
219
|
+
finally {
|
|
220
|
+
void pool.release(1);
|
|
239
221
|
}
|
|
240
|
-
|
|
241
|
-
void (() => __awaiter(this, void 0, void 0, function* () {
|
|
242
|
-
try {
|
|
243
|
-
if (abortSignalParallel === null || abortSignalParallel === void 0 ? void 0 : abortSignalParallel.aborted) {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
let promiseOrIterations = testRun(_args, _index, abortSignalParallel);
|
|
247
|
-
if (isPromiseLike(promiseOrIterations)) {
|
|
248
|
-
promiseOrIterations = yield promiseOrIterations;
|
|
249
|
-
}
|
|
250
|
-
if (!promiseOrIterations) {
|
|
251
|
-
debug = true;
|
|
252
|
-
abortControllerParallel.abort();
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
const { iterationsAsync: _iterationsAsync, iterationsSync: _iterationsSync } = promiseOrIterations;
|
|
256
|
-
iterationsAsync += _iterationsAsync;
|
|
257
|
-
iterations += _iterationsSync + _iterationsAsync;
|
|
258
|
-
}
|
|
259
|
-
catch (err) {
|
|
260
|
-
if (errorVariantFilePath) {
|
|
261
|
-
yield saveErrorVariantFile(_args, errorVariantFilePath, saveErrorVariants.argsToJson);
|
|
262
|
-
}
|
|
263
|
-
if (findBestError) {
|
|
264
|
-
bestError = {
|
|
265
|
-
error: err,
|
|
266
|
-
args: _args,
|
|
267
|
-
index: _index,
|
|
268
|
-
};
|
|
269
|
-
debug = false;
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
throw err;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
finally {
|
|
276
|
-
void pool.release(1);
|
|
277
|
-
}
|
|
278
|
-
}))();
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
if (pool) {
|
|
282
|
-
yield pool.holdWait(parallel);
|
|
283
|
-
void pool.release(parallel);
|
|
222
|
+
}))();
|
|
284
223
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
});
|
|
224
|
+
}
|
|
225
|
+
// Track cycle metrics for logging
|
|
226
|
+
prevCycleVariantsCount = variants.count;
|
|
227
|
+
prevCycleDuration = Date.now() - cycleStartTime;
|
|
228
|
+
cycleStartTime = Date.now();
|
|
229
|
+
variants.start();
|
|
292
230
|
}
|
|
293
|
-
|
|
231
|
+
if (pool) {
|
|
232
|
+
yield pool.holdWait(parallel);
|
|
233
|
+
void pool.release(parallel);
|
|
234
|
+
}
|
|
235
|
+
if (abortSignalAll === null || abortSignalAll === void 0 ? void 0 : abortSignalAll.aborted) {
|
|
236
|
+
throw abortSignalAll.reason;
|
|
237
|
+
}
|
|
238
|
+
onCompleted();
|
|
239
|
+
yield garbageCollect(1);
|
|
240
|
+
// Construct bestError from iterator state
|
|
241
|
+
const bestError = variants.limit
|
|
242
|
+
? {
|
|
243
|
+
error: variants.limit.error,
|
|
244
|
+
args: variants.limit.args,
|
|
245
|
+
index: variants.count,
|
|
246
|
+
}
|
|
247
|
+
: null;
|
|
294
248
|
return {
|
|
295
|
-
iterations
|
|
249
|
+
iterations,
|
|
296
250
|
bestError,
|
|
297
251
|
};
|
|
298
252
|
});
|
|
@@ -15,4 +15,6 @@ export declare type SaveErrorVariantsOptions<Args, SavedArgs = Args> = {
|
|
|
15
15
|
argsToJson?: null | ((args: Args) => string | SavedArgs);
|
|
16
16
|
/** Transform parsed JSON back to args */
|
|
17
17
|
jsonToArgs?: null | ((json: SavedArgs) => Args);
|
|
18
|
+
/** Use saved errors to set findBestError limits instead of throwing on replay */
|
|
19
|
+
useToFindBestError?: null | boolean;
|
|
18
20
|
};
|