aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2022-08-19 17:56:39 +0200
committerGitHub <noreply@github.com>2022-08-19 17:56:39 +0200
commit198b5d69e6fc34dfe0de038fab1859617608b2d6 (patch)
tree445b5e409b1551d14507ca27ec88ed56e6f2a656
parentchore: update generated code (#311) (diff)
downloadmason-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.lua16
-rw-r--r--tests/mason-core/installer/context_spec.lua175
-rw-r--r--tests/mason-core/installer/linker_spec.lua105
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)