Skip to content

Commit

Permalink
Merged with master after the new test runner was implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
simondel committed Mar 18, 2016
2 parents c911a7f + 67d6946 commit 94b5a6f
Show file tree
Hide file tree
Showing 28 changed files with 111 additions and 1,041 deletions.
25 changes: 25 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,31 @@
"externalConsole": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/dist"
},
{
"name": "Run stryker example",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/dist/src/Stryker.js",
"preLaunchTask": "ts",
"stopOnEntry": false,
"args": [
"-s",
"test/sampleProject/src/Add.js,test/sampleProject/src/Circle.js",
"-t",
"test/sampleProject/test/AddSpec.js,test/sampleProject/test/CircleSpec.js"
],
"cwd": "${workspaceRoot}/.",
"runtimeExecutable": null,
"runtimeArgs": [
"--nolazy"
],
"env": {
"NODE_ENV": "development"
},
"externalConsole": false,
"sourceMaps": true,
"outDir": "${workspaceRoot}/dist"
}
]
}
2 changes: 0 additions & 2 deletions src/AbstractSyntaxTreeNode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict';

import BaseTestRunner from './testrunners/BaseTestRunner';

/**
* Represents a node on the abstract syntax tree.
* @constructor
Expand Down
7 changes: 1 addition & 6 deletions src/Mutant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default class Mutant {
private scopedTestsById: RunResult[] = [];
private _scopedTestIds: number[] = [];
private _mutatedFilename: string;
private _specsRan: string[];
public specsRan: string[] = [];
private _timeSpentScopedTests = 0;

get scopedTestIds() : number[] {
Expand All @@ -68,14 +68,9 @@ export default class Mutant {
return this._timeSpentScopedTests;
}

get specsRan (){
return this._specsRan;
}

public addRunResultForTest(index: number, runResult: RunResult){
this._scopedTestIds.push(index);
this._timeSpentScopedTests += runResult.timeSpent;
runResult.specNames.forEach(specName => this._specsRan.push(specName));
this.scopedTestsById[index] = runResult;
}

Expand Down
93 changes: 44 additions & 49 deletions src/Stryker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,41 @@ import FileUtils from './utils/FileUtils';
import Mutator from './Mutator';
import Mutant from './Mutant';
import ReporterFactory from './ReporterFactory';
import TestRunnerFactory from './TestRunnerFactory';
import BaseReporter from './reporters/BaseReporter';
import BaseTestRunner from './testrunners/BaseTestRunner';
import TestFile from './TestFile';
import TestResult from './TestResult';
import StrykerOptions from './StrykerOptions';

import {StrykerOptions} from './api/core';
import TestRunnerOrchestrator from './TestRunnerOrchestrator';
import './jasmine_test_selector/JasmineTestSelector';
import './karma-runner/KarmaTestRunner';
import {RunResult, TestResult} from './api/test_runner';
import MutantRunResultMatcher from './MutantRunResultMatcher';

export default class Stryker {

fileUtils = new FileUtils();
reporter: BaseReporter;
testRunner: BaseTestRunner;
private testRunnerOrchestrator: TestRunnerOrchestrator;

/**
* The Stryker mutation tester.
* @constructor
* @param {String[]} sourceFiles - The list of source files which should be mutated.
* @param {String[]} testFiles - The list of test files.
* @param {String[]} otherFiles - The list of test files.
* @param {Object} [options] - Optional options.
*/
constructor(private sourceFiles: string[], private otherFiles: string[], options?: StrykerOptions) {
this.fileUtils.normalize(sourceFiles);
this.fileUtils.normalize(otherFiles);
this.fileUtils.createBaseTempFolder();


options = options || {};
options.testFramework = 'jasmine';
options.testRunner = 'karma';
options.port = 1234;
this.testRunnerOrchestrator = new TestRunnerOrchestrator(options, sourceFiles, otherFiles);

var reporterFactory = new ReporterFactory();
var testRunnerFactory = new TestRunnerFactory();

this.reporter = reporterFactory.getReporter('console');
this.testRunner = testRunnerFactory.getTestRunner('jasmine', options);
}

/**
Expand All @@ -44,61 +49,50 @@ export default class Stryker {
*/
runMutationTest(cb: () => void) {
console.log('INFO: Running initial test run');
this.testRunner.testAndCollectCoverage(this.sourceFiles, this.otherFiles, (testResults: TestResult[]) => {
let unsuccessfulTests = testResults.filter((result: TestResult) => {
return !result.allTestsSuccessful;
this.testRunnerOrchestrator.recordCoverage().then((runResults) => {
let unsuccessfulTests = runResults.filter((runResult: RunResult) => {
return !(runResult.failed === 0 && runResult.result === TestResult.Complete);
});
if (unsuccessfulTests.length === 0) {
console.log('INFO: Initial test run succeeded');
var mutator = new Mutator();
var mutants = mutator.mutate(this.sourceFiles);
console.log('INFO: ' + mutants.length + ' Mutants generated');

var testFilesToRemove: TestFile[] = [];
_.forEach(testResults, (testResult: TestResult) => {
testFilesToRemove = testFilesToRemove.concat(testResult.testFiles);
let mutator = new Mutator();
let mutants = mutator.mutate(this.sourceFiles);
console.log('INFO: ' + mutants.length + ' Mutants generated');

let mutantRunResultMatcher = new MutantRunResultMatcher(mutants, runResults);
mutantRunResultMatcher.matchWithMutants();

this.testRunnerOrchestrator.runMutations(mutants, this.reporter).then(() => {
this.reporter.allMutantsTested(mutants);
console.log('Done!');
cb();
});

this.testRunner.testMutants(mutants, this.sourceFiles, testResults,
(mutant: Mutant) => {
// Call the reporter like this instead of passing the function directly to ensure that `this` in the reporter is still the reporter.
this.reporter.mutantTested(mutant);
},
(mutants: Mutant[]) => {
this.reporter.allMutantsTested(mutants);

_.forEach(testFilesToRemove, (testFile: TestFile) => {
testFile.remove();
});
this.fileUtils.removeBaseTempFolder();

if (cb) {
cb();
}
});
} else {
this.logFailedTests(unsuccessfulTests);
}
});

}

/**
* Logs all (unique) tests in the array of TestResults with the message that they have failed.
* @param unsuccessfulTests - The TestResults which contain tests which have failed.
* Looks through a list of RunResults to see if all tests have passed.
* @function
* @param {RunResult[]} runResults - The list of RunResults.
* @returns {Boolean} True if all tests passed.
*/
private logFailedTests(unsuccessfulTests: TestResult[]): void {
let testFilenames: string[] = [];
unsuccessfulTests.forEach(testResult => {
testResult.testFiles.forEach(testFile => {
if (testFilenames.indexOf(testFile.name) < 0) {
testFilenames.push(testFile.name);
private logFailedTests(unsuccessfulTests: RunResult[]): void {
let specNames: string[] = [];
unsuccessfulTests.forEach(runResult => {
runResult.specNames.forEach(specName => {
if (specNames.indexOf(specName) < 0) {
specNames.push(specName);
}
});
});

console.log('ERROR: One or more tests failed in the inial test run:');
testFilenames.forEach(filename => {
specNames.forEach(filename => {
console.log('\t', filename);
});
}
Expand All @@ -107,14 +101,15 @@ export default class Stryker {
function list(val: string) {
return val.split(',');
}
//TODO: Implement the new Stryker options
program
.usage('-s <items> -t <items> [other options]')
.option('-s, --src <items>', 'A list of source files. Example: a.js,b.js', list)
.option('-o, --other-files <items>', 'A list of other files, such as test files or library files. Example: a.js,b.js', list)
.parse(process.argv);

if (program.src && program.tests) {
var stryker = new Stryker(program.src, program.tests);
if (program.src && program.otherFiles) {
var stryker = new Stryker(program.src, program.otherFiles);
stryker.runMutationTest(function() { });
}
})();
26 changes: 0 additions & 26 deletions src/StrykerOptions.ts

This file was deleted.

49 changes: 0 additions & 49 deletions src/TestRunnerFactory.ts

This file was deleted.

16 changes: 11 additions & 5 deletions src/TestRunnerOrchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ export default class TestRunnerOrchestrator {
}

recordCoverage(): Promise<RunResult[]> {
let testSelector = TestSelectorFactory.instance().create(this.options.testFrameork, { options: this.options });
let testSelector = TestSelectorFactory.instance().create(this.options.testFramework, { options: this.options });
let testRunner = IsolatedTestRunnerAdapterFactory.create(this.createTestRunSettings(this.sourceFiles, testSelector, this.options.port, true));
return this.runSingleTestsRecursive(testSelector, testRunner, [], 0);
return this.runSingleTestsRecursive(testSelector, testRunner, [], 0).then((testResults) => {
testRunner.dispose();
return testResults;
});
}

runMutations(mutants: Mutant[], reporter: BaseReporter): Promise<void> {
Expand All @@ -52,7 +55,9 @@ export default class TestRunnerOrchestrator {
.then(() => testRunners.push(nextRunner)); // mark the runner as available again
}
}
return new PromisePool(promiseProducer, testRunners.length).start();
return new PromisePool(promiseProducer, testRunners.length)
.start()
.then(() => testRunners.forEach( testRunner => testRunner.runnerAdapter.dispose()));
});
}

Expand Down Expand Up @@ -82,7 +87,7 @@ export default class TestRunnerOrchestrator {
testSelector.select([currentTestIndex])
.then(() => testRunner.run({ timeout: 10000 }))
.then(runResult => {
if (runResult.result === TestResult.Complete && (runResult.succeeded > 0 || runResult.failed > 0)) {
if (runResult.succeeded > 0 || runResult.failed > 0) {
runResults[currentTestIndex] = runResult;
resolve(this.runSingleTestsRecursive(testSelector, testRunner, runResults, currentTestIndex + 1));
} else {
Expand All @@ -99,6 +104,7 @@ export default class TestRunnerOrchestrator {
let cpuCount = os.cpus().length;
let testRunnerMetadatas: TestRunnerMetadata[] = [];
let allPromises: Promise<any>[] = [];
console.log(`INFO: Creating ${cpuCount} test runners (based on cpu count)`);
for (let i = 0; i < cpuCount; i++) {
allPromises.push(this.createTestRunner(i).then(testRunnerMetadata => testRunnerMetadatas.push(testRunnerMetadata)));
}
Expand All @@ -108,7 +114,7 @@ export default class TestRunnerOrchestrator {

private createTestRunner(portOffset: number): Promise<TestRunnerMetadata> {
return this.copyAllSourceFilesToTempFolder().then(sourceFileMap => {
let selector = TestSelectorFactory.instance().create(this.options.testFrameork, { options: this.options });
let selector = TestSelectorFactory.instance().create(this.options.testFramework, { options: this.options });
let tempSourceFiles: string[] = [];
for (let i in sourceFileMap) {
tempSourceFiles.push(sourceFileMap[i]);
Expand Down
2 changes: 1 addition & 1 deletion src/api/core/StrykerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface StrykerOptions {
/**
* The name of the test framework to use
*/
testFrameork?: string;
testFramework?: string;

/**
* The name of the test runner to use
Expand Down
2 changes: 1 addition & 1 deletion src/api/util/StrykerTempFolder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function createRandomFolder(prefix: string) {
}

function random() {
Math.ceil(Math.random() * 10000000)
return Math.ceil(Math.random() * 10000000);
}

function ensureFolderExists(path: string) {
Expand Down
Loading

0 comments on commit 94b5a6f

Please sign in to comment.