forked from eclipse-theia/theia
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
When flag is enabled the plugin deployment will be delayed until a frontend is connected. This way the deployment of the plugins cannot slow down the fronend loading time. TODO: - remove hardcoded time to wait for connection to finish. - discuss name of flag
- Loading branch information
Showing
9 changed files
with
468 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2017 TypeFox and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
import { injectable} from 'inversify'; | ||
import * as events from 'events'; | ||
import { timeout } from '../common/promise-util'; | ||
|
||
@injectable() | ||
export class ClientConnectionNotifier { | ||
|
||
static readonly CLIENT_CONNECTED = 'clientConnected'; | ||
|
||
readonly connectionEvent = new events.EventEmitter(); | ||
|
||
currentlyConnected = false; | ||
|
||
async clientConnected(): Promise<void> { | ||
if (!this.currentlyConnected) { | ||
this.currentlyConnected = true; | ||
// wait for the connection process to finish TODO: find useful mechanism to avoid hardcoded time | ||
await timeout(5000); | ||
this.connectionEvent.emit(ClientConnectionNotifier.CLIENT_CONNECTED); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2021 STMicroelectronics and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
// @ts-check | ||
const fsx = require('fs-extra'); | ||
const { resolve } = require('path'); | ||
const { spawn, ChildProcess } = require('child_process'); | ||
const { delay, lcp, isLCP, measure } = require('./common-performance'); | ||
const traceConfigTemplate = require('./electron-trace-config.json'); | ||
const { exit } = require('process'); | ||
|
||
const basePath = resolve(__dirname, '../..'); | ||
const profilesPath = resolve(__dirname, './profiles/'); | ||
const electronExample = resolve(basePath, 'examples/electron'); | ||
const theia = resolve(electronExample, 'node_modules/.bin/theia'); | ||
|
||
let name = 'Electron Frontend Startup'; | ||
let folder = 'electron'; | ||
let runs = 10; | ||
let workspace = resolve('./workspace'); | ||
let debugging = false; | ||
|
||
(async () => { | ||
let defaultWorkspace = true; | ||
|
||
const yargs = require('yargs'); | ||
const args = yargs(process.argv.slice(2)).option('name', { | ||
alias: 'n', | ||
desc: 'A name for the test suite', | ||
type: 'string', | ||
default: name | ||
}).option('folder', { | ||
alias: 'f', | ||
desc: 'Name of a folder within the "profiles" folder in which to collect trace logs', | ||
type: 'string', | ||
default: folder | ||
}).option('runs', { | ||
alias: 'r', | ||
desc: 'The number of times to run the test', | ||
type: 'number', | ||
default: runs | ||
}).option('workspace', { | ||
alias: 'w', | ||
desc: 'Path to a Theia workspace to open', | ||
type: 'string', | ||
default: workspace | ||
}).option('debug', { | ||
alias: 'X', | ||
desc: 'Whether to log debug information', | ||
boolean: true | ||
}).wrap(Math.min(120, yargs.terminalWidth())).argv; | ||
|
||
if (args.name) { | ||
name = args.name.toString(); | ||
} | ||
if (args.folder) { | ||
folder = args.folder.toString(); | ||
} | ||
if (args.workspace) { | ||
workspace = args.workspace.toString(); | ||
if (resolve(workspace) !== workspace) { | ||
console.log('Workspace path must be an absolute path:', workspace); | ||
exit(1); | ||
} | ||
defaultWorkspace = false; | ||
} | ||
if (args.runs) { | ||
runs = parseInt(args.runs.toString()); | ||
} | ||
debugging = args.debug; | ||
|
||
// Verify that the application exists | ||
const indexHTML = resolve(electronExample, 'src-gen/frontend/index.html'); | ||
if (!fsx.existsSync(indexHTML)) { | ||
console.error('Electron example app does not exist. Please build it before running this script.'); | ||
process.exit(1); | ||
} | ||
|
||
if (defaultWorkspace) { | ||
// Ensure that it exists | ||
fsx.ensureDirSync(workspace); | ||
} | ||
|
||
await measurePerformance(); | ||
})(); | ||
|
||
async function measurePerformance() { | ||
fsx.emptyDirSync(resolve(profilesPath, folder)); | ||
const traceConfigPath = resolve(profilesPath, folder, 'trace-config.json'); | ||
|
||
/** | ||
* Generate trace config from the template. | ||
* @param {number} runNr | ||
* @returns {string} the output trace file path | ||
*/ | ||
const traceConfigGenerator = (runNr) => { | ||
const traceConfig = { ...traceConfigTemplate }; | ||
const traceFilePath = resolve(profilesPath, folder, `${runNr}.json`); | ||
traceConfig.result_file = traceFilePath | ||
fsx.writeFileSync(traceConfigPath, JSON.stringify(traceConfig, undefined, 2), 'utf-8'); | ||
return traceFilePath; | ||
}; | ||
|
||
const exitHandler = (andExit = false) => { | ||
return () => { | ||
if (electron && !electron.killed) { | ||
process.kill(-electron.pid, 'SIGINT'); | ||
} | ||
if (andExit) { | ||
process.exit(); | ||
} | ||
} | ||
}; | ||
|
||
// Be sure not to leave a detached Electron child process | ||
process.on('exit', exitHandler()); | ||
process.on('SIGINT', exitHandler(true)); | ||
process.on('SIGTERM', exitHandler(true)); | ||
|
||
let electron; | ||
|
||
/** @type import('./common-performance').TestFunction */ | ||
const testScenario = async (runNr) => { | ||
const traceFile = traceConfigGenerator(runNr); | ||
electron = await launchElectron(traceConfigPath); | ||
|
||
electron.stderr.on('data', data => analyzeStderr(data.toString())); | ||
|
||
// Wait long enough to be sure that tracing has finished. Kill the process group | ||
// because the 'theia' child process was detached | ||
await delay(traceConfigTemplate.startup_duration * 1_000 * 3 / 2) | ||
.then(() => electron.exitCode !== null || process.kill(-electron.pid, 'SIGINT')); | ||
electron = undefined; | ||
return traceFile; | ||
}; | ||
|
||
measure(name, lcp, runs, testScenario, hasNonzeroTimestamp, isLCP); | ||
} | ||
|
||
/** | ||
* Launch the Electron app as a detached child process with tracing configured to start | ||
* immediately upon launch. The child process is detached because otherwise the attempt | ||
* to signal it to terminate when the test run is complete will not terminate the entire | ||
* process tree but only the root `theia` process, leaving the electron app instance | ||
* running until eventually this script itself exits. | ||
* | ||
* @param {string} traceConfigPath the path to the tracing configuration file with which to initiate tracing | ||
* @returns {Promise<ChildProcess>} the Electron child process, if successfully launched | ||
*/ | ||
async function launchElectron(traceConfigPath) { | ||
const args = ['start', workspace, '--plugins=local-dir:../../plugins', `--trace-config-file=${traceConfigPath}`, `--fastStartup`]; | ||
if (process.platform === 'linux') { | ||
args.push('--headless'); | ||
} | ||
return spawn(theia, args, { cwd: electronExample, detached: true }); | ||
} | ||
|
||
function hasNonzeroTimestamp(traceEvent) { | ||
return traceEvent.hasOwnProperty('ts') // The traces don't have explicit nulls or undefineds | ||
&& traceEvent.ts > 0; | ||
} | ||
|
||
/** | ||
* Analyze a `chunk` of text on the standard error stream of the child process. | ||
* If running in debug mode, this will always at least print out the `chunk` to the console. | ||
* In addition, the text is analyzed to look for known conditions that will invalidate the | ||
* test procedure and cause the script to bail. These include: | ||
* | ||
* - the native browser modules not being built correctly for Electron | ||
* | ||
* @param {string} chunk a chunk of standard error text from the child process | ||
*/ | ||
function analyzeStderr(chunk) { | ||
if (debugging) { | ||
console.error('>', chunk.trimEnd()); | ||
} | ||
|
||
if (chunk.includes('Error: Module did not self-register')) { | ||
console.error('Native browser modules are not built properly. Please rebuild the workspace and try again.'); | ||
exit(1); | ||
} | ||
} |
Oops, something went wrong.