Skip to content

Commit

Permalink
Add restriction for string literals (#43)
Browse files Browse the repository at this point in the history
Co-authored-by: Ivo Broekhof <[email protected]>
  • Loading branch information
dvcopae and brokhiv authored Nov 26, 2023
1 parent b152138 commit 4d8db03
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 7 deletions.
9 changes: 7 additions & 2 deletions packages/api/schema/stryker-core.json
Original file line number Diff line number Diff line change
Expand Up @@ -754,9 +754,14 @@
"description": "Replace ```\"\"``` with ```\"Stryker was here!\"```."
},
{
"const": "Interpolation",
"title": "InterpolationMutator",
"const": "EmptyInterpolation",
"title": "EmptyInterpolation",
"description": "Replace ```s\"foo ${bar}\"``` with ```s\"\"```."
},
{
"const": "FillInterpolation",
"title": "FillInterpolation",
"description": "Replace ```s\"\"``` with ```s\"Stryker was here!\"```."
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ import { MutationLevel } from '@stryker-mutator/api/core';
export interface RunLevelOptions {
runLevel?: MutationLevel;
}
export type MutationOperator = Record<string, { replacementOperator: any; mutatorName: string }>;
32 changes: 28 additions & 4 deletions packages/instrumenter/src/mutators/string-literal-mutator.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
import babel, { type NodePath } from '@babel/core';

import { NodeMutator } from './node-mutator.js';
import { MutationOperator } from './mutation-level-options.js';

const { types } = babel;

const operators: MutationOperator = {
FillString: { replacementOperator: types.stringLiteral('Stryker was here!'), mutatorName: 'FillString' },
EmptyString: { replacementOperator: types.stringLiteral(''), mutatorName: 'EmptyString' },
EmptyInterpolation: { replacementOperator: types.templateLiteral([types.templateElement({ raw: '' })], []), mutatorName: 'EmptyInterpolation' },
FillInterpolation: {
replacementOperator: types.templateLiteral([types.templateElement({ raw: 'Stryker was here!' })], []),
mutatorName: 'FillInterpolation',
},
};

export const stringLiteralMutator: NodeMutator = {
name: 'StringLiteral',

*mutate(path) {
*mutate(path, operations: string[] | undefined) {
if (path.isTemplateLiteral()) {
const replacement = path.node.quasis.length === 1 && path.node.quasis[0].value.raw.length === 0 ? 'Stryker was here!' : '';
yield types.templateLiteral([types.templateElement({ raw: replacement })], []);
const stringIsEmpty = path.node.quasis.length === 1 && path.node.quasis[0].value.raw.length === 0;
if (
operations === undefined ||
(stringIsEmpty && operations.includes(operators.FillInterpolation.mutatorName)) ||
(!stringIsEmpty && operations.includes(operators.EmptyInterpolation.mutatorName))
) {
yield stringIsEmpty ? operators.FillInterpolation.replacementOperator : operators.EmptyInterpolation.replacementOperator;
}
}
if (path.isStringLiteral() && isValidParent(path)) {
yield types.stringLiteral(path.node.value.length === 0 ? 'Stryker was here!' : '');
const stringIsEmpty = path.node.value.length === 0;
if (
operations === undefined ||
(stringIsEmpty && operations.includes(operators.FillString.mutatorName)) ||
(!stringIsEmpty && operations.includes(operators.EmptyString.mutatorName))
) {
yield stringIsEmpty ? operators.FillString.replacementOperator : operators.EmptyString.replacementOperator;
}
}
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';

import { expectJSMutation } from '../../helpers/expect-mutation.js';
import { expectJSMutation, expectJSMutationWithLevel } from '../../helpers/expect-mutation.js';
import { stringLiteralMutator as sut } from '../../../src/mutators/string-literal-mutator.js';

describe(sut.name, () => {
Expand Down Expand Up @@ -112,4 +112,30 @@ describe(sut.name, () => {
expectJSMutation(sut, '<Record class="row" />');
});
});

describe('mutation level', () => {
it('should only mutate EmptyString and EmptyInterpolation from all possible mutations', () => {
expectJSMutationWithLevel(
sut,
['EmptyString', 'EmptyInterpolation'],
'const bar = "bar"; const foo = `name: ${level_name}`; const emptyString=""; const emptyInterp=``',
'const bar = ""; const foo = `name: ${level_name}`; const emptyString=""; const emptyInterp=``', // empties string
'const bar = "bar"; const foo = ``; const emptyString=""; const emptyInterp=``', // empties interpolation
);
});
it('should block the mutators', () => {
expectJSMutationWithLevel(sut, [], 'const bar = "bar"; const foo = `name: ${level_name}`; const emptyString=""; const emptyInterp=``');
});
it('should mutate everything', () => {
expectJSMutationWithLevel(
sut,
undefined,
'const bar = "bar"; const foo = `name: ${level_name}`; const emptyString=""; const emptyInterp=``',
'const bar = ""; const foo = `name: ${level_name}`; const emptyString=""; const emptyInterp=``', // empties string literal
'const bar = "bar"; const foo = ``; const emptyString=""; const emptyInterp=``', // empties interpolation
'const bar = "bar"; const foo = `name: ${level_name}`; const emptyString="Stryker was here!"; const emptyInterp=``', // fills string literal
'const bar = "bar"; const foo = `name: ${level_name}`; const emptyString=""; const emptyInterp=`Stryker was here!`', // fills interpolation
);
});
});
});
1 change: 1 addition & 0 deletions testing-project/stryker.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"name": "default",
"ArithmeticOperator": ["+To-", "-To+", "*To/"],
"ArrayDeclaration": ["EmptyArray", "FilledArray", "FilledArrayConstructor"],
"StringLiteral": ["EmptyString", "FillString", "EmptyInterpolation", "FillInterpolation"]
"AssignmentOperator": ["-=To+=", "<<=To>>=", "&&=To||="],
"BooleanLiteral": ["TrueToFalse", "RemoveNegation"]
},
Expand Down

0 comments on commit 4d8db03

Please sign in to comment.