Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { MarkdownString } from '../../../../../base/common/htmlContent.js';
8
import { dispose } from '../../../../../base/common/lifecycle.js';
9
import { URI as uri } from '../../../../../base/common/uri.js';
10
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
11
import { Range } from '../../../../../editor/common/core/range.js';
12
import { ILanguageService } from '../../../../../editor/common/languages/language.js';
13
import { OverviewRulerLane } from '../../../../../editor/common/model.js';
14
import { LanguageService } from '../../../../../editor/common/services/languageService.js';
15
import { createTextModel } from '../../../../../editor/test/common/testTextModel.js';
16
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
17
import { ILabelService } from '../../../../../platform/label/common/label.js';
18
import { NullLogService } from '../../../../../platform/log/common/log.js';
19
import { StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
20
import { createBreakpointDecorations } from '../../browser/breakpointEditorContribution.js';
21
import { getBreakpointMessageAndIcon, getExpandedBodySize } from '../../browser/breakpointsView.js';
22
import { DataBreakpointSetType, IBreakpointData, IBreakpointUpdateData, IDebugService, State } from '../../common/debug.js';
23
import { Breakpoint, DebugModel } from '../../common/debugModel.js';
24
import { createTestSession } from './callStack.test.js';
25
import { createMockDebugModel, mockUriIdentityService } from './mockDebugModel.js';
26
import { MockDebugService, MockDebugStorage } from '../common/mockDebug.js';
27
import { MockLabelService } from '../../../../services/label/test/common/mockLabelService.js';
28
import { TestStorageService } from '../../../../test/common/workbenchTestServices.js';
29
30
function addBreakpointsAndCheckEvents(model: DebugModel, uri: uri, data: IBreakpointData[]) {
31
let eventCount = 0;
32
const toDispose = model.onDidChangeBreakpoints(e => {
33
assert.strictEqual(e?.sessionOnly, false);
34
assert.strictEqual(e?.changed, undefined);
35
assert.strictEqual(e?.removed, undefined);
36
const added = e?.added;
37
assert.notStrictEqual(added, undefined);
38
assert.strictEqual(added!.length, data.length);
39
eventCount++;
40
dispose(toDispose);
41
for (let i = 0; i < data.length; i++) {
42
assert.strictEqual(e.added![i] instanceof Breakpoint, true);
43
assert.strictEqual((e.added![i] as Breakpoint).lineNumber, data[i].lineNumber);
44
}
45
});
46
const bps = model.addBreakpoints(uri, data);
47
assert.strictEqual(eventCount, 1);
48
return bps;
49
}
50
51
suite('Debug - Breakpoints', () => {
52
let model: DebugModel;
53
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
54
55
setup(() => {
56
model = createMockDebugModel(disposables);
57
});
58
59
// Breakpoints
60
61
test('simple', () => {
62
const modelUri = uri.file('/myfolder/myfile.js');
63
64
addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);
65
assert.strictEqual(model.areBreakpointsActivated(), true);
66
assert.strictEqual(model.getBreakpoints().length, 2);
67
68
let eventCount = 0;
69
const toDispose = model.onDidChangeBreakpoints(e => {
70
eventCount++;
71
assert.strictEqual(e?.added, undefined);
72
assert.strictEqual(e?.sessionOnly, false);
73
assert.strictEqual(e?.removed?.length, 2);
74
assert.strictEqual(e?.changed, undefined);
75
76
dispose(toDispose);
77
});
78
79
model.removeBreakpoints(model.getBreakpoints());
80
assert.strictEqual(eventCount, 1);
81
assert.strictEqual(model.getBreakpoints().length, 0);
82
});
83
84
test('toggling', () => {
85
const modelUri = uri.file('/myfolder/myfile.js');
86
87
addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);
88
addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 12, enabled: true, condition: 'fake condition' }]);
89
assert.strictEqual(model.getBreakpoints().length, 3);
90
const bp = model.getBreakpoints().pop();
91
if (bp) {
92
model.removeBreakpoints([bp]);
93
}
94
assert.strictEqual(model.getBreakpoints().length, 2);
95
96
model.setBreakpointsActivated(false);
97
assert.strictEqual(model.areBreakpointsActivated(), false);
98
model.setBreakpointsActivated(true);
99
assert.strictEqual(model.areBreakpointsActivated(), true);
100
});
101
102
test('two files', () => {
103
const modelUri1 = uri.file('/myfolder/my file first.js');
104
const modelUri2 = uri.file('/secondfolder/second/second file.js');
105
addBreakpointsAndCheckEvents(model, modelUri1, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);
106
assert.strictEqual(getExpandedBodySize(model, undefined, 9), 44);
107
108
addBreakpointsAndCheckEvents(model, modelUri2, [{ lineNumber: 1, enabled: true }, { lineNumber: 2, enabled: true }, { lineNumber: 3, enabled: false }]);
109
assert.strictEqual(getExpandedBodySize(model, undefined, 9), 110);
110
111
assert.strictEqual(model.getBreakpoints().length, 5);
112
assert.strictEqual(model.getBreakpoints({ uri: modelUri1 }).length, 2);
113
assert.strictEqual(model.getBreakpoints({ uri: modelUri2 }).length, 3);
114
assert.strictEqual(model.getBreakpoints({ lineNumber: 5 }).length, 1);
115
assert.strictEqual(model.getBreakpoints({ column: 5 }).length, 0);
116
117
const bp = model.getBreakpoints()[0];
118
const update = new Map<string, IBreakpointUpdateData>();
119
update.set(bp.getId(), { lineNumber: 100 });
120
let eventFired = false;
121
const toDispose = model.onDidChangeBreakpoints(e => {
122
eventFired = true;
123
assert.strictEqual(e?.added, undefined);
124
assert.strictEqual(e?.removed, undefined);
125
assert.strictEqual(e?.changed?.length, 1);
126
dispose(toDispose);
127
});
128
model.updateBreakpoints(update);
129
assert.strictEqual(eventFired, true);
130
assert.strictEqual(bp.lineNumber, 100);
131
132
assert.strictEqual(model.getBreakpoints({ enabledOnly: true }).length, 3);
133
model.enableOrDisableAllBreakpoints(false);
134
model.getBreakpoints().forEach(bp => {
135
assert.strictEqual(bp.enabled, false);
136
});
137
assert.strictEqual(model.getBreakpoints({ enabledOnly: true }).length, 0);
138
139
model.setEnablement(bp, true);
140
assert.strictEqual(bp.enabled, true);
141
142
model.removeBreakpoints(model.getBreakpoints({ uri: modelUri1 }));
143
assert.strictEqual(getExpandedBodySize(model, undefined, 9), 66);
144
145
assert.strictEqual(model.getBreakpoints().length, 3);
146
});
147
148
test('conditions', () => {
149
const modelUri1 = uri.file('/myfolder/my file first.js');
150
addBreakpointsAndCheckEvents(model, modelUri1, [{ lineNumber: 5, condition: 'i < 5', hitCondition: '17' }, { lineNumber: 10, condition: 'j < 3' }]);
151
const breakpoints = model.getBreakpoints();
152
153
assert.strictEqual(breakpoints[0].condition, 'i < 5');
154
assert.strictEqual(breakpoints[0].hitCondition, '17');
155
assert.strictEqual(breakpoints[1].condition, 'j < 3');
156
assert.strictEqual(!!breakpoints[1].hitCondition, false);
157
158
assert.strictEqual(model.getBreakpoints().length, 2);
159
model.removeBreakpoints(model.getBreakpoints());
160
assert.strictEqual(model.getBreakpoints().length, 0);
161
});
162
163
test('function breakpoints', () => {
164
model.addFunctionBreakpoint({ name: 'foo' }, '1');
165
model.addFunctionBreakpoint({ name: 'bar' }, '2');
166
model.updateFunctionBreakpoint('1', { name: 'fooUpdated' });
167
model.updateFunctionBreakpoint('2', { name: 'barUpdated' });
168
169
const functionBps = model.getFunctionBreakpoints();
170
assert.strictEqual(functionBps[0].name, 'fooUpdated');
171
assert.strictEqual(functionBps[1].name, 'barUpdated');
172
173
model.removeFunctionBreakpoints();
174
assert.strictEqual(model.getFunctionBreakpoints().length, 0);
175
});
176
177
test('multiple sessions', () => {
178
const modelUri = uri.file('/myfolder/myfile.js');
179
addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 5, enabled: true, condition: 'x > 5' }, { lineNumber: 10, enabled: false }]);
180
const breakpoints = model.getBreakpoints();
181
const session = disposables.add(createTestSession(model));
182
const data = new Map<string, DebugProtocol.Breakpoint>();
183
184
assert.strictEqual(breakpoints[0].lineNumber, 5);
185
assert.strictEqual(breakpoints[1].lineNumber, 10);
186
187
data.set(breakpoints[0].getId(), { verified: false, line: 10 });
188
data.set(breakpoints[1].getId(), { verified: true, line: 50 });
189
model.setBreakpointSessionData(session.getId(), {}, data);
190
assert.strictEqual(breakpoints[0].lineNumber, 5);
191
assert.strictEqual(breakpoints[1].lineNumber, 50);
192
193
const session2 = disposables.add(createTestSession(model));
194
const data2 = new Map<string, DebugProtocol.Breakpoint>();
195
data2.set(breakpoints[0].getId(), { verified: true, line: 100 });
196
data2.set(breakpoints[1].getId(), { verified: true, line: 500 });
197
model.setBreakpointSessionData(session2.getId(), {}, data2);
198
199
// Breakpoint is verified only once, show that line
200
assert.strictEqual(breakpoints[0].lineNumber, 100);
201
// Breakpoint is verified two times, show the original line
202
assert.strictEqual(breakpoints[1].lineNumber, 10);
203
204
model.setBreakpointSessionData(session.getId(), {}, undefined);
205
// No more double session verification
206
assert.strictEqual(breakpoints[0].lineNumber, 100);
207
assert.strictEqual(breakpoints[1].lineNumber, 500);
208
209
assert.strictEqual(breakpoints[0].supported, false);
210
const data3 = new Map<string, DebugProtocol.Breakpoint>();
211
data3.set(breakpoints[0].getId(), { verified: true, line: 500 });
212
model.setBreakpointSessionData(session2.getId(), { supportsConditionalBreakpoints: true }, data2);
213
assert.strictEqual(breakpoints[0].supported, true);
214
});
215
216
test('exception breakpoints', () => {
217
let eventCount = 0;
218
disposables.add(model.onDidChangeBreakpoints(() => eventCount++));
219
model.setExceptionBreakpointsForSession("session-id-1", [{ filter: 'uncaught', label: 'UNCAUGHT', default: true }]);
220
assert.strictEqual(eventCount, 1);
221
let exceptionBreakpoints = model.getExceptionBreakpointsForSession("session-id-1");
222
assert.strictEqual(exceptionBreakpoints.length, 1);
223
assert.strictEqual(exceptionBreakpoints[0].filter, 'uncaught');
224
assert.strictEqual(exceptionBreakpoints[0].enabled, true);
225
226
model.setExceptionBreakpointsForSession("session-id-2", [{ filter: 'uncaught', label: 'UNCAUGHT' }, { filter: 'caught', label: 'CAUGHT' }]);
227
assert.strictEqual(eventCount, 2);
228
exceptionBreakpoints = model.getExceptionBreakpointsForSession("session-id-2");
229
assert.strictEqual(exceptionBreakpoints.length, 2);
230
assert.strictEqual(exceptionBreakpoints[0].filter, 'uncaught');
231
assert.strictEqual(exceptionBreakpoints[0].enabled, true);
232
assert.strictEqual(exceptionBreakpoints[1].filter, 'caught');
233
assert.strictEqual(exceptionBreakpoints[1].label, 'CAUGHT');
234
assert.strictEqual(exceptionBreakpoints[1].enabled, false);
235
236
model.setExceptionBreakpointsForSession("session-id-3", [{ filter: 'all', label: 'ALL' }]);
237
assert.strictEqual(eventCount, 3);
238
assert.strictEqual(model.getExceptionBreakpointsForSession("session-id-3").length, 1);
239
exceptionBreakpoints = model.getExceptionBreakpoints();
240
assert.strictEqual(exceptionBreakpoints[0].filter, 'uncaught');
241
assert.strictEqual(exceptionBreakpoints[0].enabled, true);
242
assert.strictEqual(exceptionBreakpoints[1].filter, 'caught');
243
assert.strictEqual(exceptionBreakpoints[1].label, 'CAUGHT');
244
assert.strictEqual(exceptionBreakpoints[1].enabled, false);
245
assert.strictEqual(exceptionBreakpoints[2].filter, 'all');
246
assert.strictEqual(exceptionBreakpoints[2].label, 'ALL');
247
});
248
249
test('exception breakpoints multiple sessions', () => {
250
let eventCount = 0;
251
disposables.add(model.onDidChangeBreakpoints(() => eventCount++));
252
253
model.setExceptionBreakpointsForSession("session-id-4", [{ filter: 'uncaught', label: 'UNCAUGHT', default: true }, { filter: 'caught', label: 'CAUGHT' }]);
254
model.setExceptionBreakpointFallbackSession("session-id-4");
255
assert.strictEqual(eventCount, 1);
256
let exceptionBreakpointsForSession = model.getExceptionBreakpointsForSession("session-id-4");
257
assert.strictEqual(exceptionBreakpointsForSession.length, 2);
258
assert.strictEqual(exceptionBreakpointsForSession[0].filter, 'uncaught');
259
assert.strictEqual(exceptionBreakpointsForSession[1].filter, 'caught');
260
261
model.setExceptionBreakpointsForSession("session-id-5", [{ filter: 'all', label: 'ALL' }, { filter: 'caught', label: 'CAUGHT' }]);
262
assert.strictEqual(eventCount, 2);
263
exceptionBreakpointsForSession = model.getExceptionBreakpointsForSession("session-id-5");
264
let exceptionBreakpointsForUndefined = model.getExceptionBreakpointsForSession(undefined);
265
assert.strictEqual(exceptionBreakpointsForSession.length, 2);
266
assert.strictEqual(exceptionBreakpointsForSession[0].filter, 'caught');
267
assert.strictEqual(exceptionBreakpointsForSession[1].filter, 'all');
268
assert.strictEqual(exceptionBreakpointsForUndefined.length, 2);
269
assert.strictEqual(exceptionBreakpointsForUndefined[0].filter, 'uncaught');
270
assert.strictEqual(exceptionBreakpointsForUndefined[1].filter, 'caught');
271
272
model.removeExceptionBreakpointsForSession("session-id-4");
273
assert.strictEqual(eventCount, 2);
274
exceptionBreakpointsForUndefined = model.getExceptionBreakpointsForSession(undefined);
275
assert.strictEqual(exceptionBreakpointsForUndefined.length, 2);
276
assert.strictEqual(exceptionBreakpointsForUndefined[0].filter, 'uncaught');
277
assert.strictEqual(exceptionBreakpointsForUndefined[1].filter, 'caught');
278
279
model.setExceptionBreakpointFallbackSession("session-id-5");
280
assert.strictEqual(eventCount, 2);
281
exceptionBreakpointsForUndefined = model.getExceptionBreakpointsForSession(undefined);
282
assert.strictEqual(exceptionBreakpointsForUndefined.length, 2);
283
assert.strictEqual(exceptionBreakpointsForUndefined[0].filter, 'caught');
284
assert.strictEqual(exceptionBreakpointsForUndefined[1].filter, 'all');
285
286
const exceptionBreakpoints = model.getExceptionBreakpoints();
287
assert.strictEqual(exceptionBreakpoints.length, 3);
288
});
289
290
test('instruction breakpoints', () => {
291
let eventCount = 0;
292
disposables.add(model.onDidChangeBreakpoints(() => eventCount++));
293
//address: string, offset: number, condition?: string, hitCondition?: string
294
model.addInstructionBreakpoint({ instructionReference: '0xCCCCFFFF', offset: 0, address: 0n, canPersist: false });
295
296
assert.strictEqual(eventCount, 1);
297
let instructionBreakpoints = model.getInstructionBreakpoints();
298
assert.strictEqual(instructionBreakpoints.length, 1);
299
assert.strictEqual(instructionBreakpoints[0].instructionReference, '0xCCCCFFFF');
300
assert.strictEqual(instructionBreakpoints[0].offset, 0);
301
302
model.addInstructionBreakpoint({ instructionReference: '0xCCCCEEEE', offset: 1, address: 0n, canPersist: false });
303
assert.strictEqual(eventCount, 2);
304
instructionBreakpoints = model.getInstructionBreakpoints();
305
assert.strictEqual(instructionBreakpoints.length, 2);
306
assert.strictEqual(instructionBreakpoints[0].instructionReference, '0xCCCCFFFF');
307
assert.strictEqual(instructionBreakpoints[0].offset, 0);
308
assert.strictEqual(instructionBreakpoints[1].instructionReference, '0xCCCCEEEE');
309
assert.strictEqual(instructionBreakpoints[1].offset, 1);
310
});
311
312
test('data breakpoints', () => {
313
let eventCount = 0;
314
disposables.add(model.onDidChangeBreakpoints(() => eventCount++));
315
316
model.addDataBreakpoint({ description: 'label', src: { type: DataBreakpointSetType.Variable, dataId: 'id' }, canPersist: true, accessTypes: ['read'], accessType: 'read' }, '1');
317
model.addDataBreakpoint({ description: 'second', src: { type: DataBreakpointSetType.Variable, dataId: 'secondId' }, canPersist: false, accessTypes: ['readWrite'], accessType: 'readWrite' }, '2');
318
model.updateDataBreakpoint('1', { condition: 'aCondition' });
319
model.updateDataBreakpoint('2', { hitCondition: '10' });
320
const dataBreakpoints = model.getDataBreakpoints();
321
assert.strictEqual(dataBreakpoints[0].canPersist, true);
322
assert.deepStrictEqual(dataBreakpoints[0].src, { type: DataBreakpointSetType.Variable, dataId: 'id' });
323
assert.strictEqual(dataBreakpoints[0].accessType, 'read');
324
assert.strictEqual(dataBreakpoints[0].condition, 'aCondition');
325
assert.strictEqual(dataBreakpoints[1].canPersist, false);
326
assert.strictEqual(dataBreakpoints[1].description, 'second');
327
assert.strictEqual(dataBreakpoints[1].accessType, 'readWrite');
328
assert.strictEqual(dataBreakpoints[1].hitCondition, '10');
329
330
assert.strictEqual(eventCount, 4);
331
332
model.removeDataBreakpoints(dataBreakpoints[0].getId());
333
assert.strictEqual(eventCount, 5);
334
assert.strictEqual(model.getDataBreakpoints().length, 1);
335
336
model.removeDataBreakpoints();
337
assert.strictEqual(model.getDataBreakpoints().length, 0);
338
assert.strictEqual(eventCount, 6);
339
});
340
341
test('message and class name', () => {
342
const modelUri = uri.file('/myfolder/my file first.js');
343
addBreakpointsAndCheckEvents(model, modelUri, [
344
{ lineNumber: 5, enabled: true, condition: 'x > 5' },
345
{ lineNumber: 10, enabled: false },
346
{ lineNumber: 12, enabled: true, logMessage: 'hello' },
347
{ lineNumber: 15, enabled: true, hitCondition: '12' },
348
{ lineNumber: 500, enabled: true },
349
]);
350
const breakpoints = model.getBreakpoints();
351
const ls = new MockLabelService();
352
353
let result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0], ls, model);
354
assert.strictEqual(result.message, 'Condition: x > 5');
355
assert.strictEqual(result.icon.id, 'debug-breakpoint-conditional');
356
357
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[1], ls, model);
358
assert.strictEqual(result.message, 'Disabled Breakpoint');
359
assert.strictEqual(result.icon.id, 'debug-breakpoint-disabled');
360
361
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2], ls, model);
362
assert.strictEqual(result.message, 'Log Message: hello');
363
assert.strictEqual(result.icon.id, 'debug-breakpoint-log');
364
365
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[3], ls, model);
366
assert.strictEqual(result.message, 'Hit Count: 12');
367
assert.strictEqual(result.icon.id, 'debug-breakpoint-conditional');
368
369
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[4], ls, model);
370
assert.strictEqual(result.message, ls.getUriLabel(breakpoints[4].uri));
371
assert.strictEqual(result.icon.id, 'debug-breakpoint');
372
373
result = getBreakpointMessageAndIcon(State.Stopped, false, breakpoints[2], ls, model);
374
assert.strictEqual(result.message, 'Disabled Logpoint');
375
assert.strictEqual(result.icon.id, 'debug-breakpoint-log-disabled');
376
377
model.addDataBreakpoint({ description: 'label', canPersist: true, accessTypes: ['read'], accessType: 'read', src: { type: DataBreakpointSetType.Variable, dataId: 'id' } });
378
const dataBreakpoints = model.getDataBreakpoints();
379
result = getBreakpointMessageAndIcon(State.Stopped, true, dataBreakpoints[0], ls, model);
380
assert.strictEqual(result.message, 'Data Breakpoint');
381
assert.strictEqual(result.icon.id, 'debug-breakpoint-data');
382
383
const functionBreakpoint = model.addFunctionBreakpoint({ name: 'foo' }, '1');
384
result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint, ls, model);
385
assert.strictEqual(result.message, 'Function Breakpoint');
386
assert.strictEqual(result.icon.id, 'debug-breakpoint-function');
387
388
const data = new Map<string, DebugProtocol.Breakpoint>();
389
data.set(breakpoints[0].getId(), { verified: false, line: 10 });
390
data.set(breakpoints[1].getId(), { verified: true, line: 50 });
391
data.set(breakpoints[2].getId(), { verified: true, line: 50, message: 'world' });
392
data.set(functionBreakpoint.getId(), { verified: true });
393
model.setBreakpointSessionData('mocksessionid', { supportsFunctionBreakpoints: false, supportsDataBreakpoints: true, supportsLogPoints: true }, data);
394
395
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0], ls, model);
396
assert.strictEqual(result.message, 'Unverified Breakpoint');
397
assert.strictEqual(result.icon.id, 'debug-breakpoint-unverified');
398
399
result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint, ls, model);
400
assert.strictEqual(result.message, 'Function breakpoints not supported by this debug type');
401
assert.strictEqual(result.icon.id, 'debug-breakpoint-function-unverified');
402
403
result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2], ls, model);
404
assert.strictEqual(result.message, 'Log Message: hello, world');
405
assert.strictEqual(result.icon.id, 'debug-breakpoint-log');
406
});
407
408
test('decorations', () => {
409
const modelUri = uri.file('/myfolder/my file first.js');
410
const languageId = 'testMode';
411
const textModel = createTextModel(
412
['this is line one', 'this is line two', ' this is line three it has whitespace at start', 'this is line four', 'this is line five'].join('\n'),
413
languageId
414
);
415
addBreakpointsAndCheckEvents(model, modelUri, [
416
{ lineNumber: 1, enabled: true, condition: 'x > 5' },
417
{ lineNumber: 2, column: 4, enabled: false },
418
{ lineNumber: 3, enabled: true, logMessage: 'hello' },
419
{ lineNumber: 500, enabled: true },
420
]);
421
const breakpoints = model.getBreakpoints();
422
423
const instantiationService = new TestInstantiationService();
424
const debugService = new MockDebugService();
425
debugService.getModel = () => model;
426
instantiationService.stub(IDebugService, debugService);
427
instantiationService.stub(ILabelService, new MockLabelService());
428
instantiationService.stub(ILanguageService, disposables.add(new LanguageService()));
429
let decorations = instantiationService.invokeFunction(accessor => createBreakpointDecorations(accessor, textModel, breakpoints, State.Running, true, true));
430
assert.strictEqual(decorations.length, 3); // last breakpoint filtered out since it has a large line number
431
assert.deepStrictEqual(decorations[0].range, new Range(1, 1, 1, 2));
432
assert.deepStrictEqual(decorations[1].range, new Range(2, 4, 2, 5));
433
assert.deepStrictEqual(decorations[2].range, new Range(3, 5, 3, 6));
434
assert.strictEqual(decorations[0].options.beforeContentClassName, undefined);
435
assert.strictEqual(decorations[1].options.before?.inlineClassName, `debug-breakpoint-placeholder`);
436
assert.strictEqual(decorations[0].options.overviewRuler?.position, OverviewRulerLane.Left);
437
const expected = new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }).appendCodeblock(languageId, 'Condition: x > 5');
438
assert.deepStrictEqual(decorations[0].options.glyphMarginHoverMessage, expected);
439
440
decorations = instantiationService.invokeFunction(accessor => createBreakpointDecorations(accessor, textModel, breakpoints, State.Running, true, false));
441
assert.strictEqual(decorations[0].options.overviewRuler, null);
442
443
textModel.dispose();
444
instantiationService.dispose();
445
});
446
447
test('updates when storage changes', () => {
448
const storage1 = disposables.add(new TestStorageService());
449
const debugStorage1 = disposables.add(new MockDebugStorage(storage1));
450
const model1 = disposables.add(new DebugModel(debugStorage1, <any>{ isDirty: (e: any) => false }, mockUriIdentityService, new NullLogService()));
451
452
// 1. create breakpoints in the first model
453
const modelUri = uri.file('/myfolder/my file first.js');
454
const first = [
455
{ lineNumber: 1, enabled: true, condition: 'x > 5' },
456
{ lineNumber: 2, column: 4, enabled: false },
457
];
458
459
addBreakpointsAndCheckEvents(model1, modelUri, first);
460
debugStorage1.storeBreakpoints(model1);
461
const stored = storage1.get('debug.breakpoint', StorageScope.WORKSPACE);
462
463
// 2. hydrate a new model and ensure external breakpoints get applied
464
const storage2 = disposables.add(new TestStorageService());
465
const model2 = disposables.add(new DebugModel(disposables.add(new MockDebugStorage(storage2)), <any>{ isDirty: (e: any) => false }, mockUriIdentityService, new NullLogService()));
466
storage2.store('debug.breakpoint', stored, StorageScope.WORKSPACE, StorageTarget.USER, /* external= */ true);
467
assert.deepStrictEqual(model2.getBreakpoints().map(b => b.getId()), model1.getBreakpoints().map(b => b.getId()));
468
469
// 3. ensure non-external changes are ignored
470
storage2.store('debug.breakpoint', '[]', StorageScope.WORKSPACE, StorageTarget.USER, /* external= */ false);
471
assert.deepStrictEqual(model2.getBreakpoints().map(b => b.getId()), model1.getBreakpoints().map(b => b.getId()));
472
});
473
});
474
475