Path: blob/main/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { MarkdownString } from '../../../../../base/common/htmlContent.js';7import { dispose } from '../../../../../base/common/lifecycle.js';8import { URI as uri } from '../../../../../base/common/uri.js';9import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';10import { Range } from '../../../../../editor/common/core/range.js';11import { ILanguageService } from '../../../../../editor/common/languages/language.js';12import { OverviewRulerLane } from '../../../../../editor/common/model.js';13import { LanguageService } from '../../../../../editor/common/services/languageService.js';14import { createTextModel } from '../../../../../editor/test/common/testTextModel.js';15import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';16import { ILabelService } from '../../../../../platform/label/common/label.js';17import { NullLogService } from '../../../../../platform/log/common/log.js';18import { StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';19import { createBreakpointDecorations } from '../../browser/breakpointEditorContribution.js';20import { getBreakpointMessageAndIcon, getExpandedBodySize } from '../../browser/breakpointsView.js';21import { DataBreakpointSetType, IBreakpointData, IBreakpointUpdateData, IDebugService, State } from '../../common/debug.js';22import { Breakpoint, DebugModel } from '../../common/debugModel.js';23import { createTestSession } from './callStack.test.js';24import { createMockDebugModel, mockUriIdentityService } from './mockDebugModel.js';25import { MockDebugService, MockDebugStorage } from '../common/mockDebug.js';26import { MockLabelService } from '../../../../services/label/test/common/mockLabelService.js';27import { TestStorageService } from '../../../../test/common/workbenchTestServices.js';2829function addBreakpointsAndCheckEvents(model: DebugModel, uri: uri, data: IBreakpointData[]) {30let eventCount = 0;31const toDispose = model.onDidChangeBreakpoints(e => {32assert.strictEqual(e?.sessionOnly, false);33assert.strictEqual(e?.changed, undefined);34assert.strictEqual(e?.removed, undefined);35const added = e?.added;36assert.notStrictEqual(added, undefined);37assert.strictEqual(added!.length, data.length);38eventCount++;39dispose(toDispose);40for (let i = 0; i < data.length; i++) {41assert.strictEqual(e.added![i] instanceof Breakpoint, true);42assert.strictEqual((e.added![i] as Breakpoint).lineNumber, data[i].lineNumber);43}44});45const bps = model.addBreakpoints(uri, data);46assert.strictEqual(eventCount, 1);47return bps;48}4950suite('Debug - Breakpoints', () => {51let model: DebugModel;52const disposables = ensureNoDisposablesAreLeakedInTestSuite();5354setup(() => {55model = createMockDebugModel(disposables);56});5758// Breakpoints5960test('simple', () => {61const modelUri = uri.file('/myfolder/myfile.js');6263addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);64assert.strictEqual(model.areBreakpointsActivated(), true);65assert.strictEqual(model.getBreakpoints().length, 2);6667let eventCount = 0;68const toDispose = model.onDidChangeBreakpoints(e => {69eventCount++;70assert.strictEqual(e?.added, undefined);71assert.strictEqual(e?.sessionOnly, false);72assert.strictEqual(e?.removed?.length, 2);73assert.strictEqual(e?.changed, undefined);7475dispose(toDispose);76});7778model.removeBreakpoints(model.getBreakpoints());79assert.strictEqual(eventCount, 1);80assert.strictEqual(model.getBreakpoints().length, 0);81});8283test('toggling', () => {84const modelUri = uri.file('/myfolder/myfile.js');8586addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);87addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 12, enabled: true, condition: 'fake condition' }]);88assert.strictEqual(model.getBreakpoints().length, 3);89const bp = model.getBreakpoints().pop();90if (bp) {91model.removeBreakpoints([bp]);92}93assert.strictEqual(model.getBreakpoints().length, 2);9495model.setBreakpointsActivated(false);96assert.strictEqual(model.areBreakpointsActivated(), false);97model.setBreakpointsActivated(true);98assert.strictEqual(model.areBreakpointsActivated(), true);99});100101test('two files', () => {102const modelUri1 = uri.file('/myfolder/my file first.js');103const modelUri2 = uri.file('/secondfolder/second/second file.js');104addBreakpointsAndCheckEvents(model, modelUri1, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]);105assert.strictEqual(getExpandedBodySize(model, undefined, 9), 44);106107addBreakpointsAndCheckEvents(model, modelUri2, [{ lineNumber: 1, enabled: true }, { lineNumber: 2, enabled: true }, { lineNumber: 3, enabled: false }]);108assert.strictEqual(getExpandedBodySize(model, undefined, 9), 110);109110assert.strictEqual(model.getBreakpoints().length, 5);111assert.strictEqual(model.getBreakpoints({ uri: modelUri1 }).length, 2);112assert.strictEqual(model.getBreakpoints({ uri: modelUri2 }).length, 3);113assert.strictEqual(model.getBreakpoints({ lineNumber: 5 }).length, 1);114assert.strictEqual(model.getBreakpoints({ column: 5 }).length, 0);115116const bp = model.getBreakpoints()[0];117const update = new Map<string, IBreakpointUpdateData>();118update.set(bp.getId(), { lineNumber: 100 });119let eventFired = false;120const toDispose = model.onDidChangeBreakpoints(e => {121eventFired = true;122assert.strictEqual(e?.added, undefined);123assert.strictEqual(e?.removed, undefined);124assert.strictEqual(e?.changed?.length, 1);125dispose(toDispose);126});127model.updateBreakpoints(update);128assert.strictEqual(eventFired, true);129assert.strictEqual(bp.lineNumber, 100);130131assert.strictEqual(model.getBreakpoints({ enabledOnly: true }).length, 3);132model.enableOrDisableAllBreakpoints(false);133model.getBreakpoints().forEach(bp => {134assert.strictEqual(bp.enabled, false);135});136assert.strictEqual(model.getBreakpoints({ enabledOnly: true }).length, 0);137138model.setEnablement(bp, true);139assert.strictEqual(bp.enabled, true);140141model.removeBreakpoints(model.getBreakpoints({ uri: modelUri1 }));142assert.strictEqual(getExpandedBodySize(model, undefined, 9), 66);143144assert.strictEqual(model.getBreakpoints().length, 3);145});146147test('conditions', () => {148const modelUri1 = uri.file('/myfolder/my file first.js');149addBreakpointsAndCheckEvents(model, modelUri1, [{ lineNumber: 5, condition: 'i < 5', hitCondition: '17' }, { lineNumber: 10, condition: 'j < 3' }]);150const breakpoints = model.getBreakpoints();151152assert.strictEqual(breakpoints[0].condition, 'i < 5');153assert.strictEqual(breakpoints[0].hitCondition, '17');154assert.strictEqual(breakpoints[1].condition, 'j < 3');155assert.strictEqual(!!breakpoints[1].hitCondition, false);156157assert.strictEqual(model.getBreakpoints().length, 2);158model.removeBreakpoints(model.getBreakpoints());159assert.strictEqual(model.getBreakpoints().length, 0);160});161162test('function breakpoints', () => {163model.addFunctionBreakpoint({ name: 'foo' }, '1');164model.addFunctionBreakpoint({ name: 'bar' }, '2');165model.updateFunctionBreakpoint('1', { name: 'fooUpdated' });166model.updateFunctionBreakpoint('2', { name: 'barUpdated' });167168const functionBps = model.getFunctionBreakpoints();169assert.strictEqual(functionBps[0].name, 'fooUpdated');170assert.strictEqual(functionBps[1].name, 'barUpdated');171172model.removeFunctionBreakpoints();173assert.strictEqual(model.getFunctionBreakpoints().length, 0);174});175176test('multiple sessions', () => {177const modelUri = uri.file('/myfolder/myfile.js');178addBreakpointsAndCheckEvents(model, modelUri, [{ lineNumber: 5, enabled: true, condition: 'x > 5' }, { lineNumber: 10, enabled: false }]);179const breakpoints = model.getBreakpoints();180const session = disposables.add(createTestSession(model));181const data = new Map<string, DebugProtocol.Breakpoint>();182183assert.strictEqual(breakpoints[0].lineNumber, 5);184assert.strictEqual(breakpoints[1].lineNumber, 10);185186data.set(breakpoints[0].getId(), { verified: false, line: 10 });187data.set(breakpoints[1].getId(), { verified: true, line: 50 });188model.setBreakpointSessionData(session.getId(), {}, data);189assert.strictEqual(breakpoints[0].lineNumber, 5);190assert.strictEqual(breakpoints[1].lineNumber, 50);191192const session2 = disposables.add(createTestSession(model));193const data2 = new Map<string, DebugProtocol.Breakpoint>();194data2.set(breakpoints[0].getId(), { verified: true, line: 100 });195data2.set(breakpoints[1].getId(), { verified: true, line: 500 });196model.setBreakpointSessionData(session2.getId(), {}, data2);197198// Breakpoint is verified only once, show that line199assert.strictEqual(breakpoints[0].lineNumber, 100);200// Breakpoint is verified two times, show the original line201assert.strictEqual(breakpoints[1].lineNumber, 10);202203model.setBreakpointSessionData(session.getId(), {}, undefined);204// No more double session verification205assert.strictEqual(breakpoints[0].lineNumber, 100);206assert.strictEqual(breakpoints[1].lineNumber, 500);207208assert.strictEqual(breakpoints[0].supported, false);209const data3 = new Map<string, DebugProtocol.Breakpoint>();210data3.set(breakpoints[0].getId(), { verified: true, line: 500 });211model.setBreakpointSessionData(session2.getId(), { supportsConditionalBreakpoints: true }, data2);212assert.strictEqual(breakpoints[0].supported, true);213});214215test('exception breakpoints', () => {216let eventCount = 0;217disposables.add(model.onDidChangeBreakpoints(() => eventCount++));218model.setExceptionBreakpointsForSession("session-id-1", [{ filter: 'uncaught', label: 'UNCAUGHT', default: true }]);219assert.strictEqual(eventCount, 1);220let exceptionBreakpoints = model.getExceptionBreakpointsForSession("session-id-1");221assert.strictEqual(exceptionBreakpoints.length, 1);222assert.strictEqual(exceptionBreakpoints[0].filter, 'uncaught');223assert.strictEqual(exceptionBreakpoints[0].enabled, true);224225model.setExceptionBreakpointsForSession("session-id-2", [{ filter: 'uncaught', label: 'UNCAUGHT' }, { filter: 'caught', label: 'CAUGHT' }]);226assert.strictEqual(eventCount, 2);227exceptionBreakpoints = model.getExceptionBreakpointsForSession("session-id-2");228assert.strictEqual(exceptionBreakpoints.length, 2);229assert.strictEqual(exceptionBreakpoints[0].filter, 'uncaught');230assert.strictEqual(exceptionBreakpoints[0].enabled, true);231assert.strictEqual(exceptionBreakpoints[1].filter, 'caught');232assert.strictEqual(exceptionBreakpoints[1].label, 'CAUGHT');233assert.strictEqual(exceptionBreakpoints[1].enabled, false);234235model.setExceptionBreakpointsForSession("session-id-3", [{ filter: 'all', label: 'ALL' }]);236assert.strictEqual(eventCount, 3);237assert.strictEqual(model.getExceptionBreakpointsForSession("session-id-3").length, 1);238exceptionBreakpoints = model.getExceptionBreakpoints();239assert.strictEqual(exceptionBreakpoints[0].filter, 'uncaught');240assert.strictEqual(exceptionBreakpoints[0].enabled, true);241assert.strictEqual(exceptionBreakpoints[1].filter, 'caught');242assert.strictEqual(exceptionBreakpoints[1].label, 'CAUGHT');243assert.strictEqual(exceptionBreakpoints[1].enabled, false);244assert.strictEqual(exceptionBreakpoints[2].filter, 'all');245assert.strictEqual(exceptionBreakpoints[2].label, 'ALL');246});247248test('exception breakpoints multiple sessions', () => {249let eventCount = 0;250disposables.add(model.onDidChangeBreakpoints(() => eventCount++));251252model.setExceptionBreakpointsForSession("session-id-4", [{ filter: 'uncaught', label: 'UNCAUGHT', default: true }, { filter: 'caught', label: 'CAUGHT' }]);253model.setExceptionBreakpointFallbackSession("session-id-4");254assert.strictEqual(eventCount, 1);255let exceptionBreakpointsForSession = model.getExceptionBreakpointsForSession("session-id-4");256assert.strictEqual(exceptionBreakpointsForSession.length, 2);257assert.strictEqual(exceptionBreakpointsForSession[0].filter, 'uncaught');258assert.strictEqual(exceptionBreakpointsForSession[1].filter, 'caught');259260model.setExceptionBreakpointsForSession("session-id-5", [{ filter: 'all', label: 'ALL' }, { filter: 'caught', label: 'CAUGHT' }]);261assert.strictEqual(eventCount, 2);262exceptionBreakpointsForSession = model.getExceptionBreakpointsForSession("session-id-5");263let exceptionBreakpointsForUndefined = model.getExceptionBreakpointsForSession(undefined);264assert.strictEqual(exceptionBreakpointsForSession.length, 2);265assert.strictEqual(exceptionBreakpointsForSession[0].filter, 'caught');266assert.strictEqual(exceptionBreakpointsForSession[1].filter, 'all');267assert.strictEqual(exceptionBreakpointsForUndefined.length, 2);268assert.strictEqual(exceptionBreakpointsForUndefined[0].filter, 'uncaught');269assert.strictEqual(exceptionBreakpointsForUndefined[1].filter, 'caught');270271model.removeExceptionBreakpointsForSession("session-id-4");272assert.strictEqual(eventCount, 2);273exceptionBreakpointsForUndefined = model.getExceptionBreakpointsForSession(undefined);274assert.strictEqual(exceptionBreakpointsForUndefined.length, 2);275assert.strictEqual(exceptionBreakpointsForUndefined[0].filter, 'uncaught');276assert.strictEqual(exceptionBreakpointsForUndefined[1].filter, 'caught');277278model.setExceptionBreakpointFallbackSession("session-id-5");279assert.strictEqual(eventCount, 2);280exceptionBreakpointsForUndefined = model.getExceptionBreakpointsForSession(undefined);281assert.strictEqual(exceptionBreakpointsForUndefined.length, 2);282assert.strictEqual(exceptionBreakpointsForUndefined[0].filter, 'caught');283assert.strictEqual(exceptionBreakpointsForUndefined[1].filter, 'all');284285const exceptionBreakpoints = model.getExceptionBreakpoints();286assert.strictEqual(exceptionBreakpoints.length, 3);287});288289test('instruction breakpoints', () => {290let eventCount = 0;291disposables.add(model.onDidChangeBreakpoints(() => eventCount++));292//address: string, offset: number, condition?: string, hitCondition?: string293model.addInstructionBreakpoint({ instructionReference: '0xCCCCFFFF', offset: 0, address: 0n, canPersist: false });294295assert.strictEqual(eventCount, 1);296let instructionBreakpoints = model.getInstructionBreakpoints();297assert.strictEqual(instructionBreakpoints.length, 1);298assert.strictEqual(instructionBreakpoints[0].instructionReference, '0xCCCCFFFF');299assert.strictEqual(instructionBreakpoints[0].offset, 0);300301model.addInstructionBreakpoint({ instructionReference: '0xCCCCEEEE', offset: 1, address: 0n, canPersist: false });302assert.strictEqual(eventCount, 2);303instructionBreakpoints = model.getInstructionBreakpoints();304assert.strictEqual(instructionBreakpoints.length, 2);305assert.strictEqual(instructionBreakpoints[0].instructionReference, '0xCCCCFFFF');306assert.strictEqual(instructionBreakpoints[0].offset, 0);307assert.strictEqual(instructionBreakpoints[1].instructionReference, '0xCCCCEEEE');308assert.strictEqual(instructionBreakpoints[1].offset, 1);309});310311test('data breakpoints', () => {312let eventCount = 0;313disposables.add(model.onDidChangeBreakpoints(() => eventCount++));314315model.addDataBreakpoint({ description: 'label', src: { type: DataBreakpointSetType.Variable, dataId: 'id' }, canPersist: true, accessTypes: ['read'], accessType: 'read' }, '1');316model.addDataBreakpoint({ description: 'second', src: { type: DataBreakpointSetType.Variable, dataId: 'secondId' }, canPersist: false, accessTypes: ['readWrite'], accessType: 'readWrite' }, '2');317model.updateDataBreakpoint('1', { condition: 'aCondition' });318model.updateDataBreakpoint('2', { hitCondition: '10' });319const dataBreakpoints = model.getDataBreakpoints();320assert.strictEqual(dataBreakpoints[0].canPersist, true);321assert.deepStrictEqual(dataBreakpoints[0].src, { type: DataBreakpointSetType.Variable, dataId: 'id' });322assert.strictEqual(dataBreakpoints[0].accessType, 'read');323assert.strictEqual(dataBreakpoints[0].condition, 'aCondition');324assert.strictEqual(dataBreakpoints[1].canPersist, false);325assert.strictEqual(dataBreakpoints[1].description, 'second');326assert.strictEqual(dataBreakpoints[1].accessType, 'readWrite');327assert.strictEqual(dataBreakpoints[1].hitCondition, '10');328329assert.strictEqual(eventCount, 4);330331model.removeDataBreakpoints(dataBreakpoints[0].getId());332assert.strictEqual(eventCount, 5);333assert.strictEqual(model.getDataBreakpoints().length, 1);334335model.removeDataBreakpoints();336assert.strictEqual(model.getDataBreakpoints().length, 0);337assert.strictEqual(eventCount, 6);338});339340test('message and class name', () => {341const modelUri = uri.file('/myfolder/my file first.js');342addBreakpointsAndCheckEvents(model, modelUri, [343{ lineNumber: 5, enabled: true, condition: 'x > 5' },344{ lineNumber: 10, enabled: false },345{ lineNumber: 12, enabled: true, logMessage: 'hello' },346{ lineNumber: 15, enabled: true, hitCondition: '12' },347{ lineNumber: 500, enabled: true },348]);349const breakpoints = model.getBreakpoints();350const ls = new MockLabelService();351352let result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0], ls, model);353assert.strictEqual(result.message, 'Condition: x > 5');354assert.strictEqual(result.icon.id, 'debug-breakpoint-conditional');355356result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[1], ls, model);357assert.strictEqual(result.message, 'Disabled Breakpoint');358assert.strictEqual(result.icon.id, 'debug-breakpoint-disabled');359360result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2], ls, model);361assert.strictEqual(result.message, 'Log Message: hello');362assert.strictEqual(result.icon.id, 'debug-breakpoint-log');363364result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[3], ls, model);365assert.strictEqual(result.message, 'Hit Count: 12');366assert.strictEqual(result.icon.id, 'debug-breakpoint-conditional');367368result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[4], ls, model);369assert.strictEqual(result.message, ls.getUriLabel(breakpoints[4].uri));370assert.strictEqual(result.icon.id, 'debug-breakpoint');371372result = getBreakpointMessageAndIcon(State.Stopped, false, breakpoints[2], ls, model);373assert.strictEqual(result.message, 'Disabled Logpoint');374assert.strictEqual(result.icon.id, 'debug-breakpoint-log-disabled');375376model.addDataBreakpoint({ description: 'label', canPersist: true, accessTypes: ['read'], accessType: 'read', src: { type: DataBreakpointSetType.Variable, dataId: 'id' } });377const dataBreakpoints = model.getDataBreakpoints();378result = getBreakpointMessageAndIcon(State.Stopped, true, dataBreakpoints[0], ls, model);379assert.strictEqual(result.message, 'Data Breakpoint');380assert.strictEqual(result.icon.id, 'debug-breakpoint-data');381382const functionBreakpoint = model.addFunctionBreakpoint({ name: 'foo' }, '1');383result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint, ls, model);384assert.strictEqual(result.message, 'Function Breakpoint');385assert.strictEqual(result.icon.id, 'debug-breakpoint-function');386387const data = new Map<string, DebugProtocol.Breakpoint>();388data.set(breakpoints[0].getId(), { verified: false, line: 10 });389data.set(breakpoints[1].getId(), { verified: true, line: 50 });390data.set(breakpoints[2].getId(), { verified: true, line: 50, message: 'world' });391data.set(functionBreakpoint.getId(), { verified: true });392model.setBreakpointSessionData('mocksessionid', { supportsFunctionBreakpoints: false, supportsDataBreakpoints: true, supportsLogPoints: true }, data);393394result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[0], ls, model);395assert.strictEqual(result.message, 'Unverified Breakpoint');396assert.strictEqual(result.icon.id, 'debug-breakpoint-unverified');397398result = getBreakpointMessageAndIcon(State.Stopped, true, functionBreakpoint, ls, model);399assert.strictEqual(result.message, 'Function breakpoints not supported by this debug type');400assert.strictEqual(result.icon.id, 'debug-breakpoint-function-unverified');401402result = getBreakpointMessageAndIcon(State.Stopped, true, breakpoints[2], ls, model);403assert.strictEqual(result.message, 'Log Message: hello, world');404assert.strictEqual(result.icon.id, 'debug-breakpoint-log');405});406407test('decorations', () => {408const modelUri = uri.file('/myfolder/my file first.js');409const languageId = 'testMode';410const textModel = createTextModel(411['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'),412languageId413);414addBreakpointsAndCheckEvents(model, modelUri, [415{ lineNumber: 1, enabled: true, condition: 'x > 5' },416{ lineNumber: 2, column: 4, enabled: false },417{ lineNumber: 3, enabled: true, logMessage: 'hello' },418{ lineNumber: 500, enabled: true },419]);420const breakpoints = model.getBreakpoints();421422const instantiationService = new TestInstantiationService();423const debugService = new MockDebugService();424debugService.getModel = () => model;425instantiationService.stub(IDebugService, debugService);426instantiationService.stub(ILabelService, new MockLabelService());427instantiationService.stub(ILanguageService, disposables.add(new LanguageService()));428let decorations = instantiationService.invokeFunction(accessor => createBreakpointDecorations(accessor, textModel, breakpoints, State.Running, true, true));429assert.strictEqual(decorations.length, 3); // last breakpoint filtered out since it has a large line number430assert.deepStrictEqual(decorations[0].range, new Range(1, 1, 1, 2));431assert.deepStrictEqual(decorations[1].range, new Range(2, 4, 2, 5));432assert.deepStrictEqual(decorations[2].range, new Range(3, 5, 3, 6));433assert.strictEqual(decorations[0].options.beforeContentClassName, undefined);434assert.strictEqual(decorations[1].options.before?.inlineClassName, `debug-breakpoint-placeholder`);435assert.strictEqual(decorations[0].options.overviewRuler?.position, OverviewRulerLane.Left);436const expected = new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }).appendCodeblock(languageId, 'Condition: x > 5');437assert.deepStrictEqual(decorations[0].options.glyphMarginHoverMessage, expected);438439decorations = instantiationService.invokeFunction(accessor => createBreakpointDecorations(accessor, textModel, breakpoints, State.Running, true, false));440assert.strictEqual(decorations[0].options.overviewRuler, null);441442textModel.dispose();443instantiationService.dispose();444});445446test('updates when storage changes', () => {447const storage1 = disposables.add(new TestStorageService());448const debugStorage1 = disposables.add(new MockDebugStorage(storage1));449const model1 = disposables.add(new DebugModel(debugStorage1, <any>{ isDirty: (e: any) => false }, mockUriIdentityService, new NullLogService()));450451// 1. create breakpoints in the first model452const modelUri = uri.file('/myfolder/my file first.js');453const first = [454{ lineNumber: 1, enabled: true, condition: 'x > 5' },455{ lineNumber: 2, column: 4, enabled: false },456];457458addBreakpointsAndCheckEvents(model1, modelUri, first);459debugStorage1.storeBreakpoints(model1);460const stored = storage1.get('debug.breakpoint', StorageScope.WORKSPACE);461462// 2. hydrate a new model and ensure external breakpoints get applied463const storage2 = disposables.add(new TestStorageService());464const model2 = disposables.add(new DebugModel(disposables.add(new MockDebugStorage(storage2)), <any>{ isDirty: (e: any) => false }, mockUriIdentityService, new NullLogService()));465storage2.store('debug.breakpoint', stored, StorageScope.WORKSPACE, StorageTarget.USER, /* external= */ true);466assert.deepStrictEqual(model2.getBreakpoints().map(b => b.getId()), model1.getBreakpoints().map(b => b.getId()));467468// 3. ensure non-external changes are ignored469storage2.store('debug.breakpoint', '[]', StorageScope.WORKSPACE, StorageTarget.USER, /* external= */ false);470assert.deepStrictEqual(model2.getBreakpoints().map(b => b.getId()), model1.getBreakpoints().map(b => b.getId()));471});472});473474475