aboutsummaryrefslogtreecommitdiff
path: root/test/functional/plugin/pack_spec.lua
Commit message (Collapse)AuthorAge
* test: simplify functional test expectationsLewis Russell2026-10-05
| | | | | | | | | | | | Problem: Repeated default fields and field-by-field assertions obscure the values that each functional test is meant to check. Solution: Add eq_partial() to check selected record fields while preserving list length and order. Use local helpers to fill fixed expected defaults where tests need full equality. Keep explicit absence checks and test-specific values at the call sites. AI-assisted
* fix(pack): ignore inherited GIT_INDEX_FILE #42114Jake Wang2026-09-30
| | | | | | | | | | | | Problem: An inherited Git index can leave a plugin checkout empty or prevent updates. Solution: Clear GIT_INDEX_FILE for plugin Git commands. Verify installs and updates preserve the caller environment and index. AI-assisted Signed-off-by: Jake Wang <jakezwang@users.noreply.github.com>
* test: fix wrong order of args to eq(), neq() #41499zeertzjq2026-08-26
|
* feat(pack): add support for sourcing manifest scriptsEvgeni Chasnovski2026-07-31
| | | | | | | | | | | | | | | | | | | Problem: No way for plugins to define hooks that would be executed during plugin lifecycle. Like after install/update or before update/delete. Solution: Automatically source scripts defined in plugin manifest file (if any) after triggering corresponding `PackChanged{,Pre}` events. During sourcing make some termporary adjustments: - Current directory is set to plugin's root to simplify execution of CLI calls like `make build`. - Plugin's path is ensured to be inside 'runtimepath' to allow using `require('plugin-module')` inside manifest scripts. The reason to execute after triggering event is so that there is a possibility for users to execute code both before the script (exactly on event) and after the script (more-or-less via `vim.schedule` called on the event).
* feat(pack): introduce plugin manifest supportEvgeni Chasnovski2026-07-31
| | | | | | | | | Problem: There is no agreed way for plugins to provide extra information about themselves. Like required Neovim version, install/update/delete hooks, and dependencies. Solution: Introduce basic concept of plugin manifest file `pkg.json` based on https://packspec.org/.
* fix(pack): do not write the lockfile when installing from it #41031Evgeni Chasnovski2026-07-28
| | | | | | | | | | | | Problem: Installing plugins during lockfile synchronization always writes the lockfile, even though its content is used during install. This might be a problem if the lockfile (itself or its parent directory) is not writeable (can only be read). Solution: Do not write the lockfile when installing directly from it. This is okay since the `src` and `rev` are used directly from the lockfile and don't change at this step. While potential change in `version` (that must be written to the lockfile) is handled in other code path.
* test(harness): migrate away from magic globalsJustin M. Keyes2026-07-21
| | | | | | | | | | | | | | | | | | | | | Problem: The magic globals `it`, `describe`, etc., are more trouble than they are worth. - Hooking into `after_each` requires `getfenv()` hacks. - They confuse luals/emmylua, because the top-level `.luarc.json` isn't merged with `test/.luarc.json` (apparently a luals limitation?) - They totally defeat discoverability because the user just has to "know" about the various magic symbols. So they harm DX, which means they serve no purpose at all. Solution: - Expose the test API from `testutil`, so tests can call `t.it()`, `t.describe()`, etc., in the conventional way. - Drop `getfenv()` hacks. - Drop the `setfenv()` injection in `load_chunk`. - Drop `test/_meta.lua`.
* test(pack): "vim.pack" tests fail on local machineJustin M. Keyes2026-07-18
|
* test(pack): unreliable "vim.pack" tests on Windows #40717Justin M. Keyes2026-07-13
| | | | | | | | | | | FAILED …/plugin/pack_spec.lua:92: …/plugin/pack_spec.lua @ 70: vim.pack update() can use lockfile revision as a target …/plugin/pack_spec.lua:92: …/plugin/pack_spec.lua:70: process still exists stack traceback: D:/a/neovim/neovim/test/functional/testnvim/exec_lua.lua:124: in function 'system_sync' …/plugin/pack_spec.lua:92: in function 'git_cmd' …/plugin/pack_spec.lua:113: in function 'git_add_commit' …/plugin/pack_spec.lua:1209: in function <…/plugin/pack_spec.lua:1173> fix #40167
* fix(path): normalize separators (slashes) #39155tao2026-07-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: Continue separators normalization, and try to keep it at the nvim <-> external boundary, e.g., `fn.xxx`, `api.xxx`, `:xxx` Solution: some key changes - normalize `$HOME-windows` - normalize entry points of changing directory - `nvim_set_current_dir()`, `chdir()` - normalize the named pipe - `--listen` arg, `--server` arg, `:restart` - `serverstart()`, `sockconnect()`, `serverstop()` - make `expand()` respect 'shellslash' again - clean up `did_set_shellslash` - replace `forward_slash` with `TO_SLASH` - remove obsolete `TMP_PATHSEPSTR` - remove `slash_adjust` in `do_autocmd_dirchanged`? - make `fnamemodify()` always return `/` (except when substituting separators via `:s`, `:gs` ?) Note: - these funcs still apply `slash_adjust` on return, as before: `:pwd`, `chdir()`, `exepath()`, `getcwd()` - these funcs alwarys return `/`, unlike before the normalization PRs - `getcompletion()`, `finddir()`, `findfile()` Also - clean up code, comments, formatting and tests - move `TO_SLASH` from `f_chdir` to `changedir_func` - move `TO_SLASH` from `f_bufadd` to `buflist_new` Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
* feat(pack): 'packlockfile' option #40562Evgeni Chasnovski2026-07-08
| | | | | Problem: No way to configure the lockfile location. Solution: Add 'packlockfile' option.
* feat(pack): ensure order of `PackChanged{Pre,}` events #40455Evgeni Chasnovski2026-06-27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: due to totally async install/update/checkout there is no guaranteed order of `PackChanged{Pre,}` events across different plugins. This might lead to conflicts when callback for some "main" plugin relies on features from "dependency" plugin: i.e. callback for "main" plugin can trigger before installing/updating "dependency" plugin. The installation order can be enforced by separate vim.pack.add() calls, but update/checkout can not. Solution: Trigger events in bulk independently of async execution: - `PackChangedPre` before any action for all input plugins in order they are supplied. It will also trigger even if an action will fail. - `PackChanged` after all actions finished for all sucessfully affected plugins in order they are supplied. This also comes with a couple of side effect changes: - `PackChangedPre kind=delete` is now also triggered even if the delete won't be done. This makes it more aligned with `kind=install` and `kind=update`. - Force update (`:packupdate!`) and "udpate LSP action" now do two async steps: download/compute updates and apply them. This also results in two progress reports. This is mostly a by-product of the implementation (there has to be a pre-computation of target revision for all plugins before doing `PackChangedPre` in bulk before possibly applying an update), but I kind of like it more this way as it is more explicit of what's going on. If absolutely not acceptable, there might be some hacks to mitigate it at least in code action, but I'd keep it like this.
* test: mark non-actionable tests as N/A #40207Justin M. Keyes2026-06-12
| | | | | | | | | | Problem: Listing individual "skipped" notices for each non-actionable or NVIM_TEST_INTEG test is noisy and makes the "skipped" list overwhelming and less meaningful. Solution: - Mark non-actionable tests with "N/A". - Report NVIM_TEST_INTEG tests as a separate 1-line summary.
* fix(pack): collect "Removed plugin" into one message #39840Olivia Kinnear2026-05-20
|
* feat(pack): update `get()` to be able to fetch data from plugin sourceEvgeni Chasnovski2026-05-17
| | | | | | | | | | Problem: There is currently no convenient way to programmatically check for new updates from plugin source. Running `vim.pack.update()` is one approach, but it opens a confirmation buffer that requires a manual action to close. Solution: Add `opts.offline` to `vim.pack.get()` that will first fetch new updates from plugin source before computing the output.
* feat(pack): update `get()` to return revision of a pending updateEvgeni Chasnovski2026-05-17
| | | | | | | | | | | | Problem: No convenient way to programmatically get the revision that would be checked out after `vim.pack.update()` (with `offline=true`). Doing this manually requires resolving `spec.version` which is not trivial. This data can be useful for custom reporting of pending updates or third party confirmation step. Solution: Make `get()` include a new field for the revision that points at the state after applying pending update. This is also the same as the revision of resolved `spec.version`.
* feat(pack): update `get()` to take `rev` from actual repo if `info=true`Evgeni Chasnovski2026-05-17
| | | | | | | | | | | | Problem: `vim.pack.get()` always uses lockfile as the source for the `rev` field. This is fast, but may be misleading in case of a corrupted lockfile. Solution: Compute revision from Git repo on disk if `info=true` (default), use lockfile otherwise. This does increase execution time (as a result of one extra `git ...` call for every plugin), but `info=true` is already designed to be informative and not necessarily fast.
* feat(pack): :packupdate, :packdel commands #39693Olivia Kinnear2026-05-16
| | | | | | Define `:packupdate` and `:packdel` as separate commands instead of a unified `:pack {subcommand}` because the semantics between the two commands vary differently enough that it doesn't make sense to combine them. Additionally, `:pack! update/del` looks bad.
* fix(pack): suggest "delete" code action for active plugins #39678Evgeni Chasnovski2026-05-08
| | | | | | | | | | Problem: Trying to execute code action on an active plugin without updates leads to nothing. It is more useful if code actions "do something" on a bigger portion of the confirm buffer. Solution: Suggest "delete" code action even for active plugins. Trying to execute it will first show a confirmation buffer with relevant warning of why this might be not a good idea. Confirming will delete a plugin.
* test: unreliable pack_spec.after_each: "EBUSY: resource busy or locked" #39606David Balatero2026-05-05
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: `EBUSY` during cleanup: Windows CI can intermittently fail `pack_spec.lua` with `EBUSY` while removing `site/pack/core/opt/plugindirs`. This can happen because: - the test Nvim session may still be alive when `after_each()` removes the pack directory - Windows does not allow removing a directory while another process still has an open handle below it - startup-time `vim.pack.add()` performs a real `git clone`, so process and file handle release timing can vary on slower runners Startup timeout: The startup tests can also fail before cleanup because they wait for `_G.done` with a fixed timeout. That timeout includes the time needed for startup to run `vim.pack.add()` and finish the local clone. Solution: Close before cleanup: Capture the pack, lockfile, and log paths while the test Nvim session is still available, then call `n.check_close()` before removing the pack directory. Extend Windows startup wait: Increase the `_G.done` retry budget only on Windows so startup-time `vim.pack.add()` has more time to finish on slower CI runners.
* fix(util): add and use forge link computationEvgeni Chasnovski2026-04-25
| | | | | | | | Problem: There are many Git forges each with a different way of constructing permanent links to like commits and tags. Solution: Add a private utility function that computes these special links on the best effort basis.
* feat(pack): support `textDocument/documentLink` in confirmation bufferEvgeni Chasnovski2026-04-25
| | | | | | | | | | | | | | Problem: In `vim.pack.update()` confirmation buffer it might be useful to be able to use `gx` (open link at cursor) when cursor is on something like commit or tag. Solution: Add `textDocument/documentLink` method support for the in-process LSP. This may be used by LSP clients and makes `gx` automatically work. The shortcoming is that this requires tracking how to construct a URL from source and commit/tag. Currently only GitHub hosted repositories are supported.
* fix(pack): only use tags that strictly comply with semver spec #39342Evgeni Chasnovski2026-04-23
| | | | | | | | | | | | | | | | | | Problem: Using `version=vim.version.range(...)` in plugin specification is meant to use semver-like tags. Whether a tag is semver-like was decided by a plain `vim.version.parse` which is not strict by default. This allowed treating tags like `nvim-0.6` (which is usually reserved for the latest revision compatible with Nvim<=0.6 version) like semver tags and resulted in confusing behavior (preferring `nvim-0.6` tag over `v0.2.2`, for example). Solution: Use `vim.version.range(x, { strict = true })` to decide if the tag name is semver-like or not. This allows tags like both `v1.2.3` and `1.2.3` while being consistent in what Nvim thinks is a semver string. This is technically not a breaking change since it was documented that only tags like `v<major>.<minor>.<patch>` will be recognized as semver.
* fix(pack): GIT_DIR/GIT_WORK_TREE env vars may interfere #39279fleesk2026-04-22
| | | | | | | | Problem: With GIT_DIR/GIT_WORK_TREE set, the LSP on the vim.pack.update() confirmation buffer does not show the correct git log on hover. Solution: Temporarily remove the git vars from the environment.
* test(pack): increase retry timeout for startup test #39125Evgeni Chasnovski2026-04-16
| | | | | | | | Problem: Unreliable test on Windows which sometimes fails with too many failed retries. Solution: Increase timeout in hopes that it will be enough to make it pass more frequently. This should not affect fast and already working platforms.
* test(pack): use n.rmdir() to delete directories #39046Evgeni Chasnovski2026-04-14
| | | | | | | Problem: using `vim.fs.rm(dir_path, { force = true, recursive = true })` can result in an error on Windows if the process has a handle to it. Solution: Use `n.rmdir()` helper in cases when its possible side effects (like changing working directory) does not matter.
* test: fix s390x failuresJustin M. Keyes2026-03-29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Problem: failures in s390x CI. Solution: - runtime/lua/man.lua: parse_path() can return nil but 3 callers didn't handle it. - skip some tests on s390x. TODO: - TODO: why "build/bin/xxd is not executable" on s390x? - TODO: other failures, not addressed (see below). OTHER FAILURES: FAILED test/functional/treesitter/fold_spec.lua @ 87: treesitter foldexpr recomputes fold levels after lines are added/removed test/functional/treesitter/fold_spec.lua:95: Expected objects to be the same. Passed in: (table: 0x4013c18940) { [1] = '0' [2] = '0' [3] = '0' *[4] = '0' [5] = '0' ... Expected: (table: 0x4005acf900) { [1] = '0' [2] = '0' [3] = '>1' *[4] = '1' [5] = '1' ... stack traceback: (tail call): ? test/functional/treesitter/fold_spec.lua:95: in function <test/functional/treesitter/fold_spec.lua:87> FAILED test/functional/treesitter/select_spec.lua @ 52: treesitter incremental-selection works test/functional/treesitter/select_spec.lua:63: Expected objects to be the same. Passed in: (string) 'bar(2)' Expected: (string) 'foo(1)' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:63: in function <test/functional/treesitter/select_spec.lua:52> FAILED test/functional/treesitter/select_spec.lua @ 69: treesitter incremental-selection repeat test/functional/treesitter/select_spec.lua:82: Expected objects to be the same. Passed in: (string) '2' Expected: (string) '4' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:82: in function <test/functional/treesitter/select_spec.lua:69> FAILED test/functional/treesitter/select_spec.lua @ 98: treesitter incremental-selection history test/functional/treesitter/select_spec.lua:111: Expected objects to be the same. Passed in: (string) 'bar(2)' Expected: (string) 'foo(1)' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:111: in function <test/functional/treesitter/select_spec.lua:98> FAILED test/functional/treesitter/select_spec.lua @ 186: treesitter incremental-selection with injections works test/functional/treesitter/select_spec.lua:201: Expected objects to be the same. Passed in: (string) 'lua' Expected: (string) 'foo' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:201: in function <test/functional/treesitter/select_spec.lua:186> FAILED test/functional/treesitter/select_spec.lua @ 216: treesitter incremental-selection with injections ignores overlapping nodes test/functional/treesitter/select_spec.lua:231: Expected objects to be the same. Passed in: (string) ' )' Expected: (string) ' foo(' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:231: in function <test/functional/treesitter/select_spec.lua:216> FAILED test/functional/treesitter/select_spec.lua @ 307: treesitter incremental-selection with injections handles disjointed trees test/functional/treesitter/select_spec.lua:337: Expected objects to be the same. Passed in: (string) 'int' Expected: (string) '1}' stack traceback: (tail call): ? test/functional/treesitter/select_spec.lua:337: in function <test/functional/treesitter/select_spec.lua:307> ERROR test/functional/treesitter/parser_spec.lua @ 562: treesitter parser API can run async parses with string parsers test/functional/treesitter/parser_spec.lua:565: attempt to index a nil value stack traceback: test/functional/testnvim/exec_lua.lua:124: in function <test/functional/testnvim/exec_lua.lua:105> (tail call): ? (tail call): ? test/functional/treesitter/parser_spec.lua:563: in function <test/functional/treesitter/parser_spec.lua:562> FAILED test/functional/core/job_spec.lua @ 1157: jobs jobstop() kills entire process tree #6530 test/functional/core/job_spec.lua:1244: retry() attempts: 94 test/functional/core/job_spec.lua:1246: Expected objects to be the same. Passed in: (table: 0x401dd74b30) { [name] = 'sleep <defunct>' [pid] = 33579 [ppid] = 1 } Expected: (userdata) 'vim.NIL' stack traceback: test/testutil.lua:89: in function 'retry' test/functional/core/job_spec.lua:1244: in function <test/functional/core/job_spec.lua:1157>
* fix(pack): set `source` in progress report #38511Evgeni Chasnovski2026-03-27
| | | | | | | | | Problem: Progress reports via `nvim_echo()` gained an ability to set `source` and `vim.pack` doesn't currently set one. Solution: Set `source` to 'vim.pack'. Ideally, the title then can be something else more informative (like "update", "download", etc.), but it is used when showing progress messages. So it has to be "vim.pack" in this case.
* fix(pack): add trailing newline to lockfile #38469Justin Mayhew2026-03-24
| | | | | | | | Problem: JSON files should end with a trailing newline so that Unix tools work as expected, Git doesn't report "No newline at end of file" and to avoid noise in diffs from editors and other tools adding the missing newline. Solution: Add trailing newline.
* fix(pack): ensure `data` spec is passed in events during lockfile sync #38139Evgeni Chasnovski2026-03-03
| | | | | | | | | | | | | | | | | | | | | | | Problem: During initial "bootstrap" via lockfile synchronization, the whole plugin specification is reconstructed from the lockfile data, ignoring potential user changes added in the first `vim.pack.add()`. This is enough in most situations since it is the only data needed for actual installation. However, this affects specification passed to `PackChanged[Pre]` events. In particular, `data` field is missing which can be a problem if there is a `PackChanged kind=install` hook that uses that field (like with some kind of `build` method used during install). And there might be different `version` set in `vim.pack.add()`. Solution: Pass the `specs` input of the first `vim.pack.add()` down to lockfile synchronization and use it to reconstruct plugin specification for the to-be-installed plugin. If present among the user's `specs`, it is used but with forced `src` from the lockfile (as it is the one used during installation). Note that this still has a caveat when using separate `vim.pack.add()`, as only the specs from the first input (when the lockfile synchronization happens) is taken into account.
* test: start test runners in Xtest_xdg dir (#37964)zeertzjq2026-02-20
| | | | | | | | | | | | | | This is a better way to prevent parallel tests from interfering with each other, as there are many ways files can be created and deleted in tests, so enforcing different file names is hard. Using $TMPDIR can also work in most cases, but 'backipskip' etc. have special defaults for $TMPDIR. Symlink runtime/, src/, test/ and README.md to Xtest_xdg dir to make tests more convenient (and symlinking test/ is required for busted). Also, use README.md instead of test/README.md in the Ex mode inccommand test, as test/README.md no longer contains 'N' char.
* fix(pack): explicitly close confirmation window #37861Evgeni Chasnovski2026-02-14
| | | | | | | | | Problem: Executing `nvim_buf_delete()` does not guarantee that the window which shows the buffer is going to close after `:write` or `:quit`. In particular, if there is no listed buffer present. Solution: Explicitly close the window that was created for confirmation buffer. Use `pcall` to catch cases when the window was already closed or when it is the last window.
* fix(pack): close confirmation buffer, not tabpage #37756Evgeni Chasnovski2026-02-08
|
* test(pack): cover that events work when installing from the lockfileEvgeni Chasnovski2026-01-29
|
* test(pack): use a helper to `vim.pack.add` inside `testnvim` instanceEvgeni Chasnovski2026-01-16
| | | | | | | | | | | | | | | | | | | | | | | | | | Problem: A lot of test cases require executing `vim.pack.add()` inside `testnvim` instance to install and/or add plugins. This requires access to the information about tested repo sources (stored in a Lua variable inside a runner instance), so it needs to go `exec_lua(function() vim.pack.add({...}) end)` route, which in formatted form adds two more lines. Since it is very frequent, it pads test files with (mostly avoidable and unncessary) lines. Solution: Add `vim_pack_add()` helper specifically to execute `vim.pack.add` inside `testnvim` instance. Use it where possible: usually when `vim.pack.add` is not a subject of the test. It is not quite doable for some cases, though: - When error in `vim.pack.add` is tested. - When specification includes `vim.version.range` (since `exec_lua` seems to not transfer necessary class metatables). This removes ~100 lines from the file at the cost of sometimes using `vim_pack_add` and sometimes having to reside to `vim.pack.add`. Which seems like a positive trade-off. This also allows making a more compact `exec_lua('vim.pack.update()')` instructions since `vim.pack.update` doesn't depend on the upvalues stored in test runner instance.
* test(pack): make various small cleanupsEvgeni Chasnovski2026-01-16
| | | | | | | | | | | | Problem: `vim.pack` tests might benefit from a cleanup Solution: Do the cleanup: - Use `t.retry` instead of full `vim.uv.sleep` to make tests quicker. - Use `pack_assert_content` helper for a common task of checking the content of a special Lua file inside an installed plugin. This is used as a proxy of installed plugin's state on the disk. - Use `([[...]]):format()` approach (instead of array of strings) in one more place. Should improve readability.
* fix(pack): actually checkout proper version of submodulesEvgeni Chasnovski2026-01-12
| | | | | | | | | | | | | | | | Problem: Installing plugin with submodules doesn't check out their state (due to `git clone --no-checkout` to not end up with default branch code in case of invalid `version`). Updating a plugin with submodules doesn't update their state. Solution: Update `git_checkout` helper to account for submodules. Another approach would be `git checkout --recurse-submodules ...`, but that doesn't seem to allow `--filter=blob:none` for submodules, which is nice to have. Also make `git_clone` wrapper simpler since `--no-checkout` makes `--recurse-submodules` and `--also-filter-submodules` do nothing.
* test(pack): adjust `add` startup tests to just sleep with longer timeoutEvgeni Chasnovski2026-01-12
| | | | | | | | | | | | | Problem: The `add` startup tests mock startup process which requires *some* amount of time to be done. Previous solution with `vim.wait` to wait just enough time to register that startup script has finished doesn't seem to work as intended (it just wait full time). Its timeout is also seems to be barely enough to pass on Windows CI. Which will be a problem with more `git` actions done on startup in the future/next commit. Solution: Just sleep predetermined amount of time and explicitly check if startup script finished executing.
* fix(pack): skip `git stash` during installEvgeni Chasnovski2026-01-12
| | | | | | | | | | Problem: Installing plugin is done via `git clone --no-checkout ...` (to not end up with default branch code in case of invalid `version`). This leaves cloned repo in a state that `git stash` will actually add an entry to the stash list. Although not critical, better to not have that if possible. Solution: explicitly skip `git stash` step in checkout during install.
* Merge #37113 from echasnovski/pack-better-revertJustin M. Keyes2025-12-30
|\
| * feat(pack): allow running `update()` without Internet connectionEvgeni Chasnovski2025-12-26
| | | | | | | | | | | | | | | | | | | | Problem: There is now way to run `update()` without Int