apex-mutation-testing 1.1.0 → 1.2.0-dev-53.17212276159-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/README.md +3 -2
- package/lib/adapter/apexClassRepository.d.ts +5 -1
- package/lib/adapter/apexClassRepository.js +23 -1
- package/lib/adapter/apexClassRepository.js.map +1 -1
- package/lib/adapter/apexTestRunner.d.ts +7 -2
- package/lib/adapter/apexTestRunner.js +28 -7
- package/lib/adapter/apexTestRunner.js.map +1 -1
- package/lib/mutator/arithmeticOperatorMutator.d.ts +9 -0
- package/lib/mutator/arithmeticOperatorMutator.js +36 -0
- package/lib/mutator/arithmeticOperatorMutator.js.map +1 -0
- package/lib/mutator/baseListener.d.ts +5 -0
- package/lib/mutator/baseListener.js +21 -0
- package/lib/mutator/baseListener.js.map +1 -1
- package/lib/mutator/boundaryConditionMutator.d.ts +3 -3
- package/lib/mutator/boundaryConditionMutator.js +21 -19
- package/lib/mutator/boundaryConditionMutator.js.map +1 -1
- package/lib/mutator/emptyReturnMutator.d.ts +7 -0
- package/lib/mutator/emptyReturnMutator.js +94 -0
- package/lib/mutator/emptyReturnMutator.js.map +1 -0
- package/lib/mutator/equalityConditionMutator.d.ts +6 -0
- package/lib/mutator/equalityConditionMutator.js +25 -0
- package/lib/mutator/equalityConditionMutator.js.map +1 -0
- package/lib/mutator/falseReturnMutator.d.ts +5 -0
- package/lib/mutator/falseReturnMutator.js +29 -0
- package/lib/mutator/falseReturnMutator.js.map +1 -0
- package/lib/mutator/incrementMutator.d.ts +1 -1
- package/lib/mutator/incrementMutator.js +6 -10
- package/lib/mutator/incrementMutator.js.map +1 -1
- package/lib/mutator/methodTypeListener.d.ts +12 -0
- package/lib/mutator/methodTypeListener.js +114 -0
- package/lib/mutator/methodTypeListener.js.map +1 -0
- package/lib/mutator/mutationListener.d.ts +4 -2
- package/lib/mutator/mutationListener.js +11 -1
- package/lib/mutator/mutationListener.js.map +1 -1
- package/lib/mutator/nullReturnMutator.d.ts +5 -0
- package/lib/mutator/nullReturnMutator.js +26 -0
- package/lib/mutator/nullReturnMutator.js.map +1 -0
- package/lib/mutator/returnTypeAwareBaseListener.d.ts +12 -0
- package/lib/mutator/returnTypeAwareBaseListener.js +24 -0
- package/lib/mutator/returnTypeAwareBaseListener.js.map +1 -0
- package/lib/mutator/trueReturnMutator.d.ts +5 -0
- package/lib/mutator/trueReturnMutator.js +29 -0
- package/lib/mutator/trueReturnMutator.js.map +1 -0
- package/lib/reporter/HTMLReporter.js +1 -1
- package/lib/reporter/HTMLReporter.js.map +1 -1
- package/lib/service/apexTypeResolver.d.ts +9 -0
- package/lib/service/apexTypeResolver.js +18 -0
- package/lib/service/apexTypeResolver.js.map +1 -0
- package/lib/service/mutantGenerator.d.ts +4 -1
- package/lib/service/mutantGenerator.js +30 -6
- package/lib/service/mutantGenerator.js.map +1 -1
- package/lib/service/mutationTestingService.d.ts +4 -0
- package/lib/service/mutationTestingService.js +105 -26
- package/lib/service/mutationTestingService.js.map +1 -1
- package/lib/type/ApexMethod.d.ts +29 -0
- package/lib/type/ApexMethod.js +24 -0
- package/lib/type/ApexMethod.js.map +1 -0
- package/lib/type/ApexMutation.d.ts +7 -2
- package/lib/type/MetadataComponentDependency.d.ts +6 -0
- package/lib/type/MetadataComponentDependency.js +2 -0
- package/lib/type/MetadataComponentDependency.js.map +1 -0
- package/messages/apex.mutation.test.run.md +1 -1
- package/npm-shrinkwrap.json +3992 -3112
- package/oclif.manifest.json +2 -2
- package/package.json +22 -21
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ sf plugins install apex-mutation-testing
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
sf apex mutation test run --class
|
|
19
|
+
sf apex mutation test run --apex-class MyClass --test-class MyClassTest
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## What is it mutation testing ?
|
|
@@ -104,7 +104,7 @@ DESCRIPTION
|
|
|
104
104
|
EXAMPLES
|
|
105
105
|
Run mutation testing on a class with its test file:
|
|
106
106
|
|
|
107
|
-
$ sf apex mutation test run --class
|
|
107
|
+
$ sf apex mutation test run --apex-class MyClass --test-class MyClassTest
|
|
108
108
|
```
|
|
109
109
|
<!-- commandsstop -->
|
|
110
110
|
|
|
@@ -128,6 +128,7 @@ Versioning follows [SemVer](http://semver.org/) specification.
|
|
|
128
128
|
## Authors
|
|
129
129
|
|
|
130
130
|
- **Sebastien Colladon** - Developer - [scolladon](https://github.com/scolladon)
|
|
131
|
+
- **Saman Attar** - Developer - [saman](https://github.com/SamanAttar)
|
|
131
132
|
|
|
132
133
|
Special thanks to **Sara Sali** for her [presentation at Dreamforce](https://www.youtube.com/watch?v=8PjzrTaNNns) about apex mutation testing
|
|
133
134
|
This repository is basically a port of her idea / repo to a sf plugin.
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { Connection } from '@salesforce/core';
|
|
2
2
|
import { ApexClass } from '../type/ApexClass.js';
|
|
3
|
+
import { MetadataComponentDependency } from '../type/MetadataComponentDependency.js';
|
|
3
4
|
export declare class ApexClassRepository {
|
|
4
5
|
protected readonly connection: Connection;
|
|
5
6
|
constructor(connection: Connection);
|
|
6
7
|
read(name: string): Promise<{
|
|
7
8
|
[name: string]: import("@jsforce/jsforce-node").SObjectFieldType | null;
|
|
8
9
|
} & import("@jsforce/jsforce-node").Record>;
|
|
9
|
-
|
|
10
|
+
getApexClassDependencies(classId: string): Promise<MetadataComponentDependency[]>;
|
|
11
|
+
update(apexClass: ApexClass): Promise<{
|
|
12
|
+
[name: string]: import("@jsforce/jsforce-node").SObjectFieldType | null;
|
|
13
|
+
} & import("@jsforce/jsforce-node").Record>;
|
|
10
14
|
}
|
|
@@ -9,6 +9,12 @@ export class ApexClassRepository {
|
|
|
9
9
|
.find({ Name: name })
|
|
10
10
|
.execute())[0];
|
|
11
11
|
}
|
|
12
|
+
async getApexClassDependencies(classId) {
|
|
13
|
+
return (await this.connection.tooling
|
|
14
|
+
.sobject('MetadataComponentDependency')
|
|
15
|
+
.find({ MetadataComponentId: classId })
|
|
16
|
+
.execute());
|
|
17
|
+
}
|
|
12
18
|
async update(apexClass) {
|
|
13
19
|
const container = await this.connection.tooling
|
|
14
20
|
.sobject('MetadataContainer')
|
|
@@ -22,13 +28,29 @@ export class ApexClassRepository {
|
|
|
22
28
|
Body: apexClass.Body,
|
|
23
29
|
});
|
|
24
30
|
// Create ContainerAsyncRequest to deploy
|
|
25
|
-
|
|
31
|
+
const asyncRequest = await this.connection.tooling
|
|
26
32
|
.sobject('ContainerAsyncRequest')
|
|
27
33
|
.create({
|
|
28
34
|
IsCheckOnly: false,
|
|
29
35
|
MetadataContainerId: container.id,
|
|
30
36
|
IsRunTests: true,
|
|
31
37
|
});
|
|
38
|
+
if (!asyncRequest.id) {
|
|
39
|
+
throw new Error('ContainerAsyncRequest did not return an ID');
|
|
40
|
+
}
|
|
41
|
+
const result = await this.connection.tooling
|
|
42
|
+
.sobject('ContainerAsyncRequest')
|
|
43
|
+
.retrieve(asyncRequest.id);
|
|
44
|
+
if (result['State'] === 'Failed') {
|
|
45
|
+
const messages = result['DeployDetails']?.['allComponentMessages'];
|
|
46
|
+
const formattedErrors = Array.isArray(messages)
|
|
47
|
+
? messages
|
|
48
|
+
.map(m => `[${m.fileName}:${m.lineNumber}:${m.columnNumber}] ${m.problem}`)
|
|
49
|
+
.join('\n')
|
|
50
|
+
: result['ErrorMsg'] || 'Unknown error';
|
|
51
|
+
throw new Error(`Deployment failed:\n${formattedErrors}`);
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
32
54
|
}
|
|
33
55
|
}
|
|
34
56
|
//# sourceMappingURL=apexClassRepository.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apexClassRepository.js","sourceRoot":"","sources":["../../src/adapter/apexClassRepository.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"apexClassRepository.js","sourceRoot":"","sources":["../../src/adapter/apexClassRepository.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,mBAAmB;IACC;IAA/B,YAA+B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAElD,KAAK,CAAC,IAAI,CAAC,IAAY;QAC5B,OAAO,CACL,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO;aAC1B,OAAO,CAAC,WAAW,CAAC;aACpB,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;aACpB,OAAO,EAAE,CACb,CAAC,CAAC,CAAC,CAAA;IACN,CAAC;IAEM,KAAK,CAAC,wBAAwB,CACnC,OAAe;QAEf,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO;aAClC,OAAO,CAAC,6BAA6B,CAAC;aACtC,IAAI,CAAC,EAAE,mBAAmB,EAAE,OAAO,EAAE,CAAC;aACtC,OAAO,EAAE,CAAkC,CAAA;IAChD,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,SAAoB;QACtC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO;aAC5C,OAAO,CAAC,mBAAmB,CAAC;aAC5B,MAAM,CAAC;YACN,IAAI,EAAE,gBAAgB,IAAI,CAAC,GAAG,EAAE,EAAE;SACnC,CAAC,CAAA;QAEJ,iDAAiD;QACjD,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC;YAC9D,mBAAmB,EAAE,SAAS,CAAC,EAAE;YACjC,eAAe,EAAE,SAAS,CAAC,EAAE;YAC7B,IAAI,EAAE,SAAS,CAAC,IAAI;SACrB,CAAC,CAAA;QAEF,yCAAyC;QACzC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO;aAC/C,OAAO,CAAC,uBAAuB,CAAC;aAChC,MAAM,CAAC;YACN,WAAW,EAAE,KAAK;YAClB,mBAAmB,EAAE,SAAS,CAAC,EAAE;YACjC,UAAU,EAAE,IAAI;SACjB,CAAC,CAAA;QAEJ,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;QAC/D,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO;aACzC,OAAO,CAAC,uBAAuB,CAAC;aAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;QAE5B,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAA;YAClE,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;gBAC7C,CAAC,CAAC,QAAQ;qBACL,GAAG,CACF,CAAC,CAAC,EAAE,CACF,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,OAAO,EAAE,CACnE;qBACA,IAAI,CAAC,IAAI,CAAC;gBACf,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,eAAe,CAAA;YAEzC,MAAM,IAAI,KAAK,CAAC,uBAAuB,eAAe,EAAE,CAAC,CAAA;QAC3D,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CACF"}
|
|
@@ -3,7 +3,12 @@ import { Connection } from '@salesforce/core';
|
|
|
3
3
|
export declare class ApexTestRunner {
|
|
4
4
|
protected readonly testService: TestService;
|
|
5
5
|
constructor(connection: Connection);
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
getTestMethodsPerLines(className: string): Promise<{
|
|
7
|
+
outcome: string;
|
|
8
|
+
testsRan: number;
|
|
9
|
+
failing: number;
|
|
10
|
+
testMethodsPerLine: Map<number, Set<string>>;
|
|
11
|
+
}>;
|
|
12
|
+
runTestMethods(className: string, testMethods?: Set<string>): Promise<TestResult>;
|
|
8
13
|
private runTestAsynchronous;
|
|
9
14
|
}
|
|
@@ -4,16 +4,37 @@ export class ApexTestRunner {
|
|
|
4
4
|
constructor(connection) {
|
|
5
5
|
this.testService = new TestService(connection);
|
|
6
6
|
}
|
|
7
|
-
async
|
|
8
|
-
const testResult = await this.runTestAsynchronous(
|
|
9
|
-
|
|
7
|
+
async getTestMethodsPerLines(className) {
|
|
8
|
+
const testResult = await this.runTestAsynchronous({ className }, false);
|
|
9
|
+
const testMethodsPerLine = new Map();
|
|
10
|
+
testResult.tests?.forEach(test => {
|
|
11
|
+
test.perClassCoverage?.forEach(testMethodExecutionResult => {
|
|
12
|
+
testMethodExecutionResult.coverage?.coveredLines?.forEach(line => {
|
|
13
|
+
if (!testMethodsPerLine.has(line)) {
|
|
14
|
+
testMethodsPerLine.set(line, new Set());
|
|
15
|
+
}
|
|
16
|
+
testMethodsPerLine
|
|
17
|
+
.get(line)
|
|
18
|
+
.add(testMethodExecutionResult.apexTestMethodName);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
outcome: testResult.summary.outcome,
|
|
24
|
+
testsRan: testResult.summary.testsRan,
|
|
25
|
+
failing: testResult.summary.failing,
|
|
26
|
+
testMethodsPerLine,
|
|
27
|
+
};
|
|
10
28
|
}
|
|
11
|
-
async
|
|
12
|
-
return await this.runTestAsynchronous(
|
|
29
|
+
async runTestMethods(className, testMethods = new Set()) {
|
|
30
|
+
return await this.runTestAsynchronous({
|
|
31
|
+
className,
|
|
32
|
+
testMethods: Array.from(testMethods),
|
|
33
|
+
});
|
|
13
34
|
}
|
|
14
|
-
async runTestAsynchronous(
|
|
35
|
+
async runTestAsynchronous(testPerimeter, skipCodeCoverage = true) {
|
|
15
36
|
return (await this.testService.runTestAsynchronous({
|
|
16
|
-
tests: [
|
|
37
|
+
tests: [testPerimeter],
|
|
17
38
|
testLevel: "RunSpecifiedTests" /* TestLevel.RunSpecifiedTests */,
|
|
18
39
|
skipCodeCoverage,
|
|
19
40
|
maxFailedTests: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apexTestRunner.js","sourceRoot":"","sources":["../../src/adapter/apexTestRunner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAG1E,MAAM,OAAO,cAAc;IACN,WAAW,CAAa;IAC3C,YAAY,UAAsB;QAChC,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAA;IAChD,CAAC;IAEM,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"apexTestRunner.js","sourceRoot":"","sources":["../../src/adapter/apexTestRunner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAG1E,MAAM,OAAO,cAAc;IACN,WAAW,CAAa;IAC3C,YAAY,UAAsB;QAChC,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAA;IAChD,CAAC;IAEM,KAAK,CAAC,sBAAsB,CAAC,SAAiB;QACnD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,CAAC,CAAA;QAEvE,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAuB,CAAA;QAEzD,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE;YAC/B,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,yBAAyB,CAAC,EAAE;gBACzD,yBAAyB,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE;oBAC/D,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;wBAClC,kBAAkB,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAU,CAAC,CAAA;oBACjD,CAAC;oBACD,kBAAkB;yBACf,GAAG,CAAC,IAAI,CAAE;yBACV,GAAG,CAAC,yBAAyB,CAAC,kBAAkB,CAAC,CAAA;gBACtD,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QAEF,OAAO;YACL,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,OAAO;YACnC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ;YACrC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,OAAO;YACnC,kBAAkB;SACnB,CAAA;IACH,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,SAAiB,EACjB,cAA2B,IAAI,GAAG,EAAU;QAE5C,OAAO,MAAM,IAAI,CAAC,mBAAmB,CAAC;YACpC,SAAS;YACT,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC;SACrC,CAAC,CAAA;IACJ,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAC/B,aAA4D,EAC5D,mBAA4B,IAAI;QAEhC,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAChD;YACE,KAAK,EAAE,CAAC,aAAa,CAAC;YACtB,SAAS,uDAA6B;YACtC,gBAAgB;YAChB,cAAc,EAAE,CAAC;SAClB,EACD,CAAC,gBAAgB,CAClB,CAAe,CAAA;IAClB,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
+
import { BaseListener } from './baseListener.js';
|
|
3
|
+
export declare class ArithmeticOperatorMutator extends BaseListener {
|
|
4
|
+
private readonly REPLACEMENT_MAP;
|
|
5
|
+
enterArth1Expression(ctx: ParserRuleContext): void;
|
|
6
|
+
enterArth2Expression(ctx: ParserRuleContext): void;
|
|
7
|
+
enterAssignExpression(_ctx: ParserRuleContext): void;
|
|
8
|
+
private processArithmeticOperation;
|
|
9
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TerminalNode } from 'antlr4ts/tree/index.js';
|
|
2
|
+
import { BaseListener } from './baseListener.js';
|
|
3
|
+
export class ArithmeticOperatorMutator extends BaseListener {
|
|
4
|
+
REPLACEMENT_MAP = {
|
|
5
|
+
'+': ['-', '*', '/'],
|
|
6
|
+
'-': ['+', '*', '/'],
|
|
7
|
+
'*': ['+', '-', '/'],
|
|
8
|
+
'/': ['+', '-', '*'],
|
|
9
|
+
};
|
|
10
|
+
// Handle MUL, DIV, and MOD operations (*, /, %)
|
|
11
|
+
enterArth1Expression(ctx) {
|
|
12
|
+
this.processArithmeticOperation(ctx);
|
|
13
|
+
}
|
|
14
|
+
// Handle ADD and SUB operations (+, -)
|
|
15
|
+
enterArth2Expression(ctx) {
|
|
16
|
+
this.processArithmeticOperation(ctx);
|
|
17
|
+
}
|
|
18
|
+
enterAssignExpression(_ctx) {
|
|
19
|
+
// Method intentionally left empty - enables traversal into children
|
|
20
|
+
}
|
|
21
|
+
processArithmeticOperation(ctx) {
|
|
22
|
+
if (ctx.childCount === 3) {
|
|
23
|
+
const operatorNode = ctx.getChild(1);
|
|
24
|
+
if (operatorNode instanceof TerminalNode) {
|
|
25
|
+
const operatorText = operatorNode.text;
|
|
26
|
+
const replacements = this.REPLACEMENT_MAP[operatorText];
|
|
27
|
+
if (replacements) {
|
|
28
|
+
for (const replacement of replacements) {
|
|
29
|
+
this.createMutationFromTerminalNode(operatorNode, replacement);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=arithmeticOperatorMutator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arithmeticOperatorMutator.js","sourceRoot":"","sources":["../../src/mutator/arithmeticOperatorMutator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,OAAO,yBAA0B,SAAQ,YAAY;IACxC,eAAe,GAA6B;QAC3D,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;QACpB,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;QACpB,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;QACpB,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;KACrB,CAAA;IAED,gDAAgD;IAChD,oBAAoB,CAAC,GAAsB;QACzC,IAAI,CAAC,0BAA0B,CAAC,GAAG,CAAC,CAAA;IACtC,CAAC;IAED,uCAAuC;IACvC,oBAAoB,CAAC,GAAsB;QACzC,IAAI,CAAC,0BAA0B,CAAC,GAAG,CAAC,CAAA;IACtC,CAAC;IAED,qBAAqB,CAAC,IAAuB;QAC3C,oEAAoE;IACtE,CAAC;IAEO,0BAA0B,CAAC,GAAsB;QACvD,IAAI,GAAG,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YAEpC,IAAI,YAAY,YAAY,YAAY,EAAE,CAAC;gBACzC,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAA;gBACtC,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAA;gBAEvD,IAAI,YAAY,EAAE,CAAC;oBACjB,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;wBACvC,IAAI,CAAC,8BAA8B,CAAC,YAAY,EAAE,WAAW,CAAC,CAAA;oBAChE,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import { ParserRuleContext, Token } from 'antlr4ts';
|
|
2
|
+
import { TerminalNode } from 'antlr4ts/tree/index.js';
|
|
1
3
|
import { ApexParserListener } from 'apex-parser';
|
|
2
4
|
import { ApexMutation } from '../type/ApexMutation.js';
|
|
3
5
|
export declare class BaseListener implements ApexParserListener {
|
|
4
6
|
_mutations: ApexMutation[];
|
|
7
|
+
protected createMutation(startToken: Token, endToken: Token, originalText: string, replacement: string): void;
|
|
8
|
+
protected createMutationFromParserRuleContext(ctx: ParserRuleContext, replacement: string): void;
|
|
9
|
+
protected createMutationFromTerminalNode(node: TerminalNode, replacement: string): void;
|
|
5
10
|
}
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
// @ts-ignore: Base type with just a common _mutations property
|
|
2
2
|
export class BaseListener {
|
|
3
3
|
_mutations = [];
|
|
4
|
+
createMutation(startToken, endToken, originalText, replacement) {
|
|
5
|
+
this._mutations.push({
|
|
6
|
+
mutationName: this.constructor.name,
|
|
7
|
+
target: {
|
|
8
|
+
startToken,
|
|
9
|
+
endToken,
|
|
10
|
+
text: originalText,
|
|
11
|
+
},
|
|
12
|
+
replacement,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
createMutationFromParserRuleContext(ctx, replacement) {
|
|
16
|
+
if (ctx.start && ctx.stop) {
|
|
17
|
+
this.createMutation(ctx.start, ctx.stop, ctx.text, replacement);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
createMutationFromTerminalNode(node, replacement) {
|
|
21
|
+
if (node.symbol) {
|
|
22
|
+
this.createMutation(node.symbol, node.symbol, node.text, replacement);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
4
25
|
}
|
|
5
26
|
//# sourceMappingURL=baseListener.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baseListener.js","sourceRoot":"","sources":["../../src/mutator/baseListener.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"baseListener.js","sourceRoot":"","sources":["../../src/mutator/baseListener.ts"],"names":[],"mappings":"AAKA,+DAA+D;AAC/D,MAAM,OAAO,YAAY;IACvB,UAAU,GAAmB,EAAE,CAAA;IAErB,cAAc,CACtB,UAAiB,EACjB,QAAe,EACf,YAAoB,EACpB,WAAmB;QAEnB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YACnB,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI;YACnC,MAAM,EAAE;gBACN,UAAU;gBACV,QAAQ;gBACR,IAAI,EAAE,YAAY;aACnB;YACD,WAAW;SACZ,CAAC,CAAA;IACJ,CAAC;IAES,mCAAmC,CAC3C,GAAsB,EACtB,WAAmB;QAEnB,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YAC1B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;QACjE,CAAC;IACH,CAAC;IAES,8BAA8B,CACtC,IAAkB,EAClB,WAAmB;QAEnB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;QACvE,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { BaseListener } from './baseListener.js';
|
|
2
1
|
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
+
import { BaseListener } from './baseListener.js';
|
|
3
3
|
export declare class BoundaryConditionMutator extends BaseListener {
|
|
4
|
-
private REPLACEMENT_MAP;
|
|
5
|
-
|
|
4
|
+
private readonly REPLACEMENT_MAP;
|
|
5
|
+
enterCmpExpression(ctx: ParserRuleContext): void;
|
|
6
6
|
}
|
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
import { BaseListener } from './baseListener.js';
|
|
2
1
|
import { TerminalNode } from 'antlr4ts/tree/index.js';
|
|
2
|
+
import { BaseListener } from './baseListener.js';
|
|
3
3
|
export class BoundaryConditionMutator extends BaseListener {
|
|
4
4
|
REPLACEMENT_MAP = {
|
|
5
|
-
'!=': '==',
|
|
6
|
-
'==': '!=',
|
|
7
5
|
'<': '<=',
|
|
8
6
|
'<=': '<',
|
|
9
7
|
'>': '>=',
|
|
10
8
|
'>=': '>',
|
|
11
|
-
'===': '!==',
|
|
12
|
-
'!==': '===',
|
|
13
9
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
10
|
+
enterCmpExpression(ctx) {
|
|
11
|
+
if (ctx.childCount < 3)
|
|
12
|
+
return;
|
|
13
|
+
let operatorText = '';
|
|
14
|
+
const operatorTokens = [];
|
|
15
|
+
for (let i = 0; i < ctx.childCount; i++) {
|
|
16
|
+
const child = ctx.getChild(i);
|
|
17
|
+
if (child instanceof TerminalNode) {
|
|
18
|
+
const text = child.text;
|
|
19
|
+
if (['>', '<', '=', '!'].includes(text)) {
|
|
20
|
+
operatorText += text;
|
|
21
|
+
if (child.symbol !== null) {
|
|
22
|
+
operatorTokens.push({ node: child, token: child.symbol });
|
|
23
|
+
}
|
|
28
24
|
}
|
|
29
25
|
}
|
|
30
26
|
}
|
|
27
|
+
const replacement = this.REPLACEMENT_MAP[operatorText];
|
|
28
|
+
if (replacement && operatorTokens.length > 0) {
|
|
29
|
+
const startToken = operatorTokens[0].token;
|
|
30
|
+
const endToken = operatorTokens[operatorTokens.length - 1].token;
|
|
31
|
+
this.createMutation(startToken, endToken, operatorText, replacement);
|
|
32
|
+
}
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
//# sourceMappingURL=boundaryConditionMutator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundaryConditionMutator.js","sourceRoot":"","sources":["../../src/mutator/boundaryConditionMutator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"boundaryConditionMutator.js","sourceRoot":"","sources":["../../src/mutator/boundaryConditionMutator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,OAAO,wBAAyB,SAAQ,YAAY;IACvC,eAAe,GAA2B;QACzD,GAAG,EAAE,IAAI;QACT,IAAI,EAAE,GAAG;QACT,GAAG,EAAE,IAAI;QACT,IAAI,EAAE,GAAG;KACV,CAAA;IAED,kBAAkB,CAAC,GAAsB;QACvC,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC;YAAE,OAAM;QAE9B,IAAI,YAAY,GAAG,EAAE,CAAA;QACrB,MAAM,cAAc,GAA2C,EAAE,CAAA;QAEjE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YAC7B,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;gBAClC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;gBAEvB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBACxC,YAAY,IAAI,IAAI,CAAA;oBACpB,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;wBAC1B,cAAc,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;oBAC3D,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAA;QAEtD,IAAI,WAAW,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7C,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;YAC1C,MAAM,QAAQ,GAAG,cAAc,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAA;YAChE,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CAAA;QACtE,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
+
import { ReturnTypeAwareBaseListener } from './returnTypeAwareBaseListener.js';
|
|
3
|
+
export declare class EmptyReturnMutator extends ReturnTypeAwareBaseListener {
|
|
4
|
+
enterReturnStatement(ctx: ParserRuleContext): void;
|
|
5
|
+
private generateEmptyValue;
|
|
6
|
+
isEmptyValue(type: string, expressionText: string): boolean;
|
|
7
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
+
import { ApexType } from '../type/ApexMethod.js';
|
|
3
|
+
import { ReturnTypeAwareBaseListener } from './returnTypeAwareBaseListener.js';
|
|
4
|
+
export class EmptyReturnMutator extends ReturnTypeAwareBaseListener {
|
|
5
|
+
enterReturnStatement(ctx) {
|
|
6
|
+
if (!this.isCurrentMethodTypeKnown()) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const typeInfo = this.getCurrentMethodReturnTypeInfo();
|
|
10
|
+
if (!typeInfo) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
// Skip types that can't be replaced
|
|
14
|
+
if ([
|
|
15
|
+
ApexType.VOID,
|
|
16
|
+
ApexType.BOOLEAN,
|
|
17
|
+
ApexType.SOBJECT,
|
|
18
|
+
ApexType.OBJECT,
|
|
19
|
+
ApexType.APEX_CLASS,
|
|
20
|
+
ApexType.DATE,
|
|
21
|
+
ApexType.DATETIME,
|
|
22
|
+
ApexType.TIME,
|
|
23
|
+
].includes(typeInfo.type)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!ctx.children || ctx.children.length < 2) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const expressionNode = ctx.children[1];
|
|
30
|
+
if (!(expressionNode instanceof ParserRuleContext)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (this.isEmptyValue(typeInfo.returnType, expressionNode.text)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const emptyValue = this.generateEmptyValue(typeInfo);
|
|
37
|
+
if (emptyValue) {
|
|
38
|
+
this.createMutationFromParserRuleContext(expressionNode, emptyValue);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
generateEmptyValue(typeInfo) {
|
|
42
|
+
switch (typeInfo.type) {
|
|
43
|
+
case ApexType.STRING:
|
|
44
|
+
case ApexType.ID:
|
|
45
|
+
return "''";
|
|
46
|
+
case ApexType.INTEGER:
|
|
47
|
+
return '0';
|
|
48
|
+
case ApexType.LONG:
|
|
49
|
+
return '0L';
|
|
50
|
+
case ApexType.DOUBLE:
|
|
51
|
+
case ApexType.DECIMAL:
|
|
52
|
+
return '0.0';
|
|
53
|
+
case ApexType.BLOB:
|
|
54
|
+
return "Blob.valueOf('')";
|
|
55
|
+
case ApexType.LIST:
|
|
56
|
+
case ApexType.SET:
|
|
57
|
+
case ApexType.MAP:
|
|
58
|
+
case ApexType.CUSTOM_OBJECT:
|
|
59
|
+
case ApexType.STANDARD_ENTITY:
|
|
60
|
+
return `new ${typeInfo.returnType}()`;
|
|
61
|
+
default:
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
isEmptyValue(type, expressionText) {
|
|
66
|
+
const lowerType = type.toLowerCase();
|
|
67
|
+
const emptyValuePatterns = {
|
|
68
|
+
string: expr => expr === "''",
|
|
69
|
+
integer: expr => expr === '0',
|
|
70
|
+
double: expr => expr === '0' || expr === '0.0' || !!expr.match(/^0\.0+$/),
|
|
71
|
+
decimal: expr => expr === '0' || expr === '0.0' || !!expr.match(/^0\.0+$/),
|
|
72
|
+
long: expr => expr === '0' || expr === '0L',
|
|
73
|
+
};
|
|
74
|
+
if (lowerType.startsWith('list<') || lowerType.endsWith('[]')) {
|
|
75
|
+
return (!!expressionText.match(/new\s+List<[^>]*>\s*\(\s*\)/i) ||
|
|
76
|
+
!!expressionText.match(/new\s+[^[\]]+\[\s*\]\s*\{\s*\}/));
|
|
77
|
+
}
|
|
78
|
+
if (lowerType.startsWith('set<')) {
|
|
79
|
+
return !!expressionText.match(/new\s+Set<[^>]*>\s*\(\s*\)/i);
|
|
80
|
+
}
|
|
81
|
+
if (lowerType.startsWith('map<')) {
|
|
82
|
+
return !!expressionText.match(/new\s+Map<[^>]*>\s*\(\s*\)/i);
|
|
83
|
+
}
|
|
84
|
+
if (expressionText === 'null') {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
const checkPattern = emptyValuePatterns[lowerType];
|
|
88
|
+
if (checkPattern) {
|
|
89
|
+
return checkPattern(expressionText);
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=emptyReturnMutator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emptyReturnMutator.js","sourceRoot":"","sources":["../../src/mutator/emptyReturnMutator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { TerminalNode } from 'antlr4ts/tree/index.js';
|
|
2
|
+
import { BaseListener } from './baseListener.js';
|
|
3
|
+
export class EqualityConditionMutator extends BaseListener {
|
|
4
|
+
REPLACEMENT_MAP = {
|
|
5
|
+
'==': '!=',
|
|
6
|
+
'!=': '==',
|
|
7
|
+
'===': '!==',
|
|
8
|
+
'!==': '===',
|
|
9
|
+
};
|
|
10
|
+
enterEqualityExpression(ctx) {
|
|
11
|
+
if (!ctx || ctx.childCount < 3)
|
|
12
|
+
return;
|
|
13
|
+
for (let i = 0; i < ctx.childCount; i++) {
|
|
14
|
+
const child = ctx.getChild(i);
|
|
15
|
+
if (child instanceof TerminalNode) {
|
|
16
|
+
const operatorText = child.text;
|
|
17
|
+
const replacement = this.REPLACEMENT_MAP[operatorText];
|
|
18
|
+
if (replacement) {
|
|
19
|
+
this.createMutationFromTerminalNode(child, replacement);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=equalityConditionMutator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"equalityConditionMutator.js","sourceRoot":"","sources":["../../src/mutator/equalityConditionMutator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,OAAO,wBAAyB,SAAQ,YAAY;IACvC,eAAe,GAA2B;QACzD,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,KAAK;QACZ,KAAK,EAAE,KAAK;KACb,CAAA;IAED,uBAAuB,CAAC,GAAsB;QAC5C,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC;YAAE,OAAM;QAEtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YAE7B,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;gBAClC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAA;gBAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAA;gBACtD,IAAI,WAAW,EAAE,CAAC;oBAChB,IAAI,CAAC,8BAA8B,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;gBACzD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
+
import { ApexType } from '../type/ApexMethod.js';
|
|
3
|
+
import { ReturnTypeAwareBaseListener } from './returnTypeAwareBaseListener.js';
|
|
4
|
+
export class FalseReturnMutator extends ReturnTypeAwareBaseListener {
|
|
5
|
+
enterReturnStatement(ctx) {
|
|
6
|
+
if (!this.isCurrentMethodTypeKnown()) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const typeInfo = this.getCurrentMethodReturnTypeInfo();
|
|
10
|
+
if (!typeInfo) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (typeInfo.type !== ApexType.BOOLEAN) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!ctx.children || ctx.children.length < 2) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const expressionNode = ctx.children[1];
|
|
20
|
+
if (!(expressionNode instanceof ParserRuleContext)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (expressionNode.text.trim().toLowerCase() === 'false') {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.createMutationFromParserRuleContext(expressionNode, 'false');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=falseReturnMutator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"falseReturnMutator.js","sourceRoot":"","sources":["../../src/mutator/falseReturnMutator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAA;AAE9E,MAAM,OAAO,kBAAmB,SAAQ,2BAA2B;IACjE,oBAAoB,CAAC,GAAsB;QACzC,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,CAAC;YACrC,OAAM;QACR,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,8BAA8B,EAAE,CAAA;QACtD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAM;QACR,CAAC;QAED,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;YACvC,OAAM;QACR,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7C,OAAM;QACR,CAAC;QAED,MAAM,cAAc,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;QACtC,IAAI,CAAC,CAAC,cAAc,YAAY,iBAAiB,CAAC,EAAE,CAAC;YACnD,OAAM;QACR,CAAC;QAED,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,CAAC;YACzD,OAAM;QACR,CAAC;QAED,IAAI,CAAC,mCAAmC,CAAC,cAAc,EAAE,OAAO,CAAC,CAAA;IACnE,CAAC;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseListener } from './baseListener.js';
|
|
2
1
|
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
+
import { BaseListener } from './baseListener.js';
|
|
3
3
|
export declare class IncrementMutator extends BaseListener {
|
|
4
4
|
private REPLACEMENT_MAP;
|
|
5
5
|
enterPostOpExpression(ctx: ParserRuleContext): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { BaseListener } from './baseListener.js';
|
|
2
1
|
import { TerminalNode } from 'antlr4ts/tree/index.js';
|
|
2
|
+
import { BaseListener } from './baseListener.js';
|
|
3
3
|
export class IncrementMutator extends BaseListener {
|
|
4
4
|
REPLACEMENT_MAP = {
|
|
5
5
|
'++': '--',
|
|
@@ -17,19 +17,15 @@ export class IncrementMutator extends BaseListener {
|
|
|
17
17
|
}
|
|
18
18
|
processOperation(ctx) {
|
|
19
19
|
if (ctx.childCount === 2) {
|
|
20
|
-
let
|
|
20
|
+
let operatorNode = null;
|
|
21
21
|
if (ctx.getChild(0) instanceof TerminalNode) {
|
|
22
|
-
|
|
22
|
+
operatorNode = ctx.getChild(0);
|
|
23
23
|
}
|
|
24
24
|
else if (ctx.getChild(1) instanceof TerminalNode) {
|
|
25
|
-
|
|
25
|
+
operatorNode = ctx.getChild(1);
|
|
26
26
|
}
|
|
27
|
-
if (
|
|
28
|
-
this.
|
|
29
|
-
mutationName: this.constructor.name,
|
|
30
|
-
token: symbol,
|
|
31
|
-
replacement: this.REPLACEMENT_MAP[symbol.text],
|
|
32
|
-
});
|
|
27
|
+
if (operatorNode !== null && operatorNode.text in this.REPLACEMENT_MAP) {
|
|
28
|
+
this.createMutationFromTerminalNode(operatorNode, this.REPLACEMENT_MAP[operatorNode.text]);
|
|
33
29
|
}
|
|
34
30
|
}
|
|
35
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"incrementMutator.js","sourceRoot":"","sources":["../../src/mutator/incrementMutator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"incrementMutator.js","sourceRoot":"","sources":["../../src/mutator/incrementMutator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,OAAO,gBAAiB,SAAQ,YAAY;IACxC,eAAe,GAA2B;QAChD,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,IAAI;KACX,CAAA;IAED,cAAc;IACd,eAAe;IACf,8BAA8B;IAC9B,0CAA0C;IAC1C,qBAAqB,CAAC,GAAsB;QAC1C,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAC5B,CAAC;IAED,oBAAoB,CAAC,GAAsB;QACzC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;IAC5B,CAAC;IAEO,gBAAgB,CAAC,GAAsB;QAC7C,IAAI,GAAG,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,YAAY,GAAwB,IAAI,CAAA;YAC5C,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,YAAY,EAAE,CAAC;gBAC5C,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAiB,CAAA;YAChD,CAAC;iBAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,YAAY,EAAE,CAAC;gBACnD,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAiB,CAAA;YAChD,CAAC;YAED,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACvE,IAAI,CAAC,8BAA8B,CACjC,YAAY,EACZ,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,IAAI,CAAC,CACxC,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
+
import { ApexParserListener } from 'apex-parser';
|
|
3
|
+
import { ApexMethod } from '../type/ApexMethod.js';
|
|
4
|
+
export declare class MethodTypeListener implements ApexParserListener {
|
|
5
|
+
private _apexClassTypes;
|
|
6
|
+
private _standardEntityTypes;
|
|
7
|
+
private _customObjectTypes;
|
|
8
|
+
private _methodTypeTable;
|
|
9
|
+
constructor(_apexClassTypes: Set<string>, _standardEntityTypes: Set<string>, _customObjectTypes: Set<string>);
|
|
10
|
+
getMethodTypeTable(): Map<string, ApexMethod>;
|
|
11
|
+
enterMethodDeclaration(ctx: ParserRuleContext): void;
|
|
12
|
+
}
|