Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions __mocks__/vscode.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,16 @@ module.exports = {
})),
registerTreeDataProvider: jest.fn(() => ({ dispose: jest.fn() })),
showWarningMessage: jest.fn(),
showErrorMessage: jest.fn(),
createStatusBarItem: jest.fn(),
showQuickPick: jest.fn(),
},
env: {
clipboard: {
readText: jest.fn(),
writeText: jest.fn()
}
},
workspace: {
getConfiguration: jest.fn(() => ({
get: jest.fn(),
Expand Down
148 changes: 143 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,30 +83,81 @@
{
"command": "vscode-cmsis-debugger.liveWatch.add",
"title": "Add Expression",
"icon": "$(add)"
"icon": "$(add)",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.deleteAll",
"title": "Delete All Expressions",
"icon": "$(close-all)"
"icon": "$(close-all)",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.delete",
"title": "Delete Expression",
"icon": "$(close)"
"icon": "$(close)",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.refresh",
"title": "Refresh",
"icon": "$(refresh)"
"icon": "$(refresh)",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.modify",
"title": "Modify Expression",
"icon": "$(pencil)"
"icon": "$(pencil)",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.copy",
"title": "Copy Expression",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromTextEditor",
"title": "Add to Live Watch",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromWatchWindow",
"title": "Add to Live Watch",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromVariablesView",
"title": "Add to Live Watch",
"category": "Live Watch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.showInMemoryInspector",
"title": "Show in Memory Inspector",
"category": "Live Watch"
}
],
"menus": {
"editor/context": [
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromTextEditor",
"when": "editorTextFocus && resourceScheme == file",
"group": "z_commands"
}
],
"debug/variables/context": [
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromVariablesView",
"when": "inDebugMode",
"group": "z_commands"
}
],
"debug/watch/context": [
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromWatchWindow",
"when": "inDebugMode",
"group": "z_commands"
}
],
"commandPalette": [
{
"command": "vscode-cmsis-debugger.showCpuTimeHistory",
Expand All @@ -115,9 +166,61 @@
{
"command": "vscode-cmsis-debugger.resetCpuTimeHistory",
"when": "inDebugMode"
},
{
"command": "vscode-cmsis-debugger.liveWatch.add",
"when": "true"
},
{
"command": "vscode-cmsis-debugger.liveWatch.deleteAll",
"when": "true"
},
{
"command": "vscode-cmsis-debugger.liveWatch.refresh",
"when": "inDebugMode"
},
{
"command": "vscode-cmsis-debugger.liveWatch.modify",
"when": "false"
},
{
"command": "vscode-cmsis-debugger.liveWatch.delete",
"when": "false"
},
{
"command": "vscode-cmsis-debugger.liveWatch.copy",
"when": "false"
},
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromTextEditor",
"when": "false"
},
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromWatchWindow",
"when": "false"
},
{
"command": "vscode-cmsis-debugger.liveWatch.addToLiveWatchFromVariablesView",
"when": "false"
},
{
"command": "vscode-cmsis-debugger.liveWatch.showInMemoryInspector",
"when": "false"
}
],
"view/title": [
{
"command": "vscode-cmsis-debugger.liveWatch.add",
"when": "view == cmsis-debugger.liveWatch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.deleteAll",
"when": "view == cmsis-debugger.liveWatch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.refresh",
"when": "view == cmsis-debugger.liveWatch"
},
{
"command": "vscode-cmsis-debugger.liveWatch.add",
"when": "view == cmsis-debugger.liveWatch",
Expand All @@ -144,6 +247,41 @@
"command": "vscode-cmsis-debugger.liveWatch.delete",
"when": "view == cmsis-debugger.liveWatch && viewItem == expression",
"group": "inline@2"
},
{
"command": "vscode-cmsis-debugger.liveWatch.add",
"when": "view == cmsis-debugger.liveWatch",
"group": "contextMenuG1@1"
},
{
"command": "vscode-cmsis-debugger.liveWatch.modify",
"when": "view == cmsis-debugger.liveWatch && viewItem == expression",
"group": "contextMenuG1@2"
},
{
"command": "vscode-cmsis-debugger.liveWatch.copy",
"when": "view == cmsis-debugger.liveWatch && viewItem == expression",
"group": "contextMenuG1@3"
},
{
"command": "vscode-cmsis-debugger.liveWatch.delete",
"when": "view == cmsis-debugger.liveWatch && viewItem == expression",
"group": "contextMenuG1@4"
},
{
"command": "vscode-cmsis-debugger.liveWatch.deleteAll",
"when": "view == cmsis-debugger.liveWatch",
"group": "contextMenuG2@1"
},
{
"command": "vscode-cmsis-debugger.liveWatch.refresh",
"when": "view == cmsis-debugger.liveWatch",
"group": "contextMenuG2@2"
},
{
"command": "vscode-cmsis-debugger.liveWatch.showInMemoryInspector",
"when": "view == cmsis-debugger.liveWatch && viewItem == expression",
"group": "contextMenuG3@1"
}
]
},
Expand Down
108 changes: 107 additions & 1 deletion src/views/live-watch/live-watch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,34 @@ describe('LiveWatchTreeDataProvider', () => {
await (liveWatchTreeDataProvider as any).rename(node, 'node-1-renamed');
expect(node.expression).toBe('node-1-renamed');
});

it('copy copies node expression to clipboard', async () => {
const node = makeNode('node-to-copy', { result: '1', variablesReference: 0 }, 1);
(liveWatchTreeDataProvider as any).roots = [node];
await (liveWatchTreeDataProvider as any).handleCopyCommand(node);
expect(vscode.env.clipboard.writeText).toHaveBeenCalledWith('node-to-copy');
});

it('AddFromSelection adds selected text as new live watch expression to roots', async () => {
jest.spyOn(liveWatchTreeDataProvider as any, 'evaluate').mockResolvedValue({ result: '5678', variablesReference: 0 });
// Mock the active text editor with a selection whose active position returns a word range
const fakeRange = { start: { line: 0, character: 0 }, end: { line: 0, character: 10 } };
const mockEditor: any = {
document: {
getWordRangeAtPosition: jest.fn().mockReturnValue(fakeRange),
getText: jest.fn().mockReturnValue('selected-expression')
},
selection: { active: { line: 0, character: 5 } }
};
(vscode.window as any).activeTextEditor = mockEditor;
await (liveWatchTreeDataProvider as any).handleAddFromSelectionCommand();
const roots = (liveWatchTreeDataProvider as any).roots;
expect(mockEditor.document.getWordRangeAtPosition).toHaveBeenCalledWith(mockEditor.selection.active);
expect(mockEditor.document.getText).toHaveBeenCalledWith(fakeRange);
expect(roots.length).toBe(1);
expect(roots[0].expression).toBe('selected-expression');
expect(roots[0].value.result).toBe('5678');
});
});

describe('refresh', () => {
Expand Down Expand Up @@ -247,7 +275,12 @@ describe('LiveWatchTreeDataProvider', () => {
'vscode-cmsis-debugger.liveWatch.deleteAll',
'vscode-cmsis-debugger.liveWatch.delete',
'vscode-cmsis-debugger.liveWatch.refresh',
'vscode-cmsis-debugger.liveWatch.modify'
'vscode-cmsis-debugger.liveWatch.modify',
'vscode-cmsis-debugger.liveWatch.copy',
'vscode-cmsis-debugger.liveWatch.addToLiveWatchFromTextEditor',
'vscode-cmsis-debugger.liveWatch.addToLiveWatchFromWatchWindow',
'vscode-cmsis-debugger.liveWatch.addToLiveWatchFromVariablesView',
'vscode-cmsis-debugger.liveWatch.showInMemoryInspector'
]));
});

Expand Down Expand Up @@ -324,6 +357,79 @@ describe('LiveWatchTreeDataProvider', () => {
await handler();
expect(refreshSpy).toHaveBeenCalled();
});

it('watch window command adds variable name root', async () => {
jest.spyOn(liveWatchTreeDataProvider as any, 'evaluate').mockResolvedValue({ result: 'value', variablesReference: 0 });
liveWatchTreeDataProvider.activate(tracker);
const handler = getRegisteredHandler('vscode-cmsis-debugger.liveWatch.addToLiveWatchFromWatchWindow');
expect(handler).toBeDefined();
await handler({ variable: { name: 'myWatchVariable' } });
const roots = (liveWatchTreeDataProvider as any).roots;
expect(roots.length).toBe(1);
expect(roots[0].expression).toBe('myWatchVariable');
});

it('watch window command does nothing with falsy payload', async () => {
liveWatchTreeDataProvider.activate(tracker);
const handler = getRegisteredHandler('vscode-cmsis-debugger.liveWatch.addToLiveWatchFromWatchWindow');
await handler(undefined);
expect((liveWatchTreeDataProvider as any).roots.length).toBe(0);
});

it('variables view command adds variable name root', async () => {
jest.spyOn(liveWatchTreeDataProvider as any, 'evaluate').mockResolvedValue({ result: '12345', variablesReference: 0 });
liveWatchTreeDataProvider.activate(tracker);
const handler = getRegisteredHandler('vscode-cmsis-debugger.liveWatch.addToLiveWatchFromVariablesView');
expect(handler).toBeDefined();
const payload = { container: { name: 'local' }, variable: { name: 'localVariable' } };
await handler(payload);
const roots = (liveWatchTreeDataProvider as any).roots;
expect(roots.length).toBe(1);
expect(roots[0].expression).toBe('localVariable');
});

it('variables view command does nothing when variable missing', async () => {
liveWatchTreeDataProvider.activate(tracker);
const handler = getRegisteredHandler('vscode-cmsis-debugger.liveWatch.addToLiveWatchFromVariablesView');
await handler({ container: { name: 'local' } });
expect((liveWatchTreeDataProvider as any).roots.length).toBe(0);
});

it('showInMemoryInspector command does nothing when node is undefined', async () => {
liveWatchTreeDataProvider.activate(tracker);
const handler = getRegisteredHandler('vscode-cmsis-debugger.liveWatch.showInMemoryInspector');
expect(handler).toBeDefined();
await handler(undefined);
expect(vscode.commands.executeCommand).not.toHaveBeenCalledWith('memory-inspector.show-variable', expect.anything());
});

it('showInMemoryInspector shows error if extension is missing', async () => {
(vscode.extensions.getExtension as jest.Mock).mockReturnValue(undefined);
(vscode.window.showErrorMessage as jest.Mock).mockClear();
liveWatchTreeDataProvider.activate(tracker);
const handler = getRegisteredHandler('vscode-cmsis-debugger.liveWatch.showInMemoryInspector');
const node = makeNode('node', { result: '0x1234', variablesReference: 77 }, 1);
await handler(node);
expect(vscode.window.showErrorMessage).toHaveBeenCalledWith(expect.stringContaining('Memory Inspector extension is not installed'));
expect(vscode.commands.executeCommand).not.toHaveBeenCalledWith('memory-inspector.show-variable', expect.anything());
});

it('showInMemoryInspector executes command with proper args when extension is present', async () => {
(vscode.extensions.getExtension as jest.Mock).mockReturnValue({ id: 'eclipse-cdt.memory-inspector' });
(vscode.commands.executeCommand as jest.Mock).mockResolvedValue('ok');
liveWatchTreeDataProvider.activate(tracker);
(liveWatchTreeDataProvider as any)._activeSession = { session: { id: 'session-1' } };
const handler = getRegisteredHandler('vscode-cmsis-debugger.liveWatch.showInMemoryInspector');
const node = makeNode('node', { result: '0x1234', variablesReference: 0 }, 1);
await handler(node);
const lastCall = (vscode.commands.executeCommand as jest.Mock).mock.calls.pop();
expect(lastCall[0]).toBe('memory-inspector.show-variable');
const args = lastCall[1];
expect(args.sessionId).toBe('session-1');
expect(args.container.name).toBe('node');
expect(args.variable.name).toBe('node');
expect(args.variable.memoryReference).toBe('&(node)');
});
});

describe('evaluate', () => {
Expand Down
Loading