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

Rename mutators & enhance NodeMutatorConfiguration type #68

Merged
merged 13 commits into from
Dec 9, 2023
408 changes: 172 additions & 236 deletions packages/api/schema/stryker-core.json
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we again decided to go back to the negation naming?

Large diffs are not rendered by default.

184 changes: 146 additions & 38 deletions packages/instrumenter/src/mutation-level/default-mutation-levels.json
Original file line number Diff line number Diff line change
@@ -1,44 +1,152 @@
{
"mutationLevels": [
{
"name": "level1",
"UpdateOperator": ["Post--To++"],
"EqualityOperator": ["<=To>","<=To<", "==To!=", "!=To==",">=To<"],
"ArrayDeclaration": ["EmptyArrayConstructor"],
"ConditionalExpression": ["BooleanExpressionToFalse", "BooleanExpressionToTrue"],
"UnaryOperator": ["+To-"],
"AssignmentOperator": ["??=To&&="],
"ArithmeticOperator": ["/To*","%To*","*To/"],
"OptionalChaining": ["OptionalCallExpression","OptionalMemberExpression"]
"mutationLevels":[
{
"name":"Level1",
"UpdateOperator":[
"UpdateOperator_PostfixDecrementOperator_ToPostfixIncrementOperator"
],
"EqualityOperator":[
"EqualityOperator_LessThanEqualOperator_ToGreatherThanOperator",
"EqualityOperator_LessThanEqualOperator_Boundary",
"EqualityOperator_EqualityOperator_ToInequalityOperator",
"EqualityOperator_InequalityOperator_ToEqualityOperator",
"EqualityOperator_GreatherThanEqualOperator_ToLessThanOperator"
],
"ArrayDeclaration":[
"ArrayDeclaration_ArrayConstructor_ItemsRemoval"
],
"ConditionalExpression":[
"ConditionalExpression_BooleanExpression_ToFalseLiteral",
"ConditionalExpression_BooleanExpression_ToTrueLiteral"
],
"UnaryOperator":[
"UnaryOperator_UnaryPlusOperator_ToUnaryMinusOperator"
],
"AssignmentOperator":[
"AssignmentOperator_NullishCoalescingAssignment_ToLogicalAndAssignment"
],
"ArithmeticOperator":[
"ArithmeticOperator_DivisionOperator_ToMultiplicationOperator",
"ArithmeticOperator_RemainderOperator_ToMultiplicationOperator",
"ArithmeticOperator_MultiplicationOperator_ToDivisionOperator"
],
"OptionalChaining":[
"OptionalChaining_OptionalCallExpression_OptionRemoval",
"OptionalChaining_OptionalMemberExpression_OptionRemoval"
]
},
{
"name": "level2",
"UpdateOperator": ["Post--To++", "Post++To--"],
"EqualityOperator": ["<=To>","<=To<", "==To!=", "!=To==",">=To<","<To>=",">=To>", "!==To===", ">To>="],
"ArrayDeclaration": ["EmptyArrayConstructor"],
"ConditionalExpression": ["BooleanExpressionToFalse", "BooleanExpressionToTrue","SwitchToEmpty" ],
"UnaryOperator": ["+To-"],
"AssignmentOperator": ["??=To&&="],
"ArithmeticOperator": ["/To*","%To*","*To/", "+To-","-To+"],
"OptionalChaining": ["OptionalCallExpression","OptionalMemberExpression"],
"StringLiteral": ["FillString", "FillInterpolation"],
"Regex": true,
"BooleanLiteral": ["TrueToFalse"]
"name":"Level2",
"UpdateOperator":[
"UpdateOperator_PostfixDecrementOperator_ToPostfixIncrementOperator",
"UpdateOperator_PostfixIncrementOperator_ToPostfixDecrementOperator"
],
"EqualityOperator":[
"EqualityOperator_LessThanEqualOperator_ToGreatherThanOperator",
"EqualityOperator_LessThanEqualOperator_Boundary",
"EqualityOperator_EqualityOperator_ToInequalityOperator",
"EqualityOperator_InequalityOperator_ToEqualityOperator",
"EqualityOperator_GreatherThanEqualOperator_ToLessThanOperator",
"EqualityOperator_LessThanOperator_ToGreatherThanEqualOperator",
"EqualityOperator_GreatherThanEqualOperator_Boundary",
"EqualityOperator_StrictInequalityOperator_ToStrictEqualityOperator",
"EqualityOperator_GreaterThanOperator_Boundary"
],
"ArrayDeclaration":[
"ArrayDeclaration_ArrayConstructor_ItemsRemoval"
],
"ConditionalExpression":[
"ConditionalExpression_BooleanExpression_ToFalseLiteral",
"ConditionalExpression_BooleanExpression_ToTrueLiteral",
"ConditionalExpression_SwitchStatementBody_Removal"
],
"UnaryOperator":[
"UnaryOperator_UnaryPlusOperator_ToUnaryMinusOperator"
],
"AssignmentOperator":[
"AssignmentOperator_NullishCoalescingAssignment_ToLogicalAndAssignment"
],
"ArithmeticOperator":[
"ArithmeticOperator_DivisionOperator_ToMultiplicationOperator",
"ArithmeticOperator_RemainderOperator_ToMultiplicationOperator",
"ArithmeticOperator_MultiplicationOperator_ToDivisionOperator",
"ArithmeticOperator_AdditionOperator_ToSubtractionOperator",
"ArithmeticOperator_SubtractionOperator_ToAdditionOperator"
],
"OptionalChaining":[
"OptionalChaining_OptionalCallExpression_OptionRemoval",
"OptionalChaining_OptionalMemberExpression_OptionRemoval"
],
"StringLiteral":[
"StringLiteral_EmptyStringLiteral_ToFilledStringLiteral",
"StringLiteral_EmptyInterpolatedString_ToFilledInterpolatedString"
],
"Regex":[
"Regex_Removal"
],
"BooleanLiteral":[
"BooleanLiteral_TrueLiteral_ToFalseLiteral"
]
},
{
"name": "level3",
"UpdateOperator": ["Post--To++", "Post++To--"],
"EqualityOperator": ["<=To>","<=To<", "==To!=", "!=To==",">=To<","<To>=",">=To>", "!==To===", ">To>=", "<To<=", ">To<="],
"ArrayDeclaration": ["EmptyArrayConstructor","EmptyArray", "FilledArray"],
"ConditionalExpression": ["BooleanExpressionToFalse", "BooleanExpressionToTrue","SwitchToEmpty" ],
"UnaryOperator": ["+To-", "-To+"],
"AssignmentOperator": ["??=To&&="],
"ArithmeticOperator": ["/To*","%To*","*To/", "+To-","-To+"],
"OptionalChaining": ["OptionalCallExpression","OptionalMemberExpression"],
"StringLiteral": ["FillString", "FillInterpolation"],
"Regex": true,
"BooleanLiteral": ["TrueToFalse", "FalseToTrue", "RemoveNegation"]
"name":"Level3",
"UpdateOperator":[
"UpdateOperator_PostfixDecrementOperator_ToPostfixIncrementOperator",
"UpdateOperator_PostfixIncrementOperator_ToPostfixDecrementOperator"
],
"EqualityOperator":[
"EqualityOperator_LessThanEqualOperator_ToGreatherThanOperator",
"EqualityOperator_LessThanEqualOperator_Boundary",
"EqualityOperator_EqualityOperator_ToInequalityOperator",
"EqualityOperator_InequalityOperator_ToEqualityOperator",
"EqualityOperator_GreatherThanEqualOperator_ToLessThanOperator",
"EqualityOperator_LessThanOperator_ToGreatherThanEqualOperator",
"EqualityOperator_GreatherThanEqualOperator_Boundary",
"EqualityOperator_StrictInequalityOperator_ToStrictEqualityOperator",
"EqualityOperator_GreaterThanOperator_Boundary",
"EqualityOperator_LessThanOperator_Boundary",
"EqualityOperator_GreaterThanOperator_ToLessThanEqualOperator"
],
"ArrayDeclaration":[
"ArrayDeclaration_ArrayConstructor_ItemsRemoval",
"EmptyArray",
"FilledArray"
],
"ConditionalExpression":[
"ConditionalExpression_BooleanExpression_ToFalseLiteral",
"ConditionalExpression_BooleanExpression_ToTrueLiteral",
"ConditionalExpression_SwitchStatementBody_Removal"
],
"UnaryOperator":[
"UnaryOperator_UnaryPlusOperator_ToUnaryMinusOperator",
"ArithmeticOperator_SubtractionOperator_ToAdditionOperator"
],
"AssignmentOperator":[
"AssignmentOperator_NullishCoalescingAssignment_ToLogicalAndAssignment"
],
"ArithmeticOperator":[
"ArithmeticOperator_DivisionOperator_ToMultiplicationOperator",
"ArithmeticOperator_RemainderOperator_ToMultiplicationOperator",
"ArithmeticOperator_MultiplicationOperator_ToDivisionOperator",
"ArithmeticOperator_AdditionOperator_ToSubtractionOperator",
"ArithmeticOperator_SubtractionOperator_ToAdditionOperator"
],
"OptionalChaining":[
"OptionalChaining_OptionalCallExpression_OptionRemoval",
"OptionalChaining_OptionalMemberExpression_OptionRemoval"
],
"StringLiteral":[
"StringLiteral_EmptyStringLiteral_ToFilledStringLiteral",
"StringLiteral_EmptyInterpolatedString_ToFilledInterpolatedString"
],
"Regex":[
"Regex_Removal"
],
"BooleanLiteral":[
"BooleanLiteral_TrueLiteral_ToFalseLiteral",
"BooleanLiteral_FalseLiteral_ToTrueLiteral",
"BooleanLiteral_LogicalNot_Removal"
]
}
]
}

]
}
9 changes: 5 additions & 4 deletions packages/instrumenter/src/mutation-level/mutation-level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
BooleanLiteral,
ConditionalExpression,
EqualityOperator,
LogicalOperator,
MethodExpression,
ObjectLiteral,
OptionalChaining,
Expand All @@ -18,12 +19,11 @@ import {
UpdateOperator,
} from '@stryker-mutator/api/core';

export type NodeMutatorConfiguration = Record<string, ReplacementConfiguration>;
export type NodeMutatorConfiguration<T> = Record<string, ReplacementConfiguration<T>>;

export type NodeMutatorMultiConfiguration = Record<string, ReplacementConfiguration[]>;
interface ReplacementConfiguration {
interface ReplacementConfiguration<T> {
replacement?: any;
mutationName: string;
mutationName: T;
}

export interface MutationLevel {
Expand All @@ -39,6 +39,7 @@ export interface MutationLevel {
BooleanLiteral?: BooleanLiteral[];
ConditionalExpression?: ConditionalExpression[];
EqualityOperator?: EqualityOperator[];
LogicalOperator?: LogicalOperator[];
MethodExpression?: MethodExpression[];
ObjectLiteral?: ObjectLiteral[];
OptionalChaining?: OptionalChaining[];
Expand Down
30 changes: 15 additions & 15 deletions packages/instrumenter/src/mutators/arithmetic-operator-mutator.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import type { types } from '@babel/core';

import { deepCloneNode } from '../util/index.js';
import { ArithmeticOperator } from '@stryker-mutator/api/core';

import { NodeMutatorConfiguration } from '../mutation-level/mutation-level.js';
import { deepCloneNode } from '../util/index.js';

import { NodeMutator } from './node-mutator.js';

const operators: NodeMutatorConfiguration = {
'+': { replacement: '-', mutationName: '+To-' },
'-': { replacement: '+', mutationName: '-To+' },
'*': { replacement: '/', mutationName: '*To/' },
'/': { replacement: '*', mutationName: '/To*' },
'%': { replacement: '*', mutationName: '%To*' },
};

export const arithmeticOperatorMutator: NodeMutator = {
export const arithmeticOperatorMutator: NodeMutator<ArithmeticOperator> = {
name: 'ArithmeticOperator',

operators: {
'+': { replacement: '-', mutationName: 'AdditionOperatorNegation' },
'-': { replacement: '+', mutationName: 'SubtractionOperatorNegation' },
'*': { replacement: '/', mutationName: 'MultiplicationOperatorNegation' },
'/': { replacement: '*', mutationName: 'DivisionOperatorNegation' },
'%': { replacement: '*', mutationName: 'RemainderOperatorToMultiplicationReplacement' },
},

*mutate(path, levelMutations) {
if (path.isBinaryExpression() && isSupported(path.node.operator, path.node) && isInMutationLevel(path.node, levelMutations)) {
const mutatedOperator = operators[path.node.operator].replacement;
const mutatedOperator = this.operators[path.node.operator].replacement;
const replacement = deepCloneNode(path.node);
replacement.operator = mutatedOperator;
yield replacement;
Expand All @@ -33,12 +33,12 @@ function isInMutationLevel(node: types.BinaryExpression, operations: string[] |
return true;
}

const mutatedOperator = operators[node.operator as keyof typeof operators].mutationName;
const mutatedOperator = arithmeticOperatorMutator.operators[node.operator].mutationName;
return operations.some((op) => op === mutatedOperator) ?? false;
}

function isSupported(operator: string, node: types.BinaryExpression): operator is keyof typeof operators {
if (!Object.keys(operators).includes(operator)) {
function isSupported(operator: string, node: types.BinaryExpression): boolean {
if (!Object.keys(arithmeticOperatorMutator.operators).includes(operator)) {
return false;
}

Expand Down
37 changes: 22 additions & 15 deletions packages/instrumenter/src/mutators/array-declaration-mutator.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import babel from '@babel/core';

import { ArrayDeclaration } from '@stryker-mutator/api/core';

import { deepCloneNode } from '../util/index.js';
import { NodeMutatorConfiguration } from '../mutation-level/mutation-level.js';

import { NodeMutator } from './node-mutator.js';

const { types } = babel;

const operators: NodeMutatorConfiguration = {
EmptyArray: { replacement: types.arrayExpression([types.stringLiteral('Stryker was here')]), mutationName: 'EmptyArray' },
EmptyArrayConstructor: { replacement: [types.stringLiteral('Stryker was here')], mutationName: 'EmptyArrayConstructor' },
FilledArray: { replacement: types.arrayExpression(), mutationName: 'FilledArray' },
FilledArrayConstructor: { replacement: [], mutationName: 'FilledArrayConstructor' },
};

export const arrayDeclarationMutator: NodeMutator = {
export const arrayDeclarationMutator: NodeMutator<ArrayDeclaration> = {
name: 'ArrayDeclaration',

operators: {
ArrayLiteralItemsFill: {
replacement: types.arrayExpression([types.stringLiteral('Stryker was here')]),
mutationName: 'ArrayLiteralItemsFill',
},
ArrayConstructorItemsFill: { replacement: [types.stringLiteral('Stryker was here')], mutationName: 'ArrayConstructorItemsFill' },
ArrayLiteralItemsRemoval: { replacement: types.arrayExpression(), mutationName: 'ArrayLiteralItemsRemoval' },
ArrayConstructorItemsRemoval: { replacement: [], mutationName: 'ArrayConstructorItemsRemoval' },
},

*mutate(path, levelMutations) {
// The check of the [] construct in code
if (path.isArrayExpression() && isArrayInLevel(path.node, levelMutations)) {
const replacement = path.node.elements.length > 0 ? operators.FilledArray.replacement : operators.EmptyArray.replacement;
const replacement =
path.node.elements.length > 0 ? this.operators.ArrayLiteralItemsRemoval.replacement : this.operators.ArrayLiteralItemsFill.replacement;
yield replacement;
}
// Check for the new Array() construct in code
Expand All @@ -31,7 +36,9 @@ export const arrayDeclarationMutator: NodeMutator = {
isArrayConstructorInLevel(path.node, levelMutations)
) {
const mutatedCallArgs: babel.types.Expression[] =
path.node.arguments.length > 0 ? operators.FilledArrayConstructor.replacement : operators.EmptyArrayConstructor.replacement;
path.node.arguments.length > 0
? this.operators.ArrayConstructorItemsRemoval.replacement
: this.operators.ArrayConstructorItemsFill.replacement;

const replacement = types.isNewExpression(path.node)
? types.newExpression(deepCloneNode(path.node.callee), mutatedCallArgs)
Expand All @@ -48,8 +55,8 @@ function isArrayInLevel(node: babel.types.ArrayExpression, levelMutations: strin
}

return (
(levelMutations.includes(operators.FilledArray.mutationName) && node.elements.length > 0) ||
(levelMutations.includes(operators.EmptyArray.mutationName) && node.elements.length === 0)
(levelMutations.includes(arrayDeclarationMutator.operators.ArrayLiteralItemsRemoval.mutationName) && node.elements.length > 0) ||
(levelMutations.includes(arrayDeclarationMutator.operators.ArrayLiteralItemsFill.mutationName) && node.elements.length === 0)
);
}

Expand All @@ -60,7 +67,7 @@ function isArrayConstructorInLevel(node: babel.types.CallExpression | babel.type
}

return (
(levelMutations.includes(operators.FilledArrayConstructor.mutationName) && node.arguments.length > 0) ||
(levelMutations.includes(operators.EmptyArrayConstructor.mutationName) && node.arguments.length === 0)
(levelMutations.includes(arrayDeclarationMutator.operators.ArrayConstructorItemsRemoval.mutationName) && node.arguments.length > 0) ||
(levelMutations.includes(arrayDeclarationMutator.operators.ArrayConstructorItemsFill.mutationName) && node.arguments.length === 0)
);
}
12 changes: 9 additions & 3 deletions packages/instrumenter/src/mutators/arrow-function-mutator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ import babel from '@babel/core';

const { types } = babel;

import { ArrowFunction } from '@stryker-mutator/api/core';

import { NodeMutator } from './index.js';

export const arrowFunctionMutator: NodeMutator = {
export const arrowFunctionMutator: NodeMutator<ArrowFunction> = {
name: 'ArrowFunction',

operators: {
ArrowFunctionRemoval: { mutationName: 'ArrowFunctionRemoval' },
},

*mutate(path, levelMutations) {
if (
path.isArrowFunctionExpression() &&
Expand All @@ -19,6 +25,6 @@ export const arrowFunctionMutator: NodeMutator = {
},
};

function isInMutationLevel(operations: string[] | undefined): boolean {
return operations === undefined || operations.length > 0;
function isInMutationLevel(levelMutations: string[] | undefined): boolean {
return levelMutations === undefined || levelMutations.includes(arrowFunctionMutator.operators.ArrowFunctionRemoval.mutationName);
}
Loading