@systemfsoftware/stryker-js 7.0.0 → 8.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/CHANGELOG.md +10 -0
- package/dist/config.d.mts +12 -2
- package/dist/index.d.mts +73 -18
- package/dist/index.mjs +2 -2
- package/dist/main.mjs +1487 -544
- package/dist/{mutation-test.cell-689vUxaq.d.mts → mutation-test.cell-Ch-QUquW.d.mts} +1 -1
- package/dist/{output-mode-C5DpB11I.d.mts → output-mode-CE6VNHfH.d.mts} +38 -6
- package/dist/promises.d.mts +2 -2
- package/dist/promises.mjs +1 -1
- package/dist/{src-BY5pFAY5.mjs → src-WMC3thmX.mjs} +458 -283
- package/package.json +9 -7
- package/dist/stryker-config-CTcO2hiS.d.mts +0 -13
|
@@ -54,8 +54,10 @@ import * as RpcClient from "effect/unstable/rpc/RpcClient";
|
|
|
54
54
|
import * as Schedule from "effect/Schedule";
|
|
55
55
|
import * as Data from "effect/Data";
|
|
56
56
|
import * as Equivalence from "effect/Equivalence";
|
|
57
|
+
import * as Metric from "effect/Metric";
|
|
57
58
|
import * as Pool from "effect/Pool";
|
|
58
59
|
import * as Semaphore from "effect/Semaphore";
|
|
60
|
+
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
59
61
|
import { randomBytes } from "@noble/hashes/utils.js";
|
|
60
62
|
import "effect/Sink";
|
|
61
63
|
import * as NodeFileSystem$1 from "@effect/platform-node-shared/NodeFileSystem";
|
|
@@ -7917,6 +7919,24 @@ const instrument = (files, options, basePath) => Effect$1.gen(function* () {
|
|
|
7917
7919
|
mutants
|
|
7918
7920
|
});
|
|
7919
7921
|
});
|
|
7922
|
+
//#endregion
|
|
7923
|
+
//#region ../stryker-js-plugin-interface/dist/index.mjs
|
|
7924
|
+
/** A position inside a file, in the coordinates the wire carries — 0-based line and column. */
|
|
7925
|
+
const CheckerPositionWire = S.Struct({
|
|
7926
|
+
line: S.Finite,
|
|
7927
|
+
column: S.Finite
|
|
7928
|
+
});
|
|
7929
|
+
const CheckerLocationWire = S.Struct({
|
|
7930
|
+
start: CheckerPositionWire,
|
|
7931
|
+
end: CheckerPositionWire
|
|
7932
|
+
});
|
|
7933
|
+
const CheckerMutantWire = S.Struct({
|
|
7934
|
+
id: S.NonEmptyString,
|
|
7935
|
+
fileName: S.NonEmptyString,
|
|
7936
|
+
mutatorName: S.NonEmptyString,
|
|
7937
|
+
replacement: S.String,
|
|
7938
|
+
location: CheckerLocationWire
|
|
7939
|
+
});
|
|
7920
7940
|
S.Literals(["passed", "compileError"]);
|
|
7921
7941
|
const CheckResultSchema$1 = S.Union([S.Struct({ status: S.Literal("passed") }), S.Struct({
|
|
7922
7942
|
status: S.Literal("compileError"),
|
|
@@ -8185,7 +8205,7 @@ const TestRunnerDryRunRequest = S.Struct({ options: DryRunOptionsSchema });
|
|
|
8185
8205
|
const TestRunnerMutantRunRequest = S.Struct({ options: MutantRunOptionsSchema });
|
|
8186
8206
|
const CheckerRequest = S.Struct({
|
|
8187
8207
|
checkerName: S.String,
|
|
8188
|
-
mutants: S.Array(
|
|
8208
|
+
mutants: S.Array(CheckerMutantWire)
|
|
8189
8209
|
});
|
|
8190
8210
|
const CheckerCheckResult = S.Record(S.String, CheckResultSchema$1);
|
|
8191
8211
|
const CheckerGroupResult = S.Array(S.Array(S.String));
|
|
@@ -9323,6 +9343,39 @@ const PreviousFileSchema = S.Struct({
|
|
|
9323
9343
|
const PreviousTestFileSchema = S.Struct({ source: S.optional(S.String) });
|
|
9324
9344
|
const PreviousFilesSchema = S.Record(S.String, PreviousFileSchema);
|
|
9325
9345
|
const PreviousTestFilesSchema = S.Record(S.String, PreviousTestFileSchema);
|
|
9346
|
+
//#endregion
|
|
9347
|
+
//#region src/mutant-result-mapping.ts
|
|
9348
|
+
const toSchemaPosition = (pos) => ({
|
|
9349
|
+
column: pos.column + 1,
|
|
9350
|
+
line: pos.line + 1
|
|
9351
|
+
});
|
|
9352
|
+
const toSchemaLocation = (location) => ({
|
|
9353
|
+
start: toSchemaPosition(location.start),
|
|
9354
|
+
end: toSchemaPosition(location.end)
|
|
9355
|
+
});
|
|
9356
|
+
const checkStatusToMutantStatus = (_status) => "CompileError";
|
|
9357
|
+
const mutantResult = (mutant, status, outcome = {}) => ({
|
|
9358
|
+
_tag: "Mutant",
|
|
9359
|
+
id: mutant.id,
|
|
9360
|
+
fileName: mutant.fileName,
|
|
9361
|
+
mutatorName: mutant.mutatorName,
|
|
9362
|
+
replacement: mutant.replacement,
|
|
9363
|
+
location: toSchemaLocation(mutant.location),
|
|
9364
|
+
status,
|
|
9365
|
+
coveredBy: mutant.coveredBy,
|
|
9366
|
+
static: mutant.static,
|
|
9367
|
+
testsCompleted: mutant.testsCompleted,
|
|
9368
|
+
description: mutant.description,
|
|
9369
|
+
...outcome
|
|
9370
|
+
});
|
|
9371
|
+
const mapRunResult = (mutant, result) => Match.value(result).pipe(Match.discriminator("status")("error", (errored) => mutantResult(mutant, "RuntimeError", { statusReason: errored.errorMessage })), Match.discriminator("status")("killed", (killed) => mutantResult(mutant, "Killed", {
|
|
9372
|
+
testsCompleted: killed.nrOfTests,
|
|
9373
|
+
killedBy: [...killed.killedBy],
|
|
9374
|
+
statusReason: killed.failureMessage
|
|
9375
|
+
})), Match.discriminator("status")("timeout", (timedOut) => {
|
|
9376
|
+
if (timedOut.reason === void 0) return mutantResult(mutant, "Timeout");
|
|
9377
|
+
return mutantResult(mutant, "Timeout", { statusReason: timedOut.reason });
|
|
9378
|
+
}), Match.discriminator("status")("survived", (survived) => mutantResult(mutant, "Survived", { testsCompleted: survived.nrOfTests })), Match.exhaustive);
|
|
9326
9379
|
const ZERO = 0;
|
|
9327
9380
|
const firstDefined = (first, second) => Option.getOrElse(Option.fromNullishOr(first), () => second);
|
|
9328
9381
|
const staticField = (isStatic) => Option.match(Option.fromUndefinedOr(isStatic), {
|
|
@@ -9587,6 +9640,18 @@ const decidePlans = (mutants, testCoverage, options, timeOverheadMS, globalTestF
|
|
|
9587
9640
|
const command = coverageToCommand(mutants, testCoverage, options, timeOverheadMS, globalTestFilter, sandboxFileByName);
|
|
9588
9641
|
return makeMutantTestPlanner(command);
|
|
9589
9642
|
};
|
|
9643
|
+
const isRunPlan = (plan) => plan.plan === "Run";
|
|
9644
|
+
const isEarlyPlan = (plan) => !isRunPlan(plan);
|
|
9645
|
+
const earlyResultOf = (plan) => Object.assign({}, plan.mutant, {
|
|
9646
|
+
location: toSchemaLocation(plan.mutant.location),
|
|
9647
|
+
status: plan.mutant.status ?? "Ignored"
|
|
9648
|
+
});
|
|
9649
|
+
const partitionRunPlans = (plans) => ({
|
|
9650
|
+
runPlans: plans.filter(isRunPlan),
|
|
9651
|
+
earlyResults: plans.filter(isEarlyPlan).map(earlyResultOf)
|
|
9652
|
+
});
|
|
9653
|
+
const byReloadEnvironment = (left, right) => Number(left.runOptions.reloadEnvironment) - Number(right.runOptions.reloadEnvironment);
|
|
9654
|
+
const sortRunPlans = (plans) => [...plans].sort(byReloadEnvironment);
|
|
9590
9655
|
const REMEMBERED_STATUS = /* @__PURE__ */ new Set([
|
|
9591
9656
|
"Killed",
|
|
9592
9657
|
"Survived",
|
|
@@ -12972,6 +13037,29 @@ const admitMutationTest = Workflow.make(MutationTestCommand, (command) => Match.
|
|
|
12972
13037
|
allowEmpty: true
|
|
12973
13038
|
}, () => Result.succeed(MutationTestNoTests.make({}))), Match.orElse(() => Result.succeed(MutationTestProceed.make({})))));
|
|
12974
13039
|
//#endregion
|
|
13040
|
+
//#region src/checker-mutant-wire.ts
|
|
13041
|
+
const CheckerMutantFromMutant = Mutant.pipe(S.decodeTo(CheckerMutantWire, SchemaTransformation.transform({
|
|
13042
|
+
decode: (m) => ({
|
|
13043
|
+
id: m.id,
|
|
13044
|
+
fileName: m.fileName,
|
|
13045
|
+
mutatorName: m.mutatorName,
|
|
13046
|
+
replacement: m.replacement,
|
|
13047
|
+
location: m.location
|
|
13048
|
+
}),
|
|
13049
|
+
encode: (w) => Mutant.make({
|
|
13050
|
+
id: w.id,
|
|
13051
|
+
fileName: w.fileName,
|
|
13052
|
+
mutatorName: w.mutatorName,
|
|
13053
|
+
replacement: w.replacement,
|
|
13054
|
+
location: w.location
|
|
13055
|
+
})
|
|
13056
|
+
})));
|
|
13057
|
+
const wireRecordOf = (mutant) => Result.mapError(S.decodeUnknownResult(CheckerMutantFromMutant)(mutant), (error) => ({
|
|
13058
|
+
id: mutant.id,
|
|
13059
|
+
fileName: mutant.fileName,
|
|
13060
|
+
reason: error.message
|
|
13061
|
+
}));
|
|
13062
|
+
//#endregion
|
|
12975
13063
|
//#region src/admit-checker-answer.workflow.ts
|
|
12976
13064
|
var CheckerAnsweredUnrequested = class extends S.TaggedError()("CheckerAnsweredUnrequested", {
|
|
12977
13065
|
checkerName: S.String,
|
|
@@ -13039,6 +13127,19 @@ const evaluateCheckResult = (command) => {
|
|
|
13039
13127
|
};
|
|
13040
13128
|
const admitCheckerAnswer = Workflow.make(CheckerCommand, (command) => Match.value(command.phase).pipe(Match.when("group", () => evaluateGroup(command)), Match.when("check", () => evaluateCheckResult(command)), Match.exhaustive));
|
|
13041
13129
|
//#endregion
|
|
13130
|
+
//#region src/metrics.ts
|
|
13131
|
+
const checkerDuration = Metric.timer("stryker.checker.duration", { description: "Checker worker RPC duration in milliseconds" });
|
|
13132
|
+
const checkerMutantsChecked = Metric.counter("stryker.checker.mutants.checked", { description: "Total number of mutants a checker worker answered for" });
|
|
13133
|
+
const checkerMutantsSkipped = Metric.counter("stryker.checker.mutants.skipped", { description: "Total number of mutants dropped because they cannot be described to a checker" });
|
|
13134
|
+
const checkerRpcFailures = Metric.counter("stryker.checker.rpc_failures", {
|
|
13135
|
+
description: "Checker worker RPC calls that did not complete, excluding interruptions",
|
|
13136
|
+
incremental: true
|
|
13137
|
+
});
|
|
13138
|
+
const checkerProcessCrashes = Metric.counter("stryker.checker.process_crashes", {
|
|
13139
|
+
description: "Checker worker processes that never became usable",
|
|
13140
|
+
incremental: true
|
|
13141
|
+
});
|
|
13142
|
+
//#endregion
|
|
13042
13143
|
//#region src/Checker.ts
|
|
13043
13144
|
/**
|
|
13044
13145
|
* Checker — the Checker capability.
|
|
@@ -13154,16 +13255,20 @@ const makeCheckerChildProcess = (params) => Effect$1.gen(function* () {
|
|
|
13154
13255
|
execArgv: [...params.execArgv],
|
|
13155
13256
|
optionsJson,
|
|
13156
13257
|
tempDirPrefix: "stryker-checker-"
|
|
13157
|
-
}).pipe(Effect$1.mapError((error) => Match.value(error).pipe(Match.tag("ChildProcessCrashedError", "OutOfMemoryError", (crash) => crash), Match.tag("WorkerBootTimeoutError", () => crashed(`Checker worker failed to start: its boot window closed before it accepted the RPC connection`)), Match.exhaustive)));
|
|
13258
|
+
}).pipe(Effect$1.mapError((error) => Match.value(error).pipe(Match.tag("ChildProcessCrashedError", "OutOfMemoryError", (crash) => crash), Match.tag("WorkerBootTimeoutError", () => crashed(`Checker worker failed to start: its boot window closed before it accepted the RPC connection`)), Match.exhaustive)), Effect$1.tapError(() => Metric.update(checkerProcessCrashes, 1)));
|
|
13259
|
+
const recordCheckerCall = (spanName, checkerName, mutants, call) => call.pipe(Effect$1.withSpan(spanName, { attributes: {
|
|
13260
|
+
"stryker.checker.name": checkerName,
|
|
13261
|
+
"stryker.mutants.count": mutants.length
|
|
13262
|
+
} }), Effect$1.timed, Effect$1.onExit((exit) => Match.value(exit).pipe(Match.tag("Success", () => Metric.update(checkerMutantsChecked, mutants.length)), Match.tag("Failure", (failure) => Match.value(Cause.hasInterruptsOnly(failure.cause)).pipe(Match.when(true, () => Effect$1.void), Match.orElse(() => Metric.update(checkerRpcFailures, 1)))), Match.exhaustive)), Effect$1.tap(([duration]) => Metric.update(checkerDuration, duration)), Effect$1.map(([, result]) => result), Effect$1.mapError((error) => crashed(error.message)));
|
|
13158
13263
|
return {
|
|
13159
|
-
check: (checkerName, mutants) => client.check({
|
|
13264
|
+
check: (checkerName, mutants) => recordCheckerCall("stryker.checker.check", checkerName, mutants, client.check({
|
|
13160
13265
|
checkerName,
|
|
13161
13266
|
mutants: [...mutants]
|
|
13162
|
-
})
|
|
13163
|
-
group: (checkerName, mutants) => client.group({
|
|
13267
|
+
})),
|
|
13268
|
+
group: (checkerName, mutants) => recordCheckerCall("stryker.checker.group", checkerName, mutants, client.group({
|
|
13164
13269
|
checkerName,
|
|
13165
13270
|
mutants: [...mutants]
|
|
13166
|
-
})
|
|
13271
|
+
}))
|
|
13167
13272
|
};
|
|
13168
13273
|
});
|
|
13169
13274
|
const createCheckerFactory = (options, fileDescriptions, workerEntrypoint, idGenerator, workingDirectory) => makeCheckerChildProcess({
|
|
@@ -13194,6 +13299,69 @@ const writeGroupOutcome = (plans, checkerName, outcome) => Result.match(outcome,
|
|
|
13194
13299
|
onFailure: (error) => Effect$1.fail(error),
|
|
13195
13300
|
onSuccess: (decision) => writeGroupDecision(plans, checkerName, decision)
|
|
13196
13301
|
});
|
|
13302
|
+
const partitionMutantsForWire = (plans) => {
|
|
13303
|
+
const wire = [];
|
|
13304
|
+
const undescribable = [];
|
|
13305
|
+
plans.forEach((plan) => Result.match(wireRecordOf(plan.mutant), {
|
|
13306
|
+
onSuccess: (wireMutant) => {
|
|
13307
|
+
wire.push(wireMutant);
|
|
13308
|
+
},
|
|
13309
|
+
onFailure: (refused) => {
|
|
13310
|
+
undescribable.push(refused);
|
|
13311
|
+
}
|
|
13312
|
+
}));
|
|
13313
|
+
return {
|
|
13314
|
+
wire,
|
|
13315
|
+
undescribable
|
|
13316
|
+
};
|
|
13317
|
+
};
|
|
13318
|
+
const compileErrorAnswersOf = (undescribable) => Object.fromEntries(undescribable.map((mutant) => [mutant.id, {
|
|
13319
|
+
status: "compileError",
|
|
13320
|
+
reason: mutant.reason
|
|
13321
|
+
}]));
|
|
13322
|
+
const singletonGroupsOf = (undescribable) => undescribable.map((mutant) => [mutant.id]);
|
|
13323
|
+
const undescribableIdsOf = (undescribable) => new Set(undescribable.map((mutant) => mutant.id));
|
|
13324
|
+
const lookupOf = (partitioned) => ({
|
|
13325
|
+
wireById: new Map(partitioned.wire.map((mutant) => [mutant.id, mutant])),
|
|
13326
|
+
undescribableById: new Map(partitioned.undescribable.map((mutant) => [mutant.id, mutant]))
|
|
13327
|
+
});
|
|
13328
|
+
const selectedFromLookup = (plans, lookup) => {
|
|
13329
|
+
const wire = [];
|
|
13330
|
+
const undescribable = [];
|
|
13331
|
+
plans.forEach((plan) => {
|
|
13332
|
+
Option.match(Option.fromUndefinedOr(lookup.wireById.get(plan.mutant.id)), {
|
|
13333
|
+
onSome: (wired) => {
|
|
13334
|
+
wire.push(wired);
|
|
13335
|
+
},
|
|
13336
|
+
onNone: () => Option.match(Option.fromUndefinedOr(lookup.undescribableById.get(plan.mutant.id)), {
|
|
13337
|
+
onSome: (refused) => {
|
|
13338
|
+
undescribable.push(refused);
|
|
13339
|
+
},
|
|
13340
|
+
onNone: () => {
|
|
13341
|
+
Result.match(wireRecordOf(plan.mutant), {
|
|
13342
|
+
onSuccess: (wired) => {
|
|
13343
|
+
wire.push(wired);
|
|
13344
|
+
},
|
|
13345
|
+
onFailure: (refused) => {
|
|
13346
|
+
undescribable.push(refused);
|
|
13347
|
+
}
|
|
13348
|
+
});
|
|
13349
|
+
}
|
|
13350
|
+
})
|
|
13351
|
+
});
|
|
13352
|
+
});
|
|
13353
|
+
return {
|
|
13354
|
+
wire,
|
|
13355
|
+
undescribable
|
|
13356
|
+
};
|
|
13357
|
+
};
|
|
13358
|
+
const SKIPPED_IDS_IN_WARNING = 5;
|
|
13359
|
+
const skippedIdsOf = (undescribable) => `${undescribable.slice(0, SKIPPED_IDS_IN_WARNING).map((item) => item.id).join(", ")}${Option.match(Option.liftPredicate(undescribable.length, (count) => count > SKIPPED_IDS_IN_WARNING), {
|
|
13360
|
+
onNone: () => "",
|
|
13361
|
+
onSome: (count) => `, +${count - SKIPPED_IDS_IN_WARNING} more`
|
|
13362
|
+
})}`;
|
|
13363
|
+
const refusalReasonsOf = (undescribable) => [...new Set(undescribable.map((mutant) => mutant.reason))].join("; ");
|
|
13364
|
+
const logSkippedMutants = (checkerName, undescribable) => Match.value(undescribable.length).pipe(Match.when(0, () => Effect$1.void), Match.orElse(() => Effect$1.logWarning(`Checker "${checkerName}" skipped ${undescribable.length} mutant(s) it cannot be told about: ${refusalReasonsOf(undescribable)} (${skippedIdsOf(undescribable)})`)));
|
|
13197
13365
|
/**
|
|
13198
13366
|
* Ask a checker about run plans and get run plans back.
|
|
13199
13367
|
*
|
|
@@ -13202,13 +13370,26 @@ const writeGroupOutcome = (plans, checkerName, outcome) => Result.match(outcome,
|
|
|
13202
13370
|
* project the plans down, call the checker, and hand the answers to the pure decision
|
|
13203
13371
|
* that joins them back. The join is where the work is, and it is pure.
|
|
13204
13372
|
*/
|
|
13205
|
-
const checkPlans = (checker, checkerName, plans) => {
|
|
13373
|
+
const checkPlans = (checker, checkerName, plans, lookup) => {
|
|
13206
13374
|
return Cell.layer({
|
|
13207
|
-
read: (command) =>
|
|
13208
|
-
|
|
13209
|
-
|
|
13210
|
-
|
|
13211
|
-
|
|
13375
|
+
read: (command) => Effect$1.gen(function* () {
|
|
13376
|
+
const partitioned = Option.match(Option.fromUndefinedOr(command.lookup), {
|
|
13377
|
+
onSome: (known) => selectedFromLookup(command.plans, known),
|
|
13378
|
+
onNone: () => partitionMutantsForWire(command.plans)
|
|
13379
|
+
});
|
|
13380
|
+
yield* logSkippedMutants(command.checkerName, partitioned.undescribable);
|
|
13381
|
+
if (partitioned.undescribable.length > 0) yield* Metric.update(checkerMutantsSkipped, partitioned.undescribable.length);
|
|
13382
|
+
yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
|
|
13383
|
+
const answers = yield* command.checker.check(command.checkerName, partitioned.wire);
|
|
13384
|
+
return {
|
|
13385
|
+
checkerName: command.checkerName,
|
|
13386
|
+
requestedIds: command.plans.map((plan) => plan.mutant.id),
|
|
13387
|
+
answers: {
|
|
13388
|
+
...compileErrorAnswersOf(partitioned.undescribable),
|
|
13389
|
+
...answers
|
|
13390
|
+
}
|
|
13391
|
+
};
|
|
13392
|
+
}),
|
|
13212
13393
|
decode: (raw) => Result.succeed(CheckerCommand.make({
|
|
13213
13394
|
checkerName: raw.checkerName,
|
|
13214
13395
|
requestedIds: [...raw.requestedIds],
|
|
@@ -13221,19 +13402,29 @@ const checkPlans = (checker, checkerName, plans) => {
|
|
|
13221
13402
|
}).run({
|
|
13222
13403
|
checker,
|
|
13223
13404
|
checkerName,
|
|
13224
|
-
plans
|
|
13405
|
+
plans,
|
|
13406
|
+
lookup
|
|
13225
13407
|
});
|
|
13226
13408
|
};
|
|
13227
13409
|
/**
|
|
13228
13410
|
* Ask a checker how to group run plans, and get groups of run plans back.
|
|
13229
13411
|
*/
|
|
13230
|
-
const groupPlans = (checker, checkerName, plans) => {
|
|
13412
|
+
const groupPlans = (checker, checkerName, plans, lookup) => {
|
|
13231
13413
|
return Cell.layer({
|
|
13232
|
-
read: (command) =>
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13414
|
+
read: (command) => Effect$1.gen(function* () {
|
|
13415
|
+
const partitioned = Option.match(Option.fromUndefinedOr(command.lookup), {
|
|
13416
|
+
onSome: (known) => selectedFromLookup(command.plans, known),
|
|
13417
|
+
onNone: () => partitionMutantsForWire(command.plans)
|
|
13418
|
+
});
|
|
13419
|
+
yield* Effect$1.annotateCurrentSpan({ "stryker.checker.skipped_mutants_count": partitioned.undescribable.length });
|
|
13420
|
+
const undescribableIds = undescribableIdsOf(partitioned.undescribable);
|
|
13421
|
+
const withoutSkipped = (yield* command.checker.group(command.checkerName, partitioned.wire)).map((group) => group.filter((id) => !undescribableIds.has(id))).filter((group) => group.length > 0);
|
|
13422
|
+
return {
|
|
13423
|
+
checkerName: command.checkerName,
|
|
13424
|
+
requestedIds: command.plans.map((plan) => plan.mutant.id),
|
|
13425
|
+
idGroups: [...singletonGroupsOf(partitioned.undescribable), ...withoutSkipped]
|
|
13426
|
+
};
|
|
13427
|
+
}),
|
|
13237
13428
|
decode: (raw) => Result.succeed(CheckerCommand.make({
|
|
13238
13429
|
checkerName: raw.checkerName,
|
|
13239
13430
|
requestedIds: [...raw.requestedIds],
|
|
@@ -13246,47 +13437,16 @@ const groupPlans = (checker, checkerName, plans) => {
|
|
|
13246
13437
|
}).run({
|
|
13247
13438
|
checker,
|
|
13248
13439
|
checkerName,
|
|
13249
|
-
plans
|
|
13440
|
+
plans,
|
|
13441
|
+
lookup
|
|
13250
13442
|
});
|
|
13251
13443
|
};
|
|
13252
13444
|
const checkGroupedPlans = (checker, checkerName, plans) => Effect$1.gen(function* () {
|
|
13253
|
-
const
|
|
13254
|
-
|
|
13445
|
+
const lookup = lookupOf(partitionMutantsForWire(plans));
|
|
13446
|
+
const groups = yield* groupPlans(checker, checkerName, plans, lookup);
|
|
13447
|
+
return (yield* Effect$1.forEach(groups, (group) => checkPlans(checker, checkerName, group, lookup), { concurrency: 1 })).flat();
|
|
13255
13448
|
});
|
|
13256
13449
|
//#endregion
|
|
13257
|
-
//#region src/mutant-result-mapping.ts
|
|
13258
|
-
const toSchemaPosition = (pos) => ({
|
|
13259
|
-
column: pos.column + 1,
|
|
13260
|
-
line: pos.line + 1
|
|
13261
|
-
});
|
|
13262
|
-
const toSchemaLocation = (location) => ({
|
|
13263
|
-
start: toSchemaPosition(location.start),
|
|
13264
|
-
end: toSchemaPosition(location.end)
|
|
13265
|
-
});
|
|
13266
|
-
const checkStatusToMutantStatus = (_status) => "CompileError";
|
|
13267
|
-
const mutantResult = (mutant, status, outcome = {}) => ({
|
|
13268
|
-
_tag: "Mutant",
|
|
13269
|
-
id: mutant.id,
|
|
13270
|
-
fileName: mutant.fileName,
|
|
13271
|
-
mutatorName: mutant.mutatorName,
|
|
13272
|
-
replacement: mutant.replacement,
|
|
13273
|
-
location: toSchemaLocation(mutant.location),
|
|
13274
|
-
status,
|
|
13275
|
-
coveredBy: mutant.coveredBy,
|
|
13276
|
-
static: mutant.static,
|
|
13277
|
-
testsCompleted: mutant.testsCompleted,
|
|
13278
|
-
description: mutant.description,
|
|
13279
|
-
...outcome
|
|
13280
|
-
});
|
|
13281
|
-
const mapRunResult = (mutant, result) => Match.value(result).pipe(Match.discriminator("status")("error", (errored) => mutantResult(mutant, "RuntimeError", { statusReason: errored.errorMessage })), Match.discriminator("status")("killed", (killed) => mutantResult(mutant, "Killed", {
|
|
13282
|
-
testsCompleted: killed.nrOfTests,
|
|
13283
|
-
killedBy: [...killed.killedBy],
|
|
13284
|
-
statusReason: killed.failureMessage
|
|
13285
|
-
})), Match.discriminator("status")("timeout", (timedOut) => {
|
|
13286
|
-
if (timedOut.reason === void 0) return mutantResult(mutant, "Timeout");
|
|
13287
|
-
return mutantResult(mutant, "Timeout", { statusReason: timedOut.reason });
|
|
13288
|
-
}), Match.discriminator("status")("survived", (survived) => mutantResult(mutant, "Survived", { testsCompleted: survived.nrOfTests })), Match.exhaustive);
|
|
13289
|
-
//#endregion
|
|
13290
13450
|
//#region src/calculate-metrics.ts
|
|
13291
13451
|
const countStatus = (mutants, status) => mutants.filter((mutant) => mutant.status === status).length;
|
|
13292
13452
|
const percentage = (numerator, denominator) => numerator / denominator * 100;
|
|
@@ -13471,7 +13631,7 @@ const assembleTestFiles = (input) => {
|
|
|
13471
13631
|
//#region package.json
|
|
13472
13632
|
var package_default = {
|
|
13473
13633
|
name: "@systemfsoftware/stryker-js",
|
|
13474
|
-
version: "
|
|
13634
|
+
version: "8.0.0",
|
|
13475
13635
|
repository: {
|
|
13476
13636
|
"type": "git",
|
|
13477
13637
|
"url": "git+https://github.com/systemfsoftware/stryker-js-effect.git",
|
|
@@ -13503,7 +13663,9 @@ var package_default = {
|
|
|
13503
13663
|
"@effect/platform-node-shared": "catalog:",
|
|
13504
13664
|
"@noble/hashes": "catalog:",
|
|
13505
13665
|
"@opentelemetry/api": "^1.9.0",
|
|
13666
|
+
"@opentelemetry/exporter-metrics-otlp-http": "^0.208.0",
|
|
13506
13667
|
"@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
|
|
13668
|
+
"@opentelemetry/sdk-metrics": "^2.2.0",
|
|
13507
13669
|
"@opentelemetry/sdk-trace-base": "^2.2.0",
|
|
13508
13670
|
"@systemfsoftware/effect-cell-types": "catalog:"
|
|
13509
13671
|
},
|
|
@@ -13925,25 +14087,6 @@ const rememberedResultsOf = (mutants, remembered) => {
|
|
|
13925
14087
|
onSome: (mutant) => [rememberedResultOf(mutant, entry)]
|
|
13926
14088
|
}));
|
|
13927
14089
|
};
|
|
13928
|
-
const isRunPlan = (plan) => plan.plan === "Run";
|
|
13929
|
-
const earlyResultOf = (plan) => Object.assign({}, plan.mutant, {
|
|
13930
|
-
location: toSchemaLocation(plan.mutant.location),
|
|
13931
|
-
status: plan.mutant.status ?? "Ignored"
|
|
13932
|
-
});
|
|
13933
|
-
const collectPlan = (plan, coveredPlans, earlyResults) => Match.value(plan).pipe(Match.when(isRunPlan, (runPlan) => {
|
|
13934
|
-
coveredPlans.push(runPlan);
|
|
13935
|
-
}), Match.orElse((earlyPlan) => {
|
|
13936
|
-
earlyResults.push(earlyResultOf(earlyPlan));
|
|
13937
|
-
}));
|
|
13938
|
-
const partitionPlans = (plans) => {
|
|
13939
|
-
const coveredPlans = [];
|
|
13940
|
-
const earlyResults = [];
|
|
13941
|
-
plans.forEach((plan) => collectPlan(plan, coveredPlans, earlyResults));
|
|
13942
|
-
return {
|
|
13943
|
-
coveredPlans,
|
|
13944
|
-
earlyResults
|
|
13945
|
-
};
|
|
13946
|
-
};
|
|
13947
14090
|
const VALID_MUTANT_STATUS_SET = /* @__PURE__ */ new Set([
|
|
13948
14091
|
"Killed",
|
|
13949
14092
|
"Survived",
|
|
@@ -13961,6 +14104,8 @@ const toReportedMutant = (mutant) => Object.assign(mutant, {
|
|
|
13961
14104
|
coveredBy: mutant.coveredBy,
|
|
13962
14105
|
static: mutant.static
|
|
13963
14106
|
});
|
|
14107
|
+
const CHECKER_ACQUIRE_RETRIES = 2;
|
|
14108
|
+
const isCheckerCrash = (error) => Match.value(error).pipe(Match.tag("ChildProcessCrashedError", "OutOfMemoryError", () => true), Match.orElse(() => false));
|
|
13964
14109
|
const makeCheckerPool = (prev, idGenerator) => Match.value(prev.options.checkers.length === 0).pipe(Match.when(true, () => Effect$1.as(Effect$1.void, void 0)), Match.orElse(() => Pool.make({
|
|
13965
14110
|
acquire: Effect$1.forEach(prev.options.checkers, (checker) => Effect$1.gen(function* () {
|
|
13966
14111
|
const resolved = yield* resolveConfiguredWorkerSpawn({
|
|
@@ -13971,7 +14116,10 @@ const makeCheckerPool = (prev, idGenerator) => Match.value(prev.options.checkers
|
|
|
13971
14116
|
const service = yield* createCheckerFactory({
|
|
13972
14117
|
...prev.options,
|
|
13973
14118
|
checkers: [checker]
|
|
13974
|
-
}, prev.project.fileDescriptions, resolved.spawn.entrypoint, idGenerator, prev.sandbox.workingDirectory)
|
|
14119
|
+
}, prev.project.fileDescriptions, resolved.spawn.entrypoint, idGenerator, prev.sandbox.workingDirectory).pipe(Effect$1.retry({
|
|
14120
|
+
times: CHECKER_ACQUIRE_RETRIES,
|
|
14121
|
+
while: isCheckerCrash
|
|
14122
|
+
}));
|
|
13975
14123
|
return {
|
|
13976
14124
|
checkerName: resolved.name,
|
|
13977
14125
|
checker: service
|
|
@@ -13994,6 +14142,28 @@ const checkPlansWithConfiguredCheckers = (checkerPool, plans, reporting) => Opti
|
|
|
13994
14142
|
}).pipe(Effect$1.as(checked.filter(([, result]) => result.status === "passed").map(([plan]) => plan))))));
|
|
13995
14143
|
}))
|
|
13996
14144
|
});
|
|
14145
|
+
const isPlannable = (mutant) => Result.isSuccess(wireRecordOf(mutant));
|
|
14146
|
+
const DROPPED_IDS_IN_WARNING = 5;
|
|
14147
|
+
const partitionPlannable = (mutants) => {
|
|
14148
|
+
const plannable = [];
|
|
14149
|
+
const dropped = [];
|
|
14150
|
+
mutants.forEach((mutant) => {
|
|
14151
|
+
if (isPlannable(mutant)) plannable.push(mutant);
|
|
14152
|
+
else dropped.push(mutant);
|
|
14153
|
+
});
|
|
14154
|
+
return {
|
|
14155
|
+
plannable,
|
|
14156
|
+
dropped
|
|
14157
|
+
};
|
|
14158
|
+
};
|
|
14159
|
+
const droppedIdsOf = (dropped) => `${dropped.slice(0, DROPPED_IDS_IN_WARNING).map((mutant) => mutant.id).join(", ")}${Option.match(Option.liftPredicate(dropped.length, (count) => count > DROPPED_IDS_IN_WARNING), {
|
|
14160
|
+
onNone: () => "",
|
|
14161
|
+
onSome: (count) => `, +${count - DROPPED_IDS_IN_WARNING} more`
|
|
14162
|
+
})}`;
|
|
14163
|
+
const reportDroppedMutants = (dropped) => Match.value(dropped.length).pipe(Match.when(0, () => Effect$1.void), Match.orElse(() => Effect$1.gen(function* () {
|
|
14164
|
+
yield* Metric.update(checkerMutantsSkipped, dropped.length);
|
|
14165
|
+
yield* Effect$1.logWarning(`${dropped.length} mutant(s) cannot be described to a checker and were left out of the run (${droppedIdsOf(dropped)})`);
|
|
14166
|
+
})));
|
|
13997
14167
|
const mutationTestCell$1 = Cell.layer({
|
|
13998
14168
|
read: (command) => Effect$1.gen(function* () {
|
|
13999
14169
|
yield* Scope.Scope;
|
|
@@ -14007,156 +14177,147 @@ const mutationTestCell$1 = Cell.layer({
|
|
|
14007
14177
|
})),
|
|
14008
14178
|
decide: admitMutationTest,
|
|
14009
14179
|
encode: (outcome) => outcome,
|
|
14010
|
-
write: (outcome, raw) =>
|
|
14011
|
-
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
|
|
14019
|
-
|
|
14020
|
-
|
|
14021
|
-
|
|
14022
|
-
|
|
14023
|
-
|
|
14024
|
-
|
|
14025
|
-
|
|
14026
|
-
yield*
|
|
14027
|
-
|
|
14028
|
-
|
|
14029
|
-
}));
|
|
14030
|
-
yield* Effect$1.logInfo("The dry-run has been completed successfully. No mutations have been executed.");
|
|
14031
|
-
return {
|
|
14032
|
-
results: [],
|
|
14033
|
-
verdict: null
|
|
14034
|
-
};
|
|
14035
|
-
})), Match.tag("MutationTestNoTests", () => Effect$1.gen(function* () {
|
|
14036
|
-
const env = yield* RunEnvironment;
|
|
14037
|
-
const queue = yield* RunEvents;
|
|
14038
|
-
const now = yield* Clock.currentTimeMillis;
|
|
14039
|
-
const elapsed = Duration.millis(now - env.runStartedAt);
|
|
14040
|
-
yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
|
|
14041
|
-
const nowEmit = yield* Clock.currentTimeMillis;
|
|
14042
|
-
yield* Queue.offer(queue, PhaseEntered.make({
|
|
14043
|
-
phase: "mutation-test",
|
|
14044
|
-
elapsedMs: nowEmit - env.runStartedAt
|
|
14045
|
-
}));
|
|
14046
|
-
return {
|
|
14047
|
-
results: [],
|
|
14048
|
-
verdict: null
|
|
14049
|
-
};
|
|
14050
|
-
})), Match.tag("MutationTestProceed", () => Effect$1.gen(function* () {
|
|
14051
|
-
const prev = raw.prev;
|
|
14052
|
-
const env = yield* RunEnvironment;
|
|
14053
|
-
yield* Effect$1.gen(function* () {
|
|
14180
|
+
write: (outcome, raw) => {
|
|
14181
|
+
const { dropped, plannable: plannableMutants } = partitionPlannable(raw.prev.mutants);
|
|
14182
|
+
return withPhaseSpan("mutationTest", {
|
|
14183
|
+
mutantCount: raw.prev.mutants.length,
|
|
14184
|
+
skippedMutantCount: dropped.length,
|
|
14185
|
+
testCount: raw.prev.dryRunResult.tests.length
|
|
14186
|
+
}, () => Effect$1.gen(function* () {
|
|
14187
|
+
yield* reportDroppedMutants(dropped);
|
|
14188
|
+
const decision = yield* Result.match(outcome, {
|
|
14189
|
+
onFailure: (err) => Effect$1.fail(StageError.make({
|
|
14190
|
+
stage: err.stage,
|
|
14191
|
+
reason: err.reason,
|
|
14192
|
+
cause: err
|
|
14193
|
+
})),
|
|
14194
|
+
onSuccess: (d) => Effect$1.succeed(d)
|
|
14195
|
+
});
|
|
14196
|
+
return yield* Match.value(decision).pipe(Match.tag("MutationTestDryRunOnly", () => Effect$1.gen(function* () {
|
|
14197
|
+
const env = yield* RunEnvironment;
|
|
14198
|
+
const queue = yield* RunEvents;
|
|
14054
14199
|
const nowEmit = yield* Clock.currentTimeMillis;
|
|
14200
|
+
yield* Queue.offer(queue, PhaseEntered.make({
|
|
14201
|
+
phase: "mutation-test",
|
|
14202
|
+
elapsedMs: nowEmit - env.runStartedAt
|
|
14203
|
+
}));
|
|
14204
|
+
yield* Effect$1.logInfo("The dry-run has been completed successfully. No mutations have been executed.");
|
|
14205
|
+
return {
|
|
14206
|
+
results: [],
|
|
14207
|
+
verdict: null
|
|
14208
|
+
};
|
|
14209
|
+
})), Match.tag("MutationTestNoTests", () => Effect$1.gen(function* () {
|
|
14210
|
+
const env = yield* RunEnvironment;
|
|
14055
14211
|
const queue = yield* RunEvents;
|
|
14212
|
+
const now = yield* Clock.currentTimeMillis;
|
|
14213
|
+
const elapsed = Duration.millis(now - env.runStartedAt);
|
|
14214
|
+
yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
|
|
14215
|
+
const nowEmit = yield* Clock.currentTimeMillis;
|
|
14056
14216
|
yield* Queue.offer(queue, PhaseEntered.make({
|
|
14057
14217
|
phase: "mutation-test",
|
|
14058
14218
|
elapsedMs: nowEmit - env.runStartedAt
|
|
14059
14219
|
}));
|
|
14060
|
-
});
|
|
14061
|
-
const idGenerator = yield* IdGenerator;
|
|
14062
|
-
const checkerPool = yield* makeCheckerPool(prev, idGenerator);
|
|
14063
|
-
const testRunnerContext = {
|
|
14064
|
-
options: prev.options,
|
|
14065
|
-
fileDescriptions: prev.project.fileDescriptions,
|
|
14066
|
-
sandboxWorkingDirectory: prev.sandbox.workingDirectory,
|
|
14067
|
-
idGenerator,
|
|
14068
|
-
retire: Effect$1.void,
|
|
14069
|
-
testFiles: prev.project.testFiles.map((file) => prev.sandbox.sandboxFileFor(file))
|
|
14070
|
-
};
|
|
14071
|
-
const testRunnerPool = yield* Pool.make({
|
|
14072
|
-
acquire: buildTestRunner(testRunnerContext, Effect$1.suspend(() => {
|
|
14073
|
-
const runnerConfigured = prev.options.testRunner;
|
|
14074
|
-
const runnerLabel = Match.value(runnerConfigured).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
|
|
14075
|
-
return resolveConfiguredWorkerSpawn({
|
|
14076
|
-
loaded: prev.loadedPlugins,
|
|
14077
|
-
kind: "TestRunner",
|
|
14078
|
-
configured: runnerConfigured
|
|
14079
|
-
}).pipe(Effect$1.mapError(missingWorkerEntry("mutationTest", "test runner", runnerLabel)), Effect$1.flatMap(({ spawn }) => makeChildProcessTestRunner({
|
|
14080
|
-
options: prev.options,
|
|
14081
|
-
fileDescriptions: prev.project.fileDescriptions,
|
|
14082
|
-
sandboxWorkingDirectory: prev.sandbox.workingDirectory,
|
|
14083
|
-
workerEntrypoint: spawn.entrypoint,
|
|
14084
|
-
idGenerator
|
|
14085
|
-
})));
|
|
14086
|
-
})),
|
|
14087
|
-
size: prev.concurrency.testRunners
|
|
14088
|
-
});
|
|
14089
|
-
const reporting = makeMutationReportingService({
|
|
14090
|
-
reporterStage: prev.reporterStage,
|
|
14091
|
-
options: prev.options,
|
|
14092
|
-
project: prev.project,
|
|
14093
|
-
testCoverage: prev.testCoverage,
|
|
14094
|
-
runId: env.runId,
|
|
14095
|
-
resolvedMode: env.resolvedMode,
|
|
14096
|
-
sandboxDirectory: prev.sandbox.workingDirectory,
|
|
14097
|
-
basePath: env.basePath
|
|
14098
|
-
});
|
|
14099
|
-
const sandboxFileByName = Object.fromEntries([...MutableHashMap.keys(prev.project.filesToMutate)].map((name) => [name, prev.sandbox.sandboxFileFor(name)]));
|
|
14100
|
-
const currentRelativeFiles = yield* readCurrentRelativeFiles(prev.project, env.basePath);
|
|
14101
|
-
const incremental = incrementalDiff({
|
|
14102
|
-
currentMutants: prev.mutants,
|
|
14103
|
-
testCoverage: prev.testCoverage,
|
|
14104
|
-
incrementalReport: prev.project.incrementalReport,
|
|
14105
|
-
currentRelativeFiles,
|
|
14106
|
-
basePath: env.basePath,
|
|
14107
|
-
force: prev.options.force
|
|
14108
|
-
});
|
|
14109
|
-
const rememberedResults = rememberedResultsOf(prev.mutants, incremental.remembered);
|
|
14110
|
-
yield* Effect$1.when(Effect$1.logInfo(`Incremental mode: reusing ${rememberedResults.length} mutant result(s), running ${incremental.mutants.length} mutant(s).`), Effect$1.succeed(rememberedResults.length > 0));
|
|
14111
|
-
const { coveredPlans, earlyResults: noCoverageResults } = partitionPlans(yield* decidePlans(incremental.mutants, prev.testCoverage, {
|
|
14112
|
-
disableBail: prev.options.disableBail,
|
|
14113
|
-
timeoutMS: prev.options.timeoutMS,
|
|
14114
|
-
timeoutFactor: prev.options.timeoutFactor,
|
|
14115
|
-
ignoreStatic: prev.options.ignoreStatic
|
|
14116
|
-
}, Duration.toMillis(prev.timeOverhead), void 0, sandboxFileByName));
|
|
14117
|
-
const sortedPlans = [...coveredPlans].sort((a, b) => Number(a.runOptions.reloadEnvironment) - Number(b.runOptions.reloadEnvironment));
|
|
14118
|
-
const allPlansForReporter = [...sortedPlans];
|
|
14119
|
-
yield* offerReporterEvent(prev.reporterStage, MutationTestingPlanReady.make({
|
|
14120
|
-
total: allPlansForReporter.length + noCoverageResults.length + rememberedResults.length,
|
|
14121
|
-
plans: allPlansForReporter.map((plan) => ({
|
|
14122
|
-
mutantId: plan.mutant.id,
|
|
14123
|
-
plan: plan.plan,
|
|
14124
|
-
netTime: plan.netTime,
|
|
14125
|
-
reloadEnvironment: plan.runOptions.reloadEnvironment
|
|
14126
|
-
}))
|
|
14127
|
-
})).pipe(Effect$1.ignoreCause);
|
|
14128
|
-
{
|
|
14129
|
-
const queue2 = yield* RunEvents;
|
|
14130
|
-
yield* Queue.offer(queue2, PlanKnown.make({ total: allPlansForReporter.length + noCoverageResults.length }));
|
|
14131
|
-
}
|
|
14132
|
-
const passedPlans = yield* checkPlansWithConfiguredCheckers(checkerPool, sortedPlans, reporting);
|
|
14133
|
-
const testRunnerStream = Stream.fromIterable(passedPlans);
|
|
14134
|
-
const plannedTotal = allPlansForReporter.length + noCoverageResults.length + rememberedResults.length;
|
|
14135
|
-
const pathService = yield* Path.Path;
|
|
14136
|
-
const progressQueue = yield* RunEvents;
|
|
14137
|
-
const completedRef = yield* Ref.make(0);
|
|
14138
|
-
const preparedStreamableOf = (result) => {
|
|
14139
|
-
if (!isMutantStatus(result.status)) return;
|
|
14140
14220
|
return {
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
location: toSchemaLocation(result.location)
|
|
14221
|
+
results: [],
|
|
14222
|
+
verdict: null
|
|
14144
14223
|
};
|
|
14145
|
-
}
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
|
|
14151
|
-
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
const
|
|
14159
|
-
|
|
14224
|
+
})), Match.tag("MutationTestProceed", () => Effect$1.gen(function* () {
|
|
14225
|
+
const prev = raw.prev;
|
|
14226
|
+
const env = yield* RunEnvironment;
|
|
14227
|
+
yield* Effect$1.gen(function* () {
|
|
14228
|
+
const nowEmit = yield* Clock.currentTimeMillis;
|
|
14229
|
+
const queue = yield* RunEvents;
|
|
14230
|
+
yield* Queue.offer(queue, PhaseEntered.make({
|
|
14231
|
+
phase: "mutation-test",
|
|
14232
|
+
elapsedMs: nowEmit - env.runStartedAt
|
|
14233
|
+
}));
|
|
14234
|
+
});
|
|
14235
|
+
const idGenerator = yield* IdGenerator;
|
|
14236
|
+
const checkerPool = yield* makeCheckerPool(prev, idGenerator);
|
|
14237
|
+
const testRunnerContext = {
|
|
14238
|
+
options: prev.options,
|
|
14239
|
+
fileDescriptions: prev.project.fileDescriptions,
|
|
14240
|
+
sandboxWorkingDirectory: prev.sandbox.workingDirectory,
|
|
14241
|
+
idGenerator,
|
|
14242
|
+
retire: Effect$1.void,
|
|
14243
|
+
testFiles: prev.project.testFiles.map((file) => prev.sandbox.sandboxFileFor(file))
|
|
14244
|
+
};
|
|
14245
|
+
const testRunnerPool = yield* Pool.make({
|
|
14246
|
+
acquire: buildTestRunner(testRunnerContext, Effect$1.suspend(() => {
|
|
14247
|
+
const runnerConfigured = prev.options.testRunner;
|
|
14248
|
+
const runnerLabel = Match.value(runnerConfigured).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
|
|
14249
|
+
return resolveConfiguredWorkerSpawn({
|
|
14250
|
+
loaded: prev.loadedPlugins,
|
|
14251
|
+
kind: "TestRunner",
|
|
14252
|
+
configured: runnerConfigured
|
|
14253
|
+
}).pipe(Effect$1.mapError(missingWorkerEntry("mutationTest", "test runner", runnerLabel)), Effect$1.flatMap(({ spawn }) => makeChildProcessTestRunner({
|
|
14254
|
+
options: prev.options,
|
|
14255
|
+
fileDescriptions: prev.project.fileDescriptions,
|
|
14256
|
+
sandboxWorkingDirectory: prev.sandbox.workingDirectory,
|
|
14257
|
+
workerEntrypoint: spawn.entrypoint,
|
|
14258
|
+
idGenerator
|
|
14259
|
+
})));
|
|
14260
|
+
})),
|
|
14261
|
+
size: prev.concurrency.testRunners
|
|
14262
|
+
});
|
|
14263
|
+
const reporting = makeMutationReportingService({
|
|
14264
|
+
reporterStage: prev.reporterStage,
|
|
14265
|
+
options: prev.options,
|
|
14266
|
+
project: prev.project,
|
|
14267
|
+
testCoverage: prev.testCoverage,
|
|
14268
|
+
runId: env.runId,
|
|
14269
|
+
resolvedMode: env.resolvedMode,
|
|
14270
|
+
sandboxDirectory: prev.sandbox.workingDirectory,
|
|
14271
|
+
basePath: env.basePath
|
|
14272
|
+
});
|
|
14273
|
+
const sandboxFileByName = Object.fromEntries([...MutableHashMap.keys(prev.project.filesToMutate)].map((name) => [name, prev.sandbox.sandboxFileFor(name)]));
|
|
14274
|
+
const currentRelativeFiles = yield* readCurrentRelativeFiles(prev.project, env.basePath);
|
|
14275
|
+
const incremental = incrementalDiff({
|
|
14276
|
+
currentMutants: plannableMutants,
|
|
14277
|
+
testCoverage: prev.testCoverage,
|
|
14278
|
+
incrementalReport: prev.project.incrementalReport,
|
|
14279
|
+
currentRelativeFiles,
|
|
14280
|
+
basePath: env.basePath,
|
|
14281
|
+
force: prev.options.force
|
|
14282
|
+
});
|
|
14283
|
+
const rememberedResults = rememberedResultsOf(plannableMutants, incremental.remembered);
|
|
14284
|
+
yield* Effect$1.when(Effect$1.logInfo(`Incremental mode: reusing ${rememberedResults.length} mutant result(s), running ${incremental.mutants.length} mutant(s).`), Effect$1.succeed(rememberedResults.length > 0));
|
|
14285
|
+
const { runPlans, earlyResults: noCoverageResults } = partitionRunPlans(yield* decidePlans(incremental.mutants, prev.testCoverage, {
|
|
14286
|
+
disableBail: prev.options.disableBail,
|
|
14287
|
+
timeoutMS: prev.options.timeoutMS,
|
|
14288
|
+
timeoutFactor: prev.options.timeoutFactor,
|
|
14289
|
+
ignoreStatic: prev.options.ignoreStatic
|
|
14290
|
+
}, Duration.toMillis(prev.timeOverhead), void 0, sandboxFileByName));
|
|
14291
|
+
const sortedPlans = sortRunPlans(runPlans);
|
|
14292
|
+
const allPlansForReporter = [...sortedPlans];
|
|
14293
|
+
yield* offerReporterEvent(prev.reporterStage, MutationTestingPlanReady.make({
|
|
14294
|
+
total: allPlansForReporter.length + noCoverageResults.length + rememberedResults.length,
|
|
14295
|
+
plans: allPlansForReporter.map((plan) => ({
|
|
14296
|
+
mutantId: plan.mutant.id,
|
|
14297
|
+
plan: plan.plan,
|
|
14298
|
+
netTime: plan.netTime,
|
|
14299
|
+
reloadEnvironment: plan.runOptions.reloadEnvironment
|
|
14300
|
+
}))
|
|
14301
|
+
})).pipe(Effect$1.ignoreCause);
|
|
14302
|
+
{
|
|
14303
|
+
const queue2 = yield* RunEvents;
|
|
14304
|
+
yield* Queue.offer(queue2, PlanKnown.make({ total: allPlansForReporter.length + noCoverageResults.length }));
|
|
14305
|
+
}
|
|
14306
|
+
const passedPlans = yield* checkPlansWithConfiguredCheckers(checkerPool, sortedPlans, reporting);
|
|
14307
|
+
const testRunnerStream = Stream.fromIterable(passedPlans);
|
|
14308
|
+
const plannedTotal = allPlansForReporter.length + noCoverageResults.length + rememberedResults.length;
|
|
14309
|
+
const pathService = yield* Path.Path;
|
|
14310
|
+
const progressQueue = yield* RunEvents;
|
|
14311
|
+
const completedRef = yield* Ref.make(0);
|
|
14312
|
+
const preparedStreamableOf = (result) => {
|
|
14313
|
+
if (!isMutantStatus(result.status)) return;
|
|
14314
|
+
return {
|
|
14315
|
+
status: result.status,
|
|
14316
|
+
file: reportFileName(pathService.relative(env.basePath, result.fileName)),
|
|
14317
|
+
location: toSchemaLocation(result.location)
|
|
14318
|
+
};
|
|
14319
|
+
};
|
|
14320
|
+
const toStreamEvent = (result, completed, prepared) => MutantTested.make({
|
|
14160
14321
|
id: result.id,
|
|
14161
14322
|
status: prepared.status,
|
|
14162
14323
|
file: prepared.file,
|
|
@@ -14165,71 +14326,85 @@ const mutationTestCell$1 = Cell.layer({
|
|
|
14165
14326
|
replacement: result.replacement,
|
|
14166
14327
|
completed,
|
|
14167
14328
|
total: plannedTotal
|
|
14329
|
+
});
|
|
14330
|
+
const offerFinished = (result, prepared) => Effect$1.gen(function* () {
|
|
14331
|
+
if (prepared === void 0) return;
|
|
14332
|
+
const completed = yield* Ref.updateAndGet(completedRef, (n) => n + 1);
|
|
14333
|
+
yield* Queue.offer(progressQueue, RunMutantTested.make({
|
|
14334
|
+
id: result.id,
|
|
14335
|
+
status: prepared.status,
|
|
14336
|
+
file: prepared.file,
|
|
14337
|
+
location: prepared.location,
|
|
14338
|
+
mutator: result.mutatorName,
|
|
14339
|
+
replacement: result.replacement,
|
|
14340
|
+
completed,
|
|
14341
|
+
total: plannedTotal
|
|
14342
|
+
}));
|
|
14343
|
+
return completed;
|
|
14344
|
+
});
|
|
14345
|
+
const reportStreamTested = (result, completed, prepared) => offerReporterEvent(prev.reporterStage, toStreamEvent(result, completed, prepared)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Reporter stream failed handling mutantTested", cause)));
|
|
14346
|
+
const offerStreamTested = (result, completed, prepared) => Option.match(Option.all([Option.fromNullishOr(completed), Option.fromNullishOr(prepared)]), {
|
|
14347
|
+
onNone: () => Effect$1.void,
|
|
14348
|
+
onSome: ([done, streamable]) => reportStreamTested(result, done, streamable)
|
|
14349
|
+
});
|
|
14350
|
+
const announceSettledMutant = (result) => Effect$1.gen(function* () {
|
|
14351
|
+
const prepared = preparedStreamableOf(result);
|
|
14352
|
+
const completed = yield* offerFinished(result, prepared);
|
|
14353
|
+
yield* offerStreamTested(result, completed, prepared);
|
|
14354
|
+
});
|
|
14355
|
+
yield* Effect$1.forEach([...rememberedResults, ...noCoverageResults], announceSettledMutant, {
|
|
14356
|
+
concurrency: 1,
|
|
14357
|
+
discard: true
|
|
14358
|
+
});
|
|
14359
|
+
const completedMutants = yield* Ref.make([...rememberedResults, ...noCoverageResults]);
|
|
14360
|
+
const checkpointGate = yield* Semaphore.make(1);
|
|
14361
|
+
yield* reporting.checkpoint(yield* Ref.get(completedMutants)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
|
|
14362
|
+
const persist = (result) => checkpointGate.withPermits(1)(Effect$1.gen(function* () {
|
|
14363
|
+
const next = yield* Ref.updateAndGet(completedMutants, (prev) => [...prev, result]);
|
|
14364
|
+
yield* reporting.checkpoint(next).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
|
|
14168
14365
|
}));
|
|
14169
|
-
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
14176
|
-
|
|
14177
|
-
|
|
14178
|
-
|
|
14179
|
-
|
|
14180
|
-
|
|
14181
|
-
|
|
14182
|
-
|
|
14183
|
-
|
|
14184
|
-
|
|
14185
|
-
|
|
14186
|
-
|
|
14187
|
-
|
|
14188
|
-
|
|
14189
|
-
|
|
14190
|
-
|
|
14191
|
-
|
|
14192
|
-
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14197
|
-
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14209
|
-
|
|
14210
|
-
|
|
14211
|
-
...noCoverageResults,
|
|
14212
|
-
...runResults
|
|
14213
|
-
];
|
|
14214
|
-
const outcomeResult = yield* reporting.reportAll(allResults);
|
|
14215
|
-
const doneNow = yield* Clock.currentTimeMillis;
|
|
14216
|
-
const elapsed = Duration.millis(doneNow - env.runStartedAt);
|
|
14217
|
-
yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
|
|
14218
|
-
return outcomeResult;
|
|
14219
|
-
})), Match.exhaustive);
|
|
14220
|
-
})).pipe(Effect$1.mapError((cause) => Match.value(cause).pipe(Match.tag("StageError", (stage) => stage), Match.tag("CheckerAnsweredUnrequested", (breach) => StageError.make({
|
|
14221
|
-
stage: "mutationTest",
|
|
14222
|
-
reason: `Checker "${breach.checkerName}" answered about mutants it was not asked about (${breach.phase} phase): ${breach.unrequestedIds.join(", ")}`,
|
|
14223
|
-
cause: breach
|
|
14224
|
-
})), Match.tag("CheckerSkippedRequested", (breach) => StageError.make({
|
|
14225
|
-
stage: "mutationTest",
|
|
14226
|
-
reason: `Checker "${breach.checkerName}" skipped requested mutants (${breach.phase} phase): ${breach.missingIds.join(", ")}`,
|
|
14227
|
-
cause: breach
|
|
14228
|
-
})), Match.tag("ChildProcessCrashedError", "OutOfMemoryError", "PlatformError", "TestRunnerFailed", () => StageError.make({
|
|
14229
|
-
stage: "mutationTest",
|
|
14230
|
-
reason: "Mutation testing failed",
|
|
14231
|
-
cause
|
|
14232
|
-
})), Match.exhaustive)))
|
|
14366
|
+
const runResults = yield* withPhaseSpan("mutationTest.batch", {
|
|
14367
|
+
total: plannedTotal,
|
|
14368
|
+
testRunners: prev.concurrency.testRunners
|
|
14369
|
+
}, () => Stream.mapEffect(testRunnerStream, (plan) => Effect$1.scoped(Effect$1.gen(function* () {
|
|
14370
|
+
const pool = testRunnerPool;
|
|
14371
|
+
const runner = yield* Pool.get(pool);
|
|
14372
|
+
const result = yield* runner.mutantRun(plan.runOptions).pipe(Effect$1.catchTags({
|
|
14373
|
+
OutOfMemoryError: (error) => Effect$1.flatMap(Pool.invalidate(pool, runner), () => Effect$1.fail(error)),
|
|
14374
|
+
ChildProcessCrashedError: (error) => Effect$1.flatMap(Pool.invalidate(pool, runner), () => Effect$1.fail(error))
|
|
14375
|
+
}));
|
|
14376
|
+
const reported = yield* reporting.reportMutantRunResult(toReportedMutant(plan.mutant), result);
|
|
14377
|
+
const prepared = preparedStreamableOf(reported);
|
|
14378
|
+
const finished = yield* offerFinished(reported, prepared);
|
|
14379
|
+
yield* offerStreamTested(reported, finished, prepared);
|
|
14380
|
+
yield* persist(reported);
|
|
14381
|
+
return reported;
|
|
14382
|
+
})), { concurrency: Math.max(1, prev.concurrency.testRunners) }).pipe(Stream.runCollect, Effect$1.map((chunk) => [...chunk])));
|
|
14383
|
+
const allResults = [
|
|
14384
|
+
...rememberedResults,
|
|
14385
|
+
...noCoverageResults,
|
|
14386
|
+
...runResults
|
|
14387
|
+
];
|
|
14388
|
+
const outcomeResult = yield* reporting.reportAll(allResults);
|
|
14389
|
+
const doneNow = yield* Clock.currentTimeMillis;
|
|
14390
|
+
const elapsed = Duration.millis(doneNow - env.runStartedAt);
|
|
14391
|
+
yield* Effect$1.logInfo(`Done in ${Duration.format(elapsed)}.`);
|
|
14392
|
+
return outcomeResult;
|
|
14393
|
+
})), Match.exhaustive);
|
|
14394
|
+
})).pipe(Effect$1.mapError((cause) => Match.value(cause).pipe(Match.tag("StageError", (stage) => stage), Match.tag("CheckerAnsweredUnrequested", (breach) => StageError.make({
|
|
14395
|
+
stage: "mutationTest",
|
|
14396
|
+
reason: `Checker "${breach.checkerName}" answered about mutants it was not asked about (${breach.phase} phase): ${breach.unrequestedIds.join(", ")}`,
|
|
14397
|
+
cause: breach
|
|
14398
|
+
})), Match.tag("CheckerSkippedRequested", (breach) => StageError.make({
|
|
14399
|
+
stage: "mutationTest",
|
|
14400
|
+
reason: `Checker "${breach.checkerName}" skipped requested mutants (${breach.phase} phase): ${breach.missingIds.join(", ")}`,
|
|
14401
|
+
cause: breach
|
|
14402
|
+
})), Match.tag("ChildProcessCrashedError", "OutOfMemoryError", "PlatformError", "TestRunnerFailed", () => StageError.make({
|
|
14403
|
+
stage: "mutationTest",
|
|
14404
|
+
reason: "Mutation testing failed",
|
|
14405
|
+
cause
|
|
14406
|
+
})), Match.exhaustive)));
|
|
14407
|
+
}
|
|
14233
14408
|
});
|
|
14234
14409
|
//#endregion
|
|
14235
14410
|
//#region src/reporter-output.ts
|
|
@@ -15437,4 +15612,4 @@ const strykerCell = (options, targetMutatePatterns) => Effect$1.gen(function* ()
|
|
|
15437
15612
|
}, prepareCommandOf(options, patterns));
|
|
15438
15613
|
});
|
|
15439
15614
|
//#endregion
|
|
15440
|
-
export {
|
|
15615
|
+
export { StrykerError as $, FileResultSchema as $t, describeErrors as A, RunStarted as At, WorkerLauncher as B, defaultOptions$1 as Bt, matchesFile as C, Heartbeat as Ct, reporterWorkerFactory as D, RunFailed as Dt, REPORTER_EVENT_BATCH_BOUND as E, PlanKnown as Et, mergeConfigs as F, verdictExitClass as Ft, ChildProcessCrashedError as G, CheckerCheckResult as Gt, makeWorkerClient as H, isModuleSpecifier$1 as Ht, readConfig as I, CONFIG_SYNTAX_HELP$1 as It, VmRunner as J, CheckerMutantWire as Jt, OutOfMemoryError as K, CheckerFailed as Kt, validateOptions as L, SUPPORTED_CONFIG_FILE_NAMES$1 as Lt, importModule as M, EXIT_CODE as Mt, initialExtendsStepState as N, highestExitClass as Nt, spawnReporterWorker as O, RunMutantTested as Ot, loadConfigCell as P, resolveExitCode as Pt, vmTestRunner as Q, ExitClass as Qt, buildTestRunner as R, createDefaultOptions$1 as Rt, createFileMatcher as S, RunIdentity as St, RunEnvironment as T, PhaseEntered as Tt, IdGenerator as U, isWarningEnabled$1 as Ut, connectRetry as V, findUnserializables$1 as Vt, classifyWorkerExit as W, optionsPath$1 as Wt, vmRunnerCapabilities as X, CheckerRpcs as Xt, isVmRunner as Y, CheckerRequest as Yt, vmRunnerName as Z, DryRunCompleted as Zt, checkerMutantsSkipped as _, MutantStatusSchema as _n, forkOptionsSchema as _t, shouldKeepTempDir as a, MutationTestingPlanReady as an, ConfigFileUnsupportedError as at, CheckerAnsweredUnrequested as b, StageError as bt, buildVerdictEnvelope as c, ReporterEventBatch as cn, ExtendsStepRead as ct, strykerVersion as d, StrykerOptionsSchema as dn, ExtendsStepStateSchema as dt, MetricsSchema as en, ConfigDocumentSchema as et, calculateMetrics as f, TestRunnerConfigSchema as fn, ImportedModuleSchema as ft, checkerMutantsChecked as g, MutantActivationSchema as gn, extendsPropertySchema as gt, checkerDuration as h, LocationSchema$1 as hn, ReadConfigCommand as ht, mutationTestCell as i, MutationTestResultSchema$1 as in, ConfigFileUnreadableError as it, forkCoreSchema as j, VerdictReached as jt, decideExtendsStep as k, RunPhase as kt, generateRunId as l, ReporterInitOptions as ln, ExtendsStepRefused as lt, checkGroupedPlans as m, WorkerPluginKind as mn, MergeResult as mt, RUN_EVENTS_QUEUE_BOUND as n, MutantTested as nn, ConfigFileInvalidError as nt, ACTIONABLE_STATUSES as o, ReporterAck as on, ExtendsStepDocumentSchema as ot, countMutants as p, TraceContextMiddleware as pn, MergeCommand as pt, WorkerBootTimeoutError as q, CheckerGroupResult as qt, makeRunLayer as r, MutationTestReportReady as rn, ConfigFileNotFoundError as rt, VERDICT_ENVELOPE_SCHEMA_VERSION as s, ReporterDrained as sn, ExtendsStepDone as st, strykerCell as t, MutantResultSchema as tn, ConfigError as tt, isActionableStatus as u, ReporterRpcs as un, ExtendsStepResolve as ut, checkerProcessCrashes as v, PositionSchema$2 as vn, survivorsPriorReport as vt, IncrementalReportSchema as w, HelpRendered as wt, CheckerSkippedRequested as x, RunEvents as xt, checkerRpcFailures as y, toRelativeNormalizedFileName as yt, isCommandRunner as z, deepFreeze$1 as zt };
|