Skip to content

Commit 84a56f4

Browse files
committed
adding tests and adjusting lint issues
1 parent a2b4016 commit 84a56f4

4 files changed

Lines changed: 179 additions & 2 deletions

File tree

__mocks__/vscode.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ class MockTreeItem {
4747
}
4848
}
4949

50+
class MockRange {
51+
start;
52+
end;
53+
constructor(startLine, startCharacter, endLine, endCharacter) {
54+
this.start = { line: startLine, character: startCharacter };
55+
this.end = { line: endLine, character: endCharacter };
56+
}
57+
}
58+
5059
module.exports = {
5160
EventEmitter: jest.fn(() => {
5261
const callbacks = [];
@@ -60,6 +69,7 @@ module.exports = {
6069
};
6170
}),
6271
Uri: URI,
72+
Range: MockRange,
6373
TreeItem: MockTreeItem,
6474
TreeItemCollapsibleState: MockTreeItemCollapsibleState,
6575
window: {
@@ -86,6 +96,8 @@ module.exports = {
8696
reveal: jest.fn().mockResolvedValue(undefined),
8797
})),
8898
registerTreeDataProvider: jest.fn(() => ({ dispose: jest.fn() })),
99+
createTextEditorDecorationType: jest.fn(() => ({ dispose: jest.fn() })),
100+
onDidChangeActiveTextEditor: jest.fn(() => ({ dispose: jest.fn() })),
89101
showErrorMessage: jest.fn(),
90102
showInformationMessage: jest.fn(() => Promise.resolve(undefined)),
91103
showWarningMessage: jest.fn(),

src/desktop/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { GenericCommands } from '../features/generic-commands';
2727
import { ComponentViewer } from '../views/component-viewer/component-viewer';
2828
import { ComponentViewerTreeDataProvider } from '../views/component-viewer/component-viewer-tree-view';
2929
import { CorePeripherals } from '../views/core-peripherals/core-peripherals';
30-
import {SourceFileHighlighting} from '../features/source-file-highlighting/source-file-highlighting';
30+
import { SourceFileHighlighting } from '../features/source-file-highlighting/source-file-highlighting';
3131

3232
const BUILTIN_TOOLS_PATHS = [
3333
'tools/pyocd/pyocd',
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/**
2+
* Copyright 2026 Arm Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
// generated with AI
17+
18+
import * as vscode from 'vscode';
19+
import { SourceFileHighlighting } from './source-file-highlighting';
20+
import { debugSessionFactory, extensionContextFactory } from '../../__test__/vscode.factory';
21+
22+
type ActiveDebugSessionListener = (session: vscode.DebugSession | undefined) => void;
23+
type ActiveTextEditorListener = (editor: vscode.TextEditor | undefined) => void;
24+
25+
function makeDocument(options: {
26+
fileName?: string;
27+
lineCount?: number;
28+
scheme?: string;
29+
} = {}): vscode.TextDocument {
30+
const fileName = options.fileName ?? '/workspace/source/main.c';
31+
const uri = options.scheme === 'file' || options.scheme === undefined
32+
? vscode.Uri.file(fileName)
33+
: { ...vscode.Uri.file(fileName), scheme: options.scheme };
34+
return {
35+
fileName,
36+
uri,
37+
lineCount: options.lineCount ?? 12
38+
} as vscode.TextDocument;
39+
}
40+
41+
function makeEditor(document: vscode.TextDocument = makeDocument()): jest.Mocked<vscode.TextEditor> {
42+
return {
43+
document,
44+
setDecorations: jest.fn()
45+
} as unknown as jest.Mocked<vscode.TextEditor>;
46+
}
47+
48+
async function waitForAsyncCallbacks(): Promise<void> {
49+
await Promise.resolve();
50+
await Promise.resolve();
51+
}
52+
53+
describe('SourceFileHighlighting', () => {
54+
let activeDebugSessionListener: ActiveDebugSessionListener;
55+
let activeTextEditorListener: ActiveTextEditorListener;
56+
let debugSessionDisposable: vscode.Disposable;
57+
let editorDisposable: vscode.Disposable;
58+
59+
beforeEach(() => {
60+
jest.clearAllMocks();
61+
debugSessionDisposable = { dispose: jest.fn() };
62+
editorDisposable = { dispose: jest.fn() };
63+
(vscode.debug.onDidChangeActiveDebugSession as jest.Mock).mockImplementation((listener: ActiveDebugSessionListener) => {
64+
activeDebugSessionListener = listener;
65+
return debugSessionDisposable;
66+
});
67+
(vscode.window.onDidChangeActiveTextEditor as jest.Mock).mockImplementation((listener: ActiveTextEditorListener) => {
68+
activeTextEditorListener = listener;
69+
return editorDisposable;
70+
});
71+
});
72+
73+
it('registers active debug session and active editor listeners', () => {
74+
const context = extensionContextFactory();
75+
const sourceFileHighlighting = new SourceFileHighlighting(context);
76+
77+
sourceFileHighlighting.activate();
78+
79+
expect(vscode.debug.onDidChangeActiveDebugSession).toHaveBeenCalledWith(expect.any(Function));
80+
expect(vscode.window.onDidChangeActiveTextEditor).toHaveBeenCalledWith(expect.any(Function));
81+
expect(context.subscriptions).toContain(debugSessionDisposable);
82+
});
83+
84+
it('requests breakpoint locations for the active file and highlights unique executable lines', async () => {
85+
const context = extensionContextFactory();
86+
const sourceFileHighlighting = new SourceFileHighlighting(context);
87+
const debugSession = debugSessionFactory({ name: 'test-session', type: 'gdbtarget', request: 'launch' });
88+
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce({
89+
breakpoints: [
90+
{ line: 2 },
91+
{ line: 7 },
92+
{ line: 2 }
93+
]
94+
});
95+
const editor = makeEditor(makeDocument({ fileName: '/workspace/source/main.c', lineCount: 20 }));
96+
97+
sourceFileHighlighting.activate();
98+
activeDebugSessionListener(debugSession);
99+
activeTextEditorListener(editor);
100+
await waitForAsyncCallbacks();
101+
102+
expect(debugSession.customRequest).toHaveBeenCalledWith('breakpointLocations', {
103+
source: { path: '/workspace/source/main.c' },
104+
line: 1,
105+
endLine: 20
106+
});
107+
expect(editor.setDecorations).toHaveBeenCalledTimes(1);
108+
expect(editor.setDecorations).toHaveBeenCalledWith(
109+
(vscode.window.createTextEditorDecorationType as jest.Mock).mock.results[0].value,
110+
[
111+
{ range: new vscode.Range(1, 0, 1, 0) },
112+
{ range: new vscode.Range(6, 0, 6, 0) }
113+
]
114+
);
115+
});
116+
117+
it('does not request breakpoint locations before an active debug session exists', async () => {
118+
const context = extensionContextFactory();
119+
const sourceFileHighlighting = new SourceFileHighlighting(context);
120+
const debugSession = debugSessionFactory({ name: 'test-session', type: 'gdbtarget', request: 'launch' });
121+
const editor = makeEditor();
122+
123+
sourceFileHighlighting.activate();
124+
activeTextEditorListener(editor);
125+
await waitForAsyncCallbacks();
126+
127+
expect(debugSession.customRequest).not.toHaveBeenCalled();
128+
expect(editor.setDecorations).not.toHaveBeenCalled();
129+
});
130+
131+
it('does not request breakpoint locations for non-file editors', async () => {
132+
const context = extensionContextFactory();
133+
const sourceFileHighlighting = new SourceFileHighlighting(context);
134+
const debugSession = debugSessionFactory({ name: 'test-session', type: 'gdbtarget', request: 'launch' });
135+
const editor = makeEditor(makeDocument({ scheme: 'untitled' }));
136+
137+
sourceFileHighlighting.activate();
138+
activeDebugSessionListener(debugSession);
139+
activeTextEditorListener(editor);
140+
await waitForAsyncCallbacks();
141+
142+
expect(debugSession.customRequest).not.toHaveBeenCalled();
143+
expect(editor.setDecorations).not.toHaveBeenCalled();
144+
});
145+
146+
it('does not decorate when the adapter returns no breakpoint locations', async () => {
147+
const context = extensionContextFactory();
148+
const sourceFileHighlighting = new SourceFileHighlighting(context);
149+
const debugSession = debugSessionFactory({ name: 'test-session', type: 'gdbtarget', request: 'launch' });
150+
(debugSession.customRequest as jest.Mock).mockResolvedValueOnce(undefined);
151+
const editor = makeEditor();
152+
153+
sourceFileHighlighting.activate();
154+
activeDebugSessionListener(debugSession);
155+
activeTextEditorListener(editor);
156+
await waitForAsyncCallbacks();
157+
158+
expect(debugSession.customRequest).toHaveBeenCalledWith('breakpointLocations', {
159+
source: { path: '/workspace/source/main.c' },
160+
line: 1,
161+
endLine: 12
162+
});
163+
expect(editor.setDecorations).not.toHaveBeenCalled();
164+
});
165+
});

src/features/source-file-highlighting/source-file-highlighting.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export class SourceFileHighlighting {
2222
private context: vscode.ExtensionContext;
2323
private executableLineDecorator = vscode.window.createTextEditorDecorationType({
2424
// turn it red for testing
25-
backgroundColor: 'rgba(255, 0, 0, 0.3)',
25+
backgroundColor: 'rgba(222, 199, 199, 0.3)',
2626
// only highlight the margin of the line to avoid obscuring code
2727
isWholeLine: true,
2828
});

0 commit comments

Comments
 (0)