Skip to content

Commit

Permalink
Rename mutators & enhance NodeMutatorConfiguration type (#68)
Browse files Browse the repository at this point in the history
* Rename mutators in the schema

* Change negation to substraction in arhitmeticop

* Rename mutators, stable

* Enforce type with a generic NodeMutatorConfiguration

* Get rid of NodeMutatorMultiConfiguration

* Move operators inside nodemutator interface

* Update conditionalLevel test name

* Finish aritmetic through conditional operators, stable

* Fix arithmetic through object-literal operators, stable

* Fix up optional-chaining

* Finish renaming mutators

* Reorder stryker-core to reduce generated warnings

* Use the same key for the mutator operators when there is no reason to be more specific
  • Loading branch information
dvcopae authored and Ja4pp committed Jan 14, 2024
1 parent 6aac475 commit 49e7b69
Show file tree
Hide file tree
Showing 43 changed files with 791 additions and 594 deletions.
408 changes: 172 additions & 236 deletions packages/api/schema/stryker-core.json

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

0 comments on commit 49e7b69

Please sign in to comment.