2017-02-16 14:25:17 +01:00
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Copyright ( c ) Microsoft Corporation . All rights reserved .
* Licensed under the MIT License . See License . txt in the project root for license information .
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
2020-12-17 19:16:42 +01:00
// mocha disables running through electron by default. Note that this must
// come before any mocha imports.
process . env . MOCHA _COLORS = '1' ;
2021-11-19 11:12:49 +01:00
const { app , BrowserWindow , ipcMain , crashReporter } = require ( 'electron' ) ;
const product = require ( '../../../product.json' ) ;
2017-02-16 14:25:17 +01:00
const { tmpdir } = require ( 'os' ) ;
2021-11-19 11:12:49 +01:00
const { existsSync , mkdirSync } = require ( 'fs' ) ;
2017-04-28 12:57:09 +02:00
const path = require ( 'path' ) ;
const mocha = require ( 'mocha' ) ;
const events = require ( 'events' ) ;
2020-08-10 11:20:34 +02:00
const MochaJUnitReporter = require ( 'mocha-junit-reporter' ) ;
2018-10-03 17:38:36 +02:00
const url = require ( 'url' ) ;
2021-04-27 01:31:26 +02:00
const net = require ( 'net' ) ;
2020-12-17 19:16:42 +01:00
const createStatsCollector = require ( 'mocha/lib/stats-collector' ) ;
2021-07-13 02:28:01 +02:00
const { applyReporter , importMochaReporter } = require ( '../reporter' ) ;
2017-02-16 14:25:17 +01:00
2020-07-21 09:39:34 +02:00
// Disable render process reuse, we still have
// non-context aware native modules in the renderer.
app . allowRendererProcessReuse = false ;
2017-02-16 14:25:17 +01:00
const optimist = require ( 'optimist' )
2017-04-12 15:40:08 +02:00
. describe ( 'grep' , 'only run tests matching <pattern>' ) . alias ( 'grep' , 'g' ) . alias ( 'grep' , 'f' ) . string ( 'grep' )
2017-02-16 14:49:59 +01:00
. describe ( 'run' , 'only run tests from <file>' ) . string ( 'run' )
2020-02-07 16:49:44 +01:00
. describe ( 'runGlob' , 'only run tests matching <file_pattern>' ) . alias ( 'runGlob' , 'glob' ) . alias ( 'runGlob' , 'runGrep' ) . string ( 'runGlob' )
2017-04-04 15:47:15 +02:00
. describe ( 'build' , 'run with build output (out-build)' ) . boolean ( 'build' )
2017-04-04 16:56:19 +02:00
. describe ( 'coverage' , 'generate coverage report' ) . boolean ( 'coverage' )
2017-04-12 15:40:08 +02:00
. describe ( 'debug' , 'open dev tools, keep window open, reuse app data' ) . string ( 'debug' )
2020-12-17 19:16:42 +01:00
. describe ( 'reporter' , 'the mocha reporter' ) . string ( 'reporter' ) . default ( 'reporter' , 'spec' )
2018-03-07 22:03:07 +01:00
. describe ( 'reporter-options' , 'the mocha reporter options' ) . string ( 'reporter-options' ) . default ( 'reporter-options' , '' )
2021-04-27 01:31:26 +02:00
. describe ( 'wait-server' , 'port to connect to and wait before running tests' )
2021-04-29 21:19:36 +02:00
. describe ( 'timeout' , 'timeout for tests' )
2021-11-19 11:12:49 +01:00
. describe ( 'crash-reporter-directory' , 'crash reporter directory' ) . string ( 'crash-reporter-directory' )
2018-06-12 17:24:23 +02:00
. describe ( 'tfs' ) . string ( 'tfs' )
2017-04-12 15:40:08 +02:00
. describe ( 'help' , 'show the help' ) . alias ( 'help' , 'h' ) ;
2017-02-16 14:25:17 +01:00
2017-02-16 14:49:59 +01:00
const argv = optimist . argv ;
2017-02-16 14:25:17 +01:00
2017-04-12 15:40:08 +02:00
if ( argv . help ) {
optimist . showHelp ( ) ;
process . exit ( 0 ) ;
}
2021-11-19 11:12:49 +01:00
let crashReporterDirectory = argv [ 'crash-reporter-directory' ] ;
if ( crashReporterDirectory ) {
crashReporterDirectory = path . normalize ( crashReporterDirectory ) ;
if ( ! path . isAbsolute ( crashReporterDirectory ) ) {
console . error ( ` The path ' ${ crashReporterDirectory } ' specified for --crash-reporter-directory must be absolute. ` ) ;
app . exit ( 1 ) ;
}
if ( ! existsSync ( crashReporterDirectory ) ) {
try {
mkdirSync ( crashReporterDirectory ) ;
} catch ( error ) {
console . error ( ` The path ' ${ crashReporterDirectory } ' specified for --crash-reporter-directory does not seem to exist or cannot be created. ` ) ;
app . exit ( 1 ) ;
}
}
// Crashes are stored in the crashDumps directory by default, so we
// need to change that directory to the provided one
console . log ( ` Found --crash-reporter-directory argument. Setting crashDumps directory to be ' ${ crashReporterDirectory } ' ` ) ;
app . setPath ( 'crashDumps' , crashReporterDirectory ) ;
crashReporter . start ( {
companyName : 'Microsoft' ,
productName : process . env [ 'VSCODE_DEV' ] ? ` ${ product . nameShort } Dev ` : product . nameShort ,
uploadToServer : false ,
compress : true
} ) ;
}
2017-04-04 12:19:44 +02:00
if ( ! argv . debug ) {
2021-11-19 11:12:49 +01:00
app . setPath ( 'userData' , path . join ( tmpdir ( ) , ` vscode-tests- ${ Date . now ( ) } ` ) ) ;
2017-02-16 16:07:15 +01:00
}
2017-02-16 14:25:17 +01:00
2017-04-28 12:57:09 +02:00
function deserializeSuite ( suite ) {
return {
2018-03-07 22:03:07 +01:00
root : suite . root ,
suites : suite . suites ,
tests : suite . tests ,
2017-04-28 12:57:09 +02:00
title : suite . title ,
2020-12-17 19:16:42 +01:00
titlePath : ( ) => suite . titlePath ,
2017-04-28 12:57:09 +02:00
fullTitle : ( ) => suite . fullTitle ,
timeout : ( ) => suite . timeout ,
retries : ( ) => suite . retries ,
slow : ( ) => suite . slow ,
2017-11-11 07:26:47 +01:00
bail : ( ) => suite . bail
2017-04-28 12:57:09 +02:00
} ;
}
function deserializeRunnable ( runnable ) {
return {
title : runnable . title ,
2020-12-17 19:16:42 +01:00
titlePath : ( ) => runnable . titlePath ,
2017-04-28 12:57:09 +02:00
fullTitle : ( ) => runnable . fullTitle ,
async : runnable . async ,
slow : ( ) => runnable . slow ,
speed : runnable . speed ,
2020-07-27 23:07:07 +02:00
duration : runnable . duration ,
currentRetry : ( ) => runnable . currentRetry
2017-04-28 12:57:09 +02:00
} ;
}
function deserializeError ( err ) {
const inspect = err . inspect ;
err . inspect = ( ) => inspect ;
2021-09-13 18:44:06 +02:00
// Unfortunately, mocha rewrites and formats err.actual/err.expected.
// This formatting is hard to reverse, so err.*JSON includes the unformatted value.
2021-04-21 08:13:08 +02:00
if ( err . actual ) {
err . actual = JSON . parse ( err . actual ) . value ;
2021-09-13 18:44:06 +02:00
err . actualJSON = err . actual ;
2021-04-21 08:13:08 +02:00
}
if ( err . expected ) {
err . expected = JSON . parse ( err . expected ) . value ;
2021-09-13 18:44:06 +02:00
err . expectedJSON = err . expected ;
2021-04-21 08:13:08 +02:00
}
2017-04-28 12:57:09 +02:00
return err ;
}
class IPCRunner extends events . EventEmitter {
constructor ( ) {
super ( ) ;
this . didFail = false ;
2021-03-29 21:05:34 +02:00
this . didEnd = false ;
2017-04-28 12:57:09 +02:00
ipcMain . on ( 'start' , ( ) => this . emit ( 'start' ) ) ;
2021-03-29 21:05:34 +02:00
ipcMain . on ( 'end' , ( ) => {
this . didEnd = true ;
this . emit ( 'end' ) ;
} ) ;
2017-04-28 12:57:09 +02:00
ipcMain . on ( 'suite' , ( e , suite ) => this . emit ( 'suite' , deserializeSuite ( suite ) ) ) ;
ipcMain . on ( 'suite end' , ( e , suite ) => this . emit ( 'suite end' , deserializeSuite ( suite ) ) ) ;
ipcMain . on ( 'test' , ( e , test ) => this . emit ( 'test' , deserializeRunnable ( test ) ) ) ;
ipcMain . on ( 'test end' , ( e , test ) => this . emit ( 'test end' , deserializeRunnable ( test ) ) ) ;
ipcMain . on ( 'hook' , ( e , hook ) => this . emit ( 'hook' , deserializeRunnable ( hook ) ) ) ;
ipcMain . on ( 'hook end' , ( e , hook ) => this . emit ( 'hook end' , deserializeRunnable ( hook ) ) ) ;
ipcMain . on ( 'pass' , ( e , test ) => this . emit ( 'pass' , deserializeRunnable ( test ) ) ) ;
ipcMain . on ( 'fail' , ( e , test , err ) => {
this . didFail = true ;
this . emit ( 'fail' , deserializeRunnable ( test ) , deserializeError ( err ) ) ;
} ) ;
ipcMain . on ( 'pending' , ( e , test ) => this . emit ( 'pending' , deserializeRunnable ( test ) ) ) ;
}
}
2017-02-16 14:25:17 +01:00
app . on ( 'ready' , ( ) => {
2019-06-24 16:47:26 +02:00
ipcMain . on ( 'error' , ( _ , err ) => {
if ( ! argv . debug ) {
console . error ( err ) ;
app . exit ( 1 ) ;
}
} ) ;
2021-04-06 08:55:31 +02:00
// We need to provide a basic `ISandboxConfiguration`
// for our preload script to function properly because
// some of our types depend on it (e.g. product.ts).
ipcMain . handle ( 'vscode:test-vscode-window-config' , async ( ) => {
return {
product : {
version : '1.x.y' ,
nameShort : 'Code - OSS Dev' ,
nameLong : 'Code - OSS Dev' ,
applicationName : 'code-oss' ,
dataFolderName : '.vscode-oss' ,
urlProtocol : 'code-oss' ,
}
} ;
} ) ;
2021-04-01 08:10:40 +02:00
2021-04-09 08:28:21 +02:00
// No-op since invoke the IPC as part of IIFE in the preload.
ipcMain . handle ( 'vscode:fetchShellEnv' , event => { } ) ;
2017-02-16 14:25:17 +01:00
const win = new BrowserWindow ( {
height : 600 ,
width : 800 ,
2017-03-24 10:28:46 +01:00
show : false ,
2017-03-24 10:17:48 +01:00
webPreferences : {
2020-05-27 21:47:39 +02:00
preload : path . join ( _ _dirname , '..' , '..' , '..' , 'src' , 'vs' , 'base' , 'parts' , 'sandbox' , 'electron-browser' , 'preload.js' ) , // ensure similar environment as VSCode as tests may depend on this
2021-04-01 08:10:40 +02:00
additionalArguments : [ ` --vscode-window-config=vscode:test-vscode-window-config ` ] ,
2020-07-20 10:30:20 +02:00
nodeIntegration : true ,
2021-04-09 08:28:21 +02:00
contextIsolation : false ,
2020-06-16 18:52:29 +02:00
enableWebSQL : false ,
2020-09-11 09:49:18 +02:00
spellcheck : false ,
2021-07-07 18:28:53 +02:00
nativeWindowOpen : true
2017-03-24 10:17:48 +01:00
}
2017-02-16 14:25:17 +01:00
} ) ;
win . webContents . on ( 'did-finish-load' , ( ) => {
2017-04-04 12:19:44 +02:00
if ( argv . debug ) {
2017-03-24 10:28:46 +01:00
win . show ( ) ;
2019-07-10 10:26:54 +02:00
win . webContents . openDevTools ( ) ;
2017-02-16 14:25:17 +01:00
}
2021-04-27 01:31:26 +02:00
if ( argv . waitServer ) {
waitForServer ( Number ( argv . waitServer ) ) . then ( sendRun ) ;
} else {
sendRun ( ) ;
}
2017-02-16 14:25:17 +01:00
} ) ;
2021-04-27 01:31:26 +02:00
async function waitForServer ( port ) {
let timeout ;
let socket ;
return new Promise ( resolve => {
socket = net . connect ( port , '127.0.0.1' ) ;
socket . on ( 'error' , e => {
console . error ( 'error connecting to waitServer' , e ) ;
resolve ( ) ;
} ) ;
socket . on ( 'close' , ( ) => {
resolve ( ) ;
} ) ;
timeout = setTimeout ( ( ) => {
console . error ( 'timed out waiting for before starting tests debugger' ) ;
resolve ( ) ;
2021-07-09 23:17:37 +02:00
} , 15000 ) ;
2021-04-27 01:31:26 +02:00
} ) . finally ( ( ) => {
if ( socket ) {
socket . end ( ) ;
}
clearTimeout ( timeout ) ;
} ) ;
}
function sendRun ( ) {
win . webContents . send ( 'run' , argv ) ;
}
2018-10-03 17:38:36 +02:00
win . loadURL ( url . format ( { pathname : path . join ( _ _dirname , 'renderer.html' ) , protocol : 'file:' , slashes : true } ) ) ;
2017-02-16 14:25:17 +01:00
2018-03-07 22:03:07 +01:00
const runner = new IPCRunner ( ) ;
2020-12-17 19:16:42 +01:00
createStatsCollector ( runner ) ;
2017-02-16 14:25:17 +01:00
2021-03-29 21:05:34 +02:00
// Handle renderer crashes, #117068
win . webContents . on ( 'render-process-gone' , ( evt , details ) => {
if ( ! runner . didEnd ) {
console . error ( ` Renderer process crashed with: ${ JSON . stringify ( details ) } ` ) ;
app . exit ( 1 ) ;
}
} ) ;
2018-03-07 22:03:07 +01:00
if ( argv . tfs ) {
2018-06-12 17:24:23 +02:00
new mocha . reporters . Spec ( runner ) ;
2020-08-10 11:20:34 +02:00
new MochaJUnitReporter ( runner , {
reporterOptions : {
testsuitesTitle : ` ${ argv . tfs } ${ process . platform } ` ,
2020-08-18 18:08:37 +02:00
mochaFile : process . env . BUILD _ARTIFACTSTAGINGDIRECTORY ? path . join ( process . env . BUILD _ARTIFACTSTAGINGDIRECTORY , ` test-results/ ${ process . platform } - ${ process . arch } - ${ argv . tfs . toLowerCase ( ) . replace ( /[^\w]/g , '-' ) } -results.xml ` ) : undefined
2020-08-10 11:20:34 +02:00
}
} ) ;
2018-03-07 22:03:07 +01:00
} else {
2020-12-17 19:16:42 +01:00
// mocha patches symbols to use windows escape codes, but it seems like
// Electron mangles these in its output.
if ( process . platform === 'win32' ) {
Object . assign ( importMochaReporter ( 'base' ) . symbols , {
ok : '+' ,
err : 'X' ,
dot : '.' ,
} ) ;
}
2018-03-07 22:03:07 +01:00
2021-07-13 02:28:01 +02:00
applyReporter ( runner , argv ) ;
2018-03-07 22:03:07 +01:00
}
2017-02-16 14:25:17 +01:00
2017-04-28 12:57:09 +02:00
if ( ! argv . debug ) {
ipcMain . on ( 'all done' , ( ) => app . exit ( runner . didFail ? 1 : 0 ) ) ;
}
2017-02-16 14:25:17 +01:00
} ) ;