Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(excludedMutations): Implement new naming of mutators #1855

Merged
merged 11 commits into from
Nov 23, 2019
Merged
22 changes: 11 additions & 11 deletions e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions e2e/test/typescript-transpiling/verify/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ describe('Verify stryker has ran correctly', () => {
it('should report correct score', async () => {
await expectMetricsResult({
metrics: produceMetrics({
killed: 18,
mutationScore: 66.67,
mutationScoreBasedOnCoveredCode: 66.67,
killed: 16,
mutationScore: 64,
mutationScoreBasedOnCoveredCode: 64,
survived: 9,
totalCovered: 27,
totalDetected: 18,
totalMutants: 27,
totalCovered: 25,
totalDetected: 16,
totalMutants: 25,
totalUndetected: 9,
totalValid: 27
totalValid: 25
})
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/core/StrykerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ interface StrykerOptions {
* * The `name` property is mandatory and contains the name of the mutant generator to use.
* * For example: 'javascript', 'typescript'
* * The `excludedMutations` property is mandatory and contains the names of the specific mutation types to exclude from testing.
* * The values must match the given names of the mutations. For example: 'BinaryExpression', 'BooleanSubstitution', etc.
* * The values must match the given names of the mutations. For example: 'ArithmeticOperator', 'EqualityOperator', etc.
*/
mutator: string | Partial<MutatorDescriptor>;

Expand Down
56 changes: 56 additions & 0 deletions packages/core/src/config/ConfigReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,64 @@ export default class ConfigReader {
this.log.debug(`Loaded config: ${JSON.stringify(config, null, 2)}`);
}

this.fixDeprecations(config);

return config;
}
private fixDeprecations(config: Config) {
nicojs marked this conversation as resolved.
Show resolved Hide resolved
if (!(typeof config.mutator === 'string') && config.mutator.excludedMutations) {
const newExcludedMutations: string[] = [];
config.mutator.excludedMutations.forEach(legacyExcludedMutation =>
newExcludedMutations.push(...this.getNewExcludedMutations(legacyExcludedMutation))
);
config.mutator.excludedMutations = newExcludedMutations;
}
}
private getNewExcludedMutations(legacyExcludedMutation: string): string[] {
const newMutations: string[] = [];
switch (legacyExcludedMutation) {
case 'ArrayLiteral':
case 'ArrayNewExpression':
newMutations.push('ArrayDeclaration');
break;
case 'BinaryExpression':
newMutations.push(...['ArithmeticOperator', 'EqualityOperator', 'LogicalOperator']);
break;
case 'Block':
newMutations.push('BlockStatement');
break;
case 'BooleanSubstitution':
newMutations.push('BooleanLiteral');
break;
case 'DoStatement':
case 'ForStatement':
case 'IfStatement':
case 'SwitchCase':
case 'WhileStatement':
newMutations.push('ConditionalExpression');
break;
case 'PrefixUnaryExpression':
newMutations.push('UnaryOperator', 'UpdateOperator', 'BooleanLiteral');
break;
case 'PostfixUnaryExpression':
newMutations.push('UpdateOperator');
break;
default:
break;
}

if (newMutations.length > 0) {
this.log.warn(
`DEPRECATED: The mutation name "${legacyExcludedMutation}" is deprecated. Please migrate your config. For now ${legacyExcludedMutation} will be replaced with: ${newMutations.join(
', '
)}. A list of mutations and their names can be found here: https://github.com/stryker-mutator/stryker-handbook/blob/master/mutator-types.md`
);
} else {
newMutations.push(legacyExcludedMutation);
}

return newMutations;
}

private loadConfigModule(): Function {
// Dummy module to be returned if no config file is loaded.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,39 @@ describe(ConfigReader.name, () => {
expect(testInjector.logger.warn).not.called;
});

it('should migrate deprecated settings', () => {
sut = createSut({ configFile: 'testResources/config-reader/deprecated.conf.js' });

result = sut.readConfig();

expect(typeof result.mutator).to.not.be.eq('string');
if (typeof result.mutator !== 'string') {
expect(result.mutator.excludedMutations).to.deep.eq([
'ArrayDeclaration',
'ArrayDeclaration',
'ArithmeticOperator',
'EqualityOperator',
'LogicalOperator',
'BlockStatement',
'BooleanLiteral',
'ConditionalExpression',
'ConditionalExpression',
'ConditionalExpression',
'UnaryOperator',
'UpdateOperator',
'BooleanLiteral',
'UpdateOperator',
'ConditionalExpression',
'ConditionalExpression',
'ObjectLiteral',
'ArrowFunctionMutator'
]);
}
expect(testInjector.logger.warn).to.have.been.calledWith(
'DEPRECATED: The mutation name "BinaryExpression" is deprecated. Please migrate your config. For now BinaryExpression will be replaced with: ArithmeticOperator, EqualityOperator, LogicalOperator. A list of mutations and their names can be found here: https://github.com/stryker-mutator/stryker-handbook/blob/master/mutator-types.md'
);
});

describe('with CLI options', () => {
it('should give precedence to CLI options', () => {
sut = createSut({ configFile: 'testResources/config-reader/valid.conf.js', read: false });
Expand Down
23 changes: 23 additions & 0 deletions packages/core/testResources/config-reader/deprecated.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = function(config){
config.set({
mutator: {
name: 'javascript',
excludedMutations: [
'ArrayLiteral',
'ArrayNewExpression',
'BinaryExpression',
'Block',
'BooleanSubstitution',
'DoStatement',
'ForStatement',
'IfStatement',
'PrefixUnaryExpression',
'PostfixUnaryExpression',
'SwitchCase',
'WhileStatement',
'ObjectLiteral',
'ArrowFunctionMutator'
]
}
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as types from '@babel/types';

import { NodeMutator } from './NodeMutator';

export default class ArithmeticOperatorMutator implements NodeMutator {
private readonly operators: { [targetedOperator: string]: string | string[] } = {
'+': '-',
'-': '+',
'*': '/',
'/': '*',
'%': '*'
};

public name = 'ArithmeticOperator';

public mutate(node: types.Node, clone: <T extends types.Node>(node: T, deep?: boolean) => T): types.Node[] {
if (types.isBinaryExpression(node)) {
let mutatedOperators = this.operators[node.operator];
if (mutatedOperators) {
if (typeof mutatedOperators === 'string') {
mutatedOperators = [mutatedOperators];
}

return mutatedOperators.map<types.Node>(mutatedOperator => {
const mutatedNode = clone(node);
mutatedNode.operator = mutatedOperator as any;
return mutatedNode;
});
}
}

return [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as types from '@babel/types';

import { NodeMutator } from './NodeMutator';

/**
* Represents a mutator which can remove the content of an array's elements.
*/
export default class ArrayDeclarationMutator implements NodeMutator {
public name = 'ArrayDeclaration';

public mutate(node: types.Node, copy: <T extends types.Node>(obj: T, deep?: boolean) => T): types.Node[] {
const nodes: types.Node[] = [];

if (types.isArrayExpression(node)) {
const mutatedNode = copy(node);
mutatedNode.elements = node.elements.length ? [] : [types.stringLiteral('Stryker was here')];
nodes.push(mutatedNode);
} else if ((types.isCallExpression(node) || types.isNewExpression(node)) && types.isIdentifier(node.callee) && node.callee.name === 'Array') {
const mutatedNode = copy(node);
mutatedNode.arguments = node.arguments.length ? [] : [types.arrayExpression()];
nodes.push(mutatedNode);
}

return nodes;
}
}
22 changes: 0 additions & 22 deletions packages/javascript-mutator/src/mutators/ArrayLiteralMutator.ts

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { NodeMutator } from './NodeMutator';
/**
* Represents a mutator which can remove the content of a Block.
*/
export default class BlockMutator implements NodeMutator {
public name = 'Block';
export default class BlockStatementMutator implements NodeMutator {
public name = 'BlockStatement';

public mutate(node: types.Node, copy: <T extends types.Node>(obj: T, deep?: boolean) => T): types.Node[] {
const nodes: types.Node[] = [];
Expand Down
Loading