diff options
Diffstat (limited to 'src/tools/rust-analyzer/editors/code/tests')
5 files changed, 413 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/editors/code/tests/runTests.ts b/src/tools/rust-analyzer/editors/code/tests/runTests.ts new file mode 100644 index 00000000000..08632ec3b49 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/runTests.ts @@ -0,0 +1,43 @@ +import * as path from "path"; +import * as fs from "fs"; + +import { runTests } from "@vscode/test-electron"; + +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", extensionDevelopmentPath]; + + // 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); +}); diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/index.ts b/src/tools/rust-analyzer/editors/code/tests/unit/index.ts new file mode 100644 index 00000000000..8ad46546abd --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/index.ts @@ -0,0 +1,87 @@ +import * as assert from "node:assert/strict"; +import { readdir } from "fs/promises"; +import * as path from "path"; + +class Test { + readonly name: string; + readonly promise: Promise<void>; + + constructor(name: string, promise: Promise<void>) { + this.name = name; + this.promise = promise; + } +} + +class Suite { + tests: Test[]; + + constructor() { + this.tests = []; + } + + public addTest(name: string, f: () => Promise<void>): void { + const test = new Test(name, f()); + this.tests.push(test); + } + + public async run(): Promise<void> { + let failed = 0; + for (const test of this.tests) { + try { + await test.promise; + ok(` ✔ ${test.name}`); + } catch (e) { + assert.ok(e instanceof Error); + error(` ✖︎ ${test.name}\n ${e.stack}`); + failed += 1; + } + } + if (failed) { + const plural = failed > 1 ? "s" : ""; + throw new Error(`${failed} failed test${plural}`); + } + } +} + +export class Context { + public async suite(name: string, f: (ctx: Suite) => void): Promise<void> { + const ctx = new Suite(); + f(ctx); + try { + ok(`⌛︎ ${name}`); + await ctx.run(); + ok(`✔ ${name}`); + } catch (e) { + assert.ok(e instanceof Error); + error(`✖︎ ${name}\n ${e.stack}`); + throw e; + } + } +} + +export async function run(): Promise<void> { + const context = new Context(); + + const testFiles = (await readdir(path.resolve(__dirname))).filter((name) => + name.endsWith(".test.js"), + ); + for (const testFile of testFiles) { + try { + const testModule = require(path.resolve(__dirname, testFile)); + await testModule.getTests(context); + } catch (e) { + error(`${e}`); + throw e; + } + } +} + +function ok(message: string): void { + // eslint-disable-next-line no-console + console.log(`\x1b[32m${message}\x1b[0m`); +} + +function error(message: string): void { + // eslint-disable-next-line no-console + console.error(`\x1b[31m${message}\x1b[0m`); +} diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts new file mode 100644 index 00000000000..eeb8608a42d --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/launch_config.test.ts @@ -0,0 +1,99 @@ +import * as assert from "assert"; +import { Cargo } from "../../src/toolchain"; +import type { Context } from "."; + +export async function getTests(ctx: Context) { + await ctx.suite("Launch configuration/Lens", (suite) => { + suite.addTest("A binary", async () => { + const args = Cargo.artifactSpec([ + "build", + "--package", + "pkg_name", + "--bin", + "pkg_name", + ]); + + assert.deepStrictEqual(args.cargoArgs, [ + "build", + "--package", + "pkg_name", + "--bin", + "pkg_name", + "--message-format=json", + ]); + assert.deepStrictEqual(args.filter, undefined); + }); + + suite.addTest("One of Multiple Binaries", async () => { + const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "build", + "--package", + "pkg_name", + "--bin", + "bin1", + "--message-format=json", + ]); + assert.deepStrictEqual(args.filter, undefined); + }); + + suite.addTest("A test", async () => { + const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "test", + "--package", + "pkg_name", + "--lib", + "--no-run", + "--message-format=json", + ]); + assert.notDeepStrictEqual(args.filter, undefined); + }); + }); + + await ctx.suite("Launch configuration/QuickPick", (suite) => { + suite.addTest("A binary", async () => { + const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "build", + "--package", + "pkg_name", + "--bin", + "pkg_name", + "--message-format=json", + ]); + assert.deepStrictEqual(args.filter, undefined); + }); + + suite.addTest("One of Multiple Binaries", async () => { + const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "build", + "--package", + "pkg_name", + "--bin", + "bin2", + "--message-format=json", + ]); + assert.deepStrictEqual(args.filter, undefined); + }); + + suite.addTest("A test", async () => { + const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]); + + assert.deepStrictEqual(args.cargoArgs, [ + "test", + "--package", + "pkg_name", + "--lib", + "--message-format=json", + "--no-run", + ]); + assert.notDeepStrictEqual(args.filter, undefined); + }); + }); +} diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts new file mode 100644 index 00000000000..b1407ce0193 --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/runnable_env.test.ts @@ -0,0 +1,121 @@ +import * as assert from "assert"; +import { prepareEnv } from "../../src/run"; +import type { RunnableEnvCfg } from "../../src/config"; +import type { Context } from "."; +import type * as ra from "../../src/lsp_ext"; + +function makeRunnable(label: string): ra.Runnable { + return { + label, + kind: "cargo", + args: { + cargoArgs: [], + executableArgs: [], + cargoExtraArgs: [], + }, + }; +} + +function fakePrepareEnv(runnableName: string, config: RunnableEnvCfg): Record<string, string> { + const runnable = makeRunnable(runnableName); + return prepareEnv(runnable, config); +} + +export async function getTests(ctx: Context) { + await ctx.suite("Runnable env", (suite) => { + suite.addTest("Global config works", async () => { + const binEnv = fakePrepareEnv("run project_name", { GLOBAL: "g" }); + assert.strictEqual(binEnv["GLOBAL"], "g"); + + const testEnv = fakePrepareEnv("test some::mod::test_name", { GLOBAL: "g" }); + assert.strictEqual(testEnv["GLOBAL"], "g"); + }); + + suite.addTest("null mask works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + ]; + const binEnv = fakePrepareEnv("run project_name", config); + assert.strictEqual(binEnv["DATA"], "data"); + + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "data"); + }); + + suite.addTest("order works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + { + env: { DATA: "newdata" }, + }, + ]; + const binEnv = fakePrepareEnv("run project_name", config); + assert.strictEqual(binEnv["DATA"], "newdata"); + + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "newdata"); + }); + + suite.addTest("mask works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + { + mask: "^run", + env: { DATA: "rundata" }, + }, + { + mask: "special_test$", + env: { DATA: "special_test" }, + }, + ]; + const binEnv = fakePrepareEnv("run project_name", config); + assert.strictEqual(binEnv["DATA"], "rundata"); + + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "data"); + + const specialTestEnv = fakePrepareEnv("test some::mod::special_test", config); + assert.strictEqual(specialTestEnv["DATA"], "special_test"); + }); + + suite.addTest("exact test name works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + { + mask: "some::mod::test_name", + env: { DATA: "test special" }, + }, + ]; + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "test special"); + + const specialTestEnv = fakePrepareEnv("test some::mod::another_test", config); + assert.strictEqual(specialTestEnv["DATA"], "data"); + }); + + suite.addTest("test mod name works", async () => { + const config = [ + { + env: { DATA: "data" }, + }, + { + mask: "some::mod", + env: { DATA: "mod special" }, + }, + ]; + const testEnv = fakePrepareEnv("test some::mod::test_name", config); + assert.strictEqual(testEnv["DATA"], "mod special"); + + const specialTestEnv = fakePrepareEnv("test some::mod::another_test", config); + assert.strictEqual(specialTestEnv["DATA"], "mod special"); + }); + }); +} diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/settings.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/settings.test.ts new file mode 100644 index 00000000000..bafb9569a1c --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/tests/unit/settings.test.ts @@ -0,0 +1,63 @@ +import * as assert from "assert"; +import type { Context } from "."; +import { substituteVariablesInEnv } from "../../src/config"; + +export async function getTests(ctx: Context) { + await ctx.suite("Server Env Settings", (suite) => { + suite.addTest("Replacing Env Variables", async () => { + const envJson = { + USING_MY_VAR: "${env:MY_VAR} test ${env:MY_VAR}", + MY_VAR: "test", + }; + const expectedEnv = { + USING_MY_VAR: "test test test", + MY_VAR: "test", + }; + const actualEnv = await substituteVariablesInEnv(envJson); + assert.deepStrictEqual(actualEnv, expectedEnv); + }); + + suite.addTest("Circular dependencies remain as is", async () => { + const envJson = { + A_USES_B: "${env:B_USES_A}", + B_USES_A: "${env:A_USES_B}", + C_USES_ITSELF: "${env:C_USES_ITSELF}", + D_USES_C: "${env:C_USES_ITSELF}", + E_IS_ISOLATED: "test", + F_USES_E: "${env:E_IS_ISOLATED}", + }; + const expectedEnv = { + A_USES_B: "${env:B_USES_A}", + B_USES_A: "${env:A_USES_B}", + C_USES_ITSELF: "${env:C_USES_ITSELF}", + D_USES_C: "${env:C_USES_ITSELF}", + E_IS_ISOLATED: "test", + F_USES_E: "test", + }; + const actualEnv = await substituteVariablesInEnv(envJson); + assert.deepStrictEqual(actualEnv, expectedEnv); + }); + + suite.addTest("Should support external variables", async () => { + process.env["TEST_VARIABLE"] = "test"; + const envJson = { + USING_EXTERNAL_VAR: "${env:TEST_VARIABLE} test ${env:TEST_VARIABLE}", + }; + const expectedEnv = { + USING_EXTERNAL_VAR: "test test test", + }; + + const actualEnv = await substituteVariablesInEnv(envJson); + assert.deepStrictEqual(actualEnv, expectedEnv); + delete process.env["TEST_VARIABLE"]; + }); + + suite.addTest("should support VSCode variables", async () => { + const envJson = { + USING_VSCODE_VAR: "${workspaceFolderBasename}", + }; + const actualEnv = await substituteVariablesInEnv(envJson); + assert.deepStrictEqual(actualEnv["USING_VSCODE_VAR"], "code"); + }); + }); +} |
