Skip to content

Commit 8b97b4b

Browse files
committed
test/integration/goDebug: test to change to correct goroutine when stepping
The debugger needs to switch to the correct goroutine if the threadId in the request does not match the current goroutine. This change checks the current state to decide if it should switch goroutines, and then does so. This adds a test for 'next' and 'step in' requests that switch goroutines. Updates #118 Change-Id: I553ebbed6de1001e0e7da756278fcc717884f946 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/300769 Trust: Suzy Mueller <[email protected]> Run-TryBot: Suzy Mueller <[email protected]> Reviewed-by: Hyang-Ah Hana Kim <[email protected]> TryBot-Result: kokoro <[email protected]>
1 parent ea2be8f commit 8b97b4b

File tree

3 files changed

+147
-0
lines changed

3 files changed

+147
-0
lines changed

test/integration/goDebug.test.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,6 +1525,126 @@ const testAll = (isDlvDap: boolean) => {
15251525
});
15261526
});
15271527

1528+
suite('switch goroutine', () => {
1529+
async function runSwitchGoroutineTest(stepFunction: string) {
1530+
const PROGRAM = path.join(DATA_ROOT, 'goroutineTest');
1531+
const FILE = path.join(PROGRAM, 'main.go');
1532+
const BREAKPOINT_LINE = 14;
1533+
1534+
const config = {
1535+
name: 'Launch',
1536+
type: 'go',
1537+
request: 'launch',
1538+
mode: 'debug',
1539+
program: PROGRAM
1540+
};
1541+
const debugConfig = await initializeDebugConfig(config);
1542+
await dc.hitBreakpoint(debugConfig, getBreakpointLocation(FILE, BREAKPOINT_LINE));
1543+
// Clear breakpoints to make sure they do not interrupt the stepping.
1544+
const breakpointsResult = await dc.setBreakpointsRequest({
1545+
source: { path: FILE },
1546+
breakpoints: []
1547+
});
1548+
assert.ok(breakpointsResult.success);
1549+
1550+
const threadsResponse = await dc.threadsRequest();
1551+
assert.ok(threadsResponse.success);
1552+
const run1Goroutine = threadsResponse.body.threads.find((val) => val.name.indexOf('main.run1') >= 0);
1553+
const run2Goroutine = threadsResponse.body.threads.find((val) => val.name.indexOf('main.run2') >= 0);
1554+
1555+
// runStepFunction runs the necessary step function and resolves if it succeeded.
1556+
async function runStepFunction(
1557+
args: { threadId: number },
1558+
resolve: (value: void | PromiseLike<void>) => void,
1559+
reject: (reason?: any) => void
1560+
) {
1561+
const callback = (resp: any) => {
1562+
assert.ok(resp.success);
1563+
resolve();
1564+
};
1565+
switch (stepFunction) {
1566+
case 'next':
1567+
callback(await dc.nextRequest(args));
1568+
break;
1569+
case 'step in':
1570+
callback(await dc.stepInRequest(args));
1571+
break;
1572+
case 'step out':
1573+
// TODO(suzmue): write a test for step out.
1574+
reject(new Error('step out will never complete on this program'));
1575+
break;
1576+
default:
1577+
reject(new Error(`not a valid step function ${stepFunction}`));
1578+
}
1579+
}
1580+
1581+
// The program is currently stopped on the goroutine in main.run2.
1582+
// Test switching go routines by stepping in:
1583+
// 1. main.run2
1584+
// 2. main.run1 (switch routine)
1585+
// 3. main.run1
1586+
// 4. main.run2 (switch routine)
1587+
1588+
// Next on the goroutine in main.run2
1589+
await Promise.all([
1590+
new Promise<void>((resolve, reject) => {
1591+
const args = { threadId: run2Goroutine.id };
1592+
return runStepFunction(args, resolve, reject);
1593+
}),
1594+
dc.waitForEvent('stopped').then((event) => {
1595+
assert.strictEqual(event.body.reason, 'step');
1596+
assert.strictEqual(event.body.threadId, run2Goroutine.id);
1597+
})
1598+
]);
1599+
1600+
// Next on the goroutine in main.run1
1601+
await Promise.all([
1602+
new Promise<void>((resolve, reject) => {
1603+
const args = { threadId: run1Goroutine.id };
1604+
return runStepFunction(args, resolve, reject);
1605+
}),
1606+
dc.waitForEvent('stopped').then((event) => {
1607+
assert.strictEqual(event.body.reason, 'step');
1608+
assert.strictEqual(event.body.threadId, run1Goroutine.id);
1609+
})
1610+
]);
1611+
1612+
// Next on the goroutine in main.run1
1613+
await Promise.all([
1614+
new Promise<void>((resolve, reject) => {
1615+
const args = { threadId: run1Goroutine.id };
1616+
return runStepFunction(args, resolve, reject);
1617+
}),
1618+
dc.waitForEvent('stopped').then((event) => {
1619+
assert.strictEqual(event.body.reason, 'step');
1620+
assert.strictEqual(event.body.threadId, run1Goroutine.id);
1621+
})
1622+
]);
1623+
1624+
// Next on the goroutine in main.run2
1625+
await Promise.all([
1626+
new Promise<void>((resolve, reject) => {
1627+
const args = { threadId: run2Goroutine.id };
1628+
return runStepFunction(args, resolve, reject);
1629+
}),
1630+
dc.waitForEvent('stopped').then((event) => {
1631+
assert.strictEqual(event.body.reason, 'step');
1632+
assert.strictEqual(event.body.threadId, run2Goroutine.id);
1633+
})
1634+
]);
1635+
}
1636+
1637+
test.skip('next', async () => {
1638+
// neither debug adapter implements this behavior
1639+
await runSwitchGoroutineTest('next');
1640+
});
1641+
1642+
test.skip('step in', async () => {
1643+
// neither debug adapter implements this behavior
1644+
await runSwitchGoroutineTest('step in');
1645+
});
1646+
});
1647+
15281648
suite('substitute path', () => {
15291649
// TODO(suzmue): add unit tests for substitutePath.
15301650
let tmpDir: string;

test/testdata/goroutineTest/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/golang/vscode-go/gofixtures/goroutineTest
2+
3+
go 1.16

test/testdata/goroutineTest/main.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package main
2+
3+
func run1() {
4+
x := 0
5+
for {
6+
x++
7+
x *= 4
8+
}
9+
}
10+
11+
func run2() {
12+
x := 0
13+
for {
14+
x++
15+
x *= 4
16+
}
17+
}
18+
19+
func main() {
20+
go run1()
21+
go run2()
22+
for {
23+
}
24+
}

0 commit comments

Comments
 (0)