@stryker-mutator/core
Version:
The extendable JavaScript mutation testing framework
117 lines • 5.86 kB
JavaScript
import path from 'path';
import { URL } from 'url';
import { testInjector, LoggingServer } from '@stryker-mutator/test-helpers';
import { expect } from 'chai';
import { filter } from 'rxjs/operators';
import { Task } from '@stryker-mutator/util';
import { ChildProcessCrashedError } from '../../../src/child-proxy/child-process-crashed-error.js';
import { ChildProcessProxy } from '../../../src/child-proxy/child-process-proxy.js';
import { OutOfMemoryError } from '../../../src/child-proxy/out-of-memory-error.js';
import { currentLogMock } from '../../helpers/log-mock.js';
import { sleep } from '../../helpers/test-utils.js';
import { IdGenerator } from '../../../src/child-proxy/id-generator.js';
import { Echo } from './echo.js';
describe(ChildProcessProxy.name, () => {
let sut;
let loggingServer;
let log;
let fileDescriptions;
const testRunnerName = 'echoRunner';
const workingDir = '..';
const idGenerator = new IdGenerator();
beforeEach(async () => {
fileDescriptions = {
'src/foo.js': { mutate: true },
'src/foo.spec.js': { mutate: false },
};
loggingServer = new LoggingServer();
const port = await loggingServer.listen();
testInjector.options.testRunner = testRunnerName;
log = currentLogMock();
idGenerator.next();
sut = ChildProcessProxy.create(new URL('./echo.js', import.meta.url).toString(), { port, level: "debug" /* LogLevel.Debug */ }, testInjector.options, fileDescriptions, [], workingDir, Echo, [
'--no-warnings',
'--max-old-space-size=32', // reduce the amount of time we have to wait on the OOM test
], idGenerator);
});
afterEach(async () => {
try {
await sut.dispose();
await loggingServer.dispose();
}
catch (error) {
console.error(error);
}
});
it('should be able to get direct result', async () => {
const actual = await sut.proxy.say('hello');
expect(actual).eq(`${testRunnerName}: hello`);
});
it('should be able to get delayed result', async () => {
const actual = await sut.proxy.sayDelayed('hello', 2);
expect(actual).eq(`${testRunnerName}: hello (2 ms)`);
});
it('should provide fileDescriptions correctly', async () => {
const actual = await sut.proxy.echoFiles();
expect(actual).deep.eq(fileDescriptions);
});
it('should set the current working directory', async () => {
const actual = await sut.proxy.cwd();
expect(actual).eq(path.resolve(workingDir));
});
it('should use `execArgv` to start the child process', async () => {
await sut.proxy.warning();
expect(sut.stderr).not.includes('Foo warning');
});
it('should be able to receive a promise rejection', async () => {
await expect(sut.proxy.reject('Foobar error')).rejectedWith('Foobar error');
});
it('should be able to receive public properties as promised', async () => {
expect(await sut.proxy.testRunnerName()).eq(testRunnerName);
});
it('should be able to log on debug when LogLevel.Debug is allowed', async () => {
const logEventTask = new Task();
loggingServer.event$.pipe(filter((event) => event.categoryName === Echo.name)).subscribe(logEventTask.resolve.bind(logEventTask));
await sut.proxy.debug('test message');
const logger = await logEventTask.promise;
expect(logger.categoryName).eq(Echo.name);
expect(logger.data).deep.eq(['test message']);
});
it('should not log on trace if LogLevel.Debug is allowed as min log level', async () => {
const logEventTask = new Task();
loggingServer.event$.pipe(filter((event) => event.categoryName === Echo.name)).subscribe(logEventTask.resolve.bind(logEventTask));
await sut.proxy.trace('foo');
await sut.proxy.debug('bar');
const logger = await logEventTask.promise;
expect(logger.categoryName).eq(Echo.name);
expect(logger.data).deep.eq(['bar']);
expect(toLogLevel(logger.level)).eq("debug" /* LogLevel.Debug */);
});
it('should reject when the child process exits', () => {
return expect(sut.proxy.exit(42)).rejectedWith(ChildProcessCrashedError);
});
it('should log stdout and stderr on warning when a child process crashed', async () => {
await sut.proxy.stdout('stdout message');
await sut.proxy.stderr('stderr message');
// Give nodejs the chance to flush the stdout and stderr buffers
await sleep(10);
await expect(sut.proxy.exit(12)).rejected;
const call = log.warn.getCall(0);
expect(call.args[0]).matches(/Child process \[pid \d+\] exited unexpectedly with exit code 12 \(without signal\)\. Last part of stdout and stderr was/g);
expect(call.args[0]).includes('stdout message');
expect(call.args[0]).includes('stderr message');
});
it('should immediately reject any subsequent calls when the child process exits', async () => {
await expect(sut.proxy.exit(1)).rejected;
await expect(sut.proxy.say('something')).rejectedWith(ChildProcessCrashedError);
});
it('should throw an OutOfMemoryError if the process went out-of-memory', async function () {
this.retries(5);
await expect(sut.proxy.memoryLeak()).rejectedWith(OutOfMemoryError);
});
});
function toLogLevel(level) {
const levelName = level.levelStr.toLowerCase();
return ["debug" /* LogLevel.Debug */, "error" /* LogLevel.Error */, "fatal" /* LogLevel.Fatal */, "info" /* LogLevel.Information */, "off" /* LogLevel.Off */, "trace" /* LogLevel.Trace */, "warn" /* LogLevel.Warning */].find((logLevel) => logLevel === levelName);
}
//# sourceMappingURL=child-process-proxy.it.spec.js.map