dev_test.txt (21594B)
1 *dev_test.txt* Nvim 2 3 4 NVIM REFERENCE MANUAL 5 6 7 Writing tests for Nvim *dev-test* 8 9 Type |gO| to see the table of contents. 10 11 ============================================================================== 12 Writing tests for Nvim 13 14 Nvim has a powerful yet simple test framework. It's approximately 7x better 15 than whatever you use at work. 16 17 Each test starts a new Nvim process (10-30ms) which is discarded after the 18 test finishes. You assert stuff using `t.eq()` and `screen:expect()` (which 19 automatically waits as needed). That's pretty much it. 20 21 TODO: Expose the test framework as a public interface, for use in 3P plugins: 22 https://github.com/neovim/neovim/issues/34592 23 24 ============================================================================== 25 Test framework 26 27 Tests are broadly divided into unit tests (`test/unit/`), functional tests 28 (`test/functional/`) and old tests (`test/old/testdir/`). 29 30 - Unit testing is achieved by compiling the tests as a shared library which is 31 loaded and called by [LuaJit FFI](https://luajit.org/ext_ffi.html). 32 - Functional tests are driven by RPC, so they do not require LuaJit (as 33 opposed to Lua). They are essentially "integration" tests, they test the 34 full system. But they are fast. 35 36 You can learn [Lua concepts 15 minutes](https://learnxinyminutes.com/docs/lua/), 37 see also |lua-guide|. Use any existing test as a template to start writing new 38 tests, or see |dev-quickstart|. 39 40 Tests are run by the `/cmake/RunTests.cmake` script using `busted` (a Lua test-runner). 41 For some failures, `./build/nvim.log` (or `$NVIM_LOG_FILE`) may provide insight. 42 43 Depending on the presence of binaries (e.g., `xclip`) some tests will be 44 skipped. 45 46 ============================================================================== 47 Test Layout 48 49 - `/test/benchmark` : benchmarks 50 - `/test/functional` : functional tests 51 - `/test/unit` : unit tests 52 - `/test/old/testdir` : old tests (from Vim) 53 - `/test/config` : contains `*.in` files which are transformed into `*.lua` 54 files using `configure_file` CMake command: this is for accessing CMake 55 variables in Lua tests. 56 - `/test/includes` : include-files for use by luajit `ffi.cdef` C definitions 57 parser: normally used to make macros not accessible via this mechanism 58 accessible the other way. 59 - `/test/*/preload.lua` : modules preloaded by busted `--helper` option 60 - `/test/**/testutil.lua` : common utility functions in the context of the test 61 runner 62 - `/test/**/testnvim.lua` : common utility functions in the context of the 63 test session (RPC channel to the Nvim child process created by clear() for each test) 64 - `/test/*/**/*_spec.lua` : actual tests. Files that do not end with 65 `_spec.lua` are libraries like `/test/**/testutil.lua`, except that they have 66 some common topic. 67 68 69 ============================================================================== 70 Running tests *dev-run-test* 71 72 EXECUTING TESTS 73 74 To run all tests (except "old" tests): > 75 make test 76 77 To run only _unit_ tests: > 78 make unittest 79 80 To run only _functional_ tests: > 81 make functionaltest 82 83 To run functional tests in parallel (used in CI): > 84 cmake --build build --target functionaltest_parallel -j2 85 cmake --build build --target functionaltest_summary 86 87 88 LEGACY TESTS 89 90 To run all legacy Vim tests: > 91 make oldtest 92 93 To run a _single_ legacy test file you can use either: > 94 # Specify only the test file name, not the full path. 95 make oldtest TEST_FILE=test_syntax.vim 96 or: > 97 make test/old/testdir/test_syntax.vim 98 99 100 DEBUGGING TESTS 101 102 - Each test gets a test id which looks like "T123". This also appears in the 103 log file. Child processes spawned from a test appear in the logs with the 104 _parent_ name followed by "/c". Example: > 105 106 DBG 2022-06-15T18:37:45.226 T57.58016.0 UI: flush 107 DBG 2022-06-15T18:37:45.226 T57.58016.0 inbuf_poll:442: blocking... events_enabled=0 events_pending=0 108 DBG 2022-06-15T18:37:45.227 T57.58016.0/c UI: stop 109 INF 2022-06-15T18:37:45.227 T57.58016.0/c os_exit:595: Nvim exit: 0 110 DBG 2022-06-15T18:37:45.229 T57.58016.0 read_cb:118: closing Stream (0x7fd5d700ea18): EOF (end of file) 111 INF 2022-06-15T18:37:45.229 T57.58016.0 on_proc_exit:400: exited: pid=58017 status=0 stoptime=0 112 113 - You can set `$GDB` to [run functional tests under gdbserver](https://github.com/neovim/neovim/pull/1527): >sh 114 GDB=1 TEST_FILE=test/functional/api/buffer_spec.lua TEST_FILTER='nvim_buf_set_text works$' make functionaltest 115 < 116 Read more about |dev-filter-test|. 117 118 Then, in another terminal: >sh 119 gdb -ex 'target remote localhost:7777' build/bin/nvim 120 < 121 If `$VALGRIND` is also set it will pass `--vgdb=yes` to valgrind instead of 122 starting gdbserver directly. 123 124 See `nvim_argv` in `test/functional/testnvim.lua`. 125 126 - Hanging tests can happen due to unexpected "press-enter" prompts. The 127 default screen width is 50 columns. Commands that try to print lines longer 128 than 50 columns in the command-line, e.g. `:edit very...long...path`, will 129 trigger the prompt. Try using a shorter path, or `:silent edit`. 130 - If you can't figure out what is going on, try to visualize the screen. Put 131 this at the beginning of your test: > 132 local Screen = require('test.functional.ui.screen') 133 local screen = Screen.new() 134 screen:attach() 135 < Then put `screen:snapshot_util()` anywhere in your test. See the comments in 136 `test/functional/ui/screen.lua` for more info. 137 138 DEBUGGING LUA TEST CODE 139 140 Debugging Lua test code is a bit involved. Get your shopping list ready, you'll 141 need to install and configure: 142 143 1. [nvim-dap](https://github.com/mfussenegger/nvim-dap) 144 2. [local-lua-debugger-vscode](https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#local-lua-debugger-vscode) 145 3. [nlua](https://github.com/mfussenegger/nlua) 146 4. [one-small-step-for-vimkind](https://github.com/jbyuki/one-small-step-for-vimkind) (called `osv`) 147 5. A `nbusted` command in `$PATH`. This command can be a copy of `busted` with 148 `exec '/usr/bin/lua5.1'"` replaced with `"exec '/usr/bin/nlua'"` (or the 149 path to your `nlua`) 150 151 152 The setup roughly looks like this: > 153 154 ┌─────────────────────────┐ 155 │ nvim used for debugging │◄────┐ 156 └─────────────────────────┘ │ 157 │ │ 158 ▼ │ 159 ┌─────────────────┐ │ 160 │ local-lua-debug │ │ 161 └─────────────────┘ │ 162 │ │ 163 ▼ │ 164 ┌─────────┐ │ 165 │ nbusted │ │ 166 └─────────┘ │ 167 │ │ 168 ▼ │ 169 ┌───────────┐ │ 170 │ test-case │ │ 171 └───────────┘ │ 172 │ │ 173 ▼ │ 174 ┌────────────────────┐ │ 175 │ nvim test-instance │ │ 176 └────────────────────┘ │ 177 │ ┌─────┐ │ 178 └──►│ osv │─────────────────┘ 179 └─────┘ 180 181 182 With these installed you can use a configuration like this: > 183 184 local dap = require("dap") 185 186 187 local function free_port() 188 local tcp = vim.loop.new_tcp() 189 assert(tcp) 190 tcp:bind('127.0.0.1', 0) 191 local port = tcp:getsockname().port 192 tcp:shutdown() 193 tcp:close() 194 return port 195 end 196 197 198 local name = "nvim-test-case" -- arbitrary name 199 local config = { 200 name = name, 201 202 -- value of type must match the key used in `dap.adapters["local-lua"] = ...` from step 2) 203 type = "local-lua", 204 205 request = "launch", 206 cwd = "${workspaceFolder}", 207 program = { 208 command = "nbusted", 209 }, 210 args = { 211 "--ignore-lua", 212 "--lazy", 213 "--helper=test/functional/preload.lua", 214 "--lpath=build/?.lua", 215 "--lpath=?.lua", 216 217 -- path to file to debug, could be replaced with a hardcoded string 218 function() 219 return vim.api.nvim_buf_get_name(0) 220 end, 221 222 -- You can filter to specific test-case by adding: 223 -- '--filter="' .. test_case_name .. '"', 224 }, 225 env = { 226 OSV_PORT = free_port 227 } 228 } 229 230 -- Whenever the config is used it needs to launch a second debug session that attaches to `osv` 231 -- This makes it possible to step into `exec_lua` code blocks 232 setmetatable(config, { 233 234 __call = function(c) 235 ---@param session dap.Session 236 dap.listeners.after.event_initialized["nvim_debug"] = function(session) 237 if session.config.name ~= name then 238 return 239 end 240 dap.listeners.after.event_initialized["nvim_debug"] = nil 241 vim.defer_fn(function() 242 dap.run({ 243 name = "attach-osv", 244 type = "nlua", -- value must match the `dap.adapters` definition key for osv 245 request = "attach", 246 port = session.config.env.OSV_PORT, 247 }) 248 end, 500) 249 end 250 251 return c 252 end, 253 }) 254 255 256 You can either add this configuration to your `dap.configurations.lua` list as 257 described in `:help dap-configuration` or create it dynamically in a 258 user-command or function and call it directly via `dap.run(config)`. The latter 259 is useful if you use treesitter to find the test case around a cursor location 260 with a query like the following and set the `--filter` property to it. >query 261 262 (function_call 263 name: (identifier) @name (#any-of? @name "describe" "it") 264 arguments: (arguments 265 (string) @str 266 ) 267 ) 268 269 Limitations: 270 271 - You need to add the following boilerplate to each spec file where you want to 272 be able to stop at breakpoints within the test-case code: > 273 if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then 274 require("lldebugger").start() 275 end 276 < This is a [local-lua-debugger limitation](https://github.com/tomblind/local-lua-debugger-vscode?tab=readme-ov-file#busted) 277 - You cannot step into code of files which get baked into the nvim binary 278 (such as `_core/*.lua` and `inspect.lua`). 279 280 281 ------------------------------------------------------------------------------ 282 Filtering tests *dev-filter-test* 283 284 FILTER BY NAME 285 286 Tests can be filtered by setting a pattern of test name to `TEST_FILTER` or `TEST_FILTER_OUT`. > 287 288 it('foo api',function() 289 ... 290 end) 291 it('bar api',function() 292 ... 293 end) 294 295 To run only test with filter name: > 296 TEST_FILTER='foo.*api' make functionaltest 297 298 To run all tests except ones matching a filter: > 299 TEST_FILTER_OUT='foo.*api' make functionaltest 300 301 FILTER BY FILE 302 303 To run a _specific_ unit test: > 304 TEST_FILE=test/unit/foo.lua make unittest 305 306 or > 307 cmake -E env "TEST_FILE=test/unit/foo.lua" cmake --build build --target unittest 308 309 To run a _specific_ functional test: > 310 TEST_FILE=test/functional/foo.lua make functionaltest 311 312 or > 313 cmake -E env "TEST_FILE=test/functional/foo.lua" cmake --build build --target functionaltest 314 315 To _repeat_ a test: > 316 BUSTED_ARGS="--repeat=100 --no-keep-going" TEST_FILE=test/functional/foo_spec.lua make functionaltest 317 318 or > 319 cmake -E env "TEST_FILE=test/functional/foo_spec.lua" cmake -E env BUSTED_ARGS="--repeat=100 --no-keep-going" cmake --build build --target functionaltest 320 321 FILTER BY TAG 322 323 Tests can be "tagged" by adding `#` before a token in the test description. > 324 325 it('#foo bar baz', function() 326 ... 327 end) 328 it('#foo another test', function() 329 ... 330 end) 331 332 To run only the tagged tests: > 333 TEST_TAG=foo make functionaltest 334 335 NOTE: 336 337 - `TEST_FILE` is not a pattern string like `TEST_TAG` or `TEST_FILTER`. The 338 given value to `TEST_FILE` must be a path to an existing file. 339 - Both `TEST_TAG` and `TEST_FILTER` filter tests by the string descriptions 340 found in `it()` and `describe()`. 341 342 343 ============================================================================== 344 Writing tests *dev-write-test* 345 346 GUIDELINES 347 348 - Luajit needs to know about type and constant declarations used in function 349 prototypes. The 350 [testutil.lua](https://github.com/neovim/neovim/blob/master/test/unit/testutil.lua) 351 file automatically parses `types.h`, so types used in the tested functions 352 could be moved to it to avoid having to rewrite the declarations in the test 353 files. 354 - `#define` constants must be rewritten `const` or `enum` so they can be 355 "visible" to the tests. 356 - Use `pending()` or `t.skip()` to skip tests. 357 - Example: https://github.com/neovim/neovim/commit/5c1dc0fbe7388528875aff9d7b5055ad718014de#diff-bf80b24c724b0004e8418102f68b0679R18 358 - Note: If a test is skipped because of a non-actionable reason, we don't 359 want it to appear in the "pending" list. Include "N/A" in the skip 360 description, then it won't be added to the "pending" list. For example, if 361 a test is skipped because it's running on a non-LuaJit system, including 362 it in the "pending" list is just noise. Thus, its pending reason should 363 say "N/A": > 364 pending('N/A: missing LuaJIT FFI') 365 < 366 - Do not silently skip the test with `if-else`. If a functional test depends 367 on some external factor (e.g. the existence of `md5sum` on `$PATH`), _and_ 368 you can't mock or fake the dependency, then skip the test via `pending()` 369 if the external factor is missing. This ensures that the _total_ 370 test-count (success + fail + error + pending) is the same in all 371 environments. 372 - Note: `pending()` is ignored if it is missing an argument, unless it is 373 [contained in an `it()` block](https://github.com/neovim/neovim/blob/d21690a66e7eb5ebef18046c7a79ef898966d786/test/functional/ex_cmds/grep_spec.lua#L11). 374 Provide empty function argument if the `pending()` call is outside `it()` 375 Example: https://github.com/neovim/neovim/commit/5c1dc0fbe7388528875aff9d7b5055ad718014de#diff-bf80b24c724b0004e8418102f68b0679R18 376 377 WHERE TESTS GO 378 379 Tests in `/test/unit` and `/test/functional` are divided into groups by the 380 semantic component they are testing. 381 382 - Unit tests (`test/unit/`) should match 1-to-1 with the structure of 383 `src/nvim/`, because they are testing functions directly. E.g. unit-tests 384 for `src/nvim/undo.c` should live in `test/unit/undo_spec.lua`. 385 - Functional tests (`test/functional/`) are higher-level (plugins, UI, user 386 input) than unit tests; they are organized by concept. 387 - Try to find an existing `test/functional/*/*_spec.lua` group that makes 388 sense, before creating a new one. 389 390 391 ------------------------------------------------------------------------------ 392 Fixing tests *dev-fix-test* 393 394 FIXING HARNESS WARNINGS 395 396 > Nvim session T123 took 2000 milliseconds to exit 397 > This indicates a likely problem with the test even if it passed! 398 399 This may indicate a leak, because Nvim waits on uv handles before exiting. 400 Example: https://github.com/neovim/neovim/pull/35768 401 402 403 FIXING LINT FAILURES 404 405 `make lint` (and `make lintlua`) runs [LuaLS](https://github.com/LuaLS/lua-language-server/wiki/Annotations) 406 on the test code. 407 408 If a LuaLS/EmmyLS warning must be ignored, specify the warning code. Example: >lua 409 410 ---@diagnostic disable-next-line: unused-vararg 411 412 https://github.com/LuaLS/lua-language-server/wiki/Annotations#diagnostic 413 414 Ignore the smallest applicable scope (e.g. inside a function, not at the top of 415 the file). 416 417 418 ============================================================================== 419 Configuration *dev-test-config* 420 421 (TODO: clean this up, too many variables and some of them are not used anymore.) 422 423 Test behaviour is affected by environment variables. Currently supported 424 (Functional, Unit, Benchmarks) (when Defined; when set to _1_; when defined, 425 treated as Integer; when defined, treated as String; when defined, treated as 426 Number; !must be defined to function properly): 427 428 - `BUSTED_ARGS` (F) (U): arguments forwarded to `busted`. 429 430 - `CC` (U) (S): specifies which C compiler to use to preprocess files. 431 Currently only compilers with gcc-compatible arguments are supported. 432 433 - `GDB` (F) (D): makes nvim instances to be run under `gdbserver`. It will be 434 accessible on `localhost:7777`: use `gdb build/bin/nvim`, type `target remote :7777` inside. 435 436 - `GDBSERVER_PORT` (F) (I): overrides port used for `GDB`. 437 438 - `LOG_DIR` (FU) (S!): specifies where to seek for valgrind and ASAN log files. 439 440 - `VALGRIND` (F) (D): makes nvim instances to be run under `valgrind`. Log 441 files are named `valgrind-%p.log` in this case. Note that non-empty valgrind 442 log may fail tests. Valgrind arguments may be seen in 443 `/test/functional/testnvim.lua`. May be used in conjunction with `GDB`. 444 445 - `VALGRIND_LOG` (F) (S): overrides valgrind log file name used for `VALGRIND`. 446 447 - `TEST_COLORS` (F) (U) (D): enable pretty colors in test runner. Set to true by default. 448 449 - `TEST_SKIP_FRAGILE` (F) (D): makes test suite skip some fragile tests. 450 451 - `TEST_TIMEOUT` (FU) (I): specifies maximum time, in seconds, before the test 452 suite run is killed 453 454 - `NVIM_TEST` (FU) (D): lets Nvim process detect that it is running in a test. 455 456 - `NVIM_LUA_NOTRACK` (F) (D): disable reference counting of Lua objects 457 458 - `NVIM_PRG` (F) (S): path to Nvim executable (default: `build/bin/nvim`). 459 460 - `NVIM_TEST_MAIN_CDEFS` (U) (1): makes `ffi.cdef` run in main process. This 461 raises a possibility of bugs due to conflicts in header definitions, despite 462 the counters, but greatly speeds up unit tests by not requiring `ffi.cdef` to 463 do parsing of big strings with C definitions. 464 465 - `NVIM_TEST_PRINT_I` (U) (1): makes `cimport` print preprocessed, but not yet 466 filtered through `formatc` headers. Used to debug `formatc`. Printing is done 467 with the line numbers. 468 469 - `NVIM_TEST_PRINT_CDEF` (U) (1): makes `cimport` print final lines which will 470 be then passed to `ffi.cdef`. Used to debug errors `ffi.cdef` happens to 471 throw sometimes. 472 473 - `NVIM_TEST_PRINT_SYSCALLS` (U) (1): makes it print to stderr when syscall 474 wrappers are called and what they returned. Used to debug code which makes 475 unit tests be executed in separate processes. 476 477 - `NVIM_TEST_RUN_FAILING_TESTS` (U) (1): makes `itp` run tests which are known 478 to fail (marked by setting third argument to `true`). 479 480 - `NVIM_TEST_CORE_*` (FU) (S): a set of environment variables which specify 481 where to search for core files. Are supposed to be defined all at once. 482 483 - `NVIM_TEST_CORE_GLOB_DIRECTORY` (FU) (S): directory where core files are 484 located. May be `.`. This directory is then recursively searched for core 485 files. Note: this variable must be defined for any of the following to have 486 any effect. 487 488 - `NVIM_TEST_CORE_GLOB_RE` (FU) (S): regular expression which must be matched 489 by core files. E.g. `/core[^/]*$`. May be absent, in which case any file is 490 considered to be matched. 491 492 - `NVIM_TEST_CORE_EXC_RE` (FU) (S): regular expression which excludes certain 493 directories from searching for core files inside. E.g. use `^/%.deps$` to not 494 search inside `/.deps`. If absent, nothing is excluded. 495 496 - `NVIM_TEST_CORE_DB_CMD` (FU) (S): command to get backtrace out of the 497 debugger. E.g. `gdb -n -batch -ex "thread apply all bt full" 498 "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"`. Defaults to the example command. 499 This debug command may use environment variables `_NVIM_TEST_APP` (path to 500 application which is being debugged: normally either nvim or luajit) and 501 `_NVIM_TEST_CORE` (core file to get backtrace from). 502 503 - `NVIM_TEST_CORE_RANDOM_SKIP` (FU) (D): makes `check_cores` not check cores 504 after approximately 90% of the tests. Should be used when finding cores is 505 too hard for some reason. Normally (on OS X or when 506 `NVIM_TEST_CORE_GLOB_DIRECTORY` is defined and this variable is not) cores 507 are checked for after each test. 508 509 - `NVIM_TEST_INTEG` (F) (D): enables integration tests that makes real network 510 calls. By default these tests are skipped. When set to `1`, tests requiring external 511 HTTP requests (e.g `vim.net.request()`) will be run. 512 513 - `NVIM_TEST_RUN_TESTTEST` (U) (1): allows running 514 `test/unit/testtest_spec.lua` used to check how testing infrastructure works. 515 516 - `NVIM_TEST_TRACE_LEVEL` (U) (N): specifies unit tests tracing level: 517 - `0` disables tracing (the fastest, but you get no data if tests crash and 518 no core dump was generated), 519 - `1` leaves only C function calls and returns in the trace (faster than 520 recording everything), 521 - `2` records all function calls, returns and executed Lua source lines. 522 523 - `NVIM_TEST_TRACE_ON_ERROR` (U) (1): makes unit tests yield trace on error in 524 addition to regular error message. 525 526 - `NVIM_TEST_MAXTRACE` (U) (N): specifies maximum number of trace lines to 527 keep. Default is 1024. 528 529 - `OSV_PORT`: (F): launches `osv` listening on the given port within nvim test 530 instances. 531 532 533 vim:tw=78:ts=8:sw=4:et:ft=help:norl: