Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(stryker): #336 Configurable tmp folder location #475

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/stryker-api/src/config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default class Config implements StrykerOptions {
mutator: string = 'es5';
transpilers: string[] = [];
maxConcurrentTestRunners: number = Infinity;
tempDir: string = '.stryker-tmp';
thresholds: MutationScoreThresholds = {
high: 80,
low: 60,
Expand Down
8 changes: 8 additions & 0 deletions packages/stryker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,11 @@ Set `break` to `null` (default) to never let the process crash.
Set the `log4js` log level that Stryker uses (default is `info`). Possible values: `fatal`, `error`, `warn`, `info`, `debug`, `trace`, `all` and `off`.
*Note*: Test runners are run as child processes of the Stryker Node process. All output (stdout) of the `testRunner` is logged as `trace`.
Thus, to see logging output from the test runner set the `logLevel` to `all` or `trace`.

#### Temporary folder
**Command line:** `--tempDir .stryker-tmp`
**Config file:** `tempDir: '.stryker-tmp'`
**Default value:** `.stryker-tmp`
**Mandatory**: no
**Description:**
Set the name of the directory that is used by Stryker as a working directory. This directory is used to keep copies of your files that are used by Stryker to mutate your code safely. This directory will be cleaned after a successful run. **Warning!** If you configure an absolute path (for example `/tmp/stryker`), node-based test runners like mocha will fail, because your `node_modules` cannot be loaded. Use with caution.
4 changes: 2 additions & 2 deletions packages/stryker/src/Sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { wrapInClosure } from './utils/objectUtils';
import TestRunnerDecorator from './isolated-runner/TestRunnerDecorator';
import ResilientTestRunnerFactory from './isolated-runner/ResilientTestRunnerFactory';
import IsolatedRunnerOptions from './isolated-runner/IsolatedRunnerOptions';
import { TempFolder } from './utils/TempFolder';
import { TempDir } from './utils/TempDir';
import * as fileUtils from './utils/fileUtils';
import TestableMutant from './TestableMutant';
import TranspiledMutant from './TranspiledMutant';
Expand All @@ -28,7 +28,7 @@ export default class Sandbox {
private testHooksFile = path.resolve('___testHooksForStryker.js');

private constructor(private options: Config, private index: number, files: ReadonlyArray<File>, private testFramework: TestFramework | null) {
this.workingFolder = TempFolder.instance().createRandomFolder('sandbox');
this.workingFolder = TempDir.instance().createRandomFolder('sandbox');
this.log.debug('Creating a sandbox for files in %s', this.workingFolder);
this.files = files.slice(); // Create a copy
if (testFramework) {
Expand Down
6 changes: 3 additions & 3 deletions packages/stryker/src/Stryker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import PluginLoader from './PluginLoader';
import ScoreResultCalculator from './ScoreResultCalculator';
import ConfigValidator from './ConfigValidator';
import { freezeRecursively, isPromise } from './utils/objectUtils';
import { TempFolder } from './utils/TempFolder';
import { TempDir } from './utils/TempDir';
import * as log4js from 'log4js';
import Timer from './utils/Timer';
import StrictReporter from './reporters/StrictReporter';
Expand Down Expand Up @@ -48,7 +48,7 @@ export default class Stryker {
async runMutationTest(): Promise<MutantResult[]> {
this.timer.reset();
const inputFiles = await new InputFileResolver(this.config.mutate, this.config.files, this.reporter).resolve();
TempFolder.instance().initialize();
TempDir.instance().initialize(this.config.tempDir);
const initialTestRunProcess = this.createInitialTestRunner(inputFiles);
const initialTestRunResult = await initialTestRunProcess.run();
const testableMutants = await this.mutate(inputFiles, initialTestRunResult);
Expand All @@ -57,7 +57,7 @@ export default class Stryker {
const mutantResults = await mutationTestExecutor.run(testableMutants);
this.reportScore(mutantResults);
await this.wrapUpReporter();
await TempFolder.instance().clean();
await TempDir.instance().clean();
await this.logDone();
return mutantResults;
} else {
Expand Down
1 change: 1 addition & 0 deletions packages/stryker/src/StrykerCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default class StrykerCli {
.option('--timeoutFactor <number>', 'Tweak the standard deviation relative to the normal test run of a mutated test', parseFloat)
.option('--maxConcurrentTestRunners <n>', 'Set the number of max concurrent test runner to spawn (default: cpuCount)', parseInt)
.option('--logLevel <level>', 'Set the log4js log level. Possible values: fatal, error, warn, info, debug, trace, all and off. Default is "info"')
.option('--tempDir <name>', 'Set the name of the directory that is used by Stryker as a working directory. This directory will be cleaned after a successful run')
.parse(this.argv);

setGlobalLogLevel(program['logLevel'] || 'info');
Expand Down
74 changes: 0 additions & 74 deletions packages/stryker/src/utils/StrykerTempFolder.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import { getLogger } from 'log4js';
import { deleteDir } from './fileUtils';


export class TempFolder {
private readonly log = getLogger(TempFolder.name);
export class TempDir {
private readonly log = getLogger(TempDir.name);
baseTempFolder: string;
tempFolder: string;

private constructor() { }

initialize(tempDirName = '.stryker-tmp') {
initialize(tempDirName: string) {
this.baseTempFolder = path.join(process.cwd(), tempDirName);
this.tempFolder = path.join(this.baseTempFolder, this.random().toString());
this.log.debug(`Using stryker temp folder ${this.baseTempFolder}`);
mkdirp.sync(this.baseTempFolder);
mkdirp.sync(this.tempFolder);
}
Expand Down Expand Up @@ -74,10 +75,10 @@ export class TempFolder {
return Math.ceil(Math.random() * 10000000);
}

private static _instance: TempFolder;
private static _instance: TempDir;
static instance() {
if (!this._instance) {
this._instance = new TempFolder();
this._instance = new TempDir();
}
return this._instance;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/stryker/test/unit/SandboxSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { FileKind, File } from 'stryker-api/core';
import { TextFile } from 'stryker-api/src/core/File';
import { wrapInClosure } from '../../src/utils/objectUtils';
import Sandbox from '../../src/Sandbox';
import { TempFolder } from '../../src/utils/TempFolder';
import { TempDir } from '../../src/utils/TempDir';
import ResilientTestRunnerFactory from '../../src/isolated-runner/ResilientTestRunnerFactory';
import IsolatedRunnerOptions from '../../src/isolated-runner/IsolatedRunnerOptions';
import TestableMutant from '../../src/TestableMutant';
Expand Down Expand Up @@ -50,7 +50,7 @@ describe('Sandbox', () => {
notMutatedFile,
];
files = (textFiles as File[]).concat([webFile({ name: webFileUrl, mutated: false, included: true, transpiled: false })]);
sandbox.stub(TempFolder.instance(), 'createRandomFolder').returns(workingFolder);
sandbox.stub(TempDir.instance(), 'createRandomFolder').returns(workingFolder);
fileSystemStub = sandbox.stub(fileUtils, 'writeFile');
fileSystemStub.resolves();
sandbox.stub(mkdirp, 'sync').returns('');
Expand Down Expand Up @@ -82,7 +82,7 @@ describe('Sandbox', () => {
});

it('should have created a workingFolder', () => {
expect(TempFolder.instance().createRandomFolder).to.have.been.calledWith('sandbox');
expect(TempDir.instance().createRandomFolder).to.have.been.calledWith('sandbox');
});

it('should have created the isolated test runner without framework hook', () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/stryker/test/unit/StrykerSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import MutationTestExecutor, * as mutationTestExecutor from '../../src/process/M
import ConfigValidator, * as configValidator from '../../src/ConfigValidator';
import ScoreResultCalculator, * as scoreResultCalculatorModule from '../../src/ScoreResultCalculator';
import PluginLoader, * as pluginLoader from '../../src/PluginLoader';
import { TempFolder } from '../../src/utils/TempFolder';
import { TempDir } from '../../src/utils/TempDir';
import currentLogMock from '../helpers/log4jsMock';
import { mock, Mock, testFramework as testFrameworkMock, textFile, config, runResult, testableMutant, mutantResult } from '../helpers/producers';
import BroadcastReporter from '../../src/reporters/BroadcastReporter';
Expand Down Expand Up @@ -44,7 +44,7 @@ describe('Stryker', function () {
let pluginLoaderMock: Mock<PluginLoader>;
let strykerConfig: Config;
let reporter: Mock<BroadcastReporter>;
let tempFolderMock: Mock<TempFolder>;
let tempFolderMock: Mock<TempDir>;
let scoreResultCalculator: ScoreResultCalculator;

beforeEach(() => {
Expand Down Expand Up @@ -74,8 +74,8 @@ describe('Stryker', function () {
sandbox.stub(configReader, 'default').returns(configReaderMock);
sandbox.stub(pluginLoader, 'default').returns(pluginLoaderMock);
sandbox.stub(inputFileResolver, 'default').returns(inputFileResolverMock);
tempFolderMock = mock(TempFolder);
sandbox.stub(TempFolder, 'instance').returns(tempFolderMock);
tempFolderMock = mock(TempDir);
sandbox.stub(TempDir, 'instance').returns(tempFolderMock);
tempFolderMock.clean.resolves();
scoreResultCalculator = new ScoreResultCalculator();
sandbox.stub(scoreResultCalculator, 'determineExitCode').returns(sandbox.stub());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import * as mkdirp from 'mkdirp';
import * as fs from 'mz/fs';
import { TempFolder } from '../../../src/utils/TempFolder';
import { TempDir } from '../../../src/utils/TempDir';
import * as fileUtils from '../../../src/utils/fileUtils';

describe('TempFolder', () => {
let sandbox: sinon.SinonSandbox;
describe('tempDir', () => {
let cwdStub: sinon.SinonStub;
let randomStub: sinon.SinonStub;
let deleteDirStub: sinon.SinonStub;
const mockCwd = '/x/y/z/some/dir';
const nameTempDir = '.stryker-tmp';

beforeEach(() => {
sandbox = sinon.createSandbox();

sandbox.stub(mkdirp, 'sync');
sandbox.stub(fs, 'writeFile');
deleteDirStub = sandbox.stub(fileUtils, 'deleteDir');
cwdStub = sandbox.stub(process, 'cwd');
cwdStub.returns(mockCwd);
randomStub = sandbox.stub(TempFolder.instance(), 'random');
randomStub = sandbox.stub(TempDir.instance(), 'random');
randomStub.returns('rand');

TempFolder.instance().baseTempFolder = '';
TempFolder.instance().tempFolder = '';
TempDir.instance().baseTempFolder = '';
TempDir.instance().tempFolder = '';
});
afterEach(() => sandbox.restore());

describe('createRandomFolder', () => {
describe('when temp folder is initialized', () => {
beforeEach(() => TempFolder.instance().initialize());
beforeEach(() => TempDir.instance().initialize(nameTempDir));
it('should create dir with correct path', () => {
const result = TempFolder.instance().createRandomFolder('prefix');
const result = TempDir.instance().createRandomFolder('prefix');

expect(mkdirp.sync).to.have.been.calledThrice;
expect(result.includes('prefix')).to.be.true;
Expand All @@ -42,33 +38,36 @@ describe('TempFolder', () => {
describe('when temp folder is not initialized', () => {
it('should throw error', () => {
expect(() => {
TempFolder.instance().createRandomFolder('prefix');
TempDir.instance().createRandomFolder('prefix');
}).to.throw();
});
});
});

describe('clean', () => {
describe('when temp folder is initialized', () => {
beforeEach(() => TempFolder.instance().initialize());
it('should call deleteDir fileApi', () => {
beforeEach(() => {
TempDir.instance().initialize(nameTempDir);
});

it('should call deleteDir fileApi', async () => {
deleteDirStub.resolves('delResolveStub');

const tempFolderInstance = TempFolder.instance();
const result = tempFolderInstance.clean();
const tempFolderInstance = TempDir.instance();
const result = await tempFolderInstance.clean();

expect(fileUtils.deleteDir).to.have.been.calledWith(
tempFolderInstance.baseTempFolder
);

result.then(data => expect(data).equals('delResolveStub'));
expect(result).equals('delResolveStub');
});
});

describe('when temp folder is not initialized', () => {
it('should throw error', () => {
expect(() => {
TempFolder.instance().createRandomFolder('prefix');
TempDir.instance().createRandomFolder('prefix');
}).to.throw();
});
});
Expand Down