@@ -30,66 +30,115 @@ export async function launch(options: LaunchOptions): Promise<{ serverProcess: C
3030 } ;
3131}
3232
33+ // --- Start Positron ---
34+ // Modified `launchServer` function to add support for multiple ports to enable parallel test
35+ // execution of browser tests. Also added helper functions: `getServerArgs`, `resolveServerLocation`,
36+ // and `startServer` to make this code easier to read.
3337async function launchServer ( options : LaunchOptions ) {
3438 const { userDataDir, codePath, extensionsPath, logger, logsPath } = options ;
3539 const serverLogsPath = join ( logsPath , 'server' ) ;
3640 const codeServerPath = codePath ?? process . env . VSCODE_REMOTE_SERVER_PATH ;
3741 const agentFolder = userDataDir ;
42+
3843 await measureAndLog ( ( ) => mkdirp ( agentFolder ) , `mkdirp(${ agentFolder } )` , logger ) ;
3944
4045 const env = {
4146 VSCODE_REMOTE_SERVER_PATH : codeServerPath ,
42- ...process . env
47+ ...process . env ,
4348 } ;
4449
50+ const maxRetries = 10 ;
51+ let serverProcess : ChildProcess | null = null ;
52+ let endpoint : string | undefined ;
53+
54+ for ( let attempts = 0 ; attempts < maxRetries ; attempts ++ ) {
55+ const currentPort = port ++ ;
56+ const args = getServerArgs ( currentPort , extensionsPath , agentFolder , serverLogsPath , options . verbose ) ;
57+ const serverLocation = resolveServerLocation ( codeServerPath , logger ) ;
58+
59+ logger . log ( `Attempting to start server on port ${ currentPort } ` ) ;
60+ logger . log ( `Command: '${ serverLocation } ' ${ args . join ( ' ' ) } ` ) ;
61+
62+ try {
63+ serverProcess = await startServer ( serverLocation , args , env , logger ) ;
64+ endpoint = await measureAndLog (
65+ ( ) => waitForEndpoint ( serverProcess ! , logger ) ,
66+ 'waitForEndpoint(serverProcess)' ,
67+ logger
68+ ) ;
69+
70+ logger . log ( `Server started successfully on port ${ currentPort } ` ) ;
71+ break ; // Exit loop on success
72+ } catch ( error ) {
73+ if ( ( error as Error ) . message . includes ( 'EADDRINUSE' ) ) {
74+ logger . log ( `Port ${ currentPort } is already in use. Retrying...` ) ;
75+ serverProcess ?. kill ( ) ;
76+ } else {
77+ throw error ; // Rethrow non-port-related errors
78+ }
79+ }
80+ }
81+
82+ if ( ! serverProcess || ! endpoint ) {
83+ throw new Error ( 'Failed to launch the server after multiple attempts.' ) ;
84+ }
85+
86+ return { serverProcess, endpoint } ;
87+ }
88+
89+ function getServerArgs (
90+ port : number ,
91+ extensionsPath : string ,
92+ agentFolder : string ,
93+ logsPath : string ,
94+ verbose ?: boolean
95+ ) : string [ ] {
4596 const args = [
4697 '--disable-telemetry' ,
4798 '--disable-workspace-trust' ,
48- `--port=${ port ++ } ` ,
99+ `--port=${ port } ` ,
49100 '--enable-smoke-test-driver' ,
50101 `--extensions-dir=${ extensionsPath } ` ,
51102 `--server-data-dir=${ agentFolder } ` ,
52103 '--accept-server-license-terms' ,
53- `--logsPath=${ serverLogsPath } ` ,
54- // --- Start Positron ---
55- `--connection-token` ,
56- `dev-token`
57- // --- End Positron ---
104+ `--logsPath=${ logsPath } ` ,
105+ '--connection-token' ,
106+ 'dev-token' ,
58107 ] ;
59108
60- if ( options . verbose ) {
109+ if ( verbose ) {
61110 args . push ( '--log=trace' ) ;
62111 }
63112
64- let serverLocation : string | undefined ;
113+ return args ;
114+ }
115+
116+ function resolveServerLocation ( codeServerPath : string | undefined , logger : Logger ) : string {
65117 if ( codeServerPath ) {
66118 const { serverApplicationName } = require ( join ( codeServerPath , 'product.json' ) ) ;
67- serverLocation = join ( codeServerPath , 'bin' , `${ serverApplicationName } ${ process . platform === 'win32' ? '.cmd' : '' } ` ) ;
68-
69- logger . log ( `Starting built server from '${ serverLocation } '` ) ;
70- } else {
71- serverLocation = join ( root , `scripts/code-server.${ process . platform === 'win32' ? 'bat' : 'sh' } ` ) ;
72-
73- logger . log ( `Starting server out of sources from '${ serverLocation } '` ) ;
119+ const serverLocation = join ( codeServerPath , 'bin' , `${ serverApplicationName } ${ process . platform === 'win32' ? '.cmd' : '' } ` ) ;
120+ logger . log ( `Using built server from '${ serverLocation } '` ) ;
121+ return serverLocation ;
74122 }
75123
76- logger . log ( `Storing log files into '${ serverLogsPath } '` ) ;
77-
78- logger . log ( `Command line: '${ serverLocation } ' ${ args . join ( ' ' ) } ` ) ;
79- const shell : boolean = ( process . platform === 'win32' ) ;
80- const serverProcess = spawn (
81- serverLocation ,
82- args ,
83- { env, shell }
84- ) ;
85-
86- logger . log ( `Started server for browser smoke tests (pid: ${ serverProcess . pid } )` ) ;
124+ const scriptPath = join ( root , `scripts/code-server.${ process . platform === 'win32' ? 'bat' : 'sh' } ` ) ;
125+ logger . log ( `Using source server from '${ scriptPath } '` ) ;
126+ return scriptPath ;
127+ }
87128
88- return {
89- serverProcess,
90- endpoint : await measureAndLog ( ( ) => waitForEndpoint ( serverProcess , logger ) , 'waitForEndpoint(serverProcess)' , logger )
91- } ;
129+ async function startServer (
130+ serverLocation : string ,
131+ args : string [ ] ,
132+ env : NodeJS . ProcessEnv ,
133+ logger : Logger
134+ ) : Promise < ChildProcess > {
135+ logger . log ( `Starting server: ${ serverLocation } ` ) ;
136+ const serverProcess = spawn ( serverLocation , args , { env, shell : process . platform === 'win32' } ) ;
137+ logger . log ( `Server started (pid: ${ serverProcess . pid } )` ) ;
138+ return serverProcess ;
92139}
140+ // --- End Positron ---
141+
93142
94143async function launchBrowser ( options : LaunchOptions , endpoint : string ) {
95144 const { logger, workspacePath, tracing, headless } = options ;
0 commit comments