mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
147 lines
4.2 KiB
JavaScript
147 lines
4.2 KiB
JavaScript
![]() |
/**
|
||
|
* Copyright Microsoft Corporation. All rights reserved.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
const child_process = require('child_process');
|
||
|
const path = require('path');
|
||
|
const { EventEmitter } = require('events');
|
||
|
const Mocha = require('mocha');
|
||
|
const { Serializer } = require('v8');
|
||
|
|
||
|
const constants = Mocha.Runner.constants;
|
||
|
|
||
|
class Runner extends EventEmitter {
|
||
|
constructor(options) {
|
||
|
super();
|
||
|
this._maxWorkers = options.maxWorkers;
|
||
|
this._workers = new Set();
|
||
|
this._freeWorkers = [];
|
||
|
this._callbacks = [];
|
||
|
this._workerId = 0;
|
||
|
this.stats = {
|
||
|
duration: 0,
|
||
|
failures: 0,
|
||
|
passes: 0,
|
||
|
pending: 0,
|
||
|
tests: 0,
|
||
|
};
|
||
|
this._reporter = new options.reporter(this, {});
|
||
|
}
|
||
|
|
||
|
async run(files) {
|
||
|
this.emit(constants.EVENT_RUN_BEGIN, {});
|
||
|
const result = new Promise(f => this._runCallback = f);
|
||
|
for (const file of files) {
|
||
|
const worker = await this._obtainWorker();
|
||
|
worker.send({ method: 'run', params: file });
|
||
|
}
|
||
|
await result;
|
||
|
this.emit(constants.EVENT_RUN_END, {});
|
||
|
}
|
||
|
|
||
|
async _obtainWorker() {
|
||
|
if (this._freeWorkers.length)
|
||
|
return this._freeWorkers.pop();
|
||
|
|
||
|
if (this._workers.size < this._maxWorkers) {
|
||
|
const worker = child_process.fork(path.join(__dirname, 'worker.js'), {
|
||
|
detached: false
|
||
|
});
|
||
|
let readyCallback;
|
||
|
const result = new Promise(f => readyCallback = f);
|
||
|
worker.send({ method: 'init', params: ++this._workerId });
|
||
|
worker.on('message', message => {
|
||
|
if (message.method === 'ready')
|
||
|
readyCallback();
|
||
|
this._messageFromWorker(worker, message);
|
||
|
});
|
||
|
worker.on('exit', () => {
|
||
|
this._workers.delete(worker);
|
||
|
if (!this._workers.size)
|
||
|
this._runCallback();
|
||
|
});
|
||
|
this._workers.add(worker);
|
||
|
await result;
|
||
|
return worker;
|
||
|
}
|
||
|
|
||
|
return new Promise(f => this._callbacks.push(f));
|
||
|
}
|
||
|
|
||
|
_messageFromWorker(worker, message) {
|
||
|
const { method, params } = message;
|
||
|
switch (method) {
|
||
|
case 'done': {
|
||
|
if (this._callbacks.length) {
|
||
|
const callback = this._callbacks.shift();
|
||
|
callback(worker);
|
||
|
} else {
|
||
|
this._freeWorkers.push(worker);
|
||
|
if (this._freeWorkers.length === this._workers.size) {
|
||
|
this._runCallback();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case 'start':
|
||
|
break;
|
||
|
case 'test':
|
||
|
this.emit(constants.EVENT_TEST_BEGIN, this._parse(params.test));
|
||
|
break;
|
||
|
case 'pending':
|
||
|
this.emit(constants.EVENT_TEST_PENDING, this._parse(params.test));
|
||
|
break;
|
||
|
case 'pass':
|
||
|
this.emit(constants.EVENT_TEST_PASS, this._parse(params.test));
|
||
|
break;
|
||
|
case 'fail':
|
||
|
const test = this._parse(params.test);
|
||
|
this.emit(constants.EVENT_TEST_FAIL, test, params.error);
|
||
|
break;
|
||
|
case 'end':
|
||
|
this.stats.duration += params.stats.duration;
|
||
|
this.stats.failures += params.stats.failures;
|
||
|
this.stats.passes += params.stats.passes;
|
||
|
this.stats.pending += params.stats.pending;
|
||
|
this.stats.tests += params.stats.tests;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_parse(serialized) {
|
||
|
return {
|
||
|
...serialized,
|
||
|
currentRetry: () => serialized.currentRetry,
|
||
|
fullTitle: () => serialized.fullTitle,
|
||
|
slow: () => serialized.slow,
|
||
|
timeout: () => serialized.timeout,
|
||
|
titlePath: () => serialized.titlePath,
|
||
|
isPending: () => serialized.isPending,
|
||
|
parent: {
|
||
|
fullTitle: () => ''
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
async stop() {
|
||
|
const result = new Promise(f => this._stopCallback = f);
|
||
|
for (const worker of this._workers)
|
||
|
worker.send({ method: 'stop' });
|
||
|
await result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = { Runner };
|