Merge pull request #24868 from Microsoft/parallelAsyncTests
Support async tests in runtests-parallel
This commit is contained in:
Коммит
6c8ecc7386
|
@ -10,7 +10,7 @@
|
|||
"integrity": "sha1-z6I7xYQPkQTOMqZedNt+epdLvuE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"acorn": "5.5.3",
|
||||
"acorn": "5.7.1",
|
||||
"css": "2.2.1",
|
||||
"normalize-path": "2.1.1",
|
||||
"source-map": "0.5.7",
|
||||
|
@ -18,9 +18,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz",
|
||||
"integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==",
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
|
||||
"integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,9 @@
|
|||
}
|
||||
},
|
||||
"@octokit/rest": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.8.1.tgz",
|
||||
"integrity": "sha512-IpC/ctwwauiiSrnNTHOG4CyAPz5YwEX8wSSGuTBb0M1mJcAYJCaYZr11dSZTB4K2p2XFY4AY5+SZcW5aub3hSQ==",
|
||||
"version": "15.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-15.8.2.tgz",
|
||||
"integrity": "sha512-hMUDI6NveJE49rGYfNfXT2CiHODhQMfbqFAa2h8TjR3GrfI1wnfSlsYeGZe4D/Qu+Svqlg9eUisoeIvYWz1yZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"before-after-hook": "1.1.0",
|
||||
|
@ -79,9 +79,9 @@
|
|||
}
|
||||
},
|
||||
"@types/chai": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz",
|
||||
"integrity": "sha512-f5dXGzOJycyzSMdaXVhiBhauL4dYydXwVpavfQ1mVCaGjR56a9QfklXObUxlIY9bGTmCPHEEZ04I16BZ/8w5ww==",
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.4.tgz",
|
||||
"integrity": "sha512-h6+VEw2Vr3ORiFCyyJmcho2zALnUq9cvdB/IO8Xs9itrJVCenC7o26A6+m7D0ihTTr65eS259H5/Ghl/VjYs6g==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/convert-source-map": {
|
||||
|
@ -214,9 +214,9 @@
|
|||
}
|
||||
},
|
||||
"@types/mocha": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.1.tgz",
|
||||
"integrity": "sha512-dOrgprHnkDaj1pmrwdcMAf0QRNQzqTB5rxJph+iIQshSmIvtgRqJ0nim8u1vvXU8iOXZrH96+M46JDFTPLingA==",
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.2.tgz",
|
||||
"integrity": "sha512-tfg9rh2qQhBW6SBqpvfqTgU6lHWGhQURoTrn7NeDF+CEkO9JGYbkzU23EXu6p3bnvDxLeeSX8ohAA23urvWeNw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
|
@ -370,15 +370,6 @@
|
|||
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-colors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
|
||||
"integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-wrap": "0.1.0"
|
||||
}
|
||||
},
|
||||
"ansi-cyan": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
|
||||
|
@ -2132,6 +2123,25 @@
|
|||
"map-cache": "0.2.2"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz",
|
||||
"integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"jsonfile": "4.0.0",
|
||||
"universalify": "0.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"graceful-fs": {
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
||||
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"fs-mkdirp-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
|
||||
|
@ -2590,7 +2600,7 @@
|
|||
"requires": {
|
||||
"@gulp-sourcemaps/identity-map": "1.0.1",
|
||||
"@gulp-sourcemaps/map-sources": "1.0.0",
|
||||
"acorn": "5.6.2",
|
||||
"acorn": "5.7.1",
|
||||
"convert-source-map": "1.5.1",
|
||||
"css": "2.2.1",
|
||||
"debug-fabulous": "1.1.0",
|
||||
|
@ -2602,9 +2612,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.6.2.tgz",
|
||||
"integrity": "sha512-zUzo1E5dI2Ey8+82egfnttyMlMZ2y0D8xOCO3PNPPlYXpl8NZvF6Qk9L9BEtJs+43FqEmfBViDqc5d1ckRDguw==",
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz",
|
||||
"integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"graceful-fs": {
|
||||
|
@ -2622,19 +2632,25 @@
|
|||
}
|
||||
},
|
||||
"gulp-typescript": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-4.0.2.tgz",
|
||||
"integrity": "sha512-Hhbn5Aa2l3T+tnn0KqsG6RRJmcYEsr3byTL2nBpNBeAK8pqug9Od4AwddU4JEI+hRw7mzZyjRbB8DDWR6paGVA==",
|
||||
"version": "5.0.0-alpha.1",
|
||||
"resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-5.0.0-alpha.1.tgz",
|
||||
"integrity": "sha512-B2Zfup9R5p/hvZowVWdthCt/vrDxiwIQ1Ehi/CsHb6qRB66PVhDeF6Yw/d6HF/3wtka/XxI9zsTjiRb+3bsrJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-colors": "1.1.0",
|
||||
"plugin-error": "0.1.2",
|
||||
"source-map": "0.6.1",
|
||||
"ansi-colors": "2.0.1",
|
||||
"plugin-error": "1.0.1",
|
||||
"source-map": "0.7.3",
|
||||
"through2": "2.0.3",
|
||||
"vinyl": "2.1.0",
|
||||
"vinyl-fs": "3.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-colors": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-2.0.1.tgz",
|
||||
"integrity": "sha512-qUIXfMVe0LoHCFPD6dGtjDDuVoP7B2DWBXIfd5aN/hGNIZDndQmqCwNjCChzxi8TPPGmBV4TB3XPc0VfgR7iIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"glob-stream": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
|
||||
|
@ -2677,10 +2693,33 @@
|
|||
"readable-stream": "2.3.6"
|
||||
}
|
||||
},
|
||||
"plugin-error": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
|
||||
"integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-colors": "1.1.0",
|
||||
"arr-diff": "4.0.0",
|
||||
"arr-union": "3.1.0",
|
||||
"extend-shallow": "3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-colors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
|
||||
"integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-wrap": "0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"unique-stream": {
|
||||
|
@ -3438,6 +3477,24 @@
|
|||
"jsonify": "0.0.0"
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"graceful-fs": {
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
||||
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsonify": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
|
||||
|
@ -5721,6 +5778,12 @@
|
|||
"integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=",
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
|
||||
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=",
|
||||
"dev": true
|
||||
},
|
||||
"unset-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
"@types/minimatch": "latest",
|
||||
"@types/minimist": "latest",
|
||||
"@types/mkdirp": "latest",
|
||||
"@types/mocha": "^5.2.2",
|
||||
"@types/mocha": "latest",
|
||||
"@types/node": "8.5.5",
|
||||
"@types/q": "latest",
|
||||
"@types/run-sequence": "latest",
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,109 @@
|
|||
import Mocha = require("../../");
|
||||
|
||||
export = common;
|
||||
|
||||
declare function common(suites: Mocha.Suite[], context: Mocha.MochaGlobals, mocha: Mocha): common.CommonFunctions;
|
||||
|
||||
declare namespace common {
|
||||
export interface CommonFunctions {
|
||||
/**
|
||||
* This is only present if flag --delay is passed into Mocha. It triggers
|
||||
* root suite execution.
|
||||
*/
|
||||
runWithSuite(suite: Mocha.Suite): () => void;
|
||||
|
||||
/**
|
||||
* Execute before running tests.
|
||||
*/
|
||||
before(fn?: Mocha.Func | Mocha.AsyncFunc): void;
|
||||
|
||||
/**
|
||||
* Execute before running tests.
|
||||
*/
|
||||
before(name: string, fn?: Mocha.Func | Mocha.AsyncFunc): void;
|
||||
|
||||
/**
|
||||
* Execute after running tests.
|
||||
*/
|
||||
after(fn?: Mocha.Func | Mocha.AsyncFunc): void;
|
||||
|
||||
/**
|
||||
* Execute after running tests.
|
||||
*/
|
||||
after(name: string, fn?: Mocha.Func | Mocha.AsyncFunc): void;
|
||||
|
||||
/**
|
||||
* Execute before each test case.
|
||||
*/
|
||||
beforeEach(fn?: Mocha.Func | Mocha.AsyncFunc): void;
|
||||
|
||||
/**
|
||||
* Execute before each test case.
|
||||
*/
|
||||
beforeEach(name: string, fn?: Mocha.Func | Mocha.AsyncFunc): void;
|
||||
|
||||
/**
|
||||
* Execute after each test case.
|
||||
*/
|
||||
afterEach(fn?: Mocha.Func | Mocha.AsyncFunc): void;
|
||||
|
||||
/**
|
||||
* Execute after each test case.
|
||||
*/
|
||||
afterEach(name: string, fn?: Mocha.Func | Mocha.AsyncFunc): void;
|
||||
|
||||
suite: SuiteFunctions;
|
||||
test: TestFunctions;
|
||||
}
|
||||
|
||||
export interface CreateOptions {
|
||||
/** Title of suite */
|
||||
title: string;
|
||||
|
||||
/** Suite function */
|
||||
fn?: (this: Mocha.Suite) => void;
|
||||
|
||||
/** Is suite pending? */
|
||||
pending?: boolean;
|
||||
|
||||
/** Filepath where this Suite resides */
|
||||
file?: string;
|
||||
|
||||
/** Is suite exclusive? */
|
||||
isOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface SuiteFunctions {
|
||||
/**
|
||||
* Create an exclusive Suite; convenience function
|
||||
*/
|
||||
only(opts: CreateOptions): Mocha.Suite;
|
||||
|
||||
/**
|
||||
* Create a Suite, but skip it; convenience function
|
||||
*/
|
||||
skip(opts: CreateOptions): Mocha.Suite;
|
||||
|
||||
/**
|
||||
* Creates a suite.
|
||||
*/
|
||||
create(opts: CreateOptions): Mocha.Suite;
|
||||
}
|
||||
|
||||
export interface TestFunctions {
|
||||
/**
|
||||
* Exclusive test-case.
|
||||
*/
|
||||
only(mocha: Mocha, test: Mocha.Test): Mocha.Test;
|
||||
|
||||
/**
|
||||
* Pending test case.
|
||||
*/
|
||||
skip(title: string): void;
|
||||
|
||||
/**
|
||||
* Number of retry attempts
|
||||
*/
|
||||
retries(n: number): void;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
export = milliseconds;
|
||||
|
||||
/**
|
||||
* Parse the given `str` and return milliseconds.
|
||||
*
|
||||
* @see {@link https://mochajs.org/api/module-milliseconds.html}
|
||||
* @see {@link https://mochajs.org/api/module-milliseconds.html#~parse}
|
||||
*/
|
||||
declare function milliseconds(val: string): number;
|
||||
|
||||
/**
|
||||
* Format for `ms`.
|
||||
*
|
||||
* @see {@link https://mochajs.org/api/module-milliseconds.html}
|
||||
* @see {@link https://mochajs.org/api/module-milliseconds.html#~format}
|
||||
*/
|
||||
declare function milliseconds(val: number): string;
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "@types/mocha",
|
||||
"private": true,
|
||||
"version": "5.2.1"
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,16 +1,90 @@
|
|||
/// <reference path="./host.ts" />
|
||||
/// <reference path="./worker.ts" />
|
||||
namespace Harness.Parallel {
|
||||
export type ParallelTestMessage = { type: "test", payload: { runner: TestRunnerKind | "unittest", file: string } } | never;
|
||||
export type ParallelBatchMessage = { type: "batch", payload: ParallelTestMessage["payload"][] } | never;
|
||||
export type ParallelCloseMessage = { type: "close" } | never;
|
||||
export interface RunnerTask {
|
||||
runner: TestRunnerKind;
|
||||
file: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface UnitTestTask {
|
||||
runner: "unittest";
|
||||
file: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export type Task = RunnerTask | UnitTestTask;
|
||||
|
||||
export interface TestInfo {
|
||||
name: string[];
|
||||
}
|
||||
|
||||
export interface ErrorInfo {
|
||||
name: string[];
|
||||
error: string;
|
||||
stack: string;
|
||||
}
|
||||
|
||||
export interface TaskTimeout {
|
||||
duration: number | "reset";
|
||||
}
|
||||
|
||||
export interface TaskResult {
|
||||
passing: number;
|
||||
errors: ErrorInfo[];
|
||||
passes: TestInfo[];
|
||||
duration: number;
|
||||
task: Task;
|
||||
}
|
||||
|
||||
export interface ParallelTestMessage {
|
||||
type: "test";
|
||||
payload: Task;
|
||||
}
|
||||
|
||||
export interface ParallelBatchMessage {
|
||||
type: "batch";
|
||||
payload: Task[];
|
||||
}
|
||||
|
||||
export interface ParallelCloseMessage {
|
||||
type: "close";
|
||||
}
|
||||
|
||||
export type ParallelHostMessage = ParallelTestMessage | ParallelCloseMessage | ParallelBatchMessage;
|
||||
|
||||
export type ParallelErrorMessage = { type: "error", payload: { error: string, stack: string, name?: string[] } } | never;
|
||||
export type TestInfo = { name: string[] } | never;
|
||||
export type ErrorInfo = ParallelErrorMessage["payload"] & TestInfo;
|
||||
export type ParallelResultMessage = { type: "result", payload: { passing: number, errors: ErrorInfo[], passes: TestInfo[], duration: number, runner: TestRunnerKind | "unittest", file: string } } | never;
|
||||
export type ParallelBatchProgressMessage = { type: "progress", payload: ParallelResultMessage["payload"] } | never;
|
||||
export type ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: number | "reset" } } | never;
|
||||
export interface ParallelErrorMessage {
|
||||
type: "error";
|
||||
payload: { error: string, stack: string, name?: string[] };
|
||||
}
|
||||
|
||||
export interface ParallelResultMessage {
|
||||
type: "result";
|
||||
payload: TaskResult;
|
||||
}
|
||||
|
||||
export interface ParallelBatchProgressMessage {
|
||||
type: "progress";
|
||||
payload: TaskResult;
|
||||
}
|
||||
|
||||
export interface ParallelTimeoutChangeMessage {
|
||||
type: "timeout";
|
||||
payload: TaskTimeout;
|
||||
}
|
||||
|
||||
export type ParallelClientMessage = ParallelErrorMessage | ParallelResultMessage | ParallelBatchProgressMessage | ParallelTimeoutChangeMessage;
|
||||
|
||||
export function shimNoopTestInterface(global: Mocha.MochaGlobals) {
|
||||
global.before = ts.noop;
|
||||
global.after = ts.noop;
|
||||
global.beforeEach = ts.noop;
|
||||
global.afterEach = ts.noop;
|
||||
global.describe = global.context = ((_: any, __: any) => { /*empty*/ }) as Mocha.SuiteFunction;
|
||||
global.describe.skip = global.xdescribe = global.xcontext = ts.noop as Mocha.PendingSuiteFunction;
|
||||
global.describe.only = ts.noop as Mocha.ExclusiveSuiteFunction;
|
||||
global.it = global.specify = ((_: any, __: any) => { /*empty*/ }) as Mocha.TestFunction;
|
||||
global.it.skip = global.xit = global.xspecify = ts.noop as Mocha.PendingTestFunction;
|
||||
global.it.only = ts.noop as Mocha.ExclusiveTestFunction;
|
||||
}
|
||||
}
|
|
@ -1,312 +1,300 @@
|
|||
// tslint:disable no-unnecessary-type-assertion (TODO: tslint can't find node types)
|
||||
|
||||
namespace Harness.Parallel.Worker {
|
||||
let errors: ErrorInfo[] = [];
|
||||
let passes: TestInfo[] = [];
|
||||
let passing = 0;
|
||||
|
||||
type MochaCallback = (this: Mocha.ISuiteCallbackContext, done: MochaDone) => void;
|
||||
type Callable = () => void;
|
||||
|
||||
type Executor = {name: string, callback: MochaCallback, kind: "suite" | "test"} | never;
|
||||
|
||||
function resetShimHarnessAndExecute(runner: RunnerBase) {
|
||||
errors = [];
|
||||
passes = [];
|
||||
passing = 0;
|
||||
testList.length = 0;
|
||||
const start = +(new Date());
|
||||
runner.initializeTests();
|
||||
testList.forEach(({ name, callback, kind }) => executeCallback(name, callback, kind));
|
||||
return { errors, passes, passing, duration: +(new Date()) - start };
|
||||
}
|
||||
|
||||
|
||||
let beforeEachFunc: Callable;
|
||||
const namestack: string[] = [];
|
||||
let testList: Executor[] = [];
|
||||
function shimMochaHarness() {
|
||||
(global as any).before = undefined;
|
||||
(global as any).after = undefined;
|
||||
(global as any).beforeEach = undefined;
|
||||
(global as any).describe = ((name, callback) => {
|
||||
testList.push({ name, callback, kind: "suite" });
|
||||
}) as Mocha.IContextDefinition;
|
||||
(global as any).describe.skip = ts.noop;
|
||||
(global as any).it = ((name, callback) => {
|
||||
if (!testList) {
|
||||
throw new Error("Tests must occur within a describe block");
|
||||
}
|
||||
testList.push({ name, callback: callback!, kind: "test" });
|
||||
}) as Mocha.ITestDefinition;
|
||||
(global as any).it.skip = ts.noop;
|
||||
}
|
||||
|
||||
function setTimeoutAndExecute(timeout: number | undefined, f: () => void) {
|
||||
if (timeout !== undefined) {
|
||||
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: timeout } };
|
||||
process.send!(timeoutMsg);
|
||||
}
|
||||
f();
|
||||
if (timeout !== undefined) {
|
||||
// Reset timeout
|
||||
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: "reset" } };
|
||||
process.send!(timeoutMsg);
|
||||
}
|
||||
}
|
||||
|
||||
function executeSuiteCallback(name: string, callback: MochaCallback) {
|
||||
let timeout: number | undefined;
|
||||
const fakeContext: Mocha.ISuiteCallbackContext = {
|
||||
retries() { return this; },
|
||||
slow() { return this; },
|
||||
timeout(n: number) {
|
||||
timeout = n;
|
||||
return this;
|
||||
},
|
||||
};
|
||||
namestack.push(name);
|
||||
let beforeFunc: Callable | undefined;
|
||||
(before as any) = (cb: Callable) => beforeFunc = cb;
|
||||
let afterFunc: Callable | undefined;
|
||||
(after as any) = (cb: Callable) => afterFunc = cb;
|
||||
const savedBeforeEach = beforeEachFunc;
|
||||
(beforeEach as any) = (cb: Callable) => beforeEachFunc = cb;
|
||||
const savedTestList = testList;
|
||||
|
||||
testList = [];
|
||||
try {
|
||||
callback.call(fakeContext);
|
||||
}
|
||||
catch (e) {
|
||||
errors.push({ error: `Error executing suite: ${e.message}`, stack: e.stack, name: [...namestack] });
|
||||
return cleanup();
|
||||
}
|
||||
try {
|
||||
if (beforeFunc) {
|
||||
beforeFunc();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
errors.push({ error: `Error executing before function: ${e.message}`, stack: e.stack, name: [...namestack] });
|
||||
return cleanup();
|
||||
}
|
||||
finally {
|
||||
beforeFunc = undefined;
|
||||
}
|
||||
|
||||
setTimeoutAndExecute(timeout, () => {
|
||||
testList.forEach(({ name, callback, kind }) => executeCallback(name, callback, kind));
|
||||
});
|
||||
|
||||
try {
|
||||
if (afterFunc) {
|
||||
afterFunc();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
errors.push({ error: `Error executing after function: ${e.message}`, stack: e.stack, name: [...namestack] });
|
||||
}
|
||||
finally {
|
||||
afterFunc = undefined;
|
||||
cleanup();
|
||||
}
|
||||
function cleanup() {
|
||||
testList.length = 0;
|
||||
testList = savedTestList;
|
||||
beforeEachFunc = savedBeforeEach;
|
||||
namestack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
function executeCallback(name: string, callback: MochaCallback, kind: "suite" | "test") {
|
||||
if (kind === "suite") {
|
||||
executeSuiteCallback(name, callback);
|
||||
}
|
||||
else {
|
||||
executeTestCallback(name, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function executeTestCallback(name: string, callback: MochaCallback) {
|
||||
let timeout: number | undefined;
|
||||
const fakeContext: Mocha.ITestCallbackContext = {
|
||||
skip() { return this; },
|
||||
timeout(n: number) {
|
||||
timeout = n;
|
||||
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: timeout } };
|
||||
process.send!(timeoutMsg);
|
||||
return this;
|
||||
},
|
||||
retries() { return this; },
|
||||
slow() { return this; },
|
||||
};
|
||||
namestack.push(name);
|
||||
if (beforeEachFunc) {
|
||||
try {
|
||||
beforeEachFunc();
|
||||
}
|
||||
catch (error) {
|
||||
errors.push({ error: error.message, stack: error.stack, name: [...namestack] });
|
||||
namestack.pop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (callback.length === 0) {
|
||||
try {
|
||||
// TODO: If we ever start using async test completions, polyfill promise return handling
|
||||
callback.call(fakeContext);
|
||||
passes.push({ name: [...namestack] });
|
||||
}
|
||||
catch (error) {
|
||||
errors.push({ error: error.message, stack: error.stack, name: [...namestack] });
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
namestack.pop();
|
||||
if (timeout !== undefined) {
|
||||
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: "reset" } };
|
||||
process.send!(timeoutMsg);
|
||||
}
|
||||
}
|
||||
passing++;
|
||||
}
|
||||
else {
|
||||
// Uses `done` callback
|
||||
let completed = false;
|
||||
try {
|
||||
callback.call(fakeContext, (err: any) => {
|
||||
if (completed) {
|
||||
throw new Error(`done() callback called multiple times; ensure it is only called once.`);
|
||||
}
|
||||
if (err) {
|
||||
errors.push({ error: err.toString(), stack: "", name: [...namestack] });
|
||||
}
|
||||
else {
|
||||
passes.push({ name: [...namestack] });
|
||||
passing++;
|
||||
}
|
||||
completed = true;
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
errors.push({ error: error.message, stack: error.stack, name: [...namestack] });
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
namestack.pop();
|
||||
if (timeout !== undefined) {
|
||||
const timeoutMsg: ParallelTimeoutChangeMessage = { type: "timeout", payload: { duration: "reset" } };
|
||||
process.send!(timeoutMsg);
|
||||
}
|
||||
}
|
||||
if (!completed) {
|
||||
errors.push({ error: "Test completes asynchronously, which is unsupported by the parallel harness", stack: "", name: [...namestack] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function start() {
|
||||
let initialized = false;
|
||||
const runners = ts.createMap<RunnerBase>();
|
||||
process.on("message", (data: ParallelHostMessage) => {
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
shimMochaHarness();
|
||||
function hookUncaughtExceptions() {
|
||||
if (!exceptionsHooked) {
|
||||
process.on("uncaughtException", handleUncaughtException);
|
||||
process.on("unhandledRejection", handleUncaughtException);
|
||||
exceptionsHooked = true;
|
||||
}
|
||||
switch (data.type) {
|
||||
case "test":
|
||||
const { runner, file } = data.payload;
|
||||
if (!runner) {
|
||||
console.error(data);
|
||||
}
|
||||
const message: ParallelResultMessage = { type: "result", payload: handleTest(runner, file) };
|
||||
process.send!(message);
|
||||
break;
|
||||
case "close":
|
||||
process.exit(0);
|
||||
break;
|
||||
case "batch": {
|
||||
const items = data.payload;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const { runner, file } = items[i];
|
||||
if (!runner) {
|
||||
console.error(data);
|
||||
}
|
||||
let message: ParallelBatchProgressMessage | ParallelResultMessage;
|
||||
const payload = handleTest(runner, file);
|
||||
if (i === (items.length - 1)) {
|
||||
message = { type: "result", payload };
|
||||
}
|
||||
else {
|
||||
message = { type: "progress", payload };
|
||||
}
|
||||
process.send!(message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
process.on("uncaughtException", error => {
|
||||
const message: ParallelErrorMessage = { type: "error", payload: { error: error.message, stack: error.stack!, name: [...namestack] } };
|
||||
try {
|
||||
process.send!(message);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
if (!runUnitTests) {
|
||||
// ensure unit tests do not get run
|
||||
(global as any).describe = ts.noop;
|
||||
}
|
||||
else {
|
||||
initialized = true;
|
||||
shimMochaHarness();
|
||||
}
|
||||
|
||||
function handleTest(runner: TestRunnerKind | "unittest", file: string) {
|
||||
collectUnitTestsIfNeeded();
|
||||
if (runner === unittest) {
|
||||
return executeUnitTest(file);
|
||||
function unhookUncaughtExceptions() {
|
||||
if (exceptionsHooked) {
|
||||
process.removeListener("uncaughtException", handleUncaughtException);
|
||||
process.removeListener("unhandledRejection", handleUncaughtException);
|
||||
exceptionsHooked = false;
|
||||
}
|
||||
}
|
||||
|
||||
let exceptionsHooked = false;
|
||||
hookUncaughtExceptions();
|
||||
|
||||
// tslint:disable-next-line:variable-name - Capitalization is aligned with the global `Mocha` namespace for typespace/namespace references.
|
||||
const Mocha = require("mocha") as typeof import("mocha");
|
||||
|
||||
/**
|
||||
* Mixin helper.
|
||||
* @param base The base class constructor.
|
||||
* @param mixins The mixins to apply to the constructor.
|
||||
*/
|
||||
function mixin<T extends new (...args: any[]) => any>(base: T, ...mixins: ((klass: T) => T)[]) {
|
||||
for (const mixin of mixins) {
|
||||
base = mixin(base);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes in overrides for `resetTimeout` and `clearTimeout` to support parallel test execution in a worker.
|
||||
*/
|
||||
function Timeout<T extends typeof Mocha.Runnable>(base: T) {
|
||||
return class extends (base as typeof Mocha.Runnable) {
|
||||
resetTimeout() {
|
||||
this.clearTimeout();
|
||||
if (this.enableTimeouts()) {
|
||||
sendMessage({ type: "timeout", payload: { duration: this.timeout() || 1e9 } });
|
||||
this.timer = true;
|
||||
}
|
||||
}
|
||||
clearTimeout() {
|
||||
if (this.timer) {
|
||||
sendMessage({ type: "timeout", payload: { duration: "reset" } });
|
||||
this.timer = false;
|
||||
}
|
||||
}
|
||||
} as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes in an override for `clone` to support parallel test execution in a worker.
|
||||
*/
|
||||
function Clone<T extends typeof Mocha.Suite | typeof Mocha.Test>(base: T) {
|
||||
return class extends (base as new (...args: any[]) => { clone(): any; }) {
|
||||
clone() {
|
||||
const cloned = super.clone();
|
||||
Object.setPrototypeOf(cloned, this.constructor.prototype);
|
||||
return cloned;
|
||||
}
|
||||
} as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Mocha.Suite` subclass to support parallel test execution in a worker.
|
||||
*/
|
||||
class Suite extends mixin(Mocha.Suite, Clone) {
|
||||
_createHook(title: string, fn?: Mocha.Func | Mocha.AsyncFunc) {
|
||||
const hook = super._createHook(title, fn);
|
||||
Object.setPrototypeOf(hook, Hook.prototype);
|
||||
return hook;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Mocha.Hook` subclass to support parallel test execution in a worker.
|
||||
*/
|
||||
class Hook extends mixin(Mocha.Hook, Timeout) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Mocha.Test` subclass to support parallel test execution in a worker.
|
||||
*/
|
||||
class Test extends mixin(Mocha.Test, Timeout, Clone) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Shims a 'bdd'-style test interface to support parallel test execution in a worker.
|
||||
* @param rootSuite The root suite.
|
||||
* @param context The test context (usually the NodeJS `global` object).
|
||||
*/
|
||||
function shimTestInterface(rootSuite: Mocha.Suite, context: Mocha.MochaGlobals) {
|
||||
// tslint:disable-next-line:variable-name
|
||||
const suites = [rootSuite];
|
||||
context.before = (title: string | Mocha.Func | Mocha.AsyncFunc, fn?: Mocha.Func | Mocha.AsyncFunc) => { suites[0].beforeAll(title as string, fn); };
|
||||
context.after = (title: string | Mocha.Func | Mocha.AsyncFunc, fn?: Mocha.Func | Mocha.AsyncFunc) => { suites[0].afterAll(title as string, fn); };
|
||||
context.beforeEach = (title: string | Mocha.Func | Mocha.AsyncFunc, fn?: Mocha.Func | Mocha.AsyncFunc) => { suites[0].beforeEach(title as string, fn); };
|
||||
context.afterEach = (title: string | Mocha.Func | Mocha.AsyncFunc, fn?: Mocha.Func | Mocha.AsyncFunc) => { suites[0].afterEach(title as string, fn); };
|
||||
context.describe = context.context = ((title: string, fn: (this: Mocha.Suite) => void) => addSuite(title, fn)) as Mocha.SuiteFunction;
|
||||
context.describe.skip = context.xdescribe = context.xcontext = (title: string) => addSuite(title, /*fn*/ undefined);
|
||||
context.describe.only = (title: string, fn?: (this: Mocha.Suite) => void) => addSuite(title, fn);
|
||||
context.it = context.specify = ((title: string | Mocha.Func | Mocha.AsyncFunc, fn?: Mocha.Func | Mocha.AsyncFunc) => addTest(title, fn)) as Mocha.TestFunction;
|
||||
context.it.skip = context.xit = context.xspecify = (title: string | Mocha.Func | Mocha.AsyncFunc) => addTest(typeof title === "function" ? title.name : title, /*fn*/ undefined);
|
||||
context.it.only = (title: string | Mocha.Func | Mocha.AsyncFunc, fn?: Mocha.Func | Mocha.AsyncFunc) => addTest(title, fn);
|
||||
|
||||
function addSuite(title: string, fn: ((this: Mocha.Suite) => void) | undefined): Mocha.Suite {
|
||||
const suite = new Suite(title, suites[0].ctx);
|
||||
suites[0].addSuite(suite);
|
||||
suite.pending = !fn;
|
||||
suites.unshift(suite);
|
||||
if (fn) {
|
||||
fn.call(suite);
|
||||
}
|
||||
suites.shift();
|
||||
return suite;
|
||||
}
|
||||
|
||||
function addTest(title: string | Mocha.Func | Mocha.AsyncFunc, fn: Mocha.Func | Mocha.AsyncFunc | undefined): Mocha.Test {
|
||||
if (typeof title === "function") fn = title, title = fn.name;
|
||||
const test = new Test(title, suites[0].pending ? undefined : fn);
|
||||
suites[0].addTest(test);
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the tests in the requested task.
|
||||
*/
|
||||
function runTests(task: Task, fn: (payload: TaskResult) => void) {
|
||||
if (task.runner === "unittest") {
|
||||
return runUnitTests(task, fn);
|
||||
}
|
||||
else {
|
||||
if (!runners.has(runner)) {
|
||||
runners.set(runner, createRunner(runner));
|
||||
return runFileTests(task, fn);
|
||||
}
|
||||
}
|
||||
|
||||
function runUnitTests(task: UnitTestTask, fn: (payload: TaskResult) => void) {
|
||||
if (!unitTestSuiteMap && unitTestSuite.suites.length) {
|
||||
unitTestSuiteMap = ts.createMap<Mocha.Suite>();
|
||||
for (const suite of unitTestSuite.suites) {
|
||||
unitTestSuiteMap.set(suite.title, suite);
|
||||
}
|
||||
const instance = runners.get(runner)!;
|
||||
instance.tests = [file];
|
||||
return { ...resetShimHarnessAndExecute(instance), runner, file };
|
||||
}
|
||||
|
||||
if (!unitTestSuiteMap) {
|
||||
throw new Error(`Asked to run unit test ${task.file}, but no unit tests were discovered!`);
|
||||
}
|
||||
|
||||
const suite = unitTestSuiteMap.get(task.file);
|
||||
if (!suite) {
|
||||
throw new Error(`Unit test with name "${task.file}" was asked to be run, but such a test does not exist!`);
|
||||
}
|
||||
|
||||
const root = new Suite("", new Mocha.Context());
|
||||
root.timeout(globalTimeout || 40_000);
|
||||
root.addSuite(suite);
|
||||
Object.setPrototypeOf(suite.ctx, root.ctx);
|
||||
|
||||
runSuite(task, suite, payload => {
|
||||
suite.parent = unitTestSuite;
|
||||
Object.setPrototypeOf(suite.ctx, unitTestSuite.ctx);
|
||||
fn(payload);
|
||||
});
|
||||
}
|
||||
|
||||
function runFileTests(task: RunnerTask, fn: (result: TaskResult) => void) {
|
||||
let instance = runners.get(task.runner);
|
||||
if (!instance) runners.set(task.runner, instance = createRunner(task.runner));
|
||||
instance.tests = [task.file];
|
||||
|
||||
const suite = new Suite("", new Mocha.Context());
|
||||
suite.timeout(globalTimeout || 40_000);
|
||||
|
||||
shimTestInterface(suite, global);
|
||||
instance.initializeTests();
|
||||
|
||||
runSuite(task, suite, fn);
|
||||
}
|
||||
|
||||
function runSuite(task: Task, suite: Mocha.Suite, fn: (result: TaskResult) => void) {
|
||||
const errors: ErrorInfo[] = [];
|
||||
const passes: TestInfo[] = [];
|
||||
const start = +new Date();
|
||||
const runner = new Mocha.Runner(suite, /*delay*/ false);
|
||||
const uncaught = (err: any) => runner.uncaught(err);
|
||||
|
||||
runner
|
||||
.on("start", () => {
|
||||
unhookUncaughtExceptions(); // turn off global uncaught handling
|
||||
process.on("unhandledRejection", uncaught); // turn on unhandled rejection handling (not currently handled in mocha)
|
||||
})
|
||||
.on("pass", (test: Mocha.Test) => {
|
||||
passes.push({ name: test.titlePath() });
|
||||
})
|
||||
.on("fail", (test: Mocha.Test | Mocha.Hook, err: any) => {
|
||||
errors.push({ name: test.titlePath(), error: err.message, stack: err.stack });
|
||||
})
|
||||
.on("end", () => {
|
||||
process.removeListener("unhandledRejection", uncaught);
|
||||
hookUncaughtExceptions();
|
||||
})
|
||||
.run(() => {
|
||||
fn({ task, errors, passes, passing: passes.length, duration: +new Date() - start });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a message received from the host is well-formed.
|
||||
*/
|
||||
function validateHostMessage(message: ParallelHostMessage) {
|
||||
switch (message.type) {
|
||||
case "test": return validateTest(message.payload);
|
||||
case "batch": return validateBatch(message.payload);
|
||||
case "close": return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unittest: "unittest" = "unittest";
|
||||
let unitTests: {[name: string]: MochaCallback};
|
||||
function collectUnitTestsIfNeeded() {
|
||||
if (!unitTests && testList.length) {
|
||||
unitTests = {};
|
||||
for (const test of testList) {
|
||||
unitTests[test.name] = test.callback;
|
||||
/**
|
||||
* Validates a test task is well formed.
|
||||
*/
|
||||
function validateTest(task: Task) {
|
||||
return !!task && !!task.runner && !!task.file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a batch of test tasks are well formed.
|
||||
*/
|
||||
function validateBatch(tasks: Task[]) {
|
||||
return !!tasks && Array.isArray(tasks) && tasks.length > 0 && tasks.every(validateTest);
|
||||
}
|
||||
|
||||
function processHostMessage(message: ParallelHostMessage) {
|
||||
if (!validateHostMessage(message)) {
|
||||
console.log("Invalid message:", message);
|
||||
return;
|
||||
}
|
||||
testList.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function executeUnitTest(name: string) {
|
||||
if (!unitTests) {
|
||||
throw new Error(`Asked to run unit test ${name}, but no unit tests were discovered!`);
|
||||
switch (message.type) {
|
||||
case "test": return processTest(message.payload, /*last*/ true);
|
||||
case "batch": return processBatch(message.payload);
|
||||
case "close": return process.exit(0);
|
||||
}
|
||||
}
|
||||
if (unitTests[name]) {
|
||||
errors = [];
|
||||
passes = [];
|
||||
passing = 0;
|
||||
const start = +(new Date());
|
||||
executeSuiteCallback(name, unitTests[name]);
|
||||
delete unitTests[name];
|
||||
return { file: name, runner: unittest, errors, passes, passing, duration: +(new Date()) - start };
|
||||
|
||||
function processTest(task: Task, last: boolean, fn?: () => void) {
|
||||
runTests(task, payload => {
|
||||
sendMessage(last ? { type: "result", payload } : { type: "progress", payload });
|
||||
if (fn) fn();
|
||||
});
|
||||
}
|
||||
throw new Error(`Unit test with name "${name}" was asked to be run, but such a test does not exist!`);
|
||||
|
||||
function processBatch(tasks: Task[], fn?: () => void) {
|
||||
const next = () => {
|
||||
const task = tasks.shift();
|
||||
if (task) return processTest(task, tasks.length === 0, next);
|
||||
if (fn) fn();
|
||||
};
|
||||
next();
|
||||
}
|
||||
|
||||
function handleUncaughtException(err: any) {
|
||||
const error = err instanceof Error ? err : new Error("" + err);
|
||||
sendMessage({ type: "error", payload: { error: error.message, stack: error.stack! } });
|
||||
}
|
||||
|
||||
function sendMessage(message: ParallelClientMessage) {
|
||||
process.send!(message);
|
||||
}
|
||||
|
||||
// A cache of test harness Runner instances.
|
||||
const runners = ts.createMap<RunnerBase>();
|
||||
|
||||
// The root suite for all unit tests.
|
||||
let unitTestSuite: Suite;
|
||||
let unitTestSuiteMap: ts.Map<Mocha.Suite>;
|
||||
|
||||
if (runUnitTests) {
|
||||
unitTestSuite = new Suite("", new Mocha.Context());
|
||||
unitTestSuite.timeout(globalTimeout || 40_000);
|
||||
shimTestInterface(unitTestSuite, global);
|
||||
}
|
||||
else {
|
||||
// ensure unit tests do not get run
|
||||
shimNoopTestInterface(global);
|
||||
}
|
||||
|
||||
process.on("message", processHostMessage);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче