diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 030c36e1d50..cd63330d0ab 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -7,6 +7,7 @@ import { Workbench } from './areas/workbench/workbench'; import * as fs from 'fs'; import * as cp from 'child_process'; import { Code, spawn, SpawnOptions } from './vscode/code'; +import { Logger } from './logger'; export enum Quality { Dev, @@ -19,7 +20,6 @@ export interface ApplicationOptions extends SpawnOptions { workspacePath: string; workspaceFilePath: string; waitTime: number; - verbose: boolean; } export class Application { @@ -42,6 +42,10 @@ export class Application { return this._workbench; } + get logger(): Logger { + return this.options.logger; + } + get workspacePath(): string { return this.options.workspacePath; } @@ -103,7 +107,7 @@ export class Application { workspacePath: workspaceOrFolder, userDataDir: this.options.userDataDir, extensionsPath: this.options.extensionsPath, - verbose: this.options.verbose, + logger: this.options.logger, extraArgs }); diff --git a/test/smoke/src/logger.ts b/test/smoke/src/logger.ts new file mode 100644 index 00000000000..b36b502d6ae --- /dev/null +++ b/test/smoke/src/logger.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { appendFileSync, writeFileSync } from 'fs'; +import { format } from 'util'; +import { EOL } from 'os'; + +export interface Logger { + log(message: string, ...args: any[]): void; +} + +export class ConsoleLogger implements Logger { + + log(message: string, ...args: any[]): void { + console.log('**', message, ...args); + } +} + +export class FileLogger implements Logger { + + constructor(private path: string) { + writeFileSync(path, ''); + } + + log(message: string, ...args: any[]): void { + const date = new Date().toISOString(); + appendFileSync(this.path, `[${date}] ${format(message, ...args)}${EOL}`); + } +} + +export class MultiLogger implements Logger { + + constructor(private loggers: Logger[]) { } + + log(message: string, ...args: any[]): void { + for (const logger of this.loggers) { + logger.log(message, ...args); + } + } +} \ No newline at end of file diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index ea4f1365bf2..8398e0d3944 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -27,7 +27,7 @@ import { setup as setupDataExtensionTests } from './areas/extensions/extensions. import { setup as setupTerminalTests } from './areas/terminal/terminal.test'; import { setup as setupDataMultirootTests } from './areas/multiroot/multiroot.test'; import { setup as setupDataLocalizationTests } from './areas/workbench/localization.test'; -import { polling } from './vscode/code'; +import { MultiLogger, Logger, ConsoleLogger, FileLogger } from './logger'; const tmpDir = tmp.dirSync({ prefix: 't' }) as { name: string; removeCallback: Function; }; const testDataPath = tmpDir.name; @@ -41,7 +41,8 @@ const opts = minimist(args, { 'wait-time', 'test-repo', 'keybindings', - 'screenshots' + 'screenshots', + 'log' ], boolean: [ 'verbose' @@ -238,6 +239,16 @@ async function setup(): Promise { } function createApp(quality: Quality): Application { + const loggers: Logger[] = []; + + if (opts.verbose) { + loggers.push(new ConsoleLogger()); + } + + if (opts.log) { + loggers.push(new FileLogger(opts.log)); + } + return new Application({ quality, codePath: opts.build, @@ -246,7 +257,7 @@ function createApp(quality: Quality): Application { extensionsPath, workspaceFilePath, waitTime: parseInt(opts['wait-time'] || '0') || 20, - verbose: opts.verbose + logger: new MultiLogger(loggers) }); } @@ -289,11 +300,23 @@ describe('Test', () => { const name = this.currentTest.fullTitle().replace(/[^a-z0-9\-]/ig, '_'); const screenshotPath = path.join(screenshotsPath, `${name}.png`); - console.log('Last poll message: ', polling.lastTimeoutMessage); + if (opts.log) { + app.logger.log('*** Scr eenshot recorded:', screenshotPath); + } + fs.writeFileSync(screenshotPath, buffer); }); } + if (opts.log) { + beforeEach(async function () { + const app = this.app as Application; + const title = this.currentTest.fullTitle(); + + app.logger.log('***', title.replace(/./g, '=')); + }); + } + setupDataLossTests(); setupDataExplorerTests(); setupDataPreferencesTests(); diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index 5240380e776..c60984c6441 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -8,6 +8,7 @@ import * as cp from 'child_process'; import * as os from 'os'; import { tmpName } from 'tmp'; import { IDriver, connect as connectDriver, IDisposable, IElement } from './driver'; +import { Logger } from '../logger'; const repoPath = path.join(__dirname, '../../../..'); @@ -57,13 +58,13 @@ function getBuildOutPath(root: string): string { } } -async function connect(child: cp.ChildProcess, outPath: string, handlePath: string, verbose: boolean): Promise { +async function connect(child: cp.ChildProcess, outPath: string, handlePath: string, logger: Logger): Promise { let errCount = 0; while (true) { try { const { client, driver } = await connectDriver(outPath, handlePath); - return new Code(child, client, driver, verbose); + return new Code(child, client, driver, logger); } catch (err) { if (++errCount > 50) { child.kill(); @@ -85,7 +86,7 @@ export interface SpawnOptions { workspacePath: string; userDataDir: string; extensionsPath: string; - verbose: boolean; + logger: Logger; extraArgs?: string[]; } @@ -127,22 +128,14 @@ export async function spawn(options: SpawnOptions): Promise { const spawnOptions: cp.SpawnOptions = {}; - if (options.verbose) { - spawnOptions.stdio = 'inherit'; - } - const child = cp.spawn(electronPath, args, spawnOptions); instances.add(child); child.once('exit', () => instances.delete(child)); - return connect(child, outPath, handle, options.verbose); + return connect(child, outPath, handle, options.logger); } -export const polling = { - lastTimeoutMessage: '' -}; - async function poll( fn: () => Promise, acceptFn: (result: T) => boolean, @@ -159,7 +152,6 @@ async function poll( let result; try { - polling.lastTimeoutMessage = timeoutMessage; result = await fn(); if (acceptFn(result)) { @@ -187,24 +179,20 @@ export class Code { private process: cp.ChildProcess, private client: IDisposable, driver: IDriver, - verbose: boolean + readonly logger: Logger ) { - if (verbose) { - this.driver = new Proxy(driver, { - get(target, prop, receiver) { - if (typeof target[prop] !== 'function') { - return target[prop]; - } - - return function (...args) { - console.log('** ', prop, ...args.filter(a => typeof a === 'string')); - return target[prop].apply(this, args); - }; + this.driver = new Proxy(driver, { + get(target, prop, receiver) { + if (typeof target[prop] !== 'function') { + return target[prop]; } - }); - } else { - this.driver = driver; - } + + return function (...args) { + logger.log(`${prop}`, ...args.filter(a => typeof a === 'string')); + return target[prop].apply(this, args); + }; + } + }); } async capturePage(): Promise {