Skip to content

Commit

Permalink
Experimentally implement shared workers
Browse files Browse the repository at this point in the history
  • Loading branch information
novemberborn committed May 9, 2020
1 parent 684379a commit d673850
Show file tree
Hide file tree
Showing 33 changed files with 949 additions and 18 deletions.
7 changes: 6 additions & 1 deletion ava.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
const skipTests = [];
if (process.versions.node < '12.16.0') {
skipTests.push('!test/shared-workers/!(requires-newish-node)/**');
}

export default {
files: ['test/**', '!test/**/{fixtures,helpers}/**'],
files: ['test/**', '!test/**/{fixtures,helpers}/**', ...skipTests],
ignoredByWatcher: ['{coverage,docs,media,test-d,test-tap}/**']
};
2 changes: 2 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const RunStatus = require('./run-status');
const fork = require('./fork');
const serializeError = require('./serialize-error');
const {getApplicableLineNumbers} = require('./line-numbers');
const sharedWorkers = require('./shared-workers');

function resolveModules(modules) {
return arrify(modules).map(name => {
Expand Down Expand Up @@ -231,6 +232,7 @@ class Api extends Emittery {

const worker = fork(file, options, apiOptions.nodeArguments);
runStatus.observeWorker(worker, file, {selectingLines: lineNumbers.length > 0});
sharedWorkers.observeWorkerProcess(worker, runStatus);

pendingWorkers.add(worker);
worker.promise.then(() => {
Expand Down
64 changes: 64 additions & 0 deletions lib/fork.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,55 @@ const serializeOptions = useAdvanced ?
options => JSON.parse(JSON.stringify(options)) : // Use JSON serialization to remove non-clonable values.
options => options;

class SharedWorkerChannel extends Emittery {
constructor({channelId, filename, initialData}, sendToFork) {
super();

this.id = channelId;
this.filename = filename;
this.initialData = initialData;
this.sendToFork = sendToFork;
}

signalReady() {
this.sendToFork({
type: 'shared-worker-ready',
channelId: this.id
});
}

signalError() {
this.sendToFork({
type: 'shared-worker-error',
channelId: this.id
});
}

emitMessage({messageId, replyTo, data}) {
this.emit('message', {
messageId,
replyTo,
data
});
}

forwardMessageToFork({messageId, replyTo, data}) {
this.sendToFork({
type: 'shared-worker-message',
channelId: this.id,
messageId,
replyTo,
data
});
}
}

let forkCounter = 0;

module.exports = (file, options, execArgv = process.execArgv) => {
const forkId = `fork/${++forkCounter}`;
const sharedWorkerChannels = new Map();

let finished = false;

const emitter = new Emittery();
Expand All @@ -32,6 +80,7 @@ module.exports = (file, options, execArgv = process.execArgv) => {
options = {
baseDir: process.cwd(),
file,
forkId,
...options
};

Expand Down Expand Up @@ -75,6 +124,16 @@ module.exports = (file, options, execArgv = process.execArgv) => {
case 'ready-for-options':
send({type: 'options', options: serializeOptions(options)});
break;
case 'shared-worker-connect': {
const channel = new SharedWorkerChannel(message.ava, send);
sharedWorkerChannels.set(channel.id, channel);
emitter.emit('connectSharedWorker', channel);
break;
}

case 'shared-worker-message':
sharedWorkerChannels.get(message.ava.channelId).emitMessage(message.ava);
break;
case 'ping':
send({type: 'pong'});
break;
Expand Down Expand Up @@ -105,6 +164,7 @@ module.exports = (file, options, execArgv = process.execArgv) => {

return {
file,
forkId,
promise,

exit() {
Expand All @@ -116,6 +176,10 @@ module.exports = (file, options, execArgv = process.execArgv) => {
send({type: 'peer-failed'});
},

onConnectSharedWorker(listener) {
return emitter.on('connectSharedWorker', listener);
},

onStateChange(listener) {
return emitter.on('stateChange', listener);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/load-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const pkgConf = require('pkg-conf');

const NO_SUCH_FILE = Symbol('no ava.config.js file');
const MISSING_DEFAULT_EXPORT = Symbol('missing default export');
const EXPERIMENTS = new Set();
const EXPERIMENTS = new Set(['sharedWorkers']);

// *Very* rudimentary support for loading ava.config.js files containing an `export default` statement.
const evaluateJsConfig = configFile => {
Expand Down
34 changes: 30 additions & 4 deletions lib/reporters/mini.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class MiniReporter {
this.prefixTitle = (testFile, title) => title;
this.previousFailures = 0;
this.removePreviousListener = null;
this.sharedWorkerErrors = [];
this.stats = null;
this.uncaughtExceptions = [];
this.unhandledRejections = [];
Expand Down Expand Up @@ -180,6 +181,9 @@ class MiniReporter {
case 'selected-test':
// Ignore
break;
case 'shared-worker-error':
this.sharedWorkerErrors.push(evt);
break;
case 'stats':
this.stats = evt.stats;
break;
Expand Down Expand Up @@ -511,7 +515,7 @@ class MiniReporter {
const shouldWriteFailFastDisclaimer = this.failFastEnabled && (this.stats.remainingTests > 0 || this.stats.files > this.stats.finishedWorkers);

if (this.failures.length > 0) {
const writeTrailingLines = shouldWriteFailFastDisclaimer || this.internalErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
const writeTrailingLines = shouldWriteFailFastDisclaimer || this.internalErrors.length > 0 || this.sharedWorkerErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
this.lineWriter.writeLine();

const last = this.failures[this.failures.length - 1];
Expand All @@ -527,7 +531,7 @@ class MiniReporter {

if (this.internalErrors.length > 0) {
const writeLeadingLine = this.failures.length === 0;
const writeTrailingLines = shouldWriteFailFastDisclaimer || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
const writeTrailingLines = shouldWriteFailFastDisclaimer || this.sharedWorkerErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;

if (writeLeadingLine) {
this.lineWriter.writeLine();
Expand All @@ -551,8 +555,30 @@ class MiniReporter {
}
}

if (this.uncaughtExceptions.length > 0) {
if (this.sharedWorkerErrors.length > 0) {
const writeLeadingLine = this.failures.length === 0 && this.internalErrors.length === 0;
const writeTrailingLines = shouldWriteFailFastDisclaimer || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;

if (writeLeadingLine) {
this.lineWriter.writeLine();
}

const last = this.sharedWorkerErrors[this.sharedWorkerErrors.length - 1];
for (const evt of this.sharedWorkerErrors) {
this.lineWriter.writeLine(colors.error(`${figures.cross} Error in shared worker`));

this.lineWriter.writeLine(colors.stack(evt.err.summary));
this.lineWriter.writeLine(colors.errorStack(evt.err.stack));
if (evt !== last || writeTrailingLines) {
this.lineWriter.writeLine();
this.lineWriter.writeLine();
this.lineWriter.writeLine();
}
}
}

if (this.uncaughtExceptions.length > 0) {
const writeLeadingLine = this.failures.length === 0 && this.internalErrors.length === 0 && this.sharedWorkerErrors.length === 0;
const writeTrailingLines = shouldWriteFailFastDisclaimer || this.unhandledRejections.length > 0;

if (writeLeadingLine) {
Expand All @@ -573,7 +599,7 @@ class MiniReporter {
}

if (this.unhandledRejections.length > 0) {
const writeLeadingLine = this.failures.length === 0 && this.internalErrors.length === 0 && this.uncaughtExceptions.length === 0;
const writeLeadingLine = this.failures.length === 0 && this.internalErrors.length === 0 && this.sharedWorkerErrors.length === 0 && this.uncaughtExceptions.length === 0;
const writeTrailingLines = shouldWriteFailFastDisclaimer;

if (writeLeadingLine) {
Expand Down
8 changes: 8 additions & 0 deletions lib/reporters/verbose.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ class VerboseReporter {
this.lineWriter.writeLine(colors.todo(`- ${this.prefixTitle(evt.testFile, evt.title)}`));
}

break;
case 'shared-worker-error':
this.lineWriter.writeLine(colors.error(`${figures.cross} Error in shared worker`));

this.lineWriter.writeLine(colors.stack(evt.err.summary));
this.lineWriter.writeLine(colors.errorStack(evt.err.stack));
this.lineWriter.writeLine();
this.lineWriter.writeLine();
break;
case 'stats':
this.stats = evt.stats;
Expand Down
3 changes: 3 additions & 0 deletions lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Runner extends Emittery {
serial: [],
todo: []
};
this.waitForReady = [];

const uniqueTestTitles = new Set();
this.registerUniqueTitle = title => {
Expand Down Expand Up @@ -435,6 +436,8 @@ class Runner extends Emittery {
});
}

await Promise.all(this.waitForReady);

if (concurrentTests.length === 0 && serialTests.length === 0) {
this.emit('finish');
// Don't run any hooks if there are no tests to run.
Expand Down
Loading

0 comments on commit d673850

Please sign in to comment.