defaults_spec.lua (40648B)
1 -- 2 -- Tests for default options and environment decisions. 3 -- 4 -- See editor/defaults_spec.lua for default autocmds, mappings, commands, and menus. 5 -- 6 7 local t = require('test.testutil') 8 local n = require('test.functional.testnvim')() 9 local Screen = require('test.functional.ui.screen') 10 11 local assert_alive = n.assert_alive 12 local assert_log = t.assert_log 13 local api = n.api 14 local command = n.command 15 local clear = n.clear 16 local exc_exec = n.exc_exec 17 local eval = n.eval 18 local eq = t.eq 19 local ok = t.ok 20 local fn = n.fn 21 local insert = n.insert 22 local neq = t.neq 23 local mkdir = t.mkdir 24 local rmdir = n.rmdir 25 local tbl_contains = vim.tbl_contains 26 local expect_exit = n.expect_exit 27 local check_close = n.check_close 28 local is_os = t.is_os 29 30 local testlog = 'Xtest-defaults-log' 31 32 describe('startup defaults', function() 33 describe(':filetype', function() 34 local function expect_filetype(expected) 35 local screen = Screen.new(50, 4) 36 command('filetype') 37 screen:expect([[ 38 ^ | 39 {1:~ }|*2 40 ]] .. expected) 41 end 42 43 it('all ON after `-u NORC`', function() 44 clear('-u', 'NORC') 45 expect_filetype('filetype detection:ON plugin:ON indent:ON |') 46 end) 47 48 it('all ON after `:syntax …` #7765', function() 49 clear('-u', 'NORC', '--cmd', 'syntax on') 50 expect_filetype('filetype detection:ON plugin:ON indent:ON |') 51 clear('-u', 'NORC', '--cmd', 'syntax off') 52 expect_filetype('filetype detection:ON plugin:ON indent:ON |') 53 end) 54 55 it('all OFF after `-u NONE`', function() 56 clear('-u', 'NONE') 57 expect_filetype('filetype detection:OFF plugin:OFF indent:OFF |') 58 end) 59 60 it('explicit OFF stays OFF', function() 61 clear('-u', 'NORC', '--cmd', 'syntax off | filetype off | filetype plugin indent off') 62 expect_filetype('filetype detection:OFF plugin:OFF indent:OFF |') 63 clear('-u', 'NORC', '--cmd', 'syntax off | filetype plugin indent off') 64 expect_filetype('filetype detection:ON plugin:OFF indent:OFF |') 65 clear('-u', 'NORC', '--cmd', 'filetype indent off') 66 expect_filetype('filetype detection:ON plugin:ON indent:OFF |') 67 clear('-u', 'NORC', '--cmd', 'syntax off | filetype off') 68 expect_filetype('filetype detection:OFF plugin:(on) indent:(on) |') 69 -- Swap the order. 70 clear('-u', 'NORC', '--cmd', 'filetype off | syntax off') 71 expect_filetype('filetype detection:OFF plugin:(on) indent:(on) |') 72 end) 73 74 it('all ON after early `:filetype … on`', function() 75 -- `:filetype … on` should not change the defaults. #7765 76 -- Only an explicit `:filetype … off` sets OFF. 77 78 clear('-u', 'NORC', '--cmd', 'filetype on') 79 expect_filetype('filetype detection:ON plugin:ON indent:ON |') 80 clear('-u', 'NORC', '--cmd', 'filetype plugin on') 81 expect_filetype('filetype detection:ON plugin:ON indent:ON |') 82 clear('-u', 'NORC', '--cmd', 'filetype indent on') 83 expect_filetype('filetype detection:ON plugin:ON indent:ON |') 84 end) 85 86 it('late `:filetype … off` stays OFF', function() 87 clear('-u', 'NORC', '-c', 'filetype off') 88 expect_filetype('filetype detection:OFF plugin:(on) indent:(on) |') 89 clear('-u', 'NORC', '-c', 'filetype plugin off') 90 expect_filetype('filetype detection:ON plugin:OFF indent:ON |') 91 clear('-u', 'NORC', '-c', 'filetype indent off') 92 expect_filetype('filetype detection:ON plugin:ON indent:OFF |') 93 end) 94 end) 95 96 describe('syntax', function() 97 it('enabled by `-u NORC`', function() 98 clear('-u', 'NORC') 99 eq(1, eval('g:syntax_on')) 100 end) 101 102 it('disabled by `-u NONE`', function() 103 clear('-u', 'NONE') 104 eq(0, eval('exists("g:syntax_on")')) 105 end) 106 107 it('`:syntax off` stays off', function() 108 -- early 109 clear('-u', 'NORC', '--cmd', 'syntax off') 110 eq(0, eval('exists("g:syntax_on")')) 111 -- late 112 clear('-u', 'NORC', '-c', 'syntax off') 113 eq(0, eval('exists("g:syntax_on")')) 114 end) 115 116 it('":if 0|syntax on|endif" does not affect default #8728', function() 117 clear('-u', 'NORC', '--cmd', ':if 0|syntax on|endif') 118 eq(1, eval('exists("g:syntax_on")')) 119 clear('-u', 'NORC', '--cmd', ':if 0|syntax off|endif') 120 eq(1, eval('exists("g:syntax_on")')) 121 end) 122 end) 123 124 describe("'fillchars'", function() 125 it('vert/fold flags', function() 126 clear() 127 local screen = Screen.new(50, 5) 128 command('set laststatus=0') 129 insert([[ 130 1 131 2 132 3 133 4]]) 134 command('normal! ggjzfj') 135 command('vsp') 136 screen:expect([[ 137 1 │1 | 138 {13:^+-- 2 lines: 2··········}│{13:+-- 2 lines: 2·········}| 139 4 │4 | 140 {1:~ }│{1:~ }| 141 | 142 ]]) 143 144 -- ambiwidth=double defaults to single-byte fillchars. 145 command('set ambiwidth=double') 146 screen:expect([[ 147 1 |1 | 148 {13:^+-- 2 lines: 2----------}|{13:+-- 2 lines: 2---------}| 149 4 |4 | 150 {1:~ }|{1:~ }| 151 | 152 ]]) 153 154 -- change "vert" character to single-cell 155 fn.setcellwidths({ { 0x2502, 0x2502, 1 } }) 156 screen:expect([[ 157 1 │1 | 158 {13:^+-- 2 lines: 2----------}│{13:+-- 2 lines: 2---------}| 159 4 │4 | 160 {1:~ }│{1:~ }| 161 | 162 ]]) 163 164 -- change "vert" character to double-cell 165 fn.setcellwidths({ { 0x2502, 0x2502, 2 } }) 166 screen:expect([[ 167 1 |1 | 168 {13:^+-- 2 lines: 2----------}|{13:+-- 2 lines: 2---------}| 169 4 |4 | 170 {1:~ }|{1:~ }| 171 | 172 ]]) 173 174 -- "vert" character should still default to single-byte fillchars because of setcellwidths(). 175 command('set ambiwidth=single') 176 screen:expect([[ 177 1 |1 | 178 {13:^+-- 2 lines: 2··········}|{13:+-- 2 lines: 2·········}| 179 4 |4 | 180 {1:~ }|{1:~ }| 181 | 182 ]]) 183 end) 184 end) 185 186 it("'shadafile' ('viminfofile')", function() 187 local env = { 188 XDG_DATA_HOME = 'Xtest-userdata', 189 XDG_STATE_HOME = 'Xtest-userstate', 190 XDG_CONFIG_HOME = 'Xtest-userconfig', 191 } 192 finally(function() 193 command('set shadafile=NONE') -- Avoid writing shada file on exit 194 rmdir('Xtest-userstate') 195 os.remove('Xtest-foo') 196 end) 197 198 clear { args = {}, args_rm = { '-i', '--cmd' }, env = env } 199 -- Default 'shadafile' is empty. 200 -- This means use the default location. :help shada-file-name 201 eq('', api.nvim_get_option_value('shadafile', {})) 202 eq('', api.nvim_get_option_value('viminfofile', {})) 203 -- Handles viminfo/viminfofile as alias for shada/shadafile. 204 eq('\n shadafile=', eval('execute("set shadafile?")')) 205 eq('\n shadafile=', eval('execute("set viminfofile?")')) 206 eq("\n shada=!,'100,<50,s10,h,r/tmp/,r/private/", eval('execute("set shada?")')) 207 eq("\n shada=!,'100,<50,s10,h,r/tmp/,r/private/", eval('execute("set viminfo?")')) 208 209 -- Remove /tmp/ exclusion so test works when run in temp directories. 210 command("set shada=!,'100,<50,s10,h") 211 -- Check that shada data (such as v:oldfiles) is saved/restored. 212 command('edit Xtest-foo') 213 command('write') 214 local f = eval('fnamemodify(@%,":p")') 215 assert(string.len(f) > 3) 216 expect_exit(command, 'qall') 217 clear { args = {}, args_rm = { '-i', '--cmd' }, env = env } 218 eq({ f }, eval('v:oldfiles')) 219 end) 220 221 it("'packpath'", function() 222 clear { 223 args_rm = { 'runtimepath' }, 224 } 225 -- Defaults to &runtimepath. 226 eq(api.nvim_get_option_value('runtimepath', {}), api.nvim_get_option_value('packpath', {})) 227 228 -- Does not follow modifications to runtimepath. 229 command('set runtimepath+=foo') 230 neq(api.nvim_get_option_value('runtimepath', {}), api.nvim_get_option_value('packpath', {})) 231 command('set packpath+=foo') 232 eq(api.nvim_get_option_value('runtimepath', {}), api.nvim_get_option_value('packpath', {})) 233 end) 234 235 it('v:progpath is set to the absolute path', function() 236 clear() 237 eq(eval("fnamemodify(v:progpath, ':p')"), eval('v:progpath')) 238 end) 239 240 describe('$NVIM_LOG_FILE', function() 241 local xdgdir = 'Xtest-startup-xdg-logpath' 242 local xdgstatedir = is_os('win') and xdgdir .. '/nvim-data' or xdgdir .. '/nvim' 243 after_each(function() 244 os.remove('Xtest-logpath') 245 rmdir(xdgdir) 246 end) 247 248 it('is used if expansion succeeds', function() 249 clear({ env = { 250 NVIM_LOG_FILE = 'Xtest-logpath', 251 } }) 252 eq('Xtest-logpath', eval('$NVIM_LOG_FILE')) 253 end) 254 255 it('defaults to stdpath("log")/nvim.log if empty', function() 256 eq(true, mkdir(xdgdir) and mkdir(xdgstatedir)) 257 clear({ 258 env = { 259 XDG_STATE_HOME = xdgdir, 260 NVIM_LOG_FILE = '', -- Empty is invalid. 261 }, 262 }) 263 eq(xdgstatedir .. '/nvim.log', t.fix_slashes(eval('$NVIM_LOG_FILE'))) 264 end) 265 266 it('defaults to stdpath("log")/nvim.log if invalid', function() 267 eq(true, mkdir(xdgdir) and mkdir(xdgstatedir)) 268 clear({ 269 env = { 270 XDG_STATE_HOME = xdgdir, 271 NVIM_LOG_FILE = '.', -- Any directory is invalid. 272 }, 273 }) 274 eq(xdgstatedir .. '/nvim.log', t.fix_slashes(eval('$NVIM_LOG_FILE'))) 275 -- Avoid "failed to open $NVIM_LOG_FILE" noise in test output. 276 expect_exit(command, 'qall!') 277 end) 278 end) 279 end) 280 281 describe('XDG defaults', function() 282 -- Need separate describe() blocks to not run clear() twice. 283 -- Do not put before_each() here for the same reasons. 284 285 after_each(function() 286 check_close() 287 os.remove(testlog) 288 end) 289 290 it("&runtimepath data-dir matches stdpath('data') #9910", function() 291 clear() 292 local rtp = eval('split(&runtimepath, ",")') 293 local rv = {} 294 local expected = ( 295 is_os('win') and { [[\nvim-data\site]], [[\nvim-data\site\after]] } 296 or { '/nvim/site', '/nvim/site/after' } 297 ) 298 299 for _, v in ipairs(rtp) do 300 local m = string.match(v, [=[[/\]nvim[^/\]*[/\]site.*$]=]) 301 if m and not tbl_contains(rv, m) then 302 table.insert(rv, m) 303 end 304 end 305 eq(expected, rv) 306 end) 307 308 describe('with empty/broken environment', function() 309 it('sets correct defaults', function() 310 clear({ 311 env = { 312 XDG_CONFIG_HOME = nil, 313 XDG_DATA_HOME = nil, 314 XDG_CACHE_HOME = nil, 315 XDG_STATE_HOME = nil, 316 XDG_RUNTIME_DIR = nil, 317 XDG_CONFIG_DIRS = nil, 318 XDG_DATA_DIRS = nil, 319 LOCALAPPDATA = nil, 320 HOMEPATH = nil, 321 HOMEDRIVE = nil, 322 HOME = nil, 323 TEMP = nil, 324 VIMRUNTIME = nil, 325 USER = nil, 326 }, 327 }) 328 329 eq('.', api.nvim_get_option_value('backupdir', {})) 330 eq('.', api.nvim_get_option_value('viewdir', {})) 331 eq('.', api.nvim_get_option_value('directory', {})) 332 eq('.', api.nvim_get_option_value('undodir', {})) 333 ok((fn.tempname()):len() > 4) 334 end) 335 end) 336 337 local function vimruntime_and_libdir() 338 local vimruntime = eval('$VIMRUNTIME') 339 -- libdir is hard to calculate reliably across various ci platforms 340 -- local libdir = string.gsub(vimruntime, "share/nvim/runtime$", "lib/nvim") 341 local libdir = api.nvim__get_lib_dir() 342 return vimruntime, libdir 343 end 344 345 local env_sep = is_os('win') and ';' or ':' 346 local data_dir = is_os('win') and 'nvim-data' or 'nvim' 347 local state_dir = is_os('win') and 'nvim-data' or 'nvim' 348 local root_path = is_os('win') and 'C:' or '' 349 350 describe('with too long XDG vars', function() 351 before_each(function() 352 clear({ 353 -- Ensure valid --listen address despite broken XDG vars (else Nvim won't start). 354 args = { '--listen', is_os('win') and '' or t.tmpname(false) }, 355 args_rm = { 'runtimepath' }, 356 env = { 357 NVIM_LOG_FILE = testlog, 358 XDG_CONFIG_HOME = (root_path .. ('/x'):rep(4096)), 359 XDG_CONFIG_DIRS = (root_path .. ('/a'):rep(2048) .. env_sep .. root_path .. ('/b'):rep( 360 2048 361 ) .. (env_sep .. root_path .. '/c'):rep(512)), 362 XDG_DATA_HOME = (root_path .. ('/X'):rep(4096)), 363 XDG_RUNTIME_DIR = (root_path .. ('/X'):rep(4096)), 364 XDG_STATE_HOME = (root_path .. ('/X'):rep(4096)), 365 XDG_DATA_DIRS = (root_path .. ('/A'):rep(2048) .. env_sep .. root_path .. ('/B'):rep( 366 2048 367 ) .. (env_sep .. root_path .. '/C'):rep(512)), 368 }, 369 }) 370 end) 371 372 it('are correctly set', function() 373 if not is_os('win') then 374 -- Broken XDG vars cause serverstart() to fail (except on Windows, where servernames are not 375 -- informed by $XDG_STATE_HOME). 376 t.matches('Failed to start server: no such file or directory', t.pcall_err(fn.serverstart)) 377 assert_log('Failed to start server: no such file or directory: /X/X/X', testlog, 10) 378 end 379 380 local vimruntime, libdir = vimruntime_and_libdir() 381 382 eq( 383 ( 384 t.fix_slashes( 385 root_path 386 .. ('/x'):rep(4096) 387 .. '/nvim' 388 .. ',' 389 .. root_path 390 .. ('/a'):rep(2048) 391 .. '/nvim' 392 .. ',' 393 .. root_path 394 .. ('/b'):rep(2048) 395 .. '/nvim' 396 .. (',' .. root_path .. '/c/nvim') 397 .. ',' 398 .. root_path 399 .. ('/X'):rep(4096) 400 .. '/' 401 .. data_dir 402 .. '/site' 403 .. ',' 404 .. root_path 405 .. ('/A'):rep(2048) 406 .. '/nvim/site' 407 .. ',' 408 .. root_path 409 .. ('/B'):rep(2048) 410 .. '/nvim/site' 411 .. (',' .. root_path .. '/C/nvim/site') 412 .. ',' 413 .. vimruntime 414 .. ',' 415 .. libdir 416 .. (',' .. root_path .. '/C/nvim/site/after') 417 .. ',' 418 .. root_path 419 .. ('/B'):rep(2048) 420 .. '/nvim/site/after' 421 .. ',' 422 .. root_path 423 .. ('/A'):rep(2048) 424 .. '/nvim/site/after' 425 .. ',' 426 .. root_path 427 .. ('/X'):rep(4096) 428 .. '/' 429 .. data_dir 430 .. '/site/after' 431 .. (',' .. root_path .. '/c/nvim/after') 432 .. ',' 433 .. root_path 434 .. ('/b'):rep(2048) 435 .. '/nvim/after' 436 .. ',' 437 .. root_path 438 .. ('/a'):rep(2048) 439 .. '/nvim/after' 440 .. ',' 441 .. root_path 442 .. ('/x'):rep(4096) 443 .. '/nvim/after' 444 ) 445 ), 446 t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) 447 ) 448 command('set runtimepath&') 449 command('set backupdir&') 450 command('set directory&') 451 command('set undodir&') 452 command('set viewdir&') 453 eq( 454 ( 455 t.fix_slashes( 456 root_path 457 .. ('/x'):rep(4096) 458 .. '/nvim' 459 .. ',' 460 .. root_path 461 .. ('/a'):rep(2048) 462 .. '/nvim' 463 .. ',' 464 .. root_path 465 .. ('/b'):rep(2048) 466 .. '/nvim' 467 .. (',' .. root_path .. '/c/nvim') 468 .. ',' 469 .. root_path 470 .. ('/X'):rep(4096) 471 .. '/' 472 .. data_dir 473 .. '/site' 474 .. ',' 475 .. root_path 476 .. ('/A'):rep(2048) 477 .. '/nvim/site' 478 .. ',' 479 .. root_path 480 .. ('/B'):rep(2048) 481 .. '/nvim/site' 482 .. (',' .. root_path .. '/C/nvim/site') 483 .. ',' 484 .. vimruntime 485 .. ',' 486 .. libdir 487 .. (',' .. root_path .. '/C/nvim/site/after') 488 .. ',' 489 .. root_path 490 .. ('/B'):rep(2048) 491 .. '/nvim/site/after' 492 .. ',' 493 .. root_path 494 .. ('/A'):rep(2048) 495 .. '/nvim/site/after' 496 .. ',' 497 .. root_path 498 .. ('/X'):rep(4096) 499 .. '/' 500 .. data_dir 501 .. '/site/after' 502 .. (',' .. root_path .. '/c/nvim/after') 503 .. ',' 504 .. root_path 505 .. ('/b'):rep(2048) 506 .. '/nvim/after' 507 .. ',' 508 .. root_path 509 .. ('/a'):rep(2048) 510 .. '/nvim/after' 511 .. ',' 512 .. root_path 513 .. ('/x'):rep(4096) 514 .. '/nvim/after' 515 ) 516 ), 517 t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) 518 ) 519 eq( 520 '.,' .. root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/backup//', 521 t.fix_slashes(api.nvim_get_option_value('backupdir', {})) 522 ) 523 eq( 524 root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/swap//', 525 t.fix_slashes(api.nvim_get_option_value('directory', {})) 526 ) 527 eq( 528 root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/undo//', 529 t.fix_slashes(api.nvim_get_option_value('undodir', {})) 530 ) 531 eq( 532 root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/view//', 533 t.fix_slashes(api.nvim_get_option_value('viewdir', {})) 534 ) 535 end) 536 end) 537 538 describe('with expandable XDG vars', function() 539 before_each(function() 540 clear({ 541 -- Ensure valid --listen address despite broken XDG vars (else Nvim won't start). 542 args = { '--listen', is_os('win') and '' or t.tmpname(false) }, 543 args_rm = { 'runtimepath' }, 544 env = { 545 NVIM_LOG_FILE = testlog, 546 XDG_CONFIG_HOME = '$XDG_DATA_HOME', 547 XDG_CONFIG_DIRS = '$XDG_DATA_DIRS', 548 XDG_DATA_HOME = '$XDG_CONFIG_HOME', 549 XDG_RUNTIME_DIR = '$XDG_RUNTIME_DIR', 550 XDG_STATE_HOME = '$XDG_CONFIG_HOME', 551 XDG_DATA_DIRS = '$XDG_CONFIG_DIRS', 552 }, 553 }) 554 end) 555 556 after_each(function() 557 command('set shadafile=NONE') -- Avoid writing shada file on exit 558 end) 559 560 it('are not expanded', function() 561 if not is_os('win') then 562 -- Broken XDG vars cause serverstart() to fail (except on Windows, where servernames are not 563 -- informed by $XDG_STATE_HOME). 564 t.matches('Failed to start server: no such file or directory', t.pcall_err(fn.serverstart)) 565 assert_log( 566 'Failed to start server: no such file or directory: %$XDG_RUNTIME_DIR%/', 567 testlog, 568 10 569 ) 570 end 571 572 local vimruntime, libdir = vimruntime_and_libdir() 573 eq( 574 ( 575 t.fix_slashes( 576 '$XDG_DATA_HOME/nvim' 577 .. ',$XDG_DATA_DIRS/nvim' 578 .. ',$XDG_CONFIG_HOME/' 579 .. data_dir 580 .. '/site' 581 .. ',$XDG_CONFIG_DIRS/nvim/site' 582 .. ',' 583 .. vimruntime 584 .. ',' 585 .. libdir 586 .. ',$XDG_CONFIG_DIRS/nvim/site/after' 587 .. ',$XDG_CONFIG_HOME/' 588 .. data_dir 589 .. '/site/after' 590 .. ',$XDG_DATA_DIRS/nvim/after' 591 .. ',$XDG_DATA_HOME/nvim/after' 592 ) 593 ), 594 t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) 595 ) 596 command('set runtimepath&') 597 command('set backupdir&') 598 command('set directory&') 599 command('set undodir&') 600 command('set viewdir&') 601 eq( 602 ( 603 t.fix_slashes( 604 '$XDG_DATA_HOME/nvim' 605 .. ',$XDG_DATA_DIRS/nvim' 606 .. ',$XDG_CONFIG_HOME/' 607 .. data_dir 608 .. '/site' 609 .. ',$XDG_CONFIG_DIRS/nvim/site' 610 .. ',' 611 .. vimruntime 612 .. ',' 613 .. libdir 614 .. ',$XDG_CONFIG_DIRS/nvim/site/after' 615 .. ',$XDG_CONFIG_HOME/' 616 .. data_dir 617 .. '/site/after' 618 .. ',$XDG_DATA_DIRS/nvim/after' 619 .. ',$XDG_DATA_HOME/nvim/after' 620 ) 621 ), 622 t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) 623 ) 624 eq( 625 ('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'), 626 t.fix_slashes(api.nvim_get_option_value('backupdir', {})) 627 ) 628 eq( 629 ('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'), 630 t.fix_slashes(api.nvim_get_option_value('directory', {})) 631 ) 632 eq( 633 ('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'), 634 t.fix_slashes(api.nvim_get_option_value('undodir', {})) 635 ) 636 eq( 637 ('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'), 638 t.fix_slashes(api.nvim_get_option_value('viewdir', {})) 639 ) 640 command('set all&') 641 eq( 642 t.fix_slashes( 643 '$XDG_DATA_HOME/nvim' 644 .. ',$XDG_DATA_DIRS/nvim' 645 .. ',$XDG_CONFIG_HOME/' 646 .. data_dir 647 .. '/site' 648 .. ',$XDG_CONFIG_DIRS/nvim/site' 649 .. ',' 650 .. vimruntime 651 .. ',' 652 .. libdir 653 .. ',$XDG_CONFIG_DIRS/nvim/site/after' 654 .. ',$XDG_CONFIG_HOME/' 655 .. data_dir 656 .. '/site/after' 657 .. ',$XDG_DATA_DIRS/nvim/after' 658 .. ',$XDG_DATA_HOME/nvim/after' 659 ), 660 t.fix_slashes(api.nvim_get_option_value('runtimepath', {})) 661 ) 662 eq( 663 ('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'), 664 t.fix_slashes(api.nvim_get_option_value('backupdir', {})) 665 ) 666 eq( 667 ('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'), 668 t.fix_slashes(api.nvim_get_option_value('directory', {})) 669 ) 670 eq( 671 ('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'), 672 t.fix_slashes(api.nvim_get_option_value('undodir', {})) 673 ) 674 eq( 675 ('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'), 676 t.fix_slashes(api.nvim_get_option_value('viewdir', {})) 677 ) 678 eq(nil, (fn.tempname()):match('XDG_RUNTIME_DIR')) 679 end) 680 end) 681 682 describe('with commas', function() 683 before_each(function() 684 clear({ 685 args_rm = { 'runtimepath' }, 686 env = { 687 XDG_CONFIG_HOME = ', , ,', 688 XDG_CONFIG_DIRS = ',-,-,' .. env_sep .. '-,-,-', 689 XDG_DATA_HOME = ',=,=,', 690 XDG_STATE_HOME = ',=,=,', 691 XDG_DATA_DIRS = ',≡,≡,' .. env_sep .. '≡,≡,≡', 692 }, 693 }) 694 end) 695 696 it('are escaped properly', function() 697 local vimruntime, libdir = vimruntime_and_libdir() 698 local path_sep = is_os('win') and '\\' or '/' 699 eq( 700 ( 701 '\\, \\, \\,' 702 .. path_sep 703 .. 'nvim' 704 .. ',\\,-\\,-\\,' 705 .. path_sep 706 .. 'nvim' 707 .. ',-\\,-\\,-' 708 .. path_sep 709 .. 'nvim' 710 .. ',\\,=\\,=\\,' 711 .. path_sep 712 .. data_dir 713 .. path_sep 714 .. 'site' 715 .. ',\\,≡\\,≡\\,' 716 .. path_sep 717 .. 'nvim' 718 .. path_sep 719 .. 'site' 720 .. ',≡\\,≡\\,≡' 721 .. path_sep 722 .. 'nvim' 723 .. path_sep 724 .. 'site' 725 .. ',' 726 .. vimruntime 727 .. ',' 728 .. libdir 729 .. ',≡\\,≡\\,≡' 730 .. path_sep 731 .. 'nvim' 732 .. path_sep 733 .. 'site' 734 .. path_sep 735 .. 'after' 736 .. ',\\,≡\\,≡\\,' 737 .. path_sep 738 .. 'nvim' 739 .. path_sep 740 .. 'site' 741 .. path_sep 742 .. 'after' 743 .. ',\\,=\\,=\\,' 744 .. path_sep 745 .. data_dir 746 .. path_sep 747 .. 'site' 748 .. path_sep 749 .. 'after' 750 .. ',-\\,-\\,-' 751 .. path_sep 752 .. 'nvim' 753 .. path_sep 754 .. 'after' 755 .. ',\\,-\\,-\\,' 756 .. path_sep 757 .. 'nvim' 758 .. path_sep 759 .. 'after' 760 .. ',\\, \\, \\,' 761 .. path_sep 762 .. 'nvim' 763 .. path_sep 764 .. 'after' 765 ), 766 api.nvim_get_option_value('runtimepath', {}) 767 ) 768 command('set runtimepath&') 769 command('set backupdir&') 770 command('set directory&') 771 command('set undodir&') 772 command('set viewdir&') 773 eq( 774 ( 775 '\\, \\, \\,' 776 .. path_sep 777 .. 'nvim' 778 .. ',\\,-\\,-\\,' 779 .. path_sep 780 .. 'nvim' 781 .. ',-\\,-\\,-' 782 .. path_sep 783 .. 'nvim' 784 .. ',\\,=\\,=\\,' 785 .. path_sep 786 .. '' 787 .. data_dir 788 .. '' 789 .. path_sep 790 .. 'site' 791 .. ',\\,≡\\,≡\\,' 792 .. path_sep 793 .. 'nvim' 794 .. path_sep 795 .. 'site' 796 .. ',≡\\,≡\\,≡' 797 .. path_sep 798 .. 'nvim' 799 .. path_sep 800 .. 'site' 801 .. ',' 802 .. vimruntime 803 .. ',' 804 .. libdir 805 .. ',≡\\,≡\\,≡' 806 .. path_sep 807 .. 'nvim' 808 .. path_sep 809 .. 'site' 810 .. path_sep 811 .. 'after' 812 .. ',\\,≡\\,≡\\,' 813 .. path_sep 814 .. 'nvim' 815 .. path_sep 816 .. 'site' 817 .. path_sep 818 .. 'after' 819 .. ',\\,=\\,=\\,' 820 .. path_sep 821 .. '' 822 .. data_dir 823 .. '' 824 .. path_sep 825 .. 'site' 826 .. path_sep 827 .. 'after' 828 .. ',-\\,-\\,-' 829 .. path_sep 830 .. 'nvim' 831 .. path_sep 832 .. 'after' 833 .. ',\\,-\\,-\\,' 834 .. path_sep 835 .. 'nvim' 836 .. path_sep 837 .. 'after' 838 .. ',\\, \\, \\,' 839 .. path_sep 840 .. 'nvim' 841 .. path_sep 842 .. 'after' 843 ), 844 api.nvim_get_option_value('runtimepath', {}) 845 ) 846 eq( 847 '.,\\,=\\,=\\,' .. path_sep .. state_dir .. '' .. path_sep .. 'backup' .. (path_sep):rep(2), 848 api.nvim_get_option_value('backupdir', {}) 849 ) 850 eq( 851 '\\,=\\,=\\,' 852 .. path_sep 853 .. '' 854 .. state_dir 855 .. '' 856 .. path_sep 857 .. 'swap' 858 .. (path_sep):rep(2), 859 api.nvim_get_option_value('directory', {}) 860 ) 861 eq( 862 '\\,=\\,=\\,' 863 .. path_sep 864 .. '' 865 .. state_dir 866 .. '' 867 .. path_sep 868 .. 'undo' 869 .. (path_sep):rep(2), 870 api.nvim_get_option_value('undodir', {}) 871 ) 872 eq( 873 '\\,=\\,=\\,' 874 .. path_sep 875 .. '' 876 .. state_dir 877 .. '' 878 .. path_sep 879 .. 'view' 880 .. (path_sep):rep(2), 881 api.nvim_get_option_value('viewdir', {}) 882 ) 883 end) 884 end) 885 end) 886 887 describe('stdpath()', function() 888 after_each(function() 889 check_close() 890 os.remove(testlog) 891 end) 892 893 --- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions 894 --- due to XDG_CONFIG_HOME, XDG_DATA_HOME and XDG_STATE_HOME being the same. 895 --- @param name string 896 local function maybe_data(name) 897 return is_os('win') and name .. '-data' or name 898 end 899 900 local datadir = maybe_data('nvim') 901 local statedir = maybe_data('nvim') 902 local env_sep = is_os('win') and ';' or ':' 903 904 it('works', function() 905 clear() -- Do not explicitly set any env vars. 906 907 eq('nvim', fn.fnamemodify(fn.stdpath('cache'), ':t')) 908 eq('nvim', fn.fnamemodify(fn.stdpath('config'), ':t')) 909 eq(datadir, fn.fnamemodify(fn.stdpath('data'), ':t')) 910 eq(statedir, fn.fnamemodify(fn.stdpath('state'), ':t')) 911 eq('table', type(fn.stdpath('config_dirs'))) 912 eq('table', type(fn.stdpath('data_dirs'))) 913 eq('string', type(fn.stdpath('run'))) 914 assert_alive() -- Check for crash. #8393 915 end) 916 917 it('failure modes', function() 918 clear() 919 eq('Vim(call):E6100: "capybara" is not a valid stdpath', exc_exec('call stdpath("capybara")')) 920 eq('Vim(call):E6100: "" is not a valid stdpath', exc_exec('call stdpath("")')) 921 eq('Vim(call):E6100: "23" is not a valid stdpath', exc_exec('call stdpath(23)')) 922 eq('Vim(call):E731: Using a Dictionary as a String', exc_exec('call stdpath({"eris": 23})')) 923 eq('Vim(call):E730: Using a List as a String', exc_exec('call stdpath([23])')) 924 end) 925 926 it('$NVIM_APPNAME', function() 927 local appname = 'NVIM_APPNAME_TEST' .. ('_'):rep(106) 928 clear({ env = { NVIM_APPNAME = appname, NVIM_LOG_FILE = testlog } }) 929 eq(appname, fn.fnamemodify(fn.stdpath('config'), ':t')) 930 eq(appname, fn.fnamemodify(fn.stdpath('cache'), ':t')) 931 eq(maybe_data(appname), fn.fnamemodify(fn.stdpath('log'), ':t')) 932 eq(maybe_data(appname), fn.fnamemodify(fn.stdpath('data'), ':t')) 933 eq(maybe_data(appname), fn.fnamemodify(fn.stdpath('state'), ':t')) 934 -- config_dirs and data_dirs are empty on windows, so don't check them on 935 -- that platform 936 if not is_os('win') then 937 eq(appname, fn.fnamemodify(fn.stdpath('config_dirs')[1], ':t')) 938 eq(appname, fn.fnamemodify(fn.stdpath('data_dirs')[1], ':t')) 939 end 940 assert_alive() -- Check for crash. #8393 941 942 -- Check that Nvim rejects invalid APPNAMEs 943 local function test_appname(testAppname, expected_exitcode) 944 local p = n.spawn_wait({ 945 args = { '--listen', 'x', '+qall!' }, 946 env = { NVIM_APPNAME = testAppname }, 947 }) 948 eq(expected_exitcode, p.status) 949 end 950 -- Invalid appnames: 951 test_appname('a/../b', 1) 952 test_appname('../a', 1) 953 test_appname('a/..', 1) 954 test_appname('..', 1) 955 test_appname('.', 1) 956 test_appname('/', 1) 957 test_appname(is_os('win') and 'C:/a/b' or '/a/b', 1) 958 -- Valid appnames: 959 test_appname('a/b', 0) 960 test_appname('a/b\\c', 0) 961 end) 962 963 it('$NVIM_APPNAME relative path', function() 964 local tmpdir = t.tmpname(false) 965 t.mkdir(tmpdir) 966 967 clear({ 968 args_rm = { '--listen' }, 969 env = { 970 NVIM_APPNAME = 'relative/appname', 971 NVIM_LOG_FILE = testlog, 972 TMPDIR = tmpdir, 973 }, 974 }) 975 976 t.matches(vim.pesc(tmpdir), t.fix_slashes(fn.tempname())) 977 t.assert_nolog('tempdir', testlog, 100) 978 t.assert_nolog('TMPDIR', testlog, 100) 979 t.matches([=[[/\\]relative%-appname.[^/\\]+]=], api.nvim_get_vvar('servername')) 980 end) 981 982 describe('returns a String', function() 983 describe('with "config"', function() 984 it('knows XDG_CONFIG_HOME', function() 985 clear({ 986 env = { 987 XDG_CONFIG_HOME = '/home/docwhat/.config', 988 }, 989 }) 990 eq('/home/docwhat/.config/nvim', t.fix_slashes(fn.stdpath('config'))) 991 end) 992 993 it('handles changes during runtime', function() 994 clear({ env = { 995 XDG_CONFIG_HOME = '/home/original', 996 } }) 997 eq('/home/original/nvim', t.fix_slashes(fn.stdpath('config'))) 998 command("let $XDG_CONFIG_HOME='/home/new'") 999 eq('/home/new/nvim', t.fix_slashes(fn.stdpath('config'))) 1000 end) 1001 1002 it("doesn't expand $VARIABLES", function() 1003 clear({ 1004 env = { 1005 XDG_CONFIG_HOME = '$VARIABLES', 1006 VARIABLES = 'this-should-not-happen', 1007 }, 1008 }) 1009 eq('$VARIABLES/nvim', t.fix_slashes(fn.stdpath('config'))) 1010 end) 1011 1012 it("doesn't expand ~/", function() 1013 clear({ env = { 1014 XDG_CONFIG_HOME = '~/frobnitz', 1015 } }) 1016 eq('~/frobnitz/nvim', t.fix_slashes(fn.stdpath('config'))) 1017 end) 1018 end) 1019 1020 describe('with "data"', function() 1021 it('knows XDG_DATA_HOME', function() 1022 clear({ env = { 1023 XDG_DATA_HOME = '/home/docwhat/.local', 1024 } }) 1025 eq('/home/docwhat/.local/' .. datadir, t.fix_slashes(fn.stdpath('data'))) 1026 end) 1027 1028 it('handles changes during runtime', function() 1029 clear({ env = { 1030 XDG_DATA_HOME = '/home/original', 1031 } }) 1032 eq('/home/original/' .. datadir, t.fix_slashes(fn.stdpath('data'))) 1033 command("let $XDG_DATA_HOME='/home/new'") 1034 eq('/home/new/' .. datadir, t.fix_slashes(fn.stdpath('data'))) 1035 end) 1036 1037 it("doesn't expand $VARIABLES", function() 1038 clear({ 1039 env = { 1040 XDG_DATA_HOME = '$VARIABLES', 1041 VARIABLES = 'this-should-not-happen', 1042 }, 1043 }) 1044 eq('$VARIABLES/' .. datadir, t.fix_slashes(fn.stdpath('data'))) 1045 end) 1046 1047 it("doesn't expand ~/", function() 1048 clear({ env = { 1049 XDG_DATA_HOME = '~/frobnitz', 1050 } }) 1051 eq('~/frobnitz/' .. datadir, t.fix_slashes(fn.stdpath('data'))) 1052 end) 1053 end) 1054 1055 describe('with "state"', function() 1056 it('knows XDG_STATE_HOME', function() 1057 clear({ 1058 env = { 1059 XDG_STATE_HOME = '/home/docwhat/.local', 1060 }, 1061 }) 1062 eq('/home/docwhat/.local/' .. statedir, t.fix_slashes(fn.stdpath('state'))) 1063 end) 1064 1065 it('handles changes during runtime', function() 1066 clear({ env = { 1067 XDG_STATE_HOME = '/home/original', 1068 } }) 1069 eq('/home/original/' .. statedir, t.fix_slashes(fn.stdpath('state'))) 1070 command("let $XDG_STATE_HOME='" .. '/home/new' .. "'") 1071 eq('/home/new/' .. statedir, t.fix_slashes(fn.stdpath('state'))) 1072 end) 1073 1074 it("doesn't expand $VARIABLES", function() 1075 clear({ 1076 env = { 1077 XDG_STATE_HOME = '$VARIABLES', 1078 VARIABLES = 'this-should-not-happen', 1079 }, 1080 }) 1081 eq('$VARIABLES/' .. statedir, t.fix_slashes(fn.stdpath('state'))) 1082 end) 1083 1084 it("doesn't expand ~/", function() 1085 clear({ env = { 1086 XDG_STATE_HOME = '~/frobnitz', 1087 } }) 1088 eq('~/frobnitz/' .. statedir, t.fix_slashes(fn.stdpath('state'))) 1089 end) 1090 end) 1091 1092 describe('with "cache"', function() 1093 it('knows XDG_CACHE_HOME', function() 1094 clear({ 1095 env = { 1096 XDG_CACHE_HOME = '/home/docwhat/.cache', 1097 }, 1098 }) 1099 eq('/home/docwhat/.cache/nvim', t.fix_slashes(fn.stdpath('cache'))) 1100 end) 1101 1102 it('handles changes during runtime', function() 1103 clear({ env = { 1104 XDG_CACHE_HOME = '/home/original', 1105 } }) 1106 eq('/home/original/nvim', t.fix_slashes(fn.stdpath('cache'))) 1107 command("let $XDG_CACHE_HOME='" .. '/home/new' .. "'") 1108 eq('/home/new/nvim', t.fix_slashes(fn.stdpath('cache'))) 1109 end) 1110 1111 it("doesn't expand $VARIABLES", function() 1112 clear({ 1113 env = { 1114 XDG_CACHE_HOME = '$VARIABLES', 1115 VARIABLES = 'this-should-not-happen', 1116 }, 1117 }) 1118 eq('$VARIABLES/nvim', t.fix_slashes(fn.stdpath('cache'))) 1119 end) 1120 1121 it("doesn't expand ~/", function() 1122 clear({ env = { 1123 XDG_CACHE_HOME = '~/frobnitz', 1124 } }) 1125 eq('~/frobnitz/nvim', t.fix_slashes(fn.stdpath('cache'))) 1126 end) 1127 end) 1128 end) 1129 1130 describe('returns a List', function() 1131 -- Some OS specific variables the system would have set. 1132 local function base_env() 1133 if is_os('win') then 1134 return { 1135 HOME = 'C:\\Users\\docwhat', -- technically, is not a usual PATH 1136 HOMEDRIVE = 'C:', 1137 HOMEPATH = '\\Users\\docwhat', 1138 LOCALAPPDATA = 'C:\\Users\\docwhat\\AppData\\Local', 1139 NVIM_LOG_FILE = testlog, 1140 TEMP = 'C:\\Users\\docwhat\\AppData\\Local\\Temp', 1141 TMPDIR = 'C:\\Users\\docwhat\\AppData\\Local\\Temp', 1142 TMP = 'C:\\Users\\docwhat\\AppData\\Local\\Temp', 1143 } 1144 else 1145 return { 1146 HOME = '/home/docwhat', 1147 HOMEDRIVE = 'HOMEDRIVE-should-be-ignored', 1148 HOMEPATH = 'HOMEPATH-should-be-ignored', 1149 LOCALAPPDATA = 'LOCALAPPDATA-should-be-ignored', 1150 NVIM_LOG_FILE = testlog, 1151 TEMP = 'TEMP-should-be-ignored', 1152 TMPDIR = 'TMPDIR-should-be-ignored', 1153 TMP = 'TMP-should-be-ignored', 1154 } 1155 end 1156 end 1157 1158 local function set_paths_via_system(var_name, paths) 1159 local env = base_env() 1160 env[var_name] = table.concat(paths, env_sep) 1161 clear({ env = env }) 1162 end 1163 1164 local function set_paths_at_runtime(var_name, paths) 1165 clear({ env = base_env() }) 1166 api.nvim_set_var('env_val', table.concat(paths, env_sep)) 1167 command(('let $%s=g:env_val'):format(var_name)) 1168 end 1169 1170 local function behaves_like_dir_list_env(msg, stdpath_arg, env_var_name, paths, expected_paths) 1171 describe(msg, function() 1172 it('set via system', function() 1173 set_paths_via_system(env_var_name, paths) 1174 eq(expected_paths, t.fix_slashes(fn.stdpath(stdpath_arg))) 1175 if not is_os('win') then 1176 assert_log( 1177 '$TMPDIR tempdir not a directory[^\n]*TMPDIR%-should%-be%-ignored', 1178 testlog, 1179 100 1180 ) 1181 end 1182 end) 1183 1184 it('set at runtime', function() 1185 set_paths_at_runtime(env_var_name, paths) 1186 eq(expected_paths, t.fix_slashes(fn.stdpath(stdpath_arg))) 1187 if not is_os('win') then 1188 assert_log( 1189 '$TMPDIR tempdir not a directory[^\n]*TMPDIR%-should%-be%-ignored', 1190 testlog, 1191 100 1192 ) 1193 end 1194 end) 1195 end) 1196 end 1197 1198 describe('with "config_dirs"', function() 1199 behaves_like_dir_list_env( 1200 'handles XDG_CONFIG_DIRS with one path', 1201 'config_dirs', 1202 'XDG_CONFIG_DIRS', 1203 { 1204 t.fix_slashes('/home/docwhat/.config'), 1205 }, 1206 { 1207 t.fix_slashes('/home/docwhat/.config/nvim'), 1208 } 1209 ) 1210 1211 behaves_like_dir_list_env( 1212 'handles XDG_CONFIG_DIRS with two paths', 1213 'config_dirs', 1214 'XDG_CONFIG_DIRS', 1215 { 1216 t.fix_slashes('/home/docwhat/.config'), 1217 t.fix_slashes('/etc/config'), 1218 }, 1219 { 1220 t.fix_slashes('/home/docwhat/.config/nvim'), 1221 t.fix_slashes('/etc/config/nvim'), 1222 } 1223 ) 1224 1225 behaves_like_dir_list_env( 1226 "doesn't expand $VAR and $IBLES", 1227 'config_dirs', 1228 'XDG_CONFIG_DIRS', 1229 { '$HOME', '$TMP' }, 1230 { 1231 t.fix_slashes('$HOME/nvim'), 1232 t.fix_slashes('$TMP/nvim'), 1233 } 1234 ) 1235 1236 behaves_like_dir_list_env("doesn't expand ~/", 'config_dirs', 'XDG_CONFIG_DIRS', { 1237 t.fix_slashes('~/.oldconfig'), 1238 t.fix_slashes('~/.olderconfig'), 1239 }, { 1240 t.fix_slashes('~/.oldconfig/nvim'), 1241 t.fix_slashes('~/.olderconfig/nvim'), 1242 }) 1243 end) 1244 1245 describe('with "data_dirs"', function() 1246 behaves_like_dir_list_env('knows XDG_DATA_DIRS with one path', 'data_dirs', 'XDG_DATA_DIRS', { 1247 t.fix_slashes('/home/docwhat/.data'), 1248 }, { 1249 t.fix_slashes('/home/docwhat/.data/nvim'), 1250 }) 1251 1252 behaves_like_dir_list_env( 1253 'knows XDG_DATA_DIRS with two paths', 1254 'data_dirs', 1255 'XDG_DATA_DIRS', 1256 { 1257 t.fix_slashes('/home/docwhat/.data'), 1258 t.fix_slashes('/etc/local'), 1259 }, 1260 { 1261 t.fix_slashes('/home/docwhat/.data/nvim'), 1262 t.fix_slashes('/etc/local/nvim'), 1263 } 1264 ) 1265 1266 behaves_like_dir_list_env( 1267 "doesn't expand $VAR and $IBLES", 1268 'data_dirs', 1269 'XDG_DATA_DIRS', 1270 { '$HOME', '$TMP' }, 1271 { 1272 t.fix_slashes('$HOME/nvim'), 1273 t.fix_slashes('$TMP/nvim'), 1274 } 1275 ) 1276 1277 behaves_like_dir_list_env("doesn't expand ~/", 'data_dirs', 'XDG_DATA_DIRS', { 1278 t.fix_slashes('~/.oldconfig'), 1279 t.fix_slashes('~/.olderconfig'), 1280 }, { 1281 t.fix_slashes('~/.oldconfig/nvim'), 1282 t.fix_slashes('~/.olderconfig/nvim'), 1283 }) 1284 end) 1285 end) 1286 end)