Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add smoke test to detect extension load and python selection #431

Merged
merged 23 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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' });
});
});
Loading