mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Merge pull request #4538 from vsrs/vscode_tests
vscode client side tests
This commit is contained in:
commit
f4f5fca101
12 changed files with 1018 additions and 33 deletions
17
.github/workflows/ci.yaml
vendored
17
.github/workflows/ci.yaml
vendored
|
@ -97,7 +97,13 @@ jobs:
|
||||||
|
|
||||||
typescript:
|
typescript:
|
||||||
name: TypeScript
|
name: TypeScript
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
@ -111,10 +117,19 @@ jobs:
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
|
|
||||||
- run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
|
- run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
|
||||||
|
if: runner.os == 'Linux'
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
|
|
||||||
- run: npm run lint
|
- run: npm run lint
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
|
|
||||||
|
- name: Run vscode tests
|
||||||
|
uses: GabrielBB/xvfb-action@v1.2
|
||||||
|
env:
|
||||||
|
VSCODE_CLI: 1
|
||||||
|
with:
|
||||||
|
run: npm --prefix ./editors/code test
|
||||||
|
# working-directory: ./editors/code # does not work: https://github.com/GabrielBB/xvfb-action/issues/8
|
||||||
|
|
||||||
- run: npm run package --scripts-prepend-node-path
|
- run: npm run package --scripts-prepend-node-path
|
||||||
working-directory: ./editors/code
|
working-directory: ./editors/code
|
||||||
|
|
34
.vscode/launch.json
vendored
34
.vscode/launch.json
vendored
|
@ -70,6 +70,28 @@
|
||||||
"__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/release/rust-analyzer"
|
"__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/release/rust-analyzer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Used for testing the extension with a local build of the LSP server (in `target/release`)
|
||||||
|
// with all other extendions loaded.
|
||||||
|
"name": "Run With Extensions",
|
||||||
|
"type": "extensionHost",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeExecutable": "${execPath}",
|
||||||
|
"args": [
|
||||||
|
"--disable-extension", "matklad.rust-analyzer",
|
||||||
|
"--extensionDevelopmentPath=${workspaceFolder}/editors/code"
|
||||||
|
],
|
||||||
|
"outFiles": [
|
||||||
|
"${workspaceFolder}/editors/code/out/**/*.js"
|
||||||
|
],
|
||||||
|
"preLaunchTask": "Build Server (Release) and Extension",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**/*.js"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/release/rust-analyzer"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// Used to attach LLDB to a running LSP server.
|
// Used to attach LLDB to a running LSP server.
|
||||||
// NOTE: Might require root permissions. For this run:
|
// NOTE: Might require root permissions. For this run:
|
||||||
|
@ -87,5 +109,17 @@
|
||||||
"rust"
|
"rust"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Run Unit Tests",
|
||||||
|
"type": "extensionHost",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeExecutable": "${execPath}",
|
||||||
|
"args": [
|
||||||
|
"--extensionDevelopmentPath=${workspaceFolder}/editors/code",
|
||||||
|
"--extensionTestsPath=${workspaceFolder}/editors/code/out/tests/unit" ],
|
||||||
|
"sourceMaps": true,
|
||||||
|
"outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ],
|
||||||
|
"preLaunchTask": "Pretest"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
12
.vscode/tasks.json
vendored
12
.vscode/tasks.json
vendored
|
@ -40,6 +40,18 @@
|
||||||
"command": "cargo build --release --package rust-analyzer",
|
"command": "cargo build --release --package rust-analyzer",
|
||||||
"problemMatcher": "$rustc"
|
"problemMatcher": "$rustc"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "Pretest",
|
||||||
|
"group": "build",
|
||||||
|
"isBackground": false,
|
||||||
|
"type": "npm",
|
||||||
|
"script": "pretest",
|
||||||
|
"path": "editors/code/",
|
||||||
|
"problemMatcher": {
|
||||||
|
"base": "$tsc",
|
||||||
|
"fileLocation": ["relative", "${workspaceFolder}/editors/code/"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"label": "Build Server and Extension",
|
"label": "Build Server and Extension",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
**
|
**
|
||||||
!out/main.js
|
!out/src/main.js
|
||||||
!package.json
|
!package.json
|
||||||
!package-lock.json
|
!package-lock.json
|
||||||
!ra_syntax_tree.tmGrammar.json
|
!ra_syntax_tree.tmGrammar.json
|
||||||
|
|
768
editors/code/package-lock.json
generated
768
editors/code/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -29,8 +29,10 @@
|
||||||
"package": "vsce package -o rust-analyzer.vsix",
|
"package": "vsce package -o rust-analyzer.vsix",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"watch": "tsc --watch",
|
"watch": "tsc --watch",
|
||||||
"lint": "tsfmt --verify && eslint -c .eslintrc.js --ext ts ./src",
|
"lint": "tsfmt --verify && eslint -c .eslintrc.js --ext ts ./src ./tests",
|
||||||
"fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src --fix"
|
"fix": " tsfmt -r && eslint -c .eslintrc.js --ext ts ./src ./tests --fix",
|
||||||
|
"pretest": "npm run build",
|
||||||
|
"test": "node ./out/tests/runTests.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.0",
|
||||||
|
@ -39,17 +41,22 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^11.1.0",
|
"@rollup/plugin-commonjs": "^11.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^7.1.3",
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
||||||
|
"@types/glob": "^7.1.1",
|
||||||
|
"@types/mocha": "^7.0.2",
|
||||||
"@types/node": "^12.12.39",
|
"@types/node": "^12.12.39",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.5.7",
|
||||||
"@types/vscode": "^1.44.0",
|
"@types/vscode": "^1.44.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||||
"@typescript-eslint/parser": "^2.33.0",
|
"@typescript-eslint/parser": "^2.33.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
|
"glob": "^7.1.6",
|
||||||
|
"mocha": "^7.1.2",
|
||||||
"rollup": "^2.10.0",
|
"rollup": "^2.10.0",
|
||||||
"tslib": "^1.12.0",
|
"tslib": "^1.12.0",
|
||||||
"typescript": "^3.9.2",
|
"typescript": "^3.9.2",
|
||||||
"typescript-formatter": "^7.2.2",
|
"typescript-formatter": "^7.2.2",
|
||||||
"vsce": "^1.75.0"
|
"vsce": "^1.75.0",
|
||||||
|
"vscode-test": "^1.3.0"
|
||||||
},
|
},
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"onLanguage:rust",
|
"onLanguage:rust",
|
||||||
|
@ -57,7 +64,7 @@
|
||||||
"onCommand:rust-analyzer.collectGarbage",
|
"onCommand:rust-analyzer.collectGarbage",
|
||||||
"workspaceContains:**/Cargo.toml"
|
"workspaceContains:**/Cargo.toml"
|
||||||
],
|
],
|
||||||
"main": "./out/main",
|
"main": "./out/src/main",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"taskDefinitions": [
|
"taskDefinitions": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,7 +6,7 @@ import nodeBuiltins from 'builtin-modules';
|
||||||
|
|
||||||
/** @type { import('rollup').RollupOptions } */
|
/** @type { import('rollup').RollupOptions } */
|
||||||
export default {
|
export default {
|
||||||
input: 'out/main.js',
|
input: 'out/src/main.js',
|
||||||
plugins: [
|
plugins: [
|
||||||
resolve({
|
resolve({
|
||||||
preferBuiltins: true
|
preferBuiltins: true
|
||||||
|
@ -20,7 +20,7 @@ export default {
|
||||||
],
|
],
|
||||||
external: [...nodeBuiltins, 'vscode'],
|
external: [...nodeBuiltins, 'vscode'],
|
||||||
output: {
|
output: {
|
||||||
file: './out/main.js',
|
file: './out/src/main.js',
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
exports: 'named'
|
exports: 'named'
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,44 @@ interface CompilationArtifact {
|
||||||
isTest: boolean;
|
isTest: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ArtifactSpec {
|
||||||
|
cargoArgs: string[];
|
||||||
|
filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function artifactSpec(args: readonly string[]): ArtifactSpec {
|
||||||
|
const cargoArgs = [...args, "--message-format=json"];
|
||||||
|
|
||||||
|
// arguments for a runnable from the quick pick should be updated.
|
||||||
|
// see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
|
||||||
|
switch (cargoArgs[0]) {
|
||||||
|
case "run": cargoArgs[0] = "build"; break;
|
||||||
|
case "test": {
|
||||||
|
if (!cargoArgs.includes("--no-run")) {
|
||||||
|
cargoArgs.push("--no-run");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: ArtifactSpec = { cargoArgs: cargoArgs };
|
||||||
|
if (cargoArgs[0] === "test") {
|
||||||
|
// for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
|
||||||
|
// produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
|
||||||
|
result.filter = (artifacts) => artifacts.filter(it => it.isTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export class Cargo {
|
export class Cargo {
|
||||||
constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
|
constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
|
||||||
|
|
||||||
private async artifactsFromArgs(cargoArgs: string[]): Promise<CompilationArtifact[]> {
|
private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> {
|
||||||
const artifacts: CompilationArtifact[] = [];
|
const artifacts: CompilationArtifact[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.runCargo(cargoArgs,
|
await this.runCargo(spec.cargoArgs,
|
||||||
message => {
|
message => {
|
||||||
if (message.reason === 'compiler-artifact' && message.executable) {
|
if (message.reason === 'compiler-artifact' && message.executable) {
|
||||||
const isBinary = message.target.crate_types.includes('bin');
|
const isBinary = message.target.crate_types.includes('bin');
|
||||||
|
@ -43,30 +73,11 @@ export class Cargo {
|
||||||
throw new Error(`Cargo invocation has failed: ${err}`);
|
throw new Error(`Cargo invocation has failed: ${err}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return artifacts;
|
return spec.filter?.(artifacts) ?? artifacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
async executableFromArgs(args: readonly string[]): Promise<string> {
|
async executableFromArgs(args: readonly string[]): Promise<string> {
|
||||||
const cargoArgs = [...args, "--message-format=json"];
|
const artifacts = await this.getArtifacts(artifactSpec(args));
|
||||||
|
|
||||||
// arguments for a runnable from the quick pick should be updated.
|
|
||||||
// see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
|
|
||||||
switch (cargoArgs[0]) {
|
|
||||||
case "run": cargoArgs[0] = "build"; break;
|
|
||||||
case "test": {
|
|
||||||
if (cargoArgs.indexOf("--no-run") === -1) {
|
|
||||||
cargoArgs.push("--no-run");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let artifacts = await this.artifactsFromArgs(cargoArgs);
|
|
||||||
if (cargoArgs[0] === "test") {
|
|
||||||
// for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
|
|
||||||
// produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
|
|
||||||
artifacts = artifacts.filter(a => a.isTest);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (artifacts.length === 0) {
|
if (artifacts.length === 0) {
|
||||||
throw new Error('No compilation artifacts');
|
throw new Error('No compilation artifacts');
|
||||||
|
|
43
editors/code/tests/runTests.ts
Normal file
43
editors/code/tests/runTests.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
import { runTests } from 'vscode-test';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// The folder containing the Extension Manifest package.json
|
||||||
|
// Passed to `--extensionDevelopmentPath`
|
||||||
|
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
||||||
|
|
||||||
|
// Minimum supported version.
|
||||||
|
const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, 'package.json'));
|
||||||
|
const json = JSON.parse(jsonData.toString());
|
||||||
|
let minimalVersion: string = json.engines.vscode;
|
||||||
|
if (minimalVersion.startsWith('^')) minimalVersion = minimalVersion.slice(1);
|
||||||
|
|
||||||
|
const launchArgs = ["--disable-extensions"];
|
||||||
|
|
||||||
|
// All test suites (either unit tests or integration tests) should be in subfolders.
|
||||||
|
const extensionTestsPath = path.resolve(__dirname, './unit/index');
|
||||||
|
|
||||||
|
// Run tests using the minimal supported version.
|
||||||
|
await runTests({
|
||||||
|
version: minimalVersion,
|
||||||
|
launchArgs,
|
||||||
|
extensionDevelopmentPath,
|
||||||
|
extensionTestsPath
|
||||||
|
});
|
||||||
|
|
||||||
|
// and the latest one
|
||||||
|
await runTests({
|
||||||
|
version: 'stable',
|
||||||
|
launchArgs,
|
||||||
|
extensionDevelopmentPath,
|
||||||
|
extensionTestsPath
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(err => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error('Failed to run tests', err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
38
editors/code/tests/unit/index.ts
Normal file
38
editors/code/tests/unit/index.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import * as path from 'path';
|
||||||
|
import Mocha from 'mocha';
|
||||||
|
import glob from 'glob';
|
||||||
|
|
||||||
|
export function run(): Promise<void> {
|
||||||
|
// Create the mocha test
|
||||||
|
const mocha = new Mocha({
|
||||||
|
ui: 'tdd',
|
||||||
|
color: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const testsRoot = __dirname;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add files to the test suite
|
||||||
|
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Run the mocha test
|
||||||
|
mocha.timeout(100000);
|
||||||
|
mocha.run(failures => {
|
||||||
|
if (failures > 0) {
|
||||||
|
reject(new Error(`${failures} tests failed.`));
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
52
editors/code/tests/unit/launch_config.test.ts
Normal file
52
editors/code/tests/unit/launch_config.test.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import * as cargo from '../../src/cargo';
|
||||||
|
|
||||||
|
suite('Launch configuration', () => {
|
||||||
|
|
||||||
|
suite('Lens', () => {
|
||||||
|
test('A binary', async () => {
|
||||||
|
const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
|
||||||
|
|
||||||
|
assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
|
||||||
|
assert.deepEqual(args.filter, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('One of Multiple Binaries', async () => {
|
||||||
|
const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
|
||||||
|
|
||||||
|
assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]);
|
||||||
|
assert.deepEqual(args.filter, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('A test', async () => {
|
||||||
|
const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
|
||||||
|
|
||||||
|
assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]);
|
||||||
|
assert.notDeepEqual(args.filter, undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('QuickPick', () => {
|
||||||
|
test('A binary', async () => {
|
||||||
|
const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
|
||||||
|
|
||||||
|
assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
|
||||||
|
assert.deepEqual(args.filter, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('One of Multiple Binaries', async () => {
|
||||||
|
const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
|
||||||
|
|
||||||
|
assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]);
|
||||||
|
assert.deepEqual(args.filter, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('A test', async () => {
|
||||||
|
const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
|
||||||
|
|
||||||
|
assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]);
|
||||||
|
assert.notDeepEqual(args.filter, undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -9,7 +9,7 @@
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"rootDir": "src",
|
"rootDir": ".",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
|
@ -18,6 +18,11 @@
|
||||||
"newLine": "LF"
|
"newLine": "LF"
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules",
|
||||||
|
".vscode-test"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"src",
|
||||||
|
"tests"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue