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

import assert = require('assert');
import uri from 'vs/base/common/uri';
I
isidor 已提交
8
import severity from 'vs/base/common/severity';
E
Erich Gamma 已提交
9
import debugmodel = require('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: debugmodel.Model;
15
	let rawSession: MockSession;
E
Erich Gamma 已提交
16 17

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

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

	// Breakpoints

	test('breakpoints simple', () => {
		var 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 39
		assert.equal(model.getBreakpoints().length, 0);
	});

	test('breakpoints toggling', () => {
		var 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 53
		assert.equal(model.areBreakpointsActivated(), true);
	});

	test('breakpoints two files', () => {
		var modelUri1 = uri.file('/myfolder/my file first.js');
I
isidor 已提交
54
		var 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 59 60

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

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

74
		model.removeBreakpoints(model.getBreakpoints().filter(bp => bp.uri.toString() === modelUri1.toString()));
E
Erich Gamma 已提交
75 76 77 78 79 80 81
		assert.equal(model.getBreakpoints().length, 3);
	});

	// Threads

	test('threads simple', () => {
		var threadId = 1;
82
		var threadName = 'firstThread';
83

I
isidor 已提交
84
		model.addProcess('mockProcess', rawSession);
E
Erich Gamma 已提交
85
		model.rawUpdate({
86
			sessionId: rawSession.getId(),
E
Erich Gamma 已提交
87 88 89 90 91 92
			threadId: threadId,
			thread: {
				id: threadId,
				name: threadName
			}
		});
I
isidor 已提交
93
		const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop();
E
Erich Gamma 已提交
94

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

I
isidor 已提交
97 98
		model.clearThreads(process.getId(), true);
		assert.equal(process.getThread(threadId), null);
E
Erich Gamma 已提交
99
	});
I
isidor 已提交
100

101
	test('threads multiple wtih allThreadsStopped', () => {
102
		const sessionStub = sinon.spy(rawSession, 'stackTrace');
103 104

		const threadId1 = 1;
105
		const threadName1 = 'firstThread';
106
		const threadId2 = 2;
107 108
		const threadName2 = 'secondThread';
		const stoppedReason = 'breakpoint';
109 110

		// Add the threads
I
isidor 已提交
111
		model.addProcess('mockProcess', rawSession);
112
		model.rawUpdate({
113
			sessionId: rawSession.getId(),
114 115 116 117 118 119 120 121
			threadId: threadId1,
			thread: {
				id: threadId1,
				name: threadName1
			}
		});

		model.rawUpdate({
122
			sessionId: rawSession.getId(),
123 124 125 126 127 128 129 130 131
			threadId: threadId2,
			thread: {
				id: threadId2,
				name: threadName2
			}
		});

		// Stopped event with all threads stopped
		model.rawUpdate({
132
			sessionId: rawSession.getId(),
133 134 135 136 137 138 139
			threadId: threadId1,
			stoppedDetails: {
				reason: stoppedReason,
				threadId: 1
			},
			allThreadsStopped: true
		});
I
isidor 已提交
140
		const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop();
141

I
isidor 已提交
142 143
		const thread1 = process.getThread(threadId1);
		const thread2 = process.getThread(threadId2);
144 145

		// at the beginning, callstacks are obtainable but not available
I
isidor 已提交
146
		assert.equal(process.getAllThreads().length, 2);
147 148
		assert.equal(thread1.name, threadName1);
		assert.equal(thread1.stopped, true);
149
		assert.equal(thread1.getCallStack(), undefined);
150 151 152
		assert.equal(thread1.stoppedDetails.reason, stoppedReason);
		assert.equal(thread2.name, threadName2);
		assert.equal(thread2.stopped, true);
153
		assert.equal(thread2.getCallStack(), undefined);
154 155 156 157
		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
158 159 160
		thread1.fetchCallStack().then(() => {
			assert.notEqual(thread1.getCallStack(), undefined);
			assert.equal(thread2.getCallStack(), undefined);
161 162 163
			assert.equal(sessionStub.callCount, 1);
		});

164 165 166
		thread2.fetchCallStack().then(() => {
			assert.notEqual(thread1.getCallStack(), undefined);
			assert.notEqual(thread2.getCallStack(), undefined);
167 168 169 170 171
			assert.equal(sessionStub.callCount, 2);
		});

		// calling multiple times getCallStack doesn't result in multiple calls
		// to the debug adapter
172 173
		thread1.fetchCallStack().then(() => {
			return thread2.fetchCallStack();
174 175 176 177 178 179 180
		}).then(() => {
			assert.equal(sessionStub.callCount, 2);
		});

		// clearing the callstack results in the callstack not being available
		thread1.clearCallStack();
		assert.equal(thread1.stopped, true);
181
		assert.equal(thread1.getCallStack(), undefined);
182 183 184

		thread2.clearCallStack();
		assert.equal(thread2.stopped, true);
185
		assert.equal(thread2.getCallStack(), undefined);
186

187
		model.clearThreads(process.getId(), true);
I
isidor 已提交
188 189 190
		assert.equal(process.getThread(threadId1), null);
		assert.equal(process.getThread(threadId2), null);
		assert.equal(process.getAllThreads().length, 0);
191 192 193
	});

	test('threads mutltiple without allThreadsStopped', () => {
194
		const sessionStub = sinon.spy(rawSession, 'stackTrace');
195 196

		const stoppedThreadId = 1;
197
		const stoppedThreadName = 'stoppedThread';
198
		const runningThreadId = 2;
199 200
		const runningThreadName = 'runningThread';
		const stoppedReason = 'breakpoint';
I
isidor 已提交
201
		model.addProcess('mockProcess', rawSession);
202 203
		// Add the threads
		model.rawUpdate({
204
			sessionId: rawSession.getId(),
205 206 207 208 209 210 211 212
			threadId: stoppedThreadId,
			thread: {
				id: stoppedThreadId,
				name: stoppedThreadName
			}
		});

		model.rawUpdate({
213
			sessionId: rawSession.getId(),
214 215 216 217 218 219 220 221 222
			threadId: runningThreadId,
			thread: {
				id: runningThreadId,
				name: runningThreadName
			}
		});

		// Stopped event with only one thread stopped
		model.rawUpdate({
223
			sessionId: rawSession.getId(),
224 225 226 227 228 229 230
			threadId: stoppedThreadId,
			stoppedDetails: {
				reason: stoppedReason,
				threadId: 1
			},
			allThreadsStopped: false
		});
I
isidor 已提交
231
		const process = model.getProcesses().filter(p => p.getId() === rawSession.getId()).pop();
232

I
isidor 已提交
233 234
		const stoppedThread = process.getThread(stoppedThreadId);
		const runningThread = process.getThread(runningThreadId);
235 236 237 238 239

		// 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 已提交
240
		assert.equal(process.getAllThreads().length, 2);
241
		assert.equal(stoppedThread.getCallStack(), undefined);
242 243 244
		assert.equal(stoppedThread.stoppedDetails.reason, stoppedReason);
		assert.equal(runningThread.name, runningThreadName);
		assert.equal(runningThread.stopped, false);
245
		assert.equal(runningThread.getCallStack(), undefined);
246 247 248 249
		assert.equal(runningThread.stoppedDetails, undefined);

		// after calling getCallStack, the callstack becomes available
		// and results in a request for the callstack in the debug adapter
250 251 252
		stoppedThread.fetchCallStack().then(() => {
			assert.notEqual(stoppedThread.getCallStack(), undefined);
			assert.equal(runningThread.getCallStack(), undefined);
253 254 255 256 257 258
			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
259
		runningThread.fetchCallStack().then(callStack => {
260 261 262 263 264 265
			assert.deepEqual(callStack, []);
			assert.equal(sessionStub.callCount, 1);
		});

		// calling multiple times getCallStack doesn't result in multiple calls
		// to the debug adapter
266
		stoppedThread.fetchCallStack().then(() => {
267 268 269 270 271 272
			assert.equal(sessionStub.callCount, 1);
		});

		// clearing the callstack results in the callstack not being available
		stoppedThread.clearCallStack();
		assert.equal(stoppedThread.stopped, true);
273
		assert.equal(stoppedThread.getCallStack(), undefined);
274

275
		model.clearThreads(process.getId(), true);
I
isidor 已提交
276 277 278
		assert.equal(process.getThread(stoppedThreadId), null);
		assert.equal(process.getThread(runningThreadId), null);
		assert.equal(process.getAllThreads().length, 0 );
279 280
	});

I
isidor 已提交
281 282 283 284 285 286 287 288 289 290 291 292
	// Expressions

	function assertWatchExpressions(watchExpressions: debugmodel.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', () => {
I
isidor 已提交
293
		assert.equal(model.getWatchExpressions().length, 0);
I
isidor 已提交
294
		const process = new debugmodel.Process('mockProcess', rawSession);
295 296
		const thread = new debugmodel.Thread(process, 'mockthread', 1);
		const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1);
297 298
		model.addWatchExpression(process, stackFrame, 'console').done();
		model.addWatchExpression(process, stackFrame, 'console').done();
I
isidor 已提交
299 300 301
		const watchExpressions = model.getWatchExpressions();
		assertWatchExpressions(watchExpressions, 'console');

302 303
		model.renameWatchExpression(process, stackFrame, watchExpressions[0].getId(), 'new_name').done();
		model.renameWatchExpression(process, stackFrame, watchExpressions[1].getId(), 'new_name').done();
I
isidor 已提交
304 305
		assertWatchExpressions(model.getWatchExpressions(), 'new_name');

306
		model.evaluateWatchExpressions(process, null);
I
isidor 已提交
307 308
		assertWatchExpressions(model.getWatchExpressions(), 'new_name');

309
		model.removeWatchExpressions();
I
isidor 已提交
310 311 312 313
		assert.equal(model.getWatchExpressions().length, 0);
	});

	test('repl expressions', () => {
I
isidor 已提交
314
		assert.equal(model.getReplElements().length, 0);
I
isidor 已提交
315
		const process = new debugmodel.Process('mockProcess', rawSession);
316 317
		const thread = new debugmodel.Thread(process, 'mockthread', 1);
		const stackFrame = new debugmodel.StackFrame(thread, 1, null, 'app.js', 1, 1);
318 319 320
		model.addReplExpression(process, stackFrame, 'myVariable').done();
		model.addReplExpression(process, stackFrame, 'myVariable').done();
		model.addReplExpression(process, stackFrame, 'myVariable').done();
I
isidor 已提交
321 322 323

		assert.equal(model.getReplElements().length, 3);
		model.getReplElements().forEach(re => {
J
Johannes Rieken 已提交
324 325 326
			assert.equal((<debugmodel.Expression>re).available, false);
			assert.equal((<debugmodel.Expression>re).name, 'myVariable');
			assert.equal((<debugmodel.Expression>re).reference, 0);
I
isidor 已提交
327 328
		});

329
		model.removeReplExpressions();
I
isidor 已提交
330 331 332 333 334 335 336 337 338 339 340
		assert.equal(model.getReplElements().length, 0);
	});

	// Repl output

	test('repl output', () => {
		model.logToRepl('first line', severity.Error);
		model.logToRepl('second line', severity.Warning);
		model.logToRepl('second line', severity.Warning);
		model.logToRepl('second line', severity.Error);

J
Johannes Rieken 已提交
341
		let elements = <debugmodel.ValueOutputElement[]>model.getReplElements();
I
isidor 已提交
342 343 344 345 346 347 348 349 350 351 352
		assert.equal(elements.length, 3);
		assert.equal(elements[0].value, 'first line');
		assert.equal(elements[0].counter, 1);
		assert.equal(elements[0].severity, severity.Error);
		assert.equal(elements[1].value, 'second line');
		assert.equal(elements[1].counter, 2);
		assert.equal(elements[1].severity, severity.Warning);

		model.appendReplOutput('1', severity.Error);
		model.appendReplOutput('2', severity.Error);
		model.appendReplOutput('3', severity.Error);
J
Johannes Rieken 已提交
353
		elements = <debugmodel.ValueOutputElement[]>model.getReplElements();
I
isidor 已提交
354 355 356 357
		assert.equal(elements.length, 4);
		assert.equal(elements[3].value, '123');
		assert.equal(elements[3].severity, severity.Error);

J
Johannes Rieken 已提交
358
		const keyValueObject = { 'key1': 2, 'key2': 'value' };
I
isidor 已提交
359
		model.logToRepl(keyValueObject);
J
Johannes Rieken 已提交
360
		const element = <debugmodel.KeyValueOutputElement>model.getReplElements()[4];
I
isidor 已提交
361 362 363
		assert.equal(element.value, 'Object');
		assert.deepEqual(element.valueObj, keyValueObject);

364
		model.removeReplExpressions();
I
isidor 已提交
365 366
		assert.equal(model.getReplElements().length, 0);
	});
E
Erich Gamma 已提交
367
});