Skip to content

Commit f346c09

Browse files
committed
switch to trace logging, add final test
1 parent 13adcaf commit f346c09

File tree

4 files changed

+194
-77
lines changed

4 files changed

+194
-77
lines changed

src/extension/extensionInit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import { IExtensionApi } from './apiTypes';
5353
import { registerHexDebugVisualizationTreeProvider } from './debugger/visualizers/inlineHexDecoder';
5454
import { PythonInlineValueProvider } from './debugger/inlineValue/pythonInlineValueProvider';
5555
import { traceLog } from './common/log/logging';
56-
import { registerConfiglessDebug } from './noConfigDebugInit';
56+
import { registerNoConfigDebug } from './noConfigDebugInit';
5757

5858
export async function registerDebugger(context: IExtensionContext): Promise<IExtensionApi> {
5959
const childProcessAttachService = new ChildProcessAttachService();
@@ -248,7 +248,7 @@ export async function registerDebugger(context: IExtensionContext): Promise<IExt
248248
window.activeTextEditor?.document.languageId === 'python',
249249
);
250250

251-
registerConfiglessDebug(context);
251+
registerNoConfigDebug(context);
252252

253253
return buildApi();
254254
}

src/extension/noConfigDebugInit.ts

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import * as fs from 'fs';
22
import * as path from 'path';
33
import { IExtensionContext } from './common/types';
4-
import { DebugSessionOptions, debug, RelativePattern } from 'vscode';
5-
import { createFileSystemWatcher } from './utils';
4+
import { DebugSessionOptions, RelativePattern } from 'vscode';
5+
import { createFileSystemWatcher, debugStartDebugging } from './utils';
6+
import { traceError, traceVerbose } from './common/log/logging';
67

78
/**
89
* Registers the configuration-less debugging setup for the extension.
@@ -17,7 +18,7 @@ import { createFileSystemWatcher } from './utils';
1718
* - `BUNDLED_DEBUGPY_PATH`: Path to the bundled debugpy library.
1819
* - `PATH`: Appends the path to the noConfigScripts directory.
1920
*/
20-
export async function registerConfiglessDebug(context: IExtensionContext): Promise<void> {
21+
export async function registerNoConfigDebug(context: IExtensionContext): Promise<void> {
2122
const collection = context.environmentVariableCollection;
2223

2324
// Add env vars for DEBUGPY_ADAPTER_ENDPOINTS, BUNDLED_DEBUGPY_PATH, and PATH
@@ -34,51 +35,48 @@ export async function registerConfiglessDebug(context: IExtensionContext): Promi
3435
// create file system watcher for the debuggerAdapterEndpointFolder for when the communication port is written
3536
context.subscriptions.push(
3637
createFileSystemWatcher(new RelativePattern(debugAdapterEndpointDir, '**/*')).onDidCreate((uri) => {
37-
// console.log(`File created: ${uri.fsPath}`);
3838
const filePath = uri.fsPath;
39-
fs.readFile(filePath, 'utf8', (err, data) => {
39+
fs.readFile(filePath, (err, data) => {
40+
const dataParse = data.toString();
4041
if (err) {
41-
// console.error(`Error reading file: ${err}`);
42+
traceError(`Error reading debuggerAdapterEndpoint.txt file: ${err}`);
4243
return;
4344
}
4445
try {
4546
// parse the client port
46-
const jsonData = JSON.parse(data);
47+
const jsonData = JSON.parse(dataParse);
4748
const clientPort = jsonData.client?.port;
48-
//console.log(`Client port: ${clientPort}`);
49+
traceVerbose(`Parsed client port: ${clientPort}`);
4950

5051
const options: DebugSessionOptions = {
5152
noDebug: false,
5253
};
5354

5455
// start debug session with the client port
55-
debug
56-
.startDebugging(
57-
undefined,
58-
{
59-
type: 'python',
60-
request: 'attach',
61-
name: 'Attach to Python',
62-
port: clientPort,
63-
host: 'localhost',
64-
},
65-
options,
66-
)
67-
.then(
68-
(started) => {
69-
if (started) {
70-
//console.log('Debug session started successfully');
71-
} else {
72-
//console.error('Failed to start debug session');
73-
}
74-
},
75-
(error) => {
76-
//console.error(`Error starting debug session: ${error}`);
77-
error;
78-
},
79-
);
56+
debugStartDebugging(
57+
undefined,
58+
{
59+
type: 'python',
60+
request: 'attach',
61+
name: 'Attach to Python',
62+
port: clientPort,
63+
host: 'localhost',
64+
},
65+
options,
66+
).then(
67+
(started) => {
68+
if (started) {
69+
traceVerbose('Successfully started debug session');
70+
} else {
71+
traceError('Error starting debug session, session not started.');
72+
}
73+
},
74+
(error) => {
75+
traceError(`Error starting debug session: ${error}`);
76+
},
77+
);
8078
} catch (parseErr) {
81-
//console.error(`Error parsing JSON: ${parseErr}`);
79+
traceError(`Error parsing JSON: ${parseErr}`);
8280
}
8381
});
8482
JSON.parse;

src/extension/utils.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1-
import { workspace } from 'vscode';
1+
import {
2+
workspace,
3+
debug,
4+
WorkspaceFolder,
5+
DebugConfiguration,
6+
DebugSession,
7+
DebugSessionOptions,
8+
FileSystemWatcher,
9+
} from 'vscode';
210

3-
export function createFileSystemWatcher(args: any) {
11+
export function createFileSystemWatcher(args: any): FileSystemWatcher {
412
return workspace.createFileSystemWatcher(args);
513
}
14+
15+
export function debugStartDebugging(
16+
folder: WorkspaceFolder | undefined,
17+
nameOrConfiguration: string | DebugConfiguration,
18+
parentSessionOrOptions?: DebugSession | DebugSessionOptions,
19+
): Thenable<boolean> {
20+
return debug.startDebugging(folder, nameOrConfiguration, parentSessionOrOptions);
21+
}
Lines changed: 142 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,182 @@
11
import * as path from 'path';
22
import { IExtensionContext } from '../../extension/common/types';
3-
import { registerConfiglessDebug } from '../../extension/noConfigDebugInit';
3+
import { registerNoConfigDebug as registerNoConfigDebug } from '../../extension/noConfigDebugInit';
44
import * as TypeMoq from 'typemoq';
55
import * as sinon from 'sinon';
6-
import { Uri } from 'vscode';
6+
import { DebugConfiguration, DebugSessionOptions, RelativePattern, Uri } from 'vscode';
77
import * as utils from '../../extension/utils';
88
import { assert } from 'console';
99
import * as fs from 'fs';
1010

11-
suite('registerConfiglessDebug', function () {
12-
this.timeout(100000); // Increase timeout to 10 seconds
13-
let replaceStub: sinon.SinonStub;
14-
let appendStub: sinon.SinonStub;
11+
suite('setup for no-config debug scenario', function () {
12+
let envVarCollectionReplaceStub: sinon.SinonStub;
13+
let envVarCollectionAppendStub: sinon.SinonStub;
1514
let context: TypeMoq.IMock<IExtensionContext>;
16-
let createFileSystemWatcher: sinon.SinonStub;
15+
let debugAdapterEndpointDir: string;
16+
let debuggerAdapterEndpointPath: string;
17+
let noConfigScriptsDir: string;
18+
let bundledDebugPath: string;
19+
let DEBUGPY_ADAPTER_ENDPOINTS = 'DEBUGPY_ADAPTER_ENDPOINTS';
20+
let BUNDLED_DEBUGPY_PATH = 'BUNDLED_DEBUGPY_PATH';
1721

22+
const testDataDir = path.join(__dirname, 'testData');
23+
const testFilePath = path.join(testDataDir, 'debuggerAdapterEndpoint.txt');
24+
suiteSetup(() => {
25+
// create file called testData/debuggerAdapterEndpoint.txt
26+
if (!fs.existsSync(testDataDir)) {
27+
fs.mkdirSync(testDataDir);
28+
}
29+
fs.writeFileSync(testFilePath, JSON.stringify({ client: { port: 5678 } }));
30+
});
1831
setup(() => {
1932
context = TypeMoq.Mock.ofType<IExtensionContext>();
2033

21-
context.setup((c) => c.storageUri).returns(() => Uri.parse('a'));
22-
context.setup((c) => (c as any).extensionPath).returns(() => 'b');
34+
context.setup((c) => (c as any).extensionPath).returns(() => 'fake/extension/path');
2335
context.setup((c) => c.subscriptions).returns(() => []);
24-
25-
createFileSystemWatcher = sinon.stub(utils, 'createFileSystemWatcher');
26-
createFileSystemWatcher.callsFake(() => {
27-
return {
28-
onDidCreate: (cb: (arg0: Uri) => void) => {
29-
cb(Uri.parse('a'));
30-
},
31-
};
32-
});
33-
sinon.stub(fs, 'readFile').callsFake(
34-
(TypeMoq.It.isAny(),
35-
TypeMoq.It.isAny(),
36-
(err, data) => {
37-
console.log(err, data);
38-
}),
39-
);
36+
debugAdapterEndpointDir = path.join(context.object.extensionPath, 'noConfigDebugAdapterEndpoints');
37+
debuggerAdapterEndpointPath = path.join(debugAdapterEndpointDir, 'debuggerAdapterEndpoint.txt');
38+
noConfigScriptsDir = path.join(context.object.extensionPath, 'bundled/scripts/noConfigScripts');
39+
bundledDebugPath = path.join(context.object.extensionPath, 'bundled/libs/debugpy');
4040
});
4141
teardown(() => {
4242
sinon.restore();
4343
});
44+
suiteTeardown(() => {
45+
if (fs.existsSync(testDataDir)) {
46+
fs.rmSync(testDataDir, { recursive: true, force: true });
47+
}
48+
});
4449

4550
test('should add environment variables for DEBUGPY_ADAPTER_ENDPOINTS, BUNDLED_DEBUGPY_PATH, and PATH', async () => {
46-
const debugAdapterEndpointDir = path.join(context.object.extensionPath, 'noConfigDebugAdapterEndpoints');
47-
const debuggerAdapterEndpointPath = path.join(debugAdapterEndpointDir, 'debuggerAdapterEndpoint.txt');
48-
const noConfigScriptsDir = path.join(context.object.extensionPath, 'bundled/scripts/noConfigScripts');
49-
const bundledDebugPath = path.join(context.object.extensionPath, 'bundled/libs/debugpy');
50-
5151
const environmentVariableCollectionMock = TypeMoq.Mock.ofType<any>();
52-
replaceStub = sinon.stub();
53-
appendStub = sinon.stub();
52+
envVarCollectionReplaceStub = sinon.stub();
53+
envVarCollectionAppendStub = sinon.stub();
54+
55+
// set up the environment variable collection mock including asserts for the key, value pairs
5456
environmentVariableCollectionMock
5557
.setup((x) => x.replace(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
5658
.callback((key, value) => {
57-
if (key === 'DEBUGPY_ADAPTER_ENDPOINTS') {
59+
if (key === DEBUGPY_ADAPTER_ENDPOINTS) {
5860
assert(value === debuggerAdapterEndpointPath);
59-
} else if (key === 'BUNDLED_DEBUGPY_PATH') {
61+
} else if (key === BUNDLED_DEBUGPY_PATH) {
6062
assert(value === bundledDebugPath);
6163
}
6264
})
63-
.returns(replaceStub);
65+
.returns(envVarCollectionReplaceStub);
6466
environmentVariableCollectionMock
6567
.setup((x) => x.append(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
6668
.callback((key, value) => {
6769
if (key === 'PATH') {
6870
assert(value === `:${noConfigScriptsDir}`);
6971
}
7072
})
71-
.returns(appendStub);
73+
.returns(envVarCollectionAppendStub);
74+
75+
context.setup((c) => c.environmentVariableCollection).returns(() => environmentVariableCollectionMock.object);
76+
77+
setupFileSystemWatchers();
78+
79+
// run init for no config debug
80+
await registerNoConfigDebug(context.object);
81+
82+
// assert that functions called right number of times
83+
sinon.assert.calledTwice(envVarCollectionReplaceStub);
84+
sinon.assert.calledOnce(envVarCollectionAppendStub);
85+
});
86+
87+
test('should create file system watcher for debuggerAdapterEndpointFolder', async () => {
88+
// Arrange
89+
const environmentVariableCollectionMock = TypeMoq.Mock.ofType<any>();
90+
context.setup((c) => c.environmentVariableCollection).returns(() => environmentVariableCollectionMock.object);
91+
let createFileSystemWatcherFunct = setupFileSystemWatchers();
7292

93+
// Act
94+
await registerNoConfigDebug(context.object);
95+
96+
// Assert
97+
sinon.assert.calledOnce(createFileSystemWatcherFunct);
98+
const expectedPattern = new RelativePattern(debugAdapterEndpointDir, '**/*');
99+
sinon.assert.calledWith(createFileSystemWatcherFunct, expectedPattern);
100+
});
101+
102+
test('should start debug session with client port', async () => {
103+
// Arrange
104+
const environmentVariableCollectionMock = TypeMoq.Mock.ofType<any>();
73105
context.setup((c) => c.environmentVariableCollection).returns(() => environmentVariableCollectionMock.object);
74106

75-
await registerConfiglessDebug(context.object);
76-
console.log('All done!');
77-
sinon.assert.calledTwice(replaceStub);
107+
// mock file sys watcher to give back test file
108+
let createFileSystemWatcherFunct: sinon.SinonStub;
109+
createFileSystemWatcherFunct = sinon.stub(utils, 'createFileSystemWatcher');
110+
createFileSystemWatcherFunct.callsFake(() => {
111+
return {
112+
onDidCreate: (callback: (arg0: Uri) => void) => {
113+
callback(Uri.parse(testFilePath));
114+
},
115+
};
116+
});
117+
118+
// create stub of fs.readFile function
119+
sinon.stub(fs, 'readFile').callsFake((_path: any, callback: (arg0: null, arg1: Buffer) => void) => {
120+
console.log('reading file');
121+
callback(null, Buffer.from(JSON.stringify({ client: { port: 5678 } })));
122+
});
123+
124+
const debugStub = sinon.stub(utils, 'debugStartDebugging').resolves(true);
125+
126+
// Act
127+
await registerNoConfigDebug(context.object);
128+
129+
// Assert
130+
sinon.assert.calledOnce(debugStub);
131+
const expectedConfig: DebugConfiguration = {
132+
type: 'python',
133+
request: 'attach',
134+
name: 'Attach to Python',
135+
port: 5678,
136+
host: 'localhost',
137+
};
138+
const optionsExpected: DebugSessionOptions = {
139+
noDebug: false,
140+
};
141+
const actualConfig = debugStub.getCall(0).args[1];
142+
const actualOptions = debugStub.getCall(0).args[2];
143+
144+
if (JSON.stringify(actualConfig) !== JSON.stringify(expectedConfig)) {
145+
console.log('Config diff:', {
146+
expected: expectedConfig,
147+
actual: actualConfig,
148+
});
149+
}
150+
151+
if (JSON.stringify(actualOptions) !== JSON.stringify(optionsExpected)) {
152+
console.log('Options diff:', {
153+
expected: optionsExpected,
154+
actual: actualOptions,
155+
});
156+
}
157+
158+
sinon.assert.calledWith(debugStub, undefined, expectedConfig, optionsExpected);
78159
});
79160
});
161+
162+
function setupFileSystemWatchers(): sinon.SinonStub {
163+
// create stub of createFileSystemWatcher function that will return a fake watcher with a callback
164+
let createFileSystemWatcherFunct: sinon.SinonStub;
165+
createFileSystemWatcherFunct = sinon.stub(utils, 'createFileSystemWatcher');
166+
createFileSystemWatcherFunct.callsFake(() => {
167+
return {
168+
onDidCreate: (callback: (arg0: Uri) => void) => {
169+
callback(Uri.parse('fake/debuggerAdapterEndpoint.txt'));
170+
},
171+
};
172+
});
173+
// create stub of fs.readFile function
174+
sinon.stub(fs, 'readFile').callsFake(
175+
(TypeMoq.It.isAny(),
176+
TypeMoq.It.isAny(),
177+
(err, data) => {
178+
console.log(err, data);
179+
}),
180+
);
181+
return createFileSystemWatcherFunct;
182+
}

0 commit comments

Comments
 (0)