Skip to content

Commit

Permalink
Add smoke test to detect extension load and python selection (microso…
Browse files Browse the repository at this point in the history
  • Loading branch information
karthiknadig authored Feb 1, 2024
1 parent 6566656 commit 12df405
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 28 deletions.
22 changes: 21 additions & 1 deletion .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@ jobs:
cache: 'npm'
cache-dependency-path: ${{ env.special-working-directory-relative }}/package-lock.json

- name: Use Python 3.8
uses: actions/setup-python@v5
with:
python-version: '3.8'

- name: Update pip, install wheel and nox
run: python -m pip install -U pip wheel nox
shell: bash

# This will install libraries to a target directory.
- name: Install bundled python libraries
run: python -m nox --session install_bundled_libs
shell: bash

- name: Install Node dependencies
run: npm ci
shell: bash
Expand All @@ -120,8 +134,14 @@ jobs:
run: npm run pretest
shell: bash

- name: Run TS tests
- name: Run TS Unit tests
uses: GabrielBB/xvfb-action@v1.6
with:
run: npm run tests
working-directory: ${{ env.special-working-directory }}

- name: Run TS Smoke tests
uses: GabrielBB/xvfb-action@v1.6
with:
run: npm run smoke-tests
working-directory: ${{ env.special-working-directory }}
45 changes: 22 additions & 23 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@
"name": "Debug Extension Only",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "npm: watch",
"presentation": {
"hidden": false,
Expand Down Expand Up @@ -42,20 +38,30 @@
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/ts_tests/index"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js",
"${workspaceFolder}/dist/**/*.js"
"outFiles": ["${workspaceFolder}/out/**/*.js", "${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "tasks: watch-tests"
},
{
"name": "TS Smoke Tests",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/ts_tests/index",
"${workspaceFolder}/src/test/ts_tests/test_data/project"
],
"outFiles": ["${workspaceFolder}/out/**/*.js", "${workspaceFolder}/dist/**/*.js"],
"env": {
"SMOKE_TESTS": "true"
},
"preLaunchTask": "tasks: watch-tests"
},
{
"name": "Python Config for test explorer (hidden)",
"type": "python",
"request": "launch",
"console": "integratedTerminal",
"purpose": [
"debug-test"
],
"purpose": ["debug-test"],
"justMyCode": true,
"presentation": {
"hidden": true,
Expand All @@ -67,12 +73,8 @@
"name": "Debug Extension (hidden)",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"args": ["--extensionDevelopmentPath=${workspaceFolder}"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"env": {
"USE_DEBUGPY": "True"
},
Expand Down Expand Up @@ -101,10 +103,7 @@
"compounds": [
{
"name": "Debug Extension and Python",
"configurations": [
"Python debug server (hidden)",
"Debug Extension (hidden)"
],
"configurations": ["Python debug server (hidden)", "Debug Extension (hidden)"],
"stopAll": true,
"preLaunchTask": "npm: watch",
"presentation": {
Expand All @@ -114,4 +113,4 @@
}
}
]
}
}
4 changes: 2 additions & 2 deletions bundled/tool/lsp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ def initialize(params: lsp.InitializeParams) -> None:
paths = "\r\n ".join(sys.path)
log_to_output(f"sys.path used to run Server:\r\n {paths}")

_update_workspace_settings_with_version_info(WORKSPACE_SETTINGS)


@LSP_SERVER.feature(lsp.EXIT)
def on_exit(_params: Optional[Any] = None) -> None:
Expand Down Expand Up @@ -374,8 +376,6 @@ def _update_workspace_settings(settings):
"workspaceFS": key,
}

_update_workspace_settings_with_version_info(WORKSPACE_SETTINGS)


def _get_settings_by_path(file_path: pathlib.Path):
workspaces = {s["workspaceFS"] for s in WORKSPACE_SETTINGS.values()}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"package": "webpack --mode production --devtool hidden-source-map",
"pretest": "npm run compile-tests && npm run compile && npm run lint",
"tests": "node ./out/test/ts_tests/runTest.js",
"smoke-tests": "node ./out/test/ts_tests/runSmokeTest.js",
"vsce-package": "vsce package -o black-formatter.vsix",
"vscode:prepublish": "npm run package",
"watch": "webpack --watch",
Expand Down
1 change: 0 additions & 1 deletion src/common/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ async function getPythonExtensionAPI(): Promise<PythonExtension | undefined> {
export async function initializePython(disposables: Disposable[]): Promise<void> {
try {
const api = await getPythonExtensionAPI();

if (api) {
disposables.push(
api.environments.onDidChangeActiveEnvironmentPath((e) => {
Expand Down
8 changes: 7 additions & 1 deletion src/test/ts_tests/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as glob from 'glob';
import Mocha from 'mocha';
import * as path from 'path';
import { env } from 'process';

export function run(): Promise<void> {
// Create the mocha test
Expand All @@ -12,7 +13,12 @@ export function run(): Promise<void> {
const testsRoot = path.resolve(__dirname, './tests');

return new Promise((c, e) => {
const files = glob.globSync('**/**.test.js', { cwd: testsRoot });
let files = [];
if (env.SMOKE_TESTS) {
files = glob.globSync('**/**.smoke.test.js', { cwd: testsRoot });
} else {
files = glob.globSync('**/**.unit.test.js', { cwd: testsRoot });
}

// Add files to the test suite
files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));
Expand Down
40 changes: 40 additions & 0 deletions src/test/ts_tests/runSmokeTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/* eslint-disable @typescript-eslint/naming-convention */

import * as cp from 'child_process';
import * as path from 'path';

import { runTests, downloadAndUnzipVSCode, resolveCliArgsFromVSCodeExecutablePath } from '@vscode/test-electron';
import { EXTENSION_ROOT_DIR } from '../../common/constants';

const TEST_PROJECT_DIR = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'ts_tests', 'test_data', 'project');

async function main() {
try {
const vscodeExecutablePath = await downloadAndUnzipVSCode('stable');

const [cli, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);
const command = path.relative(EXTENSION_ROOT_DIR, cli);
cp.spawnSync(command, [...args, '--install-extension', 'ms-python.python'], {
encoding: 'utf-8',
stdio: 'inherit',
});

const extensionDevelopmentPath = EXTENSION_ROOT_DIR;
const extensionTestsPath = path.resolve(__dirname, './index');

await runTests({
extensionDevelopmentPath,
extensionTestsPath,
extensionTestsEnv: { SMOKE_TESTS: 'true' },
launchArgs: [TEST_PROJECT_DIR],
});
} catch (err) {
console.error('Failed to run tests');
console.error(err);
process.exit(1);
}
}

main();
6 changes: 6 additions & 0 deletions src/test/ts_tests/test_data/project/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true
}
}
4 changes: 4 additions & 0 deletions src/test/ts_tests/test_data/project/myscript.formatted
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import os
import sys

print(os.fspath(sys.executable))
2 changes: 2 additions & 0 deletions src/test/ts_tests/test_data/project/myscript.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import sys
print(sys.executable)
2 changes: 2 additions & 0 deletions src/test/ts_tests/test_data/project/myscript.unformatted
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import os;import sys
print(os.fspath(sys.executable))
118 changes: 118 additions & 0 deletions src/test/ts_tests/tests/common/minimal.smoke.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import * as path from 'path';
import * as vscode from 'vscode';
import * as fsapi from 'fs-extra';
import { EXTENSION_ROOT_DIR } from '../../../../common/constants';
import { assert } from 'chai';

const TEST_PROJECT_DIR = path.join(EXTENSION_ROOT_DIR, 'src', 'test', 'ts_tests', 'test_data', 'project');
const TIMEOUT = 120000; // 120 seconds

suite('Smoke Tests', function () {
this.timeout(TIMEOUT);

let disposables: vscode.Disposable[] = [];

setup(async () => {
disposables = [];
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
});

teardown(async () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors');

disposables.forEach((d) => d.dispose());
disposables = [];
});

async function ensurePythonExt(activate?: boolean): Promise<void> {
const pythonExt = vscode.extensions.getExtension('ms-python.python');
assert.ok(pythonExt, 'Python Extension not found');
if (activate) {
await pythonExt?.activate();
}
}

async function ensureBlackExt(activate?: boolean): Promise<void> {
const extension = vscode.extensions.getExtension('ms-python.black-formatter');
assert.ok(extension, 'Black Formatter Extension not found');
if (activate) {
await extension?.activate();
}
}

test('Ensure Black Formatter Extension loads', async () => {
await vscode.workspace.openTextDocument(path.join(TEST_PROJECT_DIR, 'myscript.py'));

await ensurePythonExt(true);
await ensureBlackExt(false);

const extension = vscode.extensions.getExtension('ms-python.black-formatter');
if (extension) {
let timeout = TIMEOUT;
while (!extension.isActive && timeout > 0) {
await new Promise((resolve) => setTimeout(resolve, 100));
timeout -= 100;
}
assert.ok(extension.isActive, `Extension not activated in ${TIMEOUT / 1000} seconds`);
}
});

test('Ensure Black Formatter formats a file on save', async () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
await ensurePythonExt(true);
const scriptPath = path.join(TEST_PROJECT_DIR, 'myscript.py');

const unformatted = await fsapi.readFile(path.join(TEST_PROJECT_DIR, 'myscript.unformatted'), {
encoding: 'utf8',
});
const formatted = await fsapi.readFile(path.join(TEST_PROJECT_DIR, 'myscript.formatted'), { encoding: 'utf8' });
await fsapi.writeFile(scriptPath, unformatted, { encoding: 'utf8' });

await ensureBlackExt(true);

const doc = await vscode.workspace.openTextDocument(scriptPath);
await vscode.window.showTextDocument(doc);

const editor = vscode.window.activeTextEditor;
assert.ok(editor, 'No active editor');
assert.ok(editor?.document.uri.fsPath.endsWith('myscript.py'), 'Active editor is not myscript.py');

const formatDone = new Promise<void>((resolve) => {
const watcher = vscode.workspace.createFileSystemWatcher(
new vscode.RelativePattern(TEST_PROJECT_DIR, 'myscript.py'),
true, // We don't need create events
false, // We need change events
true, // We don't need delete events
);
disposables.push(
watcher,
watcher.onDidChange((e) => {
const text = fsapi.readFileSync(e.fsPath, { encoding: 'utf8' });
if (!text.includes(';')) {
console.log('Saved with format changes');
resolve();
} else {
console.log('Saved without format changes');
}
}),
);
});

const timer = setInterval(() => {
console.log('Saving file');
vscode.commands.executeCommand('workbench.action.files.save');
}, 1000);
disposables.push({ dispose: () => clearInterval(timer) });

await vscode.commands.executeCommand('workbench.action.files.save');
await formatDone;
const actualText = await fsapi.readFile(scriptPath, { encoding: 'utf8' });
assert.equal(actualText, formatted);

//cleanup
await fsapi.writeFile(scriptPath, '', { encoding: 'utf8' });
});
});

0 comments on commit 12df405

Please sign in to comment.