mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-09 17:52:34 -05:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { replaceWhitespace, renderExpressionValue, renderVariable } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { MockSession } from 'vs/workbench/contrib/debug/test/common/mockDebug';
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
const $ = dom.$;
|
||||
|
||||
suite('Debug - Base Debug View', () => {
|
||||
|
||||
test('replace whitespace', () => {
|
||||
assert.equal(replaceWhitespace('hey there'), 'hey there');
|
||||
assert.equal(replaceWhitespace('hey there\n'), 'hey there\\n');
|
||||
assert.equal(replaceWhitespace('hey \r there\n\t'), 'hey \\r there\\n\\t');
|
||||
assert.equal(replaceWhitespace('hey \r\t\n\t\t\n there'), 'hey \\r\\t\\n\\t\\t\\n there');
|
||||
});
|
||||
|
||||
test('render variable', () => {
|
||||
// {{SQL CARBON EDIT}} - Disable test
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { appendStylizedStringToContainer, handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
|
||||
|
||||
suite('Debug - ANSI Handling', () => {
|
||||
test('appendStylizedStringToContainer', () => {
|
||||
assert.equal('', '');
|
||||
});
|
||||
});
|
||||
153
src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts
Normal file
153
src/vs/workbench/contrib/debug/test/browser/linkDetector.test.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
suite('Debug - Link Detector', () => {
|
||||
|
||||
let linkDetector: LinkDetector;
|
||||
|
||||
/**
|
||||
* Instantiate a {@link LinkDetector} for use by the functions being tested.
|
||||
*/
|
||||
setup(() => {
|
||||
const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService();
|
||||
linkDetector = instantiationService.createInstance(LinkDetector);
|
||||
});
|
||||
|
||||
/**
|
||||
* Assert that a given Element is an anchor element with an onClick event.
|
||||
*
|
||||
* @param element The Element to verify.
|
||||
*/
|
||||
function assertElementIsLink(element: Element) {
|
||||
assert(element instanceof HTMLAnchorElement);
|
||||
assert.notEqual(null, (element as HTMLAnchorElement).onclick);
|
||||
}
|
||||
|
||||
test('noLinks', () => {
|
||||
const input = 'I am a string';
|
||||
const expectedOutput = '<span>I am a string</span>';
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(0, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal(expectedOutput, output.outerHTML);
|
||||
});
|
||||
|
||||
test('trailingNewline', () => {
|
||||
const input = 'I am a string\n';
|
||||
const expectedOutput = '<span>I am a string\n</span>';
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(0, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal(expectedOutput, output.outerHTML);
|
||||
});
|
||||
|
||||
test('singleLineLink', () => {
|
||||
const input = isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34';
|
||||
const expectedOutput = /^<span><a title=".*">.*\/foo\/bar.js:12:34<\/a><\/span>$/;
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(1, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal('A', output.firstElementChild!.tagName);
|
||||
assert(expectedOutput.test(output.outerHTML));
|
||||
assertElementIsLink(output.firstElementChild!);
|
||||
assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.firstElementChild!.textContent);
|
||||
});
|
||||
|
||||
test('relativeLink', () => {
|
||||
const input = '\./foo/bar.js';
|
||||
const expectedOutput = '<span>\./foo/bar.js</span>';
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(0, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal(expectedOutput, output.outerHTML);
|
||||
});
|
||||
|
||||
test('singleLineLinkAndText', function () {
|
||||
const input = isWindows ? 'The link: C:/foo/bar.js:12:34' : 'The link: /Users/foo/bar.js:12:34';
|
||||
const expectedOutput = /^<span><span>The link: <\/span><a title=".*">.*\/foo\/bar.js:12:34<\/a><\/span>$/;
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(2, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal('SPAN', output.children[0].tagName);
|
||||
assert.equal('A', output.children[1].tagName);
|
||||
assert(expectedOutput.test(output.outerHTML));
|
||||
assertElementIsLink(output.children[1]);
|
||||
assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.children[1].textContent);
|
||||
});
|
||||
|
||||
test('singleLineMultipleLinks', () => {
|
||||
const input = isWindows ? 'Here is a link C:/foo/bar.js:12:34 and here is another D:/boo/far.js:56:78' :
|
||||
'Here is a link /Users/foo/bar.js:12:34 and here is another /Users/boo/far.js:56:78';
|
||||
const expectedOutput = /^<span><span>Here is a link <\/span><a title=".*">.*\/foo\/bar.js:12:34<\/a><span> and here is another <\/span><a title=".*">.*\/boo\/far.js:56:78<\/a><\/span>$/;
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(4, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal('SPAN', output.children[0].tagName);
|
||||
assert.equal('A', output.children[1].tagName);
|
||||
assert.equal('SPAN', output.children[2].tagName);
|
||||
assert.equal('A', output.children[3].tagName);
|
||||
assert(expectedOutput.test(output.outerHTML));
|
||||
assertElementIsLink(output.children[1]);
|
||||
assertElementIsLink(output.children[3]);
|
||||
assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.children[1].textContent);
|
||||
assert.equal(isWindows ? 'D:/boo/far.js:56:78' : '/Users/boo/far.js:56:78', output.children[3].textContent);
|
||||
});
|
||||
|
||||
test('multilineNoLinks', () => {
|
||||
const input = 'Line one\nLine two\nLine three';
|
||||
const expectedOutput = /^<span><span>Line one\n<\/span><span>Line two\n<\/span><span>Line three<\/span><\/span>$/;
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(3, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal('SPAN', output.children[0].tagName);
|
||||
assert.equal('SPAN', output.children[1].tagName);
|
||||
assert.equal('SPAN', output.children[2].tagName);
|
||||
assert(expectedOutput.test(output.outerHTML));
|
||||
});
|
||||
|
||||
test('multilineTrailingNewline', () => {
|
||||
const input = 'I am a string\nAnd I am another\n';
|
||||
const expectedOutput = '<span><span>I am a string\n<\/span><span>And I am another\n<\/span><\/span>';
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(2, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal('SPAN', output.children[0].tagName);
|
||||
assert.equal('SPAN', output.children[1].tagName);
|
||||
assert.equal(expectedOutput, output.outerHTML);
|
||||
});
|
||||
|
||||
test('multilineWithLinks', () => {
|
||||
const input = isWindows ? 'I have a link for you\nHere it is: C:/foo/bar.js:12:34\nCool, huh?' :
|
||||
'I have a link for you\nHere it is: /Users/foo/bar.js:12:34\nCool, huh?';
|
||||
const expectedOutput = /^<span><span>I have a link for you\n<\/span><span><span>Here it is: <\/span><a title=".*">.*\/foo\/bar.js:12:34<\/a><span>\n<\/span><\/span><span>Cool, huh\?<\/span><\/span>$/;
|
||||
const output = linkDetector.handleLinks(input);
|
||||
|
||||
assert.equal(3, output.children.length);
|
||||
assert.equal('SPAN', output.tagName);
|
||||
assert.equal('SPAN', output.children[0].tagName);
|
||||
assert.equal('SPAN', output.children[1].tagName);
|
||||
assert.equal('SPAN', output.children[2].tagName);
|
||||
assert.equal('SPAN', output.children[1].children[0].tagName);
|
||||
assert.equal('A', output.children[1].children[1].tagName);
|
||||
assert.equal('SPAN', output.children[1].children[2].tagName);
|
||||
assert(expectedOutput.test(output.outerHTML));
|
||||
assertElementIsLink(output.children[1].children[1]);
|
||||
assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.children[1].children[1].textContent);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
suite('Debug - Source', () => {
|
||||
|
||||
test('from raw source', () => {
|
||||
const source = new Source({
|
||||
name: 'zz',
|
||||
path: '/xx/yy/zz',
|
||||
sourceReference: 0,
|
||||
presentationHint: 'emphasize'
|
||||
}, 'aDebugSessionId');
|
||||
|
||||
assert.equal(source.presentationHint, 'emphasize');
|
||||
assert.equal(source.name, 'zz');
|
||||
assert.equal(source.inMemory, false);
|
||||
assert.equal(source.reference, 0);
|
||||
assert.equal(source.uri.toString(), uri.file('/xx/yy/zz').toString());
|
||||
});
|
||||
|
||||
test('from raw internal source', () => {
|
||||
const source = new Source({
|
||||
name: 'internalModule.js',
|
||||
sourceReference: 11,
|
||||
presentationHint: 'deemphasize'
|
||||
}, 'aDebugSessionId');
|
||||
|
||||
assert.equal(source.presentationHint, 'deemphasize');
|
||||
assert.equal(source.name, 'internalModule.js');
|
||||
assert.equal(source.inMemory, true);
|
||||
assert.equal(source.reference, 11);
|
||||
assert.equal(source.uri.toString(), 'debug:internalModule.js?session%3DaDebugSessionId%26ref%3D11');
|
||||
});
|
||||
|
||||
test('get encoded debug data', () => {
|
||||
const checkData = (uri: uri, expectedName, expectedPath, expectedSourceReference, expectedSessionId) => {
|
||||
let { name, path, sourceReference, sessionId } = Source.getEncodedDebugData(uri);
|
||||
assert.equal(name, expectedName);
|
||||
assert.equal(path, expectedPath);
|
||||
assert.equal(sourceReference, expectedSourceReference);
|
||||
assert.equal(sessionId, expectedSessionId);
|
||||
};
|
||||
|
||||
checkData(uri.file('a/b/c/d'), 'd', isWindows ? '\\a\\b\\c\\d' : '/a/b/c/d', undefined, undefined);
|
||||
checkData(uri.from({ scheme: 'file', path: '/my/path/test.js', query: 'ref=1&session=2' }), 'test.js', isWindows ? '\\my\\path\\test.js' : '/my/path/test.js', undefined, undefined);
|
||||
|
||||
checkData(uri.from({ scheme: 'http', authority: 'www.msft.com', path: '/my/path' }), 'path', 'http://www.msft.com/my/path', undefined, undefined);
|
||||
checkData(uri.from({ scheme: 'debug', authority: 'www.msft.com', path: '/my/path', query: 'ref=100' }), 'path', '/my/path', 100, undefined);
|
||||
checkData(uri.from({ scheme: 'debug', path: 'a/b/c/d.js', query: 'session=100' }), 'd.js', 'a/b/c/d.js', undefined, 100);
|
||||
checkData(uri.from({ scheme: 'debug', path: 'a/b/c/d/foo.txt', query: 'session=100&ref=10' }), 'foo.txt', 'a/b/c/d/foo.txt', 10, 100);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { formatPII, getExactExpressionStartAndEnd } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
|
||||
suite('Debug - Utils', () => {
|
||||
test('formatPII', () => {
|
||||
assert.strictEqual(formatPII('Foo Bar', false, {}), 'Foo Bar');
|
||||
assert.strictEqual(formatPII('Foo {key} Bar', false, {}), 'Foo {key} Bar');
|
||||
assert.strictEqual(formatPII('Foo {key} Bar', false, { 'key': 'yes' }), 'Foo yes Bar');
|
||||
assert.strictEqual(formatPII('Foo {_0} Bar {_0}', true, { '_0': 'yes' }), 'Foo yes Bar yes');
|
||||
assert.strictEqual(formatPII('Foo {0} Bar {1}{2}', false, { '0': 'yes' }), 'Foo yes Bar {1}{2}');
|
||||
assert.strictEqual(formatPII('Foo {0} Bar {1}{2}', false, { '0': 'yes', '1': 'undefined' }), 'Foo yes Bar undefined{2}');
|
||||
assert.strictEqual(formatPII('Foo {_key0} Bar {key1}{key2}', true, { '_key0': 'yes', 'key1': '5', 'key2': 'false' }), 'Foo yes Bar {key1}{key2}');
|
||||
assert.strictEqual(formatPII('Foo {_key0} Bar {key1}{key2}', false, { '_key0': 'yes', 'key1': '5', 'key2': 'false' }), 'Foo yes Bar 5false');
|
||||
});
|
||||
|
||||
test('getExactExpressionStartAndEnd', () => {
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('foo', 1, 2), { start: 1, end: 3 });
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('foo', 1, 3), { start: 1, end: 3 });
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('foo', 1, 4), { start: 1, end: 3 });
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('this.name = "John"', 1, 10), { start: 1, end: 9 });
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('this.name = "John"', 6, 10), { start: 1, end: 9 });
|
||||
// Hovers over "address" should pick up this->address
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('this->address = "Main street"', 6, 10), { start: 1, end: 13 });
|
||||
// Hovers over "name" should pick up a.b.c.d.name
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('var t = a.b.c.d.name', 16, 20), { start: 9, end: 20 });
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('MyClass::StaticProp', 10, 20), { start: 1, end: 19 });
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('largeNumber = myVar?.prop', 21, 25), { start: 15, end: 25 });
|
||||
|
||||
// For example in expression 'a.b.c.d', hover was under 'b', 'a.b' should be the exact range
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('var t = a.b.c.d.name', 11, 12), { start: 9, end: 11 });
|
||||
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('var t = a.b;c.d.name', 16, 20), { start: 13, end: 20 });
|
||||
assert.deepEqual(getExactExpressionStartAndEnd('var t = a.b.c-d.name', 16, 20), { start: 15, end: 20 });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel';
|
||||
import { StackFrame, Expression, Thread } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { MockSession } from 'vs/workbench/contrib/debug/test/common/mockDebug';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
suite('Debug - View Model', () => {
|
||||
let model: ViewModel;
|
||||
|
||||
setup(() => {
|
||||
model = new ViewModel(new MockContextKeyService());
|
||||
});
|
||||
|
||||
test('focused stack frame', () => {
|
||||
assert.equal(model.focusedStackFrame, null);
|
||||
assert.equal(model.focusedThread, null);
|
||||
const session = new MockSession();
|
||||
const thread = new Thread(session, 'myThread', 1);
|
||||
const frame = new StackFrame(thread, 1, undefined!, 'app.js', 'normal', { startColumn: 1, startLineNumber: 1, endColumn: 1, endLineNumber: 1 }, 0);
|
||||
model.setFocus(frame, thread, session, false);
|
||||
|
||||
assert.equal(model.focusedStackFrame!.getId(), frame.getId());
|
||||
assert.equal(model.focusedThread!.threadId, 1);
|
||||
assert.equal(model.focusedSession!.getId(), session.getId());
|
||||
});
|
||||
|
||||
test('selected expression', () => {
|
||||
assert.equal(model.getSelectedExpression(), null);
|
||||
const expression = new Expression('my expression');
|
||||
model.setSelectedExpression(expression);
|
||||
|
||||
assert.equal(model.getSelectedExpression(), expression);
|
||||
});
|
||||
|
||||
test('multi session view and changed workbench state', () => {
|
||||
assert.equal(model.isMultiSessionView(), false);
|
||||
model.setMultiSessionView(true);
|
||||
assert.equal(model.isMultiSessionView(), true);
|
||||
});
|
||||
});
|
||||
414
src/vs/workbench/contrib/debug/test/common/mockDebug.ts
Normal file
414
src/vs/workbench/contrib/debug/test/common/mockDebug.ts
Normal file
@@ -0,0 +1,414 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { CompletionItem } from 'vs/editor/common/modes';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
export class MockDebugService implements IDebugService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
public get state(): State {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public get onWillNewSession(): Event<IDebugSession> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public get onDidNewSession(): Event<IDebugSession> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public get onDidEndSession(): Event<IDebugSession> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public get onDidChangeState(): Event<State> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public getConfigurationManager(): IConfigurationManager {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public focusStackFrame(focusedStackFrame: IStackFrame): void {
|
||||
}
|
||||
|
||||
sendAllBreakpoints(session?: IDebugSession): Promise<any> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): Promise<IBreakpoint[]> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public updateBreakpoints(uri: uri, data: { [id: string]: IBreakpointUpdateData }, sendOnResourceSaved: boolean): void { }
|
||||
|
||||
public enableOrDisableBreakpoints(enabled: boolean): Promise<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public setBreakpointsActivated(): Promise<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public removeBreakpoints(): Promise<any> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public addFunctionBreakpoint(): void { }
|
||||
|
||||
public moveWatchExpression(id: string, position: number): void { }
|
||||
|
||||
public renameFunctionBreakpoint(id: string, newFunctionName: string): Promise<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public removeFunctionBreakpoints(id?: string): Promise<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public addReplExpression(name: string): Promise<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public removeReplExpressions(): void { }
|
||||
|
||||
public addWatchExpression(name?: string): Promise<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public renameWatchExpression(id: string, newName: string): Promise<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public removeWatchExpressions(id?: string): void { }
|
||||
|
||||
public startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
public restartSession(): Promise<any> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public stopSession(): Promise<any> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public getModel(): IDebugModel {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public getViewModel(): IViewModel {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public logToRepl(session: IDebugSession, value: string): void { }
|
||||
|
||||
public sourceIsNotAvailable(uri: uri): void { }
|
||||
|
||||
public tryToAutoFocusStackFrame(thread: IThread): Promise<any> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
export class MockSession implements IDebugSession {
|
||||
getReplElements(): IReplElement[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
removeReplExpressions(): void { }
|
||||
get onDidChangeReplElements(): Event<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
addReplExpression(stackFrame: IStackFrame, name: string): Promise<void> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
appendToRepl(data: string | IExpression, severity: Severity, source?: IReplElementSource): void { }
|
||||
logToRepl(sev: Severity, args: any[], frame?: { uri: uri; line: number; column: number; }) { }
|
||||
|
||||
configuration: IConfig = { type: 'mock', name: 'mock', request: 'launch' };
|
||||
unresolvedConfiguration: IConfig = { type: 'mock', name: 'mock', request: 'launch' };
|
||||
state = State.Stopped;
|
||||
root: IWorkspaceFolder;
|
||||
capabilities: DebugProtocol.Capabilities = {};
|
||||
|
||||
getId(): string {
|
||||
return 'mock';
|
||||
}
|
||||
|
||||
getLabel(): string {
|
||||
return 'mockname';
|
||||
}
|
||||
|
||||
getSourceForUri(modelUri: uri): Source {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
getThread(threadId: number): IThread {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
get onDidCustomEvent(): Event<DebugProtocol.Event> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
get onDidLoadedSource(): Event<LoadedSourceEvent> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
get onDidChangeState(): Event<void> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
get onDidEndAdapter(): Event<AdapterEndEvent> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
setConfiguration(configuration: { resolved: IConfig, unresolved: IConfig }) { }
|
||||
|
||||
getAllThreads(): IThread[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getSource(raw: DebugProtocol.Source): Source {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
getLoadedSources(): Promise<Source[]> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise<CompletionItem[]> {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
clearThreads(removeThreads: boolean, reference?: number): void { }
|
||||
|
||||
rawUpdate(data: IRawModelUpdate): void { }
|
||||
|
||||
initialize(dbgr: IDebugger): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
launchOrAttach(config: IConfig): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
restart(): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
customRequest(request: string, args: any): Promise<DebugProtocol.Response> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
stackTrace(threadId: number, startFrame: number, levels: number): Promise<DebugProtocol.StackTraceResponse> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
exceptionInfo(threadId: number): Promise<IExceptionInfo> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
scopes(frameId: number): Promise<DebugProtocol.ScopesResponse> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise<DebugProtocol.VariablesResponse> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
evaluate(expression: string, frameId: number, context?: string): Promise<DebugProtocol.EvaluateResponse> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
restartFrame(frameId: number, threadId: number): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
next(threadId: number): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
stepIn(threadId: number): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
stepOut(threadId: number): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
stepBack(threadId: number): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
continue(threadId: number): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
reverseContinue(threadId: number): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
pause(threadId: number): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
terminateThreads(threadIds: number[]): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
setVariable(variablesReference: number, name: string, value: string): Promise<DebugProtocol.SetVariableResponse> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
loadSource(resource: uri): Promise<DebugProtocol.SourceResponse> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
terminate(restart = false): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
disconnect(restart = false): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
shutdown(): void { }
|
||||
}
|
||||
|
||||
export class MockRawSession {
|
||||
|
||||
capabilities: DebugProtocol.Capabilities;
|
||||
disconnected: boolean;
|
||||
sessionLengthInSeconds: number;
|
||||
|
||||
public readyForBreakpoints = true;
|
||||
public emittedStopped = true;
|
||||
|
||||
public getLengthInSeconds(): number {
|
||||
return 100;
|
||||
}
|
||||
|
||||
public stackTrace(args: DebugProtocol.StackTraceArguments): Promise<DebugProtocol.StackTraceResponse> {
|
||||
return Promise.resolve({
|
||||
seq: 1,
|
||||
type: 'response',
|
||||
request_seq: 1,
|
||||
success: true,
|
||||
command: 'stackTrace',
|
||||
body: {
|
||||
stackFrames: [{
|
||||
id: 1,
|
||||
name: 'mock',
|
||||
line: 5,
|
||||
column: 6
|
||||
}]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise<DebugProtocol.ExceptionInfoResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public launchOrAttach(args: IConfig): Promise<DebugProtocol.Response> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public scopes(args: DebugProtocol.ScopesArguments): Promise<DebugProtocol.ScopesResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public variables(args: DebugProtocol.VariablesArguments): Promise<DebugProtocol.VariablesResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
evaluate(args: DebugProtocol.EvaluateArguments): Promise<DebugProtocol.EvaluateResponse> {
|
||||
return Promise.resolve(null!);
|
||||
}
|
||||
|
||||
public custom(request: string, args: any): Promise<DebugProtocol.Response> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public terminate(restart = false): Promise<DebugProtocol.TerminateResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public disconnect(restart?: boolean): Promise<any> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public threads(): Promise<DebugProtocol.ThreadsResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public stepIn(args: DebugProtocol.StepInArguments): Promise<DebugProtocol.StepInResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public stepOut(args: DebugProtocol.StepOutArguments): Promise<DebugProtocol.StepOutResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public stepBack(args: DebugProtocol.StepBackArguments): Promise<DebugProtocol.StepBackResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public continue(args: DebugProtocol.ContinueArguments): Promise<DebugProtocol.ContinueResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise<DebugProtocol.ReverseContinueResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public pause(args: DebugProtocol.PauseArguments): Promise<DebugProtocol.PauseResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise<DebugProtocol.TerminateThreadsResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public setVariable(args: DebugProtocol.SetVariableArguments): Promise<DebugProtocol.SetVariableResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public restartFrame(args: DebugProtocol.RestartFrameArguments): Promise<DebugProtocol.RestartFrameResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public completions(args: DebugProtocol.CompletionsArguments): Promise<DebugProtocol.CompletionsResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public next(args: DebugProtocol.NextArguments): Promise<DebugProtocol.NextResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public source(args: DebugProtocol.SourceArguments): Promise<DebugProtocol.SourceResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise<DebugProtocol.LoadedSourcesResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise<DebugProtocol.SetBreakpointsResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise<DebugProtocol.SetFunctionBreakpointsResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise<DebugProtocol.SetExceptionBreakpointsResponse> {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
public readonly onDidStop: Event<DebugProtocol.StoppedEvent> = null!;
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import severity from 'vs/base/common/severity';
|
||||
import { SimpleReplElement, DebugModel, Expression, RawObjectReplElement, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import * as sinon from 'sinon';
|
||||
import { MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { DebugSession } from 'vs/workbench/contrib/debug/electron-browser/debugSession';
|
||||
import { ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
|
||||
|
||||
suite('Debug - Model', () => {
|
||||
let model: DebugModel;
|
||||
let rawSession: MockRawSession;
|
||||
|
||||
setup(() => {
|
||||
model = new DebugModel([], true, [], [], [], <any>{ isDirty: (e: any) => false });
|
||||
rawSession = new MockRawSession();
|
||||
});
|
||||
|
||||
// Breakpoints
|
||||
|
||||
test('breakpoints simple', () => {
|
||||
const modelUri = uri.file('/myfolder/myfile.js');
|
||||
model.addBreakpoints(modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);
|
||||
assert.equal(model.areBreakpointsActivated(), true);
|
||||
assert.equal(model.getBreakpoints().length, 2);
|
||||
|
||||
model.removeBreakpoints(model.getBreakpoints());
|
||||
assert.equal(model.getBreakpoints().length, 0);
|
||||
});
|
||||
|
||||
test('breakpoints toggling', () => {
|
||||
const modelUri = uri.file('/myfolder/myfile.js');
|
||||
model.addBreakpoints(modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);
|
||||
model.addBreakpoints(modelUri, [{ lineNumber: 12, enabled: true, condition: 'fake condition' }]);
|
||||
assert.equal(model.getBreakpoints().length, 3);
|
||||
const bp = model.getBreakpoints().pop();
|
||||
if (bp) {
|
||||
model.removeBreakpoints([bp]);
|
||||
}
|
||||
assert.equal(model.getBreakpoints().length, 2);
|
||||
|
||||
model.setBreakpointsActivated(false);
|
||||
assert.equal(model.areBreakpointsActivated(), false);
|
||||
model.setBreakpointsActivated(true);
|
||||
assert.equal(model.areBreakpointsActivated(), true);
|
||||
});
|
||||
|
||||
test('breakpoints two files', () => {
|
||||
const modelUri1 = uri.file('/myfolder/my file first.js');
|
||||
const modelUri2 = uri.file('/secondfolder/second/second file.js');
|
||||
model.addBreakpoints(modelUri1, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);
|
||||
model.addBreakpoints(modelUri2, [{ lineNumber: 1, enabled: true }, { lineNumber: 2, enabled: true }, { lineNumber: 3, enabled: false }]);
|
||||
|
||||
assert.equal(model.getBreakpoints().length, 5);
|
||||
const bp = model.getBreakpoints()[0];
|
||||
const update: any = {};
|
||||
update[bp.getId()] = { lineNumber: 100 };
|
||||
model.updateBreakpoints(update);
|
||||
assert.equal(bp.lineNumber, 100);
|
||||
|
||||
model.enableOrDisableAllBreakpoints(false);
|
||||
model.getBreakpoints().forEach(bp => {
|
||||
assert.equal(bp.enabled, false);
|
||||
});
|
||||
model.setEnablement(bp, true);
|
||||
assert.equal(bp.enabled, true);
|
||||
|
||||
model.removeBreakpoints(model.getBreakpoints({ uri: modelUri1 }));
|
||||
assert.equal(model.getBreakpoints().length, 3);
|
||||
});
|
||||
|
||||
test('breakpoints conditions', () => {
|
||||
const modelUri1 = uri.file('/myfolder/my file first.js');
|
||||
model.addBreakpoints(modelUri1, [{ lineNumber: 5, condition: 'i < 5', hitCondition: '17' }, { lineNumber: 10, condition: 'j < 3' }]);
|
||||
const breakpoints = model.getBreakpoints();
|
||||
|
||||
assert.equal(breakpoints[0].condition, 'i < 5');
|
||||
assert.equal(breakpoints[0].hitCondition, '17');
|
||||
assert.equal(breakpoints[1].condition, 'j < 3');
|
||||
assert.equal(!!breakpoints[1].hitCondition, false);
|
||||
|
||||
assert.equal(model.getBreakpoints().length, 2);
|
||||
model.removeBreakpoints(model.getBreakpoints());
|
||||
assert.equal(model.getBreakpoints().length, 0);
|
||||
});
|
||||
|
||||
test('function breakpoints', () => {
|
||||
model.addFunctionBreakpoint('foo', '1');
|
||||
model.addFunctionBreakpoint('bar', '2');
|
||||
model.renameFunctionBreakpoint('1', 'fooUpdated');
|
||||
model.renameFunctionBreakpoint('2', 'barUpdated');
|
||||
|
||||
const functionBps = model.getFunctionBreakpoints();
|
||||
assert.equal(functionBps[0].name, 'fooUpdated');
|
||||
assert.equal(functionBps[1].name, 'barUpdated');
|
||||
|
||||
model.removeFunctionBreakpoints();
|
||||
assert.equal(model.getFunctionBreakpoints().length, 0);
|
||||
});
|
||||
|
||||
// Threads
|
||||
|
||||
test('threads simple', () => {
|
||||
const threadId = 1;
|
||||
const threadName = 'firstThread';
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
model.addSession(session);
|
||||
|
||||
assert.equal(model.getSessions(true).length, 1);
|
||||
model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
threads: [{
|
||||
id: threadId,
|
||||
name: threadName
|
||||
}]
|
||||
});
|
||||
|
||||
assert.equal(session.getThread(threadId)!.name, threadName);
|
||||
|
||||
model.clearThreads(session.getId(), true);
|
||||
assert.equal(session.getThread(threadId), undefined);
|
||||
assert.equal(model.getSessions(true).length, 1);
|
||||
});
|
||||
|
||||
test('threads multiple wtih allThreadsStopped', () => {
|
||||
const threadId1 = 1;
|
||||
const threadName1 = 'firstThread';
|
||||
const threadId2 = 2;
|
||||
const threadName2 = 'secondThread';
|
||||
const stoppedReason = 'breakpoint';
|
||||
|
||||
// Add the threads
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
model.addSession(session);
|
||||
|
||||
session['raw'] = <any>rawSession;
|
||||
|
||||
model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
threads: [{
|
||||
id: threadId1,
|
||||
name: threadName1
|
||||
}]
|
||||
});
|
||||
|
||||
model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
threads: [{
|
||||
id: threadId2,
|
||||
name: threadName2
|
||||
}]
|
||||
});
|
||||
|
||||
// Stopped event with all threads stopped
|
||||
model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
threads: [{
|
||||
id: threadId1,
|
||||
name: threadName1
|
||||
}],
|
||||
stoppedDetails: {
|
||||
reason: stoppedReason,
|
||||
threadId: 1,
|
||||
allThreadsStopped: true
|
||||
},
|
||||
});
|
||||
|
||||
const thread1 = session.getThread(threadId1)!;
|
||||
const thread2 = session.getThread(threadId2)!;
|
||||
|
||||
// at the beginning, callstacks are obtainable but not available
|
||||
assert.equal(session.getAllThreads().length, 2);
|
||||
assert.equal(thread1.name, threadName1);
|
||||
assert.equal(thread1.stopped, true);
|
||||
assert.equal(thread1.getCallStack().length, 0);
|
||||
assert.equal(thread1.stoppedDetails!.reason, stoppedReason);
|
||||
assert.equal(thread2.name, threadName2);
|
||||
assert.equal(thread2.stopped, true);
|
||||
assert.equal(thread2.getCallStack().length, 0);
|
||||
assert.equal(thread2.stoppedDetails!.reason, undefined);
|
||||
|
||||
// after calling getCallStack, the callstack becomes available
|
||||
// and results in a request for the callstack in the debug adapter
|
||||
thread1.fetchCallStack().then(() => {
|
||||
assert.notEqual(thread1.getCallStack().length, 0);
|
||||
});
|
||||
|
||||
thread2.fetchCallStack().then(() => {
|
||||
assert.notEqual(thread2.getCallStack().length, 0);
|
||||
});
|
||||
|
||||
// calling multiple times getCallStack doesn't result in multiple calls
|
||||
// to the debug adapter
|
||||
thread1.fetchCallStack().then(() => {
|
||||
return thread2.fetchCallStack();
|
||||
});
|
||||
|
||||
// clearing the callstack results in the callstack not being available
|
||||
thread1.clearCallStack();
|
||||
assert.equal(thread1.stopped, true);
|
||||
assert.equal(thread1.getCallStack().length, 0);
|
||||
|
||||
thread2.clearCallStack();
|
||||
assert.equal(thread2.stopped, true);
|
||||
assert.equal(thread2.getCallStack().length, 0);
|
||||
|
||||
model.clearThreads(session.getId(), true);
|
||||
assert.equal(session.getThread(threadId1), undefined);
|
||||
assert.equal(session.getThread(threadId2), undefined);
|
||||
assert.equal(session.getAllThreads().length, 0);
|
||||
});
|
||||
|
||||
test('threads mutltiple without allThreadsStopped', () => {
|
||||
const sessionStub = sinon.spy(rawSession, 'stackTrace');
|
||||
|
||||
const stoppedThreadId = 1;
|
||||
const stoppedThreadName = 'stoppedThread';
|
||||
const runningThreadId = 2;
|
||||
const runningThreadName = 'runningThread';
|
||||
const stoppedReason = 'breakpoint';
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
model.addSession(session);
|
||||
|
||||
session['raw'] = <any>rawSession;
|
||||
|
||||
// Add the threads
|
||||
model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
threads: [{
|
||||
id: stoppedThreadId,
|
||||
name: stoppedThreadName
|
||||
}]
|
||||
});
|
||||
|
||||
model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
threads: [{
|
||||
id: runningThreadId,
|
||||
name: runningThreadName
|
||||
}]
|
||||
});
|
||||
|
||||
// Stopped event with only one thread stopped
|
||||
model.rawUpdate({
|
||||
sessionId: session.getId(),
|
||||
threads: [{
|
||||
id: 1,
|
||||
name: stoppedThreadName
|
||||
}],
|
||||
stoppedDetails: {
|
||||
reason: stoppedReason,
|
||||
threadId: 1,
|
||||
allThreadsStopped: false
|
||||
}
|
||||
});
|
||||
|
||||
const stoppedThread = session.getThread(stoppedThreadId)!;
|
||||
const runningThread = session.getThread(runningThreadId)!;
|
||||
|
||||
// the callstack for the stopped thread is obtainable but not available
|
||||
// the callstack for the running thread is not obtainable nor available
|
||||
assert.equal(stoppedThread.name, stoppedThreadName);
|
||||
assert.equal(stoppedThread.stopped, true);
|
||||
assert.equal(session.getAllThreads().length, 2);
|
||||
assert.equal(stoppedThread.getCallStack().length, 0);
|
||||
assert.equal(stoppedThread.stoppedDetails!.reason, stoppedReason);
|
||||
assert.equal(runningThread.name, runningThreadName);
|
||||
assert.equal(runningThread.stopped, false);
|
||||
assert.equal(runningThread.getCallStack().length, 0);
|
||||
assert.equal(runningThread.stoppedDetails, undefined);
|
||||
|
||||
// after calling getCallStack, the callstack becomes available
|
||||
// and results in a request for the callstack in the debug adapter
|
||||
stoppedThread.fetchCallStack().then(() => {
|
||||
assert.notEqual(stoppedThread.getCallStack().length, 0);
|
||||
assert.equal(runningThread.getCallStack().length, 0);
|
||||
assert.equal(sessionStub.callCount, 1);
|
||||
});
|
||||
|
||||
// calling getCallStack on the running thread returns empty array
|
||||
// and does not return in a request for the callstack in the debug
|
||||
// adapter
|
||||
runningThread.fetchCallStack().then(() => {
|
||||
assert.equal(runningThread.getCallStack().length, 0);
|
||||
assert.equal(sessionStub.callCount, 1);
|
||||
});
|
||||
|
||||
// clearing the callstack results in the callstack not being available
|
||||
stoppedThread.clearCallStack();
|
||||
assert.equal(stoppedThread.stopped, true);
|
||||
assert.equal(stoppedThread.getCallStack().length, 0);
|
||||
|
||||
model.clearThreads(session.getId(), true);
|
||||
assert.equal(session.getThread(stoppedThreadId), undefined);
|
||||
assert.equal(session.getThread(runningThreadId), undefined);
|
||||
assert.equal(session.getAllThreads().length, 0);
|
||||
});
|
||||
|
||||
// Expressions
|
||||
|
||||
function assertWatchExpressions(watchExpressions: Expression[], expectedName: string) {
|
||||
assert.equal(watchExpressions.length, 2);
|
||||
watchExpressions.forEach(we => {
|
||||
assert.equal(we.available, false);
|
||||
assert.equal(we.reference, 0);
|
||||
assert.equal(we.name, expectedName);
|
||||
});
|
||||
}
|
||||
|
||||
test('watch expressions', () => {
|
||||
assert.equal(model.getWatchExpressions().length, 0);
|
||||
model.addWatchExpression('console');
|
||||
model.addWatchExpression('console');
|
||||
let watchExpressions = model.getWatchExpressions();
|
||||
assertWatchExpressions(watchExpressions, 'console');
|
||||
|
||||
model.renameWatchExpression(watchExpressions[0].getId(), 'new_name');
|
||||
model.renameWatchExpression(watchExpressions[1].getId(), 'new_name');
|
||||
assertWatchExpressions(model.getWatchExpressions(), 'new_name');
|
||||
|
||||
assertWatchExpressions(model.getWatchExpressions(), 'new_name');
|
||||
|
||||
model.addWatchExpression('mockExpression');
|
||||
model.moveWatchExpression(model.getWatchExpressions()[2].getId(), 1);
|
||||
watchExpressions = model.getWatchExpressions();
|
||||
assert.equal(watchExpressions[0].name, 'new_name');
|
||||
assert.equal(watchExpressions[1].name, 'mockExpression');
|
||||
assert.equal(watchExpressions[2].name, 'new_name');
|
||||
|
||||
model.removeWatchExpressions();
|
||||
assert.equal(model.getWatchExpressions().length, 0);
|
||||
});
|
||||
|
||||
test('repl expressions', () => {
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
assert.equal(session.getReplElements().length, 0);
|
||||
model.addSession(session);
|
||||
|
||||
session['raw'] = <any>rawSession;
|
||||
const thread = new Thread(session, 'mockthread', 1);
|
||||
const stackFrame = new StackFrame(thread, 1, <any>undefined, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1);
|
||||
const replModel = new ReplModel(session);
|
||||
replModel.addReplExpression(stackFrame, 'myVariable').then();
|
||||
replModel.addReplExpression(stackFrame, 'myVariable').then();
|
||||
replModel.addReplExpression(stackFrame, 'myVariable').then();
|
||||
|
||||
assert.equal(replModel.getReplElements().length, 3);
|
||||
replModel.getReplElements().forEach(re => {
|
||||
assert.equal((<Expression>re).available, false);
|
||||
assert.equal((<Expression>re).name, 'myVariable');
|
||||
assert.equal((<Expression>re).reference, 0);
|
||||
});
|
||||
|
||||
replModel.removeReplExpressions();
|
||||
assert.equal(replModel.getReplElements().length, 0);
|
||||
});
|
||||
|
||||
test('stack frame get specific source name', () => {
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
model.addSession(session);
|
||||
|
||||
let firstStackFrame: StackFrame;
|
||||
let secondStackFrame: StackFrame;
|
||||
const thread = new class extends Thread {
|
||||
public getCallStack(): StackFrame[] {
|
||||
return [firstStackFrame, secondStackFrame];
|
||||
}
|
||||
}(session, 'mockthread', 1);
|
||||
|
||||
const firstSource = new Source({
|
||||
name: 'internalModule.js',
|
||||
path: 'a/b/c/d/internalModule.js',
|
||||
sourceReference: 10,
|
||||
}, 'aDebugSessionId');
|
||||
const secondSource = new Source({
|
||||
name: 'internalModule.js',
|
||||
path: 'z/x/c/d/internalModule.js',
|
||||
sourceReference: 11,
|
||||
}, 'aDebugSessionId');
|
||||
firstStackFrame = new StackFrame(thread, 1, firstSource, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1);
|
||||
secondStackFrame = new StackFrame(thread, 1, secondSource, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1);
|
||||
|
||||
assert.equal(firstStackFrame.getSpecificSourceName(), '.../b/c/d/internalModule.js');
|
||||
assert.equal(secondStackFrame.getSpecificSourceName(), '.../x/c/d/internalModule.js');
|
||||
});
|
||||
|
||||
// Repl output
|
||||
|
||||
test('repl output', () => {
|
||||
const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!);
|
||||
const repl = new ReplModel(session);
|
||||
repl.appendToRepl('first line\n', severity.Error);
|
||||
repl.appendToRepl('second line', severity.Error);
|
||||
repl.appendToRepl('third line', severity.Warning);
|
||||
repl.appendToRepl('fourth line', severity.Error);
|
||||
|
||||
let elements = <SimpleReplElement[]>repl.getReplElements();
|
||||
assert.equal(elements.length, 4);
|
||||
assert.equal(elements[0].value, 'first line');
|
||||
assert.equal(elements[0].severity, severity.Error);
|
||||
assert.equal(elements[1].value, 'second line');
|
||||
assert.equal(elements[1].severity, severity.Error);
|
||||
assert.equal(elements[2].value, 'third line');
|
||||
assert.equal(elements[2].severity, severity.Warning);
|
||||
assert.equal(elements[3].value, 'fourth line');
|
||||
assert.equal(elements[3].severity, severity.Error);
|
||||
|
||||
repl.appendToRepl('1', severity.Warning);
|
||||
elements = <SimpleReplElement[]>repl.getReplElements();
|
||||
assert.equal(elements.length, 5);
|
||||
assert.equal(elements[4].value, '1');
|
||||
assert.equal(elements[4].severity, severity.Warning);
|
||||
|
||||
const keyValueObject = { 'key1': 2, 'key2': 'value' };
|
||||
repl.appendToRepl(new RawObjectReplElement('fakeid', 'fake', keyValueObject), severity.Info);
|
||||
const element = <RawObjectReplElement>repl.getReplElements()[5];
|
||||
assert.equal(element.value, 'Object');
|
||||
assert.deepEqual(element.valueObj, keyValueObject);
|
||||
|
||||
repl.removeReplExpressions();
|
||||
assert.equal(repl.getReplElements().length, 0);
|
||||
});
|
||||
});
|
||||
192
src/vs/workbench/contrib/debug/test/node/debugger.test.ts
Normal file
192
src/vs/workbench/contrib/debug/test/node/debugger.test.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { join, normalize } from 'vs/base/common/path';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IDebugAdapterExecutable, IConfigurationManager, IConfig, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Debugger } from 'vs/workbench/contrib/debug/node/debugger';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExecutableDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
|
||||
suite('Debug - Debugger', () => {
|
||||
let _debugger: Debugger;
|
||||
|
||||
const extensionFolderPath = '/a/b/c/';
|
||||
const debuggerContribution = {
|
||||
type: 'mock',
|
||||
label: 'Mock Debug',
|
||||
enableBreakpointsFor: { 'languageIds': ['markdown'] },
|
||||
program: './out/mock/mockDebug.js',
|
||||
args: ['arg1', 'arg2'],
|
||||
configurationAttributes: {
|
||||
launch: {
|
||||
required: ['program'],
|
||||
properties: {
|
||||
program: {
|
||||
'type': 'string',
|
||||
'description': 'Workspace relative path to a text file.',
|
||||
'default': 'readme.md'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
variables: null!,
|
||||
initialConfigurations: [
|
||||
{
|
||||
name: 'Mock-Debug',
|
||||
type: 'mock',
|
||||
request: 'launch',
|
||||
program: 'readme.md'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const extensionDescriptor0 = <IExtensionDescription>{
|
||||
id: 'adapter',
|
||||
identifier: new ExtensionIdentifier('adapter'),
|
||||
name: 'myAdapter',
|
||||
version: '1.0.0',
|
||||
publisher: 'vscode',
|
||||
extensionLocation: URI.file(extensionFolderPath),
|
||||
isBuiltin: false,
|
||||
isUnderDevelopment: false,
|
||||
engines: null!,
|
||||
contributes: {
|
||||
'debuggers': [
|
||||
debuggerContribution
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const extensionDescriptor1 = {
|
||||
id: 'extension1',
|
||||
identifier: new ExtensionIdentifier('extension1'),
|
||||
name: 'extension1',
|
||||
version: '1.0.0',
|
||||
publisher: 'vscode',
|
||||
extensionLocation: URI.file('/e1/b/c/'),
|
||||
isBuiltin: false,
|
||||
isUnderDevelopment: false,
|
||||
engines: null!,
|
||||
contributes: {
|
||||
'debuggers': [
|
||||
{
|
||||
type: 'mock',
|
||||
runtime: 'runtime',
|
||||
runtimeArgs: ['rarg'],
|
||||
program: 'mockprogram',
|
||||
args: ['parg']
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const extensionDescriptor2 = {
|
||||
id: 'extension2',
|
||||
identifier: new ExtensionIdentifier('extension2'),
|
||||
name: 'extension2',
|
||||
version: '1.0.0',
|
||||
publisher: 'vscode',
|
||||
extensionLocation: URI.file('/e2/b/c/'),
|
||||
isBuiltin: false,
|
||||
isUnderDevelopment: false,
|
||||
engines: null!,
|
||||
contributes: {
|
||||
'debuggers': [
|
||||
{
|
||||
type: 'mock',
|
||||
win: {
|
||||
runtime: 'winRuntime',
|
||||
program: 'winProgram'
|
||||
},
|
||||
linux: {
|
||||
runtime: 'linuxRuntime',
|
||||
program: 'linuxProgram'
|
||||
},
|
||||
osx: {
|
||||
runtime: 'osxRuntime',
|
||||
program: 'osxProgram'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const configurationManager = <IConfigurationManager>{
|
||||
getDebugAdapterDescriptor(session: IDebugSession, config: IConfig): Promise<IDebugAdapterExecutable | undefined> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const configurationService = new TestConfigurationService();
|
||||
const testResourcePropertiesService = new TestTextResourcePropertiesService(configurationService);
|
||||
|
||||
setup(() => {
|
||||
_debugger = new Debugger(configurationManager, debuggerContribution, extensionDescriptor0, configurationService, testResourcePropertiesService, undefined!, undefined!, undefined!);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
_debugger = null!;
|
||||
});
|
||||
|
||||
test('attributes', () => {
|
||||
assert.equal(_debugger.type, debuggerContribution.type);
|
||||
assert.equal(_debugger.label, debuggerContribution.label);
|
||||
|
||||
const ae = ExecutableDebugAdapter.platformAdapterExecutable([extensionDescriptor0], 'mock');
|
||||
|
||||
assert.equal(ae!.command, join(extensionFolderPath, debuggerContribution.program));
|
||||
assert.deepEqual(ae!.args, debuggerContribution.args);
|
||||
});
|
||||
|
||||
test('schema attributes', () => {
|
||||
const schemaAttribute = _debugger.getSchemaAttributes()![0];
|
||||
assert.notDeepEqual(schemaAttribute, debuggerContribution.configurationAttributes);
|
||||
Object.keys(debuggerContribution.configurationAttributes.launch).forEach(key => {
|
||||
assert.deepEqual(schemaAttribute[key], debuggerContribution.configurationAttributes.launch[key]);
|
||||
});
|
||||
|
||||
assert.equal(schemaAttribute['additionalProperties'], false);
|
||||
assert.equal(!!schemaAttribute['properties']!['request'], true);
|
||||
assert.equal(!!schemaAttribute['properties']!['name'], true);
|
||||
assert.equal(!!schemaAttribute['properties']!['type'], true);
|
||||
assert.equal(!!schemaAttribute['properties']!['preLaunchTask'], true);
|
||||
});
|
||||
|
||||
test('merge platform specific attributes', () => {
|
||||
const ae = ExecutableDebugAdapter.platformAdapterExecutable([extensionDescriptor1, extensionDescriptor2], 'mock')!;
|
||||
assert.equal(ae.command, platform.isLinux ? 'linuxRuntime' : (platform.isMacintosh ? 'osxRuntime' : 'winRuntime'));
|
||||
const xprogram = platform.isLinux ? 'linuxProgram' : (platform.isMacintosh ? 'osxProgram' : 'winProgram');
|
||||
assert.deepEqual(ae.args, ['rarg', normalize('/e2/b/c/') + xprogram, 'parg']);
|
||||
});
|
||||
|
||||
test('initial config file content', () => {
|
||||
|
||||
const expected = ['{',
|
||||
' // Use IntelliSense to learn about possible attributes.',
|
||||
' // Hover to view descriptions of existing attributes.',
|
||||
' // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387',
|
||||
' "version": "0.2.0",',
|
||||
' "configurations": [',
|
||||
' {',
|
||||
' "name": "Mock-Debug",',
|
||||
' "type": "mock",',
|
||||
' "request": "launch",',
|
||||
' "program": "readme.md"',
|
||||
' }',
|
||||
' ]',
|
||||
'}'].join(testResourcePropertiesService.getEOL(URI.file('somefile')));
|
||||
|
||||
return _debugger.getInitialConfigurationContent().then(content => {
|
||||
assert.equal(content, expected);
|
||||
}, err => assert.fail(err));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user