Skip to content
Merged
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
5 changes: 2 additions & 3 deletions packages/jest-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
"jest-message-util": "^21.2.1",
"jest-runtime": "^21.2.1",
"jest-util": "^21.2.1",
"pify": "^3.0.0",
"throat": "^4.0.0",
"worker-farm": "^1.5.1"
"jest-worker": "^21.2.1",
"throat": "^4.0.0"
}
}
36 changes: 14 additions & 22 deletions packages/jest-runner/src/__tests__/test_runner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
const TestRunner = require('../index');
const {TestWatcher} = require('jest-cli');

let workerFarmMock;
let mockWorkerFarm;

jest.mock('worker-farm', () => {
const mock = jest.fn(
(options, worker) =>
(workerFarmMock = jest.fn((data, callback) =>
require(worker)(data, callback),
)),
);
mock.end = jest.fn();
return mock;
jest.mock('jest-worker', () => {
return jest.fn(worker => {
return (mockWorkerFarm = {
end: jest.fn(),
getStderr: jest.fn(),
getStdout: jest.fn(),
worker: jest.fn((data, callback) => require(worker)(data, callback)),
});
});
});

jest.mock('../test_worker', () => {});
Expand All @@ -44,15 +44,9 @@ test('injects the rawModuleMap into each worker in watch mode', () => {
{serial: false},
)
.then(() => {
expect(workerFarmMock.mock.calls).toEqual([
[
{config, globalConfig, path: './file.test.js', rawModuleMap},
expect.any(Function),
],
[
{config, globalConfig, path: './file2.test.js', rawModuleMap},
expect.any(Function),
],
expect(mockWorkerFarm.worker.mock.calls).toEqual([
[{config, globalConfig, path: './file.test.js', rawModuleMap}],
[{config, globalConfig, path: './file2.test.js', rawModuleMap}],
]);
});
});
Expand All @@ -72,15 +66,14 @@ test('does not inject the rawModuleMap in serial mode', () => {
{serial: false},
)
.then(() => {
expect(workerFarmMock.mock.calls).toEqual([
expect(mockWorkerFarm.worker.mock.calls).toEqual([
[
{
config,
globalConfig,
path: './file.test.js',
rawModuleMap: null,
},
expect.any(Function),
],
[
{
Expand All @@ -89,7 +82,6 @@ test('does not inject the rawModuleMap in serial mode', () => {
path: './file2.test.js',
rawModuleMap: null,
},
expect.any(Function),
],
]);
});
Expand Down
30 changes: 16 additions & 14 deletions packages/jest-runner/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ import type {
TestWatcher,
} from 'types/TestRunner';

import pify from 'pify';
import typeof {worker} from './test_worker';

import runTest from './run_test';
import throat from 'throat';
import workerFarm from 'worker-farm';
import Worker from 'jest-worker';

const TEST_WORKER_PATH = require.resolve('./test_worker');

type WorkerInterface = Worker & {worker: worker};

class TestRunner {
_globalConfig: GlobalConfig;

Expand Down Expand Up @@ -89,17 +92,14 @@ class TestRunner {
onResult: OnTestSuccess,
onFailure: OnTestFailure,
) {
const farm = workerFarm(
{
autoStart: true,
maxConcurrentCallsPerWorker: 1,
maxConcurrentWorkers: this._globalConfig.maxWorkers,
maxRetries: 2, // Allow for a couple of transient errors.
},
TEST_WORKER_PATH,
);
// $FlowFixMe: class object is augmented with worker when instantiating.
const worker: WorkerInterface = new Worker(TEST_WORKER_PATH, {
exposedMethods: ['worker'],
maxRetries: 3,
numWorkers: this._globalConfig.maxWorkers,
});

const mutex = throat(this._globalConfig.maxWorkers);
const worker = pify(farm);

// Send test suites to workers continuously instead of all at once to track
// the start time of individual tests.
Expand All @@ -108,8 +108,10 @@ class TestRunner {
if (watcher.isInterrupted()) {
return Promise.reject();
}

await onStart(test);
return worker({

return worker.worker({
config: test.context.config,
globalConfig: this._globalConfig,
path: test.path,
Expand Down Expand Up @@ -146,7 +148,7 @@ class TestRunner {
),
);

const cleanup = () => workerFarm.end(farm);
const cleanup = () => worker.end();
return Promise.race([runAllTests, onInterrupt]).then(cleanup, cleanup);
}
}
Expand Down
45 changes: 15 additions & 30 deletions packages/jest-runner/src/test_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@ import {separateMessageFromStack} from 'jest-message-util';
import Runtime from 'jest-runtime';
import runTest from './run_test';

type WorkerData = {|
export type WorkerData = {|
config: ProjectConfig,
globalConfig: GlobalConfig,
path: Path,
rawModuleMap?: RawModuleMap,
rawModuleMap: ?RawModuleMap,
|};

type WorkerCallback = (error: ?SerializableError, result?: TestResult) => void;

const formatError = (error: string | Error): SerializableError => {
if (typeof error === 'string') {
const {message, stack} = separateMessageFromStack(error);
Expand Down Expand Up @@ -69,33 +67,20 @@ const getResolver = (config, rawModuleMap) => {
}
};

// Cannot be ESM export because of worker-farm
module.exports = (
{config, globalConfig, path, rawModuleMap}: WorkerData,
callback: WorkerCallback,
) => {
let parentExited = false;
const disconnectCallback = () => (parentExited = true);
const removeListener = () =>
process.removeListener('disconnect', disconnectCallback);
process.on('disconnect', disconnectCallback);

export async function worker({
config,
globalConfig,
path,
rawModuleMap,
}: WorkerData): Promise<TestResult> {
try {
runTest(path, globalConfig, config, getResolver(config, rawModuleMap)).then(
result => {
removeListener();
if (!parentExited) {
callback(null, result);
}
},
error => {
removeListener();
if (!parentExited) {
callback(formatError(error));
}
},
return await runTest(
path,
globalConfig,
config,
getResolver(config, rawModuleMap),
);
} catch (error) {
callback(formatError(error));
throw formatError(error);
}
};
}