#!/usr/bin/env node const fs = require("fs"); const os = require("os"); const path = require("path"); const assert = require("assert"); const extension = require("../vscode-extension/extension"); const packageJson = require("../vscode-extension/package.json"); const extensionSource = fs.readFileSync( path.join(__dirname, "../vscode-extension/extension.js"), "utf8" ); const readme = fs.readFileSync(path.join(__dirname, "../README.md"), "utf8"); const defaultOperatorEndpoint = "https://disasmer.michelpaulissen.com:9443"; const repo = path.resolve(__dirname, ".."); assert.strictEqual(packageJson.main, "./extension.js"); assert(fs.existsSync(path.join(__dirname, "../vscode-extension", packageJson.main))); assert.deepStrictEqual(packageJson.dependencies || {}, {}); assert.match(readme, /code --extensionDevelopmentPath "\$\(pwd\)\/vscode-extension"/); const root = fs.mkdtempSync(path.join(os.tmpdir(), "disasmer-vscode-")); fs.mkdirSync(path.join(root, "envs/linux"), { recursive: true }); fs.writeFileSync(path.join(root, "envs/linux/Containerfile"), "FROM alpine\n"); fs.mkdirSync(path.join(root, ".disasmer"), { recursive: true }); fs.writeFileSync( extension.disasmerViewStatePath(root), JSON.stringify({ nodes: [{ id: "node-linux", status: "online", capabilities: "Command RootlessPodman" }], processes: [{ id: "vp-build", status: "running", entry: "build" }], logs: [{ task: "compile-linux", message: "stdout=12 stderr=0", bytes: 12 }], artifacts: [{ path: "/vfs/artifacts/app.tar.zst", status: "retained", size: 12 }], inspector: [{ label: "debug", value: "attached" }] }) ); const envs = extension.discoverEnvironmentNames(root); assert.deepStrictEqual(envs, ["linux"]); const diagnostics = extension.diagnoseEnvReferences( 'let _ = env!("linux"); let _ = env!("windows");', envs ); assert.strictEqual(diagnostics.length, 1); assert.strictEqual(diagnostics[0].name, "windows"); assert.match(diagnostics[0].message, /envs\/windows\/Containerfile/); const inspectCommand = extension.bundleInspectCommand(root, "/repo"); assert.strictEqual(inspectCommand.command, "cargo"); assert.deepStrictEqual(inspectCommand.args.slice(0, 8), [ "run", "-q", "-p", "disasmer-cli", "--bin", "disasmer", "--", "bundle" ]); assert(inspectCommand.args.includes("inspect")); assert(inspectCommand.args.includes("--project")); assert(inspectCommand.args.includes(root)); assert(inspectCommand.args.includes("--json")); const refreshed = extension.refreshBundleBeforeLaunch(root, "/repo", (command, args, options) => { assert.strictEqual(command, "cargo"); assert(args.includes("bundle")); assert.strictEqual(options.cwd, "/repo"); return { status: 0, stdout: JSON.stringify({ metadata: { identity: "sha256:abc" } }), stderr: "" }; }); assert.strictEqual(refreshed.metadata.identity, "sha256:abc"); const launch = extension.resolveDisasmerDebugConfiguration( { uri: { fsPath: root } }, {} ); assert.deepStrictEqual(launch, { name: "Disasmer: Launch Virtual Process", type: "disasmer", request: "launch", entry: "build", project: root, runtimeBackend: "local-services", operatorEndpoint: defaultOperatorEndpoint }); assert.strictEqual(extension.defaultOperatorEndpoint(), defaultOperatorEndpoint); const adapter = extension.debugAdapterExecutableSpec(root, repo); assert.strictEqual(adapter.command, "cargo"); assert.deepStrictEqual(adapter.args, [ "run", "-q", "-p", "disasmer-dap", "--bin", "disasmer-debug-dap" ]); assert.deepStrictEqual(adapter.options, { cwd: repo }); const releasedAdapterPath = path.join(root, process.platform === "win32" ? "disasmer-debug-dap.exe" : "disasmer-debug-dap"); fs.writeFileSync(releasedAdapterPath, ""); const releasedAdapter = extension.debugAdapterExecutableSpec(root, repo); assert.strictEqual(releasedAdapter.command, releasedAdapterPath); assert.deepStrictEqual(releasedAdapter.args, []); assert.deepStrictEqual(releasedAdapter.options, { cwd: root }); assert.throws( () => extension.refreshBundleBeforeLaunch(root, "/repo", () => ({ status: 1, stdout: "", stderr: "missing environment linux" })), /missing environment linux/ ); assert( packageJson.contributes.viewsContainers.activitybar.some( (container) => container.id === "disasmer" && container.title === "Disasmer" ), "package.json must contribute a Disasmer activity-bar container" ); assert( fs.existsSync(path.join(__dirname, "../vscode-extension/resources/disasmer.svg")), "Disasmer activity-bar icon must exist" ); const packageViewIds = packageJson.contributes.views.disasmer.map((view) => view.id).sort(); const descriptorViewIds = extension.disasmerViewDescriptors().map((view) => view.id).sort(); assert.deepStrictEqual(descriptorViewIds, [ "disasmer.artifacts", "disasmer.inspector", "disasmer.logs", "disasmer.nodes", "disasmer.processes" ]); assert.deepStrictEqual(packageViewIds, descriptorViewIds); for (const viewId of descriptorViewIds) { const items = extension.disasmerViewItems( extension.loadDisasmerViewState(root), viewId ); assert( items.length > 0 && !items[0].label.startsWith("No "), `${viewId} should render state-backed items` ); } assert.deepStrictEqual( extension.disasmerViewItems(extension.loadDisasmerViewState(root), "disasmer.nodes")[0], { label: "node-linux", description: "online Command RootlessPodman" } ); assert( packageJson.contributes.debuggers.some((debuggerContribution) => debuggerContribution.type === "disasmer"), "package.json must contribute the disasmer debugger type" ); for (const viewId of descriptorViewIds) { assert( packageJson.activationEvents.includes(`onView:${viewId}`), `${viewId} should activate the extension when opened` ); } const launchProperties = packageJson.contributes.debuggers[0].configurationAttributes.launch.properties; assert.strictEqual(launchProperties.runtimeBackend.default, "local-services"); assert(launchProperties.runtimeBackend.enum.includes("live-services")); assert.strictEqual(launchProperties.operatorEndpoint.default, defaultOperatorEndpoint); assert.match(extensionSource, /registerDebugAdapterDescriptorFactory\("disasmer"/); assert.match(extensionSource, /disasmer-debug-dap/); assert.match(extensionSource, /\.disasmer\/views\.json/); fs.rmSync(root, { recursive: true, force: true }); console.log("VS Code extension smoke passed");