@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.
@@ -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(Mutant)
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
- }).pipe(Effect$1.mapError((error) => crashed(error.message))),
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
- }).pipe(Effect$1.mapError((error) => crashed(error.message)))
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) => command.checker.check(command.checkerName, command.plans.map((plan) => plan.mutant)).pipe(Effect$1.map((answers) => ({
13208
- checkerName: command.checkerName,
13209
- requestedIds: command.plans.map((plan) => plan.mutant.id),
13210
- answers
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) => command.checker.group(command.checkerName, command.plans.map((plan) => plan.mutant)).pipe(Effect$1.map((idGroups) => ({
13233
- checkerName: command.checkerName,
13234
- requestedIds: command.plans.map((plan) => plan.mutant.id),
13235
- idGroups
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 groups = yield* groupPlans(checker, checkerName, plans);
13254
- return (yield* Effect$1.forEach(groups, (group) => checkPlans(checker, checkerName, group), { concurrency: 1 })).flat();
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: "7.0.0",
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) => withPhaseSpan("mutationTest", {
14011
- mutantCount: raw.prev.mutants.length,
14012
- testCount: raw.prev.dryRunResult.tests.length
14013
- }, () => Effect$1.gen(function* () {
14014
- const decision = yield* Result.match(outcome, {
14015
- onFailure: (err) => Effect$1.fail(StageError.make({
14016
- stage: err.stage,
14017
- reason: err.reason,
14018
- cause: err
14019
- })),
14020
- onSuccess: (d) => Effect$1.succeed(d)
14021
- });
14022
- return yield* Match.value(decision).pipe(Match.tag("MutationTestDryRunOnly", () => Effect$1.gen(function* () {
14023
- const env = yield* RunEnvironment;
14024
- const queue = yield* RunEvents;
14025
- const nowEmit = yield* Clock.currentTimeMillis;
14026
- yield* Queue.offer(queue, PhaseEntered.make({
14027
- phase: "mutation-test",
14028
- elapsedMs: nowEmit - env.runStartedAt
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
- status: result.status,
14142
- file: reportFileName(pathService.relative(env.basePath, result.fileName)),
14143
- location: toSchemaLocation(result.location)
14221
+ results: [],
14222
+ verdict: null
14144
14223
  };
14145
- };
14146
- const toStreamEvent = (result, completed, prepared) => MutantTested.make({
14147
- id: result.id,
14148
- status: prepared.status,
14149
- file: prepared.file,
14150
- location: prepared.location,
14151
- mutator: result.mutatorName,
14152
- replacement: result.replacement,
14153
- completed,
14154
- total: plannedTotal
14155
- });
14156
- const offerFinished = (result, prepared) => Effect$1.gen(function* () {
14157
- if (prepared === void 0) return;
14158
- const completed = yield* Ref.updateAndGet(completedRef, (n) => n + 1);
14159
- yield* Queue.offer(progressQueue, RunMutantTested.make({
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
- return completed;
14170
- });
14171
- 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)));
14172
- const offerStreamTested = (result, completed, prepared) => Option.match(Option.all([Option.fromNullishOr(completed), Option.fromNullishOr(prepared)]), {
14173
- onNone: () => Effect$1.void,
14174
- onSome: ([done, streamable]) => reportStreamTested(result, done, streamable)
14175
- });
14176
- const announceSettledMutant = (result) => Effect$1.gen(function* () {
14177
- const prepared = preparedStreamableOf(result);
14178
- const completed = yield* offerFinished(result, prepared);
14179
- yield* offerStreamTested(result, completed, prepared);
14180
- });
14181
- yield* Effect$1.forEach([...rememberedResults, ...noCoverageResults], announceSettledMutant, {
14182
- concurrency: 1,
14183
- discard: true
14184
- });
14185
- const completedMutants = yield* Ref.make([...rememberedResults, ...noCoverageResults]);
14186
- const checkpointGate = yield* Semaphore.make(1);
14187
- yield* reporting.checkpoint(yield* Ref.get(completedMutants)).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
14188
- const persist = (result) => checkpointGate.withPermits(1)(Effect$1.gen(function* () {
14189
- const next = yield* Ref.updateAndGet(completedMutants, (prev) => [...prev, result]);
14190
- yield* reporting.checkpoint(next).pipe(Effect$1.catchCause((cause) => Effect$1.logWarning("Failed to persist the mutation checkpoint", cause)));
14191
- }));
14192
- const runResults = yield* withPhaseSpan("mutationTest.batch", {
14193
- total: plannedTotal,
14194
- testRunners: prev.concurrency.testRunners
14195
- }, () => Stream.mapEffect(testRunnerStream, (plan) => Effect$1.scoped(Effect$1.gen(function* () {
14196
- const pool = testRunnerPool;
14197
- const runner = yield* Pool.get(pool);
14198
- const result = yield* runner.mutantRun(plan.runOptions).pipe(Effect$1.catchTags({
14199
- OutOfMemoryError: (error) => Effect$1.flatMap(Pool.invalidate(pool, runner), () => Effect$1.fail(error)),
14200
- ChildProcessCrashedError: (error) => Effect$1.flatMap(Pool.invalidate(pool, runner), () => Effect$1.fail(error))
14201
- }));
14202
- const reported = yield* reporting.reportMutantRunResult(toReportedMutant(plan.mutant), result);
14203
- const prepared = preparedStreamableOf(reported);
14204
- const finished = yield* offerFinished(reported, prepared);
14205
- yield* offerStreamTested(reported, finished, prepared);
14206
- yield* persist(reported);
14207
- return reported;
14208
- })), { concurrency: Math.max(1, prev.concurrency.testRunners) }).pipe(Stream.runCollect, Effect$1.map((chunk) => [...chunk])));
14209
- const allResults = [
14210
- ...rememberedResults,
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 { ConfigFileNotFoundError as $, ReporterInitOptions as $t, mergeConfigs as A, resolveExitCode as At, classifyWorkerExit as B, optionsPath$1 as Bt, spawnReporterWorker as C, RunFailed as Ct, importModule as D, VerdictReached as Dt, forkCoreSchema as E, RunStarted as Et, isCommandRunner as F, deepFreeze$1 as Ft, isVmRunner as G, MutantResultSchema as Gt, OutOfMemoryError as H, ExitClass as Ht, WorkerLauncher as I, defaultOptions$1 as It, vmTestRunner as J, MutationTestResultSchema$1 as Jt, vmRunnerCapabilities as K, MutantTested as Kt, connectRetry as L, findUnserializables$1 as Lt, resolveExtends as M, CONFIG_SYNTAX_HELP$1 as Mt, validateOptions as N, SUPPORTED_CONFIG_FILE_NAMES$1 as Nt, initialExtendsStepState as O, EXIT_CODE as Ot, buildTestRunner as P, createDefaultOptions$1 as Pt, ConfigFileInvalidError as Q, ReporterEventBatch as Qt, makeWorkerClient as R, isModuleSpecifier$1 as Rt, reporterWorkerFactory as S, PlanKnown as St, describeErrors as T, RunPhase as Tt, WorkerBootTimeoutError as U, FileResultSchema as Ut, ChildProcessCrashedError as V, DryRunCompleted as Vt, VmRunner as W, MetricsSchema as Wt, ConfigDocumentSchema as X, ReporterAck as Xt, StrykerError as Y, MutationTestingPlanReady as Yt, ConfigError as Z, ReporterDrained as Zt, createFileMatcher as _, RunEvents as _t, shouldKeepTempDir as a, LocationSchema$1 as an, ExtendsStepRefused as at, RunEnvironment as b, HelpRendered as bt, buildVerdictEnvelope as c, PositionSchema$2 as cn, ImportedModuleSchema as ct, strykerVersion as d, ReadConfigCommand as dt, ReporterRpcs as en, ConfigFileUnreadableError as et, calculateMetrics as f, extendsPropertySchema as ft, CheckerSkippedRequested as g, StageError as gt, CheckerAnsweredUnrequested as h, toRelativeNormalizedFileName as ht, mutationTestCell as i, WorkerPluginKind as in, ExtendsStepRead as it, readConfig as j, verdictExitClass as jt, loadConfigCell as k, highestExitClass as kt, generateRunId as l, MergeCommand as lt, checkGroupedPlans as m, survivorsPriorReport as mt, RUN_EVENTS_QUEUE_BOUND as n, TestRunnerConfigSchema as nn, ExtendsStepDocumentSchema as nt, ACTIONABLE_STATUSES as o, MutantActivationSchema as on, ExtendsStepResolve as ot, countMutants as p, forkOptionsSchema as pt, vmRunnerName as q, MutationTestReportReady as qt, makeRunLayer as r, TraceContextMiddleware as rn, ExtendsStepDone as rt, VERDICT_ENVELOPE_SCHEMA_VERSION as s, MutantStatusSchema as sn, ExtendsStepStateSchema as st, strykerCell as t, StrykerOptionsSchema as tn, ConfigFileUnsupportedError as tt, isActionableStatus as u, MergeResult as ut, matchesFile as v, RunIdentity as vt, decideExtendsStep as w, RunMutantTested as wt, REPORTER_EVENT_BATCH_BOUND as x, PhaseEntered as xt, IncrementalReportSchema as y, Heartbeat as yt, IdGenerator as z, isWarningEnabled$1 as zt };
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 };