chore(test runner): self profile under PWTEST_PROFILE_DIR (#7252)

`PWTEST_CLI_ALLOW_TEST_COMMAND=1 PWTEST_PROFILE_DIR=./profile node lib/cli/cli.js test -c <dir/config>`
This commit is contained in:
Dmitry Gozman 2021-06-21 14:49:43 -07:00 committed by GitHub
parent 143689e34f
commit 73a43fce60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 0 deletions

View file

@ -21,6 +21,7 @@ import * as fs from 'fs';
import * as path from 'path';
import type { Config } from './types';
import { Runner } from './runner';
import { stopProfiling, startProfiling } from './profiler';
const defaultTimeout = 30000;
const defaultReporter = process.env.CI ? 'dot' : 'list';
@ -80,6 +81,8 @@ export function addTestCommand(program: commander.CommanderStatic) {
}
async function runTests(args: string[], opts: { [key: string]: any }) {
await startProfiling();
const browserOpt = opts.browser ? opts.browser.toLowerCase() : 'chromium';
if (!['all', 'chromium', 'firefox', 'webkit'].includes(browserOpt))
throw new Error(`Unsupported browser "${opts.browser}", must be one of "all", "chromium", "firefox" or "webkit"`);
@ -129,6 +132,8 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
}
const result = await runner.run(!!opts.list, args.map(forceRegExp), opts.project || undefined);
await stopProfiling(undefined);
if (result === 'sigint')
process.exit(130);
process.exit(result === 'passed' ? 0 : 1);

48
src/test/profiler.ts Normal file
View file

@ -0,0 +1,48 @@
/**
* 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.
*/
import * as fs from 'fs';
import * as path from 'path';
const profileDir = process.env.PWTEST_PROFILE_DIR || '';
let session: import('inspector').Session;
export async function startProfiling() {
if (!profileDir)
return;
session = new (require('inspector').Session)();
session.connect();
await new Promise<void>(f => {
session.post('Profiler.enable', () => {
session.post('Profiler.start', f);
});
});
}
export async function stopProfiling(workerIndex: number | undefined) {
if (!profileDir)
return;
await new Promise<void>(f => session.post('Profiler.stop', async (err, { profile }) => {
if (!err) {
await fs.promises.mkdir(profileDir, { recursive: true });
await fs.promises.writeFile(path.join(profileDir, workerIndex === undefined ? 'runner.json' : 'worker' + workerIndex + '.json'), JSON.stringify(profile));
}
f();
}));
}

View file

@ -17,6 +17,7 @@
import { Console } from 'console';
import * as util from 'util';
import { RunPayload, TestOutputPayload, WorkerInitParams } from './ipc';
import { startProfiling, stopProfiling } from './profiler';
import { serializeError } from './util';
import { WorkerRunner } from './workerRunner';
@ -55,6 +56,7 @@ process.on('SIGINT',() => {});
process.on('SIGTERM',() => {});
let workerRunner: WorkerRunner;
let workerIndex: number | undefined;
process.on('unhandledRejection', (reason, promise) => {
if (workerRunner)
@ -69,6 +71,8 @@ process.on('uncaughtException', error => {
process.on('message', async message => {
if (message.method === 'init') {
const initParams = message.params as WorkerInitParams;
workerIndex = initParams.workerIndex;
startProfiling();
workerRunner = new WorkerRunner(initParams);
for (const event of ['testBegin', 'testEnd', 'done'])
workerRunner.on(event, sendMessageToParent.bind(null, event));
@ -96,6 +100,8 @@ async function gracefullyCloseAndExit() {
workerRunner.stop();
await workerRunner.cleanup();
}
if (workerIndex !== undefined)
await stopProfiling(workerIndex);
} catch (e) {
process.send!({ method: 'teardownError', params: { error: serializeError(e) } });
}