From d4e1a3ec3b5cf51fed87969c09793b023b7cef55 Mon Sep 17 00:00:00 2001 From: Nick Bottomley Date: Fri, 1 Jul 2016 18:03:20 -0700 Subject: [PATCH] compile to built for now --- .gitignore | 4 +- Makefile | 2 +- index.js | 1 - lib/index.js | 68 --------------- lib/matchers/base-comparative.js | 11 --- lib/matchers/base-generative.js | 11 --- lib/matchers/contains-comparative.js | 19 ----- lib/matchers/index.js | 40 --------- lib/reporters/index.js | 26 ------ lib/runners/index.js | 26 ------ lib/runners/mocha.js | 51 ----------- lib/src/runners/runner.js | 0 lib/tsc/constants/binary-operator-swaps.js | 21 ----- lib/tsc/constants/errors.js | 11 --- lib/tsc/constants/func-nodes.js | 7 -- lib/tsc/constants/js-types.js | 10 --- lib/tsc/constants/messages.js | 7 -- lib/tsc/constants/node-attrs.js | 13 --- lib/tsc/constants/node-types.js | 45 ---------- lib/tsc/constants/test-nodes.js | 9 -- lib/tsc/file-system.js | 41 --------- lib/tsc/index.js | 69 --------------- lib/tsc/make-mutants.js | 85 ------------------- lib/tsc/matchers/base-comparative.js | 13 --- lib/tsc/matchers/base-generative.js | 13 --- lib/tsc/matchers/contains-comparative.js | 21 ----- lib/tsc/matchers/index.js | 43 ---------- lib/tsc/mutators/_void-node.js | 12 --- lib/tsc/mutators/drop-member-assignment.js | 14 --- lib/tsc/mutators/drop-node.js | 12 --- lib/tsc/mutators/drop-operator.js | 13 --- lib/tsc/mutators/drop-return.js | 18 ---- lib/tsc/mutators/drop-void-call.js | 21 ----- lib/tsc/mutators/index.js | 69 --------------- lib/tsc/mutators/invert-conditional-test.js | 22 ----- .../mutators/reverse-function-parameters.js | 16 ---- lib/tsc/mutators/swap-binary-operators.js | 23 ----- lib/tsc/mutators/swap-logical-operators.js | 16 ---- lib/tsc/mutators/tweak-array-literal.js | 36 -------- lib/tsc/mutators/tweak-boolean-literal.js | 16 ---- lib/tsc/mutators/tweak-number-literal.js | 17 ---- lib/tsc/mutators/tweak-object-literal.js | 32 ------- lib/tsc/mutators/tweak-string-literal.js | 21 ----- lib/tsc/reporters/diff.js | 46 ---------- lib/tsc/reporters/index.js | 27 ------ lib/tsc/runners/index.js | 30 ------- lib/tsc/runners/mocha-child.js | 0 lib/tsc/runners/mocha.js | 53 ------------ lib/tsc/skippers/index.js | 37 -------- lib/tsc/types.js | 2 - lib/types.js | 1 - parity.js | 16 +--- src/runners/runner.ts | 0 tsc/constants/func-nodes.ts | 8 +- tsc/constants/js-types.ts | 2 +- tsc/constants/messages.ts | 2 +- tsc/constants/node-attrs.ts | 2 +- tsc/constants/node-types.ts | 2 +- tsc/constants/test-nodes.ts | 14 +-- tsc/index.ts | 14 +-- tsc/make-config.ts | 36 ++++++++ tsc/make-mutants.ts | 1 - tsc/matchers/base-comparative.ts | 9 +- tsc/matchers/base-generative.ts | 11 +-- tsc/matchers/contains-comparative.ts | 12 ++- tsc/matchers/index.ts | 8 +- tsc/mutators/_void-node.ts | 8 +- tsc/mutators/drop-member-assignment.ts | 11 +-- tsc/mutators/drop-node.ts | 11 ++- tsc/mutators/drop-operator.ts | 9 +- tsc/mutators/drop-return.ts | 9 +- tsc/mutators/drop-void-call.ts | 8 +- tsc/mutators/index.ts | 8 +- tsc/mutators/invert-conditional-test.ts | 25 +++--- tsc/mutators/reverse-function-parameters.ts | 10 +-- tsc/mutators/swap-binary-operators.ts | 20 +++-- tsc/mutators/swap-logical-operators.ts | 14 ++- tsc/mutators/tweak-array-literal.ts | 10 +-- tsc/mutators/tweak-boolean-literal.ts | 8 +- tsc/mutators/tweak-number-literal.ts | 8 +- tsc/mutators/tweak-object-literal.ts | 8 +- tsc/mutators/tweak-string-literal.ts | 8 +- tsc/reporters/diff.ts | 4 +- tsc/runners/index.ts | 7 +- tsc/runners/mocha-child.ts | 0 tsc/runners/mocha.ts | 10 +-- tsc/skippers/index.ts | 27 +++--- tsc/types.ts | 12 +-- tsconfig.json | 8 +- 89 files changed, 211 insertions(+), 1390 deletions(-) delete mode 100644 index.js delete mode 100644 lib/index.js delete mode 100644 lib/matchers/base-comparative.js delete mode 100644 lib/matchers/base-generative.js delete mode 100644 lib/matchers/contains-comparative.js delete mode 100644 lib/matchers/index.js delete mode 100644 lib/reporters/index.js delete mode 100644 lib/runners/index.js delete mode 100644 lib/runners/mocha.js delete mode 100644 lib/src/runners/runner.js delete mode 100644 lib/tsc/constants/binary-operator-swaps.js delete mode 100644 lib/tsc/constants/errors.js delete mode 100644 lib/tsc/constants/func-nodes.js delete mode 100644 lib/tsc/constants/js-types.js delete mode 100644 lib/tsc/constants/messages.js delete mode 100644 lib/tsc/constants/node-attrs.js delete mode 100644 lib/tsc/constants/node-types.js delete mode 100644 lib/tsc/constants/test-nodes.js delete mode 100644 lib/tsc/file-system.js delete mode 100644 lib/tsc/index.js delete mode 100644 lib/tsc/make-mutants.js delete mode 100644 lib/tsc/matchers/base-comparative.js delete mode 100644 lib/tsc/matchers/base-generative.js delete mode 100644 lib/tsc/matchers/contains-comparative.js delete mode 100644 lib/tsc/matchers/index.js delete mode 100644 lib/tsc/mutators/_void-node.js delete mode 100644 lib/tsc/mutators/drop-member-assignment.js delete mode 100644 lib/tsc/mutators/drop-node.js delete mode 100644 lib/tsc/mutators/drop-operator.js delete mode 100644 lib/tsc/mutators/drop-return.js delete mode 100644 lib/tsc/mutators/drop-void-call.js delete mode 100644 lib/tsc/mutators/index.js delete mode 100644 lib/tsc/mutators/invert-conditional-test.js delete mode 100644 lib/tsc/mutators/reverse-function-parameters.js delete mode 100644 lib/tsc/mutators/swap-binary-operators.js delete mode 100644 lib/tsc/mutators/swap-logical-operators.js delete mode 100644 lib/tsc/mutators/tweak-array-literal.js delete mode 100644 lib/tsc/mutators/tweak-boolean-literal.js delete mode 100644 lib/tsc/mutators/tweak-number-literal.js delete mode 100644 lib/tsc/mutators/tweak-object-literal.js delete mode 100644 lib/tsc/mutators/tweak-string-literal.js delete mode 100644 lib/tsc/reporters/diff.js delete mode 100644 lib/tsc/reporters/index.js delete mode 100644 lib/tsc/runners/index.js delete mode 100644 lib/tsc/runners/mocha-child.js delete mode 100644 lib/tsc/runners/mocha.js delete mode 100644 lib/tsc/skippers/index.js delete mode 100644 lib/tsc/types.js delete mode 100644 lib/types.js delete mode 100644 src/runners/runner.ts create mode 100644 tsc/make-config.ts delete mode 100644 tsc/runners/mocha-child.ts diff --git a/.gitignore b/.gitignore index 83926e4..ff14561 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,6 @@ node_modules .perturb example/.DS_Store example/.perturb -typings \ No newline at end of file +typings +lib +built \ No newline at end of file diff --git a/Makefile b/Makefile index 0fe39c9..2a74ddc 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,6 @@ dogfood: ./node_modules/.bin/babel-node ./src/run.js dogfood build: - ./node_modules/babel-cli/bin/babel.js --out-dir lib src, + ./node_modules/babel-cli/bin/babel.js --out-dir built src .PHONY: test example diff --git a/index.js b/index.js deleted file mode 100644 index bf63049..0000000 --- a/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("./lib"); diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 6191a25..0000000 --- a/lib/index.js +++ /dev/null @@ -1,68 +0,0 @@ -/// -/// -/// -const Bluebird = require("bluebird"); -const R = require("ramda"); -const getRunner = require("./runners/index"); -const getReporter = require("./reporters/index"); -const getMatcher = require("./matchers/index"); -const makeMutants = require("./make-mutants"); -const fileSystem = require("./file-system"); -function hasTests(m) { - return Boolean(R.path(["tests", "length"], m)); -} -module.exports = function perturb(cfg) { - const { setup, teardown, paths } = fileSystem(cfg); - const matcher = getMatcher(cfg); - const runner = getRunner(cfg.runner); - const reporter = getReporter(cfg.reporter); - // this handler does all the interesting work on a single Mutant - const handler = makeMutantHandler(runner, reporter); - // first, set up the "shadow" file system that we'll work against - return Promise.resolve(setup()) - .then(() => paths()) - .then(function ({ sources, tests }) { - const matches = matcher(sources, tests); - const [tested, untested] = R.partition(hasTests, matches); - // TODO -- surface untested file names somehow - return tested; - }) - .then(makeMutants) - .then(function (ms) { - // TODO -- right here we can serialize all the mutants before running them - // any reason we might want to do this? - // - // this is the separation point between pure data and actually executing - // the tests against mutated source code - return ms; - }) - .then(function (ms) { - return Bluebird.mapSeries(ms, handler); - }) - .then(function (rs) { - if (reporter.onFinish) { - reporter.onFinish(rs); - } - return rs; - }); -}; -function makeMutantHandler(runner, reporter) { - return function handler(m) { - let _before, _result; - return runner.prepare(m) - .then(before => { - _before = before; - return runner.run(m); - }) - .then(result => { - _result = result; - return runner.cleanup(result, _before); - }) - .then(() => { - if (reporter.onResult) { - reporter.onResult(_result); - } - return _result; - }); - }; -} diff --git a/lib/matchers/base-comparative.js b/lib/matchers/base-comparative.js deleted file mode 100644 index b828d39..0000000 --- a/lib/matchers/base-comparative.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as path from "path"; -export default { - type: "comparative", - makeMatcher: function (c) { - return function (sourceFile, testFile) { - var sourceName = sourceFile.split(path.join(c.perturbRoot, c.perturbSourceDir)).pop(); - var testName = testFile.split(path.join(c.perturbRoot, c.perturbTestDir)).pop(); - return sourceName === testName; - }; - } -}; diff --git a/lib/matchers/base-generative.js b/lib/matchers/base-generative.js deleted file mode 100644 index ad45bfb..0000000 --- a/lib/matchers/base-generative.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as path from "path"; -export default { - type: "generative", - makeMatcher: function (c) { - return function (sourceFile) { - var sourceRelPath = path.join(c.perturbRoot, c.perturbSourceDir); - var testRelPath = path.join(c.perturbRoot, c.perturbTestDir); - return sourceFile.replace(sourceRelPath, testRelPath); - }; - }, -}; diff --git a/lib/matchers/contains-comparative.js b/lib/matchers/contains-comparative.js deleted file mode 100644 index f5cdaab..0000000 --- a/lib/matchers/contains-comparative.js +++ /dev/null @@ -1,19 +0,0 @@ -import * as path from "path"; -// matches: -// source: project/lib/dir/car.js -// test: project/test/dir/car-constructor.js -// test: project/test/dir/car-start.js -// test: project/test/dir/car-drive.js -function withoutExt(file) { - return file.slice(0, -1 * path.extname(file).length); -} -export default { - type: "comparative", - makeMatcher: function (c) { - return function (sourceFile, testFile) { - var sourceName = withoutExt(sourceFile.split(c.perturbSourceDir).pop()); - var testName = withoutExt(testFile.split(c.perturbTestDir).pop()); - return testName.slice(0, sourceName.length) === sourceName; - }; - } -}; diff --git a/lib/matchers/index.js b/lib/matchers/index.js deleted file mode 100644 index 4875ce4..0000000 --- a/lib/matchers/index.js +++ /dev/null @@ -1,40 +0,0 @@ -import * as R from "ramda"; -function runComparative(compare, source, tests) { - return tests.filter(t => compare(source, t)); -} -function runGenerative(generate, source, tests) { - const name = generate(source); - return R.contains(name, tests) ? [name] : []; -} -export default function getMatcher(c) { - const matcherPlugin = getMatcherPlugin(c.matcher); - const { type } = matcherPlugin; - const matcher = matcherPlugin.makeMatcher(c); - return function findMatches(sources, tests) { - const runMatch = type === "generative" ? runGenerative : runComparative; - return sources.map(function (source) { - return { source, tests: runMatch(matcher, source, tests) }; - }); - }; -} -import baseGenerative from "./base-generative"; -import baseComparative from "./base-comparative"; -import containsComparative from "./contains-comparative"; -const builtIns = new Map([ - ["base-generative", baseGenerative], - ["base-comparative", baseComparative], - ["contains-comparative", containsComparative], -]); -function getMatcherPlugin(name) { - const plugin = builtIns.get(name); - if (plugin) - return plugin; - try { - // TODO -- runtime type check this import - return require(`perturb-matcher-plugin-${name}`); - } - catch (err) { - console.log("Fatal error: unable to resolve MATCHER plugin name", name); - throw err; - } -} diff --git a/lib/reporters/index.js b/lib/reporters/index.js deleted file mode 100644 index 6c08f48..0000000 --- a/lib/reporters/index.js +++ /dev/null @@ -1,26 +0,0 @@ -const diffReporter = require("./diff"); -const plugins = new Map([ - ["diff", diffReporter], -]); -export function locateReporterPlugins(names) { - names.forEach(function (name) { - let plugin; - try { - plugin = require(`perturb-plugin-runner-${name}`); - plugins.set(name, plugin); - return; - } - catch (err) { - // any way to recover? other locate strategy? - console.log(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - throw err; - } - }); -} -export default function get(name) { - const p = plugins.get(name); - if (p == null) { - throw new Error(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - } - return p; -} diff --git a/lib/runners/index.js b/lib/runners/index.js deleted file mode 100644 index fda7a6f..0000000 --- a/lib/runners/index.js +++ /dev/null @@ -1,26 +0,0 @@ -import mochaRunner from "./mocha"; -const plugins = new Map([ - ["mocha", mochaRunner], -]); -export function injectPlugins(names) { - names.forEach(function (name) { - let plugin; - try { - plugin = require(`perturb-plugin-runner-${name}`); - plugins.set(name, plugin); - return; - } - catch (err) { - // any way to recover? other locate strategy? - console.log(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - throw err; - } - }); -} -export default function get(name) { - const plugin = plugins.get(name); - if (plugin == null) { - throw new Error(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - } - return plugin; -} diff --git a/lib/runners/mocha.js b/lib/runners/mocha.js deleted file mode 100644 index a7d4836..0000000 --- a/lib/runners/mocha.js +++ /dev/null @@ -1,51 +0,0 @@ -/// -/// -import * as fs from "fs"; -import * as R from "ramda"; -import * as Mocha from "mocha"; -const doesNotContain = arr => item => !R.contains(item, arr); -const doesNotHave = obj => prop => !R.has(prop, obj); -const delProp = obj => prop => delete obj[prop]; -function mirror(arr) { - const out = {}; - arr.forEach(x => out[x] = x); - return out; -} -export default { - prepare: function (m) { - delete require.cache[m.sourceFile]; - fs.writeFileSync(m.sourceFile, m.mutatedSourceCode); - return Promise.resolve({ - cache: mirror(Object.keys(require.cache)), - listeners: process.listeners("uncaughtException"), - }); - }, - run: function (m) { - return new Promise(function (resolve) { - let failedOn; - const reporter = suite => suite.on("fail", test => failedOn = test); - const mocha = new Mocha({ reporter, bail: true }); - m.testFiles.forEach(t => mocha.addFile(t)); - try { - mocha.run(() => resolve(failedOn)); - } - catch (err) { - return resolve(err); - } - }) - .then(error => Object.assign({}, m, { error })); - }, - cleanup: function (m, before) { - // write the original source code back to it's location - fs.writeFileSync(m.sourceFile, m.originalSourceCode); - // remove danging uncaughtException listeners Mocha didn't clean up - process.listeners("uncaughtException") - .filter(doesNotContain(before.listeners)) - .forEach(f => process.removeListener("uncaughtException", f)); - // remove all modules that were required by this test - Object.keys(require.cache) - .filter(doesNotHave(before.cache)) - .forEach(delProp(require.cache)); - return Promise.resolve(); - } -}; diff --git a/lib/src/runners/runner.js b/lib/src/runners/runner.js deleted file mode 100644 index e69de29..0000000 diff --git a/lib/tsc/constants/binary-operator-swaps.js b/lib/tsc/constants/binary-operator-swaps.js deleted file mode 100644 index 0247a14..0000000 --- a/lib/tsc/constants/binary-operator-swaps.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - "+": "-", - "-": "+", - "*": "/", - "/": "*", - ">": "<=", - "<=": ">", - "<": ">=", - ">=": "<", - "==": "!=", - "!=": "==", - "===": "!==", - "!==": "===", - "%": "*", - "&": "|", - "|": "&", - "^": "&", - "<<": ">>", - ">>": "<<", - ">>>": "<<<", -}; diff --git a/lib/tsc/constants/errors.js b/lib/tsc/constants/errors.js deleted file mode 100644 index 1fd1f41..0000000 --- a/lib/tsc/constants/errors.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - NotKeyedIterable: "Node must be an Immutable.js keyed iterable", - WrongNodeType: "Node is of wrong type. Actual: %s; Expected: %s", - TestsFailed: "Test command `%s` failed. Tests must be passing for perturb to work properly.", - NoSourceDir: "No `sourceDir` property found in `this` context", - NoTestDir: "No `testDir` property found in `this` context", - NoMatches: "Unable to match any source and test files", - InvalidRunner: "No test runner named '%s' found.", - InvalidMutator: "Invalid mutator", - NoCallback: "Must Provide a callback function.", -}; diff --git a/lib/tsc/constants/func-nodes.js b/lib/tsc/constants/func-nodes.js deleted file mode 100644 index d8d506b..0000000 --- a/lib/tsc/constants/func-nodes.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -const node_types_1 = require("./node-types"); -module.exports = { - [node_types_1.default.FunctionDeclaration]: node_types_1.default.FunctionDeclaration, - [node_types_1.default.FunctionExpression]: node_types_1.default.FunctionExpression, - [node_types_1.default.ArrowFunctionExpression]: node_types_1.default.ArrowFunctionExpression, -}; diff --git a/lib/tsc/constants/js-types.js b/lib/tsc/constants/js-types.js deleted file mode 100644 index fc2b1f0..0000000 --- a/lib/tsc/constants/js-types.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - func: "function", - bool: "boolean", - str: "string", - num: "number", - obj: "object", - sym: "symbol", -}; diff --git a/lib/tsc/constants/messages.js b/lib/tsc/constants/messages.js deleted file mode 100644 index d6516b6..0000000 --- a/lib/tsc/constants/messages.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - TestsPassed: "Test command exited with `0`. Continuing...", - ExecutingTests: "executing `%s` ...", - DefaultTest: "npm test", -}; diff --git a/lib/tsc/constants/node-attrs.js b/lib/tsc/constants/node-attrs.js deleted file mode 100644 index 2655800..0000000 --- a/lib/tsc/constants/node-attrs.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - operator: "operator", - elements: "elements", - properties: "properties", - value: "value", - type: "type", - test: "test", - argument: "argument", - arguments: "arguments", - params: "params", -}; diff --git a/lib/tsc/constants/node-types.js b/lib/tsc/constants/node-types.js deleted file mode 100644 index aa2a4ea..0000000 --- a/lib/tsc/constants/node-types.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - AssignmentExpression: "AssignmentExpression", - ArrayExpression: "ArrayExpression", - ArrowFunctionExpression: "ArrowFunctionExpression", - BlockStatement: "BlockStatement", - BinaryExpression: "BinaryExpression", - BreakStatement: "BreakStatement", - CallExpression: "CallExpression", - CatchClause: "CatchClause", - ConditionalExpression: "ConditionalExpression", - ContinueStatement: "ContinueStatement", - DoWhileStatement: "DoWhileStatement", - DebuggerStatement: "DebuggerStatement", - EmptyStatement: "EmptyStatement", - ExpressionStatement: "ExpressionStatement", - ForStatement: "ForStatement", - ForInStatement: "ForInStatement", - FunctionDeclaration: "FunctionDeclaration", - FunctionExpression: "FunctionExpression", - Identifier: "Identifier", - IfStatement: "IfStatement", - Literal: "Literal", - LabeledStatement: "LabeledStatement", - LogicalExpression: "LogicalExpression", - MemberExpression: "MemberExpression", - NewExpression: "NewExpression", - ObjectExpression: "ObjectExpression", - Program: "Program", - Property: "Property", - ReturnStatement: "ReturnStatement", - SequenceExpression: "SequenceExpression", - SwitchStatement: "SwitchStatement", - SwitchCase: "SwitchCase", - ThisExpression: "ThisExpression", - ThrowStatement: "ThrowStatement", - TryStatement: "TryStatement", - UnaryExpression: "UnaryExpression", - UpdateExpression: "UpdateExpression", - VariableDeclaration: "VariableDeclaration", - VariableDeclarator: "VariableDeclarator", - WhileStatement: "WhileStatement", - WithStatement: "WithStatement", -}; diff --git a/lib/tsc/constants/test-nodes.js b/lib/tsc/constants/test-nodes.js deleted file mode 100644 index 43b07cf..0000000 --- a/lib/tsc/constants/test-nodes.js +++ /dev/null @@ -1,9 +0,0 @@ -const NODE_TYPES = require("./node-types"); -module.exports = { - [NODE_TYPES.IfStatement]: NODE_TYPES.IfStatement, - [NODE_TYPES.WhileStatement]: NODE_TYPES.WhileStatement, - [NODE_TYPES.DoWhileStatement]: NODE_TYPES.DoWhileStatement, - [NODE_TYPES.ForStatement]: NODE_TYPES.ForStatement, - [NODE_TYPES.ConditionalExpression]: NODE_TYPES.ConditionalExpression, - [NODE_TYPES.SwitchCase]: NODE_TYPES.SwitchCase, -}; diff --git a/lib/tsc/file-system.js b/lib/tsc/file-system.js deleted file mode 100644 index bcc966a..0000000 --- a/lib/tsc/file-system.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -const path = require("path"); -const glob = require("glob"); -const fs = require("fs-extra"); -const R = require("ramda"); -const shouldSymlink = new Set([ - "node_modules" -]); -function setupPerturbDirectory(config) { - // maybe remove this? if it exists it means there is a bug with cleanup - fs.removeSync(config.perturbRoot); - fs.mkdirSync(config.perturbRoot); - fs.copySync(config.originalSourceDir, config.perturbSourceDir); - fs.copySync(config.originalTestDir, config.perturbTestDir); - 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)); -} -function teardownPerturbDirectory(config) { - fs.removeSync(config.perturbRoot); -} -function getFilePaths(config) { - return { - sources: glob.sync(config.perturbSourceDir + config.sourceGlob), - tests: glob.sync(config.perturbTestDir + config.testGlob), - }; -} -module.exports = function createFsHelpers(c) { - return { - setup() { - setupPerturbDirectory(c); - }, - teardown() { - teardownPerturbDirectory(c); - }, - paths() { - return getFilePaths(c); - }, - }; -}; diff --git a/lib/tsc/index.js b/lib/tsc/index.js deleted file mode 100644 index a5f05eb..0000000 --- a/lib/tsc/index.js +++ /dev/null @@ -1,69 +0,0 @@ -/// -/// -/// -"use strict"; -const Bluebird = require("bluebird"); -const R = require("ramda"); -const getRunner = require("./runners/index"); -const getReporter = require("./reporters/index"); -const getMatcher = require("./matchers/index"); -const makeMutants = require("./make-mutants"); -const fileSystem = require("./file-system"); -function hasTests(m) { - return Boolean(R.path(["tests", "length"], m)); -} -module.exports = function perturb(cfg) { - const { setup, teardown, paths } = fileSystem(cfg); - const matcher = getMatcher(cfg); - const runner = getRunner(cfg.runner); - const reporter = getReporter(cfg.reporter); - // this handler does all the interesting work on a single Mutant - const handler = makeMutantHandler(runner, reporter); - // first, set up the "shadow" file system that we'll work against - return Promise.resolve(setup()) - .then(() => paths()) - .then(function ({ sources, tests }) { - const matches = matcher(sources, tests); - const [tested, untested] = R.partition(hasTests, matches); - // TODO -- surface untested file names somehow - return tested; - }) - .then(makeMutants) - .then(function (ms) { - // TODO -- right here we can serialize all the mutants before running them - // any reason we might want to do this? - // - // this is the separation point between pure data and actually executing - // the tests against mutated source code - return ms; - }) - .then(function (ms) { - return Bluebird.mapSeries(ms, handler); - }) - .then(function (rs) { - if (reporter.onFinish) { - reporter.onFinish(rs); - } - return rs; - }); -}; -function makeMutantHandler(runner, reporter) { - return function handler(m) { - let _before, _result; - return runner.prepare(m) - .then(before => { - _before = before; - return runner.run(m); - }) - .then(result => { - _result = result; - return runner.cleanup(result, _before); - }) - .then(() => { - if (reporter.onResult) { - reporter.onResult(_result); - } - return _result; - }); - }; -} diff --git a/lib/tsc/make-mutants.js b/lib/tsc/make-mutants.js deleted file mode 100644 index e1c48b1..0000000 --- a/lib/tsc/make-mutants.js +++ /dev/null @@ -1,85 +0,0 @@ -"use strict"; -const R = require("ramda"); -const fs = require("fs-extra"); -const exprima = require("esprima"); -const escodegen = require("escodegen"); -const estraverse = require("estraverse"); -const shouldSkip = require("./skippers"); -const { getMutatorsForNode, hasAvailableMutations } = require("./mutators"); -const JS_TYPES = require("./constants/js-types"); -const ESPRIMA_SETTINGS = { - loc: true, - comment: true, -}; -const FS_SETTINGS = { - encoding: "utf8", -}; -module.exports = function makeMutants(match) { - const { source, tests } = match; - const { ast, code } = parse(source); - const paths = getMutationPaths(ast).map(p => p.map(String)); - // we regenerate the source code here to make it easy for diffing - const originalSourceCode = escodegen.generate(ast); - return R.chain(mutationsFromPath, paths); - function mutationsFromPath(path) { - const node = R.path(path, ast); - return getMutatorsForNode(node) - .filter(mutatorFilterFromNode(node)) - .map(function (m) { - // this can be done more elegantly with Ramda lenses, probably - const newNode = m.mutator(node); - const updatedAst = R.assocPath(path, newNode, ast); - const mutatedSourceCode = escodegen.generate(updatedAst); - // both the original source and the mutated source are present here - // to avoid unnecessary extra code generation in mutator prep/teardown, - // and also in reporters - return { - sourceFile: source, - testFiles: tests, - path: path, - mutatorName: m.name, - astAfter: updatedAst, - astBefore: ast, - loc: node.loc, - originalSourceCode: originalSourceCode, - mutatedSourceCode: mutatedSourceCode, - }; - }); - } -}; -function getMutationPaths(ast) { - const mutationPaths = []; - estraverse.traverse(ast, { - enter: function (node) { - const path = this.path(); - if (shouldSkip(node, path)) - return this.skip(); - if (hasAvailableMutations(node)) - mutationPaths.push(path); - }, - }); - return mutationPaths; -} -function mutatorFilterFromNode(node) { - return function (mutator) { - if (mutator.filter == null) - return true; - if (mutator.filter(node)) - return true; - return false; - }; -} -function parse(source) { - const originalSource = fs.readFileSync(source).toString(); - let ast; - try { - ast = esprima.parse(originalSource, ESPRIMA_SETTINGS); - } - catch (err) { - // TODO -- better error handling here - console.log("ERROR PARSING SOURCE FILE", source); - throw err; - } - const code = escodegen.generate(ast); - return { ast: ast, code: code }; -} diff --git a/lib/tsc/matchers/base-comparative.js b/lib/tsc/matchers/base-comparative.js deleted file mode 100644 index abc6849..0000000 --- a/lib/tsc/matchers/base-comparative.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -const path = require("path"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - type: "comparative", - makeMatcher: function (c) { - return function (sourceFile, testFile) { - var sourceName = sourceFile.split(path.join(c.perturbRoot, c.perturbSourceDir)).pop(); - var testName = testFile.split(path.join(c.perturbRoot, c.perturbTestDir)).pop(); - return sourceName === testName; - }; - } -}; diff --git a/lib/tsc/matchers/base-generative.js b/lib/tsc/matchers/base-generative.js deleted file mode 100644 index 56397cf..0000000 --- a/lib/tsc/matchers/base-generative.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -const path = require("path"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - type: "generative", - makeMatcher: function (c) { - return function (sourceFile) { - var sourceRelPath = path.join(c.perturbRoot, c.perturbSourceDir); - var testRelPath = path.join(c.perturbRoot, c.perturbTestDir); - return sourceFile.replace(sourceRelPath, testRelPath); - }; - }, -}; diff --git a/lib/tsc/matchers/contains-comparative.js b/lib/tsc/matchers/contains-comparative.js deleted file mode 100644 index c285a1a..0000000 --- a/lib/tsc/matchers/contains-comparative.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -const path = require("path"); -// matches: -// source: project/lib/dir/car.js -// test: project/test/dir/car-constructor.js -// test: project/test/dir/car-start.js -// test: project/test/dir/car-drive.js -function withoutExt(file) { - return file.slice(0, -1 * path.extname(file).length); -} -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - type: "comparative", - makeMatcher: function (c) { - return function (sourceFile, testFile) { - var sourceName = withoutExt(sourceFile.split(c.perturbSourceDir).pop()); - var testName = withoutExt(testFile.split(c.perturbTestDir).pop()); - return testName.slice(0, sourceName.length) === sourceName; - }; - } -}; diff --git a/lib/tsc/matchers/index.js b/lib/tsc/matchers/index.js deleted file mode 100644 index e579432..0000000 --- a/lib/tsc/matchers/index.js +++ /dev/null @@ -1,43 +0,0 @@ -"use strict"; -const R = require("ramda"); -function runComparative(compare, source, tests) { - return tests.filter(t => compare(source, t)); -} -function runGenerative(generate, source, tests) { - const name = generate(source); - return R.contains(name, tests) ? [name] : []; -} -function getMatcher(c) { - const matcherPlugin = getMatcherPlugin(c.matcher); - const { type } = matcherPlugin; - const matcher = matcherPlugin.makeMatcher(c); - return function findMatches(sources, tests) { - const runMatch = type === "generative" ? runGenerative : runComparative; - return sources.map(function (source) { - return { source: source, tests: runMatch(matcher, source, tests) }; - }); - }; -} -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = getMatcher; -const base_generative_1 = require("./base-generative"); -const base_comparative_1 = require("./base-comparative"); -const contains_comparative_1 = require("./contains-comparative"); -const builtIns = new Map([ - ["base-generative", base_generative_1.default], - ["base-comparative", base_comparative_1.default], - ["contains-comparative", contains_comparative_1.default], -]); -function getMatcherPlugin(name) { - const plugin = builtIns.get(name); - if (plugin) - return plugin; - try { - // TODO -- runtime type check this import - return require(`perturb-matcher-plugin-${name}`); - } - catch (err) { - console.log("Fatal error: unable to resolve MATCHER plugin name", name); - throw err; - } -} diff --git a/lib/tsc/mutators/_void-node.js b/lib/tsc/mutators/_void-node.js deleted file mode 100644 index ca0c2c6..0000000 --- a/lib/tsc/mutators/_void-node.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - type: node_types_1.default.UnaryExpression, - operator: "void", - prefix: true, - argument: { - type: node_types_1.default.Literal, - value: 0, - } -}; diff --git a/lib/tsc/mutators/drop-member-assignment.js b/lib/tsc/mutators/drop-member-assignment.js deleted file mode 100644 index 801d801..0000000 --- a/lib/tsc/mutators/drop-member-assignment.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; -const R = require("ramda"); -const node_types_1 = require("../constants/node-types"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "dropMemberAssignment", - nodeTypes: [node_types_1.default.AssignmentExpression], - filter: function (node) { - return R.path(["left", "type"], node) === node_types_1.default.MemberExpression; - }, - mutator: function (node) { - return node.left; - }, -}; diff --git a/lib/tsc/mutators/drop-node.js b/lib/tsc/mutators/drop-node.js deleted file mode 100644 index f9cd8a8..0000000 --- a/lib/tsc/mutators/drop-node.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -const _void_node_1 = require("./_void-node"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "dropNode", - nodeTypes: [ - node_types_1.default.ContinueStatement, - node_types_1.default.BreakStatement, - ], - mutator: function () { return _void_node_1.default; }, -}; diff --git a/lib/tsc/mutators/drop-operator.js b/lib/tsc/mutators/drop-operator.js deleted file mode 100644 index ebab0c8..0000000 --- a/lib/tsc/mutators/drop-operator.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "dropOperator", - nodeTypes: [ - node_types_1.default.ThrowStatement, - node_types_1.default.UnaryExpression, - ], - mutator: function (node) { - return node.argument; - }, -}; diff --git a/lib/tsc/mutators/drop-return.js b/lib/tsc/mutators/drop-return.js deleted file mode 100644 index 0a42541..0000000 --- a/lib/tsc/mutators/drop-return.js +++ /dev/null @@ -1,18 +0,0 @@ -"use strict"; -const NODE_TYPES = require("../constants/node-types"); -const NODE_ATTRS = require("../constants/node-attrs"); -const voidNode = require("./_void-node"); -const R = require("ramda"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "dropReturn", - nodeTypes: [NODE_TYPES.ReturnStatement], - mutator: function (node) { - if (node.argument == null) - return voidNode; - return { - type: NODE_TYPES.ExpressionStatement, - expression: node.argument, - }; - }, -}; diff --git a/lib/tsc/mutators/drop-void-call.js b/lib/tsc/mutators/drop-void-call.js deleted file mode 100644 index 29be870..0000000 --- a/lib/tsc/mutators/drop-void-call.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -const NODE_TYPES = require("../constants/node-types"); -const R = require("ramda"); -const voidNode = require("./_void-node"); -// drops a function call made for side effects -// (the return value isn't assigned to a variable) -// (will this cause lots of test timeouts due to uncalled callbacks?) -module.exports = { - name: "dropVoidCall", - nodeTypes: [NODE_TYPES.ExpressionStatement], - filter: function (node) { - if (R.path(["expression", "type"], node) !== NODE_TYPES.CallExpression) { - return false; - } - // skip method calls of objects since often called for side effects on `this` - if (R.path(["callee", "type"], node) !== NODE_TYPES.Identifier) { - return false; - } - }, - mutator: function () { return voidNode; }, -}; diff --git a/lib/tsc/mutators/index.js b/lib/tsc/mutators/index.js deleted file mode 100644 index b8a2a16..0000000 --- a/lib/tsc/mutators/index.js +++ /dev/null @@ -1,69 +0,0 @@ -"use strict"; -const R = require("ramda"); -const coreMutators = [ - require("./drop-member-assignment"), - require("./drop-node"), - require("./drop-operator"), - require("./drop-return"), - require("./drop-void-call"), - require("./invert-conditional-test"), - require("./reverse-function-parameters"), - 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-string-literal"), -]; -// temporary stub -- this function will return false for disabled mutators (based on config) -function isMutatorEnabled(m) { - return true; -} -// temporary stub -- plugin mutators will go into this array -// const mutatorPlugins: Array = []; -// creating the internal state of this module should happen in the exported function -// so we can pass in config, which is necessary for filtering out disabled mutators -function makeMutatorIndex(names) { - const additionalMutators = locateMutatorPlugins(names); - const allMutators = coreMutators.concat(additionalMutators).filter(isMutatorEnabled); - // const index : { string: MutatorPlugin[] } = {}; - const index = {}; - allMutators.forEach(function (m) { - m.nodeTypes.forEach(function (type) { - if (index[type] == null) { - index[type] = []; - } - index[type].push(m); - }); - }); - return index; -} -function locateMutatorPlugins(names) { - return names.map(function (name) { - let plugin; - try { - plugin = require(`perturb-plugin-mutator-${name}`); - return plugin; - } - catch (err) { - // any way to recover? other locate strategy? - console.log(`unable to locate -MUTATOR- plugin "${name}" -- fatal error, exiting`); - throw err; - } - }); -} -let mutatorIndex = {}; -function injectPlugins(names) { - mutatorIndex = locateMutatorPlugins(names); -} -exports.injectPlugins = injectPlugins; -function hasAvailableMutations(node) { - return R.has(node.type, mutatorIndex); -} -exports.hasAvailableMutations = hasAvailableMutations; -function getMutatorsForNode(node) { - return R.propOr([], node.type, mutatorIndex); -} -exports.getMutatorsForNode = getMutatorsForNode; -injectPlugins([]); diff --git a/lib/tsc/mutators/invert-conditional-test.js b/lib/tsc/mutators/invert-conditional-test.js deleted file mode 100644 index 9c5683d..0000000 --- a/lib/tsc/mutators/invert-conditional-test.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; -const R = require("ramda"); -const node_types_1 = require("../constants/node-types"); -const node_attrs_1 = require("../constants/node-attrs"); -const test_nodes_1 = require("../constants/test-nodes"); -const BANG = "!"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "invertConditionalTest", - nodeTypes: Object.keys(test_nodes_1.default), - filter: function (node) { - // using get() over has() ensures it isn't null (switch case `default`!) - return Boolean(R.prop(node_attrs_1.default.test, node)); - }, - mutator: function (node) { - return R.assoc(node_attrs_1.default.test, { - type: node_types_1.default.UnaryExpression, - operator: BANG, - argument: node[node_attrs_1.default.test], - }, node); - }, -}; diff --git a/lib/tsc/mutators/reverse-function-parameters.js b/lib/tsc/mutators/reverse-function-parameters.js deleted file mode 100644 index 159e784..0000000 --- a/lib/tsc/mutators/reverse-function-parameters.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -const node_attrs_1 = require("../constants/node-attrs"); -const func_nodes_1 = require("../constants/func-nodes"); -const R = require("ramda"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "reverseFunctionParameters", - nodeTypes: Object.keys(func_nodes_1.default), - filter: function (node) { - return R.path([node_attrs_1.default.params, "length"], node) > 1; - }, - mutator: function (node) { - const params = node.params.slice().reverse(); - return R.assoc(node_attrs_1.default.params, params, node); - }, -}; diff --git a/lib/tsc/mutators/swap-binary-operators.js b/lib/tsc/mutators/swap-binary-operators.js deleted file mode 100644 index e318d05..0000000 --- a/lib/tsc/mutators/swap-binary-operators.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -const binary_operator_swaps_1 = require("../constants/binary-operator-swaps"); -const node_types_1 = require("../constants/node-types"); -const node_attrs_1 = require("../constants/node-attrs"); -const R = require("ramda"); -const NO_SWAP = { - instanceof: "instanceof", - in: "in", -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "swapBinaryOperators", - nodeTypes: [node_types_1.default.BinaryExpression], - filter: function (node) { - const op = R.prop(node_attrs_1.default.operator, node); - return !R.has(op, NO_SWAP); - }, - mutator: function (node) { - var prevOp = node[node_attrs_1.default.operator]; - var newOp = binary_operator_swaps_1.default[prevOp]; - return R.assoc(node_attrs_1.default.operator, newOp, node); - }, -}; diff --git a/lib/tsc/mutators/swap-logical-operators.js b/lib/tsc/mutators/swap-logical-operators.js deleted file mode 100644 index 9f1c01b..0000000 --- a/lib/tsc/mutators/swap-logical-operators.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -const node_attrs_1 = require("../constants/node-attrs"); -const R = require("ramda"); -const AND = "&&"; -const OR = "||"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "swapLogicalOperators", - nodeTypes: [node_types_1.default.LogicalExpression], - mutator: function (node) { - const prevOp = node[node_attrs_1.default.operator]; - const newOp = (prevOp === AND ? OR : AND); - return R.assoc(node_attrs_1.default.operator, newOp, node); - }, -}; diff --git a/lib/tsc/mutators/tweak-array-literal.js b/lib/tsc/mutators/tweak-array-literal.js deleted file mode 100644 index 5bf04df..0000000 --- a/lib/tsc/mutators/tweak-array-literal.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -const R = require("ramda"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - // drops the first declared element in an array literal - // `['a', 'b']` => `['a']` - name: "tweakArrayLiteral", - nodeTypes: [node_types_1.default.ArrayExpression], - filter: function (node) { - return R.path(["elements", "length"], node) !== 0; - }, - mutator: function (node) { - return strategies.dropFirst(node); - }, -}; -const strategies = { - dropFirst: function (node) { - return R.assoc("elements", node.elements.slice(1), node); - }, - dropLast: function (node) { - return R.assoc("elements", node.elements.slice(0, -1), node); - }, - dropRandom: function (node) { - return R.assoc("elements", dropRandom(node.elements), node); - } -}; -function dropRandom(arr) { - var i = getRandomIndex(arr); - var out = arr.slice(); - out.splice(i, 1); - return out; -} -function getRandomIndex(arr) { - return Math.floor(Math.random() * (arr.length)); -} diff --git a/lib/tsc/mutators/tweak-boolean-literal.js b/lib/tsc/mutators/tweak-boolean-literal.js deleted file mode 100644 index f370d61..0000000 --- a/lib/tsc/mutators/tweak-boolean-literal.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -const R = require("ramda"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - name: "tweakBooleanLiteral", - nodeTypes: [node_types_1.default.Literal], - filter: function (node) { - const { value } = node; - return value === true || value === false; - }, - mutator: function (node) { - const { value } = node; - return R.assoc("value", !value, node); - }, -}; diff --git a/lib/tsc/mutators/tweak-number-literal.js b/lib/tsc/mutators/tweak-number-literal.js deleted file mode 100644 index 3e30db0..0000000 --- a/lib/tsc/mutators/tweak-number-literal.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -const R = require("ramda"); -module.exports = { - // adds 1 to any number literal OR replaces 1 with 0 - // var num = 0; => var num = 1; - // var x = 735; => var x = 736; - name: "tweakNumberLiteral", - nodeTypes: [node_types_1.default.Literal], - filter: function (node) { - return typeof node.value === "number"; - }, - mutator: function (node) { - const { value } = node; - return R.assoc("value", (value === 1 ? 0 : value + 1), node); - }, -}; diff --git a/lib/tsc/mutators/tweak-object-literal.js b/lib/tsc/mutators/tweak-object-literal.js deleted file mode 100644 index 6f946a8..0000000 --- a/lib/tsc/mutators/tweak-object-literal.js +++ /dev/null @@ -1,32 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -const R = require("ramda"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - // drops the first declared property in an object literal - // `{prop1: "val1", prop2: "val2"}` => `{prop2: "val2"}` - name: "tweakObjectLiteral", - nodeTypes: [node_types_1.default.ObjectExpression], - filter: function (node) { - return R.path(["properties", "length"], node) !== 0; - }, - mutator: function (node) { - return strategies.dropFirst(node); - }, -}; -// TODO -- DRY this up w/ array literal tweak mutator and string mutator -const strategies = { - dropFirst: function (node) { - return R.assoc("properties", node.properties.slice(1), node); - }, - dropLast: function (node) { - return R.assoc("properties", node.properties.slice(0, -1), node); - }, - dropRandom: function (node) { - return R.assoc("properties", dropRandom(node.properties), node); - } -}; -function dropRandom(s) { - const pos = Math.round(Math.random() * s.length - 1); - return s.slice(0, pos) + s.slice(pos + 1); -} diff --git a/lib/tsc/mutators/tweak-string-literal.js b/lib/tsc/mutators/tweak-string-literal.js deleted file mode 100644 index 9404ba6..0000000 --- a/lib/tsc/mutators/tweak-string-literal.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -const R = require("ramda"); -const EMPTY_REPLACEMENT = "a"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - // drops first character of non-empty string; changes - // empty strings to "a" - // var s = ""; => var s = "a"; - // var name = "nick"; => var name = "ick"; - name: "tweakStringLiteral", - nodeTypes: [node_types_1.default.Literal], - filter: function (node) { - return typeof node.value === "string"; - }, - mutator: function (node) { - const { value } = node; - const replacement = value.length ? value.slice(1) : EMPTY_REPLACEMENT; - return R.assoc("value", replacement, node); - }, -}; diff --git a/lib/tsc/reporters/diff.js b/lib/tsc/reporters/diff.js deleted file mode 100644 index 0aef3b6..0000000 --- a/lib/tsc/reporters/diff.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; -const chalk = require("chalk"); -const { diffLines } = require("diff"); -const changeCase = require("change-case"); -const R = require("ramda"); -module.exports = { - name: "diff", - onResult: function (r) { - console.log(generateReport(r)); - }, - onFinish: function (rs) { - const [killed, alive] = R.partition(r => r.error, rs); - const total = rs.length; - const killCount = killed.length; - const killRate = Number((total / killCount).toFixed(4)) * 100; - console.log(`Total: ${total}. Killed: ${killCount}. Rate: ${killRate}%`); - }, -}; -function generateReport(r) { - const plus = "+ "; - const minus = "- "; - const alive = "#ALIVE: "; - const dead = "#DEAD: "; - const id = identifier(r); - if (r.error) { - return chalk.gray(id); - } - const title = chalk.red.underline(alive + id); - const diff = generateDiff(r); - return [ - title, - report.diff.map(function (entry) { - const color = entry.added ? "green" : "red"; - const sign = entry.added ? plus : minus; - return chalk[color](sign + entry.value.trim()); - }).join("\n"), - ].join("\n"); -} -function generateDiff(r) { - return diffLines(r.sourceCode, r.mutatedSourceCode) - .filter(node => node.added || node.removed); -} -function identifier(r) { - const loc = r.loc.start.line + "," + r.loc.start.column; - return changeCase.sentence(r.mutatorName) + " @" + r.loc; -} diff --git a/lib/tsc/reporters/index.js b/lib/tsc/reporters/index.js deleted file mode 100644 index 526e819..0000000 --- a/lib/tsc/reporters/index.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; -const diffReporter = require("./diff"); -const plugins = new Map([ - ["diff", diffReporter], -]); -function locateReporterPlugins(names) { - names.forEach(function (name) { - let plugin; - try { - plugin = require(`perturb-plugin-runner-${name}`); - plugins.set(name, plugin); - return; - } - catch (err) { - // any way to recover? other locate strategy? - console.log(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - throw err; - } - }); -} -module.exports = function get(name) { - const p = plugins.get(name); - if (p == null) { - throw new Error(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - } - return p; -}; diff --git a/lib/tsc/runners/index.js b/lib/tsc/runners/index.js deleted file mode 100644 index e3ba92f..0000000 --- a/lib/tsc/runners/index.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict"; -const mocha_1 = require("./mocha"); -const plugins = new Map([ - ["mocha", mocha_1.default], -]); -function injectPlugins(names) { - names.forEach(function (name) { - let plugin; - try { - plugin = require(`perturb-plugin-runner-${name}`); - plugins.set(name, plugin); - return; - } - catch (err) { - // any way to recover? other locate strategy? - console.log(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - throw err; - } - }); -} -exports.injectPlugins = injectPlugins; -function get(name) { - const plugin = plugins.get(name); - if (plugin == null) { - throw new Error(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - } - return plugin; -} -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = get; diff --git a/lib/tsc/runners/mocha-child.js b/lib/tsc/runners/mocha-child.js deleted file mode 100644 index e69de29..0000000 diff --git a/lib/tsc/runners/mocha.js b/lib/tsc/runners/mocha.js deleted file mode 100644 index 85a4973..0000000 --- a/lib/tsc/runners/mocha.js +++ /dev/null @@ -1,53 +0,0 @@ -/// -/// -"use strict"; -const fs = require("fs"); -const R = require("ramda"); -const Mocha = require("mocha"); -const doesNotContain = arr => item => !R.contains(item, arr); -const doesNotHave = obj => prop => !R.has(prop, obj); -const delProp = obj => prop => delete obj[prop]; -function mirror(arr) { - const out = {}; - arr.forEach(x => out[x] = x); - return out; -} -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = { - prepare: function (m) { - delete require.cache[m.sourceFile]; - fs.writeFileSync(m.sourceFile, m.mutatedSourceCode); - return Promise.resolve({ - cache: mirror(Object.keys(require.cache)), - listeners: process.listeners("uncaughtException"), - }); - }, - run: function (m) { - return new Promise(function (resolve) { - let failedOn; - const reporter = suite => suite.on("fail", test => failedOn = test); - const mocha = new Mocha({ reporter: reporter, bail: true }); - m.testFiles.forEach(t => mocha.addFile(t)); - try { - mocha.run(() => resolve(failedOn)); - } - catch (err) { - return resolve(err); - } - }) - .then(error => Object.assign({}, m, { error: error })); - }, - cleanup: function (m, before) { - // write the original source code back to it's location - fs.writeFileSync(m.sourceFile, m.originalSourceCode); - // remove danging uncaughtException listeners Mocha didn't clean up - process.listeners("uncaughtException") - .filter(doesNotContain(before.listeners)) - .forEach(f => process.removeListener("uncaughtException", f)); - // remove all modules that were required by this test - Object.keys(require.cache) - .filter(doesNotHave(before.cache)) - .forEach(delProp(require.cache)); - return Promise.resolve(); - } -}; diff --git a/lib/tsc/skippers/index.js b/lib/tsc/skippers/index.js deleted file mode 100644 index 2f1d6cb..0000000 --- a/lib/tsc/skippers/index.js +++ /dev/null @@ -1,37 +0,0 @@ -"use strict"; -const node_types_1 = require("../constants/node-types"); -const js_types_1 = require("../constants/js-types"); -function skipRequire(node) { - const funcNode = node; - return (funcNode.type === node_types_1.default.CallExpression && - funcNode.callee.name === "require" && - funcNode.arguments.length === 1 && - funcNode.arguments[0].type === node_types_1.default.Literal && - typeof funcNode.arguments[0].type === js_types_1.default.str); -} -function skipUseStrict(node) { - const exprNode = node; - return (exprNode.type === node_types_1.default.ExpressionStatement && - exprNode.expression.value === "use strict"); -} -const skippers = [skipRequire, skipUseStrict]; -function injectPlugins(names) { - names.forEach(function (name) { - let plugin; - try { - plugin = require(`perturb-plugin-skipper-${name}`); - skippers.push(plugin); - } - catch (err) { - // any way to recover? other locate strategy? - console.log(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); - throw err; - } - }); -} -exports.injectPlugins = injectPlugins; -function shouldSkip(node, path) { - return skippers.some(f => f(node, path)); -} -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = shouldSkip; diff --git a/lib/tsc/types.js b/lib/tsc/types.js deleted file mode 100644 index 0cbfee2..0000000 --- a/lib/tsc/types.js +++ /dev/null @@ -1,2 +0,0 @@ -/// -"use strict"; diff --git a/lib/types.js b/lib/types.js deleted file mode 100644 index d5c9d84..0000000 --- a/lib/types.js +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/parity.js b/parity.js index f337964..194ee78 100644 --- a/parity.js +++ b/parity.js @@ -1,19 +1,9 @@ require("babel-register"); -const rewrite = require("./_src"); -const original = require("./lib"); +const rewrite = require("./built"); +const original = require("./src"); -// const time = fn => (..._args) => { -// const start = Date.now(); -// const [...args, cb] = _args; -// fn(...args, function (err, result) { -// const duration = Date.now - start; -// console.log(duration); - -// }) -// } - -const run = require("./_src/run"); +const run = require("./src/run"); run(rewrite, "dogfood") .then(function (results) { diff --git a/src/runners/runner.ts b/src/runners/runner.ts deleted file mode 100644 index e69de29..0000000 diff --git a/tsc/constants/func-nodes.ts b/tsc/constants/func-nodes.ts index 1b3e377..275a7d4 100644 --- a/tsc/constants/func-nodes.ts +++ b/tsc/constants/func-nodes.ts @@ -1,7 +1,7 @@ -import NODE_TYPES from "./node-types"; +var S = require("estraverse").Syntax; module.exports = { - [NODE_TYPES.FunctionDeclaration]: NODE_TYPES.FunctionDeclaration, - [NODE_TYPES.FunctionExpression]: NODE_TYPES.FunctionExpression, - [NODE_TYPES.ArrowFunctionExpression]: NODE_TYPES.ArrowFunctionExpression, + [S.FunctionDeclaration]: S.FunctionDeclaration, + [S.FunctionExpression]: S.FunctionExpression, + [S.ArrowFunctionExpression]: S.ArrowFunctionExpression, } \ No newline at end of file diff --git a/tsc/constants/js-types.ts b/tsc/constants/js-types.ts index 15c9740..7ce8aae 100644 --- a/tsc/constants/js-types.ts +++ b/tsc/constants/js-types.ts @@ -1,4 +1,4 @@ -export default { +module.exports = { func: "function", bool: "boolean", str: "string", diff --git a/tsc/constants/messages.ts b/tsc/constants/messages.ts index 44c385c..dcba447 100644 --- a/tsc/constants/messages.ts +++ b/tsc/constants/messages.ts @@ -1,4 +1,4 @@ -export default { +module.exports = { TestsPassed: "Test command exited with `0`. Continuing...", ExecutingTests: "executing `%s` ...", DefaultTest: "npm test", diff --git a/tsc/constants/node-attrs.ts b/tsc/constants/node-attrs.ts index 9e13488..263b293 100644 --- a/tsc/constants/node-attrs.ts +++ b/tsc/constants/node-attrs.ts @@ -1,4 +1,4 @@ -export default { +module.exports = { operator: "operator", elements: "elements", properties: "properties", diff --git a/tsc/constants/node-types.ts b/tsc/constants/node-types.ts index 79e2eb9..aeefbec 100644 --- a/tsc/constants/node-types.ts +++ b/tsc/constants/node-types.ts @@ -1,4 +1,4 @@ -export default { +module.exports = { AssignmentExpression: "AssignmentExpression", ArrayExpression: "ArrayExpression", ArrowFunctionExpression: "ArrowFunctionExpression", diff --git a/tsc/constants/test-nodes.ts b/tsc/constants/test-nodes.ts index e459e28..9d61c60 100644 --- a/tsc/constants/test-nodes.ts +++ b/tsc/constants/test-nodes.ts @@ -1,10 +1,10 @@ -const NODE_TYPES = require("./node-types"); +var S = require("estraverse").Syntax; module.exports = { - [NODE_TYPES.IfStatement]: NODE_TYPES.IfStatement, - [NODE_TYPES.WhileStatement]: NODE_TYPES.WhileStatement, - [NODE_TYPES.DoWhileStatement]: NODE_TYPES.DoWhileStatement, - [NODE_TYPES.ForStatement]: NODE_TYPES.ForStatement, - [NODE_TYPES.ConditionalExpression]: NODE_TYPES.ConditionalExpression, - [NODE_TYPES.SwitchCase]: NODE_TYPES.SwitchCase, + [S.IfStatement]: S.IfStatement, + [S.WhileStatement]: S.WhileStatement, + [S.DoWhileStatement]: S.DoWhileStatement, + [S.ForStatement]: S.ForStatement, + [S.ConditionalExpression]: S.ConditionalExpression, + [S.SwitchCase]: S.SwitchCase, }; diff --git a/tsc/index.ts b/tsc/index.ts index a984e1f..4159599 100644 --- a/tsc/index.ts +++ b/tsc/index.ts @@ -2,12 +2,14 @@ /// /// -const Bluebird = require("bluebird"); const R = require("ramda"); -const getRunner = require("./runners/index"); -const getReporter = require("./reporters/index"); -const getMatcher = require("./matchers/index"); +const Bluebird = require("bluebird"); + +const getRunner = require("./runners"); +const getReporter = require("./reporters"); +const getMatcher = require("./matchers"); const makeMutants = require("./make-mutants"); +const makeConfig = require("./make-config"); const fileSystem = require("./file-system"); import { @@ -24,7 +26,9 @@ function hasTests (m: Match): boolean { return Boolean(R.path(["tests", "length"], m)); } -module.exports = function perturb (cfg: PerturbConfig) { +module.exports = function perturb (_cfg: PerturbConfig) { + const cfg = makeConfig(_cfg); + const {setup, teardown, paths} = fileSystem(cfg); const matcher = getMatcher(cfg); diff --git a/tsc/make-config.ts b/tsc/make-config.ts new file mode 100644 index 0000000..d72c268 --- /dev/null +++ b/tsc/make-config.ts @@ -0,0 +1,36 @@ +const assign = require("object-assign"); +const fs = require("fs"); + +import { PerturbConfig } from "./types"; + +const CONFIG_FILE_NAME = ".perturbrc"; + +const defaultConfig: PerturbConfig = { + projectRoot: process.cwd(), + testDir: "test", + sourceDir: "src", + perturbDir: ".perturb", + + sourceGlob: "/**/*.js", + testGlob: "/**/*.js", + + mutators: [], + skippers: [], + reporter: "diff", + matcher: "contains-comparative", + runner: "mocha", +} + +module.exports = function makeConfig (userConfig = {}): PerturbConfig { + let fileConfig; + + try { + let str = fs.readFileSync(`${process.cwd()}/${CONFIG_FILE_NAME}`).toString(); + fileConfig = JSON.parse(str); + } catch (err) { + console.log("error finding configuration file", err); + fileConfig = {}; + } + + return assign({}, defaultConfig, fileConfig, userConfig); +} diff --git a/tsc/make-mutants.ts b/tsc/make-mutants.ts index 080834c..201f6a8 100644 --- a/tsc/make-mutants.ts +++ b/tsc/make-mutants.ts @@ -6,7 +6,6 @@ const estraverse = require("estraverse"); const shouldSkip = require("./skippers"); const { getMutatorsForNode, hasAvailableMutations } = require("./mutators"); -const JS_TYPES = require("./constants/js-types"); import { Mutant, diff --git a/tsc/matchers/base-comparative.ts b/tsc/matchers/base-comparative.ts index 9b01039..1a3821d 100644 --- a/tsc/matchers/base-comparative.ts +++ b/tsc/matchers/base-comparative.ts @@ -1,4 +1,4 @@ -import * as path from "path" +const path = require("path"); import { ComparativeMatcherPlugin, @@ -10,12 +10,13 @@ import { // source: project/lib/dir/file.js // test: project/test/dir/file.js -export default { +module.exports = { type: "comparative", makeMatcher: function (c: PerturbConfig): ComparativeMatcher { return function (sourceFile: string, testFile: string): boolean { - var sourceName = sourceFile.split(path.join(c.perturbRoot, c.perturbSourceDir)).pop(); - var testName = testFile.split(path.join(c.perturbRoot, c.perturbTestDir)).pop(); + const perturbRoot = path.join(c.projectRoot, c.perturbDir); + const sourceName = sourceFile.split(path.join(perturbRoot, c.sourceDir)).pop(); + const testName = testFile.split(path.join(perturbRoot, c.testDir)).pop(); return sourceName === testName; }; } diff --git a/tsc/matchers/base-generative.ts b/tsc/matchers/base-generative.ts index 12ed73c..fd69c8f 100644 --- a/tsc/matchers/base-generative.ts +++ b/tsc/matchers/base-generative.ts @@ -1,4 +1,4 @@ -import * as path from "path" +const path = require("path"); import { GenerativeMatcherPlugin, @@ -9,13 +9,14 @@ import { // input: project/lib/dir/car.js // output: project/test/dir/car.js -export default { +module.exports = { type: "generative", makeMatcher: function(c: PerturbConfig): GenerativeMatcher { return function(sourceFile: string) { - var sourceRelPath = path.join(c.perturbRoot, c.perturbSourceDir); - var testRelPath = path.join(c.perturbRoot, c.perturbTestDir); - return sourceFile.replace(sourceRelPath, testRelPath); + const perturbRoot = path.join(c.projectRoot, c.perturbDir); + const perturbSourceDir = path.join(perturbRoot, c.sourceDir); + const perturbTestDir = path.join(perturbRoot, c.testDir); + return sourceFile.replace(perturbSourceDir, perturbTestDir); }; }, }; diff --git a/tsc/matchers/contains-comparative.ts b/tsc/matchers/contains-comparative.ts index 9d9c88c..7846d78 100644 --- a/tsc/matchers/contains-comparative.ts +++ b/tsc/matchers/contains-comparative.ts @@ -1,4 +1,4 @@ -import * as path from "path" +const path = require("path"); import { ComparativeMatcherPlugin, @@ -16,12 +16,16 @@ function withoutExt (file) { return file.slice(0, -1 * path.extname(file).length); } -export default { +module.exports = { type: "comparative", makeMatcher: function(c: PerturbConfig): ComparativeMatcher { return function(sourceFile: string, testFile: string): boolean { - var sourceName = withoutExt(sourceFile.split(c.perturbSourceDir).pop()); - var testName = withoutExt(testFile.split(c.perturbTestDir).pop()); + const perturbRoot = path.join(c.projectRoot, c.perturbDir); + const perturbSourceDir = path.join(perturbRoot, c.sourceDir); + const perturbTestDir = path.join(perturbRoot, c.testDir); + + var sourceName = withoutExt(sourceFile.split(perturbSourceDir).pop()); + var testName = withoutExt(testFile.split(perturbTestDir).pop()); return testName.slice(0, sourceName.length) === sourceName; }; } diff --git a/tsc/matchers/index.ts b/tsc/matchers/index.ts index 908fb6e..be9b920 100644 --- a/tsc/matchers/index.ts +++ b/tsc/matchers/index.ts @@ -29,7 +29,7 @@ function runGenerative ( return R.contains(name, tests) ? [name] : []; } -export default function getMatcher (c: PerturbConfig) { +module.exports = function getMatcher (c: PerturbConfig) { const matcherPlugin = getMatcherPlugin(c.matcher); const {type} = matcherPlugin; @@ -43,9 +43,9 @@ export default function getMatcher (c: PerturbConfig) { } } -import baseGenerative from "./base-generative"; -import baseComparative from "./base-comparative"; -import containsComparative from "./contains-comparative"; +const baseGenerative = require("./base-generative"); +const baseComparative = require("./base-comparative"); +const containsComparative = require("./contains-comparative"); const builtIns = new Map([ ["base-generative", baseGenerative], diff --git a/tsc/mutators/_void-node.ts b/tsc/mutators/_void-node.ts index fcef429..02956aa 100644 --- a/tsc/mutators/_void-node.ts +++ b/tsc/mutators/_void-node.ts @@ -1,11 +1,11 @@ -import NODE_TYPES from "../constants/node-types"; +var S = require("estraverse").Syntax; -export default { - type: NODE_TYPES.UnaryExpression, +module.exports = { + type: S.UnaryExpression, operator: "void", prefix: true, argument: { - type: NODE_TYPES.Literal, + type: S.Literal, value: 0, } }; diff --git a/tsc/mutators/drop-member-assignment.ts b/tsc/mutators/drop-member-assignment.ts index b6301b5..c21deb4 100644 --- a/tsc/mutators/drop-member-assignment.ts +++ b/tsc/mutators/drop-member-assignment.ts @@ -1,14 +1,15 @@ -import * as R from "ramda"; +const R = require("ramda"); +const { Syntax } = require("estraverse"); + import { MutatorPlugin } from "../types"; -import NODE_TYPES from "../constants/node-types"; // drops a member assignment // `obj.prop = 'value';` => `obj.prop;` -export default { +module.exports = { name: "dropMemberAssignment", - nodeTypes: [NODE_TYPES.AssignmentExpression], + nodeTypes: [Syntax.AssignmentExpression], filter: function (node) { - return R.path(["left", "type"], node) === NODE_TYPES.MemberExpression + return R.path(["left", "type"], node) === Syntax.MemberExpression }, mutator: function (node) { return (node).left; diff --git a/tsc/mutators/drop-node.ts b/tsc/mutators/drop-node.ts index 20ea04c..9474c28 100644 --- a/tsc/mutators/drop-node.ts +++ b/tsc/mutators/drop-node.ts @@ -1,14 +1,13 @@ -"use strict"; +const { Syntax } = require("estraverse"); +const voidNode = require("./_void-node"); -import NODE_TYPES from "../constants/node-types"; -import voidNode from "./_void-node"; import { MutatorPlugin } from "../types"; -export default { +module.exports = { name: "dropNode", nodeTypes: [ - NODE_TYPES.ContinueStatement, - NODE_TYPES.BreakStatement, + Syntax.ContinueStatement, + Syntax.BreakStatement, ], mutator: function () { return voidNode }, }; diff --git a/tsc/mutators/drop-operator.ts b/tsc/mutators/drop-operator.ts index 7d12fd0..bbfd1cb 100644 --- a/tsc/mutators/drop-operator.ts +++ b/tsc/mutators/drop-operator.ts @@ -1,8 +1,7 @@ -"use strict"; +const { Syntax } = require("estraverse"); -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; import { MutatorPlugin } from "../types"; + // throw new Error(); => new Error(); // delete obj.x; => obj.x; // typeof obj; => obj; @@ -15,8 +14,8 @@ interface ArgumentedNode extends ESTree.Node { export default { name: "dropOperator", nodeTypes: [ - NODE_TYPES.ThrowStatement, - NODE_TYPES.UnaryExpression, + Syntax.ThrowStatement, + Syntax.UnaryExpression, ], mutator: function (node) { return (node).argument; diff --git a/tsc/mutators/drop-return.ts b/tsc/mutators/drop-return.ts index 0c9c844..d98b607 100644 --- a/tsc/mutators/drop-return.ts +++ b/tsc/mutators/drop-return.ts @@ -1,5 +1,4 @@ -const NODE_TYPES = require("../constants/node-types"); -const NODE_ATTRS = require("../constants/node-attrs"); +const { Syntax } = require("estraverse") const voidNode = require("./_void-node"); const R = require("ramda"); @@ -14,13 +13,13 @@ interface MaybeArgumentedNode extends ESTree.Node { argument?: any } -export default { +module.exports = { name: "dropReturn", - nodeTypes: [NODE_TYPES.ReturnStatement], + nodeTypes: [Syntax.ReturnStatement], mutator: function (node: MaybeArgumentedNode) { if (node.argument == null) return voidNode; return { - type: NODE_TYPES.ExpressionStatement, + type: Syntax.ExpressionStatement, expression: node.argument, } }, diff --git a/tsc/mutators/drop-void-call.ts b/tsc/mutators/drop-void-call.ts index 7203804..f46ef0f 100644 --- a/tsc/mutators/drop-void-call.ts +++ b/tsc/mutators/drop-void-call.ts @@ -1,4 +1,4 @@ -const NODE_TYPES = require("../constants/node-types"); +const { Syntax } = require("estraverse"); const R = require("ramda"); const voidNode = require("./_void-node"); @@ -9,14 +9,14 @@ import { MutatorPlugin } from "../types"; // (will this cause lots of test timeouts due to uncalled callbacks?) module.exports = { name: "dropVoidCall", - nodeTypes: [NODE_TYPES.ExpressionStatement], + nodeTypes: [Syntax.ExpressionStatement], filter: function (node) { - if (R.path(["expression", "type"], node) !== NODE_TYPES.CallExpression) { + if (R.path(["expression", "type"], node) !== Syntax.CallExpression) { return false; } // skip method calls of objects since often called for side effects on `this` - if (R.path(["callee", "type"], node) !== NODE_TYPES.Identifier) { + if (R.path(["callee", "type"], node) !== Syntax.Identifier) { return false; } }, diff --git a/tsc/mutators/index.ts b/tsc/mutators/index.ts index 7b1a04a..45794f9 100644 --- a/tsc/mutators/index.ts +++ b/tsc/mutators/index.ts @@ -65,16 +65,16 @@ function locateMutatorPlugins (names: string[]): MutatorPlugin[] { let mutatorIndex = {} -export function injectPlugins (names: string[]) { +exports.injectPlugins = function (names: string[]) { mutatorIndex = locateMutatorPlugins(names); } -export function hasAvailableMutations (node: ESTree.Node): boolean { +exports.hasAvailableMutations = function (node: ESTree.Node): boolean { return R.has(node.type, mutatorIndex); } -export function getMutatorsForNode (node: ESTree.Node): MutatorPlugin[] { +exports.getMutatorsForNode = function (node: ESTree.Node): MutatorPlugin[] { return R.propOr([], node.type, mutatorIndex); } -injectPlugins([]); +exports.injectPlugins([]); diff --git a/tsc/mutators/invert-conditional-test.ts b/tsc/mutators/invert-conditional-test.ts index 7ed9217..69da0f5 100644 --- a/tsc/mutators/invert-conditional-test.ts +++ b/tsc/mutators/invert-conditional-test.ts @@ -1,27 +1,32 @@ -import * as R from "ramda"; -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; -import TEST_NODES from "../constants/test-nodes"; +const R = require("ramda"); +const { Syntax } = require("estraverse"); +const TEST_NODES = require("../constants/test-nodes"); + import { MutatorPlugin } from "../types"; const BANG = "!"; +interface TestNode extends ESTree.Node { + test: ESTree.Node +} + // inverts a conditional test with a bang // `if (isReady) {}` => `if (!(isReady)) {}` // `while (arr.length) {} => `while(!(arr.length)) {}` // `for (; i < 10; i++) {}` => `for(; (!(i < 10)); i++)` -export default { +module.exports = { name: "invertConditionalTest", nodeTypes: Object.keys(TEST_NODES), filter: function (node) { // using get() over has() ensures it isn't null (switch case `default`!) - return Boolean(R.prop(NODE_ATTRS.test, node)); + return Boolean(R.prop("test", node)); }, mutator: function (node) { - return R.assoc(NODE_ATTRS.test, { - type: NODE_TYPES.UnaryExpression, + const testNode = node; + return R.assoc("test", { + type: Syntax.UnaryExpression, operator: BANG, - argument: node[NODE_ATTRS.test], - }, node); + argument: testNode.test, + }, testNode); }, }; diff --git a/tsc/mutators/reverse-function-parameters.ts b/tsc/mutators/reverse-function-parameters.ts index 0756901..be2765d 100644 --- a/tsc/mutators/reverse-function-parameters.ts +++ b/tsc/mutators/reverse-function-parameters.ts @@ -1,6 +1,6 @@ -import NODE_ATTRS from "../constants/node-attrs"; -import FUNC_NODES from "../constants/func-nodes"; -import * as R from "ramda"; +const R = require("ramda"); +const FUNC_NODES = require("../constants/func-nodes"); + import { MutatorPlugin } from "../types"; interface FunctionNode extends ESTree.Node { @@ -13,10 +13,10 @@ export default { name: "reverseFunctionParameters", nodeTypes: Object.keys(FUNC_NODES), filter: function (node) { - return R.path([NODE_ATTRS.params, "length"], node) > 1; + return R.path(["params", "length"], node) > 1; }, mutator: function (node) { const params = (node).params.slice().reverse(); - return R.assoc(NODE_ATTRS.params, params, node); + return R.assoc("params", params, node); }, }; diff --git a/tsc/mutators/swap-binary-operators.ts b/tsc/mutators/swap-binary-operators.ts index 44b5555..9866655 100644 --- a/tsc/mutators/swap-binary-operators.ts +++ b/tsc/mutators/swap-binary-operators.ts @@ -1,7 +1,9 @@ -import BINARY_OPERATOR_SWAPS from "../constants/binary-operator-swaps"; -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; -import * as R from "ramda"; +const BINARY_OPERATOR_SWAPS = require("../constants/binary-operator-swaps"); +const NODE_TYPES = require("../constants/node-types"); +const NODE_ATTRS = require("../constants/node-attrs"); +const R = require("ramda"); +const { Syntax } = require("estraverse"); + import { MutatorPlugin } from "../types"; const NO_SWAP = { @@ -16,14 +18,14 @@ const NO_SWAP = { // `var area = w * h;` => `var area = w / h;` export default { name: "swapBinaryOperators", - nodeTypes: [NODE_TYPES.BinaryExpression], + nodeTypes: [Syntax.BinaryExpression], filter: function (node) { - const op = R.prop(NODE_ATTRS.operator, node); + const op = R.prop("operator", node); return !R.has(op, NO_SWAP); }, mutator: function (node) { - var prevOp = node[NODE_ATTRS.operator]; - var newOp = BINARY_OPERATOR_SWAPS[prevOp]; - return R.assoc(NODE_ATTRS.operator, newOp, node); + const prevOp = R.prop("operator", node); + const newOp = BINARY_OPERATOR_SWAPS[prevOp]; + return R.assoc("operator", newOp, node); }, }; diff --git a/tsc/mutators/swap-logical-operators.ts b/tsc/mutators/swap-logical-operators.ts index c4741bb..c3622db 100644 --- a/tsc/mutators/swap-logical-operators.ts +++ b/tsc/mutators/swap-logical-operators.ts @@ -1,8 +1,6 @@ -"use strict"; +const { Syntax } = require("estraverse"); +const R = require("ramda"); -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; -import * as R from "ramda"; import { MutatorPlugin } from "../types"; const AND = "&&"; @@ -11,12 +9,12 @@ const OR = "||"; // swaps && for || and vice versa // `if (x && y)` => `if (x || y)` // `while (f() || g())` => `while(f() && g())` -export default { +module.exports = { name: "swapLogicalOperators", - nodeTypes: [NODE_TYPES.LogicalExpression], + nodeTypes: [Syntax.LogicalExpression], mutator: function (node) { - const prevOp = node[NODE_ATTRS.operator] + const prevOp = R.prop("operator", node); const newOp = (prevOp === AND ? OR : AND); - return R.assoc(NODE_ATTRS.operator, newOp, node); + return R.assoc("operator", newOp, node); }, }; diff --git a/tsc/mutators/tweak-array-literal.ts b/tsc/mutators/tweak-array-literal.ts index 547480d..aa689a4 100644 --- a/tsc/mutators/tweak-array-literal.ts +++ b/tsc/mutators/tweak-array-literal.ts @@ -1,15 +1,13 @@ -"use strict"; +const R = require("ramda"); +const { Syntax } = require("estraverse"); -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; -import * as R from "ramda"; import { MutatorPlugin } from "../types"; -export default { +module.exports = { // drops the first declared element in an array literal // `['a', 'b']` => `['a']` name: "tweakArrayLiteral", - nodeTypes: [NODE_TYPES.ArrayExpression], + nodeTypes: [Syntax.ArrayExpression], filter: function (node) { return R.path(["elements", "length"], node) !== 0; }, diff --git a/tsc/mutators/tweak-boolean-literal.ts b/tsc/mutators/tweak-boolean-literal.ts index 8c404ea..8d97c59 100644 --- a/tsc/mutators/tweak-boolean-literal.ts +++ b/tsc/mutators/tweak-boolean-literal.ts @@ -1,12 +1,12 @@ -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; -import * as R from "ramda"; +const { Syntax } = require("estraverse"); +const R = require("ramda"); + import { MutatorPlugin } from "../types"; // `var isOk = true` => `var isOk = false` export default { name: "tweakBooleanLiteral", - nodeTypes: [NODE_TYPES.Literal], + nodeTypes: [Syntax.Literal], filter: function (node) { const {value} = (node); return value === true || value === false; diff --git a/tsc/mutators/tweak-number-literal.ts b/tsc/mutators/tweak-number-literal.ts index a856666..f166058 100644 --- a/tsc/mutators/tweak-number-literal.ts +++ b/tsc/mutators/tweak-number-literal.ts @@ -1,6 +1,6 @@ -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; -import * as R from "ramda"; +const { Syntax } = require("estraverse"); +const R = require("ramda"); + import { MutatorPlugin } from "../types"; interface NumberLiteral extends ESTree.Literal { @@ -12,7 +12,7 @@ module.exports = { // var num = 0; => var num = 1; // var x = 735; => var x = 736; name: "tweakNumberLiteral", - nodeTypes: [NODE_TYPES.Literal], + nodeTypes: [Syntax.Literal], filter: function (node) { return typeof node.value === "number"; }, diff --git a/tsc/mutators/tweak-object-literal.ts b/tsc/mutators/tweak-object-literal.ts index 110ce43..20854a1 100644 --- a/tsc/mutators/tweak-object-literal.ts +++ b/tsc/mutators/tweak-object-literal.ts @@ -1,13 +1,13 @@ -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; -import * as R from "ramda"; +const { Syntax } = require("estraverse"); +const R = require("ramda"); + import { MutatorPlugin } from "../types"; export default { // drops the first declared property in an object literal // `{prop1: "val1", prop2: "val2"}` => `{prop2: "val2"}` name: "tweakObjectLiteral", - nodeTypes: [NODE_TYPES.ObjectExpression], + nodeTypes: [Syntax.ObjectExpression], filter: function (node) { return R.path(["properties", "length"], node) !== 0; }, diff --git a/tsc/mutators/tweak-string-literal.ts b/tsc/mutators/tweak-string-literal.ts index b7f0977..b9a892b 100644 --- a/tsc/mutators/tweak-string-literal.ts +++ b/tsc/mutators/tweak-string-literal.ts @@ -1,6 +1,6 @@ -import NODE_TYPES from "../constants/node-types"; -import NODE_ATTRS from "../constants/node-attrs"; -import * as R from "ramda"; +const { Syntax } = require("estraverse"); +const R = require("ramda"); + import { MutatorPlugin } from "../types"; interface StringLiteral extends ESTree.Node { @@ -15,7 +15,7 @@ export default { // var s = ""; => var s = "a"; // var name = "nick"; => var name = "ick"; name: "tweakStringLiteral", - nodeTypes: [NODE_TYPES.Literal], + nodeTypes: [Syntax.Literal], filter: function (node) { return typeof (node).value === "string"; }, diff --git a/tsc/reporters/diff.ts b/tsc/reporters/diff.ts index 12bf825..eb36132 100644 --- a/tsc/reporters/diff.ts +++ b/tsc/reporters/diff.ts @@ -36,7 +36,7 @@ function generateReport (r: RunnerResult): string { return [ title, - report.diff.map(function (entry) { + diff.map(function (entry) { const color = entry.added ? "green" : "red"; const sign = entry.added ? plus : minus; return chalk[color](sign + entry.value.trim()); @@ -45,7 +45,7 @@ function generateReport (r: RunnerResult): string { } function generateDiff (r: RunnerResult) { - return diffLines(r.sourceCode, r.mutatedSourceCode) + return diffLines(r.originalSourceCode, r.mutatedSourceCode) .filter(node => node.added || node.removed); } diff --git a/tsc/runners/index.ts b/tsc/runners/index.ts index c6f3efd..658bab1 100644 --- a/tsc/runners/index.ts +++ b/tsc/runners/index.ts @@ -1,11 +1,12 @@ +const mochaRunner = require("./mocha"); + import { RunnerPlugin } from "../types"; -import mochaRunner from "./mocha"; const plugins = new Map([ [ "mocha", mochaRunner ], ]); -export function injectPlugins (names) { +function injectPlugins (names) { names.forEach(function (name) { let plugin: RunnerPlugin; try { @@ -20,7 +21,7 @@ export function injectPlugins (names) { }); } -export default function get (name: string): RunnerPlugin { +module.exports = function get (name: string): RunnerPlugin { const plugin = plugins.get(name); if (plugin == null) { throw new Error(`unable to locate -RUNNER- plugin "${name}" -- fatal error, exiting`); diff --git a/tsc/runners/mocha-child.ts b/tsc/runners/mocha-child.ts deleted file mode 100644 index e69de29..0000000 diff --git a/tsc/runners/mocha.ts b/tsc/runners/mocha.ts index 67d1f0d..76d1e2d 100644 --- a/tsc/runners/mocha.ts +++ b/tsc/runners/mocha.ts @@ -2,10 +2,10 @@ /// -import * as fs from "fs"; -import * as R from "ramda"; -import * as Bluebird from "bluebird"; -import * as Mocha from "mocha"; +const fs = require("fs"); +const R = require("ramda"); +const Bluebird = require("bluebird"); +const Mocha = require("mocha"); import { RunnerPlugin, @@ -23,7 +23,7 @@ function mirror (arr: string[]) { return out; } -export default { +module.exports = { prepare: function (m: Mutant): Promise { delete require.cache[m.sourceFile]; diff --git a/tsc/skippers/index.ts b/tsc/skippers/index.ts index a5673eb..d24149e 100644 --- a/tsc/skippers/index.ts +++ b/tsc/skippers/index.ts @@ -1,35 +1,30 @@ -import some from "lodash.some"; -import last from "lodash.last"; - -import NODE_TYPES from "../constants/node-types"; -import JS_TYPES from "../constants/js-types"; +const R = require("ramda"); +const { Syntax } = require("estraverse"); import { Skipper } from "../types"; function skipRequire (node: ESTree.Node): boolean { const funcNode = node; return ( - funcNode.type === NODE_TYPES.CallExpression && - funcNode.callee.name === "require" && - funcNode.arguments.length === 1 && - funcNode.arguments[0].type === NODE_TYPES.Literal && - typeof funcNode.arguments[0].type === JS_TYPES.str + R.prop("type", node) === Syntax.CallExpression && + R.path(["callee", "name"], node) === "require" && + R.path(["arguments", "length"], node) === 1 && + R.path(["arguments", 0, "type"], node) === Syntax.Literal && + typeof R.path(["arguments", 0, "type"], node) === "string" ); } function skipUseStrict (node: ESTree.Node): boolean { const exprNode = node; return ( - exprNode.type === NODE_TYPES.ExpressionStatement && - exprNode.expression.value === "use strict" + exprNode.type === Syntax.ExpressionStatement && + R.path(["expression", "value"], exprNode) === "use strict" ); } const skippers: Skipper[] = [ skipRequire, skipUseStrict ]; -type Path = Array - -export function injectPlugins (names) { +function injectPlugins (names) { names.forEach(function (name) { let plugin: Skipper; try { @@ -43,6 +38,6 @@ export function injectPlugins (names) { }); } -export default function shouldSkip (node: ESTree.Node, path: Path): boolean { +module.exports = function shouldSkip (node: ESTree.Node, path: string[]): boolean { return skippers.some(f => f(node, path)); } diff --git a/tsc/types.ts b/tsc/types.ts index 06372b6..03887ba 100644 --- a/tsc/types.ts +++ b/tsc/types.ts @@ -97,11 +97,13 @@ export interface PerturbConfig { matcher: string; // name of matcher plugin runner: string; // name of runner plugin - originalSourceDir: string; - originalTestDir: string; - perturbRoot: string; - perturbSourceDir: string; - perturbTestDir: string; + projectRoot: string; + perturbDir: string; + sourceDir: string; + testDir: string; + + sourceGlob: string; + testGlob: string; } export interface Mutant { diff --git a/tsconfig.json b/tsconfig.json index 5b1d978..f30e1ce 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,10 +2,10 @@ "compilerOptions": { "noImplicitAny": false, "sourceMap": false, - "target": "es6", - "module": "CommonJS", - "outDir": "lib", - "moduleResolution": "node" + "module": "commonjs", + "target": "ES6", + "outDir": "built", + "rootDir": "tsc" }, "exclude": [ "node_modules"