Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace exec with spawn in performance runner script #62270

Closed
wants to merge 9 commits into from
Closed
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
58 changes: 40 additions & 18 deletions bin/plugin/commands/performance.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const SimpleGit = require( 'simple-git' );
*/
const { formats, log } = require( '../lib/logger' );
const {
runShellScript,
spawn,
readJSONFile,
askForConfirmation,
getFilesFromDir,
Expand Down Expand Up @@ -158,14 +158,22 @@ function printStats( m, s ) {
* @param {string} runKey Unique identifier for the test run.
*/
async function runTestSuite( testSuite, testRunnerDir, runKey ) {
await runShellScript(
`npm run test:performance -- ${ testSuite }`,
testRunnerDir,
await spawn(
'node',
[
'./packages/scripts/scripts/test-playwright.js',
'--config',
'test/performance/playwright.config.ts',
testSuite,
],
{
...process.env,
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1',
WP_ARTIFACTS_PATH: ARTIFACTS_PATH,
RESULTS_ID: runKey,
cwd: testRunnerDir,
env: {
...process.env,
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1',
WP_ARTIFACTS_PATH: ARTIFACTS_PATH,
RESULTS_ID: runKey,
},
}
);
}
Expand Down Expand Up @@ -329,7 +337,10 @@ async function runPerformanceTests( branches, options ) {
const testRunnerDir = path.join( baseDir + '/tests' );

logAtIndent( 2, 'Copying source to:', formats.success( testRunnerDir ) );
await runShellScript( `cp -R ${ sourceDir } ${ testRunnerDir }` );
await spawn( 'cp', [ '-R', sourceDir, testRunnerDir ], {
shell: true,
stdio: 'ignore',
} );

logAtIndent(
2,
Expand All @@ -340,9 +351,13 @@ async function runPerformanceTests( branches, options ) {
await SimpleGit( testRunnerDir ).raw( 'checkout', testRunnerBranch );

logAtIndent( 2, 'Installing dependencies and building' );
await runShellScript(
`bash -c "source $HOME/.nvm/nvm.sh && nvm install && npm ci && npx playwright install chromium --with-deps && npm run build:packages"`,
testRunnerDir
await spawn(
'bash',
[
'-c',
'"source $HOME/.nvm/nvm.sh && nvm install && npm ci && npx playwright install chromium --with-deps && npm run build:packages"',
],
{ cwd: testRunnerDir, shell: true, stdio: 'ignore' }
);

logAtIndent( 1, 'Setting up test environments' );
Expand Down Expand Up @@ -376,16 +391,23 @@ async function runPerformanceTests( branches, options ) {
const buildDir = path.join( envDir, 'plugin' );

logAtIndent( 3, 'Copying source to:', formats.success( buildDir ) );
await runShellScript( `cp -R ${ sourceDir } ${ buildDir }` );
await spawn( 'cp', [ '-R', sourceDir, buildDir ], {
shell: true,
stdio: 'ignore',
} );

logAtIndent( 3, 'Checking out:', formats.success( branch ) );
// @ts-ignore
await SimpleGit( buildDir ).raw( 'checkout', branch );

logAtIndent( 3, 'Installing dependencies and building' );
await runShellScript(
`bash -c "source $HOME/.nvm/nvm.sh && nvm install && npm ci && npm run build"`,
buildDir
await spawn(
'bash',
[
'-c',
'"source $HOME/.nvm/nvm.sh && nvm install && npm ci && npm run build"',
],
{ cwd: buildDir, shell: true, stdio: 'ignore' }
);

const wpEnvConfigPath = path.join( envDir, '.wp-env.json' );
Expand Down Expand Up @@ -479,13 +501,13 @@ async function runPerformanceTests( branches, options ) {
const envDir = branchDirs[ branch ];

logAtIndent( 3, 'Starting environment' );
await runShellScript( `${ wpEnvPath } start`, envDir );
await spawn( wpEnvPath, [ 'start' ], envDir );

logAtIndent( 3, 'Running tests' );
await runTestSuite( testSuite, testRunnerDir, runKey );

logAtIndent( 3, 'Stopping environment' );
await runShellScript( `${ wpEnvPath } stop`, envDir );
await spawn( wpEnvPath, [ 'stop' ], envDir );
}
}
}
Expand Down
77 changes: 50 additions & 27 deletions bin/plugin/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,59 @@ const { log, formats } = require( './logger' );
/**
* Utility to run a child script
*
* @typedef {NodeJS.ProcessEnv} Env
*
* @param {string} script Script to run.
* @param {string=} cwd Working directory.
* @param {Env=} env Additional environment variables to pass to the script.
* @param {string} command
* @param {string[]} args
* @param {import('child_process').SpawnOptions} options
*/
function runShellScript( script, cwd, env = {} ) {
async function spawn( command, args, options ) {
return new Promise( ( resolve, reject ) => {
childProcess.exec(
script,
{
cwd,
env: {
NO_CHECKS: 'true',
PATH: process.env.PATH,
HOME: process.env.HOME,
USER: process.env.USER,
...env,
},
const child = childProcess.spawn( command, args, {
env: {
NO_CHECKS: 'true',
PATH: process.env.PATH,
HOME: process.env.HOME,
USER: process.env.USER,
...options.env,
},
function ( error, stdout, stderr ) {
if ( error ) {
console.log( stdout ); // Sometimes the error message is thrown via stdout.
console.log( stderr );
reject( error );
} else {
resolve( true );
}
shell: false,
stdio: 'inherit',
...options,
} );

let stdout = '';
let stderr = '';

if ( child.stdout ) {
child.stdout.on( 'data', ( data ) => {
const dataStr = data.toString();
stdout += dataStr;
process.stdout.write( dataStr ); // Print to console in real-time
} );
}

if ( child.stderr ) {
child.stderr.on( 'data', ( data ) => {
const dataStr = data.toString();
stderr += dataStr;
process.stderr.write( dataStr ); // Print to console in real-time
} );
}

child.on( 'close', ( code ) => {
if ( code === 0 ) {
resolve( { stdout, stderr } );
} else {
reject(
new Error(
`Process ${ command } exited with code ${ code }: ${ stderr }`
)
);
}
);
} );

child.on( 'error', ( error ) => {
reject( `Process ${ command } ended with error: ${ error }` );
} );
} );
}

Expand Down Expand Up @@ -144,7 +167,7 @@ module.exports = {
askForConfirmation,
runStep,
readJSONFile,
runShellScript,
spawn,
getRandomTemporaryPath,
getFilesFromDir,
};
36 changes: 24 additions & 12 deletions packages/e2e-test-utils-playwright/src/request-utils/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import * as fs from 'fs/promises';
import { dirname } from 'path';
import { expect } from '@playwright/test';
import type { APIRequestContext } from '@playwright/test';

/**
Expand All @@ -22,18 +23,29 @@ function splitRequestsToChunks( requests: BatchRequest[], chunkSize: number ) {
}

async function getAPIRootURL( request: APIRequestContext ) {
// Discover the API root url using link header.
// See https://developer.wordpress.org/rest-api/using-the-rest-api/discovery/#link-header
const response = await request.head( WP_BASE_URL );
const links = response.headers().link;
const restLink = links?.match( /<([^>]+)>; rel="https:\/\/api\.w\.org\/"/ );

if ( ! restLink ) {
throw new Error( `Failed to discover REST API endpoint.
Link header: ${ links }` );
}

const [ , rootURL ] = restLink;
let restLink: unknown = null;

await expect
.poll(
async () => {
// Discover the API root url using link header.
// See https://developer.wordpress.org/rest-api/using-the-rest-api/discovery/#link-header
const response = await request.head( WP_BASE_URL );
const links = response.headers().link;
restLink = links?.match(
/<([^>]+)>; rel="https:\/\/api\.w\.org\/"/
);

return restLink;
},
{
message: 'Failed to discover REST API endpoint.',
timeout: 60_000, // 1 minute.
}
)
.not.toBeFalsy();

const [ , rootURL ] = restLink as RegExpMatchArray;

return rootURL;
}
Expand Down
4 changes: 1 addition & 3 deletions test/performance/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ process.env.ASSETS_PATH = path.join( __dirname, 'assets' );

const config = defineConfig( {
...baseConfig,
reporter: process.env.CI
? './config/performance-reporter.ts'
: [ [ 'list' ], [ './config/performance-reporter.ts' ] ],
reporter: [ [ 'list' ], [ './config/performance-reporter.ts' ] ],
forbidOnly: !! process.env.CI,
fullyParallel: false,
retries: 0,
Expand Down
Loading