Skip to content

Commit

Permalink
integrate rewritten mutator module
Browse files Browse the repository at this point in the history
  • Loading branch information
bttmly committed May 17, 2015
1 parent 5d130fe commit 4204429
Show file tree
Hide file tree
Showing 14 changed files with 305 additions and 165 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ example-i:
./bin/perturb -r ./example -i

dogfood:
./bin/_perturb -r ./ -c 'make lint && make test'
./bin/perturb -r ./ -c 'make test'

dogfood-i:
./bin/_perturb -r ./ -i
./bin/perturb -r ./ -i

.PHONY: test example
4 changes: 1 addition & 3 deletions bin/_perturb
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@ function reporter (err, data) {
console.log(err.toString());
process.exit(1);
}
var mutants = data.matches[0].mutations;
console.log(data.meta);

console.log(data.meta.killRate);
console.log(data.meta);
}

perturb(config, reporter);
19 changes: 11 additions & 8 deletions bin/perturb
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ process.argv.slice(2).forEach(function (arg) {

console.log([bin].concat(args).join(" "));

var proc = spawn(bin, args, {stdio: 'inherit'});
var child = spawn(bin, args, {stdio: "inherit"});

proc.on('exit', function (code, signal) {
process.on('exit', function(){
child.on("exit", function (code, signal) {
console.log("child exiting...", code);
process.on("exit", function(){
if (signal) {
process.kill(process.pid, signal);
} else {
Expand All @@ -50,11 +51,13 @@ proc.on('exit', function (code, signal) {
});
});

proc.on('error', console.log);
child.on("error", function (err) {
console.log("child errored...", err);
});

// terminate children.
process.on('SIGINT', function () {
proc.kill('SIGINT'); // calls runner.abort()
proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die.
process.kill(process.pid, 'SIGINT');
process.on("SIGINT", function () {
child.kill("SIGINT"); // calls runner.abort()
child.kill("SIGTERM"); // if that didn"t work, we"re probably in an infinite loop, so make it die.
process.kill(process.pid, "SIGINT");
});
28 changes: 25 additions & 3 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ var JS_TYPES = util.constObj({
bool: "boolean",
str: "string",
num: "number",
obj: "object"
obj: "object",
sym: "symbol"
});

var BINARY_OPERATOR_SWAPS = util.constObj({
Expand All @@ -24,7 +25,15 @@ var BINARY_OPERATOR_SWAPS = util.constObj({
"==": "!=",
"!=": "==",
"===": "!==",
"!==": "==="
"!==": "===",

"%": "*",
"&": "|",
"|": "&",
"^": "&",
"<<": ">>",
">>": "<<",
">>>": "<<<"
});

var NODE_TYPES = util.mapMirror([
Expand Down Expand Up @@ -73,7 +82,8 @@ var NODE_TYPES = util.mapMirror([

var FUNC_NODES = util.mapMirror([
NODE_TYPES.FunctionDeclaration,
NODE_TYPES.FunctionExpression
NODE_TYPES.FunctionExpression,
NODE_TYPES.ArrowFunctionExpression
]);

var NODES_WITH_TEST = util.mapMirror([
Expand Down Expand Up @@ -101,10 +111,22 @@ var MESSAGES = util.constObj({
DefaultTest: "npm test"
});

var NODE_ATTRS = util.mapMirror([
"operator",
"elements",
"properties",
"value",
"type",
"test",
"argument",
"params"
]);

module.exports = {
BINARY_OPERATOR_SWAPS: BINARY_OPERATOR_SWAPS,
NODES_WITH_TEST: NODES_WITH_TEST,
NODE_TYPES: NODE_TYPES,
NODE_ATTRS: NODE_ATTRS,
FUNC_NODES: FUNC_NODES,
ISSUES_URL: ISSUES_URL,
JS_TYPES: JS_TYPES,
Expand Down
107 changes: 48 additions & 59 deletions lib/handle-match.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,22 @@ var I = require("immutable");
var fs = require("fs-extra");
var async = require("async");
var esprima = require("esprima");
var last = require("lodash.last");
var escodegen = require("escodegen");
var assign = require("object-assign");

var partial = require("lodash.partial");
var some = require("lodash.some");
var flatten = require("lodash.flatten");
var compact = require("lodash.compact");

var runMutation = require("./run-mutation");
var getMutatorForNode = require("./mutators");
var util = require("./util");
var constants = require("./constants");
var shouldSkip = require("./skip");

var NODE_TYPES = constants.NODE_TYPES;
var JS_TYPES = constants.JS_TYPES;

// called on nodes during labeling to skip
var SKIP_TRAVERSE = util.constObj({
var mutators = require("./mutators-redux");

// skip specific recursing down specific property names (just 'loc' for now)
skipByKey: (function () {
var KEYS_TO_SKIP = util.constObj({
"loc": true
});
return function skipByKey (_, path) {
return (last(path) in KEYS_TO_SKIP);
};
})(),

// skip mutating require calls when the argument is a plain string
skipRequire: function skipRequire (node) {
return (
node.type === NODE_TYPES.CallExpression &&
node.callee.name === "require" &&
node.arguments[0].type === NODE_TYPES.Literal &&
typeof node.arguments[0].type === JS_TYPES.str
);
},

// skip mutating a "use strict" invocation
skipUseStrict: function skipUseStrict (node) {
return (
node.type === NODE_TYPES.ExpressionStatement &&
node.expression.value === "use strict"
);
}
});
var JS_TYPES = constants.JS_TYPES;

var ESPRIMA_SETTINGS = util.constObj({
loc: true,
Expand Down Expand Up @@ -83,10 +53,17 @@ function handleMatch (match, done) {
};

var mutationPaths = getMutationPaths(mutableAst);
var mutations = mutationPaths.map(partial(mutationFromPath, base, immutableAst));

// var mutations = mutationPaths.map(partial(mutationFromPath, base, immutableAst));
var mutations = flatten(mutationPaths.map(mutationsFromPath(base, immutableAst)));

function finish (err, mutations) {
if (err) return done(err);

if (err) {
console.log("async map series halting with error", err);
return done(err);
}

done(null, {
sourceFile: match.sourceFile,
testFile: match.testFile,
Expand All @@ -104,11 +81,9 @@ function handleMatch (match, done) {
function getMutationPaths (ast) {
var mutationPaths = [];
traverseWithPath(ast, function (node, path) {
if (some(SKIP_TRAVERSE, function (fn) {
return fn(node, path);
})) return false;

if (shouldSkip(node)) return;
if (getMutatorForNode(node)) mutationPaths.push(path);
return true;
});
return mutationPaths;
}
Expand All @@ -119,7 +94,8 @@ function getMutationPaths (ast) {
function traverseWithPath (tree, cb, currentPath) {
currentPath = currentPath || [];
if (tree && typeof tree === JS_TYPES.obj) {
if (cb(tree, currentPath.slice()) === false) return;
// callback should return falsy to skip node
if (!cb(tree, currentPath.slice())) return;
Object.keys(tree).forEach(function (key) {
currentPath.push(key);
traverseWithPath(tree[key], cb, currentPath);
Expand All @@ -128,28 +104,41 @@ function traverseWithPath (tree, cb, currentPath) {
}
}

function mutationFromPath (obj, iAst, path) {
var node = iAst.getIn(path);
var mutator = getMutatorForNode(node);
var newIAst = iAst.updateIn(path, mutator);

var mAst = newIAst.toJS();

// try {
// var code = escodegen.generate(mAst);
// } catch (err) {
// console.log(node.get("loc"));
// console.log(mutator.name);
// console.log(JSON.stringify(node.toJS(), null, 4));
// throw err;
// }
function mutationFromPath (obj, ast, path) {
var node = ast.getIn(path);

var code = escodegen.generate(mAst);
var mutator = getMutatorForNode(node);
var updatedAst = ast.updateIn(path, mutator);
var code = escodegen.generate(updatedAst.toJS());

return assign({
ast: newIAst,
ast: updatedAst,
loc: node.get("loc"),
name: mutator.name,
mutSourceCode: code
}, obj);
}


function mutationsFromPath (data, ast) {
return function (path) {
var node = ast.getIn(path);
var results = mutators.getMutatorsForNode(node).map(function (entry) {

if (entry.filter && !entry.filter(node)) return;

var updatedAst = ast.updateIn(path, entry.mutator);
var code = escodegen.generate(updatedAst.toJS());
return assign({
ast: updatedAst,
loc: node.get("loc"),
name: entry.name,
mutSourceCode: code
}, data);
});

return compact(results);
};
}


6 changes: 3 additions & 3 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
"use strict";

// bad stuff
Error.stackTraceLimit = 100;
process.setMaxListeners(0);
// Error.stackTraceLimit = 100;
// process.setMaxListeners(0);

// var EventEmitter = require("events").EventEmitter;
var path = require("path");
var exec = require("child_process").exec;

Expand Down Expand Up @@ -35,6 +34,7 @@ function main (settings, cb) {
var cmd = settings.testCmd || MESSAGES.DefaultTest;
console.log(MESSAGES.ExecutingTests, cmd);
exec(cmd, function (err, out) {

if (err) {
return cb(new util.ChildError(ERRORS.TestsFailed, cmd, out, err.code));
}
Expand Down
Loading

0 comments on commit 4204429

Please sign in to comment.