diff options
| author | William Boman <william@redwill.se> | 2022-08-19 17:56:39 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-19 17:56:39 +0200 |
| commit | 198b5d69e6fc34dfe0de038fab1859617608b2d6 (patch) | |
| tree | 445b5e409b1551d14507ca27ec88ed56e6f2a656 | |
| parent | chore: update generated code (#311) (diff) | |
| download | mason-198b5d69e6fc34dfe0de038fab1859617608b2d6.tar mason-198b5d69e6fc34dfe0de038fab1859617608b2d6.tar.gz mason-198b5d69e6fc34dfe0de038fab1859617608b2d6.tar.bz2 mason-198b5d69e6fc34dfe0de038fab1859617608b2d6.tar.lz mason-198b5d69e6fc34dfe0de038fab1859617608b2d6.tar.xz mason-198b5d69e6fc34dfe0de038fab1859617608b2d6.tar.zst mason-198b5d69e6fc34dfe0de038fab1859617608b2d6.zip | |
test(installer): add spec files for context and linker (#314)
* feat(installer): don't write exec wrappers for targets that don't exist
Just a precaution to avoid writing broken executables.
* test(installer): add spec files for context and linker
| -rw-r--r-- | lua/mason-core/installer/context.lua | 16 | ||||
| -rw-r--r-- | tests/mason-core/installer/context_spec.lua | 175 | ||||
| -rw-r--r-- | tests/mason-core/installer/linker_spec.lua | 105 |
3 files changed, 295 insertions, 1 deletions
diff --git a/lua/mason-core/installer/context.lua b/lua/mason-core/installer/context.lua index b8706698..a3ec6021 100644 --- a/lua/mason-core/installer/context.lua +++ b/lua/mason-core/installer/context.lua @@ -203,6 +203,9 @@ end ---@param new_executable_rel_path string Relative path to the executable file to create. ---@param script_rel_path string Relative path to the Node.js script. function InstallContext:write_node_exec_wrapper(new_executable_rel_path, script_rel_path) + if not self.fs:file_exists(script_rel_path) then + error(("Cannot write Node exec wrapper for path %q as it doesn't exist."):format(script_rel_path), 0) + end return self:write_shell_exec_wrapper( new_executable_rel_path, ("node %q"):format(path.concat { @@ -215,11 +218,19 @@ end ---@param new_executable_rel_path string Relative path to the executable file to create. ---@param module string The python module to call. function InstallContext:write_pyvenv_exec_wrapper(new_executable_rel_path, module) + local pip3 = require "mason-core.managers.pip3" + local module_exists, module_err = pcall(function() + self.spawn.python { "-c", ("import %s"):format(module), with_paths = { pip3.venv_path(self.cwd:get()) } } + end) + if not module_exists then + log.fmt_error("Failed to find module %q for package %q. %s", module, self.package, module_err) + error(("Cannot write Python exec wrapper for module %q as it doesn't exist."):format(module), 0) + end return self:write_shell_exec_wrapper( new_executable_rel_path, ("%q -m %s"):format( path.concat { - require("mason-core.managers.pip3").venv_path(self.package:get_install_path()), + pip3.venv_path(self.package:get_install_path()), "python", }, module @@ -230,6 +241,9 @@ end ---@param new_executable_rel_path string Relative path to the executable file to create. ---@param target_executable_rel_path string function InstallContext:write_exec_wrapper(new_executable_rel_path, target_executable_rel_path) + if not self.fs:file_exists(target_executable_rel_path) then + error(("Cannot write exec wrapper for path %q as it doesn't exist."):format(target_executable_rel_path), 0) + end return self:write_shell_exec_wrapper( new_executable_rel_path, ("%q"):format(path.concat { diff --git a/tests/mason-core/installer/context_spec.lua b/tests/mason-core/installer/context_spec.lua new file mode 100644 index 00000000..a13bc295 --- /dev/null +++ b/tests/mason-core/installer/context_spec.lua @@ -0,0 +1,175 @@ +local stub = require "luassert.stub" +local match = require "luassert.match" +local std = require "mason-core.managers.std" +local path = require "mason-core.path" +local pip3 = require "mason-core.managers.pip3" +local registry = require "mason-registry" + +describe("installer", function() + ---@module "mason-core.platform" + local platform + + before_each(function() + package.loaded["mason-core.installer.platform"] = nil + package.loaded["mason-core.installer.context"] = nil + platform = require "mason-core.platform" + end) + + it("should write shell exec wrapper on Unix", function() + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + stub(ctx.fs, "write_file") + stub(std, "chmod") + + ctx:write_shell_exec_wrapper("my-executable", "bash -c 'echo $GREETING'", { + GREETING = "Hello World!", + }) + + assert.spy(ctx.fs.write_file).was_called(1) + assert.spy(ctx.fs.write_file).was_called_with( + match.is_ref(ctx.fs), + "my-executable", + [[#!/bin/bash +export GREETING="Hello World!" +exec bash -c 'echo $GREETING' "$@"]] + ) + end) + + it("should write shell exec wrapper on Windows", function() + platform.is_mac = false + platform.is_unix = false + platform.is_linux = false + platform.is_win = true + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + stub(ctx.fs, "write_file") + stub(std, "chmod") + + ctx:write_shell_exec_wrapper("my-executable", "cmd.exe /C echo %GREETING%", { + GREETING = "Hello World!", + }) + + assert.spy(ctx.fs.write_file).was_called(1) + assert.spy(ctx.fs.write_file).was_called_with( + match.is_ref(ctx.fs), + "my-executable.cmd", + [[@ECHO off +SET GREETING=Hello World! +cmd.exe /C echo %GREETING% %*]] + ) + end) + + it("should write Node exec wrapper", function() + local js_rel_path = path.concat { "some", "obscure", "path", "server.js" } + local dummy = registry.get_package "dummy" + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + stub(ctx, "write_shell_exec_wrapper") + stub(ctx.fs, "file_exists") + ctx.fs.file_exists.on_call_with(match.is_ref(ctx.fs), js_rel_path).returns(true) + + ctx:write_node_exec_wrapper("my-wrapper-script", js_rel_path) + + assert.spy(ctx.write_shell_exec_wrapper).was_called(1) + assert.spy(ctx.write_shell_exec_wrapper).was_called_with( + match.is_ref(ctx), + "my-wrapper-script", + ("node %q"):format(path.concat { dummy:get_install_path(), js_rel_path }) + ) + end) + + it("should not write Node exec wrapper if the target script doesn't exist", function() + local js_rel_path = path.concat { "some", "obscure", "path", "server.js" } + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + stub(ctx, "write_shell_exec_wrapper") + stub(ctx.fs, "file_exists") + ctx.fs.file_exists.on_call_with(match.is_ref(ctx.fs), js_rel_path).returns(false) + + local err = assert.has_error(function() + ctx:write_node_exec_wrapper("my-wrapper-script", js_rel_path) + end) + + assert.equals( + [[Cannot write Node exec wrapper for path "some/obscure/path/server.js" as it doesn't exist.]], + err + ) + assert.spy(ctx.write_shell_exec_wrapper).was_called(0) + end) + + it("should write Python exec wrapper", function() + local dummy = registry.get_package "dummy" + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + stub(ctx.cwd, "get") + ctx.cwd.get.returns "/tmp/placeholder" + stub(ctx, "write_shell_exec_wrapper") + stub(ctx.spawn, "python") + + ctx:write_pyvenv_exec_wrapper("my-wrapper-script", "my-module") + + assert.spy(ctx.write_shell_exec_wrapper).was_called(1) + assert.spy(ctx.write_shell_exec_wrapper).was_called_with( + match.is_ref(ctx), + "my-wrapper-script", + ("%q -m my-module"):format(path.concat { pip3.venv_path(dummy:get_install_path()), "python" }) + ) + end) + + it("should not write Python exec wrapper if module cannot be found", function() + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + stub(ctx.cwd, "get") + ctx.cwd.get.returns "/tmp/placeholder" + stub(ctx, "write_shell_exec_wrapper") + stub(ctx.spawn, "python") + + ctx.spawn.python.invokes(function() + error "" + end) + + local err = assert.has_error(function() + ctx:write_pyvenv_exec_wrapper("my-wrapper-script", "my-module") + end) + + assert.equals([[Cannot write Python exec wrapper for module "my-module" as it doesn't exist.]], err) + assert.spy(ctx.write_shell_exec_wrapper).was_called(0) + end) + + it("should write exec wrapper", function() + local dummy = registry.get_package "dummy" + local exec_rel_path = path.concat { "obscure", "path", "to", "server" } + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + stub(ctx, "write_shell_exec_wrapper") + stub(ctx.fs, "file_exists") + ctx.fs.file_exists.on_call_with(match.is_ref(ctx.fs), exec_rel_path).returns(true) + + ctx:write_exec_wrapper("my-wrapper-script", exec_rel_path) + + assert.spy(ctx.write_shell_exec_wrapper).was_called(1) + assert + .spy(ctx.write_shell_exec_wrapper) + .was_called_with( + match.is_ref(ctx), + "my-wrapper-script", + ("%q"):format(path.concat { dummy:get_install_path(), exec_rel_path }) + ) + end) + + it("should not write exec wrapper if target executable doesn't exist", function() + local exec_rel_path = path.concat { "obscure", "path", "to", "server" } + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + stub(ctx, "write_shell_exec_wrapper") + stub(ctx.fs, "file_exists") + ctx.fs.file_exists.on_call_with(match.is_ref(ctx.fs), exec_rel_path).returns(false) + + local err = assert.has_error(function() + ctx:write_exec_wrapper("my-wrapper-script", exec_rel_path) + end) + + assert.equals([[Cannot write exec wrapper for path "obscure/path/to/server" as it doesn't exist.]], err) + assert.spy(ctx.write_shell_exec_wrapper).was_called(0) + end) +end) diff --git a/tests/mason-core/installer/linker_spec.lua b/tests/mason-core/installer/linker_spec.lua new file mode 100644 index 00000000..71b7f7fe --- /dev/null +++ b/tests/mason-core/installer/linker_spec.lua @@ -0,0 +1,105 @@ +local stub = require "luassert.stub" +local fs = require "mason-core.fs" +local path = require "mason-core.path" +local registry = require "mason-registry" + +local WIN_CMD_SCRIPT = [[@ECHO off +GOTO start +:find_dp0 +SET dp0=%%~dp0 +EXIT /b +:start +SETLOCAL +CALL :find_dp0 + +endLocal & goto #_undefined_# 2>NUL || title %%COMSPEC%% & "%%dp0%%\%s" %%*]] + +describe("installer", function() + ---@module "mason-core.installer.linker" + local linker + ---@module "mason-core.platform" + local platform + + before_each(function() + package.loaded["mason-core.installer.platform"] = nil + package.loaded["mason-core.installer.linker"] = nil + platform = require "mason-core.platform" + linker = require "mason-core.installer.linker" + end) + + it( + "should symlink executable on Unix", + async_test(function() + local dummy = registry.get_package "dummy" + stub(fs.async, "file_exists") + stub(fs.async, "symlink") + stub(fs.async, "write_file") + + fs.async.file_exists.on_call_with(path.bin_prefix "my-executable").returns(false) + fs.async.file_exists.on_call_with(path.bin_prefix "another-executable").returns(false) + fs.async.file_exists + .on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "my-executable" }) + .returns(true) + fs.async.file_exists + .on_call_with(path.concat { dummy:get_install_path(), "another-executable" }) + .returns(true) + + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + ctx:link_bin("my-executable", path.concat { "nested", "path", "my-executable" }) + ctx:link_bin("another-executable", "another-executable") + linker.link(ctx) + + assert.spy(fs.async.write_file).was_called(0) + assert.spy(fs.async.symlink).was_called(2) + assert + .spy(fs.async.symlink) + .was_called_with("../packages/dummy/another-executable", path.bin_prefix "another-executable") + assert + .spy(fs.async.symlink) + .was_called_with("../packages/dummy/nested/path/my-executable", path.bin_prefix "my-executable") + end) + ) + + it( + "should write executable wrapper on Windows", + async_test(function() + platform.is_mac = false + platform.is_linux = false + platform.is_unix = false + platform.is_win = true + + local dummy = registry.get_package "dummy" + stub(fs.async, "file_exists") + stub(fs.async, "symlink") + stub(fs.async, "write_file") + + fs.async.file_exists.on_call_with(path.bin_prefix "my-executable").returns(false) + fs.async.file_exists.on_call_with(path.bin_prefix "another-executable").returns(false) + fs.async.file_exists + .on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "my-executable" }) + .returns(true) + fs.async.file_exists + .on_call_with(path.concat { dummy:get_install_path(), "another-executable" }) + .returns(true) + + local handle = InstallHandleGenerator "dummy" + local ctx = InstallContextGenerator(handle) + ctx:link_bin("my-executable", path.concat { "nested", "path", "my-executable" }) + ctx:link_bin("another-executable", "another-executable") + linker.link(ctx) + + assert.spy(fs.async.symlink).was_called(0) + assert.spy(fs.async.write_file).was_called(2) + assert + .spy(fs.async.write_file) + .was_called_with(path.bin_prefix "another-executable.cmd", WIN_CMD_SCRIPT:format "../packages/dummy/another-executable") + assert + .spy(fs.async.write_file) + .was_called_with( + path.bin_prefix "my-executable.cmd", + WIN_CMD_SCRIPT:format "../packages/dummy/nested/path/my-executable" + ) + end) + ) +end) |
