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.
- package/LICENSE.md +9 -0
- package/README.md +143 -0
- package/lib/adapter/apexClassRepository.d.ts +10 -0
- package/lib/adapter/apexClassRepository.js +34 -0
- package/lib/adapter/apexClassRepository.js.map +1 -0
- package/lib/adapter/apexTestRunner.d.ts +7 -0
- package/lib/adapter/apexTestRunner.js +16 -0
- package/lib/adapter/apexTestRunner.js.map +1 -0
- package/lib/commands/apex/mutation/test/run.d.ts +17 -0
- package/lib/commands/apex/mutation/test/run.js +55 -0
- package/lib/commands/apex/mutation/test/run.js.map +1 -0
- package/lib/mutator/baseListener.d.ts +5 -0
- package/lib/mutator/baseListener.js +5 -0
- package/lib/mutator/baseListener.js.map +1 -0
- package/lib/mutator/boundaryConditionMutator.d.ts +6 -0
- package/lib/mutator/boundaryConditionMutator.js +33 -0
- package/lib/mutator/boundaryConditionMutator.js.map +1 -0
- package/lib/mutator/incrementMutator.d.ts +8 -0
- package/lib/mutator/incrementMutator.js +37 -0
- package/lib/mutator/incrementMutator.js.map +1 -0
- package/lib/mutator/mutationListener.d.ts +9 -0
- package/lib/mutator/mutationListener.js +36 -0
- package/lib/mutator/mutationListener.js.map +1 -0
- package/lib/reporter/HTMLReporter.d.ts +5 -0
- package/lib/reporter/HTMLReporter.js +75 -0
- package/lib/reporter/HTMLReporter.js.map +1 -0
- package/lib/service/mutantGenerator.d.ts +6 -0
- package/lib/service/mutantGenerator.js +28 -0
- package/lib/service/mutantGenerator.js.map +1 -0
- package/lib/service/mutationTestingService.d.ts +17 -0
- package/lib/service/mutationTestingService.js +113 -0
- package/lib/service/mutationTestingService.js.map +1 -0
- package/lib/type/ApexClass.d.ts +4 -0
- package/lib/type/ApexClass.js +2 -0
- package/lib/type/ApexClass.js.map +1 -0
- package/lib/type/ApexMutation.d.ts +6 -0
- package/lib/type/ApexMutation.js +2 -0
- package/lib/type/ApexMutation.js.map +1 -0
- package/lib/type/ApexMutationTestResult.d.ts +22 -0
- package/lib/type/ApexMutationTestResult.js +2 -0
- package/lib/type/ApexMutationTestResult.js.map +1 -0
- package/messages/apex.mutation.test.run.md +49 -0
- package/npm-shrinkwrap.json +13119 -0
- package/oclif.manifest.json +119 -0
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"ApexClass.js","sourceRoot":"","sources":["../../src/type/ApexClass.ts"],"names":[],"mappings":""}
|
|
@@ -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 @@
|
|
|
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 💙
|