apex-mutation-testing 1.6.1 → 1.7.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.
Files changed (32) hide show
  1. package/README.md +34 -3
  2. package/lib/commands/apex/mutation/test/run.d.ts +1 -0
  3. package/lib/commands/apex/mutation/test/run.js +4 -0
  4. package/lib/commands/apex/mutation/test/run.js.map +1 -1
  5. package/lib/service/configReader.js +1 -0
  6. package/lib/service/configReader.js.map +1 -1
  7. package/lib/service/exactColoring.d.ts +25 -0
  8. package/lib/service/exactColoring.js +143 -0
  9. package/lib/service/exactColoring.js.map +1 -0
  10. package/lib/service/groupExecutor.d.ts +29 -0
  11. package/lib/service/groupExecutor.js +194 -0
  12. package/lib/service/groupExecutor.js.map +1 -0
  13. package/lib/service/mutantGenerator.d.ts +2 -0
  14. package/lib/service/mutantGenerator.js +26 -1
  15. package/lib/service/mutantGenerator.js.map +1 -1
  16. package/lib/service/mutationGrouper.d.ts +22 -0
  17. package/lib/service/mutationGrouper.js +130 -0
  18. package/lib/service/mutationGrouper.js.map +1 -0
  19. package/lib/service/mutationLocation.d.ts +12 -0
  20. package/lib/service/mutationLocation.js +54 -0
  21. package/lib/service/mutationLocation.js.map +1 -0
  22. package/lib/service/mutationTestingService.d.ts +3 -6
  23. package/lib/service/mutationTestingService.js +71 -177
  24. package/lib/service/mutationTestingService.js.map +1 -1
  25. package/lib/service/timeUtils.d.ts +1 -0
  26. package/lib/service/timeUtils.js +8 -0
  27. package/lib/service/timeUtils.js.map +1 -1
  28. package/lib/type/ApexMutationParameter.d.ts +1 -0
  29. package/messages/apex.mutation.test.run.md +13 -1
  30. package/npm-shrinkwrap.json +553 -378
  31. package/oclif.manifest.json +7 -1
  32. package/package.json +13 -13
@@ -1 +1 @@
1
- {"version":3,"file":"mutantGenerator.js","sourceRoot":"","sources":["../../src/service/mutantGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACjE,OAAO,EACL,SAAS,EACT,UAAU,EAEV,0BAA0B,EAC1B,iBAAiB,EACjB,eAAe,GAChB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAA;AACrF,OAAO,EAAE,iCAAiC,EAAE,MAAM,iDAAiD,CAAA;AACnG,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAA;AAEnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA;AAC7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAA;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAA;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA;AAC7E,OAAO,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAA;AAC7F,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAA;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAA;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAA;AACnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,6CAA6C,CAAA;AAC3F,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAA;AAK3E,MAAM,YAAY,GAAG;IACnB,oBAAoB,EAAE,qBAAqB;IAC3C,mBAAmB,EAAE,oBAAoB;IACzC,4BAA4B,EAAE,4BAA4B;IAC1D,gBAAgB,EAAE,iBAAiB;IACnC,kBAAkB,EAAE,mBAAmB;IACvC,gBAAgB,EAAE,iBAAiB;IACnC,YAAY,EAAE,aAAa;IAC3B,kBAAkB,EAAE,mBAAmB;IACvC,mBAAmB,EAAE,oBAAoB;IACzC,YAAY,EAAE,aAAa;IAC3B,SAAS,EAAE,WAAW;IACtB,eAAe,EAAE,gBAAgB;IACjC,gBAAgB,EAAE,iBAAiB;IACnC,gBAAgB,EAAE,iBAAiB;IACnC,yBAAyB,EAAE,yBAAyB;IACpD,eAAe,EAAE,gBAAgB;IACjC,cAAc,EAAE,eAAe;IAC/B,QAAQ,EAAE,UAAU;IACpB,oBAAoB,EAAE,mBAAmB;IACzC,WAAW,EAAE,YAAY;IACzB,mBAAmB,EAAE,oBAAoB;IACzC,iBAAiB,EAAE,kBAAkB;IACrC,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,YAAY;IACzB,wBAAwB,EAAE,wBAAwB;IAClD,gBAAgB,EAAE,gBAAgB;CAC1B,CAAA;AASV,MAAM,gBAAgB,GAA2B;IAC/C;QACE,IAAI,EAAE,YAAY,CAAC,oBAAoB;QACvC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,0BAA0B,CAAC,EAAE,CAAC;KACjD;IACD;QACE,IAAI,EAAE,YAAY,CAAC,mBAAmB;QACtC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,yBAAyB,CAAC,EAAE,CAAC;KAChD;IACD;QACE,IAAI,EAAE,YAAY,CAAC,4BAA4B;QAC/C,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,iCAAiC,CAAC,EAAE,CAAC;KACxD;IACD;QACE,IAAI,EAAE,YAAY,CAAC,gBAAgB;QACnC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,sBAAsB,EAAE;KAC3C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,kBAAkB;QACrC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,wBAAwB,EAAE;KAC7C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,gBAAgB;QACnC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,sBAAsB,EAAE;KAC3C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,YAAY;QAC/B,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,kBAAkB,CAAC,EAAE,CAAC;KACzC;IACD;QACE,IAAI,EAAE,YAAY,CAAC,kBAAkB;QACrC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,wBAAwB,EAAE;KAC7C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,mBAAmB;QACtC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,yBAAyB,EAAE;KAC9C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,YAAY;QAC/B,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,kBAAkB,CAAC,EAAE,CAAC;KACzC;IACD;QACE,IAAI,EAAE,YAAY,CAAC,SAAS;QAC5B,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,gBAAgB,EAAE;KACrC;IACD;QACE,IAAI,EAAE,YAAY,CAAC,eAAe;QAClC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,qBAAqB,CAAC,EAAE,CAAC;KAC5C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,gBAAgB;QACnC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,sBAAsB,EAAE;KAC3C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,gBAAgB;QACnC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,sBAAsB,EAAE;KAC3C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,yBAAyB;QAC5C,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,8BAA8B,EAAE;KACnD;IACD;QACE,IAAI,EAAE,YAAY,CAAC,eAAe;QAClC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,qBAAqB,EAAE;KAC1C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,cAAc;QACjC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,oBAAoB,CAAC,EAAE,CAAC;KAC3C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,QAAQ;QAC3B,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,eAAe,CAAC,EAAE,CAAC;KACtC;IACD;QACE,IAAI,EAAE,YAAY,CAAC,oBAAoB;QACvC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,wBAAwB,CAAC,EAAE,CAAC;KAC/C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,WAAW;QAC9B,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,iBAAiB,CAAC,EAAE,CAAC;KACxC;IACD;QACE,IAAI,EAAE,YAAY,CAAC,mBAAmB;QACtC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,yBAAyB,EAAE;KAC9C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,iBAAiB;QACpC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,uBAAuB,EAAE;KAC5C;IACD;QACE,IAAI,EAAE,YAAY,CAAC,MAAM;QACzB,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,aAAa,EAAE;KAClC;IACD;QACE,IAAI,EAAE,YAAY,CAAC,WAAW;QAC9B,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,iBAAiB,CAAC,EAAE,CAAC;KACxC;IACD;QACE,IAAI,EAAE,YAAY,CAAC,wBAAwB;QAC3C,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,6BAA6B,CAAC,EAAE,CAAC;KACpD;IACD;QACE,IAAI,EAAE,YAAY,CAAC,gBAAgB;QACnC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,qBAAqB,EAAE;KAC1C;CACF,CAAA;AAYD,MAAM,OAAO,eAAe;IACnB,OAAO,CACZ,YAAoB,EACpB,YAAyB,EACzB,YAA2B,EAC3B,aAA0D,EAC1D,eAA8B,EAAE,EAChC,YAA0B,EAC1B,SAA0B;QAE1B,IAAI,IAAuB,CAAA;QAC3B,IAAI,WAA8B,CAAA;QAClC,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,GAAG,SAAS,CAAC,IAAI,CAAA;YACrB,WAAW,GAAG,SAAS,CAAC,WAAW,CAAA;QACrC,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,IAAI,SAAS,CACzB,IAAI,0BAA0B,CAAC,OAAO,EAAE,YAAY,CAAC,CACtD,CAAA;YACD,WAAW,GAAG,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAA;YAC1C,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAA;YAC1C,IAAI,GAAG,MAAM,CAAC,eAAe,EAAuB,CAAA;QACtD,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,CAAA;QAE3D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAA;QAC1E,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAE5C,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CACnC,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,CACZ,CAAA;QAED,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,QAA8B,EAAE,IAAI,CAAC,CAAA;QAElE,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,YAAY,EAAE,EAAE,WAAW,EAAE,CAAA;IAC5D,CAAC;IAEM,MAAM,CACX,QAAsB,EACtB,WAA8B;QAE9B,MAAM,QAAQ,GAAG,IAAI,mBAAmB,CAAC,WAAW,CAAC,CAAA;QACrD,QAAQ,CAAC,OAAO,CACd,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,EACrC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EACnC,QAAQ,CAAC,WAAW,CACrB,CAAA;QACD,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAA;IAC3B,CAAC;IAEO,cAAc,CAAC,aAGtB;QACC,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,gBAAgB,CAAA;QACzB,CAAC;QAED,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,IAAI,aAAa,CAAC,OAAO,IAAI,EAAE,CAAA;QAClE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;QACxD,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,gBAAgB,CAAA;QACzB,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAA;QAEjC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;QAChD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;YAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;YACnD,OAAO,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;QACrE,CAAC;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC;IAEO,mBAAmB,CAAC,cAA2B;QACrD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;QAC3E,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;YAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,OAAO,CAAC,WAAW,CAAC,0BAA0B,IAAI,cAAc,CAAC,CAAA;YACnE,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"mutantGenerator.js","sourceRoot":"","sources":["../../src/service/mutantGenerator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { ApexMutation } from '../type/ApexMutation.js';
2
+ export interface MutationGroup {
3
+ mutations: ApexMutation[];
4
+ testMethods: Set<string>;
5
+ }
6
+ export interface GroupingResult {
7
+ groups: MutationGroup[];
8
+ lowerBound: number;
9
+ }
10
+ interface GroupingInternals {
11
+ adjacency: number[][];
12
+ witness: number[];
13
+ coloring: number[];
14
+ tests: ReadonlyArray<Set<string>>;
15
+ }
16
+ type GroupingResultWithInternals = GroupingResult & {
17
+ internals: GroupingInternals;
18
+ };
19
+ export declare const groupMutations: (mutations: ReadonlyArray<ApexMutation>, testMethodsPerLine: Map<number, Set<string>>) => GroupingResult;
20
+ export declare const groupMutationsWithInternals: (mutations: ReadonlyArray<ApexMutation>, testMethodsPerLine: Map<number, Set<string>>) => GroupingResultWithInternals;
21
+ export declare const assembleGroups: (mutations: ReadonlyArray<ApexMutation>, tests: ReadonlyArray<Set<string>>, color: ReadonlyArray<number>) => MutationGroup[];
22
+ export {};
@@ -0,0 +1,130 @@
1
+ import { buildAdjacency } from './exactColoring.js';
2
+ // Partition mutations into the smallest number of conflict-free groups using
3
+ // DSATUR (Brélaz, 1979): the strongest polynomial-time graph-coloring
4
+ // heuristic. At each step, color the uncolored vertex whose **saturation**
5
+ // (count of distinct colors already used by its colored neighbors) is highest;
6
+ // tiebreak on raw degree, then input order. Provably optimal on bipartite
7
+ // graphs and several other structured classes; near-optimal on the rest.
8
+ //
9
+ // Each color = one batched deployment + one batched test run. Two mutations
10
+ // share a color iff their covering tests are pairwise disjoint, so test
11
+ // outcomes can be reverse-mapped per mutation without ambiguity.
12
+ //
13
+ // The conflict graph is the intersection graph of per-mutation test sets:
14
+ // every test method T induces a clique among the mutations it covers. The
15
+ // largest such test-induced set is a free lower bound on the chromatic
16
+ // number; pre-coloring it before DSATUR runs both gives the heuristic a
17
+ // maximally-constrained start and surfaces an optimality certificate
18
+ // (groups.length === lowerBound implies provably optimal).
19
+ export const groupMutations = (mutations, testMethodsPerLine) => {
20
+ const { groups, lowerBound } = groupMutationsWithInternals(mutations, testMethodsPerLine);
21
+ return { groups, lowerBound };
22
+ };
23
+ // Like `groupMutations` but additionally exposes the conflict graph, witness
24
+ // clique, DSATUR coloring, and per-mutation test sets. Used by the optional
25
+ // SAT-based exact-coloring path so it can re-use the work without rebuilding
26
+ // the graph.
27
+ export const groupMutationsWithInternals = (mutations, testMethodsPerLine) => {
28
+ const n = mutations.length;
29
+ if (n === 0) {
30
+ return {
31
+ groups: [],
32
+ lowerBound: 0,
33
+ internals: { adjacency: [], witness: [], coloring: [], tests: [] },
34
+ };
35
+ }
36
+ const tests = mutations.map(m => testMethodsPerLine.get(m.target.startToken.line) ?? new Set());
37
+ const adjacency = buildAdjacency(tests);
38
+ const degree = adjacency.map(neighbors => neighbors.length);
39
+ const witness = computeLowerBoundClique(tests);
40
+ const color = new Array(n).fill(-1);
41
+ const saturation = new Array(n).fill(0);
42
+ const neighborColors = Array.from({ length: n }, () => new Set());
43
+ for (let k = 0; k < witness.length; ++k) {
44
+ const v = witness[k];
45
+ color[v] = k;
46
+ propagate(v, k, adjacency, color, neighborColors, saturation);
47
+ }
48
+ for (let step = 0; step < n - witness.length; ++step) {
49
+ const pick = pickNextVertex(color, saturation, degree);
50
+ const chosenColor = pickSmallestAvailableColor(neighborColors[pick]);
51
+ color[pick] = chosenColor;
52
+ propagate(pick, chosenColor, adjacency, color, neighborColors, saturation);
53
+ }
54
+ return {
55
+ groups: assembleGroups(mutations, tests, color),
56
+ lowerBound: witness.length,
57
+ internals: { adjacency, witness, coloring: color, tests },
58
+ };
59
+ };
60
+ // Returns the indices of the largest test-induced clique. Each test method T
61
+ // induces a clique among {m : T ∈ tests(m)}; we pick the largest such bucket.
62
+ // O(n · k) where k = avg tests per mutation. Indexes by mutation (not by
63
+ // line) so two mutations sharing the same Set reference still produce two
64
+ // distinct witness entries.
65
+ const computeLowerBoundClique = (tests) => {
66
+ const testToMutations = new Map();
67
+ for (let i = 0; i < tests.length; ++i) {
68
+ for (const t of tests[i]) {
69
+ const bucket = testToMutations.get(t);
70
+ if (bucket === undefined)
71
+ testToMutations.set(t, [i]);
72
+ else
73
+ bucket.push(i);
74
+ }
75
+ }
76
+ let best = [];
77
+ for (const indices of testToMutations.values()) {
78
+ if (indices.length > best.length)
79
+ best = indices;
80
+ }
81
+ // Stable canonical order: ascending by mutation index. Combined with the
82
+ // strict-`>` tiebreak in pickNextVertex, makes the entire pipeline
83
+ // deterministic for fixed input.
84
+ return best.slice().sort((a, b) => a - b);
85
+ };
86
+ const propagate = (v, c, adjacency, color, neighborColors, saturation) => {
87
+ for (const neighbor of adjacency[v]) {
88
+ if (color[neighbor] === -1 && !neighborColors[neighbor].has(c)) {
89
+ neighborColors[neighbor].add(c);
90
+ ++saturation[neighbor];
91
+ }
92
+ }
93
+ };
94
+ // Strict `>` comparisons mean equal-key candidates retain the first-encountered
95
+ // pick — which is the lowest unprocessed index. Switching to `>=` would
96
+ // silently invert determinism on every tied pair; keep the contract.
97
+ const pickNextVertex = (color, saturation, degree) => {
98
+ let pick = -1;
99
+ for (let i = 0; i < color.length; ++i) {
100
+ if (color[i] !== -1)
101
+ continue;
102
+ if (pick === -1 ||
103
+ saturation[i] > saturation[pick] ||
104
+ (saturation[i] === saturation[pick] && degree[i] > degree[pick])) {
105
+ pick = i;
106
+ }
107
+ }
108
+ return pick;
109
+ };
110
+ const pickSmallestAvailableColor = (neighborColors) => {
111
+ let candidate = 0;
112
+ while (neighborColors.has(candidate)) {
113
+ ++candidate;
114
+ }
115
+ return candidate;
116
+ };
117
+ export const assembleGroups = (mutations, tests, color) => {
118
+ const groups = [];
119
+ for (let i = 0; i < mutations.length; ++i) {
120
+ const c = color[i];
121
+ while (groups.length <= c) {
122
+ groups.push({ mutations: [], testMethods: new Set() });
123
+ }
124
+ groups[c].mutations.push(mutations[i]);
125
+ for (const t of tests[i])
126
+ groups[c].testMethods.add(t);
127
+ }
128
+ return groups;
129
+ };
130
+ //# sourceMappingURL=mutationGrouper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutationGrouper.js","sourceRoot":"","sources":["../../src/service/mutationGrouper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { ApexMutation } from '../type/ApexMutation.js';
2
+ export declare const calculateMutationPosition: (mutation: ApexMutation) => {
3
+ start: {
4
+ line: number;
5
+ column: number;
6
+ };
7
+ end: {
8
+ line: number;
9
+ column: number;
10
+ };
11
+ };
12
+ export declare const extractMutationOriginalText: (mutation: ApexMutation, sourceContent: string) => string;
@@ -0,0 +1,54 @@
1
+ // Advance a 1-indexed (line, column) cursor through `text`, returning the
2
+ // position immediately AFTER the last character. Handles tokens whose text
3
+ // spans newlines (multi-line string literals, block comments).
4
+ //
5
+ // Used to compute the Stryker `end` position for a mutation: ANTLR tokens
6
+ // expose `line` and `charPositionInLine` for the START of the token but not
7
+ // past the end; walking `endToken.text` closes that gap without needing a
8
+ // separate line-offset index over the whole source.
9
+ const advancePosition = (text, startLine, startColumn) => {
10
+ let line = startLine;
11
+ let column = startColumn;
12
+ for (let i = 0; i < text.length; i++) {
13
+ if (text.charCodeAt(i) === 10 /* \n */) {
14
+ line++;
15
+ column = 1;
16
+ }
17
+ else {
18
+ column++;
19
+ }
20
+ }
21
+ return { line, column };
22
+ };
23
+ export const calculateMutationPosition = (mutation) => {
24
+ const start = mutation.target.startToken;
25
+ const end = mutation.target.endToken;
26
+ if (start.startIndex === undefined ||
27
+ end.stopIndex === undefined ||
28
+ end.text === undefined) {
29
+ throw new Error(`Failed to calculate position for mutation: ${mutation.mutationName}`);
30
+ }
31
+ // ANTLR tokens expose the position of the FIRST character directly.
32
+ // The Stryker `end` position is exclusive (one past the last char), so
33
+ // we walk endToken.text to advance from the end token's own start.
34
+ // This correctly handles tokens that span newlines (multi-line string
35
+ // literals, block comments).
36
+ return {
37
+ start: {
38
+ line: start.line,
39
+ column: start.charPositionInLine + 1,
40
+ },
41
+ end: advancePosition(end.text, end.line, end.charPositionInLine + 1),
42
+ };
43
+ };
44
+ export const extractMutationOriginalText = (mutation, sourceContent) => {
45
+ const start = mutation.target.startToken;
46
+ const end = mutation.target.endToken;
47
+ if (start.startIndex !== undefined &&
48
+ end.stopIndex !== undefined &&
49
+ sourceContent) {
50
+ return sourceContent.substring(start.startIndex, end.stopIndex + 1);
51
+ }
52
+ throw new Error(`Failed to extract original text for mutation: ${mutation.mutationName}`);
53
+ };
54
+ //# sourceMappingURL=mutationLocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutationLocation.js","sourceRoot":"","sources":["../../src/service/mutationLocation.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,2EAA2E;AAC3E,+DAA+D;AAC/D,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,0EAA0E;AAC1E,oDAAoD;AACpD,MAAM,eAAe,GAAG,CACtB,IAAY,EACZ,SAAiB,EACjB,WAAmB,EACe,EAAE;IACpC,IAAI,IAAI,GAAG,SAAS,CAAA;IACpB,IAAI,MAAM,GAAG,WAAW,CAAA;IACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC;YACvC,IAAI,EAAE,CAAA;YACN,MAAM,GAAG,CAAC,CAAA;QACZ,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,CAAA;QACV,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;AACzB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,QAAsB,EAItB,EAAE;IACF,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAA;IACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAA;IAEpC,IACE,KAAK,CAAC,UAAU,KAAK,SAAS;QAC9B,GAAG,CAAC,SAAS,KAAK,SAAS;QAC3B,GAAG,CAAC,IAAI,KAAK,SAAS,EACtB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,8CAA8C,QAAQ,CAAC,YAAY,EAAE,CACtE,CAAA;IACH,CAAC;IAED,oEAAoE;IACpE,uEAAuE;IACvE,mEAAmE;IACnE,sEAAsE;IACtE,6BAA6B;IAC7B,OAAO;QACL,KAAK,EAAE;YACL,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM,EAAE,KAAK,CAAC,kBAAkB,GAAG,CAAC;SACrC;QACD,GAAG,EAAE,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,kBAAkB,GAAG,CAAC,CAAC;KACrE,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,QAAsB,EACtB,aAAqB,EACb,EAAE;IACV,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAA;IACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAA;IAEpC,IACE,KAAK,CAAC,UAAU,KAAK,SAAS;QAC9B,GAAG,CAAC,SAAS,KAAK,SAAS;QAC3B,aAAa,EACb,CAAC;QACD,OAAO,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAAA;IACrE,CAAC;IAED,MAAM,IAAI,KAAK,CACb,iDAAiD,QAAQ,CAAC,YAAY,EAAE,CACzE,CAAA;AACH,CAAC,CAAA"}
@@ -16,12 +16,12 @@ export declare class MutationTestingService {
16
16
  protected readonly excludeTestMethods: string[] | undefined;
17
17
  private readonly skipPatterns;
18
18
  private readonly allowedLines;
19
+ private readonly mutationGroupingEnabled;
19
20
  private apexClassContent;
20
- constructor(progress: Progress, spinner: Spinner, connection: Connection, { apexClassName, apexTestClassName, dryRun, includeMutators, excludeMutators, includeTestMethods, excludeTestMethods, skipPatterns, lines, }: ApexMutationParameter, messages: Messages<string>);
21
+ constructor(progress: Progress, spinner: Spinner, connection: Connection, { apexClassName, apexTestClassName, dryRun, includeMutators, excludeMutators, includeTestMethods, excludeTestMethods, skipPatterns, lines, mutationGrouping, }: ApexMutationParameter, messages: Messages<string>);
21
22
  process(): Promise<ApexMutationTestResult>;
23
+ private planGroups;
22
24
  calculateScore(mutationResult: ApexMutationTestResult): number;
23
- private buildMutantResult;
24
- private calculateMutationPosition;
25
25
  private filterTestMethods;
26
26
  private createAdapters;
27
27
  private fetchApexClass;
@@ -35,8 +35,5 @@ export declare class MutationTestingService {
35
35
  private displayTimeEstimate;
36
36
  private buildDryRunResult;
37
37
  private executeMutationLoop;
38
- private evaluateMutation;
39
- private formatRemainingTime;
40
38
  private rollback;
41
- private extractMutationOriginalText;
42
39
  }
@@ -2,59 +2,14 @@ import { ApexClassRepository } from '../adapter/apexClassRepository.js';
2
2
  import { ApexTestRunner } from '../adapter/apexTestRunner.js';
3
3
  import { SObjectDescribeRepository } from '../adapter/sObjectDescribeRepository.js';
4
4
  import { ConfigReader } from './configReader.js';
5
+ import { decideExactOutcome, solveColoring } from './exactColoring.js';
6
+ import { GroupExecutor } from './groupExecutor.js';
5
7
  import { MutantGenerator } from './mutantGenerator.js';
8
+ import { assembleGroups, groupMutationsWithInternals, } from './mutationGrouper.js';
9
+ import { calculateMutationPosition, extractMutationOriginalText, } from './mutationLocation.js';
6
10
  import { formatDuration, timeExecution } from './timeUtils.js';
7
11
  import { TypeDiscoverer } from './typeDiscoverer.js';
8
12
  import { ApexClassTypeMatcher, SObjectTypeMatcher } from './typeMatcher.js';
9
- /**
10
- * Advance a 1-indexed (line, column) cursor through `text`, returning the
11
- * position immediately AFTER the last character. Handles tokens whose text
12
- * spans newlines (multi-line string literals, block comments).
13
- *
14
- * Used to compute the Stryker `end` position for a mutation: ANTLR tokens
15
- * expose `line` and `charPositionInLine` for the START of the token but not
16
- * past the end; walking `endToken.text` closes that gap without needing a
17
- * separate line-offset index over the whole source.
18
- */
19
- function advancePosition(text, startLine, startColumn) {
20
- let line = startLine;
21
- let column = startColumn;
22
- for (let i = 0; i < text.length; i++) {
23
- if (text.charCodeAt(i) === 10 /* \n */) {
24
- line++;
25
- column = 1;
26
- }
27
- else {
28
- column++;
29
- }
30
- }
31
- return { line, column };
32
- }
33
- const errorStrategies = [
34
- {
35
- matches: msg => msg.startsWith('Deployment failed:'),
36
- classify: (msg, targetInfo) => ({
37
- status: 'CompileError',
38
- statusReason: msg,
39
- progressMessage: `Mutation result: compile error at line ${targetInfo.line}`,
40
- }),
41
- },
42
- {
43
- matches: msg => msg.includes('LIMIT_USAGE_FOR_NS'),
44
- classify: msg => ({
45
- status: 'Killed',
46
- progressMessage: `Mutation result: mutant killed (${msg})`,
47
- }),
48
- },
49
- {
50
- matches: () => true,
51
- classify: msg => ({
52
- status: 'RuntimeError',
53
- statusReason: msg,
54
- progressMessage: `Mutation result: runtime error (${msg})`,
55
- }),
56
- },
57
- ];
58
13
  export class MutationTestingService {
59
14
  progress;
60
15
  spinner;
@@ -69,8 +24,9 @@ export class MutationTestingService {
69
24
  excludeTestMethods;
70
25
  skipPatterns;
71
26
  allowedLines;
27
+ mutationGroupingEnabled;
72
28
  apexClassContent = '';
73
- constructor(progress, spinner, connection, { apexClassName, apexTestClassName, dryRun, includeMutators, excludeMutators, includeTestMethods, excludeTestMethods, skipPatterns, lines, }, messages) {
29
+ constructor(progress, spinner, connection, { apexClassName, apexTestClassName, dryRun, includeMutators, excludeMutators, includeTestMethods, excludeTestMethods, skipPatterns, lines, mutationGrouping, }, messages) {
74
30
  this.progress = progress;
75
31
  this.spinner = spinner;
76
32
  this.connection = connection;
@@ -84,6 +40,7 @@ export class MutationTestingService {
84
40
  this.excludeTestMethods = excludeTestMethods;
85
41
  this.skipPatterns = ConfigReader.compileSkipPatterns(skipPatterns);
86
42
  this.allowedLines = ConfigReader.parseLineRanges(lines);
43
+ this.mutationGroupingEnabled = mutationGrouping ?? false;
87
44
  }
88
45
  async process() {
89
46
  const { apexClassRepository, apexTestRunner } = this.createAdapters();
@@ -94,14 +51,55 @@ export class MutationTestingService {
94
51
  const { testMethodsPerLine, testTime } = await this.runBaselineTests(apexTestRunner);
95
52
  const coveredLines = this.extractCoveredLines(testMethodsPerLine);
96
53
  const { mutations, mutantGenerator, tokenStream } = this.generateMutations(apexClass, coveredLines, typeAnalysis);
97
- this.displayTimeEstimate(deployTime, testTime, mutations.length);
98
54
  if (this.dryRun) {
55
+ this.displayTimeEstimate(deployTime, testTime, mutations.length, mutations.length);
99
56
  return this.buildDryRunResult(apexClass, mutations);
100
57
  }
101
- const result = await this.executeMutationLoop(apexClass, mutations, mutantGenerator, tokenStream, testMethodsPerLine, apexTestRunner, apexClassRepository);
58
+ const groups = await this.planGroups(mutations, testMethodsPerLine);
59
+ this.displayTimeEstimate(deployTime, testTime, mutations.length, groups.length);
60
+ const result = await this.executeMutationLoop(apexClass, mutations, groups, mutantGenerator, tokenStream, testMethodsPerLine, apexTestRunner, apexClassRepository);
102
61
  await this.rollback(apexClass, apexClassRepository);
103
62
  return result;
104
63
  }
64
+ async planGroups(mutations, testMethodsPerLine) {
65
+ if (!this.mutationGroupingEnabled) {
66
+ // No grouping: one mutation per group. Inlined here rather than going
67
+ // through groupMutations to avoid building the conflict graph for the
68
+ // common (default-off) case.
69
+ return mutations.map(m => ({
70
+ mutations: [m],
71
+ // extractCoveredLines guarantees the line is in the map.
72
+ testMethods: testMethodsPerLine.get(m.target.startToken.line),
73
+ }));
74
+ }
75
+ this.spinner.start(`Grouping ${mutations.length} mutations to minimize deployments`, undefined, { stdout: true });
76
+ const { groups: dsaturGroups, lowerBound, internals, } = groupMutationsWithInternals(mutations, testMethodsPerLine);
77
+ let groups = dsaturGroups;
78
+ const exact = solveColoring({
79
+ adjacency: internals.adjacency,
80
+ n: mutations.length,
81
+ lowerBound,
82
+ dsaturColors: dsaturGroups.length,
83
+ witness: internals.witness,
84
+ dsaturColoring: internals.coloring,
85
+ });
86
+ const decision = decideExactOutcome(exact, dsaturGroups.length);
87
+ const exactSuffix = decision.suffix;
88
+ if (decision.useGroups === 'exact') {
89
+ groups = assembleGroups(mutations, internals.tests, exact.coloring);
90
+ }
91
+ // Division is safe: generateMutations throws when mutations is empty,
92
+ // so planGroups is never reached with mutations.length === 0.
93
+ const savingsPct = Math.round((1 - groups.length / mutations.length) * 100);
94
+ this.spinner.stop(this.messages.getMessage('info.groupingPlan', [
95
+ String(mutations.length),
96
+ String(groups.length),
97
+ String(savingsPct),
98
+ String(lowerBound),
99
+ exactSuffix,
100
+ ]));
101
+ return groups;
102
+ }
105
103
  calculateScore(mutationResult) {
106
104
  const validMutants = mutationResult.mutants.filter(mutant => mutant.status !== 'CompileError');
107
105
  if (validMutants.length === 0) {
@@ -112,42 +110,6 @@ export class MutationTestingService {
112
110
  validMutants.length) *
113
111
  100);
114
112
  }
115
- buildMutantResult(mutation, testResult, targetInfo) {
116
- const mutationStatus = testResult.summary.outcome === 'Passed' ? 'Survived' : 'Killed';
117
- const location = this.calculateMutationPosition(mutation);
118
- const originalText = this.extractMutationOriginalText(mutation);
119
- return {
120
- id: `${this.apexClassName}-${targetInfo.line}-${targetInfo.column}-${targetInfo.tokenIndex}-${Date.now()}`,
121
- mutatorName: mutation.mutationName,
122
- status: mutationStatus,
123
- location,
124
- replacement: mutation.replacement,
125
- original: originalText,
126
- };
127
- }
128
- calculateMutationPosition(mutation) {
129
- const start = mutation.target.startToken;
130
- const end = mutation.target.endToken;
131
- if (start.startIndex === undefined ||
132
- end.stopIndex === undefined ||
133
- end.text === undefined) {
134
- throw new Error(`Failed to calculate position for mutation: ${mutation.mutationName}`);
135
- }
136
- // ANTLR tokens expose the position of the FIRST character directly.
137
- // The Stryker `end` position is exclusive (one past the last char), so
138
- // we walk endToken.text to advance from the end token's own start.
139
- // This correctly handles tokens that span newlines (multi-line string
140
- // literals, block comments).
141
- return {
142
- start: {
143
- line: start.line,
144
- column: start.charPositionInLine + 1,
145
- },
146
- // ANTLR tokens always expose `text`; treat an undefined text as a
147
- // programmer error rather than silently swallowing with an empty default.
148
- end: advancePosition(end.text, end.line, end.charPositionInLine + 1),
149
- };
150
- }
151
113
  filterTestMethods(testMethodsPerLine) {
152
114
  const filterSet = this.includeTestMethods
153
115
  ? new Set(this.includeTestMethods)
@@ -288,8 +250,8 @@ export class MutationTestingService {
288
250
  return { exclude: this.excludeMutators };
289
251
  return undefined;
290
252
  }
291
- displayTimeEstimate(deployTime, testTime, mutationCount) {
292
- const totalEstimateMs = (deployTime + testTime) * mutationCount;
253
+ displayTimeEstimate(deployTime, testTime, mutationCount, groupCount) {
254
+ const totalEstimateMs = (deployTime + testTime) * groupCount;
293
255
  this.spinner.start(this.messages.getMessage('info.timeEstimate', [
294
256
  formatDuration(totalEstimateMs),
295
257
  ]), undefined, { stdout: true });
@@ -297,6 +259,7 @@ export class MutationTestingService {
297
259
  formatDuration(deployTime),
298
260
  formatDuration(testTime),
299
261
  String(mutationCount),
262
+ String(groupCount),
300
263
  ]));
301
264
  }
302
265
  buildDryRunResult(apexClass, mutations) {
@@ -308,96 +271,37 @@ export class MutationTestingService {
308
271
  id: `${this.apexClassName}-${mutation.target.startToken.line}-${mutation.target.startToken.charPositionInLine}-${mutation.target.startToken.tokenIndex}-${Date.now()}`,
309
272
  mutatorName: mutation.mutationName,
310
273
  status: 'Pending',
311
- location: this.calculateMutationPosition(mutation),
274
+ location: calculateMutationPosition(mutation),
312
275
  replacement: mutation.replacement,
313
- original: this.extractMutationOriginalText(mutation),
276
+ original: extractMutationOriginalText(mutation, this.apexClassContent),
314
277
  })),
315
278
  };
316
279
  }
317
- async executeMutationLoop(apexClass, mutations, mutantGenerator, tokenStream, testMethodsPerLine, apexTestRunner, apexClassRepository) {
318
- const mutationResults = {
319
- sourceFile: this.apexClassName,
320
- sourceFileContent: apexClass.Body,
321
- testFile: this.apexTestClassName,
322
- mutants: [],
323
- };
280
+ async executeMutationLoop(apexClass, mutations, groups, mutantGenerator, tokenStream, testMethodsPerLine, apexTestRunner, apexClassRepository) {
324
281
  this.progress.start(mutations.length, { info: 'Starting mutation testing' }, {
325
282
  title: 'MUTATION TESTING PROGRESS',
326
283
  format: '%s | {bar} | {value}/{total} {info}',
327
284
  });
328
- let mutationCount = 0;
285
+ const executor = new GroupExecutor(apexClass, this.apexClassName, this.apexTestClassName, this.apexClassContent, tokenStream, testMethodsPerLine, mutantGenerator, apexTestRunner, apexClassRepository, this.progress, this.messages);
286
+ const indexByMutation = new Map(mutations.map((m, i) => [m, i]));
287
+ const orderedResults = new Array(mutations.length).fill(null);
288
+ let completed = 0;
329
289
  const loopStartTime = performance.now();
330
- for (const mutation of mutations) {
331
- const remainingText = this.formatRemainingTime(loopStartTime, mutationCount, mutations.length);
332
- this.progress.update(mutationCount, {
333
- info: `${remainingText}Deploying "${mutation.replacement}" mutation at line ${mutation.target.startToken.line}`,
334
- });
335
- const testMethods = testMethodsPerLine.get(mutation.target.startToken.line);
336
- if (testMethods) {
337
- this.progress.update(mutationCount, {
338
- info: `${remainingText}Running ${testMethods.size} tests methods for "${mutation.replacement}" mutation at line ${mutation.target.startToken.line}`,
339
- });
290
+ for (const group of groups) {
291
+ const mutantResults = await executor.evaluate(group, completed, loopStartTime, mutations.length);
292
+ for (let i = 0; i < group.mutations.length; ++i) {
293
+ const idx = indexByMutation.get(group.mutations[i]);
294
+ orderedResults[idx] = mutantResults[i];
340
295
  }
341
- const { mutantResult, progressMessage } = await this.evaluateMutation(mutation, mutantGenerator, tokenStream, apexClass, testMethodsPerLine, apexTestRunner, apexClassRepository);
342
- mutationResults.mutants.push(mutantResult);
343
- ++mutationCount;
344
- const updatedRemainingText = this.formatRemainingTime(loopStartTime, mutationCount, mutations.length);
345
- this.progress.update(mutationCount, {
346
- info: `${updatedRemainingText}${progressMessage}`,
347
- });
296
+ completed += group.mutations.length;
348
297
  }
349
298
  this.progress.finish({ info: 'All mutations evaluated' });
350
- return mutationResults;
351
- }
352
- async evaluateMutation(mutation, mutantGenerator, tokenStream, apexClass, testMethodsPerLine, apexTestRunner, apexClassRepository) {
353
- const mutatedVersion = mutantGenerator.mutate(mutation, tokenStream);
354
- const targetInfo = {
355
- line: mutation.target.startToken.line,
356
- column: mutation.target.startToken.charPositionInLine,
357
- tokenIndex: mutation.target.startToken.tokenIndex,
358
- text: mutation.target.text,
299
+ return {
300
+ sourceFile: this.apexClassName,
301
+ sourceFileContent: apexClass.Body,
302
+ testFile: this.apexTestClassName,
303
+ mutants: orderedResults.filter((r) => r !== null),
359
304
  };
360
- try {
361
- await apexClassRepository.update({
362
- Id: apexClass.Id,
363
- Body: mutatedVersion,
364
- });
365
- const testMethods = testMethodsPerLine.get(targetInfo.line);
366
- const testResult = await apexTestRunner.runTestMethods(this.apexTestClassName, testMethods);
367
- return {
368
- mutantResult: this.buildMutantResult(mutation, testResult, targetInfo),
369
- progressMessage: `Mutation result: ${testResult.summary.outcome === 'Passed' ? 'zombie' : 'mutant killed'}`,
370
- };
371
- }
372
- catch (error) {
373
- const errorMessage = error instanceof Error ? error.message : String(error);
374
- const location = this.calculateMutationPosition(mutation);
375
- const originalText = this.extractMutationOriginalText(mutation);
376
- const strategy = errorStrategies.find(s => s.matches(errorMessage));
377
- const classification = strategy.classify(errorMessage, targetInfo);
378
- return {
379
- mutantResult: {
380
- id: `${this.apexClassName}-${targetInfo.line}-${targetInfo.column}-${targetInfo.tokenIndex}-${Date.now()}`,
381
- mutatorName: mutation.mutationName,
382
- status: classification.status,
383
- ...(classification.statusReason && {
384
- statusReason: classification.statusReason,
385
- }),
386
- location,
387
- replacement: mutation.replacement,
388
- original: originalText,
389
- },
390
- progressMessage: classification.progressMessage,
391
- };
392
- }
393
- }
394
- formatRemainingTime(loopStartTime, completedCount, totalCount) {
395
- if (completedCount === 0)
396
- return '';
397
- const elapsed = performance.now() - loopStartTime;
398
- const avgPerMutant = elapsed / completedCount;
399
- const remainingMs = avgPerMutant * (totalCount - completedCount);
400
- return `Remaining: ${formatDuration(remainingMs)} | `;
401
305
  }
402
306
  async rollback(apexClass, apexClassRepository) {
403
307
  this.spinner.start(`Rolling back "${this.apexClassName}" ApexClass to its original state`, undefined, { stdout: true });
@@ -411,15 +315,5 @@ export class MutationTestingService {
411
315
  throw new Error(`Rollback of '${this.apexClassName}' failed. The class on the target org is still in a mutated state. Redeploy manually. Underlying cause: ${cause}`);
412
316
  }
413
317
  }
414
- extractMutationOriginalText(mutation) {
415
- const start = mutation.target.startToken;
416
- const end = mutation.target.endToken;
417
- if (start.startIndex !== undefined &&
418
- end.stopIndex !== undefined &&
419
- this.apexClassContent) {
420
- return this.apexClassContent.substring(start.startIndex, end.stopIndex + 1);
421
- }
422
- throw new Error(`Failed to extract original text for mutation: ${mutation.mutationName}`);
423
- }
424
318
  }
425
319
  //# sourceMappingURL=mutationTestingService.js.map