Skip to content

Commit

Permalink
get executable working again
Browse files Browse the repository at this point in the history
  • Loading branch information
bttmly committed Nov 25, 2016
1 parent 7d41680 commit ddfe643
Show file tree
Hide file tree
Showing 43 changed files with 4,952 additions and 305 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ node_modules
.lock-wscript

.perturb
example/.DS_Store
.DS_Store
example/.perturb
typings
lib
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
test: compile
NODE_ENV=testing ./node_modules/.bin/_mocha ./test --recursive
NODE_ENV=testing ./node_modules/.bin/_mocha ./test/setup ./test --recursive

test-bail: compile
NODE_ENV=testing ./node_modules/.bin/_mocha ./test --bail --recursive
NODE_ENV=testing ./node_modules/.bin/_mocha ./test/setup ./test --recursive --bail

compile:
rm -rf ./built
Expand Down
62 changes: 21 additions & 41 deletions bin/_perturb
Original file line number Diff line number Diff line change
@@ -1,36 +1,23 @@
#!/usr/bin/env node

throw new Error("The CLI is currently out of sync with the library :( -- use the library directly");

var exec = require("child_process").exec;
var path = require("path");

var program = require("commander");

var perturb = require("../");
var generateConfig = require("../lib/config");
var omitUndefined = require("../lib/util/omit-undefined");
var MESSAGES = require("../lib/constant/messages");
var makeConfig = require("../built/make-config");
var MESSAGES = require("../built/constants/messages");

var pkg = require("../package.json");


var perturb = require("../");

program
.version(pkg.version)
.option("-r, --rootDir <rootDir>", "root directory of the project")
.option("-r, --projectRoot <rootDir>", "root directory of the project")
.option("-t, --testDir <testDir>", "test directory relative to root directory")
.option("-s, --sourceDir <sourceDir>", "source directory relative to root directory")
.option("-x, --testGlob <testGlob>", "glob for selecting files in test directory")
.option("-y, --sourceGlob <sourceGlob>", "glob for selecting files in source directory")
.option("-c, --testCmd <testCmd>", "test command")
// .option("-i, --interception", "use interception strategy (no disk I/O)")
// .option("-v, --verbose", "lots of logging")
.option("-k, --killRate", "minimum kill rate to exit with code 0")
// .option("-p, --parallel", "run mutants in parallel (careful!)")
.option("-u, --runner <runner>", "name of runner or runner plugin")
.option("-b, --bail", "")
// .option("--version")
.parse(process.argv);

if (program.rootDir && program.rootDir[0] !== "/") {
Expand All @@ -44,36 +31,29 @@ var userConfig = omitUndefined({
testGlob: program.testGlob,
sourceGlob: program.sourceGlob,
testCmd: program.testCmd,
interception: program.interception,
runner: program.runner,
});

// this will become the default reporter
function reporter (err, data) {
if (err) throw err;
console.log("reporter...");
console.log(data.meta);
}

// figure out how exec handles command line args
function runTests (settings, cb) {
var cmd = settings.testCmd || MESSAGES.DefaultTest;
console.log(MESSAGES.ExecutingTests, cmd);
exec(cmd, function (err) {
if (err) return cb(err);
console.log(MESSAGES.TestsPassed);
cb(null, settings);
});
}

runTests(generateConfig(userConfig), function (err, config) {
if (err) throw err;
perturb(config, reporter);
});

// sync errors inside perturb don't seem to properly cause a non-zero exit w/o this
process.on("uncaughtException", function (err) {
console.log("uncaught error in perturb process", err);
throw err;
});

// sync errors inside perturb don't seem to properly cause a non-zero exit w/o this
process.on("unhandledRejection", function (err) {
throw err;
});

// start!
perturb(userConfig).then(function (results) {
console.log("DONE -- COUNT:", results.length);
});

function omitUndefined (obj) {
const ret = {};
for (key in obj) {
if (obj[key] != null) ret[key] = obj[key]
}
return ret;
}
6 changes: 3 additions & 3 deletions script/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function run (perturb, which, runner) {
sourceDir: "built",
runner: runner || "mocha",
reporter: "diff",
testCmd: "make test-bail",
testCmd: "make test",
};
break;

Expand Down Expand Up @@ -46,9 +46,9 @@ if (!module.parent) {

// rethrowing it or exiting immediately seems to close the process before
// the entire error stack gets printed. So let's ease it up a bit.
process.on("unhandledRejection", err => {
process.on("unhandledRejection", err => {
console.log(err);
setTimeout(() => process.exit(1), 100);
});

module.exports = run;
module.exports = run;
8 changes: 4 additions & 4 deletions src/comments.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import R = require("ramda");
const debug = require("debug")("comments");
// const debug = require("debug")("comments");

const ENABLING_COMMENT = "perturb-enable:";
const DISABLING_COMMENT = "perturb-disable:";
Expand Down Expand Up @@ -71,12 +71,12 @@ class CommentManager {
_applyOperator (op: Operator) {
switch (op.type) {
case ENABLE: {
debug("enabling", op.name);
// debug("enabling", op.name);
this._disabled.delete(op.name); break;
}
case DISABLE: {
debug("disabling", op.name);
this._disabled.add(op.name); break;
// debug("disabling", op.name);
this._disabled.add(op.name);
}
}
}
Expand Down
1 change: 0 additions & 1 deletion src/filters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,3 @@ function inject (name) {
throw err;
}
}

121 changes: 62 additions & 59 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ import locateMutants = require("./locate-mutants");
import mutators = require("./mutators");
import parseMatch = require("./parse-match");

function hasTests (m: Match): boolean {
return Boolean(R.path(["tests", "length"], m));
}

const mapSeriesP = R.curry(R.flip(Bluebird.mapSeries));

async function perturb (_cfg: PerturbConfig) {
Expand All @@ -35,68 +31,71 @@ async function perturb (_cfg: PerturbConfig) {
const handler = makeMutantHandler(runner, reporter);
const locator = locateMutants(mutators.getMutatorsForNode);

let start;

// const testRun: Promise<void> = process.env.SKIP_TEST ? Promise.resolve() : runTest(cfg);

// first run the tests, otherwise why bother at all?
return spawnP(cfg.testCmd)
await spawnP(cfg.testCmd);

try {
// then, set up the "shadow" file system that we'll work against
.then(() => setup())
await setup();

// read those "shadow" directories and find the source and test files
.then(() => paths())
const { sources, tests } = await paths();

// use the matcher function to group {sourceFile, testFiles}
.then(function ({ sources, tests }) {
// TODO -- this section has become a catch-all for various crap that
// is actually orthogonal (tested/untested, start time, logging)
const matches = matcher(sources, tests);

const [tested, untested] = R.partition(hasTests, matches);

// TODO -- surface untested file names somehow
// console.log("untested files:", untested.map(m => m.source).join("\n"));

if (tested.length === 0) {
throw new Error("No matched files!");
}

const parsedMatches = tested
.map(parseMatch(locator))
.map(pm => {
pm.locations = pm.locations.filter(locationFilter);
return pm;
});

start = Date.now();
return R.chain(makeMutants, parsedMatches);
})
.then(sanityCheckAndSideEffects)
// TODO -- this section has become a catch-all for various crap that
// is actually orthogonal (tested/untested, start time, logging)
const matches = matcher(sources, tests);

const [ tested, untested ] = R.partition(hasTests, matches);

// TODO -- surface untested file names somehow
// console.log("untested files:", untested.map(m => m.source).join("\n"));
if (tested.length === 0) {
throw new Error("No matched files!");
}

const parsedMatches = tested
.map(parseMatch(locator))
.map(pm => {
pm.locations = pm.locations.filter(locationFilter);
return pm;
});

const start = Date.now();

// create the mutant objects from the matched files
let mutants = await R.chain(makeMutants, parsedMatches);

// let's just check if everything is okay...
await sanityCheckAndSideEffects(mutants)

if (process.env.SKIP_RUN) {
console.log("SKIP RUN:", mutants.length)
mutants = [];
}

// run the mutatnts and gather the results
.then(function (ms: Mutant[]) {
if (process.env.SKIP_RUN) {
console.log("SKIP RUN:", ms.length)
return [];
}

return mapSeriesP(handler, ms);
})
// run the final results handler, if supplied, then pass the results back
// to the caller
.then(function (rs: RunnerResult[]) {
// some run metadata here:
// duration: number
// runner: string
// sourceCount: number

const duration = (Date.now() - start) / 1000;
console.log("duration:", duration, "rate:", (rs.length / duration), "/s");

if (reporter.onFinish) {
reporter.onFinish(rs);
}
return rs;
})
.finally(teardown)
const results: RunnerResult[] = await mapSeriesP(handler, mutants);

const duration = (Date.now() - start) / 1000;
console.log("duration:", duration, "rate:", (results.length / duration), "/s");

if (reporter.onFinish) {
reporter.onFinish(results);
}

// TODO -- provide some run metadata here:
// duration: number
// runner: string
// sourceCount: number

return results;

} finally {
await teardown();
}
}

function makeMutantHandler (runner: RunnerPlugin, reporter: ReporterPlugin) {
Expand Down Expand Up @@ -127,6 +126,10 @@ function spawnP (fullCommand: string): Promise<void> {
});
}

function hasTests (m: Match): boolean {
return Boolean(R.path(["tests", "length"], m));
}

// TODO -- remove this, use Bluebird or something
Promise.prototype.finally = function (cb) {
return this.then(
Expand Down
13 changes: 7 additions & 6 deletions src/locate-mutants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@ import R = require("ramda");
import estraverse = require("estraverse");
import CommentManager = require("./comments");

const debug = require("debug")("locate-mutants");
// const debug = require("debug")("locate-mutants");f

type PluginService = (n: ESTree.Node) => MutatorPlugin[]

// one nice thing about this is that the plugins that are returned
// originate with the MutatorFinder argument, which simplifies testing
// originate with the PluginService argument, which simplifies testing
function locateMutants (mutatorsForNode: PluginService, ast: ESTree.Node): MutantLocation[] {
const mutantLocations: MutantLocation[] = [];
const manager = new CommentManager();

estraverse.traverse(ast, {
enter (node: ESTree.Node) {
debug("enter", node.type);
// debug("enter", node.type);
manager.applyLeading(node);
const path: string[] = this.path();
const locations = mutatorsForNode(node)
.filter(plugin => {
const status = manager.isEnabled(plugin.name);
if (!status) debug("disabled", plugin.name);
return status;
return manager.isEnabled(plugin.name);
// const status = manager.isEnabled(plugin.name);
// if (!status) debug("disabled", plugin.name);
// return status;
})
.filter(plugin => plugin.filter == null || plugin.filter(node))
.map(plugin => ({node, path, mutator: plugin}));
Expand Down
16 changes: 11 additions & 5 deletions src/make-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import fs = require("fs");
const assign = require("object-assign");

const CONFIG_FILE_NAME = ".perturbrc";
const DEFAULT_RUNNER = "mocha";
const DEFAULT_MATCHER = "contains-comparative";

const defaultConfig: PerturbConfig = {
testCmd: "npm test",

projectRoot: process.cwd(),
testDir: "test",
sourceDir: "src",
Expand All @@ -18,8 +20,8 @@ const defaultConfig: PerturbConfig = {
mutators: [],
skippers: [],
reporter: "diff",
matcher: "contains-comparative",
runner: "mocha-child-process",
matcher: DEFAULT_MATCHER,
runner: DEFAULT_RUNNER,
}

function makeConfig (userConfig = {}): PerturbConfig {
Expand All @@ -33,7 +35,11 @@ function makeConfig (userConfig = {}): PerturbConfig {
fileConfig = {};
}

return <PerturbConfig>assign({}, defaultConfig, fileConfig, userConfig);
const cfg = <PerturbConfig>assign({}, defaultConfig, fileConfig, userConfig);

// TODO -- validate shape here?

return cfg;
}

export = makeConfig;
export = makeConfig;
Loading

0 comments on commit ddfe643

Please sign in to comment.