debugModel.test.ts 14.3 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

I
isidor 已提交
6
import * as assert from 'assert';
E
Erich Gamma 已提交
7
import uri from 'vs/base/common/uri';
I
isidor 已提交
8
import severity from 'vs/base/common/severity';
9
import { OutputElement, Model, Process, Expression, OutputNameValueElement, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel';
10
import * as sinon from 'sinon';
11
import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug';
E
Erich Gamma 已提交
12 13

suite('Debug - Model', () => {
14
	let model: Model;
15
	let rawSession: MockSession;
E
Erich Gamma 已提交
16 17

	setup(() => {
18
		model = new Model([], true, [], [], []);
19
		rawSession = new MockSession();
E
Erich Gamma 已提交
20 21 22 23 24 25 26 27 28
	});

	teardown(() => {
		model = null;
	});

	// Breakpoints

	test('breakpoints simple', () => {
I
isidor 已提交
29
		const modelUri = uri.file('/myfolder/myfile.js');
30
		model.addBreakpoints(modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);
E
Erich Gamma 已提交
31 32 33
		assert.equal(model.areBreakpointsActivated(), true);
		assert.equal(model.getBreakpoints().length, 2);

34
		model.removeBreakpoints(model.getBreakpoints());
E
Erich Gamma 已提交
35 36 37 38
		assert.equal(model.getBreakpoints().length, 0);
	});

	test('breakpoints toggling', () => {
I
isidor 已提交
39
		const modelUri = uri.file('/myfolder/myfile.js');
40 41
		model.addBreakpoints(modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);
		model.addBreakpoints(modelUri, [{ lineNumber: 12, enabled: true, condition: 'fake condition' }]);
E
Erich Gamma 已提交
42
		assert.equal(model.getBreakpoints().length, 3);
43
		model.removeBreakpoints([model.getBreakpoints().pop()]);
E
Erich Gamma 已提交
44 45
		assert.equal(model.getBreakpoints().length, 2);

46
		model.setBreakpointsActivated(false);
E
Erich Gamma 已提交
47
		assert.equal(model.areBreakpointsActivated(), false);
48
		model.setBreakpointsActivated(true);
E
Erich Gamma 已提交
49 50 51 52
		assert.equal(model.areBreakpointsActivated(), true);
	});

	test('breakpoints two files', () => {
I
isidor 已提交
53 54
		const modelUri1 = uri.file('/myfolder/my file first.js');
		const modelUri2 = uri.file('/secondfolder/second/second file.js');
55 56
		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 }]);
E
Erich Gamma 已提交
57 58

		assert.equal(model.getBreakpoints().length, 5);
I
isidor 已提交
59
		const bp = model.getBreakpoints()[0];
J
Johannes Rieken 已提交
60
		const update: any = {};
61 62
		update[bp.getId()] = { line: 100, verified: false };
		model.updateBreakpoints(update);
E
Erich Gamma 已提交
63 64 65 66 67 68
		assert.equal(bp.lineNumber, 100);

		model.enableOrDisableAllBreakpoints(false);
		model.getBreakpoints().forEach(bp => {
			assert.equal(bp.enabled, false);
		});
69
		model.setEnablement(bp, true);
E
Erich Gamma 已提交
70 71
		assert.equal(bp.enabled, true);

72
		model.removeBreakpoints(model.getBreakpoints().filter(bp => bp.uri.toString() === modelUri1.toString()));
E
Erich Gamma 已提交
73 74 75
		assert.equal(model.getBreakpoints().length, 3);
	});

I
isidor 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	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);
	});

E
Erich Gamma 已提交
91 92 93
	// Threads

	test('threads simple', () => {
I
isidor 已提交
94 95
		const threadId = 1;
		const threadName = 'firstThread';
96

97
		model.addProcess({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession);
I
isidor 已提交
98
		assert.equal(model.getProcesses().length, 1);
E
Erich Gamma 已提交
99
		model.rawUpdate({
100
			sessionId: rawSession.getId(),
E
Erich Gamma 已提交
101 102 103 104 105 106
			threadId: threadId,
			thread: {
				id: threadId,
				name: threadName
			}
		});
I
isidor 已提交
107
		const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop();
E
Erich Gamma 已提交
108

I
isidor 已提交
109
		assert.equal(process.getThread(threadId).name, threadName);
E
Erich Gamma 已提交
110

I
isidor 已提交
111 112
		model.clearThreads(process.getId(), true);
		assert.equal(process.getThread(threadId), null);
I
isidor 已提交
113 114 115
		assert.equal(model.getProcesses().length, 1);
		model.removeProcess(process.getId());
		assert.equal(model.getProcesses().length, 0);
E
Erich Gamma 已提交
116
	});
I
isidor 已提交
117

118
	test('threads multiple wtih allThreadsStopped', () => {
119
		const sessionStub = sinon.spy(rawSession, 'stackTrace');
120 121

		const threadId1 = 1;
122
		const threadName1 = 'firstThread';
123
		const threadId2 = 2;
124 125
		const threadName2 = 'secondThread';
		const stoppedReason = 'breakpoint';
126 127

		// Add the threads
128
		model.addProcess({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession);
129
		model.rawUpdate({
130
			sessionId: rawSession.getId(),
131 132 133 134 135 136 137 138
			threadId: threadId1,
			thread: {
				id: threadId1,
				name: threadName1
			}
		});

		model.rawUpdate({
139
			sessionId: rawSession.getId(),
140 141 142 143 144 145 146 147 148
			threadId: threadId2,
			thread: {
				id: threadId2,
				name: threadName2
			}
		});

		// Stopped event with all threads stopped
		model.rawUpdate({
149
			sessionId: rawSession.getId(),
150 151 152 153 154 155 156
			threadId: threadId1,
			stoppedDetails: {
				reason: stoppedReason,
				threadId: 1
			},
			allThreadsStopped: true
		});
I
isidor 已提交
157
		const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop();
158

I
isidor 已提交
159 160
		const thread1 = process.getThread(threadId1);
		const thread2 = process.getThread(threadId2);
161 162

		// at the beginning, callstacks are obtainable but not available
I
isidor 已提交
163
		assert.equal(process.getAllThreads().length, 2);
164 165
		assert.equal(thread1.name, threadName1);
		assert.equal(thread1.stopped, true);
I
isidor 已提交
166
		assert.equal(thread1.getCallStack().length, 0);
167 168 169
		assert.equal(thread1.stoppedDetails.reason, stoppedReason);
		assert.equal(thread2.name, threadName2);
		assert.equal(thread2.stopped, true);
I
isidor 已提交
170
		assert.equal(thread2.getCallStack().length, 0);
171 172 173 174
		assert.equal(thread2.stoppedDetails.reason, stoppedReason);

		// after calling getCallStack, the callstack becomes available
		// and results in a request for the callstack in the debug adapter
175
		thread1.fetchCallStack().then(() => {
I
isidor 已提交
176 177
			assert.notEqual(thread1.getCallStack().length, 0);
			assert.equal(thread2.getCallStack().length, 0);
178 179 180
			assert.equal(sessionStub.callCount, 1);
		});

181
		thread2.fetchCallStack().then(() => {
I
isidor 已提交
182 183
			assert.notEqual(thread1.getCallStack().length, 0);
			assert.notEqual(thread2.getCallStack().length, 0);
184 185 186 187 188
			assert.equal(sessionStub.callCount, 2);
		});

		// calling multiple times getCallStack doesn't result in multiple calls
		// to the debug adapter
189 190
		thread1.fetchCallStack().then(() => {
			return thread2.fetchCallStack();
191
		}).then(() => {
I
isidor 已提交
192
			assert.equal(sessionStub.callCount, 4);
193 194 195 196 197
		});

		// clearing the callstack results in the callstack not being available
		thread1.clearCallStack();
		assert.equal(thread1.stopped, true);
I
isidor 已提交
198
		assert.equal(thread1.getCallStack().length, 0);
199 200 201

		thread2.clearCallStack();
		assert.equal(thread2.stopped, true);
I
isidor 已提交
202
		assert.equal(thread2.getCallStack().length, 0);
203

204
		model.clearThreads(process.getId(), true);
I
isidor 已提交
205 206 207
		assert.equal(process.getThread(threadId1), null);
		assert.equal(process.getThread(threadId2), null);
		assert.equal(process.getAllThreads().length, 0);
208 209 210
	});

	test('threads mutltiple without allThreadsStopped', () => {
211
		const sessionStub = sinon.spy(rawSession, 'stackTrace');
212 213

		const stoppedThreadId = 1;
214
		const stoppedThreadName = 'stoppedThread';
215
		const runningThreadId = 2;
216 217
		const runningThreadName = 'runningThread';
		const stoppedReason = 'breakpoint';
218
		model.addProcess({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession);
219 220
		// Add the threads
		model.rawUpdate({
221
			sessionId: rawSession.getId(),
222 223 224 225 226 227 228 229
			threadId: stoppedThreadId,
			thread: {
				id: stoppedThreadId,
				name: stoppedThreadName
			}
		});

		model.rawUpdate({
230
			sessionId: rawSession.getId(),
231 232 233 234 235 236 237 238 239
			threadId: runningThreadId,
			thread: {
				id: runningThreadId,
				name: runningThreadName
			}
		});

		// Stopped event with only one thread stopped
		model.rawUpdate({
240
			sessionId: rawSession.getId(),
241 242 243 244 245 246 247
			threadId: stoppedThreadId,
			stoppedDetails: {
				reason: stoppedReason,
				threadId: 1
			},
			allThreadsStopped: false
		});
I
isidor 已提交
248
		const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop();
249

I
isidor 已提交
250 251
		const stoppedThread = process.getThread(stoppedThreadId);
		const runningThread = process.getThread(runningThreadId);
252 253 254 255 256

		// 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);
I
isidor 已提交
257
		assert.equal(process.getAllThreads().length, 2);
I
isidor 已提交
258
		assert.equal(stoppedThread.getCallStack().length, 0);
259 260 261
		assert.equal(stoppedThread.stoppedDetails.reason, stoppedReason);
		assert.equal(runningThread.name, runningThreadName);
		assert.equal(runningThread.stopped, false);
I
isidor 已提交
262
		assert.equal(runningThread.getCallStack().length, 0);
263 264 265 266
		assert.equal(runningThread.stoppedDetails, undefined);

		// after calling getCallStack, the callstack becomes available
		// and results in a request for the callstack in the debug adapter
267
		stoppedThread.fetchCallStack().then(() => {
I
isidor 已提交
268 269
			assert.notEqual(stoppedThread.getCallStack().length, 0);
			assert.equal(runningThread.getCallStack().length, 0);
270 271 272 273 274 275
			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
I
isidor 已提交
276 277
		runningThread.fetchCallStack().then(() => {
			assert.equal(runningThread.getCallStack().length, 0);
278 279 280 281 282 283
			assert.equal(sessionStub.callCount, 1);
		});

		// clearing the callstack results in the callstack not being available
		stoppedThread.clearCallStack();
		assert.equal(stoppedThread.stopped, true);
I
isidor 已提交
284
		assert.equal(stoppedThread.getCallStack().length, 0);
285

286
		model.clearThreads(process.getId(), true);
I
isidor 已提交
287 288
		assert.equal(process.getThread(stoppedThreadId), null);
		assert.equal(process.getThread(runningThreadId), null);
289
		assert.equal(process.getAllThreads().length, 0);
290 291
	});

I
isidor 已提交
292 293
	// Expressions

294
	function assertWatchExpressions(watchExpressions: Expression[], expectedName: string) {
I
isidor 已提交
295 296 297 298 299 300 301 302 303
		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', () => {
I
isidor 已提交
304
		assert.equal(model.getWatchExpressions().length, 0);
305
		const process = new Process({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession);
306
		const thread = new Thread(process, 'mockthread', 1);
307
		const stackFrame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: undefined, endColumn: undefined }, 0);
308 309
		model.addWatchExpression(process, stackFrame, 'console').done();
		model.addWatchExpression(process, stackFrame, 'console').done();
I
isidor 已提交
310
		let watchExpressions = model.getWatchExpressions();
I
isidor 已提交
311 312
		assertWatchExpressions(watchExpressions, 'console');

313 314
		model.renameWatchExpression(process, stackFrame, watchExpressions[0].getId(), 'new_name').done();
		model.renameWatchExpression(process, stackFrame, watchExpressions[1].getId(), 'new_name').done();
I
isidor 已提交
315 316
		assertWatchExpressions(model.getWatchExpressions(), 'new_name');

317
		model.evaluateWatchExpressions(process, null);
I
isidor 已提交
318 319
		assertWatchExpressions(model.getWatchExpressions(), 'new_name');

I
isidor 已提交
320 321 322 323 324 325 326
		model.addWatchExpression(process, stackFrame, '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');

327
		model.removeWatchExpressions();
I
isidor 已提交
328 329 330 331
		assert.equal(model.getWatchExpressions().length, 0);
	});

	test('repl expressions', () => {
I
isidor 已提交
332
		assert.equal(model.getReplElements().length, 0);
333
		const process = new Process({ name: 'mockProcess', type: 'node', request: 'launch' }, rawSession);
334
		const thread = new Thread(process, 'mockthread', 1);
335
		const stackFrame = new StackFrame(thread, 1, null, 'app.js', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1);
336 337 338
		model.addReplExpression(process, stackFrame, 'myVariable').done();
		model.addReplExpression(process, stackFrame, 'myVariable').done();
		model.addReplExpression(process, stackFrame, 'myVariable').done();
I
isidor 已提交
339 340 341

		assert.equal(model.getReplElements().length, 3);
		model.getReplElements().forEach(re => {
342 343 344
			assert.equal((<Expression>re).available, false);
			assert.equal((<Expression>re).name, 'myVariable');
			assert.equal((<Expression>re).reference, 0);
I
isidor 已提交
345 346
		});

347
		model.removeReplExpressions();
I
isidor 已提交
348 349 350 351 352 353
		assert.equal(model.getReplElements().length, 0);
	});

	// Repl output

	test('repl output', () => {
I
isidor 已提交
354
		model.appendToRepl('first line\n', severity.Error);
355
		model.appendToRepl('second line', severity.Error);
I
isidor 已提交
356 357 358 359
		model.appendToRepl('second line', severity.Error);
		model.appendToRepl('third line', severity.Warning);
		model.appendToRepl('third line', severity.Warning);
		model.appendToRepl('fourth line', severity.Error);
I
isidor 已提交
360

361
		let elements = <OutputElement[]>model.getReplElements();
I
isidor 已提交
362
		assert.equal(elements.length, 4);
363
		assert.equal(elements[0].value, 'first line');
I
isidor 已提交
364 365
		assert.equal(elements[0].counter, 1);
		assert.equal(elements[0].severity, severity.Error);
366
		assert.equal(elements[1].value, 'second line');
I
isidor 已提交
367
		assert.equal(elements[1].counter, 2);
I
isidor 已提交
368 369 370 371 372 373 374
		assert.equal(elements[1].severity, severity.Error);
		assert.equal(elements[2].value, 'third line');
		assert.equal(elements[2].counter, 2);
		assert.equal(elements[2].severity, severity.Warning);
		assert.equal(elements[3].value, 'fourth line');
		assert.equal(elements[3].counter, 1);
		assert.equal(elements[3].severity, severity.Error);
I
isidor 已提交
375

I
isidor 已提交
376
		model.appendToRepl('1', severity.Warning);
377
		elements = <OutputElement[]>model.getReplElements();
I
isidor 已提交
378 379 380
		assert.equal(elements.length, 5);
		assert.equal(elements[4].value, '1');
		assert.equal(elements[4].severity, severity.Warning);
I
isidor 已提交
381

J
Johannes Rieken 已提交
382
		const keyValueObject = { 'key1': 2, 'key2': 'value' };
I
isidor 已提交
383
		model.appendToRepl(new OutputNameValueElement('fake', keyValueObject), null);
I
isidor 已提交
384
		const element = <OutputNameValueElement>model.getReplElements()[5];
I
isidor 已提交
385 386 387
		assert.equal(element.value, 'Object');
		assert.deepEqual(element.valueObj, keyValueObject);

388
		model.removeReplExpressions();
I
isidor 已提交
389 390
		assert.equal(model.getReplElements().length, 0);
	});
E
Erich Gamma 已提交
391
});