Skip to content

Commit 4636370

Browse files
authored
Added unit test attaching a debugger (#597)
Fixes #251
1 parent ad806fa commit 4636370

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed

src/test/debugger/attach.test.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { expect, use } from 'chai';
5+
import * as chaiAsPromised from 'chai-as-promised';
6+
import { ChildProcess } from 'child_process';
7+
import * as getFreePort from 'get-port';
8+
import { EOL } from 'os';
9+
import * as path from 'path';
10+
import { ThreadEvent } from 'vscode-debugadapter';
11+
import { DebugClient } from 'vscode-debugadapter-testsupport';
12+
import { createDeferred } from '../../client/common/helpers';
13+
import { BufferDecoder } from '../../client/common/process/decoder';
14+
import { ProcessService } from '../../client/common/process/proc';
15+
import { AttachRequestArguments } from '../../client/debugger/Common/Contracts';
16+
import { initialize } from '../initialize';
17+
18+
use(chaiAsPromised);
19+
20+
const fileToDebug = path.join(__dirname, '..', '..', '..', 'src', 'testMultiRootWkspc', 'workspace5', 'remoteDebugger.py');
21+
const ptvsdPath = path.join(__dirname, '..', '..', '..', 'pythonFiles', 'PythonTools');
22+
const DEBUG_ADAPTER = path.join(__dirname, '..', '..', 'client', 'debugger', 'Main.js');
23+
24+
// tslint:disable-next-line:max-func-body-length
25+
suite('Attach Debugger', () => {
26+
let debugClient: DebugClient;
27+
let procToKill: ChildProcess;
28+
suiteSetup(function () {
29+
// tslint:disable-next-line:no-invalid-this
30+
this.skip();
31+
return initialize();
32+
});
33+
34+
setup(async () => {
35+
await new Promise(resolve => setTimeout(resolve, 1000));
36+
debugClient = new DebugClient('node', DEBUG_ADAPTER, 'python');
37+
await debugClient.start();
38+
});
39+
teardown(async () => {
40+
// Wait for a second before starting another test (sometimes, sockets take a while to get closed).
41+
await new Promise(resolve => setTimeout(resolve, 1000));
42+
try {
43+
debugClient.stop();
44+
// tslint:disable-next-line:no-empty
45+
} catch (ex) { }
46+
if (procToKill) {
47+
procToKill.kill();
48+
}
49+
});
50+
test('Confirm we are able to attach to a running program', async () => {
51+
const port = await getFreePort({ host: 'localhost', port: 3000 });
52+
const args: AttachRequestArguments = {
53+
localRoot: path.dirname(fileToDebug),
54+
remoteRoot: path.dirname(fileToDebug),
55+
port: port,
56+
host: 'localhost',
57+
secret: 'super_secret'
58+
};
59+
60+
const customEnv = { ...process.env };
61+
62+
// Set the path for PTVSD to be picked up.
63+
// tslint:disable-next-line:no-string-literal
64+
customEnv['PYTHONPATH'] = ptvsdPath;
65+
const procService = new ProcessService(new BufferDecoder());
66+
const result = procService.execObservable('python', [fileToDebug, port.toString()], { env: customEnv, cwd: path.dirname(fileToDebug) });
67+
procToKill = result.proc;
68+
69+
const completed = createDeferred();
70+
const expectedOutputs = [
71+
{ value: 'start', deferred: createDeferred() },
72+
{ value: 'Peter Smith', deferred: createDeferred() },
73+
{ value: 'end', deferred: createDeferred() }
74+
];
75+
const startOutputReceived = expectedOutputs[0].deferred.promise;
76+
const firstOutputReceived = expectedOutputs[1].deferred.promise;
77+
const secondOutputReceived = expectedOutputs[2].deferred.promise;
78+
79+
result.out.subscribe(output => {
80+
if (expectedOutputs[0].value === output.out) {
81+
expectedOutputs.shift()!.deferred.resolve();
82+
}
83+
}, ex => {
84+
completed.reject(ex);
85+
}, () => {
86+
completed.resolve();
87+
});
88+
89+
await startOutputReceived;
90+
91+
const threadIdPromise = createDeferred<number>();
92+
debugClient.on('thread', (data: ThreadEvent) => {
93+
if (data.body.reason === 'started') {
94+
threadIdPromise.resolve(data.body.threadId);
95+
}
96+
});
97+
98+
const initializePromise = debugClient.initializeRequest({
99+
adapterID: 'python',
100+
linesStartAt1: true,
101+
columnsStartAt1: true,
102+
supportsRunInTerminalRequest: true,
103+
pathFormat: 'path'
104+
});
105+
await debugClient.attachRequest(args);
106+
await initializePromise;
107+
108+
// Wait till we get the thread of the program.
109+
const threadId = await threadIdPromise.promise;
110+
expect(threadId).to.be.greaterThan(0, 'ThreadId not received');
111+
112+
// Continue the program.
113+
await debugClient.continueRequest({ threadId });
114+
115+
// Value for input prompt.
116+
result.proc.stdin.write(`Peter Smith${EOL}`);
117+
await firstOutputReceived;
118+
119+
result.proc.stdin.write(`${EOL}`);
120+
await secondOutputReceived;
121+
await completed.promise;
122+
123+
await debugClient.waitForEvent('terminated');
124+
});
125+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import sys
2+
import ptvsd
3+
import time
4+
5+
sys.stdout.write('start')
6+
sys.stdout.flush()
7+
address = ('0.0.0.0', int(sys.argv[1]))
8+
ptvsd.enable_attach('super_secret', address)
9+
ptvsd.wait_for_attach()
10+
11+
name = input()
12+
sys.stdout.write(name)
13+
sys.stdout.flush()
14+
input()
15+
sys.stdout.write('end')
16+
sys.stdout.flush()

0 commit comments

Comments
 (0)