aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2023-10-11 16:31:50 +0200
committerWilliam Boman <william@redwill.se>2025-02-19 09:22:40 +0100
commit047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc (patch)
treec50c22cd05d3605fc5a1e8eb902ffeb11e339697
parentrefactor(receipt): change receipt structure and remove old builder APIs (#1521) (diff)
downloadmason-047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc.tar
mason-047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc.tar.gz
mason-047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc.tar.bz2
mason-047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc.tar.lz
mason-047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc.tar.xz
mason-047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc.tar.zst
mason-047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc.zip
refactor!: refactor installer internals and add new Package class methods (#1523)
This contains the following changes: 1) `Package:install()` now accepts a second, optional, callback argument which is called when installation finishes (successfully or not). 2) Adds a `Package:is_installing()` method. This contains the following breaking changes: 1) `Package:install()` will now error when called while an installation is already ongoing. Use the new `Package:is_installing()` method to check whether an installation is already running. This also refactors large portions of the tests by removing test globals, removing async_test, and adding the `mason-test` Lua module instead. Test helpers via globals are problematic to work with due to not being detected through tools like the Lua language server without additional configuration. This has been replaced with a Lua module `mason-test`. `async_test` has also been removed in favour of explicitly making use of the `mason-core.async` API. These changes stands for a significant portion of the diff.
-rw-r--r--doc/reference.md41
-rw-r--r--lua/mason-core/installer/compiler/compilers/cargo.lua (renamed from lua/mason-core/installer/registry/providers/cargo.lua)2
-rw-r--r--lua/mason-core/installer/compiler/compilers/composer.lua (renamed from lua/mason-core/installer/registry/providers/composer.lua)2
-rw-r--r--lua/mason-core/installer/compiler/compilers/gem.lua (renamed from lua/mason-core/installer/registry/providers/gem.lua)2
-rw-r--r--lua/mason-core/installer/compiler/compilers/generic/build.lua (renamed from lua/mason-core/installer/registry/providers/generic/build.lua)4
-rw-r--r--lua/mason-core/installer/compiler/compilers/generic/download.lua (renamed from lua/mason-core/installer/registry/providers/generic/download.lua)4
-rw-r--r--lua/mason-core/installer/compiler/compilers/generic/init.lua (renamed from lua/mason-core/installer/registry/providers/generic/init.lua)8
-rw-r--r--lua/mason-core/installer/compiler/compilers/github/build.lua (renamed from lua/mason-core/installer/registry/providers/github/build.lua)4
-rw-r--r--lua/mason-core/installer/compiler/compilers/github/init.lua (renamed from lua/mason-core/installer/registry/providers/github/init.lua)10
-rw-r--r--lua/mason-core/installer/compiler/compilers/github/release.lua (renamed from lua/mason-core/installer/registry/providers/github/release.lua)11
-rw-r--r--lua/mason-core/installer/compiler/compilers/golang.lua (renamed from lua/mason-core/installer/registry/providers/golang.lua)2
-rw-r--r--lua/mason-core/installer/compiler/compilers/luarocks.lua (renamed from lua/mason-core/installer/registry/providers/luarocks.lua)0
-rw-r--r--lua/mason-core/installer/compiler/compilers/mason.lua (renamed from lua/mason-core/installer/registry/providers/mason.lua)0
-rw-r--r--lua/mason-core/installer/compiler/compilers/npm.lua (renamed from lua/mason-core/installer/registry/providers/npm.lua)0
-rw-r--r--lua/mason-core/installer/compiler/compilers/nuget.lua (renamed from lua/mason-core/installer/registry/providers/nuget.lua)0
-rw-r--r--lua/mason-core/installer/compiler/compilers/opam.lua (renamed from lua/mason-core/installer/registry/providers/opam.lua)0
-rw-r--r--lua/mason-core/installer/compiler/compilers/openvsx.lua (renamed from lua/mason-core/installer/registry/providers/openvsx.lua)7
-rw-r--r--lua/mason-core/installer/compiler/compilers/pypi.lua (renamed from lua/mason-core/installer/registry/providers/pypi.lua)2
-rw-r--r--lua/mason-core/installer/compiler/expr.lua (renamed from lua/mason-core/installer/registry/expr.lua)0
-rw-r--r--lua/mason-core/installer/compiler/init.lua (renamed from lua/mason-core/installer/registry/init.lua)66
-rw-r--r--lua/mason-core/installer/compiler/link.lua (renamed from lua/mason-core/installer/registry/link.lua)2
-rw-r--r--lua/mason-core/installer/compiler/schemas.lua (renamed from lua/mason-core/installer/registry/schemas.lua)2
-rw-r--r--lua/mason-core/installer/compiler/util.lua (renamed from lua/mason-core/installer/registry/util.lua)0
-rw-r--r--lua/mason-core/installer/context/cwd.lua48
-rw-r--r--lua/mason-core/installer/context/fs.lua108
-rw-r--r--lua/mason-core/installer/context/init.lua (renamed from lua/mason-core/installer/context.lua)250
-rw-r--r--lua/mason-core/installer/context/spawn.lua46
-rw-r--r--lua/mason-core/installer/handle.lua4
-rw-r--r--lua/mason-core/installer/init.lua255
-rw-r--r--lua/mason-core/installer/location.lua63
-rw-r--r--lua/mason-core/installer/runner.lua218
-rw-r--r--lua/mason-core/package/init.lua114
-rw-r--r--lua/mason-core/receipt.lua14
-rw-r--r--lua/mason-registry/sources/util.lua4
-rw-r--r--lua/mason-test/helpers.lua33
-rw-r--r--tests/helpers/lua/luassertx.lua21
-rw-r--r--tests/helpers/lua/test_helpers.lua70
-rw-r--r--tests/mason-core/EventEmitter_spec.lua29
-rw-r--r--tests/mason-core/async/async_spec.lua210
-rw-r--r--tests/mason-core/fetch_spec.lua207
-rw-r--r--tests/mason-core/fs_spec.lua21
-rw-r--r--tests/mason-core/installer/context_spec.lua46
-rw-r--r--tests/mason-core/installer/handle_spec.lua65
-rw-r--r--tests/mason-core/installer/installer_spec.lua217
-rw-r--r--tests/mason-core/installer/linker_spec.lua212
-rw-r--r--tests/mason-core/installer/managers/cargo_spec.lua26
-rw-r--r--tests/mason-core/installer/managers/common_spec.lua17
-rw-r--r--tests/mason-core/installer/managers/composer_spec.lua10
-rw-r--r--tests/mason-core/installer/managers/gem_spec.lua20
-rw-r--r--tests/mason-core/installer/managers/golang_spec.lua14
-rw-r--r--tests/mason-core/installer/managers/luarocks_spec.lua28
-rw-r--r--tests/mason-core/installer/managers/npm_spec.lua32
-rw-r--r--tests/mason-core/installer/managers/nuget_spec.lua10
-rw-r--r--tests/mason-core/installer/managers/opam_spec.lua10
-rw-r--r--tests/mason-core/installer/managers/powershell_spec.lua34
-rw-r--r--tests/mason-core/installer/managers/pypi_spec.lua50
-rw-r--r--tests/mason-core/installer/managers/std_spec.lua56
-rw-r--r--tests/mason-core/installer/registry/compilers/cargo_spec.lua (renamed from tests/mason-core/installer/registry/providers/cargo_spec.lua)28
-rw-r--r--tests/mason-core/installer/registry/compilers/composer_spec.lua (renamed from tests/mason-core/installer/registry/providers/composer_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/gem_spec.lua (renamed from tests/mason-core/installer/registry/providers/gem_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/generic/build_spec.lua (renamed from tests/mason-core/installer/registry/providers/generic/build_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/generic/download_spec.lua (renamed from tests/mason-core/installer/registry/providers/generic/download_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/github/build_spec.lua (renamed from tests/mason-core/installer/registry/providers/github/build_spec.lua)2
-rw-r--r--tests/mason-core/installer/registry/compilers/github/release_spec.lua (renamed from tests/mason-core/installer/registry/providers/github/release_spec.lua)24
-rw-r--r--tests/mason-core/installer/registry/compilers/golang_spec.lua (renamed from tests/mason-core/installer/registry/providers/golang_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/luarocks_spec.lua (renamed from tests/mason-core/installer/registry/providers/luarocks_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/npm_spec.lua (renamed from tests/mason-core/installer/registry/providers/npm_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/nuget_spec.lua (renamed from tests/mason-core/installer/registry/providers/nuget_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/opam_spec.lua (renamed from tests/mason-core/installer/registry/providers/opam_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/compilers/openvsx_spec.lua (renamed from tests/mason-core/installer/registry/providers/openvsx_spec.lua)11
-rw-r--r--tests/mason-core/installer/registry/compilers/pypi_spec.lua (renamed from tests/mason-core/installer/registry/providers/pypi_spec.lua)18
-rw-r--r--tests/mason-core/installer/registry/expr_spec.lua2
-rw-r--r--tests/mason-core/installer/registry/installer_spec.lua103
-rw-r--r--tests/mason-core/installer/registry/link_spec.lua25
-rw-r--r--tests/mason-core/installer/registry/util_spec.lua20
-rw-r--r--tests/mason-core/installer/runner_spec.lua300
-rw-r--r--tests/mason-core/package/package_spec.lua123
-rw-r--r--tests/mason-core/platform_spec.lua10
-rw-r--r--tests/mason-core/process_spec.lua35
-rw-r--r--tests/mason-core/result_spec.lua40
-rw-r--r--tests/mason-core/spawn_spec.lua337
-rw-r--r--tests/mason-core/terminator_spec.lua167
-rw-r--r--tests/mason-core/ui_spec.lua308
-rw-r--r--tests/mason-registry/api_spec.lua10
-rw-r--r--tests/mason/api/command_spec.lua169
-rw-r--r--tests/minimal_init.vim17
-rw-r--r--vim.yml17
87 files changed, 2482 insertions, 2177 deletions
diff --git a/doc/reference.md b/doc/reference.md
index 2f23e793..ef00425c 100644
--- a/doc/reference.md
+++ b/doc/reference.md
@@ -34,7 +34,8 @@ RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as de
- [`Package.License`](#packagelicense)
- [`Package.new({spec})`](#packagenewspec)
- [`Package.spec`](#packagespec)
- - [`Package:install({opts?})`](#packageinstallopts)
+ - [`Package:is_installing()`](#packageis_installing)
+ - [`Package:install({opts?}, {callback?})`](#packageinstallopts-callback)
- [`Package:uninstall()`](#packageuninstall)
- [`Package:is_installed()`](#packageis_installed)
- [`Package:get_install_path()`](#packageget_install_path)
@@ -82,12 +83,12 @@ RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as de
The `mason-registry` Lua module extends the [EventEmitter](#eventemitter) interface and emits the following events:
-| Event | Handler signature |
-| --------------------------- | ------------------------------------------ |
-| `package:handle` | `fun(pkg: Package, handle: InstallHandle)` |
-| `package:install:success` | `fun(pkg: Package, handle: InstallHandle)` |
-| `package:install:failed` | `fun(pkg: Package, handle: InstallHandle)` |
-| `package:uninstall:success` | `fun(pkg: Package)` |
+| Event | Handler signature |
+| --------------------------- | ------------------------------------------------------ |
+| `package:handle` | `fun(pkg: Package, handle: InstallHandle)` |
+| `package:install:success` | `fun(pkg: Package, handle: InstallHandle)` |
+| `package:install:failed` | `fun(pkg: Package, handle: InstallHandle, error: any)` |
+| `package:uninstall:success` | `fun(pkg: Package)` |
The following is an example for how to register handlers for events:
@@ -135,11 +136,11 @@ The `Package` class encapsulates the installation instructions and metadata abou
This class extends the [EventEmitter](#eventemitter) interface and emits the following events:
-| Event | Handler signature |
-| ------------------- | ---------------------------- |
-| `install:success` | `fun(handle: InstallHandle)` |
-| `install:failed` | `fun(handle: InstallHandle)` |
-| `uninstall:success` | `fun()` |
+| Event | Handler signature |
+| ------------------- | ------------------------------------------------------ |
+| `install:success` | `fun(handle: InstallHandle)` |
+| `install:failed` | `fun(pkg: Package, handle: InstallHandle, error: any)` |
+| `uninstall:success` | `fun()` |
### `Package.Parse({package_identifier})`
@@ -193,22 +194,28 @@ Similar as [`Package.Lang`](#packagelang) but for SPDX license identifiers.
**Type**: [`RegistryPackageSpec`](#registrypackagespec)
-### `Package:install({opts?})`
+### `Package:is_installing()`
+
+**Returns:** `boolean`
+
+### `Package:install({opts?}, {callback?})`
**Parameters:**
- `opts?`: [`PackageInstallOpts`](#packageinstallopts-1) (optional)
+- `callback?`: `fun(success: boolean, result: any)` (optional) - Callback to be called when package installation completes. _Note: this is called before events (["package:install:success"](#registry-events), ["install:success"](#package)) are emitted._
**Returns:** [`InstallHandle`](#installhandle)
-Installs the package instance this method is being called on. Accepts an
-optional `{opts}` argument, which can be used to specify a desired version to
-install.
+Installs the package instance this method is being called on. Accepts an optional `{opts}` argument which can be used to
+for example specify which version to install (see [`PackageInstallOpts`](#packageinstallopts-1)), and an optional
+`{callback}` argument which is called when the installation finishes.
The returned [`InstallHandle`](#installhandle) can be used to observe progress and control the installation process
(e.g., cancelling).
-_Note that if the package already have an active handle registered, that handler is returned instead of a new one._
+_Note that if the package is already being installed this method will error. See
+[`Package:is_installing()`](#packageis_installing)._
### `Package:uninstall()`
diff --git a/lua/mason-core/installer/registry/providers/cargo.lua b/lua/mason-core/installer/compiler/compilers/cargo.lua
index f4904b73..e0f281c5 100644
--- a/lua/mason-core/installer/registry/providers/cargo.lua
+++ b/lua/mason-core/installer/compiler/compilers/cargo.lua
@@ -1,7 +1,7 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local providers = require "mason-core.providers"
-local util = require "mason-core.installer.registry.util"
+local util = require "mason-core.installer.compiler.util"
local M = {}
diff --git a/lua/mason-core/installer/registry/providers/composer.lua b/lua/mason-core/installer/compiler/compilers/composer.lua
index d85dd2ba..259512a2 100644
--- a/lua/mason-core/installer/registry/providers/composer.lua
+++ b/lua/mason-core/installer/compiler/compilers/composer.lua
@@ -1,6 +1,6 @@
local Result = require "mason-core.result"
local providers = require "mason-core.providers"
-local util = require "mason-core.installer.registry.util"
+local util = require "mason-core.installer.compiler.util"
local M = {}
diff --git a/lua/mason-core/installer/registry/providers/gem.lua b/lua/mason-core/installer/compiler/compilers/gem.lua
index 9653f116..7a343eec 100644
--- a/lua/mason-core/installer/registry/providers/gem.lua
+++ b/lua/mason-core/installer/compiler/compilers/gem.lua
@@ -1,7 +1,7 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local providers = require "mason-core.providers"
-local util = require "mason-core.installer.registry.util"
+local util = require "mason-core.installer.compiler.util"
local M = {}
diff --git a/lua/mason-core/installer/registry/providers/generic/build.lua b/lua/mason-core/installer/compiler/compilers/generic/build.lua
index a0d517d8..df97a118 100644
--- a/lua/mason-core/installer/registry/providers/generic/build.lua
+++ b/lua/mason-core/installer/compiler/compilers/generic/build.lua
@@ -1,8 +1,8 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local common = require "mason-core.installer.managers.common"
-local expr = require "mason-core.installer.registry.expr"
-local util = require "mason-core.installer.registry.util"
+local expr = require "mason-core.installer.compiler.expr"
+local util = require "mason-core.installer.compiler.util"
local M = {}
diff --git a/lua/mason-core/installer/registry/providers/generic/download.lua b/lua/mason-core/installer/compiler/compilers/generic/download.lua
index 4622a844..37e54d96 100644
--- a/lua/mason-core/installer/registry/providers/generic/download.lua
+++ b/lua/mason-core/installer/compiler/compilers/generic/download.lua
@@ -1,8 +1,8 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local common = require "mason-core.installer.managers.common"
-local expr = require "mason-core.installer.registry.expr"
-local util = require "mason-core.installer.registry.util"
+local expr = require "mason-core.installer.compiler.expr"
+local util = require "mason-core.installer.compiler.util"
local M = {}
diff --git a/lua/mason-core/installer/registry/providers/generic/init.lua b/lua/mason-core/installer/compiler/compilers/generic/init.lua
index 1bf79e94..8206883f 100644
--- a/lua/mason-core/installer/registry/providers/generic/init.lua
+++ b/lua/mason-core/installer/compiler/compilers/generic/init.lua
@@ -9,10 +9,10 @@ local M = {}
function M.parse(source, purl, opts)
if source.download then
source = source --[[@as GenericDownloadSource]]
- return require("mason-core.installer.registry.providers.generic.download").parse(source, purl, opts)
+ return require("mason-core.installer.compiler.compilers.generic.download").parse(source, purl, opts)
elseif source.build then
source = source --[[@as GenericBuildSource]]
- return require("mason-core.installer.registry.providers.generic.build").parse(source, purl, opts)
+ return require("mason-core.installer.compiler.compilers.generic.build").parse(source, purl, opts)
else
return Result.failure "Unknown source type."
end
@@ -24,10 +24,10 @@ end
function M.install(ctx, source)
if source.download then
source = source --[[@as ParsedGenericDownloadSource]]
- return require("mason-core.installer.registry.providers.generic.download").install(ctx, source)
+ return require("mason-core.installer.compiler.compilers.generic.download").install(ctx, source)
elseif source.build then
source = source --[[@as ParsedGenericBuildSource]]
- return require("mason-core.installer.registry.providers.generic.build").install(ctx, source)
+ return require("mason-core.installer.compiler.compilers.generic.build").install(ctx, source)
else
return Result.failure "Unknown source type."
end
diff --git a/lua/mason-core/installer/registry/providers/github/build.lua b/lua/mason-core/installer/compiler/compilers/github/build.lua
index 1c17bb1a..22f3e3cc 100644
--- a/lua/mason-core/installer/registry/providers/github/build.lua
+++ b/lua/mason-core/installer/compiler/compilers/github/build.lua
@@ -1,8 +1,8 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local common = require "mason-core.installer.managers.common"
-local expr = require "mason-core.installer.registry.expr"
-local util = require "mason-core.installer.registry.util"
+local expr = require "mason-core.installer.compiler.expr"
+local util = require "mason-core.installer.compiler.util"
local M = {}
diff --git a/lua/mason-core/installer/registry/providers/github/init.lua b/lua/mason-core/installer/compiler/compilers/github/init.lua
index 0d68f3a5..d8646975 100644
--- a/lua/mason-core/installer/registry/providers/github/init.lua
+++ b/lua/mason-core/installer/compiler/compilers/github/init.lua
@@ -8,10 +8,10 @@ local M = {}
function M.parse(source, purl, opts)
if source.asset then
source = source --[[@as GitHubReleaseSource]]
- return require("mason-core.installer.registry.providers.github.release").parse(source, purl, opts)
+ return require("mason-core.installer.compiler.compilers.github.release").parse(source, purl, opts)
elseif source.build then
source = source --[[@as GitHubBuildSource]]
- return require("mason-core.installer.registry.providers.github.build").parse(source, purl, opts)
+ return require("mason-core.installer.compiler.compilers.github.build").parse(source, purl, opts)
else
return Result.failure "Unknown source type."
end
@@ -23,10 +23,10 @@ end
function M.install(ctx, source, purl)
if source.asset then
source = source--[[@as ParsedGitHubReleaseSource]]
- return require("mason-core.installer.registry.providers.github.release").install(ctx, source)
+ return require("mason-core.installer.compiler.compilers.github.release").install(ctx, source)
elseif source.build then
source = source--[[@as ParsedGitHubBuildSource]]
- return require("mason-core.installer.registry.providers.github.build").install(ctx, source)
+ return require("mason-core.installer.compiler.compilers.github.build").install(ctx, source)
else
return Result.failure "Unknown source type."
end
@@ -37,7 +37,7 @@ end
---@param source GitHubReleaseSource | GitHubBuildSource
function M.get_versions(purl, source)
if source.asset then
- return require("mason-core.installer.registry.providers.github.release").get_versions(purl)
+ return require("mason-core.installer.compiler.compilers.github.release").get_versions(purl)
elseif source.build then
-- We can't yet reliably determine the true source (release, tag, commit, etc.) for "build" sources.
return Result.failure "Unimplemented."
diff --git a/lua/mason-core/installer/registry/providers/github/release.lua b/lua/mason-core/installer/compiler/compilers/github/release.lua
index 8c8a8a8f..39f7d862 100644
--- a/lua/mason-core/installer/registry/providers/github/release.lua
+++ b/lua/mason-core/installer/compiler/compilers/github/release.lua
@@ -1,13 +1,16 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local common = require "mason-core.installer.managers.common"
-local expr = require "mason-core.installer.registry.expr"
+local expr = require "mason-core.installer.compiler.expr"
local providers = require "mason-core.providers"
local settings = require "mason.settings"
-local util = require "mason-core.installer.registry.util"
+local util = require "mason-core.installer.compiler.util"
+
+---@class GitHubReleaseSourceAsset : FileDownloadSpec
+---@field target? Platform | Platform[]
---@class GitHubReleaseSource : RegistryPackageSource
----@field asset FileDownloadSpec | FileDownloadSpec[]
+---@field asset GitHubReleaseSourceAsset | GitHubReleaseSourceAsset[]
local M = {}
@@ -17,7 +20,7 @@ local M = {}
function M.parse(source, purl, opts)
return Result.try(function(try)
local expr_ctx = { version = purl.version }
- ---@type FileDownloadSpec
+ ---@type GitHubReleaseSourceAsset
local asset = try(util.coalesce_by_target(try(expr.tbl_interpolate(source.asset, expr_ctx)), opts))
local downloads = common.parse_downloads(asset, function(file)
diff --git a/lua/mason-core/installer/registry/providers/golang.lua b/lua/mason-core/installer/compiler/compilers/golang.lua
index 896d9bf9..01807088 100644
--- a/lua/mason-core/installer/registry/providers/golang.lua
+++ b/lua/mason-core/installer/compiler/compilers/golang.lua
@@ -1,7 +1,7 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local providers = require "mason-core.providers"
-local util = require "mason-core.installer.registry.util"
+local util = require "mason-core.installer.compiler.util"
local M = {}
diff --git a/lua/mason-core/installer/registry/providers/luarocks.lua b/lua/mason-core/installer/compiler/compilers/luarocks.lua
index 356857c0..356857c0 100644
--- a/lua/mason-core/installer/registry/providers/luarocks.lua
+++ b/lua/mason-core/installer/compiler/compilers/luarocks.lua
diff --git a/lua/mason-core/installer/registry/providers/mason.lua b/lua/mason-core/installer/compiler/compilers/mason.lua
index 3490ebaa..3490ebaa 100644
--- a/lua/mason-core/installer/registry/providers/mason.lua
+++ b/lua/mason-core/installer/compiler/compilers/mason.lua
diff --git a/lua/mason-core/installer/registry/providers/npm.lua b/lua/mason-core/installer/compiler/compilers/npm.lua
index e8489fe8..e8489fe8 100644
--- a/lua/mason-core/installer/registry/providers/npm.lua
+++ b/lua/mason-core/installer/compiler/compilers/npm.lua
diff --git a/lua/mason-core/installer/registry/providers/nuget.lua b/lua/mason-core/installer/compiler/compilers/nuget.lua
index 370c7b95..370c7b95 100644
--- a/lua/mason-core/installer/registry/providers/nuget.lua
+++ b/lua/mason-core/installer/compiler/compilers/nuget.lua
diff --git a/lua/mason-core/installer/registry/providers/opam.lua b/lua/mason-core/installer/compiler/compilers/opam.lua
index 276686ae..276686ae 100644
--- a/lua/mason-core/installer/registry/providers/opam.lua
+++ b/lua/mason-core/installer/compiler/compilers/opam.lua
diff --git a/lua/mason-core/installer/registry/providers/openvsx.lua b/lua/mason-core/installer/compiler/compilers/openvsx.lua
index df52807a..bf31e2f9 100644
--- a/lua/mason-core/installer/registry/providers/openvsx.lua
+++ b/lua/mason-core/installer/compiler/compilers/openvsx.lua
@@ -1,16 +1,17 @@
local Result = require "mason-core.result"
local common = require "mason-core.installer.managers.common"
-local expr = require "mason-core.installer.registry.expr"
+local expr = require "mason-core.installer.compiler.expr"
local providers = require "mason-core.providers"
-local util = require "mason-core.installer.registry.util"
+local util = require "mason-core.installer.compiler.util"
local M = {}
---@class OpenVSXSourceDownload : FileDownloadSpec
+---@field target? Platform | Platform[]
---@field target_platform? string
---@class OpenVSXSource : RegistryPackageSource
----@field download FileDownloadSpec | FileDownloadSpec[]
+---@field download OpenVSXSourceDownload | OpenVSXSourceDownload[]
---@param source OpenVSXSource
---@param purl Purl
diff --git a/lua/mason-core/installer/registry/providers/pypi.lua b/lua/mason-core/installer/compiler/compilers/pypi.lua
index 3fe6f89e..c44fcfe1 100644
--- a/lua/mason-core/installer/registry/providers/pypi.lua
+++ b/lua/mason-core/installer/compiler/compilers/pypi.lua
@@ -2,7 +2,7 @@ local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local providers = require "mason-core.providers"
local settings = require "mason.settings"
-local util = require "mason-core.installer.registry.util"
+local util = require "mason-core.installer.compiler.util"
local M = {}
diff --git a/lua/mason-core/installer/registry/expr.lua b/lua/mason-core/installer/compiler/expr.lua
index a07fc00d..a07fc00d 100644
--- a/lua/mason-core/installer/registry/expr.lua
+++ b/lua/mason-core/installer/compiler/expr.lua
diff --git a/lua/mason-core/installer/registry/init.lua b/lua/mason-core/installer/compiler/init.lua
index 7376db86..e1df6784 100644
--- a/lua/mason-core/installer/registry/init.lua
+++ b/lua/mason-core/installer/compiler/init.lua
@@ -3,10 +3,10 @@ local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local a = require "mason-core.async"
-local link = require "mason-core.installer.registry.link"
+local link = require "mason-core.installer.compiler.link"
local log = require "mason-core.log"
-local schemas = require "mason-core.installer.registry.schemas"
-local util = require "mason-core.installer.registry.util"
+local schemas = require "mason-core.installer.compiler.schemas"
+local util = require "mason-core.installer.compiler.util"
local M = {}
@@ -15,35 +15,37 @@ M.SCHEMA_CAP = _.set_of {
"registry+v1",
}
----@type table<string, InstallerProvider>
-local PROVIDERS = {}
+---@type table<string, InstallerCompiler>
+local COMPILERS = {}
---@param id string
----@param provider InstallerProvider
-function M.register_provider(id, provider)
- PROVIDERS[id] = provider
+---@param compiler InstallerCompiler
+function M.register_compiler(id, compiler)
+ COMPILERS[id] = compiler
end
-M.register_provider("cargo", _.lazy_require "mason-core.installer.registry.providers.cargo")
-M.register_provider("composer", _.lazy_require "mason-core.installer.registry.providers.composer")
-M.register_provider("gem", _.lazy_require "mason-core.installer.registry.providers.gem")
-M.register_provider("generic", _.lazy_require "mason-core.installer.registry.providers.generic")
-M.register_provider("github", _.lazy_require "mason-core.installer.registry.providers.github")
-M.register_provider("golang", _.lazy_require "mason-core.installer.registry.providers.golang")
-M.register_provider("luarocks", _.lazy_require "mason-core.installer.registry.providers.luarocks")
-M.register_provider("npm", _.lazy_require "mason-core.installer.registry.providers.npm")
-M.register_provider("nuget", _.lazy_require "mason-core.installer.registry.providers.nuget")
-M.register_provider("opam", _.lazy_require "mason-core.installer.registry.providers.opam")
-M.register_provider("openvsx", _.lazy_require "mason-core.installer.registry.providers.openvsx")
-M.register_provider("pypi", _.lazy_require "mason-core.installer.registry.providers.pypi")
-M.register_provider("mason", _.lazy_require "mason-core.installer.registry.providers.mason")
+M.register_compiler("cargo", _.lazy_require "mason-core.installer.compiler.compilers.cargo")
+M.register_compiler("composer", _.lazy_require "mason-core.installer.compiler.compilers.composer")
+M.register_compiler("gem", _.lazy_require "mason-core.installer.compiler.compilers.gem")
+M.register_compiler("generic", _.lazy_require "mason-core.installer.compiler.compilers.generic")
+M.register_compiler("github", _.lazy_require "mason-core.installer.compiler.compilers.github")
+M.register_compiler("golang", _.lazy_require "mason-core.installer.compiler.compilers.golang")
+M.register_compiler("luarocks", _.lazy_require "mason-core.installer.compiler.compilers.luarocks")
+M.register_compiler("mason", _.lazy_require "mason-core.installer.compiler.compilers.mason")
+M.register_compiler("npm", _.lazy_require "mason-core.installer.compiler.compilers.npm")
+M.register_compiler("nuget", _.lazy_require "mason-core.installer.compiler.compilers.nuget")
+M.register_compiler("opam", _.lazy_require "mason-core.installer.compiler.compilers.opam")
+M.register_compiler("openvsx", _.lazy_require "mason-core.installer.compiler.compilers.openvsx")
+M.register_compiler("pypi", _.lazy_require "mason-core.installer.compiler.compilers.pypi")
---@param purl Purl
-function M.get_provider(purl)
- return Optional.of_nilable(PROVIDERS[purl.type]):ok_or(("Unknown purl type: %s"):format(purl.type))
+---@return Result # Result<InstallerCompiler>
+function M.get_compiler(purl)
+ return Optional.of_nilable(COMPILERS[purl.type])
+ :ok_or(("Current version of mason.nvim is not capable of parsing package type %q."):format(purl.type))
end
----@class InstallerProvider
+---@class InstallerCompiler
---@field parse fun(source: RegistryPackageSource, purl: Purl, opts: PackageInstallOpts): Result
---@field install async fun(ctx: InstallContext, source: ParsedPackageSource, purl: Purl): Result
---@field get_versions async fun(purl: Purl, source: RegistryPackageSource): Result # Result<string[]>
@@ -128,13 +130,13 @@ function M.parse(spec, opts)
purl.version = opts.version
end
- ---@type InstallerProvider
- local provider = try(M.get_provider(purl))
- log.trace("Found provider for purl.", source.id)
- local parsed_source = try(provider.parse(source, purl, opts))
+ ---@type InstallerCompiler
+ local compiler = try(M.get_compiler(purl))
+ log.trace("Found compiler for purl.", source.id)
+ local parsed_source = try(compiler.parse(source, purl, opts))
log.trace("Parsed source for purl.", source.id, parsed_source)
return {
- provider = provider,
+ compiler = compiler,
source = vim.tbl_extend("keep", parsed_source, source),
raw_source = source,
purl = purl,
@@ -167,7 +169,7 @@ function M.compile(spec, opts)
{ _.T, _.identity },
}
- ---@type { purl: Purl, provider: InstallerProvider, source: ParsedPackageSource, raw_source: RegistryPackageSource }
+ ---@type { purl: Purl, compiler: InstallerCompiler, source: ParsedPackageSource, raw_source: RegistryPackageSource }
local parsed = try(M.parse(spec, opts):map_err(map_parse_err))
---@async
@@ -176,13 +178,13 @@ function M.compile(spec, opts)
return Result.try(function(try)
if ctx.opts.version then
try(util.ensure_valid_version(function()
- return parsed.provider.get_versions(parsed.purl, parsed.raw_source)
+ return parsed.compiler.get_versions(parsed.purl, parsed.raw_source)
end))
end
-- Run installer
a.scheduler()
- try(parsed.provider.install(ctx, parsed.source, parsed.purl))
+ try(parsed.compiler.install(ctx, parsed.source, parsed.purl))
if spec.schemas then
local result = schemas.download(ctx, spec, parsed.purl, parsed.source):on_failure(function(err)
diff --git a/lua/mason-core/installer/registry/link.lua b/lua/mason-core/installer/compiler/link.lua
index 85e751b7..5d136322 100644
--- a/lua/mason-core/installer/registry/link.lua
+++ b/lua/mason-core/installer/compiler/link.lua
@@ -2,7 +2,7 @@ local Optional = require "mason-core.optional"
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local a = require "mason-core.async"
-local expr = require "mason-core.installer.registry.expr"
+local expr = require "mason-core.installer.compiler.expr"
local fs = require "mason-core.fs"
local log = require "mason-core.log"
local path = require "mason-core.path"
diff --git a/lua/mason-core/installer/registry/schemas.lua b/lua/mason-core/installer/compiler/schemas.lua
index f9d044af..5e578dbd 100644
--- a/lua/mason-core/installer/registry/schemas.lua
+++ b/lua/mason-core/installer/compiler/schemas.lua
@@ -1,7 +1,7 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local a = require "mason-core.async"
-local expr = require "mason-core.installer.registry.expr"
+local expr = require "mason-core.installer.compiler.expr"
local fetch = require "mason-core.fetch"
local log = require "mason-core.log"
local path = require "mason-core.path"
diff --git a/lua/mason-core/installer/registry/util.lua b/lua/mason-core/installer/compiler/util.lua
index b3735c9c..b3735c9c 100644
--- a/lua/mason-core/installer/registry/util.lua
+++ b/lua/mason-core/installer/compiler/util.lua
diff --git a/lua/mason-core/installer/context/cwd.lua b/lua/mason-core/installer/context/cwd.lua
new file mode 100644
index 00000000..4f645fbb
--- /dev/null
+++ b/lua/mason-core/installer/context/cwd.lua
@@ -0,0 +1,48 @@
+local Result = require "mason-core.result"
+local fs = require "mason-core.fs"
+local path = require "mason-core.path"
+
+---@class InstallContextCwd
+---@field private location InstallLocation Defines the upper boundary for which paths are allowed as cwd.
+---@field private cwd string?
+local InstallContextCwd = {}
+InstallContextCwd.__index = InstallContextCwd
+
+---@param location InstallLocation
+function InstallContextCwd.new(location)
+ assert(location, "location not provided")
+ return setmetatable({
+ location = location,
+ cwd = nil,
+ }, InstallContextCwd)
+end
+
+---@param handle InstallHandle
+function InstallContextCwd:initialize(handle)
+ return Result.try(function(try)
+ local staging_dir = self.location:staging(handle.package.name)
+ if fs.async.dir_exists(staging_dir) then
+ try(Result.pcall(fs.async.rmrf, staging_dir))
+ end
+ try(Result.pcall(fs.async.mkdirp, staging_dir))
+ self:set(staging_dir)
+ end)
+end
+
+function InstallContextCwd:get()
+ assert(self.cwd ~= nil, "Tried to access cwd before it was set.")
+ return self.cwd
+end
+
+---@param new_abs_cwd string
+function InstallContextCwd:set(new_abs_cwd)
+ assert(type(new_abs_cwd) == "string", "new_cwd is not a string")
+ assert(
+ path.is_subdirectory(self.location:get_dir(), new_abs_cwd),
+ ("%q is not a subdirectory of %q"):format(new_abs_cwd, self.location)
+ )
+ self.cwd = new_abs_cwd
+ return self
+end
+
+return InstallContextCwd
diff --git a/lua/mason-core/installer/context/fs.lua b/lua/mason-core/installer/context/fs.lua
new file mode 100644
index 00000000..5c51fb56
--- /dev/null
+++ b/lua/mason-core/installer/context/fs.lua
@@ -0,0 +1,108 @@
+local fs = require "mason-core.fs"
+local log = require "mason-core.log"
+local path = require "mason-core.path"
+
+---@class InstallContextFs
+---@field private cwd InstallContextCwd
+local InstallContextFs = {}
+InstallContextFs.__index = InstallContextFs
+
+---@param cwd InstallContextCwd
+function InstallContextFs.new(cwd)
+ return setmetatable({ cwd = cwd }, InstallContextFs)
+end
+
+---@async
+---@param rel_path string The relative path from the current working directory to the file to append.
+---@param contents string
+function InstallContextFs:append_file(rel_path, contents)
+ return fs.async.append_file(path.concat { self.cwd:get(), rel_path }, contents)
+end
+
+---@async
+---@param rel_path string The relative path from the current working directory to the file to write.
+---@param contents string
+function InstallContextFs:write_file(rel_path, contents)
+ return fs.async.write_file(path.concat { self.cwd:get(), rel_path }, contents)
+end
+
+---@async
+---@param rel_path string The relative path from the current working directory to the file to read.
+function InstallContextFs:read_file(rel_path)
+ return fs.async.read_file(path.concat { self.cwd:get(), rel_path })
+end
+
+---@async
+---@param rel_path string The relative path from the current working directory.
+function InstallContextFs:file_exists(rel_path)
+ return fs.async.file_exists(path.concat { self.cwd:get(), rel_path })
+end
+
+---@async
+---@param rel_path string The relative path from the current working directory.
+function InstallContextFs:dir_exists(rel_path)
+ return fs.async.dir_exists(path.concat { self.cwd:get(), rel_path })
+end
+
+---@async
+---@param rel_path string The relative path from the current working directory.
+function InstallContextFs:rmrf(rel_path)
+ return fs.async.rmrf(path.concat { self.cwd:get(), rel_path })
+end
+
+---@async
+---@param rel_path string The relative path from the current working directory.
+function InstallContextFs:unlink(rel_path)
+ return fs.async.unlink(path.concat { self.cwd:get(), rel_path })
+end
+
+---@async
+---@param old_path string
+---@param new_path string
+function InstallContextFs:rename(old_path, new_path)
+ return fs.async.rename(path.concat { self.cwd:get(), old_path }, path.concat { self.cwd:get(), new_path })
+end
+
+---@async
+---@param dir_path string
+function InstallContextFs:mkdir(dir_path)
+ return fs.async.mkdir(path.concat { self.cwd:get(), dir_path })
+end
+
+---@async
+---@param dir_path string
+function InstallContextFs:mkdirp(dir_path)
+ return fs.async.mkdirp(path.concat { self.cwd:get(), dir_path })
+end
+
+---@async
+---@param file_path string
+function InstallContextFs:chmod_exec(file_path)
+ local bit = require "bit"
+ -- see chmod(2)
+ local USR_EXEC = 0x40
+ local GRP_EXEC = 0x8
+ local ALL_EXEC = 0x1
+ local EXEC = bit.bor(USR_EXEC, GRP_EXEC, ALL_EXEC)
+ local fstat = self:fstat(file_path)
+ if bit.band(fstat.mode, EXEC) ~= EXEC then
+ local plus_exec = bit.bor(fstat.mode, EXEC)
+ log.fmt_debug("Setting exec flags on file %s %o -> %o", file_path, fstat.mode, plus_exec)
+ self:chmod(file_path, plus_exec) -- chmod +x
+ end
+end
+
+---@async
+---@param file_path string
+---@param mode integer
+function InstallContextFs:chmod(file_path, mode)
+ return fs.async.chmod(path.concat { self.cwd:get(), file_path }, mode)
+end
+
+---@async
+---@param file_path string
+function InstallContextFs:fstat(file_path)
+ return fs.async.fstat(path.concat { self.cwd:get(), file_path })
+end
+
+return InstallContextFs
diff --git a/lua/mason-core/installer/context.lua b/lua/mason-core/installer/context/init.lua
index a991cd9f..0d178c4e 100644
--- a/lua/mason-core/installer/context.lua
+++ b/lua/mason-core/installer/context/init.lua
@@ -1,213 +1,37 @@
-local Optional = require "mason-core.optional"
+local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local fs = require "mason-core.fs"
local log = require "mason-core.log"
local path = require "mason-core.path"
local platform = require "mason-core.platform"
local receipt = require "mason-core.receipt"
-local spawn = require "mason-core.spawn"
-
----@class ContextualSpawn
----@field strict_mode boolean Whether spawn failures should raise an exception rather then return a Result.
----@field cwd CwdManager
----@field handle InstallHandle
----@field [string] async fun(opts: SpawnArgs): Result
-local ContextualSpawn = {}
-
----@param cwd CwdManager
----@param handle InstallHandle
----@param strict_mode boolean
-function ContextualSpawn.new(cwd, handle, strict_mode)
- return setmetatable({ cwd = cwd, handle = handle, strict_mode = strict_mode }, ContextualSpawn)
-end
-
----@param cmd string
-function ContextualSpawn:__index(cmd)
- ---@param args JobSpawnOpts
- return function(args)
- args.cwd = args.cwd or self.cwd:get()
- args.stdio_sink = args.stdio_sink or self.handle.stdio.sink
- local on_spawn = args.on_spawn
- local captured_handle
- args.on_spawn = function(handle, stdio, pid, ...)
- captured_handle = handle
- self.handle:register_spawn_handle(handle, pid, cmd, spawn._flatten_cmd_args(args))
- if on_spawn then
- on_spawn(handle, stdio, pid, ...)
- end
- end
- local function pop_spawn_stack()
- if captured_handle then
- self.handle:deregister_spawn_handle(captured_handle)
- end
- end
- local result = spawn[cmd](args):on_success(pop_spawn_stack):on_failure(pop_spawn_stack)
- if self.strict_mode then
- return result:get_or_throw()
- else
- return result
- end
- end
-end
-
----@class ContextualFs
----@field private cwd CwdManager
-local ContextualFs = {}
-ContextualFs.__index = ContextualFs
-
----@param cwd CwdManager
-function ContextualFs.new(cwd)
- return setmetatable({ cwd = cwd }, ContextualFs)
-end
-
----@async
----@param rel_path string The relative path from the current working directory to the file to append.
----@param contents string
-function ContextualFs:append_file(rel_path, contents)
- return fs.async.append_file(path.concat { self.cwd:get(), rel_path }, contents)
-end
-
----@async
----@param rel_path string The relative path from the current working directory to the file to write.
----@param contents string
-function ContextualFs:write_file(rel_path, contents)
- return fs.async.write_file(path.concat { self.cwd:get(), rel_path }, contents)
-end
-
----@async
----@param rel_path string The relative path from the current working directory to the file to read.
-function ContextualFs:read_file(rel_path)
- return fs.async.read_file(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param rel_path string The relative path from the current working directory.
-function ContextualFs:file_exists(rel_path)
- return fs.async.file_exists(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param rel_path string The relative path from the current working directory.
-function ContextualFs:dir_exists(rel_path)
- return fs.async.dir_exists(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param rel_path string The relative path from the current working directory.
-function ContextualFs:rmrf(rel_path)
- return fs.async.rmrf(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param rel_path string The relative path from the current working directory.
-function ContextualFs:unlink(rel_path)
- return fs.async.unlink(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param old_path string
----@param new_path string
-function ContextualFs:rename(old_path, new_path)
- return fs.async.rename(path.concat { self.cwd:get(), old_path }, path.concat { self.cwd:get(), new_path })
-end
-
----@async
----@param dir_path string
-function ContextualFs:mkdir(dir_path)
- return fs.async.mkdir(path.concat { self.cwd:get(), dir_path })
-end
-
----@async
----@param dir_path string
-function ContextualFs:mkdirp(dir_path)
- return fs.async.mkdirp(path.concat { self.cwd:get(), dir_path })
-end
-
----@async
----@param file_path string
-function ContextualFs:chmod_exec(file_path)
- local bit = require "bit"
- -- see chmod(2)
- local USR_EXEC = 0x40
- local GRP_EXEC = 0x8
- local ALL_EXEC = 0x1
- local EXEC = bit.bor(USR_EXEC, GRP_EXEC, ALL_EXEC)
- local fstat = self:fstat(file_path)
- if bit.band(fstat.mode, EXEC) ~= EXEC then
- local plus_exec = bit.bor(fstat.mode, EXEC)
- log.fmt_debug("Setting exec flags on file %s %o -> %o", file_path, fstat.mode, plus_exec)
- self:chmod(file_path, plus_exec) -- chmod +x
- end
-end
-
----@async
----@param file_path string
----@param mode integer
-function ContextualFs:chmod(file_path, mode)
- return fs.async.chmod(path.concat { self.cwd:get(), file_path }, mode)
-end
-
----@async
----@param file_path string
-function ContextualFs:fstat(file_path)
- return fs.async.fstat(path.concat { self.cwd:get(), file_path })
-end
-
----@class CwdManager
----@field private install_prefix string Defines the upper boundary for which paths are allowed as cwd.
----@field private cwd string
-local CwdManager = {}
-CwdManager.__index = CwdManager
-
-function CwdManager.new(install_prefix)
- assert(type(install_prefix) == "string", "install_prefix not provided")
- return setmetatable({
- install_prefix = install_prefix,
- cwd = nil,
- }, CwdManager)
-end
-
-function CwdManager:get()
- assert(self.cwd ~= nil, "Tried to access cwd before it was set.")
- return self.cwd
-end
-
----@param new_cwd string
-function CwdManager:set(new_cwd)
- assert(type(new_cwd) == "string", "new_cwd is not a string")
- assert(
- path.is_subdirectory(self.install_prefix, new_cwd),
- ("%q is not a subdirectory of %q"):format(new_cwd, self.install_prefix)
- )
- self.cwd = new_cwd
-end
---@class InstallContext
----@field public receipt InstallReceiptBuilder
----@field public requested_version Optional
----@field public fs ContextualFs
----@field public spawn ContextualSpawn
----@field public handle InstallHandle
----@field public package Package
----@field public cwd CwdManager
----@field public opts PackageInstallOpts
----@field public stdio_sink StdioSink
+---@field receipt InstallReceiptBuilder
+---@field fs InstallContextFs
+---@field spawn InstallContextSpawn
+---@field handle InstallHandle
+---@field package Package
+---@field cwd InstallContextCwd
+---@field opts PackageInstallOpts
+---@field stdio_sink StdioSink
---@field links { bin: table<string, string>, share: table<string, string>, opt: table<string, string> }
local InstallContext = {}
InstallContext.__index = InstallContext
---@param handle InstallHandle
+---@param cwd InstallContextCwd
+---@param spawn InstallContextSpawn
+---@param fs InstallContextFs
---@param opts PackageInstallOpts
-function InstallContext.new(handle, opts)
- local cwd_manager = CwdManager.new(path.install_prefix())
+function InstallContext.new(handle, cwd, spawn, fs, opts)
return setmetatable({
- cwd = cwd_manager,
- spawn = ContextualSpawn.new(cwd_manager, handle, false),
+ cwd = cwd,
+ spawn = spawn,
handle = handle,
package = handle.package, -- for convenience
- fs = ContextualFs.new(cwd_manager),
+ fs = fs,
receipt = receipt.InstallReceiptBuilder.new(),
- requested_version = Optional.of_nilable(opts.version),
stdio_sink = handle.stdio.sink,
links = {
bin = {},
@@ -227,8 +51,8 @@ function InstallContext:promote_cwd()
return
end
log.fmt_debug("Promoting cwd %s to %s", cwd, install_path)
- -- 1. Unlink any existing installation
- self.handle.package:unlink()
+ -- 1. Uninstall any existing installation
+ self.handle.package:uninstall()
-- 2. Prepare for renaming cwd to destination
if platform.is.unix then
-- Some Unix systems will raise an error when renaming a directory to a destination that does not already exist.
@@ -396,4 +220,42 @@ function InstallContext:link_bin(executable, rel_path)
return self
end
+InstallContext.CONTEXT_REQUEST = {}
+
+---@generic T
+---@param fn fun(context: InstallContext): T
+---@return T
+function InstallContext:execute(fn)
+ local thread = coroutine.create(function(...)
+ -- We wrap the function to allow it to be a spy instance (in which case it's not actually a function, but a
+ -- callable metatable - coroutine.create strictly expects functions only)
+ return fn(...)
+ end)
+ local step
+ local ret_val
+ step = function(...)
+ local ok, result = coroutine.resume(thread, ...)
+ if not ok then
+ error(result, 0)
+ elseif result == InstallContext.CONTEXT_REQUEST then
+ step(self)
+ elseif coroutine.status(thread) == "suspended" then
+ -- yield to parent coroutine
+ step(coroutine.yield(result))
+ else
+ ret_val = result
+ end
+ end
+ step(self)
+ return ret_val
+end
+
+---@async
+function InstallContext:build_receipt()
+ log.fmt_debug("Building receipt for %s", self.package)
+ return Result.pcall(function()
+ return self.receipt:with_name(self.package.name):with_completion_time(vim.loop.gettimeofday()):build()
+ end)
+end
+
return InstallContext
diff --git a/lua/mason-core/installer/context/spawn.lua b/lua/mason-core/installer/context/spawn.lua
new file mode 100644
index 00000000..6528c4b3
--- /dev/null
+++ b/lua/mason-core/installer/context/spawn.lua
@@ -0,0 +1,46 @@
+local spawn = require "mason-core.spawn"
+
+---@class InstallContextSpawn
+---@field strict_mode boolean Whether spawn failures should raise an exception rather then return a Result.
+---@field private cwd InstallContextCwd
+---@field private handle InstallHandle
+---@field [string] async fun(opts: SpawnArgs): Result
+local InstallContextSpawn = {}
+
+---@param cwd InstallContextCwd
+---@param handle InstallHandle
+---@param strict_mode boolean
+function InstallContextSpawn.new(cwd, handle, strict_mode)
+ return setmetatable({ cwd = cwd, handle = handle, strict_mode = strict_mode }, InstallContextSpawn)
+end
+
+---@param cmd string
+function InstallContextSpawn:__index(cmd)
+ ---@param args JobSpawnOpts
+ return function(args)
+ args.cwd = args.cwd or self.cwd:get()
+ args.stdio_sink = args.stdio_sink or self.handle.stdio.sink
+ local on_spawn = args.on_spawn
+ local captured_handle
+ args.on_spawn = function(handle, stdio, pid, ...)
+ captured_handle = handle
+ self.handle:register_spawn_handle(handle, pid, cmd, spawn._flatten_cmd_args(args))
+ if on_spawn then
+ on_spawn(handle, stdio, pid, ...)
+ end
+ end
+ local function pop_spawn_stack()
+ if captured_handle then
+ self.handle:deregister_spawn_handle(captured_handle)
+ end
+ end
+ local result = spawn[cmd](args):on_success(pop_spawn_stack):on_failure(pop_spawn_stack)
+ if self.strict_mode then
+ return result:get_or_throw()
+ else
+ return result
+ end
+ end
+end
+
+return InstallContextSpawn
diff --git a/lua/mason-core/installer/handle.lua b/lua/mason-core/installer/handle.lua
index f9b03557..96acbdd1 100644
--- a/lua/mason-core/installer/handle.lua
+++ b/lua/mason-core/installer/handle.lua
@@ -120,6 +120,10 @@ function InstallHandle:is_closed()
return self.state == "CLOSED"
end
+function InstallHandle:is_closing()
+ return self:is_closed() or self.is_terminated
+end
+
---@param new_state InstallHandleState
function InstallHandle:set_state(new_state)
local old_state = self.state
diff --git a/lua/mason-core/installer/init.lua b/lua/mason-core/installer/init.lua
index 45bba46b..37c74fcb 100644
--- a/lua/mason-core/installer/init.lua
+++ b/lua/mason-core/installer/init.lua
@@ -1,263 +1,10 @@
local InstallContext = require "mason-core.installer.context"
-local Result = require "mason-core.result"
-local _ = require "mason-core.functional"
-local a = require "mason-core.async"
-local control = require "mason-core.async.control"
-local fs = require "mason-core.fs"
-local linker = require "mason-core.installer.linker"
-local log = require "mason-core.log"
-local path = require "mason-core.path"
-local settings = require "mason.settings"
-
-local Semaphore = control.Semaphore
-
-local sem = Semaphore.new(settings.current.max_concurrent_installers)
local M = {}
----@async
-function M.create_prefix_dirs()
- return Result.try(function(try)
- for _, p in ipairs {
- path.install_prefix(),
- path.bin_prefix(),
- path.share_prefix(),
- path.package_prefix(),
- path.package_build_prefix(),
- } do
- if not fs.async.dir_exists(p) then
- try(Result.pcall(fs.async.mkdirp, p))
- end
- end
- end)
-end
-
----@async
----@param context InstallContext
-local function build_receipt(context)
- return Result.pcall(function()
- log.fmt_debug("Building receipt for %s", context.package)
- return context.receipt:with_name(context.package.name):with_completion_time(vim.loop.gettimeofday()):build()
- end)
-end
-
-local CONTEXT_REQUEST = {}
-
---@return InstallContext
function M.context()
- return coroutine.yield(CONTEXT_REQUEST)
-end
-
----@async
----@param ctx InstallContext
-local function lock_package(ctx)
- log.debug("Attempting to lock package", ctx.package)
- local lockfile = path.package_lock(ctx.package.name)
- if not ctx.opts.force and fs.async.file_exists(lockfile) then
- log.error("Lockfile already exists.", ctx.package)
- return Result.failure(
- ("Lockfile exists, installation is already running in another process (pid: %s). Run with :MasonInstall --force to bypass."):format(
- fs.sync.read_file(lockfile)
- )
- )
- end
- a.scheduler()
- fs.async.write_file(lockfile, vim.fn.getpid())
- log.debug("Wrote lockfile", ctx.package)
- return Result.success(lockfile)
-end
-
----@async
----@param context InstallContext
-function M.prepare_installer(context)
- local installer = require "mason-core.installer.registry"
- return Result.try(function(try)
- local package_build_prefix = path.package_build_prefix(context.package.name)
- if fs.async.dir_exists(package_build_prefix) then
- try(Result.pcall(fs.async.rmrf, package_build_prefix))
- end
- try(Result.pcall(fs.async.mkdirp, package_build_prefix))
- context.cwd:set(package_build_prefix)
-
- return try(installer.compile(context.handle.package.spec, context.opts))
- end)
-end
-
----@generic T
----@param context InstallContext
----@param fn fun(context: InstallContext): T
----@return T
-function M.exec_in_context(context, fn)
- local thread = coroutine.create(function(...)
- -- We wrap the function to allow it to be a spy instance (in which case it's not actually a function, but a
- -- callable metatable - coroutine.create strictly expects functions only)
- return fn(...)
- end)
- local step
- local ret_val
- step = function(...)
- local ok, result = coroutine.resume(thread, ...)
- if not ok then
- error(result, 0)
- elseif result == CONTEXT_REQUEST then
- step(context)
- elseif coroutine.status(thread) == "suspended" then
- -- yield to parent coroutine
- step(coroutine.yield(result))
- else
- ret_val = result
- end
- end
- context.receipt:with_start_time(vim.loop.gettimeofday())
- step(context)
- return ret_val
-end
-
----@async
----@param context InstallContext
----@param installer async fun(ctx: InstallContext)
-local function run_installer(context, installer)
- local handle = context.handle
- return Result.pcall(function()
- return a.wait(function(resolve, reject)
- local cancel_thread = a.run(M.exec_in_context, function(success, result)
- if success then
- resolve(result)
- else
- reject(result)
- end
- end, context, installer)
-
- handle:once("terminate", function()
- cancel_thread()
- if handle:is_closed() then
- reject "Installation was aborted."
- else
- handle:once("closed", function()
- reject "Installation was aborted."
- end)
- end
- end)
- end)
- end)
-end
-
----@async
----@param handle InstallHandle
----@param opts PackageInstallOpts
-function M.execute(handle, opts)
- if handle:is_active() or handle:is_closed() then
- log.fmt_debug("Received active or closed handle %s", handle)
- return Result.failure "Invalid handle state."
- end
-
- handle:queued()
- local permit = sem:acquire()
- if handle:is_closed() then
- permit:forget()
- log.fmt_trace("Installation was aborted %s", handle)
- return Result.failure "Installation was aborted."
- end
- log.fmt_trace("Activating handle %s", handle)
- handle:active()
-
- local pkg = handle.package
- local context = InstallContext.new(handle, opts)
- local tailed_output = {}
-
- if opts.debug then
- local function append_log(chunk)
- tailed_output[#tailed_output + 1] = chunk
- end
- handle:on("stdout", append_log)
- handle:on("stderr", append_log)
- end
-
- log.fmt_info("Executing installer for %s %s", pkg, opts)
-
- return M.create_prefix_dirs()
- :and_then(function()
- return lock_package(context)
- end)
- :and_then(function(lockfile)
- local release_lock = _.partial(pcall, fs.async.unlink, lockfile)
- return Result.try(function(try)
- -- 1. prepare directories and initialize cwd
- local installer = try(M.prepare_installer(context))
-
- -- 2. execute installer
- try(run_installer(context, installer))
-
- -- 3. promote temporary installation dir
- try(Result.pcall(function()
- context:promote_cwd()
- end))
-
- -- 4. link package
- try(linker.link(context))
-
- -- 5. build & write receipt
- ---@type InstallReceipt
- local receipt = try(build_receipt(context))
- try(Result.pcall(function()
- receipt:write(context.cwd:get())
- end))
- end)
- :on_success(function()
- release_lock()
- if opts.debug then
- context.fs:write_file("mason-debug.log", table.concat(tailed_output, ""))
- end
- end)
- :on_failure(function()
- release_lock()
- if not opts.debug then
- -- clean up installation dir
- pcall(function()
- fs.async.rmrf(context.cwd:get())
- end)
- else
- context.fs:write_file("mason-debug.log", table.concat(tailed_output, ""))
- context.stdio_sink.stdout(
- ("[debug] Installation directory retained at %q.\n"):format(context.cwd:get())
- )
- end
-
- -- unlink linked executables (in the occasion an error occurs after linking)
- build_receipt(context):on_success(function(receipt)
- linker.unlink(context.package, receipt):on_failure(function(err)
- log.error("Failed to unlink failed installation", err)
- end)
- end)
- end)
- end)
- :on_success(function()
- permit:forget()
- handle:close()
- log.fmt_info("Installation succeeded for %s", pkg)
- end)
- :on_failure(function(failure)
- permit:forget()
- log.fmt_error("Installation failed for %s error=%s", pkg, failure)
- context.stdio_sink.stderr(tostring(failure))
- context.stdio_sink.stderr "\n"
-
- if not handle:is_closed() and not handle.is_terminated then
- handle:close()
- end
- end)
-end
-
----Runs the provided async functions concurrently and returns their result, once all are resolved.
----This is really just a wrapper around a.wait_all() that makes sure to patch the coroutine context before creating the
----new async execution contexts.
----@async
----@param suspend_fns async fun(ctx: InstallContext)[]
-function M.run_concurrently(suspend_fns)
- local context = M.context()
- return a.wait_all(_.map(function(suspend_fn)
- return _.partial(M.exec_in_context, context, suspend_fn)
- end, suspend_fns))
+ return coroutine.yield(InstallContext.CONTEXT_REQUEST)
end
return M
diff --git a/lua/mason-core/installer/location.lua b/lua/mason-core/installer/location.lua
new file mode 100644
index 00000000..2cc038e4
--- /dev/null
+++ b/lua/mason-core/installer/location.lua
@@ -0,0 +1,63 @@
+local Path = require "mason-core.path"
+local Result = require "mason-core.result"
+local fs = require "mason-core.fs"
+
+---@class InstallLocation
+---@field private dir string
+local InstallLocation = {}
+InstallLocation.__index = InstallLocation
+
+---@param dir string
+function InstallLocation.new(dir)
+ return setmetatable({
+ dir = dir,
+ }, InstallLocation)
+end
+
+function InstallLocation:get_dir()
+ return self.dir
+end
+
+---@async
+function InstallLocation:initialize()
+ return Result.try(function(try)
+ for _, p in ipairs {
+ self.dir,
+ self:bin(),
+ self:share(),
+ self:package(),
+ self:staging(),
+ } do
+ if not fs.async.dir_exists(p) then
+ try(Result.pcall(fs.async.mkdirp, p))
+ end
+ end
+ end)
+end
+
+---@param path string?
+function InstallLocation:bin(path)
+ return Path.concat { self.dir, "bin", path }
+end
+
+---@param path string?
+function InstallLocation:share(path)
+ return Path.concat { self.dir, "share", path }
+end
+
+---@param path string?
+function InstallLocation:package(path)
+ return Path.concat { self.dir, "packages", path }
+end
+
+---@param path string?
+function InstallLocation:staging(path)
+ return Path.concat { self.dir, "staging", path }
+end
+
+---@param name string
+function InstallLocation:lockfile(name)
+ return self:staging(("%s.lock"):format(name))
+end
+
+return InstallLocation
diff --git a/lua/mason-core/installer/runner.lua b/lua/mason-core/installer/runner.lua
new file mode 100644
index 00000000..175610d5
--- /dev/null
+++ b/lua/mason-core/installer/runner.lua
@@ -0,0 +1,218 @@
+local Result = require "mason-core.result"
+local _ = require "mason-core.functional"
+local a = require "mason-core.async"
+local compiler = require "mason-core.installer.compiler"
+local fs = require "mason-core.fs"
+local linker = require "mason-core.installer.linker"
+local log = require "mason-core.log"
+local registry = require "mason-registry"
+
+local InstallContext = require "mason-core.installer.context"
+local InstallContextCwd = require "mason-core.installer.context.cwd"
+local InstallContextFs = require "mason-core.installer.context.fs"
+local InstallContextSpawn = require "mason-core.installer.context.spawn"
+
+---@class InstallRunner
+---@field location InstallLocation
+---@field handle InstallHandle
+---@field semaphore Semaphore
+---@field permit Permit?
+local InstallRunner = {}
+InstallRunner.__index = InstallRunner
+
+---@param location InstallLocation
+---@param handle InstallHandle
+---@param semaphore Semaphore
+function InstallRunner.new(location, handle, semaphore)
+ return setmetatable({
+ location = location,
+ semaphore = semaphore,
+ handle = handle,
+ }, InstallRunner)
+end
+
+---@param opts PackageInstallOpts
+---@param callback? fun(success: boolean, result: any)
+function InstallRunner:execute(opts, callback)
+ local handle = self.handle
+ log.fmt_info("Executing installer for %s %s", handle.package, opts)
+
+ local context_cwd = InstallContextCwd.new(self.location)
+ local context_spawn = InstallContextSpawn.new(context_cwd, handle, false)
+ local context_fs = InstallContextFs.new(context_cwd)
+ local context = InstallContext.new(handle, context_cwd, context_spawn, context_fs, opts)
+
+ local tailed_output = {}
+
+ if opts.debug then
+ local function append_log(chunk)
+ tailed_output[#tailed_output + 1] = chunk
+ end
+ handle:on("stdout", append_log)
+ handle:on("stderr", append_log)
+ end
+
+ ---@async
+ local function finalize_logs(success, result)
+ if not success then
+ context.stdio_sink.stderr(tostring(result))
+ context.stdio_sink.stderr "\n"
+ end
+
+ if opts.debug then
+ context.fs:write_file("mason-debug.log", table.concat(tailed_output, ""))
+ context.stdio_sink.stdout(("[debug] Installation directory retained at %q.\n"):format(context.cwd:get()))
+ end
+ end
+
+ ---@async
+ local finalize = a.scope(function(success, result)
+ finalize_logs(success, result)
+
+ if not opts.debug and not success then
+ -- clean up installation dir
+ pcall(function()
+ fs.async.rmrf(context.cwd:get())
+ end)
+ end
+
+ if not handle:is_closing() then
+ handle:close()
+ end
+
+ self:release_lock()
+ self:release_permit()
+
+ if callback then
+ callback(success, result)
+ end
+
+ if success then
+ log.fmt_info("Installation succeeded for %s", handle.package)
+ handle.package:emit("install:success", handle)
+ registry:emit("package:install:success", handle.package, handle)
+ else
+ log.fmt_error("Installation failed for %s error=%s", handle.package, result)
+ handle.package:emit("install:failed", handle, result)
+ registry:emit("package:install:failed", handle.package, handle, result)
+ end
+ end)
+
+ local cancel_execution = a.run(function()
+ return Result.try(function(try)
+ try(self:acquire_permit())
+ try(self.location:initialize())
+ try(self:acquire_lock(opts.force))
+
+ context.receipt:with_start_time(vim.loop.gettimeofday())
+
+ -- 1. initialize working directory
+ try(context_cwd:initialize(handle))
+
+ -- 2. run installer
+ ---@type async fun(ctx: InstallContext): Result
+ local installer = try(compiler.compile(handle.package.spec, opts))
+ try(context:execute(installer))
+
+ -- 3. promote temporary installation dir
+ try(Result.pcall(function()
+ context:promote_cwd()
+ end))
+
+ -- 4. link package & write receipt
+ return linker
+ .link(context)
+ :and_then(function()
+ return context:build_receipt(context)
+ end)
+ :and_then(
+ ---@param receipt InstallReceipt
+ function(receipt)
+ return receipt:write(context.cwd:get())
+ end
+ )
+ :on_failure(function()
+ -- unlink any links that were made before failure
+ context:build_receipt():on_success(
+ ---@param receipt InstallReceipt
+ function(receipt)
+ linker.unlink(handle.package, receipt):on_failure(function(err)
+ log.error("Failed to unlink failed installation.", err)
+ end)
+ end
+ )
+ end)
+ end):get_or_throw()
+ end, finalize)
+
+ handle:once("terminate", function()
+ cancel_execution()
+ local function on_close()
+ finalize(false, "Installation was aborted.")
+ end
+ if handle:is_closed() then
+ on_close()
+ else
+ handle:once("closed", on_close)
+ end
+ end)
+end
+
+---@async
+---@private
+function InstallRunner:release_lock()
+ pcall(fs.async.unlink, self.location:lockfile(self.handle.package.name))
+end
+
+---@async
+---@param force boolean?
+---@private
+function InstallRunner:acquire_lock(force)
+ local pkg = self.handle.package
+ log.debug("Attempting to lock package", pkg)
+ local lockfile = self.location:lockfile(pkg.name)
+ if force ~= true and fs.async.file_exists(lockfile) then
+ log.error("Lockfile already exists.", pkg)
+ return Result.failure(
+ ("Lockfile exists, installation is already running in another process (pid: %s). Run with :MasonInstall --force to bypass."):format(
+ fs.async.read_file(lockfile)
+ )
+ )
+ end
+ a.scheduler()
+ fs.async.write_file(lockfile, vim.fn.getpid())
+ log.debug("Wrote lockfile", pkg)
+ return Result.success(lockfile)
+end
+
+---@async
+---@private
+function InstallRunner:acquire_permit()
+ local handle = self.handle
+ if handle:is_active() or handle:is_closed() then
+ log.fmt_debug("Received active or closed handle %s", handle)
+ return Result.failure "Invalid handle state."
+ end
+
+ handle:queued()
+ local permit = self.semaphore:acquire()
+ if handle:is_closed() then
+ permit:forget()
+ log.fmt_trace("Installation was aborted %s", handle)
+ return Result.failure "Installation was aborted."
+ end
+ log.fmt_trace("Activating handle %s", handle)
+ handle:active()
+ self.permit = permit
+ return Result.success()
+end
+
+---@private
+function InstallRunner:release_permit()
+ if self.permit then
+ self.permit:forget()
+ self.permit = nil
+ end
+end
+
+return InstallRunner
diff --git a/lua/mason-core/package/init.lua b/lua/mason-core/package/init.lua
index bc98a72a..b0da8a61 100644
--- a/lua/mason-core/package/init.lua
+++ b/lua/mason-core/package/init.lua
@@ -1,14 +1,17 @@
local EventEmitter = require "mason-core.EventEmitter"
+local InstallLocation = require "mason-core.installer.location"
+local InstallRunner = require "mason-core.installer.runner"
local Optional = require "mason-core.optional"
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
-local a = require "mason-core.async"
local fs = require "mason-core.fs"
local log = require "mason-core.log"
local path = require "mason-core.path"
local platform = require "mason-core.platform"
local registry = require "mason-registry"
+local settings = require "mason.settings"
+local Semaphore = require("mason-core.async.control").Semaphore
---@class Package : EventEmitter
---@field name string
@@ -135,81 +138,56 @@ end
---@alias PackageInstallOpts { version?: string, debug?: boolean, target?: string, force?: boolean, strict?: boolean }
----@param opts? PackageInstallOpts
----@return InstallHandle
-function Package:install(opts)
- opts = opts or {}
+-- TODO this needs to be elsewhere
+local semaphore = Semaphore.new(settings.current.max_concurrent_installers)
+
+function Package:is_installing()
return self:get_handle()
- :map(function(handle)
- if not handle:is_closed() then
- log.fmt_debug("Handle %s already exist for package %s", handle, self)
- return handle
+ :map(
+ ---@param handle InstallHandle
+ function(handle)
+ return not handle:is_closed()
end
- end)
- :or_else_get(function()
- local handle = self:new_handle()
- a.run(
- require("mason-core.installer").execute,
- ---@param success boolean
- ---@param result Result
- function(success, result)
- if not success then
- -- Installer failed abnormally (i.e. unexpected exception in the installer code itself).
- log.error("Unexpected error", result)
- handle.stdio.sink.stderr(tostring(result))
- handle.stdio.sink.stderr "\nInstallation failed abnormally. Please report this error."
- self:emit("install:failed", handle)
- registry:emit("package:install:failed", self, handle)
+ )
+ :or_else(false)
+end
- -- We terminate _after_ emitting failure events because [termination -> failed] have different
- -- meaning than [failed -> terminate] ([termination -> failed] is interpreted as a triggered
- -- termination).
- if not handle:is_closed() and not handle.is_terminated then
- handle:terminate()
- end
- return
- end
- result
- :on_success(function()
- self:emit("install:success", handle)
- registry:emit("package:install:success", self, handle)
- end)
- :on_failure(function()
- self:emit("install:failed", handle)
- registry:emit("package:install:failed", self, handle)
- end)
- end,
- handle,
- opts
- )
- return handle
- end)
+---@param opts? PackageInstallOpts
+---@param callback? fun(success: boolean, result: any)
+---@return InstallHandle
+function Package:install(opts, callback)
+ opts = opts or {}
+ assert(not self:is_installing(), "Package is already installing.")
+ local handle = self:new_handle()
+ local runner = InstallRunner.new(InstallLocation.new(settings.current.install_root_dir), handle, semaphore)
+ runner:execute(opts, callback)
+ return handle
end
+---@return boolean
function Package:uninstall()
- local was_unlinked = self:unlink()
- if was_unlinked then
- self:emit "uninstall:success"
- registry:emit("package:uninstall:success", self)
- end
- return was_unlinked
+ return self:get_receipt()
+ :map(function(receipt)
+ self:unlink(receipt)
+ self:emit("uninstall:success", receipt)
+ registry:emit("package:uninstall:success", self, receipt)
+ return true
+ end)
+ :or_else(false)
end
-function Package:unlink()
+---@private
+---@param receipt InstallReceipt
+function Package:unlink(receipt)
log.fmt_trace("Unlinking %s", self)
local install_path = self:get_install_path()
+
-- 1. Unlink
- self:get_receipt():if_present(function(receipt)
- local linker = require "mason-core.installer.linker"
- linker.unlink(self, receipt):get_or_throw()
- end)
+ local linker = require "mason-core.installer.linker"
+ linker.unlink(self, receipt):get_or_throw()
-- 2. Remove installation artifacts
- if fs.sync.dir_exists(install_path) then
- fs.sync.rmrf(install_path)
- return true
- end
- return false
+ fs.sync.rmrf(install_path)
end
function Package:is_installed()
@@ -260,18 +238,18 @@ end
---@param opts? PackageInstallOpts
function Package:is_installable(opts)
- return require("mason-core.installer.registry").parse(self.spec, opts or {}):is_success()
+ return require("mason-core.installer.compiler").parse(self.spec, opts or {}):is_success()
end
---@return Result # Result<string[]>
function Package:get_all_versions()
- local registry_installer = require "mason-core.installer.registry"
+ local compiler = require "mason-core.installer.compiler"
return Result.try(function(try)
---@type Purl
local purl = try(Purl.parse(self.spec.source.id))
- ---@type InstallerProvider
- local provider = try(registry_installer.get_provider(purl))
- return provider.get_versions(purl, self.spec.source)
+ ---@type InstallerCompiler
+ local compiler = try(compiler.get_compiler(purl))
+ return compiler.get_versions(purl, self.spec.source)
end)
end
diff --git a/lua/mason-core/receipt.lua b/lua/mason-core/receipt.lua
index d9fe9d88..748cab38 100644
--- a/lua/mason-core/receipt.lua
+++ b/lua/mason-core/receipt.lua
@@ -1,3 +1,7 @@
+local Result = require "mason-core.result"
+local fs = require "mason-core.fs"
+local path = require "mason-core.path"
+
local M = {}
---@alias InstallReceiptSchemaVersion
@@ -56,11 +60,11 @@ function InstallReceipt:get_links()
end
---@async
----@param cwd string
-function InstallReceipt:write(cwd)
- local path = require "mason-core.path"
- local fs = require "mason-core.fs"
- fs.async.write_file(path.concat { cwd, "mason-receipt.json" }, vim.json.encode(self))
+---@param dir string
+function InstallReceipt:write(dir)
+ return Result.pcall(function()
+ fs.async.write_file(path.concat { dir, "mason-receipt.json" }, vim.json.encode(self))
+ end)
end
---@class InstallReceiptBuilder
diff --git a/lua/mason-registry/sources/util.lua b/lua/mason-registry/sources/util.lua
index 80d5f16f..04ab7845 100644
--- a/lua/mason-registry/sources/util.lua
+++ b/lua/mason-registry/sources/util.lua
@@ -1,8 +1,8 @@
local Optional = require "mason-core.optional"
local Pkg = require "mason-core.package"
local _ = require "mason-core.functional"
+local compiler = require "mason-core.installer.compiler"
local log = require "mason-core.log"
-local registry_installer = require "mason-core.installer.registry"
local M = {}
@@ -10,7 +10,7 @@ local M = {}
function M.map_registry_spec(spec)
spec.schema = spec.schema or "registry+v1"
- if not registry_installer.SCHEMA_CAP[spec.schema] then
+ if not compiler.SCHEMA_CAP[spec.schema] then
log.fmt_debug("Excluding package=%s with unsupported schema_version=%s", spec.name, spec.schema)
return Optional.empty()
end
diff --git a/lua/mason-test/helpers.lua b/lua/mason-test/helpers.lua
new file mode 100644
index 00000000..57b486ea
--- /dev/null
+++ b/lua/mason-test/helpers.lua
@@ -0,0 +1,33 @@
+local InstallContext = require "mason-core.installer.context"
+local InstallContextCwd = require "mason-core.installer.context.cwd"
+local InstallContextFs = require "mason-core.installer.context.fs"
+local InstallContextSpawn = require "mason-core.installer.context.spawn"
+local InstallHandle = require "mason-core.installer.handle"
+local InstallLocation = require "mason-core.installer.location"
+local Result = require "mason-core.result"
+local registry = require "mason-registry"
+local spy = require "luassert.spy"
+
+local M = {}
+
+---@param opts? { install_opts?: PackageInstallOpts, package?: string }
+function M.create_context(opts)
+ local pkg = registry.get_package(opts and opts.package or "dummy")
+ local handle = InstallHandle.new(pkg)
+ local location = InstallLocation.new "/tmp/install-dir"
+ local context_cwd = InstallContextCwd.new(location):set(location.dir)
+ local context_spawn = InstallContextSpawn.new(context_cwd, handle, false)
+ local context_fs = InstallContextFs.new(context_cwd)
+ local context = InstallContext.new(handle, context_cwd, context_spawn, context_fs, opts and opts.install_opts or {})
+ context.spawn = setmetatable({}, {
+ __index = function(s, cmd)
+ s[cmd] = spy.new(function()
+ return Result.success { stdout = nil, stderr = nil }
+ end)
+ return s[cmd]
+ end,
+ })
+ return context
+end
+
+return M
diff --git a/tests/helpers/lua/luassertx.lua b/tests/helpers/lua/luassertx.lua
index e9bc4e44..3de3bf15 100644
--- a/tests/helpers/lua/luassertx.lua
+++ b/tests/helpers/lua/luassertx.lua
@@ -2,6 +2,26 @@ local a = require "mason-core.async"
local assert = require "luassert"
local match = require "luassert.match"
+local function wait(_, arguments)
+ ---@type (fun()) Function to execute until it does not error.
+ local assertions_fn = arguments[1]
+ ---@type number Timeout in milliseconds. Defaults to 5000.
+ local timeout = arguments[2] or 5000
+
+ local err
+ if
+ not vim.wait(timeout, function()
+ local ok, err_ = pcall(assertions_fn)
+ err = err_
+ return ok
+ end, math.min(timeout, 100))
+ then
+ error(err)
+ end
+
+ return true
+end
+
local function wait_for(_, arguments)
---@type (fun()) Function to execute until it does not error.
local assertions_fn = arguments[1]
@@ -76,3 +96,4 @@ assert:register("matcher", "list_containing", list_containing)
assert:register("matcher", "instanceof", instanceof)
assert:register("matcher", "capture", capture)
assert:register("assertion", "wait_for", wait_for)
+assert:register("assertion", "wait", wait)
diff --git a/tests/helpers/lua/test_helpers.lua b/tests/helpers/lua/test_helpers.lua
deleted file mode 100644
index c7d6f983..00000000
--- a/tests/helpers/lua/test_helpers.lua
+++ /dev/null
@@ -1,70 +0,0 @@
----@diagnostic disable: lowercase-global
-local spy = require "luassert.spy"
-local util = require "luassert.util"
-
-local InstallContext = require "mason-core.installer.context"
-local InstallHandle = require "mason-core.installer.handle"
-local Result = require "mason-core.result"
-local a = require "mason-core.async"
-local path = require "mason-core.path"
-local registry = require "mason-registry"
-
--- selene: allow(unused_variable)
-function async_test(suspend_fn)
- return function()
- local ok, err = pcall(a.run_blocking, suspend_fn)
- if not ok then
- error(err, util.errorlevel())
- end
- end
-end
-
--- selene: allow(unscoped_variables, incorrect_standard_library_use)
-mockx = {
- just_runs = function() end,
- returns = function(val)
- return function()
- return val
- end
- end,
- throws = function(exception)
- return function()
- error(exception, 2)
- end
- end,
-}
-
----@param opts? PackageInstallOpts
-function create_dummy_context(opts)
- local ctx = InstallContextGenerator(InstallHandleGenerator "registry", opts)
- ctx.cwd:set(path.package_build_prefix "registry")
- ctx.spawn = setmetatable({}, {
- __index = function(s, cmd)
- s[cmd] = spy.new(function()
- return Result.success { stdout = nil, stderr = nil }
- end)
- return s[cmd]
- end,
- })
- return ctx
-end
-
--- selene: allow(unused_variable)
----@param package_name string
-function InstallHandleGenerator(package_name)
- return InstallHandle.new(registry.get_package(package_name))
-end
-
--- selene: allow(unused_variable)
----@param handle InstallHandle
----@param opts PackageInstallOpts?
-function InstallContextGenerator(handle, opts)
- local context = InstallContext.new(handle, opts or {})
- context.spawn = setmetatable({ strict_mode = true }, {
- __index = function(self, cmd)
- self[cmd] = spy.new(mockx.just_runs())
- return self[cmd]
- end,
- })
- return context
-end
diff --git a/tests/mason-core/EventEmitter_spec.lua b/tests/mason-core/EventEmitter_spec.lua
index caec8a80..76a9964b 100644
--- a/tests/mason-core/EventEmitter_spec.lua
+++ b/tests/mason-core/EventEmitter_spec.lua
@@ -32,26 +32,23 @@ describe("EventEmitter", function()
it("should remove registered event handlers", function()
local emitter = EventEmitter.init(setmetatable({}, { __index = EventEmitter }))
- local my_event_handler = spy.new()
- emitter:on("my:event", my_event_handler --[[@as fun()]])
- emitter:once("my:event", my_event_handler --[[@as fun()]])
+ local my_event_handler = spy.new() --[[@as fun()]]
+ emitter:on("my:event", my_event_handler)
+ emitter:once("my:event", my_event_handler)
- emitter:off("my:event", my_event_handler --[[@as fun()]])
+ emitter:off("my:event", my_event_handler)
emitter:emit("my:event", { table = "value" })
assert.spy(my_event_handler).was_called(0)
end)
- it(
- "should print errors in handlers",
- async_test(function()
- spy.on(vim.api, "nvim_err_writeln")
- local emitter = EventEmitter.init(setmetatable({}, { __index = EventEmitter }))
- emitter:on("event", mockx.throws "My error.")
- emitter:emit "event"
- a.wait(vim.schedule)
- assert.spy(vim.api.nvim_err_writeln).was_called(1)
- assert.spy(vim.api.nvim_err_writeln).was_called_with "My error."
- end)
- )
+ it("should print errors in handlers", function()
+ spy.on(vim.api, "nvim_err_writeln")
+ local emitter = EventEmitter.init(setmetatable({}, { __index = EventEmitter }))
+ emitter:on("event", mockx.throws "My error.")
+ emitter:emit "event"
+ a.run_blocking(a.wait, vim.schedule)
+ assert.spy(vim.api.nvim_err_writeln).was_called(1)
+ assert.spy(vim.api.nvim_err_writeln).was_called_with "My error."
+ end)
end)
diff --git a/tests/mason-core/async/async_spec.lua b/tests/mason-core/async/async_spec.lua
index 61eeeb1b..a4197b85 100644
--- a/tests/mason-core/async/async_spec.lua
+++ b/tests/mason-core/async/async_spec.lua
@@ -31,51 +31,39 @@ describe("async", function()
assert.equals(8, value)
end)
- it(
- "should pass arguments to .run",
- async_test(function()
- local callback = spy.new()
- local start = timestamp()
- a.run(a.sleep, callback, 100)
- assert.wait_for(function()
- assert.spy(callback).was_called(1)
- local stop = timestamp()
- local grace_ms = 25
- assert.is_true((stop - start) >= (100 - grace_ms))
- end, 150)
- end)
- )
+ it("should pass arguments to .run", function()
+ local fn = spy.new()
+ a.run(function(...)
+ fn(...)
+ end, spy.new(), 100, 200)
+ assert.spy(fn).was_called(1)
+ assert.spy(fn).was_called_with(100, 200)
+ end)
- it(
- "should wrap callback-style async functions",
- async_test(function()
- local stdio = process.in_memory_sink()
- local success, exit_code = a.promisify(process.spawn)("env", {
- args = {},
- env = { "FOO=BAR", "BAR=BAZ" },
- stdio_sink = stdio.sink,
- })
- assert.is_true(success)
- assert.equals(0, exit_code)
- assert.equals("FOO=BAR\nBAR=BAZ\n", table.concat(stdio.buffers.stdout, ""))
- end)
- )
+ it("should wrap callback-style async functions via promisify", function()
+ local async_spawn = _.compose(_.table_pack, a.promisify(process.spawn))
+ local stdio = process.in_memory_sink()
+ local success, exit_code = unpack(a.run_blocking(async_spawn, "env", {
+ args = {},
+ env = { "FOO=BAR", "BAR=BAZ" },
+ stdio_sink = stdio.sink,
+ }))
+ assert.is_true(success)
+ assert.equals(0, exit_code)
+ assert.equals("FOO=BAR\nBAR=BAZ\n", table.concat(stdio.buffers.stdout, ""))
+ end)
- it(
- "should reject callback-style functions",
- async_test(function()
- local err = assert.has_error(function()
- a.promisify(function(arg1, cb)
- cb(arg1, nil)
- end, true) "påskmust"
- end)
- assert.equals(err, "påskmust")
+ it("should propagate errors in callback-style functions via promisify", function()
+ local err = assert.has_error(function()
+ a.run_blocking(a.promisify(function(cb)
+ cb "Error message."
+ end, true))
end)
- )
+ assert.equals(err, "Error message.")
+ end)
- it(
- "should return all values",
- async_test(function()
+ it("should return all values from a.wait", function()
+ a.run_blocking(function()
local val1, val2, val3 = a.wait(function(resolve)
resolve(1, 2, 3)
end)
@@ -83,35 +71,32 @@ describe("async", function()
assert.equals(2, val2)
assert.equals(3, val3)
end)
- )
+ end)
- it(
- "should cancel coroutine",
- async_test(function()
- local james_bond = spy.new()
- local poutine = a.scope(function()
- a.sleep(100)
- james_bond()
+ it("should cancel coroutine", function()
+ local capture = spy.new()
+ a.run_blocking(function()
+ local cancel = a.scope(function()
+ a.sleep(10)
+ capture()
end)()
- poutine()
- a.sleep(200)
- assert.spy(james_bond).was_not.called()
+ cancel()
+ a.sleep(20)
end)
- )
+ assert.spy(capture).was_not.called()
+ end)
- it(
- "should raise error if async function raises error",
- async_test(function()
+ it("should raise error if async function raises error", function()
+ a.run_blocking(function()
local err = assert.has.errors(a.promisify(function()
error "something went wrong"
end))
assert.is_true(match.has_match "something went wrong$"(err))
end)
- )
+ end)
- it(
- "should raise error if async function rejects",
- async_test(function()
+ it("should raise error if async function rejects", function()
+ a.run_blocking(function()
local err = assert.has.errors(function()
a.wait(function(_, reject)
reject "This is an error"
@@ -119,18 +104,17 @@ describe("async", function()
end)
assert.equals("This is an error", err)
end)
- )
+ end)
- it(
- "should pass nil arguments to promisified functions",
- async_test(function()
- local fn = spy.new(function(_, _, _, _, _, _, _, cb)
- cb()
- end)
+ it("should pass nil arguments to promisified functions", function()
+ local fn = spy.new(function(_, _, _, _, _, _, _, cb)
+ cb()
+ end)
+ a.run_blocking(function()
a.promisify(fn)(nil, 2, nil, 4, nil, nil, 7)
- assert.spy(fn).was_called_with(nil, 2, nil, 4, nil, nil, 7, match.is_function())
end)
- )
+ assert.spy(fn).was_called_with(nil, 2, nil, 4, nil, nil, 7, match.is_function())
+ end)
it("should accept yielding non-promise values to parent coroutine context", function()
local thread = coroutine.create(function(val)
@@ -143,60 +127,56 @@ describe("async", function()
assert.equals(1337, value)
end)
- it(
- "should run all suspending functions concurrently",
- async_test(function()
- local start = timestamp()
- local function sleep(ms, ret_val)
- return function()
- a.sleep(ms)
- return ret_val
- end
+ it("should run all suspending functions concurrently", function()
+ local function sleep(ms, ret_val)
+ return function()
+ a.sleep(ms)
+ return ret_val
end
- local one, two, three, four, five = a.wait_all {
+ end
+ local start = timestamp()
+ local one, two, three, four, five = unpack(a.run_blocking(function()
+ return _.table_pack(a.wait_all {
sleep(100, 1),
sleep(100, "two"),
sleep(100, "three"),
sleep(100, 4),
sleep(100, 5),
- }
- local grace = 50
- local delta = timestamp() - start
- assert.is_true(delta <= (100 + grace))
- assert.is_true(delta >= (100 - grace))
- assert.equals(1, one)
- assert.equals("two", two)
- assert.equals("three", three)
- assert.equals(4, four)
- assert.equals(5, five)
- end)
- )
+ })
+ end))
+ local grace = 50
+ local delta = timestamp() - start
+ assert.is_true(delta <= (100 + grace))
+ assert.is_true(delta >= (100 - grace))
+ assert.equals(1, one)
+ assert.equals("two", two)
+ assert.equals("three", three)
+ assert.equals(4, four)
+ assert.equals(5, five)
+ end)
- it(
- "should run all suspending functions concurrently",
- async_test(function()
- local start = timestamp()
- local called = spy.new()
- local function sleep(ms, ret_val)
- return function()
- a.sleep(ms)
- called()
- return ret_val
- end
+ it("should run all suspending functions concurrently", function()
+ local start = timestamp()
+ local called = spy.new()
+ local function sleep(ms, ret_val)
+ return function()
+ a.sleep(ms)
+ called()
+ return ret_val
end
- local first = a.wait_first {
- sleep(150, 1),
- sleep(50, "first"),
- sleep(150, "three"),
- sleep(150, 4),
- sleep(150, 5),
- }
- local grace = 20
- local delta = timestamp() - start
- assert.is_true(delta <= (50 + grace))
- assert.equals("first", first)
- end)
- )
+ end
+ local first = a.run_blocking(a.wait_first, {
+ sleep(150, 1),
+ sleep(50, "first"),
+ sleep(150, "three"),
+ sleep(150, 4),
+ sleep(150, 5),
+ })
+ local grace = 20
+ local delta = timestamp() - start
+ assert.is_true(delta <= (50 + grace))
+ assert.equals("first", first)
+ end)
it("should yield back immediately when not providing any functions", function()
assert.is_nil(a.wait_first {})
diff --git a/tests/mason-core/fetch_spec.lua b/tests/mason-core/fetch_spec.lua
index 107b6417..5a890318 100644
--- a/tests/mason-core/fetch_spec.lua
+++ b/tests/mason-core/fetch_spec.lua
@@ -6,118 +6,115 @@ local stub = require "luassert.stub"
local version = require "mason.version"
describe("fetch", function()
- it(
- "should exhaust all candidates",
- async_test(function()
- stub(spawn, "wget")
- stub(spawn, "curl")
- spawn.wget.returns(Result.failure "wget failure")
- spawn.curl.returns(Result.failure "curl failure")
+ local snapshot
- local result = fetch("https://api.github.com", {
- headers = { ["X-Custom-Header"] = "here" },
- })
- assert.is_true(result:is_failure())
- assert.spy(spawn.wget).was_called(1)
- assert.spy(spawn.curl).was_called(1)
- assert.spy(spawn.wget).was_called_with {
- {
- ("--header=User-Agent: mason.nvim %s (+https://github.com/williamboman/mason.nvim)"):format(
- version.VERSION
- ),
- "--header=X-Custom-Header: here",
- },
- "-nv",
- "-o",
- "/dev/null",
- "-O",
- "-",
- "--timeout=30",
- "--method=GET",
- vim.NIL, -- body-data
- "https://api.github.com",
- }
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
- assert.spy(spawn.curl).was_called_with(match.tbl_containing {
- match.same {
- {
- "-H",
- ("User-Agent: mason.nvim %s (+https://github.com/williamboman/mason.nvim)"):format(
- version.VERSION
- ),
- },
- {
- "-H",
- "X-Custom-Header: here",
- },
- },
- "-fsSL",
- match.same { "-X", "GET" },
- vim.NIL, -- data
- vim.NIL, -- out file
- match.same { "--connect-timeout", 30 },
- "https://api.github.com",
- on_spawn = match.is_function(),
- })
- end)
- )
+ after_each(function()
+ snapshot:revert()
+ end)
- it(
- "should return stdout",
- async_test(function()
- stub(spawn, "wget")
- spawn.wget.returns(Result.success {
- stdout = [[{"data": "here"}]],
- })
- local result = fetch "https://api.github.com/data"
- assert.is_true(result:is_success())
- assert.equals([[{"data": "here"}]], result:get_or_throw())
- end)
- )
+ it("should exhaust all candidates", function()
+ stub(spawn, "wget")
+ stub(spawn, "curl")
+ spawn.wget.returns(Result.failure "wget failure")
+ spawn.curl.returns(Result.failure "curl failure")
- it(
- "should respect out_file opt",
- async_test(function()
- stub(spawn, "wget")
- stub(spawn, "curl")
- spawn.wget.returns(Result.failure "wget failure")
- spawn.curl.returns(Result.failure "curl failure")
- fetch("https://api.github.com/data", { out_file = "/test.json" })
+ local result = fetch("https://api.github.com", {
+ headers = { ["X-Custom-Header"] = "here" },
+ })
+ assert.is_true(result:is_failure())
+ assert.spy(spawn.wget).was_called(1)
+ assert.spy(spawn.curl).was_called(1)
+ assert.spy(spawn.wget).was_called_with {
+ {
+ ("--header=User-Agent: mason.nvim %s (+https://github.com/williamboman/mason.nvim)"):format(
+ version.VERSION
+ ),
+ "--header=X-Custom-Header: here",
+ },
+ "-nv",
+ "-o",
+ "/dev/null",
+ "-O",
+ "-",
+ "--timeout=30",
+ "--method=GET",
+ vim.NIL, -- body-data
+ "https://api.github.com",
+ }
- assert.spy(spawn.wget).was_called_with {
+ assert.spy(spawn.curl).was_called_with(match.tbl_containing {
+ match.same {
+ {
+ "-H",
+ ("User-Agent: mason.nvim %s (+https://github.com/williamboman/mason.nvim)"):format(version.VERSION),
+ },
{
- ("--header=User-Agent: mason.nvim %s (+https://github.com/williamboman/mason.nvim)"):format(
- version.VERSION
- ),
+ "-H",
+ "X-Custom-Header: here",
},
- "-nv",
- "-o",
- "/dev/null",
- "-O",
- "/test.json",
- "--timeout=30",
- "--method=GET",
- vim.NIL, -- body-data
- "https://api.github.com/data",
- }
+ },
+ "-fsSL",
+ match.same { "-X", "GET" },
+ vim.NIL, -- data
+ vim.NIL, -- out file
+ match.same { "--connect-timeout", 30 },
+ "https://api.github.com",
+ on_spawn = match.is_function(),
+ })
+ end)
- assert.spy(spawn.curl).was_called_with(match.tbl_containing {
- match.same {
- {
- "-H",
- ("User-Agent: mason.nvim %s (+https://github.com/williamboman/mason.nvim)"):format(
- version.VERSION
- ),
- },
+ it("should return stdout", function()
+ stub(spawn, "curl")
+ spawn.curl.returns(Result.success {
+ stdout = [[{"data": "here"}]],
+ })
+ local result = fetch "https://api.github.com/data"
+ assert.is_true(result:is_success())
+ assert.equals([[{"data": "here"}]], result:get_or_throw())
+ end)
+
+ it("should respect out_file opt", function()
+ stub(spawn, "wget")
+ stub(spawn, "curl")
+ spawn.wget.returns(Result.failure "wget failure")
+ spawn.curl.returns(Result.failure "curl failure")
+ fetch("https://api.github.com/data", { out_file = "/test.json" })
+
+ assert.spy(spawn.wget).was_called_with {
+ {
+ ("--header=User-Agent: mason.nvim %s (+https://github.com/williamboman/mason.nvim)"):format(
+ version.VERSION
+ ),
+ },
+ "-nv",
+ "-o",
+ "/dev/null",
+ "-O",
+ "/test.json",
+ "--timeout=30",
+ "--method=GET",
+ vim.NIL, -- body-data
+ "https://api.github.com/data",
+ }
+
+ assert.spy(spawn.curl).was_called_with(match.tbl_containing {
+ match.same {
+ {
+ "-H",
+ ("User-Agent: mason.nvim %s (+https://github.com/williamboman/mason.nvim)"):format(version.VERSION),
},
- "-fsSL",
- match.same { "-X", "GET" },
- vim.NIL, -- data
- match.same { "-o", "/test.json" },
- match.same { "--connect-timeout", 30 },
- "https://api.github.com/data",
- on_spawn = match.is_function(),
- })
- end)
- )
+ },
+ "-fsSL",
+ match.same { "-X", "GET" },
+ vim.NIL, -- data
+ match.same { "-o", "/test.json" },
+ match.same { "--connect-timeout", 30 },
+ "https://api.github.com/data",
+ on_spawn = match.is_function(),
+ })
+ end)
end)
diff --git a/tests/mason-core/fs_spec.lua b/tests/mason-core/fs_spec.lua
index 38f97eeb..bd3696da 100644
--- a/tests/mason-core/fs_spec.lua
+++ b/tests/mason-core/fs_spec.lua
@@ -8,17 +8,14 @@ describe("fs", function()
}
end)
- it(
- "refuses to rmrf paths outside of boundary",
- async_test(function()
- local e = assert.has_error(function()
- fs.async.rmrf "/thisisa/path"
- end)
-
- assert.equals(
- [[Refusing to rmrf "/thisisa/path" which is outside of the allowed boundary "/foo". Please report this error at https://github.com/williamboman/mason.nvim/issues/new]],
- e
- )
+ it("refuses to rmrf paths outside of boundary", function()
+ local e = assert.has_error(function()
+ fs.sync.rmrf "/thisisa/path"
end)
- )
+
+ assert.equals(
+ [[Refusing to rmrf "/thisisa/path" which is outside of the allowed boundary "/foo". Please report this error at https://github.com/williamboman/mason.nvim/issues/new]],
+ e
+ )
+ end)
end)
diff --git a/tests/mason-core/installer/context_spec.lua b/tests/mason-core/installer/context_spec.lua
index 646f7e30..9c1805cb 100644
--- a/tests/mason-core/installer/context_spec.lua
+++ b/tests/mason-core/installer/context_spec.lua
@@ -3,10 +3,20 @@ local path = require "mason-core.path"
local pypi = require "mason-core.installer.managers.pypi"
local registry = require "mason-registry"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
describe("installer", function()
---@module "mason-core.platform"
local platform
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
before_each(function()
package.loaded["mason-core.installer.platform"] = nil
@@ -15,8 +25,7 @@ describe("installer", function()
end)
it("should write shell exec wrapper on Unix", function()
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "write_file")
stub(ctx.fs, "file_exists")
stub(ctx.fs, "dir_exists")
@@ -44,8 +53,7 @@ exec bash -c 'echo $GREETING' "$@"]]
platform.is.unix = false
platform.is.linux = false
platform.is.win = true
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "write_file")
stub(ctx.fs, "file_exists")
stub(ctx.fs, "dir_exists")
@@ -68,8 +76,7 @@ cmd.exe /C echo %GREETING% %*]]
it("should not write shell exec wrapper if new executable path already exists", function()
local exec_rel_path = path.concat { "obscure", "path", "to", "server" }
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
stub(ctx.fs, "dir_exists")
ctx.fs.file_exists.on_call_with(match.is_ref(ctx.fs), exec_rel_path).returns(true)
@@ -86,8 +93,7 @@ cmd.exe /C echo %GREETING% %*]]
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)
+ local ctx = test_helpers.create_context()
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)
@@ -105,8 +111,7 @@ cmd.exe /C echo %GREETING% %*]]
it("should write Ruby 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)
+ local ctx = test_helpers.create_context()
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)
@@ -123,8 +128,7 @@ cmd.exe /C echo %GREETING% %*]]
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)
+ local ctx = test_helpers.create_context()
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)
@@ -142,8 +146,7 @@ cmd.exe /C echo %GREETING% %*]]
it("should write Python exec wrapper", function()
local dummy = registry.get_package "dummy"
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
+ local ctx = test_helpers.create_context()
stub(ctx.cwd, "get")
ctx.cwd.get.returns "/tmp/placeholder"
stub(ctx, "write_shell_exec_wrapper")
@@ -159,8 +162,7 @@ cmd.exe /C echo %GREETING% %*]]
end)
it("should not write Python exec wrapper if module cannot be found", function()
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
+ local ctx = test_helpers.create_context()
stub(ctx.cwd, "get")
ctx.cwd.get.returns "/tmp/placeholder"
stub(ctx, "write_shell_exec_wrapper")
@@ -181,8 +183,7 @@ cmd.exe /C echo %GREETING% %*]]
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)
+ local ctx = test_helpers.create_context()
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)
@@ -201,8 +202,7 @@ cmd.exe /C echo %GREETING% %*]]
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)
+ local ctx = test_helpers.create_context()
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)
@@ -218,8 +218,7 @@ cmd.exe /C echo %GREETING% %*]]
it("should write PHP exec wrapper", function()
local php_rel_path = path.concat { "some", "obscure", "path", "cli.php" }
local dummy = registry.get_package "dummy"
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
+ local ctx = test_helpers.create_context()
stub(ctx, "write_shell_exec_wrapper")
stub(ctx.fs, "file_exists")
ctx.fs.file_exists.on_call_with(match.is_ref(ctx.fs), php_rel_path).returns(true)
@@ -236,8 +235,7 @@ cmd.exe /C echo %GREETING% %*]]
it("should not write PHP exec wrapper if the target script doesn't exist", function()
local php_rel_path = path.concat { "some", "obscure", "path", "cli.php" }
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
+ local ctx = test_helpers.create_context()
stub(ctx, "write_shell_exec_wrapper")
stub(ctx.fs, "file_exists")
ctx.fs.file_exists.on_call_with(match.is_ref(ctx.fs), php_rel_path).returns(false)
diff --git a/tests/mason-core/installer/handle_spec.lua b/tests/mason-core/installer/handle_spec.lua
index c301b28b..66a9a5c4 100644
--- a/tests/mason-core/installer/handle_spec.lua
+++ b/tests/mason-core/installer/handle_spec.lua
@@ -4,6 +4,16 @@ local spy = require "luassert.spy"
local stub = require "luassert.stub"
describe("installer handle", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should register spawn handle", function()
local handle = InstallHandle.new(mock.new {})
local spawn_handle_change_handler = spy.new()
@@ -72,36 +82,33 @@ describe("installer handle", function()
assert.spy(kill_handler).was_called_with(9)
end)
- it(
- "should terminate handle",
- async_test(function()
- local process = require "mason-core.process"
- stub(process, "kill")
- local uv_handle1 = {}
- local uv_handle2 = {}
- local handle = InstallHandle.new(mock.new {})
- local kill_handler = spy.new()
- local terminate_handler = spy.new()
- local closed_handler = spy.new()
+ it("should terminate handle", function()
+ local process = require "mason-core.process"
+ stub(process, "kill")
+ local uv_handle1 = {}
+ local uv_handle2 = {}
+ local handle = InstallHandle.new(mock.new {})
+ local kill_handler = spy.new()
+ local terminate_handler = spy.new()
+ local closed_handler = spy.new()
- handle:once("kill", kill_handler)
- handle:once("terminate", terminate_handler)
- handle:once("closed", closed_handler)
- handle.state = "ACTIVE"
- handle.spawn_handles = { { uv_handle = uv_handle2 }, { uv_handle = uv_handle2 } }
- handle:terminate()
+ handle:once("kill", kill_handler)
+ handle:once("terminate", terminate_handler)
+ handle:once("closed", closed_handler)
+ handle.state = "ACTIVE"
+ handle.spawn_handles = { { uv_handle = uv_handle2 }, { uv_handle = uv_handle2 } }
+ handle:terminate()
- assert.spy(process.kill).was_called(2)
- assert.spy(process.kill).was_called_with(uv_handle1, 15)
- assert.spy(process.kill).was_called_with(uv_handle2, 15)
- assert.spy(kill_handler).was_called(1)
- assert.spy(kill_handler).was_called_with(15)
- assert.spy(terminate_handler).was_called(1)
- assert.is_true(handle.is_terminated)
- assert.wait_for(function()
- assert.is_true(handle:is_closed())
- assert.spy(closed_handler).was_called(1)
- end)
+ assert.spy(process.kill).was_called(2)
+ assert.spy(process.kill).was_called_with(uv_handle1, 15)
+ assert.spy(process.kill).was_called_with(uv_handle2, 15)
+ assert.spy(kill_handler).was_called(1)
+ assert.spy(kill_handler).was_called_with(15)
+ assert.spy(terminate_handler).was_called(1)
+ assert.is_true(handle.is_terminated)
+ assert.wait(function()
+ assert.is_true(handle:is_closed())
+ assert.spy(closed_handler).was_called(1)
end)
- )
+ end)
end)
diff --git a/tests/mason-core/installer/installer_spec.lua b/tests/mason-core/installer/installer_spec.lua
deleted file mode 100644
index 3e291308..00000000
--- a/tests/mason-core/installer/installer_spec.lua
+++ /dev/null
@@ -1,217 +0,0 @@
-local InstallContext = require "mason-core.installer.context"
-local Result = require "mason-core.result"
-local a = require "mason-core.async"
-local fs = require "mason-core.fs"
-local installer = require "mason-core.installer"
-local match = require "luassert.match"
-local path = require "mason-core.path"
-local spy = require "luassert.spy"
-local stub = require "luassert.stub"
-
-local function timestamp()
- local seconds, microseconds = vim.loop.gettimeofday()
- return (seconds * 1000) + math.floor(microseconds / 1000)
-end
-
-describe("installer", function()
- before_each(function()
- package.loaded["dummy_package"] = nil
- end)
-
- it(
- "should call installer",
- async_test(function()
- spy.on(fs.async, "mkdirp")
- spy.on(fs.async, "rename")
-
- local handle = InstallHandleGenerator "dummy"
- spy.on(handle.package.spec.source, "install")
- local result = installer.execute(handle, {})
-
- assert.is_nil(result:err_or_nil())
- assert.spy(handle.package.spec.source.install).was_called(1)
- assert
- .spy(handle.package.spec.source.install)
- .was_called_with(match.instanceof(InstallContext), match.is_table())
- assert.spy(fs.async.mkdirp).was_called_with(path.package_build_prefix "dummy")
- assert.spy(fs.async.rename).was_called_with(path.package_build_prefix "dummy", path.package_prefix "dummy")
- end)
- )
-
- it(
- "should return failure if installer errors",
- async_test(function()
- spy.on(fs.async, "rmrf")
- spy.on(fs.async, "rename")
- local installer_fn = spy.new(function()
- error("something went wrong. don't try again.", 0)
- end)
- local handler = InstallHandleGenerator "dummy"
- stub(handler.package.spec.source, "install", installer_fn)
- local result = installer.execute(handler, {})
- assert.spy(installer_fn).was_called(1)
- assert.is_true(result:is_failure())
- assert.equals("something went wrong. don't try again.", result:err_or_nil())
- assert.spy(fs.async.rmrf).was_called_with(path.package_build_prefix "dummy")
- assert.spy(fs.async.rename).was_not_called()
- end)
- )
-
- it(
- "should write receipt",
- async_test(function()
- spy.on(fs.async, "write_file")
- local handle = InstallHandleGenerator "dummy"
- stub(handle.package.spec.source, "install", function(ctx)
- ctx.fs:write_file("target", "")
- ctx.fs:write_file("file.jar", "")
- ctx.fs:write_file("opt-cmd", "")
- end)
- handle.package.spec.bin = {
- ["executable"] = "target",
- }
- handle.package.spec.share = {
- ["package/file.jar"] = "file.jar",
- }
- handle.package.spec.opt = {
- ["package/bin/opt-cmd"] = "opt-cmd",
- }
- installer.execute(handle, {})
- handle.package.spec.bin = {}
- handle.package.spec.share = {}
- handle.package.spec.opt = {}
- assert.spy(fs.async.write_file).was_called_with(
- ("%s/mason-receipt.json"):format(handle.package:get_install_path()),
- match.capture(function(arg)
- ---@type InstallReceipt
- local receipt = vim.json.decode(arg)
- assert.is_true(match.tbl_containing {
- name = "dummy",
- source = match.same {
- type = handle.package.spec.schema,
- id = handle.package.spec.source.id,
- },
- schema_version = "1.2",
- metrics = match.is_table(),
- links = match.same {
- bin = { executable = "target" },
- share = { ["package/file.jar"] = "file.jar" },
- opt = { ["package/bin/opt-cmd"] = "opt-cmd" },
- },
- }(receipt))
- end)
- )
- end)
- )
-
- it(
- "should run async functions concurrently",
- async_test(function()
- spy.on(fs.async, "write_file")
- local capture = spy.new()
- local start = timestamp()
- local handle = InstallHandleGenerator "dummy"
- stub(handle.package.spec.source, "install", function(ctx)
- capture(installer.run_concurrently {
- function()
- a.sleep(100)
- return installer.context()
- end,
- function()
- a.sleep(100)
- return "two"
- end,
- function()
- a.sleep(100)
- return "three"
- end,
- })
- end)
- installer.execute(handle, {})
- local stop = timestamp()
- local grace_ms = 25
- assert.is_true((stop - start) >= (100 - grace_ms))
- assert.spy(capture).was_called_with(match.instanceof(InstallContext), "two", "three")
- end)
- )
-
- it(
- "should write log files if debug is true",
- async_test(function()
- spy.on(fs.async, "write_file")
- local handle = InstallHandleGenerator "dummy"
- stub(handle.package.spec.source, "install", function(ctx)
- ctx.stdio_sink.stdout "Hello stdout!\n"
- ctx.stdio_sink.stderr "Hello "
- ctx.stdio_sink.stderr "stderr!"
- end)
- installer.execute(handle, { debug = true })
- assert
- .spy(fs.async.write_file)
- .was_called_with(path.package_prefix "dummy/mason-debug.log", "Hello stdout!\nHello stderr!")
- end)
- )
-
- it(
- "should raise spawn errors in strict mode",
- async_test(function()
- local handle = InstallHandleGenerator "dummy"
- stub(handle.package.spec.source, "install", function(ctx)
- ctx.spawn.bash { "-c", "exit 42" }
- end)
- local result = installer.execute(handle, { debug = true })
- assert.same(
- Result.failure {
- exit_code = 42,
- signal = 0,
- },
- result
- )
- assert.equals("spawn: bash failed with exit code 42 and signal 0. ", tostring(result:err_or_nil()))
- end)
- )
-
- it(
- "should lock package",
- async_test(function()
- local handle = InstallHandleGenerator "dummy"
- local callback = spy.new()
- stub(handle.package.spec.source, "install", function()
- a.sleep(3000)
- end)
-
- a.run(function()
- return installer.execute(handle, { debug = true })
- end, callback)
-
- assert.wait_for(function()
- assert.is_true(fs.sync.file_exists(path.package_lock "dummy"))
- end)
- handle:terminate()
- assert.wait_for(function()
- assert.spy(callback).was_called(1)
- end)
- assert.is_false(fs.sync.file_exists(path.package_lock "dummy"))
- end)
- )
-
- it(
- "should not run installer if package lock exists",
- async_test(function()
- local handle = InstallHandleGenerator "dummy"
- local install = spy.new()
- stub(handle.package.spec.source, "install", install)
-
- fs.sync.write_file(path.package_lock "dummy", "dummypid")
- local result = installer.execute(handle, { debug = true })
- assert.is_true(fs.sync.file_exists(path.package_lock "dummy"))
- fs.sync.unlink(path.package_lock "dummy")
-
- assert.spy(install).was_not_called()
- assert.equals(
- "Lockfile exists, installation is already running in another process (pid: dummypid). Run with :MasonInstall --force to bypass.",
- result:err_or_nil()
- )
- end)
- )
-end)
diff --git a/tests/mason-core/installer/linker_spec.lua b/tests/mason-core/installer/linker_spec.lua
index 8bcf2607..9684f57d 100644
--- a/tests/mason-core/installer/linker_spec.lua
+++ b/tests/mason-core/installer/linker_spec.lua
@@ -1,7 +1,9 @@
+local a = require "mason-core.async"
local fs = require "mason-core.fs"
local path = require "mason-core.path"
local registry = require "mason-registry"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
local WIN_CMD_SCRIPT = [[@ECHO off
GOTO start
@@ -15,6 +17,16 @@ CALL :find_dp0
endLocal & goto #_undefined_# 2>NUL || title %%COMSPEC%% & "%s" %%*]]
describe("linker", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
---@module "mason-core.installer.linker"
local linker
---@module "mason-core.platform"
@@ -27,130 +39,119 @@ describe("linker", function()
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")
+ it("should symlink executable on Unix", 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)
+ 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")
- assert.is_true(linker.link(ctx):is_success())
+ local ctx = test_helpers.create_context()
+ ctx:link_bin("my-executable", path.concat { "nested", "path", "my-executable" })
+ ctx:link_bin("another-executable", "another-executable")
+ local result = a.run_blocking(linker.link, ctx)
+ assert.is_true(result:is_success())
- 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(path.concat { dummy:get_install_path(), "another-executable" }, path.bin_prefix "another-executable")
- assert.spy(fs.async.symlink).was_called_with(
+ 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(path.concat { dummy:get_install_path(), "another-executable" }, path.bin_prefix "another-executable")
+ assert
+ .spy(fs.async.symlink)
+ .was_called_with(
path.concat { dummy:get_install_path(), "nested", "path", "my-executable" },
path.bin_prefix "my-executable"
)
- end)
- )
+ end)
- it(
- "should write executable wrapper on Windows",
- async_test(function()
- platform.is.darwin = false
- platform.is.mac = false
- platform.is.linux = false
- platform.is.unix = false
- platform.is.win = true
+ it("should write executable wrapper on Windows", function()
+ platform.is.darwin = false
+ 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")
+ 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)
+ 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")
- assert.is_true(linker.link(ctx):is_success())
+ local ctx = test_helpers.create_context()
+ ctx:link_bin("my-executable", path.concat { "nested", "path", "my-executable" })
+ ctx:link_bin("another-executable", "another-executable")
- 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(path.concat { dummy:get_install_path(), "another-executable" })
- )
- assert.spy(fs.async.write_file).was_called_with(
- path.bin_prefix "my-executable.cmd",
- WIN_CMD_SCRIPT:format(path.concat { dummy:get_install_path(), "nested", "path", "my-executable" })
- )
- end)
- )
+ local result = a.run_blocking(linker.link, ctx)
+ assert.is_true(result:is_success())
- it(
- "should symlink share files",
- async_test(function()
- local dummy = registry.get_package "dummy"
- stub(fs.async, "mkdirp")
- stub(fs.async, "dir_exists")
- stub(fs.async, "file_exists")
- stub(fs.async, "symlink")
- stub(fs.async, "write_file")
+ 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(path.concat { dummy:get_install_path(), "another-executable" })
+ )
+ assert.spy(fs.async.write_file).was_called_with(
+ path.bin_prefix "my-executable.cmd",
+ WIN_CMD_SCRIPT:format(path.concat { dummy:get_install_path(), "nested", "path", "my-executable" })
+ )
+ end)
- -- mock non-existent dest files
- fs.async.file_exists.on_call_with(path.share_prefix "share-file").returns(false)
- fs.async.file_exists.on_call_with(path.share_prefix(path.concat { "nested", "share-file" })).returns(false)
+ it("should symlink share files", function()
+ local dummy = registry.get_package "dummy"
+ stub(fs.async, "mkdirp")
+ stub(fs.async, "dir_exists")
+ stub(fs.async, "file_exists")
+ stub(fs.async, "symlink")
+ stub(fs.async, "write_file")
- fs.async.dir_exists.on_call_with(path.share_prefix()).returns(false)
- fs.async.dir_exists.on_call_with(path.share_prefix "nested/path").returns(false)
+ -- mock non-existent dest files
+ fs.async.file_exists.on_call_with(path.share_prefix "share-file").returns(false)
+ fs.async.file_exists.on_call_with(path.share_prefix(path.concat { "nested", "share-file" })).returns(false)
- -- mock existent source files
- fs.async.file_exists.on_call_with(path.concat { dummy:get_install_path(), "share-file" }).returns(true)
- fs.async.file_exists
- .on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" })
- .returns(true)
+ fs.async.dir_exists.on_call_with(path.share_prefix()).returns(false)
+ fs.async.dir_exists.on_call_with(path.share_prefix "nested/path").returns(false)
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
- ctx.links.share["nested/path/share-file"] = path.concat { "nested", "path", "to", "share-file" }
- ctx.links.share["share-file"] = "share-file"
+ -- mock existent source files
+ fs.async.file_exists.on_call_with(path.concat { dummy:get_install_path(), "share-file" }).returns(true)
+ fs.async.file_exists
+ .on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" })
+ .returns(true)
- local result = linker.link(ctx)
+ local ctx = test_helpers.create_context()
+ ctx.links.share["nested/path/share-file"] = path.concat { "nested", "path", "to", "share-file" }
+ ctx.links.share["share-file"] = "share-file"
- assert.is_true(result:is_success())
+ local result = a.run_blocking(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(path.concat { dummy:get_install_path(), "share-file" }, path.share_prefix "share-file")
- assert.spy(fs.async.symlink).was_called_with(
- path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" },
- path.share_prefix "nested/path/share-file"
- )
+ assert.is_true(result:is_success())
- assert.spy(fs.async.mkdirp).was_called(2)
- assert.spy(fs.async.mkdirp).was_called_with(path.share_prefix())
- assert.spy(fs.async.mkdirp).was_called_with(path.share_prefix "nested/path")
- end)
- )
+ 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(path.concat { dummy:get_install_path(), "share-file" }, path.share_prefix "share-file")
+ assert.spy(fs.async.symlink).was_called_with(
+ path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" },
+ path.share_prefix "nested/path/share-file"
+ )
+
+ assert.spy(fs.async.mkdirp).was_called(2)
+ assert.spy(fs.async.mkdirp).was_called_with(path.share_prefix())
+ assert.spy(fs.async.mkdirp).was_called_with(path.share_prefix "nested/path")
+ end)
it("should copy share files on Windows", function()
platform.is.darwin = false
@@ -178,8 +179,7 @@ describe("linker", function()
.on_call_with(path.concat { dummy:get_install_path(), "nested", "path", "to", "share-file" })
.returns(true)
- local handle = InstallHandleGenerator "dummy"
- local ctx = InstallContextGenerator(handle)
+ local ctx = test_helpers.create_context()
ctx.links.share["nested/path/share-file"] = path.concat { "nested", "path", "to", "share-file" }
ctx.links.share["share-file"] = "share-file"
diff --git a/tests/mason-core/installer/managers/cargo_spec.lua b/tests/mason-core/installer/managers/cargo_spec.lua
index 475c2c86..bc5c5f21 100644
--- a/tests/mason-core/installer/managers/cargo_spec.lua
+++ b/tests/mason-core/installer/managers/cargo_spec.lua
@@ -1,12 +1,12 @@
local cargo = require "mason-core.installer.managers.cargo"
-local installer = require "mason-core.installer"
local spy = require "luassert.spy"
+local test_helpers = require "mason-test.helpers"
describe("cargo manager", function()
it("should install", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
cargo.install("my-crate", "1.0.0")
end)
@@ -23,10 +23,10 @@ describe("cargo manager", function()
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
cargo.install("my-crate", "1.0.0")
end)
@@ -34,8 +34,8 @@ describe("cargo manager", function()
end)
it("should install locked", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
cargo.install("my-crate", "1.0.0", {
locked = true,
})
@@ -54,8 +54,8 @@ describe("cargo manager", function()
end)
it("should install provided features", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
cargo.install("my-crate", "1.0.0", {
features = "lsp,cli",
})
@@ -74,8 +74,8 @@ describe("cargo manager", function()
end)
it("should install git tag source", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
cargo.install("my-crate", "1.0.0", {
git = {
url = "https://github.com/neovim/neovim",
@@ -96,8 +96,8 @@ describe("cargo manager", function()
end)
it("should install git rev source", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
cargo.install("my-crate", "16dfc89abd413c391e5b63ae5d132c22843ce9a7", {
git = {
url = "https://github.com/neovim/neovim",
diff --git a/tests/mason-core/installer/managers/common_spec.lua b/tests/mason-core/installer/managers/common_spec.lua
index e72d7697..16d3ba52 100644
--- a/tests/mason-core/installer/managers/common_spec.lua
+++ b/tests/mason-core/installer/managers/common_spec.lua
@@ -7,6 +7,7 @@ local mock = require "luassert.mock"
local spy = require "luassert.spy"
local std = require "mason-core.installer.managers.std"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
describe("common manager :: download", function()
it("should parse download files from common structure", function()
@@ -54,11 +55,11 @@ describe("common manager :: download", function()
end)
it("should download files", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(std, "download_file", mockx.returns(Result.success()))
stub(std, "unpack", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return common.download_files(ctx, {
{ out_file = "file.jar", download_url = "https://example.com/file.jar" },
{ out_file = "LICENSE.md", download_url = "https://example.com/LICENSE" },
@@ -75,12 +76,12 @@ describe("common manager :: download", function()
end)
it("should download files to specified directory", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(std, "download_file", mockx.returns(Result.success()))
stub(std, "unpack", mockx.returns(Result.success()))
stub(ctx.fs, "mkdirp")
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return common.download_files(ctx, {
{ out_file = "lib/file.jar", download_url = "https://example.com/file.jar" },
{ out_file = "doc/LICENSE.md", download_url = "https://example.com/LICENSE" },
@@ -99,7 +100,7 @@ end)
describe("common manager :: build", function()
it("should run build instruction", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local uv = require "mason-core.async.uv"
spy.on(ctx, "promote_cwd")
stub(uv, "write")
@@ -115,7 +116,7 @@ describe("common manager :: build", function()
end
)
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return common.run_build_instruction {
run = [[npm install && npm run compile]],
env = {
@@ -143,11 +144,11 @@ describe("common manager :: build", function()
end)
it("should promote cwd if not staged", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx, "promote_cwd")
stub(ctx.spawn, "bash", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return common.run_build_instruction {
run = "make",
staged = false,
diff --git a/tests/mason-core/installer/managers/composer_spec.lua b/tests/mason-core/installer/managers/composer_spec.lua
index a8ccaffb..f3887c68 100644
--- a/tests/mason-core/installer/managers/composer_spec.lua
+++ b/tests/mason-core/installer/managers/composer_spec.lua
@@ -1,11 +1,11 @@
local composer = require "mason-core.installer.managers.composer"
-local installer = require "mason-core.installer"
local spy = require "luassert.spy"
+local test_helpers = require "mason-test.helpers"
describe("composer manager", function()
it("should install", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
composer.install("my-package", "1.0.0")
end)
@@ -22,10 +22,10 @@ describe("composer manager", function()
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
composer.install("my-package", "1.0.0")
end)
diff --git a/tests/mason-core/installer/managers/gem_spec.lua b/tests/mason-core/installer/managers/gem_spec.lua
index 7ac8c33e..83b8d96a 100644
--- a/tests/mason-core/installer/managers/gem_spec.lua
+++ b/tests/mason-core/installer/managers/gem_spec.lua
@@ -1,13 +1,15 @@
local gem = require "mason-core.installer.managers.gem"
-local installer = require "mason-core.installer"
local spy = require "luassert.spy"
+local test_helper = require "mason-test.helpers"
describe("gem manager", function()
it("should install", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
- gem.install("my-gem", "1.0.0")
+ local ctx = test_helper.create_context()
+
+ local result = ctx:execute(function()
+ return gem.install("my-gem", "1.0.0")
end)
+ assert.is_true(result:is_success())
assert.spy(ctx.spawn.gem).was_called(1)
assert.spy(ctx.spawn.gem).was_called_with {
@@ -20,15 +22,15 @@ describe("gem manager", function()
"my-gem:1.0.0",
vim.NIL, -- extra_packages
env = {
- GEM_HOME = ctx.cwd:get(),
+ GEM_HOME = "/tmp/install-dir",
},
}
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helper.create_context()
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
gem.install("my-gem", "1.0.0")
end)
@@ -36,8 +38,8 @@ describe("gem manager", function()
end)
it("should install extra packages", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helper.create_context()
+ ctx:execute(function()
gem.install("my-gem", "1.0.0", {
extra_packages = { "extra-gem" },
})
diff --git a/tests/mason-core/installer/managers/golang_spec.lua b/tests/mason-core/installer/managers/golang_spec.lua
index 58e4c4b8..e1a99cbd 100644
--- a/tests/mason-core/installer/managers/golang_spec.lua
+++ b/tests/mason-core/installer/managers/golang_spec.lua
@@ -1,12 +1,12 @@
local golang = require "mason-core.installer.managers.golang"
-local installer = require "mason-core.installer"
local spy = require "luassert.spy"
+local test_helpers = require "mason-test.helpers"
describe("golang manager", function()
it("should install", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
golang.install("my-golang", "1.0.0")
end)
@@ -22,10 +22,10 @@ describe("golang manager", function()
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
golang.install("my-golang", "1.0.0")
end)
@@ -33,8 +33,8 @@ describe("golang manager", function()
end)
it("should install extra packages", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
golang.install("my-golang", "1.0.0", {
extra_packages = { "extra", "package" },
})
diff --git a/tests/mason-core/installer/managers/luarocks_spec.lua b/tests/mason-core/installer/managers/luarocks_spec.lua
index 3be963a8..406c5c51 100644
--- a/tests/mason-core/installer/managers/luarocks_spec.lua
+++ b/tests/mason-core/installer/managers/luarocks_spec.lua
@@ -1,13 +1,23 @@
-local installer = require "mason-core.installer"
local luarocks = require "mason-core.installer.managers.luarocks"
local spy = require "luassert.spy"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
describe("luarocks manager", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx, "promote_cwd")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
luarocks.install("my-rock", "1.0.0")
end)
@@ -23,9 +33,9 @@ describe("luarocks manager", function()
end)
it("should install dev mode", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx, "promote_cwd")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
luarocks.install("my-rock", "1.0.0", {
dev = true,
})
@@ -43,9 +53,9 @@ describe("luarocks manager", function()
end)
it("should install using provided server", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx, "promote_cwd")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
luarocks.install("my-rock", "1.0.0", {
server = "https://luarocks.org/dev",
})
@@ -63,10 +73,10 @@ describe("luarocks manager", function()
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx, "promote_cwd")
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
luarocks.install("my-rock", "1.0.0")
end)
diff --git a/tests/mason-core/installer/managers/npm_spec.lua b/tests/mason-core/installer/managers/npm_spec.lua
index 59a8c84f..b2fabc80 100644
--- a/tests/mason-core/installer/managers/npm_spec.lua
+++ b/tests/mason-core/installer/managers/npm_spec.lua
@@ -1,21 +1,31 @@
local Result = require "mason-core.result"
-local installer = require "mason-core.installer"
local match = require "luassert.match"
local npm = require "mason-core.installer.managers.npm"
local spawn = require "mason-core.spawn"
local spy = require "luassert.spy"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
describe("npm manager", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should init package.json", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "append_file")
stub(spawn, "npm")
spawn.npm.returns(Result.success {})
spawn.npm.on_call_with({ "version", "--json" }).returns(Result.success {
stdout = [[ { "npm": "8.1.0" } ]],
})
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
npm.init()
end)
@@ -30,14 +40,14 @@ describe("npm manager", function()
end)
it("should use install-strategy on npm >= 9", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "append_file")
stub(spawn, "npm")
spawn.npm.returns(Result.success {})
spawn.npm.on_call_with({ "version", "--json" }).returns(Result.success {
stdout = [[ { "npm": "9.1.0" } ]],
})
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
npm.init()
end)
@@ -51,8 +61,8 @@ describe("npm manager", function()
end)
it("should install", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
npm.install("my-package", "1.0.0")
end)
@@ -65,8 +75,8 @@ describe("npm manager", function()
end)
it("should install extra packages", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
npm.install("my-package", "1.0.0", {
extra_packages = { "extra-package" },
})
@@ -81,10 +91,10 @@ describe("npm manager", function()
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
npm.install("my-package", "1.0.0")
end)
diff --git a/tests/mason-core/installer/managers/nuget_spec.lua b/tests/mason-core/installer/managers/nuget_spec.lua
index 8d4b0e87..fdfbdc82 100644
--- a/tests/mason-core/installer/managers/nuget_spec.lua
+++ b/tests/mason-core/installer/managers/nuget_spec.lua
@@ -1,11 +1,11 @@
-local installer = require "mason-core.installer"
local nuget = require "mason-core.installer.managers.nuget"
local spy = require "luassert.spy"
+local test_helpers = require "mason-test.helpers"
describe("nuget manager", function()
it("should install", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
nuget.install("nuget-package", "1.0.0")
end)
@@ -21,10 +21,10 @@ describe("nuget manager", function()
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
nuget.install("nuget-package", "1.0.0")
end)
diff --git a/tests/mason-core/installer/managers/opam_spec.lua b/tests/mason-core/installer/managers/opam_spec.lua
index cc552114..51f116e8 100644
--- a/tests/mason-core/installer/managers/opam_spec.lua
+++ b/tests/mason-core/installer/managers/opam_spec.lua
@@ -1,12 +1,12 @@
-local installer = require "mason-core.installer"
local opam = require "mason-core.installer.managers.opam"
local spy = require "luassert.spy"
+local test_helpers = require "mason-test.helpers"
describe("opam manager", function()
it("should install", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
opam.install("opam-package", "1.0.0")
end)
@@ -21,10 +21,10 @@ describe("opam manager", function()
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
opam.install("opam-package", "1.0.0")
end)
diff --git a/tests/mason-core/installer/managers/powershell_spec.lua b/tests/mason-core/installer/managers/powershell_spec.lua
index 86bbe1f9..14478305 100644
--- a/tests/mason-core/installer/managers/powershell_spec.lua
+++ b/tests/mason-core/installer/managers/powershell_spec.lua
@@ -1,3 +1,4 @@
+local a = require "mason-core.async"
local match = require "luassert.match"
local mock = require "luassert.mock"
local spawn = require "mason-core.spawn"
@@ -5,6 +6,16 @@ local spy = require "luassert.spy"
local stub = require "luassert.stub"
describe("powershell manager", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
local function powershell()
package.loaded["mason-core.installer.managers.powershell"] = nil
return require "mason-core.installer.managers.powershell"
@@ -22,21 +33,18 @@ describe("powershell manager", function()
assert.spy(spawn.powershell).was_called(0)
end)
- it(
- "should use powershell if pwsh is not available",
- async_test(function()
- stub(spawn, "pwsh", function() end)
- stub(spawn, "powershell", function() end)
- stub(vim.fn, "executable")
- vim.fn.executable.on_call_with("pwsh").returns(0)
+ it("should use powershell if pwsh is not available", function()
+ stub(spawn, "pwsh", function() end)
+ stub(spawn, "powershell", function() end)
+ stub(vim.fn, "executable")
+ vim.fn.executable.on_call_with("pwsh").returns(0)
- local powershell = powershell()
- powershell.command "echo 'Is this bash?'"
+ local powershell = powershell()
+ a.run_blocking(powershell.command, "echo 'Is this bash?'")
- assert.spy(spawn.pwsh).was_called(0)
- assert.spy(spawn.powershell).was_called(1)
- end)
- )
+ assert.spy(spawn.pwsh).was_called(0)
+ assert.spy(spawn.powershell).was_called(1)
+ end)
it("should use the provided spawner for commands", function()
spy.on(spawn, "pwsh")
diff --git a/tests/mason-core/installer/managers/pypi_spec.lua b/tests/mason-core/installer/managers/pypi_spec.lua
index 6689e350..f3a7e429 100644
--- a/tests/mason-core/installer/managers/pypi_spec.lua
+++ b/tests/mason-core/installer/managers/pypi_spec.lua
@@ -1,5 +1,4 @@
local Result = require "mason-core.result"
-local installer = require "mason-core.installer"
local match = require "luassert.match"
local path = require "mason-core.path"
local providers = require "mason-core.providers"
@@ -7,6 +6,7 @@ local pypi = require "mason-core.installer.managers.pypi"
local spawn = require "mason-core.spawn"
local spy = require "luassert.spy"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param ctx InstallContext
local function venv_py(ctx)
@@ -19,17 +19,24 @@ local function venv_py(ctx)
end
describe("pypi manager", function()
+ local snapshot
+
before_each(function()
+ snapshot = assert.snapshot()
stub(spawn, "python3", mockx.returns(Result.success()))
spawn.python3.on_call_with({ "--version" }).returns(Result.success { stdout = "Python 3.11.0" })
end)
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should init venv without upgrading pip", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx, "promote_cwd")
stub(providers.pypi, "get_supported_python_versions", mockx.returns(Result.failure()))
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
pypi.init { package = { name = "cmake-language-server", version = "0.1.10" }, upgrade_pip = false }
end)
@@ -44,13 +51,13 @@ describe("pypi manager", function()
end)
it("should init venv and upgrade pip", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx, "promote_cwd")
stub(ctx.fs, "file_exists")
stub(providers.pypi, "get_supported_python_versions", mockx.returns(Result.failure()))
ctx.fs.file_exists.on_call_with(match.ref(ctx.fs), "venv/bin/python").returns(true)
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
pypi.init {
package = { name = "cmake-language-server", version = "0.1.10" },
upgrade_pip = true,
@@ -80,7 +87,7 @@ describe("pypi manager", function()
end)
it("should find versioned candidates during init", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx, "promote_cwd")
stub(ctx.fs, "file_exists")
stub(providers.pypi, "get_supported_python_versions", mockx.returns(Result.success ">=3.12"))
@@ -90,7 +97,7 @@ describe("pypi manager", function()
spawn["python3.12"].on_call_with({ "--version" }).returns(Result.success { stdout = "Python 3.12.0" })
ctx.fs.file_exists.on_call_with(match.ref(ctx.fs), "venv/bin/python").returns(true)
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
pypi.init {
package = { name = "cmake-language-server", version = "0.1.10" },
upgrade_pip = false,
@@ -109,7 +116,7 @@ describe("pypi manager", function()
end)
it("should error if unable to find a suitable python3 version", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
spy.on(ctx.stdio_sink, "stderr")
stub(ctx, "promote_cwd")
stub(ctx.fs, "file_exists")
@@ -123,7 +130,7 @@ describe("pypi manager", function()
stub(spawn, "python3", mockx.returns(Result.success()))
spawn.python3.on_call_with({ "--version" }).returns(Result.success { stdout = "Python 3.5.0" })
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return pypi.init {
package = { name = "cmake-language-server", version = "0.1.10" },
upgrade_pip = false,
@@ -143,7 +150,7 @@ describe("pypi manager", function()
it(
"should default to stock version if unable to find suitable versioned candidate during init and when force=true",
function()
- local ctx = create_dummy_context { force = true }
+ local ctx = test_helpers.create_context { install_opts = { force = true } }
spy.on(ctx.stdio_sink, "stderr")
stub(ctx, "promote_cwd")
stub(ctx.fs, "file_exists")
@@ -157,7 +164,7 @@ describe("pypi manager", function()
stub(spawn, "python3", mockx.returns(Result.success()))
spawn.python3.on_call_with({ "--version" }).returns(Result.success { stdout = "Python 3.5.0" })
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
pypi.init {
package = { name = "cmake-language-server", version = "0.1.10" },
upgrade_pip = true,
@@ -180,7 +187,7 @@ describe("pypi manager", function()
)
it("should prioritize stock python", function()
- local ctx = create_dummy_context { force = true }
+ local ctx = test_helpers.create_context { install_opts = { force = true } }
spy.on(ctx.stdio_sink, "stderr")
stub(ctx, "promote_cwd")
stub(ctx.fs, "file_exists")
@@ -190,7 +197,7 @@ describe("pypi manager", function()
stub(spawn, "python3", mockx.returns(Result.success()))
spawn.python3.on_call_with({ "--version" }).returns(Result.success { stdout = "Python 3.8.0" })
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
pypi.init {
package = { name = "cmake-language-server", version = "0.1.10" },
upgrade_pip = true,
@@ -210,10 +217,11 @@ describe("pypi manager", function()
end)
it("should install", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
ctx.fs.file_exists.on_call_with(match.ref(ctx.fs), "venv/bin/python").returns(true)
- installer.exec_in_context(ctx, function()
+
+ ctx:execute(function()
pypi.install("pypi-package", "1.0.0")
end)
@@ -234,12 +242,12 @@ describe("pypi manager", function()
end)
it("should write output", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
ctx.fs.file_exists.on_call_with(match.ref(ctx.fs), "venv/bin/python").returns(true)
spy.on(ctx.stdio_sink, "stdout")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
pypi.install("pypi-package", "1.0.0")
end)
@@ -247,11 +255,11 @@ describe("pypi manager", function()
end)
it("should install extra specifier", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
ctx.fs.file_exists.on_call_with(match.ref(ctx.fs), "venv/bin/python").returns(true)
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
pypi.install("pypi-package", "1.0.0", {
extra = "lsp",
})
@@ -274,10 +282,10 @@ describe("pypi manager", function()
end)
it("should install extra packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
ctx.fs.file_exists.on_call_with(match.ref(ctx.fs), "venv/bin/python").returns(true)
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
pypi.install("pypi-package", "1.0.0", {
extra_packages = { "extra-package" },
install_extra_args = { "--proxy", "http://localhost:9000" },
diff --git a/tests/mason-core/installer/managers/std_spec.lua b/tests/mason-core/installer/managers/std_spec.lua
index dea342bc..20caac18 100644
--- a/tests/mason-core/installer/managers/std_spec.lua
+++ b/tests/mason-core/installer/managers/std_spec.lua
@@ -1,12 +1,22 @@
-local installer = require "mason-core.installer"
local match = require "luassert.match"
local std = require "mason-core.installer.managers.std"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
describe("std unpack [Unix]", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should unpack .gz", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
std.unpack "file.gz"
end)
@@ -21,12 +31,12 @@ describe("std unpack [Unix]", function()
end)
it("should use gtar if available", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "unlink")
stub(vim.fn, "executable")
vim.fn.executable.on_call_with("gtar").returns(1)
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
std.unpack "file.tar.gz"
end)
@@ -35,9 +45,9 @@ describe("std unpack [Unix]", function()
end)
it("should unpack .tar", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "unlink")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
std.unpack "file.tar"
end)
@@ -48,9 +58,9 @@ describe("std unpack [Unix]", function()
end)
it("should unpack .tar.bz2", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "unlink")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
std.unpack "file.tar.bz2"
end)
@@ -61,9 +71,9 @@ describe("std unpack [Unix]", function()
end)
it("should unpack .tar.gz", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "unlink")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
std.unpack "file.tar.gz"
end)
@@ -74,9 +84,9 @@ describe("std unpack [Unix]", function()
end)
it("should unpack .tar.xz", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "unlink")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
std.unpack "file.tar.xz"
end)
@@ -87,9 +97,9 @@ describe("std unpack [Unix]", function()
end)
it("should unpack .tar.zst", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "unlink")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
std.unpack "file.tar.zst"
end)
@@ -101,9 +111,9 @@ describe("std unpack [Unix]", function()
end)
it("should unpack .vsix", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "unlink")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
std.unpack "file.vsix"
end)
@@ -114,9 +124,9 @@ describe("std unpack [Unix]", function()
end)
it("should unpack .zip", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "unlink")
- installer.exec_in_context(ctx, function()
+ ctx:execute(function()
std.unpack "file.zip"
end)
@@ -129,8 +139,8 @@ end)
describe("std clone", function()
it("should clone", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
std.clone "https://github.com/williamboman/mason.nvim"
end)
@@ -146,8 +156,8 @@ describe("std clone", function()
end)
it("should clone and checkout rev", function()
- local ctx = create_dummy_context()
- installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context()
+ ctx:execute(function()
std.clone("https://github.com/williamboman/mason.nvim", {
rev = "e1fd03b1856cb5ad8425f49e18353dc524b02f91",
recursive = true,
diff --git a/tests/mason-core/installer/registry/providers/cargo_spec.lua b/tests/mason-core/installer/registry/compilers/cargo_spec.lua
index 1bdad5f4..69ac446d 100644
--- a/tests/mason-core/installer/registry/providers/cargo_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/cargo_spec.lua
@@ -1,9 +1,9 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local cargo = require "mason-core.installer.registry.providers.cargo"
-local installer = require "mason-core.installer"
+local cargo = require "mason-core.installer.compiler.compilers.cargo"
local providers = require "mason-core.providers"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -94,12 +94,22 @@ describe("cargo provider :: parsing", function()
end)
describe("cargo provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install cargo packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.cargo"
stub(manager, "install", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return cargo.install(ctx, {
crate = "crate-name",
version = "1.2.0",
@@ -120,6 +130,16 @@ describe("cargo provider :: installing", function()
end)
describe("cargo provider :: versions", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should recognize github cargo source", function()
stub(providers.github, "get_all_tags", function()
return Result.success { "1.0.0", "2.0.0", "3.0.0" }
diff --git a/tests/mason-core/installer/registry/providers/composer_spec.lua b/tests/mason-core/installer/registry/compilers/composer_spec.lua
index 8b771ff9..c184adf5 100644
--- a/tests/mason-core/installer/registry/providers/composer_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/composer_spec.lua
@@ -1,8 +1,8 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local composer = require "mason-core.installer.registry.providers.composer"
-local installer = require "mason-core.installer"
+local composer = require "mason-core.installer.compiler.compilers.composer"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -26,12 +26,22 @@ describe("composer provider :: parsing", function()
end)
describe("composer provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install composer packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.composer"
stub(manager, "install", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return composer.install(ctx, {
package = "vendor/package",
version = "1.2.0",
diff --git a/tests/mason-core/installer/registry/providers/gem_spec.lua b/tests/mason-core/installer/registry/compilers/gem_spec.lua
index 965cdbe8..b38bba33 100644
--- a/tests/mason-core/installer/registry/providers/gem_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/gem_spec.lua
@@ -1,8 +1,8 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local gem = require "mason-core.installer.registry.providers.gem"
-local installer = require "mason-core.installer"
+local gem = require "mason-core.installer.compiler.compilers.gem"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -31,12 +31,22 @@ describe("gem provider :: parsing", function()
end)
describe("gem provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install gem packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.gem"
stub(manager, "install", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return gem.install(ctx, {
package = "package",
version = "5.2.0",
diff --git a/tests/mason-core/installer/registry/providers/generic/build_spec.lua b/tests/mason-core/installer/registry/compilers/generic/build_spec.lua
index 443cb99a..8b8baeab 100644
--- a/tests/mason-core/installer/registry/providers/generic/build_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/generic/build_spec.lua
@@ -1,8 +1,8 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local generic = require "mason-core.installer.registry.providers.generic"
-local installer = require "mason-core.installer"
+local generic = require "mason-core.installer.compiler.compilers.generic"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -119,12 +119,22 @@ describe("generic provider :: build :: parsing", function()
end)
describe("generic provider :: build :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local common = require "mason-core.installer.managers.common"
stub(common, "run_build_instruction", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return generic.install(ctx, {
build = {
run = "make",
diff --git a/tests/mason-core/installer/registry/providers/generic/download_spec.lua b/tests/mason-core/installer/registry/compilers/generic/download_spec.lua
index 4bcb1976..4046d898 100644
--- a/tests/mason-core/installer/registry/providers/generic/download_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/generic/download_spec.lua
@@ -1,9 +1,9 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local generic = require "mason-core.installer.registry.providers.generic"
-local installer = require "mason-core.installer"
local match = require "luassert.match"
+local generic = require "mason-core.installer.compiler.compilers.generic"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -99,12 +99,22 @@ describe("generic provider :: download :: parsing", function()
end)
describe("generic provider :: download :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install generic packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local common = require "mason-core.installer.managers.common"
stub(common, "download_files", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return generic.install(ctx, {
downloads = {
{
diff --git a/tests/mason-core/installer/registry/providers/github/build_spec.lua b/tests/mason-core/installer/registry/compilers/github/build_spec.lua
index 17667d2c..0adc00fe 100644
--- a/tests/mason-core/installer/registry/providers/github/build_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/github/build_spec.lua
@@ -1,6 +1,6 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local github = require "mason-core.installer.registry.providers.github"
+local github = require "mason-core.installer.compiler.compilers.github"
---@param overrides Purl
local function purl(overrides)
diff --git a/tests/mason-core/installer/registry/providers/github/release_spec.lua b/tests/mason-core/installer/registry/compilers/github/release_spec.lua
index a6648b33..3cfbabc5 100644
--- a/tests/mason-core/installer/registry/providers/github/release_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/github/release_spec.lua
@@ -1,11 +1,11 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
local common = require "mason-core.installer.managers.common"
-local github = require "mason-core.installer.registry.providers.github"
-local installer = require "mason-core.installer"
+local compiler = require "mason-core.installer.compiler"
+local github = require "mason-core.installer.compiler.compilers.github"
local match = require "luassert.match"
-local registry_installer = require "mason-core.installer.registry"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -198,7 +198,7 @@ describe("github provider :: release :: parsing", function()
end)
it("should upsert version overrides", function()
- local result = registry_installer.parse({
+ local result = compiler.parse({
schema = "registry+v1",
source = {
id = "pkg:github/owner/repo@1.2.3",
@@ -252,7 +252,7 @@ describe("github provider :: release :: parsing", function()
end)
it("should override source if version override provides its own purl id", function()
- local result = registry_installer.parse({
+ local result = compiler.parse({
schema = "registry+v1",
source = {
id = "pkg:github/owner/repo@1.2.3",
@@ -280,14 +280,24 @@ describe("github provider :: release :: parsing", function()
end)
describe("github provider :: release :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install github release assets", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local std = require "mason-core.installer.managers.std"
stub(std, "download_file", mockx.returns(Result.success()))
stub(std, "unpack", mockx.returns(Result.success()))
stub(common, "download_files", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return github.install(ctx, {
repo = "namespace/name",
asset = {
diff --git a/tests/mason-core/installer/registry/providers/golang_spec.lua b/tests/mason-core/installer/registry/compilers/golang_spec.lua
index 6ba57272..8a3abc8a 100644
--- a/tests/mason-core/installer/registry/providers/golang_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/golang_spec.lua
@@ -1,8 +1,8 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local golang = require "mason-core.installer.registry.providers.golang"
-local installer = require "mason-core.installer"
+local golang = require "mason-core.installer.compiler.compilers.golang"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -27,12 +27,22 @@ describe("golang provider :: parsing", function()
end)
describe("golang provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install golang packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.golang"
stub(manager, "install", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return golang.install(ctx, {
package = "namespace/package",
version = "v1.5.0",
diff --git a/tests/mason-core/installer/registry/providers/luarocks_spec.lua b/tests/mason-core/installer/registry/compilers/luarocks_spec.lua
index 0a4ea9ad..b8642fcf 100644
--- a/tests/mason-core/installer/registry/providers/luarocks_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/luarocks_spec.lua
@@ -1,9 +1,9 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local installer = require "mason-core.installer"
-local luarocks = require "mason-core.installer.registry.providers.luarocks"
+local luarocks = require "mason-core.installer.compiler.compilers.luarocks"
local match = require "luassert.match"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -53,13 +53,23 @@ describe("luarocks provider :: parsing", function()
end)
describe("luarocks provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install luarocks packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.luarocks"
local ret_val = Result.success()
stub(manager, "install", mockx.returns(ret_val))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return luarocks.install(ctx, {
package = "namespace/name",
version = "1.0.0",
diff --git a/tests/mason-core/installer/registry/providers/npm_spec.lua b/tests/mason-core/installer/registry/compilers/npm_spec.lua
index b39d092a..680df5bc 100644
--- a/tests/mason-core/installer/registry/providers/npm_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/npm_spec.lua
@@ -1,8 +1,8 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local installer = require "mason-core.installer"
-local npm = require "mason-core.installer.registry.providers.npm"
+local npm = require "mason-core.installer.compiler.compilers.npm"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -27,13 +27,23 @@ describe("npm provider :: parsing", function()
end)
describe("npm provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install npm packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.npm"
stub(manager, "init", mockx.returns(Result.success()))
stub(manager, "install", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return npm.install(ctx, {
package = "@namespace/package",
version = "v1.5.0",
diff --git a/tests/mason-core/installer/registry/providers/nuget_spec.lua b/tests/mason-core/installer/registry/compilers/nuget_spec.lua
index 2437d8de..f514e666 100644
--- a/tests/mason-core/installer/registry/providers/nuget_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/nuget_spec.lua
@@ -1,8 +1,8 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local installer = require "mason-core.installer"
-local nuget = require "mason-core.installer.registry.providers.nuget"
+local nuget = require "mason-core.installer.compiler.compilers.nuget"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -26,12 +26,22 @@ describe("nuget provider :: parsing", function()
end)
describe("nuget provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install nuget packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.nuget"
stub(manager, "install", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return nuget.install(ctx, {
package = "package",
version = "1.5.0",
diff --git a/tests/mason-core/installer/registry/providers/opam_spec.lua b/tests/mason-core/installer/registry/compilers/opam_spec.lua
index c0f73b02..c2c7638e 100644
--- a/tests/mason-core/installer/registry/providers/opam_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/opam_spec.lua
@@ -1,8 +1,8 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local installer = require "mason-core.installer"
-local opam = require "mason-core.installer.registry.providers.opam"
+local opam = require "mason-core.installer.compiler.compilers.opam"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -26,12 +26,22 @@ describe("opam provider :: parsing", function()
end)
describe("opam provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install opam packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.opam"
stub(manager, "install", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return opam.install(ctx, {
package = "package",
version = "1.5.0",
diff --git a/tests/mason-core/installer/registry/providers/openvsx_spec.lua b/tests/mason-core/installer/registry/compilers/openvsx_spec.lua
index 1452ea0f..d3868a69 100644
--- a/tests/mason-core/installer/registry/providers/openvsx_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/openvsx_spec.lua
@@ -1,10 +1,10 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
local common = require "mason-core.installer.managers.common"
-local installer = require "mason-core.installer"
local match = require "luassert.match"
-local openvsx = require "mason-core.installer.registry.providers.openvsx"
+local openvsx = require "mason-core.installer.compiler.compilers.openvsx"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -117,13 +117,10 @@ end)
describe("openvsx provider :: download :: installing", function()
it("should install openvsx assets", function()
- local ctx = create_dummy_context()
- local std = require "mason-core.installer.managers.std"
- stub(std, "download_file", mockx.returns(Result.success()))
- stub(std, "unpack", mockx.returns(Result.success()))
+ local ctx = test_helpers.create_context()
stub(common, "download_files", mockx.returns(Result.success()))
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return openvsx.install(ctx, {
download = {
file = "file-1.10.1.jar",
diff --git a/tests/mason-core/installer/registry/providers/pypi_spec.lua b/tests/mason-core/installer/registry/compilers/pypi_spec.lua
index 539ba53b..61742b4e 100644
--- a/tests/mason-core/installer/registry/providers/pypi_spec.lua
+++ b/tests/mason-core/installer/registry/compilers/pypi_spec.lua
@@ -1,9 +1,9 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
-local installer = require "mason-core.installer"
-local pypi = require "mason-core.installer.registry.providers.pypi"
+local pypi = require "mason-core.installer.compiler.compilers.pypi"
local settings = require "mason.settings"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
---@param overrides Purl
local function purl(overrides)
@@ -44,8 +44,18 @@ describe("pypi provider :: parsing", function()
end)
describe("pypi provider :: installing", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should install pypi packages", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
local manager = require "mason-core.installer.managers.pypi"
stub(manager, "init", mockx.returns(Result.success()))
stub(manager, "install", mockx.returns(Result.success()))
@@ -56,7 +66,7 @@ describe("pypi provider :: installing", function()
},
}
- local result = installer.exec_in_context(ctx, function()
+ local result = ctx:execute(function()
return pypi.install(ctx, {
package = "package",
extra = "lsp",
diff --git a/tests/mason-core/installer/registry/expr_spec.lua b/tests/mason-core/installer/registry/expr_spec.lua
index 65994dfa..944a5983 100644
--- a/tests/mason-core/installer/registry/expr_spec.lua
+++ b/tests/mason-core/installer/registry/expr_spec.lua
@@ -1,6 +1,6 @@
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
-local expr = require "mason-core.installer.registry.expr"
+local expr = require "mason-core.installer.compiler.expr"
local match = require "luassert.match"
describe("registry expressions", function()
diff --git a/tests/mason-core/installer/registry/installer_spec.lua b/tests/mason-core/installer/registry/installer_spec.lua
index 51d9035e..93c91444 100644
--- a/tests/mason-core/installer/registry/installer_spec.lua
+++ b/tests/mason-core/installer/registry/installer_spec.lua
@@ -1,12 +1,13 @@
local Result = require "mason-core.result"
-local installer = require "mason-core.installer.registry"
+local compiler = require "mason-core.installer.compiler"
local match = require "luassert.match"
local spy = require "luassert.spy"
local stub = require "luassert.stub"
-local util = require "mason-core.installer.registry.util"
+local test_helpers = require "mason-test.helpers"
+local util = require "mason-core.installer.compiler.util"
----@type InstallerProvider
-local dummy_provider = {
+---@type InstallerCompiler
+local dummy_compiler = {
---@param source RegistryPackageSource
---@param purl Purl
---@param opts PackageInstallOpts
@@ -36,9 +37,9 @@ local dummy_provider = {
describe("registry installer :: parsing", function()
it("should parse valid package specs", function()
- installer.register_provider("dummy", dummy_provider)
+ compiler.register_compiler("dummy", dummy_compiler)
- local result = installer.parse({
+ local result = compiler.parse({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -48,7 +49,7 @@ describe("registry installer :: parsing", function()
local parsed = result:get_or_nil()
assert.is_true(result:is_success())
- assert.is_true(match.is_ref(dummy_provider)(parsed.provider))
+ assert.is_true(match.is_ref(dummy_compiler)(parsed.compiler))
assert.same({
name = "package-name",
scheme = "pkg",
@@ -63,9 +64,9 @@ describe("registry installer :: parsing", function()
end)
it("should keep unmapped fields", function()
- installer.register_provider("dummy", dummy_provider)
+ compiler.register_compiler("dummy", dummy_compiler)
- local result = installer.parse({
+ local result = compiler.parse({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -83,9 +84,9 @@ describe("registry installer :: parsing", function()
end)
it("should reject incompatible schema versions", function()
- installer.register_provider("dummy", dummy_provider)
+ compiler.register_compiler("dummy", dummy_compiler)
- local result = installer.parse({
+ local result = compiler.parse({
schema = "registry+v1337",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -98,9 +99,9 @@ describe("registry installer :: parsing", function()
end)
it("should use requested version", function()
- installer.register_provider("dummy", dummy_provider)
+ compiler.register_compiler("dummy", dummy_compiler)
- local result = installer.parse({
+ local result = compiler.parse({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -119,9 +120,9 @@ describe("registry installer :: parsing", function()
end)
it("should handle PLATFORM_UNSUPPORTED", function()
- installer.register_provider("dummy", dummy_provider)
+ compiler.register_compiler("dummy", dummy_compiler)
- local result = installer.compile({
+ local result = compiler.compile({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -133,9 +134,9 @@ describe("registry installer :: parsing", function()
end)
it("should error upon parsing failures", function()
- installer.register_provider("dummy", dummy_provider)
+ compiler.register_compiler("dummy", dummy_compiler)
- local result = installer.compile({
+ local result = compiler.compile({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -148,14 +149,24 @@ describe("registry installer :: parsing", function()
end)
describe("registry installer :: compiling", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should run compiled installer function successfully", function()
- installer.register_provider("dummy", dummy_provider)
- spy.on(dummy_provider, "get_versions")
+ compiler.register_compiler("dummy", dummy_compiler)
+ spy.on(dummy_compiler, "get_versions")
---@type PackageInstallOpts
local opts = {}
- local result = installer.compile({
+ local result = compiler.compile({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -165,21 +176,21 @@ describe("registry installer :: compiling", function()
assert.is_true(result:is_success())
local installer_fn = result:get_or_throw()
- local ctx = create_dummy_context(opts)
- local installer_result = require("mason-core.installer").exec_in_context(ctx, installer_fn)
+ local ctx = test_helpers.create_context()
+ local installer_result = ctx:execute(installer_fn)
assert.same(Result.success(), installer_result)
- assert.spy(dummy_provider.get_versions).was_not_called()
+ assert.spy(dummy_compiler.get_versions).was_not_called()
end)
it("should ensure valid version", function()
- installer.register_provider("dummy", dummy_provider)
- spy.on(dummy_provider, "get_versions")
+ compiler.register_compiler("dummy", dummy_compiler)
+ spy.on(dummy_compiler, "get_versions")
---@type PackageInstallOpts
local opts = { version = "v2.0.0" }
- local result = installer.compile({
+ local result = compiler.compile({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -189,12 +200,12 @@ describe("registry installer :: compiling", function()
assert.is_true(result:is_success())
local installer_fn = result:get_or_throw()
- local ctx = create_dummy_context(opts)
- local installer_result = require("mason-core.installer").exec_in_context(ctx, installer_fn)
+ local ctx = test_helpers.create_context { install_opts = opts }
+ local installer_result = ctx:execute(installer_fn)
assert.same(Result.success(), installer_result)
- assert.spy(dummy_provider.get_versions).was_called(1)
- assert.spy(dummy_provider.get_versions).was_called_with({
+ assert.spy(dummy_compiler.get_versions).was_called(1)
+ assert.spy(dummy_compiler.get_versions).was_called_with({
name = "package-name",
scheme = "pkg",
type = "dummy",
@@ -205,13 +216,13 @@ describe("registry installer :: compiling", function()
end)
it("should reject invalid version", function()
- installer.register_provider("dummy", dummy_provider)
- spy.on(dummy_provider, "get_versions")
+ compiler.register_compiler("dummy", dummy_compiler)
+ spy.on(dummy_compiler, "get_versions")
---@type PackageInstallOpts
local opts = { version = "v13.3.7" }
- local result = installer.compile({
+ local result = compiler.compile({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -221,14 +232,14 @@ describe("registry installer :: compiling", function()
assert.is_true(result:is_success())
local installer_fn = result:get_or_throw()
- local ctx = create_dummy_context(opts)
+ local ctx = test_helpers.create_context { install_opts = opts }
local err = assert.has_error(function()
- require("mason-core.installer").exec_in_context(ctx, installer_fn)
+ ctx:execute(installer_fn)
end)
assert.equals([[Version "v13.3.7" is not available.]], err)
- assert.spy(dummy_provider.get_versions).was_called(1)
- assert.spy(dummy_provider.get_versions).was_called_with({
+ assert.spy(dummy_compiler.get_versions).was_called(1)
+ assert.spy(dummy_compiler.get_versions).was_called_with({
name = "package-name",
scheme = "pkg",
type = "dummy",
@@ -239,12 +250,12 @@ describe("registry installer :: compiling", function()
end)
it("should raise errors upon installer failures", function()
- installer.register_provider("dummy", dummy_provider)
+ compiler.register_compiler("dummy", dummy_compiler)
---@type PackageInstallOpts
local opts = {}
- local result = installer.compile({
+ local result = compiler.compile({
schema = "registry+v1",
source = {
id = "pkg:dummy/package-name@v1.2.3",
@@ -255,16 +266,16 @@ describe("registry installer :: compiling", function()
assert.is_true(result:is_success())
local installer_fn = result:get_or_nil()
- local ctx = create_dummy_context(opts)
+ local ctx = test_helpers.create_context()
local err = assert.has_error(function()
- require("mason-core.installer").exec_in_context(ctx, installer_fn)
+ ctx:execute(installer_fn)
end)
assert.equals("This is a failure.", err)
end)
it("should register links", function()
- installer.register_provider("dummy", dummy_provider)
- local link = require "mason-core.installer.registry.link"
+ compiler.register_compiler("dummy", dummy_compiler)
+ local link = require "mason-core.installer.compiler.link"
stub(link, "bin", mockx.returns(Result.success()))
stub(link, "share", mockx.returns(Result.success()))
stub(link, "opt", mockx.returns(Result.success()))
@@ -281,13 +292,13 @@ describe("registry installer :: compiling", function()
---@type PackageInstallOpts
local opts = {}
- local result = installer.compile(spec, opts)
+ local result = compiler.compile(spec, opts)
assert.is_true(result:is_success())
local installer_fn = result:get_or_nil()
- local ctx = create_dummy_context(opts)
- local installer_result = require("mason-core.installer").exec_in_context(ctx, installer_fn)
+ local ctx = test_helpers.create_context()
+ local installer_result = ctx:execute(installer_fn)
assert.is_true(installer_result:is_success())
for _, spy in ipairs { link.bin, link.share, link.opt } do
diff --git a/tests/mason-core/installer/registry/link_spec.lua b/tests/mason-core/installer/registry/link_spec.lua
index eb6af1cc..62777bc9 100644
--- a/tests/mason-core/installer/registry/link_spec.lua
+++ b/tests/mason-core/installer/registry/link_spec.lua
@@ -1,14 +1,25 @@
local Purl = require "mason-core.purl"
local Result = require "mason-core.result"
local fs = require "mason-core.fs"
-local link = require "mason-core.installer.registry.link"
+local link = require "mason-core.installer.compiler.link"
local match = require "luassert.match"
local path = require "mason-core.path"
local stub = require "luassert.stub"
+local test_helpers = require "mason-test.helpers"
describe("registry linker", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
it("should expand bin table", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
stub(ctx.fs, "chmod")
stub(ctx.fs, "fstat")
@@ -45,7 +56,7 @@ describe("registry linker", function()
end)
it("should chmod executable if necessary", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
stub(ctx.fs, "chmod")
stub(ctx.fs, "fstat")
@@ -74,7 +85,7 @@ describe("registry linker", function()
end)
it("should interpolate bin table", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
stub(ctx.fs, "chmod")
stub(ctx.fs, "fstat")
@@ -106,7 +117,7 @@ describe("registry linker", function()
end)
it("should delegate bin paths", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
stub(ctx.fs, "chmod")
stub(ctx.fs, "fstat")
@@ -144,7 +155,7 @@ describe("registry linker", function()
end)
it("should register share links", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
stub(fs.sync, "file_exists")
stub(vim.fn, "glob")
@@ -192,7 +203,7 @@ describe("registry linker", function()
end)
it("should register opt links", function()
- local ctx = create_dummy_context()
+ local ctx = test_helpers.create_context()
stub(ctx.fs, "file_exists")
stub(fs.sync, "file_exists")
stub(vim.fn, "glob")
diff --git a/tests/mason-core/installer/registry/util_spec.lua b/tests/mason-core/installer/registry/util_spec.lua
index 851164d0..be687f36 100644
--- a/tests/mason-core/installer/registry/util_spec.lua
+++ b/tests/mason-core/installer/registry/util_spec.lua
@@ -1,8 +1,8 @@
local Result = require "mason-core.result"
-local installer = require "mason-core.installer"
local match = require "luassert.match"
local platform = require "mason-core.platform"
-local util = require "mason-core.installer.registry.util"
+local test_helpers = require "mason-test.helpers"
+local util = require "mason-core.installer.compiler.util"
describe("registry installer util", function()
it("should coalesce single target", function()
@@ -40,8 +40,8 @@ describe("registry installer util", function()
end)
it("should accept valid version", function()
- local ctx = create_dummy_context { version = "1.0.0" }
- local result = installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context { install_opts = { version = "1.0.0" } }
+ local result = ctx:execute(function()
return util.ensure_valid_version(function()
return Result.success { "1.0.0", "2.0.0", "3.0.0" }
end)
@@ -50,8 +50,8 @@ describe("registry installer util", function()
end)
it("should reject invalid version", function()
- local ctx = create_dummy_context { version = "13.3.7" }
- local result = installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context { install_opts = { version = "13.3.7" } }
+ local result = ctx:execute(function()
return util.ensure_valid_version(function()
return Result.success { "1.0.0", "2.0.0", "3.0.0" }
end)
@@ -60,8 +60,8 @@ describe("registry installer util", function()
end)
it("should gracefully accept version if unable to resolve available versions", function()
- local ctx = create_dummy_context { version = "13.3.7" }
- local result = installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context { install_opts = { version = "13.3.7" } }
+ local result = ctx:execute(function()
return util.ensure_valid_version(function()
return Result.failure()
end)
@@ -70,8 +70,8 @@ describe("registry installer util", function()
end)
it("should accept version if in force mode", function()
- local ctx = create_dummy_context { version = "13.3.7", force = true }
- local result = installer.exec_in_context(ctx, function()
+ local ctx = test_helpers.create_context { install_opts = { version = "13.3.7", force = true } }
+ local result = ctx:execute(function()
return util.ensure_valid_version(function()
return Result.success { "1.0.0" }
end)
diff --git a/tests/mason-core/installer/runner_spec.lua b/tests/mason-core/installer/runner_spec.lua
new file mode 100644
index 00000000..b39a75ac
--- /dev/null
+++ b/tests/mason-core/installer/runner_spec.lua
@@ -0,0 +1,300 @@
+local InstallHandle = require "mason-core.installer.handle"
+local InstallLocation = require "mason-core.installer.location"
+local InstallRunner = require "mason-core.installer.runner"
+local fs = require "mason-core.fs"
+local match = require "luassert.match"
+local spy = require "luassert.spy"
+local stub = require "luassert.stub"
+local Semaphore = require("mason-core.async.control").Semaphore
+local a = require "mason-core.async"
+local registry = require "mason-registry"
+local settings = require "mason.settings"
+
+describe("install runner ::", function()
+ local dummy = registry.get_package "dummy"
+ local dummy2 = registry.get_package "dummy2"
+
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
+ before_each(function()
+ dummy:uninstall()
+ dummy2:uninstall()
+ end)
+
+ describe("locking ::", function()
+ it("should respect semaphore locks", function()
+ local semaphore = Semaphore.new(1)
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local dummy_handle = InstallHandle.new(dummy)
+ local runner_1 = InstallRunner.new(location, dummy_handle, semaphore)
+ local runner_2 = InstallRunner.new(location, InstallHandle.new(dummy2), semaphore)
+
+ stub(dummy.spec.source, "install", function()
+ a.sleep(10000)
+ end)
+ spy.on(dummy2.spec.source, "install")
+
+ runner_1:execute {}
+ runner_2:execute {}
+
+ assert.wait(function()
+ assert.spy(dummy.spec.source.install).was_called(1)
+ assert.spy(dummy2.spec.source.install).was_not_called()
+ end)
+
+ dummy_handle:terminate()
+
+ assert.wait(function()
+ assert.spy(dummy2.spec.source.install).was_called(1)
+ end)
+ end)
+
+ it("should write lockfile", function()
+ local semaphore = Semaphore.new(1)
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local dummy_handle = InstallHandle.new(dummy)
+ local runner = InstallRunner.new(location, dummy_handle, semaphore)
+
+ spy.on(fs.async, "write_file")
+
+ runner:execute {}
+
+ assert.wait(function()
+ assert.spy(fs.async.write_file).was_called_with(location:lockfile(dummy.name), vim.fn.getpid())
+ end)
+ end)
+
+ it("should abort installation if installation lock exists", function()
+ local semaphore = Semaphore.new(1)
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local dummy_handle = InstallHandle.new(dummy)
+ local runner = InstallRunner.new(location, dummy_handle, semaphore)
+
+ stub(fs.async, "file_exists")
+ stub(fs.async, "read_file")
+ fs.async.file_exists.on_call_with(location:lockfile(dummy.name)).returns(true)
+ fs.async.read_file.on_call_with(location:lockfile(dummy.name)).returns "1337"
+
+ local callback = spy.new()
+ runner:execute({}, callback)
+
+ assert.wait(function()
+ assert.spy(callback).was_called()
+ assert.spy(callback).was_called_with(
+ false,
+ "Lockfile exists, installation is already running in another process (pid: 1337). Run with :MasonInstall --force to bypass."
+ )
+ end)
+ end)
+
+ it("should not abort installation if installation lock exists with force=true", function()
+ local semaphore = Semaphore.new(1)
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local dummy_handle = InstallHandle.new(dummy)
+ local runner = InstallRunner.new(location, dummy_handle, semaphore)
+
+ stub(fs.async, "file_exists")
+ stub(fs.async, "read_file")
+ fs.async.file_exists.on_call_with(location:lockfile(dummy.name)).returns(true)
+ fs.async.read_file.on_call_with(location:lockfile(dummy.name)).returns "1337"
+
+ local callback = spy.new()
+ runner:execute({ force = true }, callback)
+
+ assert.wait(function()
+ assert.spy(callback).was_called()
+ assert.spy(callback).was_called_with(true, nil)
+ end)
+ end)
+
+ it("should release lock after successful installation", function()
+ local semaphore = Semaphore.new(1)
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local dummy_handle = InstallHandle.new(dummy)
+ local runner = InstallRunner.new(location, dummy_handle, semaphore)
+
+ local callback = spy.new()
+ runner:execute({}, callback)
+
+ assert.wait(function()
+ assert.is_true(fs.sync.file_exists(location:lockfile(dummy.name)))
+ end)
+ assert.wait(function()
+ assert.spy(callback).was_called()
+ end)
+ assert.is_false(fs.sync.file_exists(location:lockfile(dummy.name)))
+ end)
+ end)
+
+ it("should initialize install location", function()
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local runner = InstallRunner.new(location, InstallHandle.new(registry.get_package "dummy"), Semaphore.new(1))
+
+ spy.on(location, "initialize")
+
+ runner:execute {}
+
+ assert.wait(function()
+ assert.spy(location.initialize).was_called(1)
+ end)
+ end)
+
+ describe("receipt ::", function()
+ it("should write receipt", function()
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local runner =
+ InstallRunner.new(location, InstallHandle.new(registry.get_package "dummy"), Semaphore.new(1))
+
+ runner:execute {}
+
+ assert.wait(function()
+ local receipt_file = location:package "dummy/mason-receipt.json"
+ assert.is_true(fs.sync.file_exists(receipt_file))
+ assert.is_true(match.tbl_containing {
+ name = "dummy",
+ schema_version = "1.2",
+ metrics = match.tbl_containing {
+ completion_time = match.is_number(),
+ start_time = match.is_number(),
+ },
+ source = match.same {
+ id = "pkg:mason/dummy@1.0.0",
+ type = "registry+v1",
+ },
+ links = match.same {
+ bin = {},
+ opt = {},
+ share = {},
+ },
+ }(vim.json.decode(fs.sync.read_file(receipt_file))))
+ end)
+ end)
+ end)
+
+ it("should emit failures", function()
+ local registry_spy = spy.new()
+ local package_spy = spy.new()
+ registry:once("package:install:failed", registry_spy)
+ dummy:once("install:failed", package_spy)
+
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local handle = InstallHandle.new(registry.get_package "dummy")
+ local runner = InstallRunner.new(location, handle, Semaphore.new(1))
+
+ stub(dummy.spec.source, "install", function()
+ error("I've made a mistake.", 0)
+ end)
+
+ local callback = spy.new()
+ runner:execute({}, callback)
+
+ assert.wait(function()
+ assert.spy(registry_spy).was_called(1)
+ assert.spy(registry_spy).was_called_with(match.is_ref(dummy), match.is_ref(handle), "I've made a mistake.")
+ assert.spy(package_spy).was_called(1)
+ assert.spy(package_spy).was_called_with(match.is_ref(handle), "I've made a mistake.")
+
+ assert.spy(callback).was_called(1)
+ assert.spy(callback).was_called_with(false, "I've made a mistake.")
+ end, 10)
+ end)
+
+ it("should terminate installation", function()
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local handle = InstallHandle.new(registry.get_package "dummy")
+ local runner = InstallRunner.new(location, handle, Semaphore.new(1))
+
+ local capture = spy.new()
+ stub(dummy.spec.source, "install", function()
+ capture()
+ handle:terminate()
+ a.sleep(0)
+ capture()
+ end)
+
+ local callback = spy.new()
+
+ runner:execute({}, callback)
+
+ assert.wait(function()
+ assert.spy(callback).was_called(1)
+ assert.spy(callback).was_called_with(false, "Installation was aborted.")
+
+ assert.spy(capture).was_called(1)
+ end)
+ end)
+
+ it("should write debug logs when debug=true", function()
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local handle = InstallHandle.new(registry.get_package "dummy")
+ local runner = InstallRunner.new(location, handle, Semaphore.new(1))
+
+ stub(dummy.spec.source, "install", function(ctx)
+ ctx.stdio_sink.stdout "Hello "
+ ctx.stdio_sink.stderr "world!"
+ end)
+
+ local callback = spy.new()
+ runner:execute({ debug = true }, callback)
+
+ assert.wait(function()
+ assert.spy(callback).was_called()
+ assert.spy(callback).was_called_with(true, nil)
+ end)
+ assert.is_true(fs.sync.file_exists(location:package "dummy/mason-debug.log"))
+ assert.equals("Hello world!", fs.sync.read_file(location:package "dummy/mason-debug.log"))
+ end)
+
+ it("should not retain installation directory on failure", function()
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local handle = InstallHandle.new(registry.get_package "dummy")
+ local runner = InstallRunner.new(location, handle, Semaphore.new(1))
+
+ stub(dummy.spec.source, "install", function(ctx)
+ ctx.stdio_sink.stderr "Something will go terribly wrong.\n"
+ error("This went terribly wrong.", 0)
+ end)
+
+ local callback = spy.new()
+ runner:execute({}, callback)
+
+ assert.wait(function()
+ assert.spy(callback).was_called()
+ assert.spy(callback).was_called_with(false, "This went terribly wrong.")
+ end)
+ assert.is_false(fs.sync.dir_exists(location:staging "dummy"))
+ assert.is_false(fs.sync.dir_exists(location:package "dummy"))
+ end)
+
+ it("should retain installation directory on failure and debug=true", function()
+ local location = InstallLocation.new(settings.current.install_root_dir)
+ local handle = InstallHandle.new(registry.get_package "dummy")
+ local runner = InstallRunner.new(location, handle, Semaphore.new(1))
+
+ stub(dummy.spec.source, "install", function(ctx)
+ ctx.stdio_sink.stderr "Something will go terribly wrong.\n"
+ error("This went terribly wrong.", 0)
+ end)
+
+ local callback = spy.new()
+ runner:execute({ debug = true }, callback)
+
+ assert.wait(function()
+ assert.spy(callback).was_called()
+ assert.spy(callback).was_called_with(false, "This went terribly wrong.")
+ end)
+ assert.is_true(fs.sync.dir_exists(location:staging "dummy"))
+ assert.equals(
+ "Something will go terribly wrong.\nThis went terribly wrong.\n",
+ fs.sync.read_file(location:staging "dummy/mason-debug.log")
+ )
+ end)
+end)
diff --git a/tests/mason-core/package/package_spec.lua b/tests/mason-core/package/package_spec.lua
index 67d49387..622a2ee4 100644
--- a/tests/mason-core/package/package_spec.lua
+++ b/tests/mason-core/package/package_spec.lua
@@ -7,6 +7,16 @@ local spy = require "luassert.spy"
local stub = require "luassert.stub"
describe("package", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
before_each(function()
registry.get_package("dummy"):uninstall()
package.loaded["dummy_package"] = nil
@@ -37,7 +47,7 @@ describe("package", function()
source = {
id = "pkg:mason/package@1",
install = function() end,
- }
+ },
}
local function spec(fields)
return setmetatable(fields, { __index = valid_spec })
@@ -105,26 +115,25 @@ describe("package", function()
dummy.handle = nil
end)
- it(
- "should successfully install package",
- async_test(function()
- local dummy = registry.get_package "dummy"
- local package_install_success_handler = spy.new()
- local package_install_failed_handler = spy.new()
- local install_success_handler = spy.new()
- local install_failed_handler = spy.new()
- registry:once("package:install:success", package_install_success_handler)
- registry:once("package:install:failed", package_install_failed_handler)
- dummy:once("install:success", install_success_handler)
- dummy:once("install:failed", install_failed_handler)
+ it("should successfully install package", function()
+ local dummy = registry.get_package "dummy"
+ local package_install_success_handler = spy.new()
+ local package_install_failed_handler = spy.new()
+ local install_success_handler = spy.new()
+ local install_failed_handler = spy.new()
+ registry:once("package:install:success", package_install_success_handler)
+ registry:once("package:install:failed", package_install_failed_handler)
+ dummy:once("install:success", install_success_handler)
+ dummy:once("install:failed", install_failed_handler)
- local handle = dummy:install { version = "1337" }
+ local handle = dummy:install { version = "1337" }
- assert.wait_for(function()
- assert.is_true(handle:is_closed())
- assert.is_true(dummy:is_installed())
- end)
+ assert.wait(function()
+ assert.is_true(handle:is_closed())
+ assert.is_true(dummy:is_installed())
+ end)
+ assert.wait(function()
assert.spy(install_success_handler).was_called(1)
assert.spy(install_success_handler).was_called_with(match.is_ref(handle))
assert.spy(package_install_success_handler).was_called(1)
@@ -132,45 +141,45 @@ describe("package", function()
assert.spy(package_install_failed_handler).was_called(0)
assert.spy(install_failed_handler).was_called(0)
end)
- )
+ end)
- it(
- "should fail to install package",
- async_test(function()
- local dummy = registry.get_package "dummy"
- stub(dummy.spec.source, "install", function()
- error "I simply refuse to be installed."
- end)
- local package_install_success_handler = spy.new()
- local package_install_failed_handler = spy.new()
- local install_success_handler = spy.new()
- local install_failed_handler = spy.new()
- registry:once("package:install:success", package_install_success_handler)
- registry:once("package:install:failed", package_install_failed_handler)
- dummy:once("install:success", install_success_handler)
- dummy:once("install:failed", install_failed_handler)
+ it("should fail to install package", function()
+ local dummy = registry.get_package "dummy"
+ stub(dummy.spec.source, "install", function()
+ error("I simply refuse to be installed.", 0)
+ end)
+ local package_install_success_handler = spy.new()
+ local package_install_failed_handler = spy.new()
+ local install_success_handler = spy.new()
+ local install_failed_handler = spy.new()
+ registry:once("package:install:success", package_install_success_handler)
+ registry:once("package:install:failed", package_install_failed_handler)
+ dummy:once("install:success", install_success_handler)
+ dummy:once("install:failed", install_failed_handler)
- local handle = dummy:install { version = "1337" }
+ local handle = dummy:install { version = "1337" }
- assert.wait_for(function()
- assert.is_true(handle:is_closed())
- assert.is_false(dummy:is_installed())
- end)
+ assert.wait(function()
+ assert.is_true(handle:is_closed())
+ assert.is_false(dummy:is_installed())
+ end)
+ assert.wait(function()
assert.spy(install_failed_handler).was_called(1)
- assert.spy(install_failed_handler).was_called_with(match.is_ref(handle))
+ assert.spy(install_failed_handler).was_called_with(match.is_ref(handle), "I simply refuse to be installed.")
assert.spy(package_install_failed_handler).was_called(1)
- assert.spy(package_install_failed_handler).was_called_with(match.is_ref(dummy), match.is_ref(handle))
+ assert
+ .spy(package_install_failed_handler)
+ .was_called_with(match.is_ref(dummy), match.is_ref(handle), "I simply refuse to be installed.")
assert.spy(package_install_success_handler).was_called(0)
assert.spy(install_success_handler).was_called(0)
end)
- )
+ end)
- it(
- "should be able to start package installation outside of main loop",
- async_test(function()
- local dummy = registry.get_package "dummy"
+ it("should be able to start package installation outside of main loop", function()
+ local dummy = registry.get_package "dummy"
+ local handle = a.run_blocking(function()
-- Move outside the main loop
a.wait(function(resolve)
local timer = vim.loop.new_timer()
@@ -179,25 +188,19 @@ describe("package", function()
resolve()
end)
end)
-
assert.is_true(vim.in_fast_event())
- local handle = assert.is_not.has_error(function()
+ return assert.is_not.has_error(function()
return dummy:install()
end)
-
- assert.wait_for(function()
- assert.is_true(handle:is_closed())
- end)
end)
- )
+ end)
- it(
- "should be able to instantiate package outside of main loop",
- async_test(function()
- local dummy = registry.get_package "registry"
+ it("should be able to instantiate package outside of main loop", function()
+ local dummy = registry.get_package "registry"
- -- Move outside the main loop
+ -- Move outside the main loop
+ a.run_blocking(function ()
a.wait(function(resolve)
local timer = vim.loop.new_timer()
timer:start(0, 0, function()
@@ -207,12 +210,10 @@ describe("package", function()
end)
assert.is_true(vim.in_fast_event())
-
local pkg = assert.is_not.has_error(function()
return Pkg.new(dummy.spec)
end)
-
assert.same(dummy.spec, pkg.spec)
end)
- )
+ end)
end)
diff --git a/tests/mason-core/platform_spec.lua b/tests/mason-core/platform_spec.lua
index 48484707..88e2b42a 100644
--- a/tests/mason-core/platform_spec.lua
+++ b/tests/mason-core/platform_spec.lua
@@ -15,6 +15,16 @@ local function stub_etc_os_release(contents)
end
describe("platform", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
local function platform()
package.loaded["mason-core.platform"] = nil
return require "mason-core.platform"
diff --git a/tests/mason-core/process_spec.lua b/tests/mason-core/process_spec.lua
index 38ea94de..06330cdd 100644
--- a/tests/mason-core/process_spec.lua
+++ b/tests/mason-core/process_spec.lua
@@ -4,25 +4,22 @@ local spy = require "luassert.spy"
describe("process.spawn", function()
-- Unix only
- it(
- "should spawn command and feed output to sink",
- async_test(function()
- local stdio = process.in_memory_sink()
- local callback = spy.new()
- process.spawn("env", {
- args = {},
- env = {
- "HELLO=world",
- "MY_ENV=var",
- },
- stdio_sink = stdio.sink,
- }, callback)
+ it("should spawn command and feed output to sink", function()
+ local stdio = process.in_memory_sink()
+ local callback = spy.new()
+ process.spawn("env", {
+ args = {},
+ env = {
+ "HELLO=world",
+ "MY_ENV=var",
+ },
+ stdio_sink = stdio.sink,
+ }, callback)
- assert.wait_for(function()
- assert.spy(callback).was_called(1)
- assert.spy(callback).was_called_with(true, 0, match.is_number())
- assert.equals(table.concat(stdio.buffers.stdout, ""), "HELLO=world\nMY_ENV=var\n")
- end)
+ assert.wait(function()
+ assert.spy(callback).was_called(1)
+ assert.spy(callback).was_called_with(true, 0, match.is_number())
+ assert.equals(table.concat(stdio.buffers.stdout, ""), "HELLO=world\nMY_ENV=var\n")
end)
- )
+ end)
end)
diff --git a/tests/mason-core/result_spec.lua b/tests/mason-core/result_spec.lua
index 46f4d35c..227e53ae 100644
--- a/tests/mason-core/result_spec.lua
+++ b/tests/mason-core/result_spec.lua
@@ -250,27 +250,28 @@ describe("Result.try", function()
assert.equals("42", failure:err_or_nil())
end)
- it(
- "should allow calling async functions inside try blocks",
- async_test(function()
- assert.same(
- Result.success "Hello, world!",
- Result.try(function(try)
+ it("should allow calling async functions inside try blocks", function()
+ assert.same(
+ Result.success "Hello, world!",
+ a.run_blocking(function()
+ return Result.try(function(try)
a.sleep(10)
local hello = try(Result.success "Hello, ")
local world = try(Result.success "world!")
return hello .. world
end)
- )
- local failure = Result.try(function(try)
+ end)
+ )
+ local failure = a.run_blocking(function()
+ return Result.try(function(try)
a.sleep(10)
local err = try(Result.success "42")
error(err)
end)
- assert.is_true(failure:is_failure())
- assert.is_true(match.matches ": 42$"(failure:err_or_nil()))
end)
- )
+ assert.is_true(failure:is_failure())
+ assert.is_true(match.matches ": 42$"(failure:err_or_nil()))
+ end)
it("should not unwrap result values in try blocks", function()
assert.same(
@@ -300,12 +301,11 @@ describe("Result.try", function()
)
end)
- it(
- "should allow nesting try blocks in async scope",
- async_test(function()
- assert.same(
- Result.success "Hello from the underworld!",
- Result.try(function(try)
+ it("should allow nesting try blocks in async scope", function()
+ assert.same(
+ Result.success "Hello from the underworld!",
+ a.run_blocking(function()
+ return Result.try(function(try)
a.sleep(10)
local greeting = try(Result.success "Hello from the %s!")
a.sleep(10)
@@ -316,7 +316,7 @@ describe("Result.try", function()
return value
end)))
end)
- )
- end)
- )
+ end)
+ )
+ end)
end)
diff --git a/tests/mason-core/spawn_spec.lua b/tests/mason-core/spawn_spec.lua
index 15b9fe7d..a1432294 100644
--- a/tests/mason-core/spawn_spec.lua
+++ b/tests/mason-core/spawn_spec.lua
@@ -1,3 +1,4 @@
+local a = require "mason-core.async"
local match = require "luassert.match"
local process = require "mason-core.process"
local spawn = require "mason-core.spawn"
@@ -5,212 +6,186 @@ local spy = require "luassert.spy"
local stub = require "luassert.stub"
describe("async spawn", function()
- it(
- "should spawn commands and return stdout & stderr",
- async_test(function()
- local result = spawn.env {
- env_raw = { "FOO=bar" },
- }
- assert.is_true(result:is_success())
- assert.equals("FOO=bar\n", result:get_or_nil().stdout)
- assert.equals("", result:get_or_nil().stderr)
- end)
- )
+ local snapshot
- it(
- "should use provided stdio_sink",
- async_test(function()
- local stdio = process.in_memory_sink()
- local result = spawn.env {
- env_raw = { "FOO=bar" },
- stdio_sink = stdio.sink,
- }
- assert.is_true(result:is_success())
- assert.equals(nil, result:get_or_nil().stdout)
- assert.equals(nil, result:get_or_nil().stderr)
- assert.equals("FOO=bar\n", table.concat(stdio.buffers.stdout, ""))
- assert.equals("", table.concat(stdio.buffers.stderr, ""))
- end)
- )
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
- it(
- "should pass command arguments",
- async_test(function()
- local result = spawn.bash {
- "-c",
- 'echo "Hello $VAR"',
- env = { VAR = "world" },
- }
+ after_each(function()
+ snapshot:revert()
+ end)
- assert.is_true(result:is_success())
- assert.equals("Hello world\n", result:get_or_nil().stdout)
- assert.equals("", result:get_or_nil().stderr)
- end)
- )
+ it("should spawn commands and return stdout & stderr", function()
+ local result = a.run_blocking(spawn.env, {
+ env_raw = { "FOO=bar" },
+ })
+ assert.is_true(result:is_success())
+ assert.equals("FOO=bar\n", result:get_or_nil().stdout)
+ assert.equals("", result:get_or_nil().stderr)
+ end)
- it(
- "should ignore vim.NIL args",
- async_test(function()
- spy.on(process, "spawn")
- local result = spawn.bash {
- vim.NIL,
- vim.NIL,
- "-c",
- { vim.NIL, vim.NIL },
- 'echo "Hello $VAR"',
- env = { VAR = "world" },
- }
+ it("should use provided stdio_sink", function()
+ local stdio = process.in_memory_sink()
+ local result = a.run_blocking(spawn.env, {
+ env_raw = { "FOO=bar" },
+ stdio_sink = stdio.sink,
+ })
+ assert.is_true(result:is_success())
+ assert.equals(nil, result:get_or_nil().stdout)
+ assert.equals(nil, result:get_or_nil().stderr)
+ assert.equals("FOO=bar\n", table.concat(stdio.buffers.stdout, ""))
+ assert.equals("", table.concat(stdio.buffers.stderr, ""))
+ end)
- assert.is_true(result:is_success())
- assert.equals("Hello world\n", result:get_or_nil().stdout)
- assert.equals("", result:get_or_nil().stderr)
- assert.spy(process.spawn).was_called(1)
- assert.spy(process.spawn).was_called_with(
- "bash",
- match.tbl_containing {
- stdio_sink = match.tbl_containing {
- stdout = match.is_function(),
- stderr = match.is_function(),
- },
- env = match.list_containing "VAR=world",
- args = match.tbl_containing {
- "-c",
- 'echo "Hello $VAR"',
- },
- },
- match.is_function()
- )
- end)
- )
+ it("should pass command arguments", function()
+ local result = a.run_blocking(spawn.bash, {
+ "-c",
+ 'echo "Hello $VAR"',
+ env = { VAR = "world" },
+ })
- it(
- "should flatten table args",
- async_test(function()
- local result = spawn.bash {
- { "-c", 'echo "Hello $VAR"' },
- env = { VAR = "world" },
- }
+ assert.is_true(result:is_success())
+ assert.equals("Hello world\n", result:get_or_nil().stdout)
+ assert.equals("", result:get_or_nil().stderr)
+ end)
- assert.is_true(result:is_success())
- assert.equals("Hello world\n", result:get_or_nil().stdout)
- assert.equals("", result:get_or_nil().stderr)
- end)
- )
+ it("should ignore vim.NIL args", function()
+ spy.on(process, "spawn")
+ local result = a.run_blocking(spawn.bash, {
+ vim.NIL,
+ vim.NIL,
+ "-c",
+ { vim.NIL, vim.NIL },
+ 'echo "Hello $VAR"',
+ env = { VAR = "world" },
+ })
- it(
- "should call on_spawn",
- async_test(function()
- local on_spawn = spy.new(function(_, stdio)
- local stdin = stdio[1]
- stdin:write "im so piped rn"
- stdin:close()
- end)
+ assert.is_true(result:is_success())
+ assert.equals("Hello world\n", result:get_or_nil().stdout)
+ assert.equals("", result:get_or_nil().stderr)
+ assert.spy(process.spawn).was_called(1)
+ assert.spy(process.spawn).was_called_with(
+ "bash",
+ match.tbl_containing {
+ stdio_sink = match.tbl_containing {
+ stdout = match.is_function(),
+ stderr = match.is_function(),
+ },
+ env = match.list_containing "VAR=world",
+ args = match.tbl_containing {
+ "-c",
+ 'echo "Hello $VAR"',
+ },
+ },
+ match.is_function()
+ )
+ end)
- local result = spawn.cat {
- { "-" },
- on_spawn = on_spawn,
- }
+ it("should flatten table args", function()
+ local result = a.run_blocking(spawn.bash, {
+ { "-c", 'echo "Hello $VAR"' },
+ env = { VAR = "world" },
+ })
- assert.spy(on_spawn).was_called(1)
- assert.spy(on_spawn).was_called_with(match.is_not.is_nil(), match.is_table(), match.is_number())
- assert.is_true(result:is_success())
- assert.equals("im so piped rn", result:get_or_nil().stdout)
+ assert.is_true(result:is_success())
+ assert.equals("Hello world\n", result:get_or_nil().stdout)
+ assert.equals("", result:get_or_nil().stderr)
+ end)
+
+ it("should call on_spawn", function()
+ local on_spawn = spy.new(function(_, stdio)
+ local stdin = stdio[1]
+ stdin:write "im so piped rn"
+ stdin:close()
end)
- )
- it(
- "should not call on_spawn if spawning fails",
- async_test(function()
- local on_spawn = spy.new()
+ local result = a.run_blocking(spawn.cat, {
+ { "-" },
+ on_spawn = on_spawn,
+ })
- local result = spawn.this_cmd_doesnt_exist {
- on_spawn = on_spawn,
- }
+ assert.spy(on_spawn).was_called(1)
+ assert.spy(on_spawn).was_called_with(match.is_not.is_nil(), match.is_table(), match.is_number())
+ assert.is_true(result:is_success())
+ assert.equals("im so piped rn", result:get_or_nil().stdout)
+ end)
- assert.spy(on_spawn).was_called(0)
- assert.is_true(result:is_failure())
- end)
- )
+ it("should not call on_spawn if spawning fails", function()
+ local on_spawn = spy.new()
- it(
- "should handle failure to spawn process",
- async_test(function()
- stub(process, "spawn", function(_, _, callback)
- callback(false)
- end)
+ local result = a.run_blocking(spawn.this_cmd_doesnt_exist, {
+ on_spawn = on_spawn,
+ })
- local result = spawn.my_cmd {}
- assert.is_true(result:is_failure())
- assert.is_nil(result:err_or_nil().exit_code)
+ assert.spy(on_spawn).was_called(0)
+ assert.is_true(result:is_failure())
+ end)
+
+ it("should handle failure to spawn process", function()
+ stub(process, "spawn", function(_, _, callback)
+ callback(false)
end)
- )
- it(
- "should format failure message",
- async_test(function()
- stub(process, "spawn", function(cmd, opts, callback)
- opts.stdio_sink.stderr(("This is an error message for %s!"):format(cmd))
- callback(false, 127)
- end)
+ local result = a.run_blocking(spawn.my_cmd, {})
+ assert.is_true(result:is_failure())
+ assert.is_nil(result:err_or_nil().exit_code)
+ end)
- local result = spawn.bash {}
- assert.is_true(result:is_failure())
- assert.equals(
- "spawn: bash failed with exit code 127 and signal -. This is an error message for bash!",
- tostring(result:err_or_nil())
- )
+ it("should format failure message", function()
+ stub(process, "spawn", function(cmd, opts, callback)
+ opts.stdio_sink.stderr(("This is an error message for %s!"):format(cmd))
+ callback(false, 127)
end)
- )
- it(
- "should check whether command is executable",
- async_test(function()
- local result = spawn.my_cmd {}
- assert.is_true(result:is_failure())
- assert.equals(
- "spawn: my_cmd failed with exit code - and signal -. my_cmd is not executable",
- tostring(result:err_or_nil())
- )
- end)
- )
+ local result = a.run_blocking(spawn.bash, {})
+ assert.is_true(result:is_failure())
+ assert.equals(
+ "spawn: bash failed with exit code 127 and signal -. This is an error message for bash!",
+ tostring(result:err_or_nil())
+ )
+ end)
- it(
- "should skip checking whether command is executable",
- async_test(function()
- stub(process, "spawn", function(_, _, callback)
- callback(false, 127)
- end)
+ it("should check whether command is executable", function()
+ local result = a.run_blocking(spawn.my_cmd, {})
+ assert.is_true(result:is_failure())
+ assert.equals(
+ "spawn: my_cmd failed with exit code - and signal -. my_cmd is not executable",
+ tostring(result:err_or_nil())
+ )
+ end)
- local result = spawn.my_cmd { "arg1", check_executable = false }
- assert.is_true(result:is_failure())
- assert.spy(process.spawn).was_called(1)
- assert.spy(process.spawn).was_called_with(
- "my_cmd",
- match.tbl_containing {
- args = match.same { "arg1" },
- },
- match.is_function()
- )
+ it("should skip checking whether command is executable", function()
+ stub(process, "spawn", function(_, _, callback)
+ callback(false, 127)
end)
- )
- it(
- "should skip checking whether command is executable if with_paths is provided",
- async_test(function()
- stub(process, "spawn", function(_, _, callback)
- callback(false, 127)
- end)
+ local result = a.run_blocking(spawn.my_cmd, { "arg1", check_executable = false })
+ assert.is_true(result:is_failure())
+ assert.spy(process.spawn).was_called(1)
+ assert.spy(process.spawn).was_called_with(
+ "my_cmd",
+ match.tbl_containing {
+ args = match.same { "arg1" },
+ },
+ match.is_function()
+ )
+ end)
- local result = spawn.my_cmd { "arg1", with_paths = {} }
- assert.is_true(result:is_failure())
- assert.spy(process.spawn).was_called(1)
- assert.spy(process.spawn).was_called_with(
- "my_cmd",
- match.tbl_containing {
- args = match.same { "arg1" },
- },
- match.is_function()
- )
+ it("should skip checking whether command is executable if with_paths is provided", function()
+ stub(process, "spawn", function(_, _, callback)
+ callback(false, 127)
end)
- )
+
+ local result = a.run_blocking(spawn.my_cmd, { "arg1", with_paths = {} })
+ assert.is_true(result:is_failure())
+ assert.spy(process.spawn).was_called(1)
+ assert.spy(process.spawn).was_called_with(
+ "my_cmd",
+ match.tbl_containing {
+ args = match.same { "arg1" },
+ },
+ match.is_function()
+ )
+ end)
end)
diff --git a/tests/mason-core/terminator_spec.lua b/tests/mason-core/terminator_spec.lua
index 24c1ec25..29a3a1dd 100644
--- a/tests/mason-core/terminator_spec.lua
+++ b/tests/mason-core/terminator_spec.lua
@@ -8,112 +8,113 @@ local stub = require "luassert.stub"
local terminator = require "mason-core.terminator"
describe("terminator", function()
- it(
- "should terminate all active handles on nvim exit",
- async_test(function()
- spy.on(InstallHandle, "terminate")
- local dummy = registry.get_package "dummy"
- local dummy2 = registry.get_package "dummy2"
- for _, pkg in ipairs { dummy, dummy2 } do
- stub(pkg.spec.source, "install", function()
- a.sleep(10000)
- end)
- end
+ local snapshot
- local dummy_handle = dummy:install()
- local dummy2_handle = dummy2:install()
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
- assert.wait_for(function()
- assert.spy(dummy.spec.source.install).was_called()
- assert.spy(dummy2.spec.source.install).was_called()
+ after_each(function()
+ -- wait for scheduled calls to expire
+ a.run_blocking(a.wait, vim.schedule)
+ snapshot:revert()
+ end)
+
+ it("should terminate all active handles on nvim exit", function()
+ spy.on(InstallHandle, "terminate")
+ local dummy = registry.get_package "dummy"
+ local dummy2 = registry.get_package "dummy2"
+ for _, pkg in ipairs { dummy, dummy2 } do
+ stub(pkg.spec.source, "install", function()
+ a.sleep(10000)
end)
+ end
- terminator.terminate(5000)
+ local dummy_handle = dummy:install()
+ local dummy2_handle = dummy2:install()
- assert.spy(InstallHandle.terminate).was_called(2)
- assert.spy(InstallHandle.terminate).was_called_with(match.is_ref(dummy_handle))
- assert.spy(InstallHandle.terminate).was_called_with(match.is_ref(dummy2_handle))
- assert.wait_for(function()
- assert.is_true(dummy_handle:is_closed())
- assert.is_true(dummy2_handle:is_closed())
- end)
+ assert.wait(function()
+ assert.spy(dummy.spec.source.install).was_called()
+ assert.spy(dummy2.spec.source.install).was_called()
end)
- )
- it(
- "should print warning messages",
- async_test(function()
- spy.on(vim.api, "nvim_echo")
- spy.on(vim.api, "nvim_err_writeln")
- spy.on(InstallHandle, "terminate")
- local dummy = registry.get_package "dummy"
- local dummy2 = registry.get_package "dummy2"
- for _, pkg in ipairs { dummy, dummy2 } do
- stub(pkg.spec.source, "install", function()
- a.sleep(10000)
- end)
- end
+ terminator.terminate(5000)
- local dummy_handle = dummy:install()
- local dummy2_handle = dummy2:install()
+ assert.spy(InstallHandle.terminate).was_called(2)
+ assert.spy(InstallHandle.terminate).was_called_with(match.is_ref(dummy_handle))
+ assert.spy(InstallHandle.terminate).was_called_with(match.is_ref(dummy2_handle))
+ assert.wait(function()
+ assert.is_true(dummy_handle:is_closed())
+ assert.is_true(dummy2_handle:is_closed())
+ end)
+ end)
- assert.wait_for(function()
- assert.spy(dummy.spec.source.install).was_called()
- assert.spy(dummy2.spec.source.install).was_called()
+ it("should print warning messages", function()
+ spy.on(vim.api, "nvim_echo")
+ spy.on(vim.api, "nvim_err_writeln")
+ local dummy = registry.get_package "dummy"
+ local dummy2 = registry.get_package "dummy2"
+ for _, pkg in ipairs { dummy, dummy2 } do
+ stub(pkg.spec.source, "install", function()
+ a.sleep(10000)
end)
+ end
- terminator.terminate(5000)
+ local dummy_handle = dummy:install()
+ local dummy2_handle = dummy2:install()
- assert.spy(vim.api.nvim_echo).was_called(1)
- assert.spy(vim.api.nvim_echo).was_called_with({
- {
- "[mason.nvim] Neovim is exiting while packages are still installing. Terminating all installations…",
- "WarningMsg",
- },
- }, true, {})
+ assert.wait(function()
+ assert.spy(dummy.spec.source.install).was_called()
+ assert.spy(dummy2.spec.source.install).was_called()
+ end)
- a.wait(vim.schedule)
+ terminator.terminate(5000)
- assert.spy(vim.api.nvim_err_writeln).was_called(1)
- assert.spy(vim.api.nvim_err_writeln).was_called_with(_.dedent [[
+ assert.spy(vim.api.nvim_echo).was_called(1)
+ assert.spy(vim.api.nvim_echo).was_called_with({
+ {
+ "[mason.nvim] Neovim is exiting while packages are still installing. Terminating all installations…",
+ "WarningMsg",
+ },
+ }, true, {})
+
+ a.run_blocking(a.wait, vim.schedule)
+
+ assert.spy(vim.api.nvim_err_writeln).was_called(1)
+ assert.spy(vim.api.nvim_err_writeln).was_called_with(_.dedent [[
[mason.nvim] Neovim exited while the following packages were installing. Installation was aborted.
- dummy
- dummy2
]])
- assert.wait_for(function()
- assert.is_true(dummy_handle:is_closed())
- assert.is_true(dummy2_handle:is_closed())
- end)
+ assert.wait(function()
+ assert.is_true(dummy_handle:is_closed())
+ assert.is_true(dummy2_handle:is_closed())
end)
- )
+ end)
- it(
- "should send SIGTERM and then SIGKILL after grace period",
- async_test(function()
- spy.on(InstallHandle, "kill")
- local dummy = registry.get_package "dummy"
- stub(dummy.spec.source, "install")
- dummy.spec.source.install.invokes(function(ctx)
- -- your signals have no power here
- ctx.spawn.bash { "-c", "function noop { :; }; trap noop SIGTERM; sleep 999999;" }
- end)
+ it("should send SIGTERM and then SIGKILL after grace period", function()
+ spy.on(InstallHandle, "kill")
+ local dummy = registry.get_package "dummy"
+ stub(dummy.spec.source, "install", function(ctx)
+ -- your signals have no power here
+ ctx.spawn.bash { "-c", "function noop { :; }; trap noop SIGTERM; sleep 999999;" }
+ end)
- local handle = dummy:install()
+ local handle = dummy:install()
- assert.wait_for(function()
- assert.spy(dummy.spec.source.install).was_called()
- end)
- terminator.terminate(50)
+ assert.wait(function()
+ assert.spy(dummy.spec.source.install).was_called()
+ end)
+ terminator.terminate(50)
- assert.wait_for(function()
- assert.spy(InstallHandle.kill).was_called(2)
- assert.spy(InstallHandle.kill).was_called_with(match.is_ref(handle), 15) -- SIGTERM
- assert.spy(InstallHandle.kill).was_called_with(match.is_ref(handle), 9) -- SIGKILL
- end)
+ assert.wait(function()
+ assert.spy(InstallHandle.kill).was_called(2)
+ assert.spy(InstallHandle.kill).was_called_with(match.is_ref(handle), 15) -- SIGTERM
+ assert.spy(InstallHandle.kill).was_called_with(match.is_ref(handle), 9) -- SIGKILL
+ end)
- assert.wait_for(function()
- assert.is_true(handle:is_closed())
- end)
+ assert.wait(function()
+ assert.is_true(handle:is_closed())
end)
- )
+ end)
end)
diff --git a/tests/mason-core/ui_spec.lua b/tests/mason-core/ui_spec.lua
index 17087045..efd60712 100644
--- a/tests/mason-core/ui_spec.lua
+++ b/tests/mason-core/ui_spec.lua
@@ -142,187 +142,177 @@ describe("ui", function()
end)
describe("integration test", function()
- it(
- "calls vim APIs as expected during rendering",
- async_test(function()
- local window = display.new_view_only_win("test", "my-filetype")
+ it("calls vim APIs as expected during rendering", function()
+ local window = display.new_view_only_win("test", "my-filetype")
- window.view(function(state)
- return Ui.Node {
- Ui.Keybind("U", "EFFECT", nil, true),
- Ui.Text {
- "Line number 1!",
- state.text,
- },
- Ui.Keybind("R", "R_EFFECT", { state.text }),
- Ui.HlTextNode {
- {
- { "My highlighted text", "MyHighlightGroup" },
- },
- },
- }
- end)
-
- local mutate_state = window.state { text = "Initial state" }
-
- local clear_namespace = spy.on(vim.api, "nvim_buf_clear_namespace")
- local buf_set_option = spy.on(vim.api, "nvim_buf_set_option")
- local win_set_option = spy.on(vim.api, "nvim_win_set_option")
- local set_lines = spy.on(vim.api, "nvim_buf_set_lines")
- local set_extmark = spy.on(vim.api, "nvim_buf_set_extmark")
- local add_highlight = spy.on(vim.api, "nvim_buf_add_highlight")
- local set_keymap = spy.on(vim.keymap, "set")
-
- window.init {
- effects = {
- ["EFFECT"] = function() end,
- ["R_EFFECT"] = function() end,
+ window.view(function(state)
+ return Ui.Node {
+ Ui.Keybind("U", "EFFECT", nil, true),
+ Ui.Text {
+ "Line number 1!",
+ state.text,
},
- winhighlight = {
- "NormalFloat:MasonNormal",
- "CursorLine:MasonCursorLine",
+ Ui.Keybind("R", "R_EFFECT", { state.text }),
+ Ui.HlTextNode {
+ {
+ { "My highlighted text", "MyHighlightGroup" },
+ },
},
}
- window.open()
+ end)
- -- Initial window and buffer creation + initial render
- a.wait(vim.schedule)
+ local mutate_state = window.state { text = "Initial state" }
- assert.spy(win_set_option).was_called(9)
- assert.spy(win_set_option).was_called_with(match.is_number(), "number", false)
- assert.spy(win_set_option).was_called_with(match.is_number(), "relativenumber", false)
- assert.spy(win_set_option).was_called_with(match.is_number(), "wrap", false)
- assert.spy(win_set_option).was_called_with(match.is_number(), "spell", false)
- assert.spy(win_set_option).was_called_with(match.is_number(), "foldenable", false)
- assert.spy(win_set_option).was_called_with(match.is_number(), "signcolumn", "no")
- assert.spy(win_set_option).was_called_with(match.is_number(), "colorcolumn", "")
- assert.spy(win_set_option).was_called_with(match.is_number(), "cursorline", true)
- assert
- .spy(win_set_option)
- .was_called_with(match.is_number(), "winhighlight", "NormalFloat:MasonNormal,CursorLine:MasonCursorLine")
+ local clear_namespace = spy.on(vim.api, "nvim_buf_clear_namespace")
+ local buf_set_option = spy.on(vim.api, "nvim_buf_set_option")
+ local win_set_option = spy.on(vim.api, "nvim_win_set_option")
+ local set_lines = spy.on(vim.api, "nvim_buf_set_lines")
+ local set_extmark = spy.on(vim.api, "nvim_buf_set_extmark")
+ local add_highlight = spy.on(vim.api, "nvim_buf_add_highlight")
+ local set_keymap = spy.on(vim.keymap, "set")
- assert.spy(buf_set_option).was_called(10)
- assert.spy(buf_set_option).was_called_with(match.is_number(), "modifiable", false)
- assert.spy(buf_set_option).was_called_with(match.is_number(), "swapfile", false)
- assert.spy(buf_set_option).was_called_with(match.is_number(), "textwidth", 0)
- assert.spy(buf_set_option).was_called_with(match.is_number(), "buftype", "nofile")
- assert.spy(buf_set_option).was_called_with(match.is_number(), "bufhidden", "wipe")
- assert.spy(buf_set_option).was_called_with(match.is_number(), "buflisted", false)
- assert.spy(buf_set_option).was_called_with(match.is_number(), "filetype", "my-filetype")
- assert.spy(buf_set_option).was_called_with(match.is_number(), "undolevels", -1)
+ window.init {
+ effects = {
+ ["EFFECT"] = function() end,
+ ["R_EFFECT"] = function() end,
+ },
+ winhighlight = {
+ "NormalFloat:MasonNormal",
+ "CursorLine:MasonCursorLine",
+ },
+ }
+ window.open()
- assert.spy(set_lines).was_called(1)
- assert
- .spy(set_lines)
- .was_called_with(match.is_number(), 0, -1, false, { "Line number 1!", "Initial state", "My highlighted text" })
+ -- Initial window and buffer creation + initial render
+ a.run_blocking(a.wait, vim.schedule)
- assert.spy(set_extmark).was_called(0)
+ assert.spy(win_set_option).was_called(9)
+ assert.spy(win_set_option).was_called_with(match.is_number(), "number", false)
+ assert.spy(win_set_option).was_called_with(match.is_number(), "relativenumber", false)
+ assert.spy(win_set_option).was_called_with(match.is_number(), "wrap", false)
+ assert.spy(win_set_option).was_called_with(match.is_number(), "spell", false)
+ assert.spy(win_set_option).was_called_with(match.is_number(), "foldenable", false)
+ assert.spy(win_set_option).was_called_with(match.is_number(), "signcolumn", "no")
+ assert.spy(win_set_option).was_called_with(match.is_number(), "colorcolumn", "")
+ assert.spy(win_set_option).was_called_with(match.is_number(), "cursorline", true)
+ assert
+ .spy(win_set_option)
+ .was_called_with(match.is_number(), "winhighlight", "NormalFloat:MasonNormal,CursorLine:MasonCursorLine")
- assert.spy(add_highlight).was_called(1)
- assert
- .spy(add_highlight)
- .was_called_with(match.is_number(), match.is_number(), "MyHighlightGroup", 2, 0, 19)
+ assert.spy(buf_set_option).was_called(10)
+ assert.spy(buf_set_option).was_called_with(match.is_number(), "modifiable", false)
+ assert.spy(buf_set_option).was_called_with(match.is_number(), "swapfile", false)
+ assert.spy(buf_set_option).was_called_with(match.is_number(), "textwidth", 0)
+ assert.spy(buf_set_option).was_called_with(match.is_number(), "buftype", "nofile")
+ assert.spy(buf_set_option).was_called_with(match.is_number(), "bufhidden", "wipe")
+ assert.spy(buf_set_option).was_called_with(match.is_number(), "buflisted", false)
+ assert.spy(buf_set_option).was_called_with(match.is_number(), "filetype", "my-filetype")
+ assert.spy(buf_set_option).was_called_with(match.is_number(), "undolevels", -1)
- assert.spy(set_keymap).was_called(2)
- assert.spy(set_keymap).was_called_with(
- "n",
- "U",
- match.is_function(),
- match.tbl_containing { nowait = true, silent = true, buffer = match.is_number() }
- )
- assert.spy(set_keymap).was_called_with(
- "n",
- "R",
- match.is_function(),
- match.tbl_containing { nowait = true, silent = true, buffer = match.is_number() }
- )
+ assert.spy(set_lines).was_called(1)
+ assert
+ .spy(set_lines)
+ .was_called_with(match.is_number(), 0, -1, false, { "Line number 1!", "Initial state", "My highlighted text" })
- assert.spy(clear_namespace).was_called(1)
- assert.spy(clear_namespace).was_called_with(match.is_number(), match.is_number(), 0, -1)
+ assert.spy(set_extmark).was_called(0)
- mutate_state(function(state)
- state.text = "New state"
- end)
+ assert.spy(add_highlight).was_called(1)
+ assert.spy(add_highlight).was_called_with(match.is_number(), match.is_number(), "MyHighlightGroup", 2, 0, 19)
- assert.spy(set_lines).was_called(1)
- a.wait(vim.schedule)
- assert.spy(set_lines).was_called(2)
+ assert.spy(set_keymap).was_called(2)
+ assert.spy(set_keymap).was_called_with(
+ "n",
+ "U",
+ match.is_function(),
+ match.tbl_containing { nowait = true, silent = true, buffer = match.is_number() }
+ )
+ assert.spy(set_keymap).was_called_with(
+ "n",
+ "R",
+ match.is_function(),
+ match.tbl_containing { nowait = true, silent = true, buffer = match.is_number() }
+ )
+
+ assert.spy(clear_namespace).was_called(1)
+ assert.spy(clear_namespace).was_called_with(match.is_number(), match.is_number(), 0, -1)
- assert
- .spy(set_lines)
- .was_called_with(match.is_number(), 0, -1, false, { "Line number 1!", "New state", "My highlighted text" })
+ mutate_state(function(state)
+ state.text = "New state"
end)
- )
- it(
- "anchors to sticky cursor",
- async_test(function()
- local window = display.new_view_only_win("test", "my-filetype")
- window.view(function(state)
- local extra_lines = state.show_extra_lines
- and Ui.Text {
- "More",
- "Lines",
- "Here",
- }
- or Ui.Node {}
- return Ui.Node {
- extra_lines,
- Ui.Text {
- "Line 1",
- "Line 2",
- "Line 3",
- "Line 4",
- "Special line",
- },
- Ui.StickyCursor { id = "special" },
- Ui.Text {
- "Line 6",
- "Line 7",
- "Line 8",
- "Line 9",
- "Line 10",
- },
- }
- end)
- local mutate_state = window.state { show_extra_lines = false }
- window.init {}
- window.open()
- a.wait(vim.schedule)
- window.set_cursor { 5, 3 } -- move cursor to sticky line
- mutate_state(function(state)
- state.show_extra_lines = true
- end)
- a.wait(vim.schedule)
- local cursor = window.get_cursor()
- assert.same({ 8, 3 }, cursor)
+ assert.spy(set_lines).was_called(1)
+ a.run_blocking(a.wait, vim.schedule)
+ assert.spy(set_lines).was_called(2)
+
+ assert
+ .spy(set_lines)
+ .was_called_with(match.is_number(), 0, -1, false, { "Line number 1!", "New state", "My highlighted text" })
+ end)
+
+ it("anchors to sticky cursor", function()
+ local window = display.new_view_only_win("test", "my-filetype")
+ window.view(function(state)
+ local extra_lines = state.show_extra_lines
+ and Ui.Text {
+ "More",
+ "Lines",
+ "Here",
+ }
+ or Ui.Node {}
+ return Ui.Node {
+ extra_lines,
+ Ui.Text {
+ "Line 1",
+ "Line 2",
+ "Line 3",
+ "Line 4",
+ "Special line",
+ },
+ Ui.StickyCursor { id = "special" },
+ Ui.Text {
+ "Line 6",
+ "Line 7",
+ "Line 8",
+ "Line 9",
+ "Line 10",
+ },
+ }
end)
- )
- it(
- "should respect border ui setting",
- async_test(function()
- local nvim_open_win = spy.on(vim.api, "nvim_open_win")
+ local mutate_state = window.state { show_extra_lines = false }
+ window.init {}
+ window.open()
+ a.run_blocking(a.wait, vim.schedule)
+ window.set_cursor { 5, 3 } -- move cursor to sticky line
+ mutate_state(function(state)
+ state.show_extra_lines = true
+ end)
+ a.run_blocking(a.wait, vim.schedule)
+ local cursor = window.get_cursor()
+ assert.same({ 8, 3 }, cursor)
+ end)
- local window = display.new_view_only_win("test", "my-filetype")
- window.view(function()
- return Ui.Node {}
- end)
- window.state {}
- window.init { border = "rounded" }
- window.open()
- a.wait(vim.schedule)
+ it("should respect border ui setting", function()
+ local nvim_open_win = spy.on(vim.api, "nvim_open_win")
- assert.spy(nvim_open_win).was_called(1)
- assert.spy(nvim_open_win).was_called_with(
- match.is_number(),
- true,
- match.tbl_containing {
- border = "rounded",
- }
- )
+ local window = display.new_view_only_win("test", "my-filetype")
+ window.view(function()
+ return Ui.Node {}
end)
- )
+ window.state {}
+ window.init { border = "rounded" }
+ window.open()
+ a.run_blocking(a.wait, vim.schedule)
+
+ assert.spy(nvim_open_win).was_called(1)
+ assert.spy(nvim_open_win).was_called_with(
+ match.is_number(),
+ true,
+ match.tbl_containing {
+ border = "rounded",
+ }
+ )
+ end)
it("should not apply cascading styles to empty lines", function()
local render_output = display._render_node(
diff --git a/tests/mason-registry/api_spec.lua b/tests/mason-registry/api_spec.lua
index 039d0959..8164c901 100644
--- a/tests/mason-registry/api_spec.lua
+++ b/tests/mason-registry/api_spec.lua
@@ -3,6 +3,16 @@ local match = require "luassert.match"
local stub = require "luassert.stub"
describe("mason-registry API", function()
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
---@module "mason-registry.api"
local api
local fetch
diff --git a/tests/mason/api/command_spec.lua b/tests/mason/api/command_spec.lua
index 6cae3e0c..6945340d 100644
--- a/tests/mason/api/command_spec.lua
+++ b/tests/mason/api/command_spec.lua
@@ -9,73 +9,56 @@ local api = require "mason.api.command"
local registry = require "mason-registry"
describe(":Mason", function()
- it(
- "should open the UI window",
- async_test(function()
- api.Mason()
- a.wait(vim.schedule)
- local win = vim.api.nvim_get_current_win()
- local buf = vim.api.nvim_win_get_buf(win)
- assert.equals("mason", vim.api.nvim_buf_get_option(buf, "filetype"))
- end)
- )
+ it("should open the UI window", function()
+ api.Mason()
+ a.run_blocking(a.wait, vim.schedule)
+ local win = vim.api.nvim_get_current_win()
+ local buf = vim.api.nvim_win_get_buf(win)
+ assert.equals("mason", vim.api.nvim_buf_get_option(buf, "filetype"))
+ end)
end)
describe(":MasonInstall", function()
- it(
- "should install the provided packages",
- async_test(function()
- local dummy = registry.get_package "dummy"
- local dummy2 = registry.get_package "dummy2"
- spy.on(Pkg, "install")
- api.MasonInstall { "dummy@1.0.0", "dummy2" }
- assert.spy(Pkg.install).was_called(2)
- assert.spy(Pkg.install).was_called_with(match.is_ref(dummy), { version = "1.0.0" })
- assert
- .spy(Pkg.install)
- .was_called_with(match.is_ref(dummy2), match.tbl_containing { version = match.is_nil() })
- end)
- )
+ it("should install the provided packages", function()
+ local dummy = registry.get_package "dummy"
+ local dummy2 = registry.get_package "dummy2"
+ spy.on(Pkg, "install")
+ api.MasonInstall { "dummy@1.0.0", "dummy2" }
+ assert.spy(Pkg.install).was_called(2)
+ assert.spy(Pkg.install).was_called_with(match.is_ref(dummy), { version = "1.0.0" })
+ assert.spy(Pkg.install).was_called_with(match.is_ref(dummy2), match.tbl_containing { version = match.is_nil() })
+ end)
- it(
- "should install provided packages in debug mode",
- async_test(function()
- local dummy = registry.get_package "dummy"
- local dummy2 = registry.get_package "dummy2"
- spy.on(Pkg, "install")
- vim.cmd [[MasonInstall --debug dummy dummy2]]
- assert.spy(Pkg.install).was_called(2)
- assert.spy(Pkg.install).was_called_with(match.is_ref(dummy), { version = nil, debug = true })
- assert.spy(Pkg.install).was_called_with(match.is_ref(dummy2), { version = nil, debug = true })
- end)
- )
+ it("should install provided packages in debug mode", function()
+ local dummy = registry.get_package "dummy"
+ local dummy2 = registry.get_package "dummy2"
+ spy.on(Pkg, "install")
+ vim.cmd [[MasonInstall --debug dummy dummy2]]
+ assert.spy(Pkg.install).was_called(2)
+ assert.spy(Pkg.install).was_called_with(match.is_ref(dummy), { version = nil, debug = true })
+ assert.spy(Pkg.install).was_called_with(match.is_ref(dummy2), { version = nil, debug = true })
+ end)
- it(
- "should open the UI window",
- async_test(function()
- local dummy = registry.get_package "dummy"
- spy.on(dummy, "install")
- api.MasonInstall { "dummy" }
- local win = vim.api.nvim_get_current_win()
- local buf = vim.api.nvim_win_get_buf(win)
- assert.equals("mason", vim.api.nvim_buf_get_option(buf, "filetype"))
- end)
- )
+ it("should open the UI window", function()
+ local dummy = registry.get_package "dummy"
+ spy.on(dummy, "install")
+ api.MasonInstall { "dummy" }
+ local win = vim.api.nvim_get_current_win()
+ local buf = vim.api.nvim_win_get_buf(win)
+ assert.equals("mason", vim.api.nvim_buf_get_option(buf, "filetype"))
+ end)
end)
describe(":MasonUninstall", function()
- it(
- "should uninstall the provided packages",
- async_test(function()
- local dummy = registry.get_package "dummy"
- local dummy2 = registry.get_package "dummy"
- spy.on(Pkg, "uninstall")
- api.MasonUninstall { "dummy", "dummy2" }
- assert.spy(Pkg.uninstall).was_called(2)
- assert.spy(Pkg.uninstall).was_called_with(match.is_ref(dummy))
- assert.spy(Pkg.uninstall).was_called_with(match.is_ref(dummy2))
- end)
- )
+ it("should uninstall the provided packages", function()
+ local dummy = registry.get_package "dummy"
+ local dummy2 = registry.get_package "dummy"
+ spy.on(Pkg, "uninstall")
+ api.MasonUninstall { "dummy", "dummy2" }
+ assert.spy(Pkg.uninstall).was_called(2)
+ assert.spy(Pkg.uninstall).was_called_with(match.is_ref(dummy))
+ assert.spy(Pkg.uninstall).was_called_with(match.is_ref(dummy2))
+ end)
end)
describe(":MasonLog", function()
@@ -91,39 +74,43 @@ describe(":MasonLog", function()
end)
describe(":MasonUpdate", function()
- it(
- "should update registries",
- async_test(function()
- stub(registry, "update", function(cb)
- cb(true, { {} })
- end)
- spy.on(vim, "notify")
- api.MasonUpdate()
- assert.spy(vim.notify).was_called(2)
- assert.spy(vim.notify).was_called_with("Updating registries…", vim.log.levels.INFO, {
- title = "mason.nvim",
- })
- assert.spy(vim.notify).was_called_with("Successfully updated 1 registry.", vim.log.levels.INFO, {
- title = "mason.nvim",
- })
+ local snapshot
+
+ before_each(function()
+ snapshot = assert.snapshot()
+ end)
+
+ after_each(function()
+ snapshot:revert()
+ end)
+
+ it("should update registries", function()
+ stub(registry, "update", function(cb)
+ cb(true, { {} })
end)
- )
+ spy.on(vim, "notify")
+ api.MasonUpdate()
+ assert.spy(vim.notify).was_called(2)
+ assert.spy(vim.notify).was_called_with("Updating registries…", vim.log.levels.INFO, {
+ title = "mason.nvim",
+ })
+ assert.spy(vim.notify).was_called_with("Successfully updated 1 registry.", vim.log.levels.INFO, {
+ title = "mason.nvim",
+ })
+ end)
- it(
- "should notify errors",
- async_test(function()
- stub(registry, "update", function(cb)
- cb(false, "Some error.")
- end)
- spy.on(vim, "notify")
- api.MasonUpdate()
- assert.spy(vim.notify).was_called(2)
- assert.spy(vim.notify).was_called_with("Updating registries…", vim.log.levels.INFO, {
- title = "mason.nvim",
- })
- assert.spy(vim.notify).was_called_with("Failed to update registries: Some error.", vim.log.levels.ERROR, {
- title = "mason.nvim",
- })
+ it("should notify errors", function()
+ stub(registry, "update", function(cb)
+ cb(false, "Some error.")
end)
- )
+ spy.on(vim, "notify")
+ api.MasonUpdate()
+ assert.spy(vim.notify).was_called(2)
+ assert.spy(vim.notify).was_called_with("Updating registries…", vim.log.levels.INFO, {
+ title = "mason.nvim",
+ })
+ assert.spy(vim.notify).was_called_with("Failed to update registries: Some error.", vim.log.levels.ERROR, {
+ title = "mason.nvim",
+ })
+ end)
end)
diff --git a/tests/minimal_init.vim b/tests/minimal_init.vim
index abd07fa3..43e8367f 100644
--- a/tests/minimal_init.vim
+++ b/tests/minimal_init.vim
@@ -13,7 +13,22 @@ set packpath=$dependencies
packloadall
lua require("luassertx")
-lua require("test_helpers")
+
+lua <<EOF
+mockx = {
+ just_runs = function() end,
+ returns = function(val)
+ return function()
+ return val
+ end
+ end,
+ throws = function(exception)
+ return function()
+ error(exception, 2)
+ end
+ end,
+}
+EOF
lua <<EOF
local path = require "mason-core.path"
diff --git a/vim.yml b/vim.yml
index 1b348cf0..b0dc6796 100644
--- a/vim.yml
+++ b/vim.yml
@@ -8,23 +8,10 @@ globals:
property: read-only
vim:
any: true
- async_test:
+ assert.wait:
args:
- type: function
- assert.wait_for:
- args:
- - type: function
- create_dummy_context:
- args:
- - type: table
- required: false
- InstallHandleGenerator:
- args:
- - type: string
- InstallContextGenerator:
- args:
- - type: any
- - type: table
+ - type: number
required: false
mockx.throws:
args: