Skip to content

Commit

Permalink
rewrite seems to have reached parity
Browse files Browse the repository at this point in the history
  • Loading branch information
bttmly committed Feb 27, 2016
1 parent 3eb002a commit 8044313
Show file tree
Hide file tree
Showing 21 changed files with 283 additions and 153 deletions.
23 changes: 4 additions & 19 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,14 @@ test: lint
lint:
./node_modules/.bin/eslint ./lib/**/*.js

test-example:
./node_modules/.bin/_mocha ./example/test/**/*-test.js

example:
./bin/perturb -r ./examples/toy-lib

example-i:
./bin/perturb -r ./examples/toy-lib -i

events:
./bin/perturb -r ./examples/event-emitter

dogfood:
NODE_ENV=testing ./bin/perturb --rootDir ./ --testCmd 'make test'

dogfood-child:
NODE_ENV=testing ./bin/perturb --rootDir ./ --testCmd 'make test' --runner mochaChild

dogfood-parallel:
NODE_ENV=testing ./bin/perturb --rootDir ./ --testCmd 'make test' --runner mochaChild --parallel
example:
./node_modules/.bin/babel-node ./_src/run.js example

dogfood-i:
NODE_ENV=testing ./bin/perturb -r ./ -i
dogfood:
./node_modules/.bin/babel-node ./_src/run.js dogfood

build:
./node_modules/babel-cli/bin/babel.js --out-dir _lib _src,
Expand Down
7 changes: 4 additions & 3 deletions _src/generate-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const join = require("path").join;

const assign = require("object-assign");

const runners = require("./runners");
const getRunner = require("./runners");
const getMatcher = require("./matchers");

const DEFAULT_SOURCE = "lib";
const DEFAULT_TEST = "test";
Expand Down Expand Up @@ -37,7 +38,7 @@ function calculateExtraConfig (config) {
perturbRoot: join(config.rootDir, config.perturbDirName),
perturbSourceDir: join(config.perturbDirName, config.sourceDir),
perturbTestDir: join(config.perturbDirName, config.testDir),
runner: runners.get(config.runner),
runner: getRunner(config.runner),
});

if (newConfig.cacheInvalidationPredicate == null) {
Expand All @@ -55,7 +56,7 @@ function generateConfig (userConfig) {
const result = calculateExtraConfig(assign({}, defaultConfig, userConfig));

// TODO let configuration dictate which matchers and reporters are used
result.matcher = require("./matchers/contains-comparative-matcher")(result);
result.matcher = getMatcher("contains-comparative")(result);

// result.mutantReporter = require("./reporters").mutantReporter;

Expand Down
93 changes: 61 additions & 32 deletions _src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ const _ = require("lodash");
const async = require("async");
const glob = require("glob");
const Future = require("bluebird");
const through2 = require("through2");

const matchFiles = require("./match-files");
const makeMutations = require("./make-mutations");
const runMutation = require("./run-mutation");
const report = require("./reporters").diff;
const getReporter = require("./reporters");

const fileSystem = require("./file-system");
const generateConfig = require("./generate-config");
const mapSeriesStream = require("./util/map-series-stream");

// var ERRORS = require("./constant/errors");

Expand All @@ -43,49 +45,76 @@ type Mutation
*/


// make file-system module work like this
// (may help for spawning multiple parallel perturb
// processes side-by-side)
function tempFiles (config) {
return {
setup () {
fileSystem.setup(config);
},
teardown () {
fileSystem.teardown(config);
},
}
}

// TODO allow configure
const reporter = getReporter("diff");

function perturb (_cfg) {

const config = generateConfig(_cfg);
console.log("Pertrubing with config");
console.log(config);

return new Future(function (resolve, reject) {
const {setup, teardown} = tempFiles(config);

fileSystem.setup(config);
setup();

const start = Date.now();
const sources = pGlob(config.perturbSourceDir + config.sourceGlob);
const tests = pGlob(config.perturbTestDir + config.testGlob);
return new Future(function (resolve, reject) {

Future.props({sources, tests})
.then(function ({sources, tests}) {
const start = Date.now();
const sources = glob.sync(config.perturbSourceDir + config.sourceGlob);
const tests = glob.sync(config.perturbTestDir + config.testGlob);

var matches = matchFiles(config, sources, tests).filter(hasTests);
console.log("MATCHES", matches.length);
// if (matches.length === 0) throw new Error("no matches");

var mutations = R.chain(makeMutations, matches);
console.log("mutations", mutations.length);

mutations.forEach(m => {
if (m.sourceCode === m.mutatedSourceCode) {
throw new Error("Mutation not applied! " + m.name);
}
})

// TODO make this a stream for live result reporting
Future.mapSeries(mutations, runMutation)
.then(function (results) {
results.map(report).map(r => r.print());
console.log("kill count", results.filter(r => r.error).length, "/", results.length)
});
});
// matchFiles :: Config -> [String] -> [String] -> [Match]
const matches = matchFiles(config, sources, tests).filter(hasTests);

console.log("after matches");

// makeMutations :: Match -> [Mutation]
const mutations = R.chain(makeMutations, matches);

console.log("after mutations");

// runMutation :: Mutation -> Promise<Result>
// mapSeriesStream :: (T -> Promise<Result>) -> [T] -> ReadableStream<Promise<Result>>
const st = mapSeriesStream(runMutation, mutations)

console.log("after stream");

// resultReporter :: Result -> Any?
// provided for visual reporters to give output as the test is running
st.pipe(streamifyReporter(reporter));

// aggregateReporter :: [Result] -> Any?
//
// provided for all runners to do things like "74% mutations killed", and
// for reporters who write, say, structured results to the file system
st.on("complete", function (results) {
teardown();
resolve(results);
})
});
}

const killCount = R.pipe(
R.filter(R.prop("failed")),
R.prop("length")
);
function streamifyReporter (reporter) {
return through2.obj(function (data, enc, next) {
reporter(data).print();
next();
});
}

module.exports = perturb;
20 changes: 10 additions & 10 deletions _src/make-mutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,21 @@ var ENC_UTF8 = {

var R = require("ramda");

module.exports = function makeMutants (match) {
module.exports = function makeMutations (match) {

const {source, tests} = match;
const sourceCode = fs.readFileSync(source, ENC_UTF8);
const __ast = esprima.parse(sourceCode, ESPRIMA_SETTINGS);
let __ast;

try {
__ast = esprima.parse(sourceCode, ESPRIMA_SETTINGS);
} catch (err) {
console.log(sourceCode);
throw err;
}

const ast = I.fromJS(__ast);
const paths = getMutationPaths(__ast);

const regeneratedSource = escodegen.generate(__ast);

return paths.reduce(function (mutations, path) {
Expand Down Expand Up @@ -85,13 +92,6 @@ function getMutationPaths (ast) {
return mutationPaths;
}

// expects a mutable tree
// callback can alter the tree in place
// useful for mutating the AST in place before making it immutable
//
// TODO - we need a safe iterative function to handle this to guarantee
// we don't blow the stack
//
function traverseWithPath (tree, visitor, currentPath) {
currentPath = currentPath || [];

Expand Down
11 changes: 11 additions & 0 deletions _src/matchers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const matchers = {
"base-generative": require("./generative-matcher"),
"base-comparative": require("./comparative-matcher"),
"contains-comparative": require("./contains-comparative-matcher"),
}

module.exports = function getMatcher (name) {
if (matchers[name]) return matchers[name];
throw new Error("matching plugins not implemented");
return require("perturb-matcher-" + name);
}
2 changes: 1 addition & 1 deletion _src/mutators/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ var coreMutators = [
require("./swap-binary-operators"),
require("./swap-logical-operators"),
require("./tweak-array-literal"),
require("./tweak-object-literal"),
require("./tweak-boolean-literal"),
require("./tweak-number-literal"),
require("./tweak-object-literal"),
require("./tweak-string-literal"),
];

Expand Down
16 changes: 8 additions & 8 deletions _src/reporters/diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function diffReporter (result) {
var report = {
loc: result.loc.start.line + "," + result.loc.start.column,
name: result.name,
failedOn: result.failedOn,
error: result.error,
diff: generateDiff(result),
print: () => printDiffReport(report),
}
Expand All @@ -20,22 +20,22 @@ function identifier (report) {
}

function printDiffReport (report) {
// var alive = "\u2714 ";
// var zombie = "\u26A0 ";
// var killed = "\u2718 ";

var alive = "#ALIVE: ";
var dead = "#DEAD: ";

var id = identifier(report);

var title = chalk.gray.underline(
(report.failedOn ? dead : alive) + identifier(report)
);
if (report.error) {
console.log(chalk.gray(id));
return;
}

var title = chalk.red.underline(alive + identifier(report));
var plus = "+ ";
var minus = "- ";

if (report.error) return;

var output = [
title,
report.diff.map(function (entry) {
Expand Down
8 changes: 7 additions & 1 deletion _src/reporters/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
module.exports = {
const reporters = {
diff: require("./diff"),
}

module.exports = function getReporter (name) {
if (reporters[name]) return reporters[name];
throw new Error("reporter plugins not implemented");
return require("perturb-reporter-" + name);
}
2 changes: 2 additions & 0 deletions _src/run-mutation.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use strict";

const Future = require("bluebird");

function runMutation (mutation) {
Expand Down
62 changes: 55 additions & 7 deletions _src/run.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,59 @@
const path = require("path");

const perturb = require("./index.js");
function run (perturb, which, cb) {
let config;

const src = path.join(__dirname, "../lib");
const test = path.join(__dirname, "../test");
switch (which) {
case "dogfood":
config = {
rootDir: path.join(__dirname, ".."),
runner: "mocha",
};
break;

perturb({
rootDir: path.join(__dirname, ".."),
runner: "mocha",
});
case "example":
config = {
rootDir: path.join(__dirname, "../examples/toy-lib"),
runner: "mocha",
};
break;

default:
throw new Error("Unknown config " + which);
}

if (cb) {
return perturb(config, function (err, results) {
if (err) {
console.log("fatal error in perturb");
console.log(err);
console.log(err.stack);
cb(err);
process.exit(1);
}

console.log(results)
try {
console.log("kill count", results.filter(r => r.error).length, "/", results.length)
} catch (e) {}
cb(null, results);
});
}

return perturb(config)
.then(function (results) {
console.log("kill count", results.filter(r => r.error).length, "/", results.length)
return results;
}).catch(function (err) {
console.log("fatal error in perturb");
console.log(err);
console.log(err.stack);
process.exit(1);
});
}

if (!module.parent) {
run(proess.argv[2], require("./"))
}

module.exports = run;
16 changes: 4 additions & 12 deletions _src/runners/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,8 @@ const runners = {
mocha: require("./mocha"),
};

module.exports = {

get (name) {
return runners[name];
},

register (name, runner) {
// validate(runner)
// if (runners.get(name)) throw new Error()
runners[name] = runner;
},

module.exports = function getRunner (name) {
if (runners[name]) return runners[name];
throw new Error("runner plugins not implemented");
return require("perturb-runner-" + name);
}
Loading

0 comments on commit 8044313

Please sign in to comment.