Skip to content

Commit 8b8c86b

Browse files
committed
Add initial debugger tests
1 parent 2a55115 commit 8b8c86b

File tree

6 files changed

+206
-41
lines changed

6 files changed

+206
-41
lines changed

src/test/test-files/hScript2.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
a=1;
2+
b=2;
3+
c=3;
4+
d=4;
5+
e=5;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
import * as vet from 'vscode-extension-tester'
3+
import * as PollingUtils from '../utils/PollingUtils'
4+
5+
/**
6+
* DebuggerTester
7+
* Used to test the debugging functionality for MATLAB in VSCode.
8+
*/
9+
export class DebuggerTester {
10+
private readonly workbench: vet.Workbench
11+
public toolbar!: vet.DebugToolbar
12+
13+
public constructor (workbench: vet.Workbench) {
14+
this.workbench = workbench
15+
}
16+
17+
public async waitForToolbar (): Promise<void> {
18+
this.toolbar = await vet.DebugToolbar.create()
19+
}
20+
21+
public async assertStoppedAtLine (lineNumber: number): Promise<void> {
22+
return await PollingUtils.poll(this.getCurrentExecutionLine.bind(this), lineNumber, `Expected debugger to be stopped at ${lineNumber}`)
23+
}
24+
25+
public async assertNotDebugging (): Promise<void> {
26+
return await PollingUtils.poll(this.getCurrentExecutionLine.bind(this), -1, 'Expected debugger to not be stopped at any line')
27+
}
28+
29+
private async getCurrentExecutionLine (): Promise<number> {
30+
const editor = new vet.TextEditor()
31+
const breakpoint = await editor.getPausedBreakpoint()
32+
return await breakpoint?.getLineNumber() ?? -1
33+
}
34+
35+
public async setToolbarPosition (position: string): Promise<void> {
36+
const editor = await this.workbench.openSettings();
37+
const setting = await editor.findSettingByID('debug.toolBarLocation') as vet.ComboSetting
38+
await setting.setValue(position)
39+
return await new vet.EditorView().closeEditor('Settings')
40+
}
41+
}

src/test/tools/tester/TestSuite.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ export class TestSuite {
3131
const settings = JSON.stringify({
3232
'MATLAB.installPath': MATLAB_PATH,
3333
'window.dialogStyle': 'custom',
34-
'terminal.integrated.copyOnSelection': true
34+
'terminal.integrated.copyOnSelection': true,
35+
'debug.toolBarLocation': 'docked',
36+
'workbench.startupEditor': 'none'
3537
})
3638
fs.writeFileSync(settingsjson, settings)
3739
this.vscodeSettings = settingsjson

src/test/tools/tester/VSCodeTester.ts

Lines changed: 78 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as vet from 'vscode-extension-tester'
33
import * as path from 'path'
44
import * as PollingUtils from '../utils/PollingUtils'
55
import { TerminalTester } from './TerminalTester'
6+
import { DebuggerTester } from './DebuggerTester'
67

78
/**
89
* VSCodeTester
@@ -13,11 +14,52 @@ export class VSCodeTester {
1314
private readonly workbench: vet.Workbench
1415
private readonly statusbar: vet.StatusBar
1516
public terminal!: TerminalTester
17+
public debugger!: DebuggerTester
1618

1719
public constructor () {
1820
this.browser = vet.VSBrowser.instance
1921
this.workbench = new vet.Workbench()
2022
this.statusbar = new vet.StatusBar()
23+
this.debugger = new DebuggerTester(this.workbench)
24+
}
25+
26+
/**
27+
* Connects to MATLAB
28+
*/
29+
public async connectToMATLAB (): Promise<void> {
30+
const prompt = await this.workbench.openCommandPrompt() as vet.InputBox;
31+
await prompt.setText('>matlab.changeMatlabConnection')
32+
await this.selectQuickPick(prompt, 'MATLAB: Change MATLAB Connection')
33+
await this.selectQuickPick(prompt, 'Connect to MATLAB')
34+
return await this.assertMATLABConnected()
35+
}
36+
37+
/**
38+
* Disconnects MATLAB
39+
*/
40+
public async disconnectFromMATLAB (): Promise<void> {
41+
const prompt = await this.workbench.openCommandPrompt() as vet.InputBox;
42+
await prompt.setText('>matlab.changeMatlabConnection');
43+
await this.selectQuickPick(prompt, 'MATLAB: Change MATLAB Connection')
44+
await this.selectQuickPick(prompt, 'Disconnect from MATLAB')
45+
return await this.assertMATLABDisconnected()
46+
}
47+
48+
/**
49+
* check for quickpick to contain a label
50+
*/
51+
private async checkForQuickPick (prompt: vet.InputBox, label: string): Promise<boolean> {
52+
const quickPicks = await prompt.getQuickPicks()
53+
const labels = await Promise.all(quickPicks.map(item => item.getLabel()));
54+
return labels.includes(label);
55+
}
56+
57+
/**
58+
* wait for quickpick to contain a label
59+
*/
60+
public async selectQuickPick (prompt: vet.InputBox, label: string): Promise<void> {
61+
await PollingUtils.poll(this.checkForQuickPick.bind(this, prompt, label), true, `Expected quickpick to contain ${label}`, 5000)
62+
return await prompt.selectQuickPick(label);
2163
}
2264

2365
/**
@@ -26,7 +68,7 @@ export class VSCodeTester {
2668
private async getConnectionStatus (): Promise<string> {
2769
const statusItem = await this.statusbar.findElements(vet.By.xpath('//*[@id="MathWorks.language-matlab"]'))
2870
const status = await statusItem[0]?.getAttribute('aria-label')
29-
return status;
71+
return status
3072
}
3173

3274
/**
@@ -40,70 +82,69 @@ export class VSCodeTester {
4082
* Poll for MATLAB to disconnect from VSCode
4183
*/
4284
public async assertMATLABDisconnected (): Promise<void> {
43-
return await PollingUtils.poll(this.getConnectionStatus.bind(this), 'MATLAB: Not Connected', 'Expected MATLAB to be disonnected')
85+
return await PollingUtils.poll(this.getConnectionStatus.bind(this), 'MATLAB: Not Connected', 'Expected MATLAB to be disconnected')
86+
}
87+
88+
/**
89+
* Return the path to 'test-files' directory
90+
*/
91+
public getTestFilesDirectory (): string {
92+
return path.resolve(__dirname, '..', '..', 'test-files')
4493
}
4594

4695
/**
4796
* Open a file from the 'test-files' directory
4897
*/
49-
public async openDocument (filename: string): Promise<void> {
50-
const filepath = path.resolve(__dirname, '..', '..', 'test-files', filename)
98+
public async openEditor (filename: string): Promise<vet.TextEditor> {
99+
const filepath = path.resolve(this.getTestFilesDirectory(), filename)
51100
await this.browser.openResources(filepath)
101+
return new vet.TextEditor()
52102
}
53103

54104
/**
55105
* Close the editor that is currently active
56106
*/
57-
public async closeActiveDocument (): Promise<void> {
58-
const editor = new vet.TextEditor();
107+
public async closeActiveEditor (): Promise<void> {
108+
const editor = new vet.TextEditor()
59109
if (await editor.isDirty()) {
60-
await new vet.EditorView().closeEditor(await editor.getTitle());
61-
const dialog = new vet.ModalDialog();
62-
return await dialog.pushButton('Don\'t Save');
110+
await new vet.EditorView().closeEditor(await editor.getTitle())
111+
const dialog = new vet.ModalDialog()
112+
return await dialog.pushButton('Don\'t Save')
63113
}
64-
return await new vet.EditorView().closeEditor(await editor.getTitle());
114+
return await new vet.EditorView().closeEditor(await editor.getTitle())
65115
}
66116

67117
/**
68118
* Opens the MATLAB terminal and creates a new terminal tester
69119
*/
70120
public async openMATLABTerminal (): Promise<TerminalTester> {
71-
const prompt = await this.workbench.openCommandPrompt()
121+
const prompt = await this.workbench.openCommandPrompt() as vet.InputBox
72122
await prompt.setText('>matlab.openCommandWindow')
73-
await prompt.confirm()
74-
const terminal = await new vet.BottomBarPanel().openTerminalView();
123+
await this.selectQuickPick(prompt, 'MATLAB: Open Command Window');
124+
await this.assertMATLABConnected()
125+
const terminal = await new vet.BottomBarPanel().openTerminalView()
75126
const terminalTester = new TerminalTester(this.workbench, terminal)
76127
this.terminal = terminalTester
77128
return terminalTester
78129
}
79130

80-
public async selectFromNotification (label: string): Promise<void> {
81-
await this.waitForNotifications()
82-
const notifications = await this.workbench.getNotifications();
83-
const notification = notifications[0];
84-
return await notification.takeAction(label)
85-
}
86-
87-
public async clearNotifications (): Promise<void> {
88-
let notifications = await this.workbench.getNotifications();
89-
while (notifications.length > 0) {
90-
await notifications[0].dismiss();
91-
notifications = await this.workbench.getNotifications();
92-
}
93-
}
94-
95-
private async waitForNotifications (): Promise<void> {
96-
await PollingUtils.poll(this.doNotificationsExist.bind(this), true)
97-
}
98-
99-
private async doNotificationsExist (): Promise<boolean> {
100-
const notifications = await this.workbench.getNotifications();
101-
return notifications.length > 0
102-
}
103-
104131
public async runCurrentFile (): Promise<void> {
105132
const prompt = await this.workbench.openCommandPrompt()
106133
await prompt.setText('>matlab.runFile')
107134
return await prompt.confirm()
108135
}
136+
137+
public async setSetting (id: string, value: string): Promise<void> {
138+
const editor = await this.workbench.openSettings();
139+
const setting = await editor.findSettingByID(id) as vet.ComboSetting | vet.TextSetting;
140+
await setting.setValue(value)
141+
return await new vet.EditorView().closeEditor('Settings')
142+
}
143+
144+
public async setCheckBoxSetting (id: string, value: boolean): Promise<void> {
145+
const editor = await this.workbench.openSettings();
146+
const setting = await editor.findSettingByID(id) as vet.CheckboxSetting;
147+
await setting.setValue(value)
148+
return await new vet.EditorView().closeEditor('Settings')
149+
}
109150
}

src/test/ui/debugging.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2024 The MathWorks, Inc.
2+
import { VSCodeTester } from '../tools/tester/VSCodeTester'
3+
import { before, afterEach, after } from 'mocha';
4+
5+
suite('Debugging Smoke Tests', () => {
6+
let vs: VSCodeTester
7+
8+
before(async () => {
9+
vs = new VSCodeTester();
10+
await vs.openEditor('hScript1.m')
11+
await vs.assertMATLABConnected()
12+
await vs.closeActiveEditor()
13+
await vs.openMATLABTerminal()
14+
await vs.terminal.executeCommand(`addpath('${vs.getTestFilesDirectory()}')`)
15+
await vs.terminal.executeCommand('clc')
16+
});
17+
18+
afterEach(async () => {
19+
await vs.terminal.executeCommand('dbclear all, clc')
20+
await vs.closeActiveEditor()
21+
});
22+
23+
after(async () => {
24+
await vs.disconnectFromMATLAB()
25+
});
26+
27+
test('Basic debugging operations', async () => {
28+
await vs.setSetting('debug.toolBarLocation', 'floating');
29+
const editor = await vs.openEditor('hScript2.m')
30+
await editor.toggleBreakpoint(1)
31+
await editor.toggleBreakpoint(3)
32+
await vs.runCurrentFile()
33+
await vs.debugger.waitForToolbar()
34+
await vs.debugger.assertStoppedAtLine(1)
35+
await vs.debugger.toolbar.continue()
36+
await vs.debugger.assertStoppedAtLine(3)
37+
await vs.debugger.toolbar.stepOver()
38+
await vs.debugger.assertStoppedAtLine(4)
39+
await vs.debugger.toolbar.stop()
40+
await vs.debugger.assertNotDebugging()
41+
await editor.toggleBreakpoint(1)
42+
await editor.toggleBreakpoint(3)
43+
await vs.setSetting('debug.toolBarLocation', 'docked');
44+
})
45+
46+
test('Basic debugging operations via terminal', async () => {
47+
await vs.openEditor('hScript2.m')
48+
await vs.terminal.executeCommand('dbstop in hScript2 at 1')
49+
await vs.terminal.executeCommand('dbstop in hScript2 at 3')
50+
await vs.runCurrentFile()
51+
await vs.debugger.assertStoppedAtLine(1)
52+
await vs.terminal.assertContains('K>>', 'terminal should have K prompt')
53+
await vs.terminal.executeCommand('dbcont')
54+
await vs.debugger.assertStoppedAtLine(3)
55+
await vs.terminal.executeCommand('dbstep')
56+
await vs.debugger.assertStoppedAtLine(4)
57+
await vs.terminal.executeCommand('dbquit')
58+
await vs.debugger.assertNotDebugging()
59+
})
60+
61+
test('Executing commands while debugging', async () => {
62+
const editor = await vs.openEditor('hScript2.m')
63+
await editor.toggleBreakpoint(1)
64+
await vs.runCurrentFile()
65+
await vs.debugger.assertStoppedAtLine(1)
66+
await vs.terminal.executeCommand('12+17')
67+
await vs.terminal.assertContains('29', 'output should appear in terminal')
68+
await vs.terminal.executeCommand('dbquit')
69+
await vs.debugger.assertNotDebugging()
70+
await editor.toggleBreakpoint(1)
71+
})
72+
});

src/test/ui/terminal.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
// Copyright 2024 The MathWorks, Inc.
22
import { VSCodeTester } from '../tools/tester/VSCodeTester'
3-
import { before, afterEach } from 'mocha';
3+
import { before, afterEach, after } from 'mocha';
44

55
suite('Terminal Smoke Tests', () => {
66
let vs: VSCodeTester
77

88
before(async () => {
99
vs = new VSCodeTester();
10-
await vs.openDocument('hScript1.m')
10+
await vs.openEditor('hScript1.m')
1111
await vs.assertMATLABConnected()
12-
await vs.closeActiveDocument()
12+
await vs.closeActiveEditor()
1313
await vs.openMATLABTerminal()
1414
});
1515

1616
afterEach(async () => {
1717
await vs.terminal.executeCommand('clc');
1818
});
1919

20+
after(async () => {
21+
await vs.disconnectFromMATLAB()
22+
});
23+
2024
test('Test prompt', async () => {
2125
await vs.terminal.assertContent('>>', 'expect terminal to have ready prompt')
2226
})

0 commit comments

Comments
 (0)