UNPKG

@stryker-mutator/core

Version:

The extendable JavaScript mutation testing framework

135 lines 6.33 kB
import childProcess from 'child_process'; import os from 'os'; import { syncBuiltinESMExports } from 'module'; import { DryRunStatus, TestStatus } from '@stryker-mutator/api/test-runner'; import { errorToString } from '@stryker-mutator/util'; import { expect } from 'chai'; import sinon from 'sinon'; import { factory, assertions } from '@stryker-mutator/test-helpers'; import { CommandTestRunner } from '../../../src/test-runner/command-test-runner.js'; import { objectUtils } from '../../../src/utils/object-utils.js'; import { ChildProcessMock } from '../../helpers/child-process-mock.js'; describe(CommandTestRunner.name, () => { let childProcessMock; let killStub; let clock; let execMock; beforeEach(() => { clock = sinon.useFakeTimers(); childProcessMock = new ChildProcessMock(42); execMock = sinon.stub(childProcess, 'exec').returns(childProcessMock); killStub = sinon.stub(objectUtils, 'kill'); syncBuiltinESMExports(); }); describe(CommandTestRunner.prototype.dryRun.name, () => { it('should run `npm test` by default', async () => { await actDryRun(createSut(undefined, 'foobarDir')); expect(execMock).calledWith('npm test', { cwd: 'foobarDir', env: process.env }); }); it('should allow other commands using configuration', async () => { await actDryRun(createSut({ command: 'some other command' })); expect(execMock).calledWith('some other command'); }); it('should report successful test when the exit code = 0', async () => { const result = await actDryRun(undefined, 0, 42); const expectedResult = { status: DryRunStatus.Complete, tests: [{ id: 'all', name: 'All tests', status: TestStatus.Success, timeSpentMs: 42 }], }; expect(result).deep.eq(expectedResult); }); it('should report failed test when the exit code != 0', async () => { const sut = createSut(); const resultPromise = sut.dryRun(); childProcessMock.stdout.emit('data', 'x Test 1 failed'); childProcessMock.stderr.emit('data', '1 != 2'); childProcessMock.emit('exit', 1); const result = await resultPromise; const expectedResult = { status: DryRunStatus.Complete, tests: [{ id: 'all', name: 'All tests', status: TestStatus.Failed, timeSpentMs: 0, failureMessage: `x Test 1 failed${os.EOL}1 != 2` }], }; expect(result).deep.eq(expectedResult); }); it('should report error on error and kill the process', async () => { killStub.resolves(); const expectedError = new Error('foobar error'); const sut = createSut(); const resultPromise = sut.dryRun(); childProcessMock.emit('error', expectedError); const result = await resultPromise; const expectedResult = { errorMessage: errorToString(expectedError), status: DryRunStatus.Error, }; expect(result).deep.eq(expectedResult); }); it('should remove all listeners on exit', async () => { await actDryRun(); expect(childProcessMock.listenerCount('exit')).eq(0); expect(childProcessMock.listenerCount('error')).eq(0); expect(childProcessMock.stdout.listenerCount('data')).eq(0); expect(childProcessMock.stderr.listenerCount('data')).eq(0); }); }); describe(CommandTestRunner.prototype.mutantRun.name, () => { it('should run with __ACTIVE_MUTANT__ environment variable active', async () => { const sut = createSut(undefined, 'foobarDir'); await actMutantRun(sut, { activeMutantId: '0' }); expect(execMock).calledWith('npm test', { cwd: 'foobarDir', env: { ...process.env, __STRYKER_ACTIVE_MUTANT__: '0' } }); }); it('should convert exit code 0 to a survived mutant', async () => { const result = await actMutantRun(createSut(), { exitCode: 0 }); assertions.expectSurvived(result); }); it('should convert exit code 1 to a killed mutant', async () => { const result = await actMutantRun(createSut(), { exitCode: 1 }); assertions.expectKilled(result); expect(result.killedBy).deep.eq(['all']); }); }); describe('dispose', () => { it('should kill any running process', async () => { killStub.resolves(); const sut = createSut(); void sut.dryRun(); await sut.dispose(); expect(killStub).calledWith(childProcessMock.pid); }); it('should resolve running processes in a timeout', async () => { const sut = createSut(); const resultPromise = sut.dryRun(); await sut.dispose(); const result = await resultPromise; expect(result.status).eq(DryRunStatus.Timeout); }); it('should not kill anything if running process was already resolved', async () => { const sut = createSut(); await actDryRun(sut); await sut.dispose(); expect(killStub).not.called; }); }); async function actDryRun(sut = createSut(), exitCode = 0, elapsedTimeMS = 0) { const resultPromise = sut.dryRun(); clock.tick(elapsedTimeMS); await actTestProcessEnds(exitCode); return resultPromise; } async function actMutantRun(sut = createSut(), { exitCode = 0, activeMutantId = '0' }) { const resultPromise = sut.mutantRun({ activeMutant: factory.mutant({ id: activeMutantId }) }); await actTestProcessEnds(exitCode); return resultPromise; } function createSut(settings, workingDir = 'workingDir') { const strykerOptions = factory.strykerOptions(); if (settings) { strykerOptions.commandRunner = settings; } return new CommandTestRunner(workingDir, strykerOptions); } async function actTestProcessEnds(exitCode) { childProcessMock.emit('exit', exitCode); } }); //# sourceMappingURL=command-test-runner.spec.js.map