Skip to content

Commit

Permalink
feat(ArrayDeclarationMutator): Add new mutator. (#229)
Browse files Browse the repository at this point in the history
Add a new mutator which mutates non-empty arrays to be empty
  • Loading branch information
JellePetersHAN authored and nicojs committed Feb 1, 2017
1 parent 1a5968c commit 9805917
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/MutatorOrchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import LogicalOperatorMutator from './mutators/LogicalOperatorMutator';
import RemoveConditionalsMutator from './mutators/RemoveConditionalsMutator';
import UnaryOperatorMutator from './mutators/UnaryOperatorMutator';
import UpdateOperatorMutator from './mutators/UpdateOperatorMutator';
import ArrayDeclaratorMutator from './mutators/ArrayDeclaratorMutator';
import { Mutator, MutatorFactory } from 'stryker-api/mutant';
import { Reporter, SourceFile } from 'stryker-api/report';
import * as fileUtils from './utils/fileUtils';
Expand Down Expand Up @@ -84,6 +85,7 @@ export default class MutatorOrchestrator {
mutatorFactory.register('RemoveConditionals', RemoveConditionalsMutator);
mutatorFactory.register('UnaryOperator', UnaryOperatorMutator);
mutatorFactory.register('UpdateOperator', UpdateOperatorMutator);
mutatorFactory.register('ArrayDeclarator', ArrayDeclaratorMutator);
}

/**
Expand Down
27 changes: 27 additions & 0 deletions src/mutators/ArrayDeclaratorMutator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {Syntax} from 'esprima';
import {Mutator} from 'stryker-api/mutant';
import * as estree from 'estree';

/**
* Represents a mutator which can remove the content of an array's elements.
*/
export default class ArrayDeclaratorMutator implements Mutator {
name = 'ArrayDeclarator';

constructor() { }

applyMutations(node: estree.Node, copy: <T>(obj: T, deep?: boolean) => T): void | estree.Node | estree.Node[] {
if ((node.type === Syntax.CallExpression || node.type === Syntax.NewExpression) && node.callee.type === Syntax.Identifier && node.callee.name === 'Array' && node.arguments.length > 0) {
let mutatedNode = copy(node);
mutatedNode.arguments = [];
return mutatedNode;
}

if (node.type === Syntax.ArrayExpression && node.elements.length > 0) {
let mutatedNode = copy(node);
mutatedNode.elements = [];
return mutatedNode;
}
}
}

2 changes: 1 addition & 1 deletion test/integration/utils/fileUtilsSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('fileUtils', () => {

describe('glob', () => {
it('should resolve files', () =>
expect(fileUtils.glob('testResources/sampleProject/**/*.js')).to.eventually.have.length(9));
expect(fileUtils.glob('testResources/sampleProject/**/*.js')).to.eventually.have.length(10));

it('should not resolve to directories', () =>
expect(fileUtils.glob('testResources/vendor/**/*.js')).to.eventually.have.length(1));
Expand Down
101 changes: 101 additions & 0 deletions test/unit/mutators/ArrayDeclaratorMutatorSpec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import ArrayDeclaratorMutator from '../../../src/mutators/ArrayDeclaratorMutator';
import { expect } from 'chai';
import * as parser from '../../../src/utils/parserUtils';
import { copy } from '../../../src/utils/objectUtils';
import { Syntax } from 'esprima';
import * as estree from 'estree';

describe('BlockStatementMutator', () => {
let sut: ArrayDeclaratorMutator;

beforeEach(() => sut = new ArrayDeclaratorMutator());

const getVariableDeclaration = (program: estree.Program) => (program.body[0] as estree.VariableDeclaration);

const getArrayExpression = (program: estree.Program) => {
const variableDeclaration = getVariableDeclaration(program);
return (variableDeclaration.declarations[0].init as estree.ArrayExpression);
};

const getArrayCallExpression = (program: estree.Program) => {
const variableDeclaration = getVariableDeclaration(program);
return (variableDeclaration.declarations[0].init as estree.SimpleCallExpression);
};

const getArrayNewExpression = (program: estree.Program) => {
const variableDeclaration = getVariableDeclaration(program);
return (variableDeclaration.declarations[0].init as estree.NewExpression);
};

it('should mutate when supplied with an array expression', () => {
// Arrange
const program = parser.parse(`var array = [1,2,3];`);
const arrayExpression = getArrayExpression(program);

// Act
const actual = <estree.ArrayExpression>sut.applyMutations(arrayExpression, copy);

// Assert
expect(actual).to.be.ok;
expect(actual.nodeID).to.eq(arrayExpression.nodeID);
expect(actual.elements).to.have.length(0);
});

it('should mutate when supplied with an array `call` expression', () => {
// Arrange
const program = parser.parse(`var array = Array(1,2,3);`);
const arrayExpression = getArrayCallExpression(program);

// Act
const actual = <estree.CallExpression>sut.applyMutations(arrayExpression, copy);

// Assert
expect(actual).to.be.ok;
expect(actual.nodeID).to.eq(arrayExpression.nodeID);
expect(actual.arguments).to.have.length(0);
});

it('should mutate when supplied with an array `new` expression', () => {
// Arrange
const program = parser.parse(`var array = new Array(1,2,3);`);
const arrayExpression = getArrayNewExpression(program);

// Act
const actual = <estree.CallExpression>sut.applyMutations(arrayExpression, copy);

// Assert
expect(actual).to.be.ok;
expect(actual.nodeID).to.eq(arrayExpression.nodeID);
expect(actual.arguments).to.have.length(0);
});

it('should not mutate an empty expression', () => {
// Arrange
const program = parser.parse(`var array = []`);
const emptyArrayExpression = getArrayExpression(program);

// Act
const actual = sut.applyMutations(emptyArrayExpression, copy);
expect(actual).to.be.undefined;
});

it('should not mutate an empty `call` expression', () => {
// Arrange
const program = parser.parse(`var array = Array()`);
const emptyCallExpression = getArrayExpression(program);

// Act
const actual = sut.applyMutations(emptyCallExpression, copy);
expect(actual).to.be.undefined;
});

it('should not mutate an empty `new` expression', () => {
// Arrange
const program = parser.parse(`var array = new Array()`);
const emptyNewExpression = getArrayExpression(program);

// Act
const actual = sut.applyMutations(emptyNewExpression, copy);
expect(actual).to.be.undefined;
});
});
5 changes: 5 additions & 0 deletions testResources/sampleProject/src/Array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var array = [1,2,3];

var array2 = Array(1,2,3);

var array3 = new Array(1,2,3);

0 comments on commit 9805917

Please sign in to comment.