Use promise.reject with an Error object instead of string, to ensure we get a stack.

Add test for bug in adapterProxy.
This commit is contained in:
Rob 2015-11-04 16:52:15 -08:00
Родитель 04fe65ec19
Коммит 9fda1f99a3
9 изменённых файлов: 73 добавлений и 29 удалений

Просмотреть файл

@ -2,7 +2,7 @@
* Copyright (C) Microsoft Corporation. All rights reserved. * Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/ *--------------------------------------------------------*/
import * as Utilities from '../webkit/utilities'; import * as utils from '../webkit/utilities';
export type EventHandler = (event: DebugProtocol.Event) => void; export type EventHandler = (event: DebugProtocol.Event) => void;
@ -15,7 +15,7 @@ export class AdapterProxy {
public dispatchRequest(request: DebugProtocol.Request): Promise<any> { public dispatchRequest(request: DebugProtocol.Request): Promise<any> {
if (!(request.command in this._debugAdapter)) { if (!(request.command in this._debugAdapter)) {
Promise.reject('unknowncommand'); return utils.errP('unknowncommand');
} }
return this.transformRequest(request) return this.transformRequest(request)
@ -50,7 +50,7 @@ export class AdapterProxy {
} }
const bodyTransformMethodName = request.command + 'Response'; const bodyTransformMethodName = request.command + 'Response';
const reversedTransformers = Utilities.reversedArr(this._requestTransformers); const reversedTransformers = utils.reversedArr(this._requestTransformers);
return reversedTransformers return reversedTransformers
// If the transformer implements this command, give it a chance to modify the args. Otherwise skip it // If the transformer implements this command, give it a chance to modify the args. Otherwise skip it
.filter(transformer => bodyTransformMethodName in transformer) .filter(transformer => bodyTransformMethodName in transformer)
@ -63,7 +63,7 @@ export class AdapterProxy {
* Pass the event back through the transformers in reverse. They modify the object in place. * Pass the event back through the transformers in reverse. They modify the object in place.
*/ */
private onAdapterEvent(event: DebugProtocol.Event): void { private onAdapterEvent(event: DebugProtocol.Event): void {
const reversedTransformers = Utilities.reversedArr(this._requestTransformers); const reversedTransformers = utils.reversedArr(this._requestTransformers);
reversedTransformers reversedTransformers
.filter(transformer => event.event in transformer) .filter(transformer => event.event in transformer)
.forEach( .forEach(

Просмотреть файл

@ -0,0 +1,29 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import * as assert from 'assert';
import * as testUtils from '../testUtils';
import {AdapterProxy} from '../../adapter/adapterProxy';
suite('AdapterProxy', () => {
setup(() => {
testUtils.setupUnhandledRejectionListener();
});
teardown(() => {
testUtils.removeUnhandledRejectionListener();
});
suite('request', () => {
test('if an unknown command is issued, dispatchRequest fails', () => {
const ap = new AdapterProxy(null, <any>{ registerEventHandler: () => { } }, null);
return ap.dispatchRequest(<any>{ command: 'abc' }).then(
() => assert.fail('Expected to fail'),
e => {
assert.equal(e.message, 'unknowncommand');
});
});
});
});

Просмотреть файл

@ -11,7 +11,17 @@ export function removeUnhandledRejectionListener(): void {
} }
function unhandledRejectionListener(reason, p) { function unhandledRejectionListener(reason, p) {
console.log(`ERROR!! Unhandled promise rejection: ${reason}`); console.log('*');
console.log('**');
console.log('***');
console.log('****');
console.log('*****');
console.log(`ERROR!! Unhandled promise rejection: ${reason}. A previous test may have failed but reported success.`);
console.log('*****');
console.log('****');
console.log('***');
console.log('**');
console.log('*');
} }
export class MockEvent implements DebugProtocol.Event { export class MockEvent implements DebugProtocol.Event {

Просмотреть файл

@ -156,11 +156,11 @@ suite('Utilities', () => {
}); });
test('when the function fails, it rejects', () => { test('when the function fails, it rejects', () => {
return Utilities.retryAsync(() => Promise.reject('fail'), /*timeoutMs=*/5) return Utilities.retryAsync(() => Utilities.errP('fail'), /*timeoutMs=*/5)
.then( .then(
() => assert.fail('This promise should fail'), () => assert.fail('This promise should fail'),
e => { e => {
assert.equal(e, 'fail'); assert.equal(e.message, 'Error: fail');
}); });
}); });
}); });

Просмотреть файл

@ -8,6 +8,7 @@ import {EventEmitter} from 'events';
import * as assert from 'assert'; import * as assert from 'assert';
import * as testUtils from '../testUtils'; import * as testUtils from '../testUtils';
import * as utils from '../../webkit/utilities';
/** Not mocked - use for type only */ /** Not mocked - use for type only */
import {WebKitDebugAdapter as _WebKitDebugAdapter} from '../../webkit/webKitDebugAdapter'; import {WebKitDebugAdapter as _WebKitDebugAdapter} from '../../webkit/webKitDebugAdapter';
@ -69,7 +70,7 @@ suite('WebKitDebugAdapter', () => {
}); });
test('if unsuccessful, the promise is rejected and an initialized event is not fired', done => { test('if unsuccessful, the promise is rejected and an initialized event is not fired', done => {
mockWebKitConnection.expects('attach').returns(Promise.reject('Testing attach failed')); mockWebKitConnection.expects('attach').returns(utils.errP('Testing attach failed'));
const wkda = instantiateWKDA(); const wkda = instantiateWKDA();
wkda.registerEventHandler((event: DebugProtocol.Event) => { wkda.registerEventHandler((event: DebugProtocol.Event) => {

Просмотреть файл

@ -117,7 +117,7 @@ export function promiseTimeout(p?: Promise<any>, timeoutMs: number = 1000, timeo
} }
export function retryAsync(fn: () => Promise<any>, timeoutMs: number): Promise<any> { export function retryAsync(fn: () => Promise<any>, timeoutMs: number): Promise<any> {
var startTime = Date.now(); const startTime = Date.now();
function tryUntilTimeout(): Promise<any> { function tryUntilTimeout(): Promise<any> {
return fn().catch( return fn().catch(
@ -125,7 +125,7 @@ export function retryAsync(fn: () => Promise<any>, timeoutMs: number): Promise<a
if (Date.now() - startTime < timeoutMs) { if (Date.now() - startTime < timeoutMs) {
return tryUntilTimeout(); return tryUntilTimeout();
} else { } else {
return Promise.reject(e); return errP(e);
} }
}); });
} }
@ -291,3 +291,7 @@ export function remoteObjectToValue(object: WebKitProtocol.Runtime.RemoteObject,
return { value, variableHandleRef }; return { value, variableHandleRef };
} }
export function errP(msg: string): Promise<any> {
return Promise.reject(new Error(msg));
}

Просмотреть файл

@ -5,7 +5,7 @@
import * as WebSocket from 'ws'; import * as WebSocket from 'ws';
import * as http from 'http'; import * as http from 'http';
import {EventEmitter} from 'events'; import {EventEmitter} from 'events';
import * as Utilities from './utilities'; import * as utils from './utilities';
import {Logger} from './utilities'; import {Logger} from './utilities';
interface IMessageWithId { interface IMessageWithId {
@ -122,7 +122,7 @@ export class WebKitConnection {
*/ */
public attach(port: number): Promise<void> { public attach(port: number): Promise<void> {
Logger.log('Attempting to attach on port ' + port); Logger.log('Attempting to attach on port ' + port);
return Utilities.retryAsync(() => this._attach(port), 6000) return utils.retryAsync(() => this._attach(port), 6000)
.then(() => this.sendMessage('Debugger.enable')) .then(() => this.sendMessage('Debugger.enable'))
.then(() => this.sendMessage('Console.enable')) .then(() => this.sendMessage('Console.enable'))
.then(() => { }); .then(() => { });
@ -147,7 +147,7 @@ export class WebKitConnection {
// JSON.parse can throw // JSON.parse can throw
} }
return Promise.reject('Got response, but no valid target pages found'); return utils.errP('Got response, but no valid target pages found');
}); });
} }

Просмотреть файл

@ -6,7 +6,7 @@ import {Event} from '../common/v8Protocol';
import {StoppedEvent, InitializedEvent, TerminatedEvent, OutputEvent} from '../common/debugSession'; import {StoppedEvent, InitializedEvent, TerminatedEvent, OutputEvent} from '../common/debugSession';
import {Handles} from '../common/handles'; import {Handles} from '../common/handles';
import {WebKitConnection} from './webKitConnection'; import {WebKitConnection} from './webKitConnection';
import * as Utilities from './utilities'; import * as utils from './utilities';
import {Logger} from './utilities'; import {Logger} from './utilities';
import {formatConsoleMessage} from './consoleHelper'; import {formatConsoleMessage} from './consoleHelper';
@ -24,7 +24,7 @@ export class WebKitDebugAdapter implements IDebugAdapter {
private _variableHandles: Handles<string>; private _variableHandles: Handles<string>;
private _currentStack: WebKitProtocol.Debugger.CallFrame[]; private _currentStack: WebKitProtocol.Debugger.CallFrame[];
private _committedBreakpointsByUrl: Map<string, WebKitProtocol.Debugger.BreakpointId[]>; private _committedBreakpointsByUrl: Map<string, WebKitProtocol.Debugger.BreakpointId[]>;
private _overlayHelper: Utilities.DebounceHelper; private _overlayHelper: utils.DebounceHelper;
private _chromeProc: ChildProcess; private _chromeProc: ChildProcess;
private _webKitConnection: WebKitConnection; private _webKitConnection: WebKitConnection;
@ -37,7 +37,7 @@ export class WebKitDebugAdapter implements IDebugAdapter {
public constructor() { public constructor() {
this._variableHandles = new Handles<string>(); this._variableHandles = new Handles<string>();
this._overlayHelper = new Utilities.DebounceHelper(/*timeoutMs=*/200); this._overlayHelper = new utils.DebounceHelper(/*timeoutMs=*/200);
this.clearEverything(); this.clearEverything();
} }
@ -73,9 +73,9 @@ export class WebKitDebugAdapter implements IDebugAdapter {
} }
// Check exists? // Check exists?
const chromePath = args.runtimeExecutable || Utilities.getBrowserPath(); const chromePath = args.runtimeExecutable || utils.getBrowserPath();
if (!chromePath) { if (!chromePath) {
return Promise.reject(`Can't find Chrome - install it or set the "runtimeExecutable" field in the launch config.`); return utils.errP(`Can't find Chrome - install it or set the "runtimeExecutable" field in the launch config.`);
} }
// Start with remote debugging enabled // Start with remote debugging enabled
@ -93,7 +93,7 @@ export class WebKitDebugAdapter implements IDebugAdapter {
} else if (args.url) { } else if (args.url) {
chromeArgs.push(args.url); chromeArgs.push(args.url);
} else { } else {
return Promise.reject('The launch config must specify either the "file" or "url" field.'); return utils.errP('The launch config must specify either the "file" or "url" field.');
} }
Logger.log(`spawn('${chromePath}', ${JSON.stringify(chromeArgs) })`); Logger.log(`spawn('${chromePath}', ${JSON.stringify(chromeArgs) })`);
@ -108,11 +108,11 @@ export class WebKitDebugAdapter implements IDebugAdapter {
public attach(args: IAttachRequestArgs): Promise<void> { public attach(args: IAttachRequestArgs): Promise<void> {
if (args.address !== 'localhost' && args.address !== '127.0.0.1') { if (args.address !== 'localhost' && args.address !== '127.0.0.1') {
return Promise.reject('Remote debugging is not supported'); return utils.errP('Remote debugging is not supported');
} }
if (args.port == null) { if (args.port == null) {
return Promise.reject('The "port" field is required in the attach config.'); return utils.errP('The "port" field is required in the attach config.');
} }
if (args.diagnosticLogging) { if (args.diagnosticLogging) {
@ -144,7 +144,7 @@ export class WebKitDebugAdapter implements IDebugAdapter {
() => this.fireEvent(new InitializedEvent()), () => this.fireEvent(new InitializedEvent()),
e => { e => {
this.clearEverything(); this.clearEverything();
return Promise.reject(e); return utils.errP(e);
}); });
} else { } else {
return Promise.resolve<void>(); return Promise.resolve<void>();
@ -276,14 +276,14 @@ export class WebKitDebugAdapter implements IDebugAdapter {
.then(() => this._addBreakpoints(targetScriptUrl, args.lines, args.cols)) .then(() => this._addBreakpoints(targetScriptUrl, args.lines, args.cols))
.then(responses => ({ breakpoints: this._webkitBreakpointResponsesToODPBreakpoints(targetScriptUrl, responses, args.lines) })); .then(responses => ({ breakpoints: this._webkitBreakpointResponsesToODPBreakpoints(targetScriptUrl, responses, args.lines) }));
const setBreakpointsPTimeout = Utilities.promiseTimeout(setBreakpointsPFailOnError, /*timeoutMs*/2000, 'Set breakpoints request timed out'); const setBreakpointsPTimeout = utils.promiseTimeout(setBreakpointsPFailOnError, /*timeoutMs*/2000, 'Set breakpoints request timed out');
// Do just one setBreakpointsRequest at a time to avoid interleaving breakpoint removed/breakpoint added requests to Chrome. // Do just one setBreakpointsRequest at a time to avoid interleaving breakpoint removed/breakpoint added requests to Chrome.
// Swallow errors in the promise queue chain so it doesn't get blocked, but return the failing promise for error handling. // Swallow errors in the promise queue chain so it doesn't get blocked, but return the failing promise for error handling.
this._setBreakpointsRequestQ = setBreakpointsPTimeout.catch(() => undefined); this._setBreakpointsRequestQ = setBreakpointsPTimeout.catch(() => undefined);
return setBreakpointsPTimeout; return setBreakpointsPTimeout;
} else { } else {
return Promise.reject(`Can't find script for breakpoint request`); return utils.errP(`Can't find script for breakpoint request`);
} }
} }
@ -481,7 +481,7 @@ export class WebKitDebugAdapter implements IDebugAdapter {
return evalPromise.then(evalResponse => { return evalPromise.then(evalResponse => {
if (evalResponse.result.wasThrown) { if (evalResponse.result.wasThrown) {
const errorMessage = evalResponse.result.exceptionDetails ? evalResponse.result.exceptionDetails.text : 'Error'; const errorMessage = evalResponse.result.exceptionDetails ? evalResponse.result.exceptionDetails.text : 'Error';
return Promise.reject(errorMessage); return utils.errP(errorMessage);
} }
const { value, variablesReference } = this.remoteObjectToValue(evalResponse.result.result); const { value, variablesReference } = this.remoteObjectToValue(evalResponse.result.result);
@ -505,7 +505,7 @@ export class WebKitDebugAdapter implements IDebugAdapter {
* use it with this instance's variableHandles to create a variable handle. * use it with this instance's variableHandles to create a variable handle.
*/ */
private remoteObjectToValue(object: WebKitProtocol.Runtime.RemoteObject): { value: string, variablesReference: number } { private remoteObjectToValue(object: WebKitProtocol.Runtime.RemoteObject): { value: string, variablesReference: number } {
const { value, variableHandleRef } = Utilities.remoteObjectToValue(object); const { value, variableHandleRef } = utils.remoteObjectToValue(object);
const result = { value, variablesReference: 0 }; const result = { value, variablesReference: 0 };
if (variableHandleRef) { if (variableHandleRef) {
result.variablesReference = this._variableHandles.create(variableHandleRef); result.variablesReference = this._variableHandles.create(variableHandleRef);

Просмотреть файл

@ -61,8 +61,8 @@ export class WebKitDebugSession extends DebugSession {
}, },
e => { e => {
const eStr = e ? e.toString() : 'Unknown error'; const eStr = e ? e.toString() : 'Unknown error';
if (eStr === 'unknowncommand') { if (eStr === 'Error: unknowncommand') {
this.sendErrorResponse(response, 1014, 'Unrecognized request', null, ErrorDestination.Telemetry); this.sendErrorResponse(response, 1014, '[webkit-debug-adapter] Unrecognized request: ' + request.command, null, ErrorDestination.Telemetry);
return; return;
} }
@ -74,7 +74,7 @@ export class WebKitDebugSession extends DebugSession {
// These errors show up in the message bar at the top (or nowhere), sometimes not obvious that they // These errors show up in the message bar at the top (or nowhere), sometimes not obvious that they
// come from the adapter // come from the adapter
response.message = '[webkit-debug-adapter] ' + eStr; response.message = '[webkit-debug-adapter] ' + eStr;
Logger.log('Error: ' + eStr); Logger.log('Error: ' + e ? e.stack : eStr);
} }
response.success = false; response.success = false;