apex-mutation-testing 1.0.0-dev-6.13527894120-1

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 (45) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +143 -0
  3. package/lib/adapter/apexClassRepository.d.ts +10 -0
  4. package/lib/adapter/apexClassRepository.js +34 -0
  5. package/lib/adapter/apexClassRepository.js.map +1 -0
  6. package/lib/adapter/apexTestRunner.d.ts +7 -0
  7. package/lib/adapter/apexTestRunner.js +16 -0
  8. package/lib/adapter/apexTestRunner.js.map +1 -0
  9. package/lib/commands/apex/mutation/test/run.d.ts +17 -0
  10. package/lib/commands/apex/mutation/test/run.js +55 -0
  11. package/lib/commands/apex/mutation/test/run.js.map +1 -0
  12. package/lib/mutator/baseListener.d.ts +5 -0
  13. package/lib/mutator/baseListener.js +5 -0
  14. package/lib/mutator/baseListener.js.map +1 -0
  15. package/lib/mutator/boundaryConditionMutator.d.ts +6 -0
  16. package/lib/mutator/boundaryConditionMutator.js +33 -0
  17. package/lib/mutator/boundaryConditionMutator.js.map +1 -0
  18. package/lib/mutator/incrementMutator.d.ts +8 -0
  19. package/lib/mutator/incrementMutator.js +37 -0
  20. package/lib/mutator/incrementMutator.js.map +1 -0
  21. package/lib/mutator/mutationListener.d.ts +9 -0
  22. package/lib/mutator/mutationListener.js +36 -0
  23. package/lib/mutator/mutationListener.js.map +1 -0
  24. package/lib/reporter/HTMLReporter.d.ts +5 -0
  25. package/lib/reporter/HTMLReporter.js +75 -0
  26. package/lib/reporter/HTMLReporter.js.map +1 -0
  27. package/lib/service/mutantGenerator.d.ts +6 -0
  28. package/lib/service/mutantGenerator.js +28 -0
  29. package/lib/service/mutantGenerator.js.map +1 -0
  30. package/lib/service/mutationTestingService.d.ts +17 -0
  31. package/lib/service/mutationTestingService.js +113 -0
  32. package/lib/service/mutationTestingService.js.map +1 -0
  33. package/lib/type/ApexClass.d.ts +4 -0
  34. package/lib/type/ApexClass.js +2 -0
  35. package/lib/type/ApexClass.js.map +1 -0
  36. package/lib/type/ApexMutation.d.ts +6 -0
  37. package/lib/type/ApexMutation.js +2 -0
  38. package/lib/type/ApexMutation.js.map +1 -0
  39. package/lib/type/ApexMutationTestResult.d.ts +22 -0
  40. package/lib/type/ApexMutationTestResult.js +2 -0
  41. package/lib/type/ApexMutationTestResult.js.map +1 -0
  42. package/messages/apex.mutation.test.run.md +49 -0
  43. package/npm-shrinkwrap.json +13119 -0
  44. package/oclif.manifest.json +119 -0
  45. package/package.json +250 -0
@@ -0,0 +1,113 @@
1
+ import { ApexClassRepository } from '../adapter/apexClassRepository.js';
2
+ import { ApexTestRunner } from '../adapter/apexTestRunner.js';
3
+ import { MutantGenerator } from './mutantGenerator.js';
4
+ export class MutationTestingService {
5
+ progress;
6
+ spinner;
7
+ connection;
8
+ apexClassName;
9
+ apexClassTestName;
10
+ constructor(progress, spinner, connection, { apexClassName, apexClassTestName, }) {
11
+ this.progress = progress;
12
+ this.spinner = spinner;
13
+ this.connection = connection;
14
+ this.apexClassName = apexClassName;
15
+ this.apexClassTestName = apexClassTestName;
16
+ }
17
+ async process() {
18
+ const apexClassRepository = new ApexClassRepository(this.connection);
19
+ const apexTestRunner = new ApexTestRunner(this.connection);
20
+ this.spinner.start(`Fetching "${this.apexClassName}" ApexClass content`, undefined, {
21
+ stdout: true,
22
+ });
23
+ const apexClass = (await apexClassRepository.read(this.apexClassName));
24
+ this.spinner.stop('Done');
25
+ this.spinner.start(`Generating mutants for "${this.apexClassName}" ApexClass`, undefined, {
26
+ stdout: true,
27
+ });
28
+ const mutantGenerator = new MutantGenerator();
29
+ const mutations = mutantGenerator.compute(apexClass.Body);
30
+ const mutationResults = {
31
+ sourceFile: this.apexClassName,
32
+ sourceFileContent: apexClass.Body,
33
+ testFile: this.apexClassTestName,
34
+ mutants: [],
35
+ };
36
+ this.spinner.stop(`${mutations.length} mutations generated`);
37
+ this.progress.start(mutations.length, { info: 'Starting mutation testing' }, {
38
+ title: 'MUTATION TESTING PROGRESS',
39
+ format: '%s | {bar} | {value}/{total} {info}',
40
+ });
41
+ let mutationCount = 0;
42
+ for (const mutation of mutations) {
43
+ const mutatedVersion = mutantGenerator.mutate(mutation);
44
+ this.progress.update(mutationCount, {
45
+ info: `Deploying "${mutation.replacement}" mutation at line ${mutation.token.symbol.line}`,
46
+ });
47
+ let progressMessage;
48
+ try {
49
+ await apexClassRepository.update({
50
+ Id: apexClass.Id,
51
+ Body: mutatedVersion,
52
+ });
53
+ this.progress.update(mutationCount, {
54
+ info: `Running tests for "${mutation.replacement}" mutation at line ${mutation.token.symbol.line}`,
55
+ });
56
+ const testResult = await apexTestRunner.run(this.apexClassTestName);
57
+ const mutantResult = this.buildMutantResult(mutation, testResult);
58
+ mutationResults.mutants.push(mutantResult);
59
+ progressMessage = `Mutation result: ${testResult.summary.outcome === 'Pass' ? 'zombie' : 'mutant killed'}`;
60
+ }
61
+ catch {
62
+ progressMessage = `Issue while computing "${mutation.replacement}" mutation at line ${mutation.token.symbol.line}`;
63
+ }
64
+ ++mutationCount;
65
+ this.progress.update(mutationCount, {
66
+ info: progressMessage,
67
+ });
68
+ }
69
+ this.progress.finish({
70
+ info: `All mutations evaluated`,
71
+ });
72
+ try {
73
+ this.spinner.start(`Rolling back "${this.apexClassName}" ApexClass to its original state`, undefined, {
74
+ stdout: true,
75
+ });
76
+ await apexClassRepository.update(apexClass);
77
+ this.spinner.stop('Done');
78
+ }
79
+ catch {
80
+ this.spinner.stop('Class not rolled back, please do it manually');
81
+ }
82
+ return mutationResults;
83
+ }
84
+ calculateScore(mutationResult) {
85
+ return ((mutationResult.mutants.filter(mutant => mutant.status === 'Killed')
86
+ .length /
87
+ mutationResult.mutants.length) *
88
+ 100 || 0);
89
+ }
90
+ buildMutantResult(mutation, testResult) {
91
+ const token = mutation.token;
92
+ // TODO Handle NoCoverage
93
+ const mutationStatus = testResult.summary.outcome === 'Pass' ? 'Survived' : 'Killed';
94
+ return {
95
+ id: `${this.apexClassName}-${token.symbol.line}-${token.symbol.charPositionInLine}-${token.symbol.tokenIndex}-${Date.now()}`,
96
+ mutatorName: mutation.mutationName,
97
+ status: mutationStatus,
98
+ location: {
99
+ start: {
100
+ line: token.symbol.line,
101
+ column: token.symbol.charPositionInLine,
102
+ },
103
+ end: {
104
+ line: token.symbol.line,
105
+ column: token.symbol.charPositionInLine + mutation.replacement.length,
106
+ },
107
+ },
108
+ replacement: mutation.replacement,
109
+ original: token.text,
110
+ };
111
+ }
112
+ }
113
+ //# sourceMappingURL=mutationTestingService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutationTestingService.js","sourceRoot":"","sources":["../../src/service/mutationTestingService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export interface ApexClass {
2
+ Id: string;
3
+ Body: string;
4
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ApexClass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApexClass.js","sourceRoot":"","sources":["../../src/type/ApexClass.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { TerminalNode } from 'antlr4ts/tree';
2
+ export interface ApexMutation {
3
+ mutationName: string;
4
+ token: TerminalNode;
5
+ replacement: string;
6
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ApexMutation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApexMutation.js","sourceRoot":"","sources":["../../src/type/ApexMutation.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ export interface ApexMutationTestResult {
2
+ sourceFile: string;
3
+ sourceFileContent: string;
4
+ testFile: string;
5
+ mutants: {
6
+ id: string;
7
+ mutatorName: string;
8
+ status: 'Killed' | 'Survived' | 'NoCoverage';
9
+ location: {
10
+ start: {
11
+ line: number;
12
+ column: number;
13
+ };
14
+ end: {
15
+ line: number;
16
+ column: number;
17
+ };
18
+ };
19
+ replacement: string;
20
+ original: string;
21
+ }[];
22
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ApexMutationTestResult.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApexMutationTestResult.js","sourceRoot":"","sources":["../../src/type/ApexMutationTestResult.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ # summary
2
+
3
+ Evaluate test coverage quality by injecting mutations and measuring test detection rates
4
+
5
+ # description
6
+
7
+ The Apex Mutation Testing plugin helps evaluate the effectiveness of your Apex test classes by introducing mutations into your code and checking if your tests can detect these changes:
8
+
9
+ The plugin provides insights into how trustworthy your test suite is by measuring its ability to catch intentional code changes.
10
+
11
+ # flags.apex-class.summary
12
+
13
+ Apex class name to mutate
14
+
15
+ # flags.test-class.summary
16
+
17
+ Apex test class name to validate mutations
18
+
19
+ # flags.report-dir.summary
20
+
21
+ Path to the directory where mutation test reports will be generated
22
+
23
+ # examples
24
+
25
+ - Run mutation testing on a class with its test file:
26
+
27
+ <%= config.bin %> <%= command.id %> --class-file MyClass --test-file MyClassTest
28
+
29
+ # info.reportGenerated
30
+
31
+ Report has been generated at this location: %s
32
+
33
+ # info.CommandIsRunning
34
+
35
+ Running mutation testing for "%s" with "%s" test class
36
+
37
+ # info.CommandSuccess
38
+
39
+ Mutation score: %s%
40
+
41
+ # info.CommandFailure
42
+
43
+ Failure
44
+
45
+ # info.EncourageSponsorship
46
+
47
+ 💡 Enjoying apex-mutation-testing?
48
+ Your contribution helps us provide fast support 🚀 and high quality features 🔥
49
+ Become a sponsor: https://github.com/sponsors/scolladon 💙