fs_spec.lua (25345B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 4 local clear = n.clear 5 local exec_lua = n.exec_lua 6 local eq = t.eq 7 local mkdir_p = n.mkdir_p 8 local rmdir = n.rmdir 9 local nvim_dir = n.nvim_dir 10 local command = n.command 11 local api = n.api 12 local fn = n.fn 13 local test_build_dir = t.paths.test_build_dir 14 local test_source_path = t.paths.test_source_path 15 local nvim_prog = n.nvim_prog 16 local is_os = t.is_os 17 local mkdir = t.mkdir 18 19 local nvim_prog_basename = is_os('win') and 'nvim.exe' or 'nvim' 20 21 local link_limit = is_os('win') and 64 or (is_os('mac') or is_os('bsd')) and 33 or 41 22 23 local test_basename_dirname_eq = { 24 '~/foo/', 25 '~/foo', 26 '~/foo/bar.lua', 27 'foo.lua', 28 ' ', 29 '', 30 '.', 31 '..', 32 '../', 33 '~', 34 '/usr/bin', 35 '/usr/bin/gcc', 36 '/', 37 '/usr/', 38 '/usr', 39 'c:/usr', 40 'c:/', 41 'c:', 42 'c:/users/foo', 43 'c:/users/foo/bar.lua', 44 'c:/users/foo/bar/../', 45 '~/foo/bar\\baz', 46 } 47 48 local tests_windows_paths = { 49 'c:\\usr', 50 'c:\\', 51 'c:', 52 'c:\\users\\foo', 53 'c:\\users\\foo\\bar.lua', 54 'c:\\users\\foo\\bar\\..\\', 55 } 56 57 setup(clear) 58 59 describe('vim.fs', function() 60 describe('parents()', function() 61 it('works', function() 62 local test_dir = nvim_dir .. '/test' 63 mkdir_p(test_dir) 64 local dirs = {} --- @type string[] 65 for dir in vim.fs.parents(test_dir .. '/foo.txt') do 66 dirs[#dirs + 1] = dir 67 if dir == test_build_dir then 68 break 69 end 70 end 71 eq({ test_dir, nvim_dir, test_build_dir }, dirs) 72 rmdir(test_dir) 73 end) 74 end) 75 76 describe('dirname()', function() 77 it('works', function() 78 eq(test_build_dir, vim.fs.dirname(nvim_dir)) 79 80 ---@param paths string[] 81 ---@param is_win? boolean 82 local function test_paths(paths, is_win) 83 local gsub = is_win and [[:gsub('\\', '/')]] or '' 84 local code = string.format( 85 [[ 86 local path = ... 87 return vim.fn.fnamemodify(path,':h')%s 88 ]], 89 gsub 90 ) 91 92 for _, path in ipairs(paths) do 93 eq(exec_lua(code, path), vim.fs.dirname(path), path) 94 end 95 end 96 97 test_paths(test_basename_dirname_eq) 98 if is_os('win') then 99 test_paths(tests_windows_paths, true) 100 end 101 end) 102 end) 103 104 describe('basename()', function() 105 it('works', function() 106 eq(nvim_prog_basename, vim.fs.basename(nvim_prog)) 107 108 ---@param paths string[] 109 ---@param is_win? boolean 110 local function test_paths(paths, is_win) 111 local gsub = is_win and [[:gsub('\\', '/')]] or '' 112 local code = string.format( 113 [[ 114 local path = ... 115 return vim.fn.fnamemodify(path,':t')%s 116 ]], 117 gsub 118 ) 119 120 for _, path in ipairs(paths) do 121 eq(exec_lua(code, path), vim.fs.basename(path), path) 122 end 123 end 124 125 test_paths(test_basename_dirname_eq) 126 if is_os('win') then 127 test_paths(tests_windows_paths, true) 128 end 129 end) 130 end) 131 132 describe('dir()', function() 133 before_each(function() 134 mkdir('testd') 135 mkdir('testd/a') 136 mkdir('testd/a/b') 137 mkdir('testd/a/b/c') 138 end) 139 140 after_each(function() 141 rmdir('testd') 142 end) 143 144 it('works', function() 145 eq( 146 true, 147 exec_lua(function() 148 for name, type in vim.fs.dir(nvim_dir) do 149 if name == nvim_prog_basename and type == 'file' then 150 return true 151 end 152 end 153 return false 154 end) 155 ) 156 end) 157 158 it('works with opts.depth, opts.skip and opts.follow', function() 159 io.open('testd/a1', 'w'):close() 160 io.open('testd/b1', 'w'):close() 161 io.open('testd/c1', 'w'):close() 162 io.open('testd/a/a2', 'w'):close() 163 io.open('testd/a/b2', 'w'):close() 164 io.open('testd/a/c2', 'w'):close() 165 io.open('testd/a/b/a3', 'w'):close() 166 io.open('testd/a/b/b3', 'w'):close() 167 io.open('testd/a/b/c3', 'w'):close() 168 io.open('testd/a/b/c/a4', 'w'):close() 169 io.open('testd/a/b/c/b4', 'w'):close() 170 io.open('testd/a/b/c/c4', 'w'):close() 171 172 local function run(dir, depth, skip, follow) 173 return exec_lua(function(follow_) 174 local r = {} --- @type table<string, string> 175 local skip_f --- @type function 176 if skip then 177 skip_f = function(n0) 178 if vim.tbl_contains(skip or {}, n0) then 179 return false 180 end 181 end 182 end 183 for name, type_ in vim.fs.dir(dir, { depth = depth, skip = skip_f, follow = follow_ }) do 184 r[name] = type_ 185 end 186 return r 187 end, follow) 188 end 189 190 local exp = {} 191 192 exp['a1'] = 'file' 193 exp['b1'] = 'file' 194 exp['c1'] = 'file' 195 exp['a'] = 'directory' 196 197 eq(exp, run('testd', 1)) 198 199 exp['a/a2'] = 'file' 200 exp['a/b2'] = 'file' 201 exp['a/c2'] = 'file' 202 exp['a/b'] = 'directory' 203 local lexp = vim.deepcopy(exp) 204 205 eq(exp, run('testd', 2)) 206 207 exp['a/b/a3'] = 'file' 208 exp['a/b/b3'] = 'file' 209 exp['a/b/c3'] = 'file' 210 exp['a/b/c'] = 'directory' 211 212 eq(exp, run('testd', 3)) 213 eq(exp, run('testd', 999, { 'a/b/c' })) 214 215 exp['a/b/c/a4'] = 'file' 216 exp['a/b/c/b4'] = 'file' 217 exp['a/b/c/c4'] = 'file' 218 219 eq(exp, run('testd', 999)) 220 221 vim.uv.fs_symlink(vim.uv.fs_realpath('testd/a'), 'testd/l', { junction = true, dir = true }) 222 lexp['l'] = 'link' 223 eq(lexp, run('testd', 2, nil, false)) 224 225 lexp['l/a2'] = 'file' 226 lexp['l/b2'] = 'file' 227 lexp['l/c2'] = 'file' 228 lexp['l/b'] = 'directory' 229 eq(lexp, run('testd', 2, nil, true)) 230 end) 231 232 it('follow=true handles symlink loop', function() 233 local cwd = 'testd/a/b/c' 234 local symlink = cwd .. '/link_loop' ---@type string 235 vim.uv.fs_symlink(vim.uv.fs_realpath(cwd), symlink, { junction = true, dir = true }) 236 237 eq( 238 link_limit, 239 exec_lua(function() 240 return #vim.iter(vim.fs.dir(cwd, { depth = math.huge, follow = true })):totable() 241 end) 242 ) 243 end) 244 end) 245 246 describe('find()', function() 247 it('works', function() 248 eq( 249 { test_build_dir .. '/bin' }, 250 vim.fs.find('bin', { path = nvim_dir, upward = true, type = 'directory' }) 251 ) 252 eq({ nvim_prog }, vim.fs.find(nvim_prog_basename, { path = test_build_dir, type = 'file' })) 253 254 local parent, name = nvim_dir:match('^(.*/)([^/]+)$') 255 eq({ nvim_dir }, vim.fs.find(name, { path = parent, upward = true, type = 'directory' })) 256 end) 257 258 it('follows symlinks', function() 259 local build_dir = test_build_dir ---@type string 260 local symlink = test_source_path .. '/build_link' ---@type string 261 vim.uv.fs_symlink(build_dir, symlink, { junction = true, dir = true }) 262 263 finally(function() 264 vim.uv.fs_unlink(symlink) 265 end) 266 267 local cases = { nvim_prog, symlink .. '/bin/' .. nvim_prog_basename } 268 table.sort(cases) 269 270 eq( 271 cases, 272 vim.fs.find(nvim_prog_basename, { 273 path = test_source_path, 274 type = 'file', 275 limit = 2, 276 follow = true, 277 }) 278 ) 279 280 if t.is_zig_build() then 281 return pending('broken with build.zig') 282 end 283 eq( 284 { nvim_prog }, 285 vim.fs.find(nvim_prog_basename, { 286 path = test_source_path, 287 type = 'file', 288 limit = 2, 289 follow = false, 290 }) 291 ) 292 end) 293 294 it('follow=true handles symlink loop', function() 295 if t.is_zig_build() then 296 return pending('broken/slow with build.zig') 297 end 298 local cwd = test_source_path ---@type string 299 local symlink = test_source_path .. '/loop_link' ---@type string 300 vim.uv.fs_symlink(cwd, symlink, { junction = true, dir = true }) 301 302 finally(function() 303 vim.uv.fs_unlink(symlink) 304 end) 305 306 eq(link_limit, #vim.fs.find(nvim_prog_basename, { 307 path = test_source_path, 308 type = 'file', 309 limit = math.huge, 310 follow = true, 311 })) 312 end) 313 314 it('accepts predicate as names', function() 315 local opts = { path = nvim_dir, upward = true, type = 'directory' } 316 eq( 317 { test_build_dir .. '/bin' }, 318 vim.fs.find(function(x) 319 return x == 'bin' 320 end, opts) 321 ) 322 eq( 323 { nvim_prog }, 324 vim.fs.find(function(x) 325 return x == nvim_prog_basename 326 end, { path = test_build_dir, type = 'file' }) 327 ) 328 eq( 329 {}, 330 vim.fs.find(function(x) 331 return x == 'no-match' 332 end, opts) 333 ) 334 335 opts = { path = test_source_path .. '/contrib', limit = math.huge } 336 eq( 337 exec_lua(function() 338 return vim.tbl_map( 339 vim.fs.basename, 340 vim.fn.glob(test_source_path .. '/contrib/*', false, true) 341 ) 342 end), 343 vim.tbl_map( 344 vim.fs.basename, 345 vim.fs.find(function(_, d) 346 return d:match('[\\/]contrib$') 347 end, opts) 348 ) 349 ) 350 end) 351 end) 352 353 describe('root()', function() 354 before_each(function() 355 command('edit test/functional/fixtures/tty-test.c') 356 end) 357 358 after_each(function() 359 command('bwipe!') 360 end) 361 362 it('works with a single marker', function() 363 eq(test_source_path, exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]])) 364 end) 365 366 it('works with multiple markers', function() 367 local bufnr = api.nvim_get_current_buf() 368 eq( 369 vim.fs.joinpath(test_source_path, 'test/functional/fixtures'), 370 exec_lua([[return vim.fs.root(..., {'CMakeLists.txt', 'CMakePresets.json'})]], bufnr) 371 ) 372 end) 373 374 it('nested markers have equal priority', function() 375 local bufnr = api.nvim_get_current_buf() 376 eq( 377 vim.fs.joinpath(test_source_path, 'test/functional'), 378 exec_lua( 379 [[return vim.fs.root(..., { 'example_spec.lua', {'CMakeLists.txt', 'CMakePresets.json'}, '.luarc.json'})]], 380 bufnr 381 ) 382 ) 383 eq( 384 vim.fs.joinpath(test_source_path, 'test/functional/fixtures'), 385 exec_lua( 386 [[return vim.fs.root(..., { {'CMakeLists.txt', 'CMakePresets.json'}, 'example_spec.lua', '.luarc.json'})]], 387 bufnr 388 ) 389 ) 390 eq( 391 vim.fs.joinpath(test_source_path, 'test/functional/fixtures'), 392 exec_lua( 393 [[return vim.fs.root(..., { 394 function(name, _) 395 return name:match('%.txt$') 396 end, 397 'example_spec.lua', 398 '.luarc.json' })]], 399 bufnr 400 ) 401 ) 402 end) 403 404 it('works with a function', function() 405 ---@type string 406 local result = exec_lua(function() 407 return vim.fs.root(0, function(name, _) 408 return name:match('%.txt$') 409 end) 410 end) 411 eq(vim.fs.joinpath(test_source_path, 'test/functional/fixtures'), result) 412 end) 413 414 it('works with a filename argument', function() 415 eq(test_source_path, exec_lua([[return vim.fs.root(..., 'CMakePresets.json')]], nvim_prog)) 416 end) 417 418 it('works with a relative path', function() 419 eq( 420 test_source_path, 421 exec_lua([[return vim.fs.root(..., 'CMakePresets.json')]], vim.fs.basename(nvim_prog)) 422 ) 423 end) 424 425 it('returns CWD (absolute path) for unnamed buffers', function() 426 assert(n.fn.isabsolutepath(test_source_path) == 1) 427 command('new') 428 eq( 429 t.fix_slashes(test_source_path), 430 t.fix_slashes(exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]])) 431 ) 432 end) 433 434 it("returns CWD (absolute path) for buffers with non-empty 'buftype'", function() 435 assert(n.fn.isabsolutepath(test_source_path) == 1) 436 command('new') 437 command('set buftype=nofile') 438 command('file lua://') 439 eq( 440 t.fix_slashes(test_source_path), 441 t.fix_slashes(exec_lua([[return vim.fs.root(0, 'CMakePresets.json')]])) 442 ) 443 end) 444 445 it('returns CWD (absolute path) if no match is found', function() 446 assert(n.fn.isabsolutepath(test_source_path) == 1) 447 eq( 448 t.fix_slashes(test_source_path), 449 t.fix_slashes(exec_lua([[return vim.fs.root('file://bogus', 'CMakePresets.json')]])) 450 ) 451 end) 452 end) 453 454 describe('joinpath()', function() 455 it('works', function() 456 eq('foo/bar/baz', vim.fs.joinpath('foo', 'bar', 'baz')) 457 eq('foo/bar/baz', vim.fs.joinpath('foo', '/bar/', '/baz')) 458 end) 459 it('rewrites backslashes on Windows', function() 460 if is_os('win') then 461 eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo]], [[\\bar\\\\baz]], [[zub\]])) 462 else 463 eq([[foo/\\bar\\\\baz/zub\]], vim.fs.joinpath([[foo]], [[\\bar\\\\baz]], [[zub\]])) 464 end 465 end) 466 it('strips redundant slashes', function() 467 if is_os('win') then 468 eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo//]], [[\\bar\\\\baz]], [[zub\]])) 469 else 470 eq('foo/bar/baz/zub/', vim.fs.joinpath([[foo]], [[//bar////baz]], [[zub/]])) 471 end 472 end) 473 it('handles empty segments', function() 474 eq('foo/bar', vim.fs.joinpath('', 'foo', '', 'bar', '')) 475 eq('foo/bar', vim.fs.joinpath('', '', 'foo', 'bar', '', '')) 476 eq('', vim.fs.joinpath('')) 477 eq('', vim.fs.joinpath('', '', '', '')) 478 end) 479 end) 480 481 describe('normalize()', function() 482 it('removes trailing /', function() 483 eq('/home/user', vim.fs.normalize('/home/user/')) 484 end) 485 it('works with /', function() 486 eq('/', vim.fs.normalize('/')) 487 end) 488 it('works with ~', function() 489 eq(vim.fs.normalize(assert(vim.uv.os_homedir())) .. '/src/foo', vim.fs.normalize('~/src/foo')) 490 end) 491 it('works with environment variables', function() 492 local xdg_config_home = test_build_dir .. '/.config' 493 eq( 494 xdg_config_home .. '/nvim', 495 exec_lua(function() 496 return vim._with({ env = { XDG_CONFIG_HOME = xdg_config_home } }, function() 497 return vim.fs.normalize('$XDG_CONFIG_HOME/nvim') 498 end) 499 end) 500 ) 501 end) 502 503 -- Opts required for testing posix paths and win paths 504 local posix_opts = { win = false } 505 local win_opts = { win = true } 506 507 it('preserves leading double slashes in POSIX paths', function() 508 eq('//foo', vim.fs.normalize('//foo', posix_opts)) 509 eq('//foo/bar', vim.fs.normalize('//foo//bar////', posix_opts)) 510 eq('/foo', vim.fs.normalize('///foo', posix_opts)) 511 eq('//', vim.fs.normalize('//', posix_opts)) 512 eq('/', vim.fs.normalize('///', posix_opts)) 513 eq('/foo/bar', vim.fs.normalize('/foo//bar////', posix_opts)) 514 end) 515 516 it('normalizes drive letter', function() 517 eq('C:/', vim.fs.normalize('C:/', win_opts)) 518 eq('C:/', vim.fs.normalize('c:/', win_opts)) 519 eq('D:/', vim.fs.normalize('d:/', win_opts)) 520 eq('C:', vim.fs.normalize('C:', win_opts)) 521 eq('C:', vim.fs.normalize('c:', win_opts)) 522 eq('D:', vim.fs.normalize('d:', win_opts)) 523 eq('C:/foo/test', vim.fs.normalize('C:/foo/test/', win_opts)) 524 eq('C:/foo/test', vim.fs.normalize('c:/foo/test/', win_opts)) 525 eq('D:foo/test', vim.fs.normalize('D:foo/test/', win_opts)) 526 eq('D:foo/test', vim.fs.normalize('d:foo/test/', win_opts)) 527 end) 528 529 it('always treats paths as case-sensitive #31833', function() 530 eq('TEST', vim.fs.normalize('TEST', win_opts)) 531 eq('test', vim.fs.normalize('test', win_opts)) 532 eq('C:/FOO/test', vim.fs.normalize('C:/FOO/test', win_opts)) 533 eq('C:/foo/test', vim.fs.normalize('C:/foo/test', win_opts)) 534 eq('//SERVER/SHARE/FOO/BAR', vim.fs.normalize('//SERVER/SHARE/FOO/BAR', win_opts)) 535 eq('//server/share/foo/bar', vim.fs.normalize('//server/share/foo/bar', win_opts)) 536 eq('C:/FOO/test', vim.fs.normalize('c:/FOO/test', win_opts)) 537 end) 538 539 it('allows backslashes on unix-based os', function() 540 eq('/home/user/hello\\world', vim.fs.normalize('/home/user/hello\\world', posix_opts)) 541 end) 542 543 it('preserves / after drive letters', function() 544 eq('C:/', vim.fs.normalize([[C:\]], win_opts)) 545 end) 546 547 it('works with UNC and DOS device paths', function() 548 eq('//server/share/foo/bar', vim.fs.normalize([[\\server\\share\\\foo\bar\\\]], win_opts)) 549 eq('//system07/C$/', vim.fs.normalize([[\\system07\C$\\\\]], win_opts)) 550 eq('//./C:/foo/bar', vim.fs.normalize([[\\.\\C:\foo\\\\bar]], win_opts)) 551 eq('//?/C:/foo/bar', vim.fs.normalize([[\\?\C:\\\foo\bar\\\\]], win_opts)) 552 eq( 553 '//?/UNC/server/share/foo/bar', 554 vim.fs.normalize([[\\?\UNC\server\\\share\\\\foo\\\bar]], win_opts) 555 ) 556 eq('//./BootPartition/foo/bar', vim.fs.normalize([[\\.\BootPartition\\foo\bar]], win_opts)) 557 eq( 558 '//./Volume{12345678-1234-1234-1234-1234567890AB}/foo/bar', 559 vim.fs.normalize([[\\.\Volume{12345678-1234-1234-1234-1234567890AB}\\\foo\bar\\]], win_opts) 560 ) 561 end) 562 563 it('handles invalid UNC and DOS device paths', function() 564 eq('//server/share', vim.fs.normalize([[\\server\share]], win_opts)) 565 eq('//server/', vim.fs.normalize([[\\server\]], win_opts)) 566 eq('//./UNC/server/share', vim.fs.normalize([[\\.\UNC\server\share]], win_opts)) 567 eq('//?/UNC/server/', vim.fs.normalize([[\\?\UNC\server\]], win_opts)) 568 eq('//?/UNC/server/..', vim.fs.normalize([[\\?\UNC\server\..]], win_opts)) 569 eq('//./', vim.fs.normalize([[\\.\]], win_opts)) 570 eq('//./foo', vim.fs.normalize([[\\.\foo]], win_opts)) 571 eq('//./BootPartition', vim.fs.normalize([[\\.\BootPartition]], win_opts)) 572 end) 573 574 it('converts backward slashes', function() 575 eq('C:/Users/jdoe', vim.fs.normalize([[C:\Users\jdoe]], win_opts)) 576 end) 577 578 describe('. and .. component resolving', function() 579 it('works', function() 580 -- Windows paths 581 eq('C:/Users', vim.fs.normalize([[C:\Users\jdoe\Downloads\.\..\..\]], win_opts)) 582 eq('C:/Users/jdoe', vim.fs.normalize([[C:\Users\jdoe\Downloads\.\..\.\.\]], win_opts)) 583 eq('C:/', vim.fs.normalize('C:/Users/jdoe/Downloads/./../../../', win_opts)) 584 eq('C:foo', vim.fs.normalize([[C:foo\bar\.\..\.]], win_opts)) 585 -- POSIX paths 586 eq('/home', vim.fs.normalize('/home/jdoe/Downloads/./../..', posix_opts)) 587 eq('/home/jdoe', vim.fs.normalize('/home/jdoe/Downloads/./../././', posix_opts)) 588 eq('/', vim.fs.normalize('/home/jdoe/Downloads/./../../../', posix_opts)) 589 -- OS-agnostic relative paths 590 eq('foo/bar/baz', vim.fs.normalize('foo/bar/foobar/../baz/./')) 591 eq('foo/bar', vim.fs.normalize('foo/bar/foobar/../baz/./../../bar/./.')) 592 end) 593 594 it('works when relative path reaches current directory', function() 595 eq('C:', vim.fs.normalize('C:foo/bar/../../.', win_opts)) 596 597 eq('.', vim.fs.normalize('.')) 598 eq('.', vim.fs.normalize('././././')) 599 eq('.', vim.fs.normalize('foo/bar/../../.')) 600 end) 601 602 it('works when relative path goes outside current directory', function() 603 eq('../../foo/bar', vim.fs.normalize('../../foo/bar')) 604 eq('../foo', vim.fs.normalize('foo/bar/../../../foo')) 605 606 eq('C:../foo', vim.fs.normalize('C:../foo', win_opts)) 607 eq('C:../../foo/bar', vim.fs.normalize('C:foo/../../../foo/bar', win_opts)) 608 end) 609 610 it('.. in root directory resolves to itself', function() 611 eq('C:/', vim.fs.normalize('C:/../../', win_opts)) 612 eq('C:/foo', vim.fs.normalize('C:/foo/../../foo', win_opts)) 613 614 eq('//server/share/', vim.fs.normalize([[\\server\share\..\..]], win_opts)) 615 eq('//server/share/foo', vim.fs.normalize([[\\server\\share\foo\..\..\foo]], win_opts)) 616 617 eq('//./C:/', vim.fs.normalize([[\\.\C:\..\..]], win_opts)) 618 eq('//?/C:/foo', vim.fs.normalize([[\\?\C:\..\..\foo]], win_opts)) 619 620 eq('//./UNC/server/share/', vim.fs.normalize([[\\.\UNC\\server\share\..\..\]], win_opts)) 621 eq( 622 '//?/UNC/server/share/foo', 623 vim.fs.normalize([[\\?\UNC\server\\share\..\..\foo]], win_opts) 624 ) 625 626 eq('//?/BootPartition/', vim.fs.normalize([[\\?\BootPartition\..\..]], win_opts)) 627 eq('//./BootPartition/foo', vim.fs.normalize([[\\.\BootPartition\..\..\foo]], win_opts)) 628 629 eq('/', vim.fs.normalize('/../../', posix_opts)) 630 eq('/foo', vim.fs.normalize('/foo/../../foo', posix_opts)) 631 end) 632 end) 633 end) 634 635 describe('abspath()', function() 636 local cwd = assert(t.fix_slashes(assert(vim.uv.cwd()))) 637 local home = t.fix_slashes(assert(vim.uv.os_homedir())) 638 639 it('expands relative paths', function() 640 assert(n.fn.isabsolutepath(cwd) == 1) 641 eq(cwd, vim.fs.abspath('.')) 642 eq(cwd .. '/foo', vim.fs.abspath('foo')) 643 eq(cwd .. '/././foo', vim.fs.abspath('././foo')) 644 eq(cwd .. '/.././../foo', vim.fs.abspath('.././../foo')) 645 end) 646 647 it('works with absolute paths', function() 648 if is_os('win') then 649 eq([[C:/foo]], vim.fs.abspath([[C:\foo]])) 650 eq([[C:/foo/../.]], vim.fs.abspath([[C:\foo\..\.]])) 651 eq('//foo/bar', vim.fs.abspath('\\\\foo\\bar')) 652 else 653 eq('/foo/../.', vim.fs.abspath('/foo/../.')) 654 eq('/foo/bar', vim.fs.abspath('/foo/bar')) 655 end 656 end) 657 658 it('expands ~', function() 659 eq(home .. '/foo', vim.fs.abspath('~/foo')) 660 eq(home .. '/./.././foo', vim.fs.abspath('~/./.././foo')) 661 end) 662 663 if is_os('win') then 664 it('works with drive-specific cwd on Windows', function() 665 local cwd_drive = cwd:match('^%w:') 666 667 eq(cwd .. '/foo', vim.fs.abspath(cwd_drive .. 'foo')) 668 end) 669 end 670 end) 671 672 describe('relpath()', function() 673 it('works', function() 674 local cwd = assert(t.fix_slashes(assert(vim.uv.cwd()))) 675 local my_dir = vim.fs.joinpath(cwd, 'foo') 676 677 eq(nil, vim.fs.relpath('/var/lib', '/var')) 678 eq(nil, vim.fs.relpath('/var/lib', '/bin')) 679 eq(nil, vim.fs.relpath(my_dir, 'bin')) 680 eq(nil, vim.fs.relpath(my_dir, './bin')) 681 eq(nil, vim.fs.relpath(my_dir, '././')) 682 eq(nil, vim.fs.relpath(my_dir, '../')) 683 eq(nil, vim.fs.relpath('/var/lib', '/')) 684 eq(nil, vim.fs.relpath('/var/lib', '//')) 685 eq(nil, vim.fs.relpath(' ', '/var')) 686 eq(nil, vim.fs.relpath(' ', '/var')) 687 eq('.', vim.fs.relpath('/var/lib', '/var/lib')) 688 eq('lib', vim.fs.relpath('/var/', '/var/lib')) 689 eq('var/lib', vim.fs.relpath('/', '/var/lib')) 690 eq('bar/package.json', vim.fs.relpath('/foo/test', '/foo/test/bar/package.json')) 691 eq('foo/bar', vim.fs.relpath(cwd, 'foo/bar')) 692 eq('foo/bar', vim.fs.relpath('.', vim.fs.joinpath(cwd, 'foo/bar'))) 693 eq('bar', vim.fs.relpath('foo', 'foo/bar')) 694 eq(nil, vim.fs.relpath('/var/lib', '/var/library/foo')) 695 696 if is_os('win') then 697 eq(nil, vim.fs.relpath('/', ' ')) 698 eq(nil, vim.fs.relpath('/', 'var')) 699 else 700 local cwd_rel_root = cwd:sub(2) 701 eq(cwd_rel_root .. '/ ', vim.fs.relpath('/', ' ')) 702 eq(cwd_rel_root .. '/var', vim.fs.relpath('/', 'var')) 703 end 704 705 if is_os('win') then 706 eq(nil, vim.fs.relpath('c:/aaaa/', '/aaaa/cccc')) 707 eq(nil, vim.fs.relpath('c:/aaaa/', './aaaa/cccc')) 708 eq(nil, vim.fs.relpath('c:/aaaa/', 'aaaa/cccc')) 709 eq(nil, vim.fs.relpath('c:/blah\\blah', 'd:/games')) 710 eq(nil, vim.fs.relpath('c:/games', 'd:/games')) 711 eq(nil, vim.fs.relpath('c:/games', 'd:/games/foo')) 712 eq(nil, vim.fs.relpath('c:/aaaa/bbbb', 'c:/aaaa')) 713 eq('cccc', vim.fs.relpath('c:/aaaa/', 'c:/aaaa/cccc')) 714 eq('aaaa/bbbb', vim.fs.relpath('C:/', 'c:\\aaaa\\bbbb')) 715 eq('bar/package.json', vim.fs.relpath('C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json')) 716 eq('baz', vim.fs.relpath('\\\\foo\\bar', '\\\\foo\\bar\\baz')) 717 eq(nil, vim.fs.relpath('a/b/c', 'a\\b')) 718 eq('d', vim.fs.relpath('a/b/c', 'a\\b\\c\\d')) 719 eq('.', vim.fs.relpath('\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz')) 720 eq(nil, vim.fs.relpath('C:\\foo\\test', 'C:\\foo\\Test\\bar\\package.json')) 721 end 722 end) 723 end) 724 725 describe('rm()', function() 726 before_each(function() 727 t.mkdir('Xtest_fs-rm') 728 t.write_file('Xtest_fs-rm/file-to-link', 'File to link') 729 t.mkdir('Xtest_fs-rm/dir-to-link') 730 t.write_file('Xtest_fs-rm/dir-to-link/file', 'File in dir to link') 731 end) 732 733 after_each(function() 734 vim.uv.fs_unlink('Xtest_fs-rm/dir-to-link/file') 735 vim.uv.fs_rmdir('Xtest_fs-rm/dir-to-link') 736 vim.uv.fs_unlink('Xtest_fs-rm/file-to-link') 737 vim.uv.fs_rmdir('Xtest_fs-rm') 738 end) 739 740 it('symlink', function() 741 -- File 742 vim.uv.fs_symlink('Xtest_fs-rm/file-to-link', 'Xtest_fs-rm/file-as-link') 743 vim.fs.rm('Xtest_fs-rm/file-as-link') 744 eq(vim.uv.fs_stat('Xtest_fs-rm/file-as-link'), nil) 745 eq({ 'File to link' }, fn.readfile('Xtest_fs-rm/file-to-link')) 746 747 -- Directory 748 local function assert_rm_symlinked_dir(opts) 749 vim.uv.fs_symlink('Xtest_fs-rm/dir-to-link', 'Xtest_fs-rm/dir-as-link') 750 vim.fs.rm('Xtest_fs-rm/dir-as-link', opts) 751 eq(vim.uv.fs_stat('Xtest_fs-rm/dir-as-link'), nil) 752 eq({ 'File in dir to link' }, fn.readfile('Xtest_fs-rm/dir-to-link/file')) 753 end 754 755 assert_rm_symlinked_dir({}) 756 assert_rm_symlinked_dir({ force = true }) 757 assert_rm_symlinked_dir({ recursive = true }) 758 assert_rm_symlinked_dir({ recursive = true, force = true }) 759 end) 760 end) 761 end)