@@ -16,6 +16,8 @@ import type { BenchmarkResult } from "../ResultTypes";
1616import { fail } from "../assert.js" ;
1717import { Phase , runBenchmark } from "../runBenchmark" ;
1818
19+ import { emitResultsMocha } from "./runnerUtilities" ;
20+
1921/**
2022 * This is wrapper for Mocha's it function that runs a performance benchmark.
2123 *
@@ -64,72 +66,92 @@ export function supportParentProcess<
6466 const itFunction = args . only === true ? it . only : it ;
6567 const test = itFunction ( args . title , async ( ) => {
6668 if ( isParentProcess ) {
67- // Instead of running the benchmark in this process, create a new process.
68- // See {@link isParentProcess } for why.
69- // Launch new process, with:
70- // - mocha filter to run only this test.
71- // - --parentProcess flag removed.
72- // - --childProcess flag added (so data will be returned via stdout as json)
73-
74- // Pull the command (Node.js most likely) out of the first argument since spawnSync takes it separately.
75- const command = process . argv0 ?? fail ( "there must be a command" ) ;
76-
77- // We expect all node-specific flags to be present in execArgv so they can be passed to the child process.
78- // At some point mocha was processing the expose-gc flag itself and not passing it here, unless explicitly
79- // put in mocha's --node-option flag.
80- const childArgs = [ ...process . execArgv , ...process . argv . slice ( 1 ) ] ;
81- const processFlagIndex = childArgs . indexOf ( "--parentProcess" ) ;
82- childArgs [ processFlagIndex ] = "--childProcess" ;
83-
84- // Remove arguments for any existing test filters.
85- for ( const flag of [ "--grep" , "--fgrep" ] ) {
86- const flagIndex = childArgs . indexOf ( flag ) ;
87- if ( flagIndex > 0 ) {
88- // Remove the flag, and the argument after it (all these flags take one argument)
89- childArgs . splice ( flagIndex , 2 ) ;
90- }
91- }
69+ await emitResultsMocha ( async ( ) => {
70+ try {
71+ // Instead of running the benchmark in this process, create a new process.
72+ // See {@link isParentProcess } for why.
73+ // Launch new process, with:
74+ // - mocha filter to run only this test.
75+ // - --parentProcess flag removed.
76+ // - --childProcess flag added (so data will be returned via stdout as json)
9277
93- // Add test filter so child process only run the current test.
94- childArgs . push ( "--fgrep" , test . fullTitle ( ) ) ;
95-
96- // Remove arguments for debugging if they're present; in order to debug child processes we need
97- // to specify a new debugger port for each, or they'll fail to start. Doable, but leaving it out
98- // of scope for now.
99- let inspectArgIndex : number = - 1 ;
100- while (
101- ( inspectArgIndex = childArgs . findIndex ( ( x ) => x . match ( / ^ ( - - i n s p e c t | - - d e b u g ) .* / ) ) ) >=
102- 0
103- ) {
104- childArgs . splice ( inspectArgIndex , 1 ) ;
105- }
78+ // Pull the command (Node.js most likely) out of the first argument since spawnSync takes it separately.
79+ const command = process . argv0 ?? fail ( "there must be a command" ) ;
10680
107- // Do this import only if isParentProcess to enable running in the web as long as isParentProcess is false.
108- const childProcess = await import ( "node:child_process" ) ;
109- const result = childProcess . spawnSync ( command , childArgs , { encoding : "utf8" } ) ;
81+ // We expect all node-specific flags to be present in execArgv so they can be passed to the child process.
82+ // At some point mocha was processing the expose-gc flag itself and not passing it here, unless explicitly
83+ // put in mocha's --node-option flag.
84+ const childArgs = [ ...process . execArgv , ...process . argv . slice ( 1 ) ] ;
85+ const processFlagIndex = childArgs . indexOf ( "--parentProcess" ) ;
86+ childArgs [ processFlagIndex ] = "--childProcess" ;
11087
111- if ( result . error ) {
112- fail ( `Child process reported an error: ${ result . error . message } ` ) ;
113- }
88+ // Remove arguments for any existing test filters.
89+ for ( const flag of [ "--grep" , "--fgrep" ] ) {
90+ const flagIndex = childArgs . indexOf ( flag ) ;
91+ if ( flagIndex > 0 ) {
92+ // Remove the flag, and the argument after it (all these flags take one argument)
93+ childArgs . splice ( flagIndex , 2 ) ;
94+ }
95+ }
11496
115- if ( result . stderr !== "" ) {
116- fail ( `Child process logged errors: ${ result . stderr } ` ) ;
117- }
97+ // Add test filter so child process only run the current test.
98+ childArgs . push ( "--fgrep" , test . fullTitle ( ) ) ;
11899
119- // Find the json blob in the child's output.
120- const output =
121- result . stdout . split ( "\n" ) . find ( ( s ) => s . startsWith ( "{" ) ) ??
122- fail ( `child process must output a json blob. Got:\n${ result . stdout } ` ) ;
100+ // Remove arguments for debugging if they're present; in order to debug child processes we need
101+ // to specify a new debugger port for each, or they'll fail to start. Doable, but leaving it out
102+ // of scope for now.
103+ let inspectArgIndex : number = - 1 ;
104+ while (
105+ ( inspectArgIndex = childArgs . findIndex ( ( x ) =>
106+ x . match ( / ^ ( - - i n s p e c t | - - d e b u g ) .* / ) ,
107+ ) ) >= 0
108+ ) {
109+ childArgs . splice ( inspectArgIndex , 1 ) ;
110+ }
123111
124- test . emit ( "benchmark end" , JSON . parse ( output ) ) ;
112+ // Do this import only if isParentProcess to enable running in the web as long as isParentProcess is false.
113+ const childProcess = await import ( "node:child_process" ) ;
114+ const result = childProcess . spawnSync ( command , childArgs , { encoding : "utf8" } ) ;
115+
116+ if ( result . error ) {
117+ fail ( `Child process reported an error: ${ result . error . message } ` ) ;
118+ }
119+
120+ if ( result . stderr !== "" ) {
121+ fail ( `Child process logged errors: ${ result . stderr } ` ) ;
122+ }
123+
124+ // Find the json blob in the child's output.
125+ const output =
126+ result . stdout . split ( "\n" ) . find ( ( s ) => s . startsWith ( "{" ) ) ??
127+ fail ( `child process must output a json blob. Got:\n${ result . stdout } ` ) ;
128+
129+ return { result : JSON . parse ( output ) as BenchmarkResult } ;
130+ } catch ( error ) {
131+ return {
132+ result : { error : ( error as Error ) . message } ,
133+ exception : error as Error ,
134+ } ;
135+ }
136+ } , test ) ;
125137 return ;
126138 }
127139
128- const stats = await args . run ( ) ;
129- // Create and run a benchmark if we are in perfMode, else run the passed in function normally
130- if ( isInPerformanceTestingMode ) {
131- test . emit ( "benchmark end" , stats ) ;
132- }
140+ // Only emit results in perfMode
141+ await ( isInPerformanceTestingMode
142+ ? emitResultsMocha (
143+ async ( ) =>
144+ args . run ( ) . then (
145+ ( result ) => ( { result } ) ,
146+ ( error ) => ( {
147+ result : { error : ( error as Error ) . message } ,
148+ exception : error as Error ,
149+ } ) ,
150+ ) ,
151+ test ,
152+ )
153+ : // In non-perf mode, just run the function without emitting
154+ args . run ( ) ) ;
133155 } ) ;
134156 return test ;
135157}
0 commit comments