Skip to content

Commit

Permalink
mutations working! now need to fix tests so THEY work
Browse files Browse the repository at this point in the history
  • Loading branch information
bttmly committed Jul 2, 2016
1 parent a3b1f6c commit c45fbfc
Show file tree
Hide file tree
Showing 15 changed files with 109 additions and 83 deletions.
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ lint:
events:
./bin/perturb -r ./examples/event-emitter

example:
./node_modules/.bin/tsc
node ./run.js example

dogfood:
rm -rf ./.perturb
./node_modules/.bin/tsc
node ./run.js dogfood

Expand Down
1 change: 1 addition & 0 deletions run.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ function run (perturb, which, cb) {
config = {
rootDir: path.join(__dirname, ".."),
runner: "mocha",
sourceDir: "built",
};
break;

Expand Down
100 changes: 50 additions & 50 deletions test/helpers.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
"use strict";
// "use strict";

var esprima = require("esprima");
var _ = require("lodash");
var assign = require("object-assign");
var I = require("immutable");
var NODE_TYPES = require("../src/constants/node-types");
var mutators = require("../src/mutators").mutators;
// var esprima = require("esprima");
// var _ = require("lodash");
// var assign = require("object-assign");
// var I = require("immutable");
// var NODE_TYPES = require("../src/constants/node-types");
// var mutators = require("../src/mutators").mutators;

function isPrimitiveValue (value) {
var t = typeof value;
return (
value !== null && (
t === "symbol" ||
t === "string" ||
t === "number" ||
t === "boolean"
)
);
}
// function isPrimitiveValue (value) {
// var t = typeof value;
// return (
// value !== null && (
// t === "symbol" ||
// t === "string" ||
// t === "number" ||
// t === "boolean"
// )
// );
// }

function objIsShallow (obj) {
if (isPrimitiveValue(obj)) return false;
return Object.keys(obj).every(function (key) {
return isPrimitiveValue(obj[key]);
});
}
// function objIsShallow (obj) {
// if (isPrimitiveValue(obj)) return false;
// return Object.keys(obj).every(function (key) {
// return isPrimitiveValue(obj[key]);
// });
// }

function makeNodeOfType (type, props) {
if (!(type in NODE_TYPES)) throw new Error("Invalid node type " + type);
props = props || {};
return I.Map(assign({type: type}, props));
}
// function makeNodeOfType (type, props) {
// if (!(type in NODE_TYPES)) throw new Error("Invalid node type " + type);
// props = props || {};
// return I.Map(assign({type: type}, props));
// }

function nodeFromCode (code) {
var ast = esprima.parse(code);
if (ast.body.length !== 1) {
throw new Error("Rendered AST did not have exactly one node");
}
return I.fromJS(ast.body[0]);
}
// function nodeFromCode (code) {
// var ast = esprima.parse(code);
// if (ast.body.length !== 1) {
// throw new Error("Rendered AST did not have exactly one node");
// }
// return I.fromJS(ast.body[0]);
// }

var mutatorByName = (function () {
var m = _.indexBy(mutators, "name");
return function mutatorByName (n) {
var mutator = m[n];
if (mutator == null) throw new Error("No mutator found by name " + n);
return mutator;
};
})();
// var mutatorByName = (function () {
// var m = _.indexBy(mutators, "name");
// return function mutatorByName (n) {
// var mutator = m[n];
// if (mutator == null) throw new Error("No mutator found by name " + n);
// return mutator;
// };
// })();

module.exports = {
objIsShallow: objIsShallow,
makeNodeOfType: makeNodeOfType,
nodeFromCode: nodeFromCode,
mutatorByName: mutatorByName,
};
// module.exports = {
// objIsShallow: objIsShallow,
// makeNodeOfType: makeNodeOfType,
// nodeFromCode: nodeFromCode,
// mutatorByName: mutatorByName,
// };
23 changes: 15 additions & 8 deletions tsc/file-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,16 @@ function setupPerturbDirectory (config: PerturbConfig): void {
fs.copySync(sourceAbs, pSourceAbs);
fs.copySync(testAbs, pTestAbs);

// fs.readdirSync(config.rootDir)
// .filter(f => shouldSymlink.has(f))
// .map(item => [path.join(config.rootDir, item), path.join(config.perturbRoot, item)])
// .forEach(R.apply(fs.symlinkSync))
fs.readdirSync(projectRoot)
.filter(f => shouldSymlink.has(f))
.map(item => [path.join(projectRoot, item), path.join(pAbs, item)])
.forEach(R.apply(fs.symlinkSync))
}

function teardownPerturbDirectory (config): void {
fs.removeSync(config.perturbRoot);
const {projectRoot, sourceDir, testDir, perturbDir} = config;
const pAbs = path.join(projectRoot, perturbDir);
fs.removeSync(pAbs);
}

type FilePathResult = { sources: string[], tests: string[] };
Expand All @@ -53,10 +55,15 @@ interface FsHelper {
paths(): FilePathResult;
}

function getFilePaths (config): FilePathResult {
function getFilePaths (config: PerturbConfig): FilePathResult {
const {projectRoot, sourceDir, testDir, perturbDir} = config;
const pAbs = path.join(projectRoot, perturbDir);
const pSourceAbs = path.join(pAbs, sourceDir);
const pTestAbs = path.join(pAbs, testDir);

return {
sources: glob.sync(config.perturbSourceDir + config.sourceGlob),
tests: glob.sync(config.perturbTestDir + config.testGlob),
sources: glob.sync(pSourceAbs + config.sourceGlob),
tests: glob.sync(pTestAbs + config.testGlob),
};
}

Expand Down
12 changes: 8 additions & 4 deletions tsc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@ function hasTests (m: Match): boolean {
}

module.exports = function perturb (_cfg: PerturbConfig) {
console.log("START...");

const cfg = makeConfig(_cfg);


const {setup, teardown, paths} = fileSystem(cfg);

const matcher = getMatcher(cfg);
const runner: RunnerPlugin = getRunner(cfg.runner);
const reporter: ReporterPlugin = getReporter(cfg.reporter);

// this handler does all the interesting work on a single Mutant
const handler = makeMutantHandler(runner, reporter);

Expand All @@ -49,10 +48,15 @@ module.exports = function perturb (_cfg: PerturbConfig) {
const matches = matcher(sources, tests);
const [tested, untested] = R.partition(hasTests, matches);
// TODO -- surface untested file names somehow

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

return tested;
})
//
.then(makeMutants)
.then(matches => R.chain(makeMutants, matches))
.then(function (ms: Mutant[]) {
// TODO -- right here we can serialize all the mutants before running them
// any reason we might want to do this?
Expand Down
13 changes: 9 additions & 4 deletions tsc/make-mutants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const R = require("ramda");
const fs = require("fs-extra");
const exprima = require("esprima");
const esprima = require("esprima");
const escodegen = require("escodegen");
const estraverse = require("estraverse");

Expand All @@ -23,7 +23,6 @@ const FS_SETTINGS = {
};

module.exports = function makeMutants (match: Match): Mutant[] {

const {source, tests} = match;
const {ast, code} = parse(source);
const paths: Path[] = getMutationPaths(ast).map(p => p.map(String));
Expand Down Expand Up @@ -68,8 +67,13 @@ function getMutationPaths (ast: ESTree.Node) {
estraverse.traverse(ast, {
enter: function (node: ESTree.Node) {
const path = <Path>this.path();
if (shouldSkip(node, path)) return this.skip();
if (hasAvailableMutations(node)) mutationPaths.push(path);
if (shouldSkip(node, path)) {
return this.skip();
}

if (hasAvailableMutations(node)) {
mutationPaths.push(path)
}
},
});
return mutationPaths;
Expand All @@ -89,6 +93,7 @@ interface _ParseResult {
}

function parse (source: string): _ParseResult {
console.log("reading", source, "...");
const originalSource = fs.readFileSync(source).toString();

let ast : ESTree.Node;
Expand Down
2 changes: 1 addition & 1 deletion tsc/mutators/drop-operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface ArgumentedNode extends ESTree.Node {
argument: any
}

export default <MutatorPlugin>{
module.exports = <MutatorPlugin>{
name: "dropOperator",
nodeTypes: [
Syntax.ThrowStatement,
Expand Down
4 changes: 2 additions & 2 deletions tsc/mutators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function isMutatorEnabled (m: MutatorPlugin): boolean {
function makeMutatorIndex (names: string[]) {
const additionalMutators = locateMutatorPlugins(names);
const allMutators = coreMutators.concat(additionalMutators).filter(isMutatorEnabled);

// const index : { string: MutatorPlugin[] } = {};
const index = {};
allMutators.forEach(function (m: MutatorPlugin) {
Expand Down Expand Up @@ -77,4 +77,4 @@ exports.getMutatorsForNode = function (node: ESTree.Node): MutatorPlugin[] {
return R.propOr([], node.type, mutatorIndex);
}

exports.injectPlugins([]);
mutatorIndex = makeMutatorIndex([]);
2 changes: 1 addition & 1 deletion tsc/mutators/reverse-function-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface FunctionNode extends ESTree.Node {

// reverse the perameter order for a function expression or declaration
// `function fn (a, b) {}` => `function fn (b, a) {}`
export default <MutatorPlugin>{
module.exports = <MutatorPlugin>{
name: "reverseFunctionParameters",
nodeTypes: Object.keys(FUNC_NODES),
filter: function (node) {
Expand Down
2 changes: 1 addition & 1 deletion tsc/mutators/swap-binary-operators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const NO_SWAP = {
// `var since = new Date() - start;` => `var since = new Date() + start;`
// `var dy = rise / run;` => `var dy = rise * run;`
// `var area = w * h;` => `var area = w / h;`
export default <MutatorPlugin>{
module.exports = <MutatorPlugin>{
name: "swapBinaryOperators",
nodeTypes: [Syntax.BinaryExpression],
filter: function (node) {
Expand Down
2 changes: 1 addition & 1 deletion tsc/mutators/tweak-boolean-literal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const R = require("ramda");
import { MutatorPlugin } from "../types";

// `var isOk = true` => `var isOk = false`
export default <MutatorPlugin>{
module.exports = <MutatorPlugin>{
name: "tweakBooleanLiteral",
nodeTypes: [Syntax.Literal],
filter: function (node) {
Expand Down
2 changes: 1 addition & 1 deletion tsc/mutators/tweak-object-literal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const R = require("ramda");

import { MutatorPlugin } from "../types";

export default <MutatorPlugin>{
module.exports = <MutatorPlugin>{
// drops the first declared property in an object literal
// `{prop1: "val1", prop2: "val2"}` => `{prop2: "val2"}`
name: "tweakObjectLiteral",
Expand Down
2 changes: 1 addition & 1 deletion tsc/mutators/tweak-string-literal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface StringLiteral extends ESTree.Node {

const EMPTY_REPLACEMENT = "a";

export default <MutatorPlugin>{
module.exports = <MutatorPlugin>{
// drops first character of non-empty string; changes
// empty strings to "a"
// var s = ""; => var s = "a";
Expand Down
6 changes: 5 additions & 1 deletion tsc/reporters/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,9 @@ function generateDiff (r: RunnerResult) {

function identifier (r: RunnerResult) {
const loc = r.loc.start.line + "," + r.loc.start.column;
return changeCase.sentence(r.mutatorName) + " @" + r.loc;

// hack :/
const file = r.sourceFile.split(".perturb")[1];

return changeCase.sentence(r.mutatorName) + " " + file + "@" + loc;
}
16 changes: 12 additions & 4 deletions tsc/runners/mocha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,24 @@ module.exports = <RunnerPlugin>{
return new Promise(function(resolve) {
let failedOn;

const reporter = suite =>
suite.on("fail", test => failedOn = test);
class Reporter {
constructor (runner) {
runner.on("fail", test => failedOn = test);
}
}

const mocha = new Mocha({ reporter, bail: true });
const mocha = new Mocha({ reporter: Reporter, bail: true });

m.testFiles.forEach(t => mocha.addFile(t));

try {
mocha.run(() => resolve(failedOn));
mocha.run(function (f) {
console.log("FAILURES", f);
resolve(failedOn);
});

} catch (err) {
console.log("caught sync error starting mocha", err);
return resolve(err);
}
})
Expand Down

0 comments on commit c45fbfc

Please sign in to comment.