path_spec.lua (26001B)
1 local uv = vim.uv 2 local t = require('test.unit.testutil') 3 local itp = t.gen_itp(it) 4 5 local cimport = t.cimport 6 local eq = t.eq 7 local neq = t.neq 8 local ffi = t.ffi 9 local cstr = t.cstr 10 local to_cstr = t.to_cstr 11 local NULL = t.NULL 12 local OK = t.OK 13 local FAIL = t.FAIL 14 local mkdir = t.mkdir 15 16 cimport('string.h') 17 local cimp = cimport('./src/nvim/os/os.h', './src/nvim/path.h') 18 local options = cimport('./src/nvim/option_vars.h') 19 20 local length = 0 21 local buffer = nil 22 23 describe('path.c', function() 24 describe('path_full_dir_name', function() 25 local old_dir 26 27 setup(function() 28 old_dir = uv.cwd() 29 mkdir('unit-test-directory') 30 uv.fs_symlink(old_dir .. '/unit-test-directory', 'unit-test-symlink') 31 end) 32 33 teardown(function() 34 uv.fs_unlink('unit-test-symlink') 35 uv.fs_rmdir('unit-test-directory') 36 end) 37 38 local function path_full_dir_name(directory, buf, len) 39 directory = to_cstr(directory) 40 return cimp.path_full_dir_name(directory, buf, len) 41 end 42 43 before_each(function() 44 -- Create empty string buffer which will contain the resulting path. 45 length = string.len(old_dir) + 22 46 buffer = cstr(length, '') 47 end) 48 49 after_each(function() 50 uv.chdir(old_dir) 51 end) 52 53 itp('returns the absolute directory name of a given relative one', function() 54 eq(OK, path_full_dir_name('..', buffer, length)) 55 uv.chdir('..') 56 local expected = uv.cwd() 57 uv.chdir(old_dir) 58 eq(expected, ffi.string(buffer)) 59 end) 60 61 itp('returns the current directory name if the given string is empty', function() 62 eq(OK, path_full_dir_name('', buffer, length)) 63 eq(old_dir, ffi.string(buffer)) 64 end) 65 66 local function test_full_dir_absolute() 67 itp('works with a normal absolute dir', function() 68 eq(OK, path_full_dir_name(old_dir .. '/unit-test-directory', buffer, length)) 69 eq(old_dir .. '/unit-test-directory', ffi.string(buffer)) 70 end) 71 72 itp('works with a symlinked absolute dir', function() 73 eq(OK, path_full_dir_name(old_dir .. '/unit-test-symlink', buffer, length)) 74 eq(old_dir .. '/unit-test-directory', ffi.string(buffer)) 75 end) 76 end 77 78 test_full_dir_absolute() 79 80 describe('when cwd does not exist #28786', function() 81 before_each(function() 82 mkdir('dir-to-remove') 83 uv.chdir('dir-to-remove') 84 uv.fs_rmdir(old_dir .. '/dir-to-remove') 85 end) 86 87 test_full_dir_absolute() 88 end) 89 90 itp('works with a normal relative dir', function() 91 eq(OK, path_full_dir_name('unit-test-directory', buffer, length)) 92 eq(old_dir .. '/unit-test-directory', ffi.string(buffer)) 93 end) 94 95 itp('works with a symlinked relative dir', function() 96 eq(OK, path_full_dir_name('unit-test-symlink', buffer, length)) 97 eq(old_dir .. '/unit-test-directory', ffi.string(buffer)) 98 end) 99 100 itp('works with a non-existing relative dir', function() 101 eq(OK, path_full_dir_name('does-not-exist', buffer, length)) 102 eq(old_dir .. '/does-not-exist', ffi.string(buffer)) 103 end) 104 105 itp('fails with a non-existing absolute dir', function() 106 eq(FAIL, path_full_dir_name('/does_not_exist', buffer, length)) 107 end) 108 end) 109 110 describe('path_full_compare', function() 111 local function path_full_compare(s1, s2, cn, ee) 112 s1 = to_cstr(s1) 113 s2 = to_cstr(s2) 114 return cimp.path_full_compare(s1, s2, cn or 0, ee or 1) 115 end 116 117 local f1 = 'f1.o' 118 local f2 = 'f2.o' 119 before_each(function() 120 -- create the three files that will be used in this spec 121 io.open(f1, 'w'):close() 122 io.open(f2, 'w'):close() 123 end) 124 125 after_each(function() 126 os.remove(f1) 127 os.remove(f2) 128 end) 129 130 itp('returns kEqualFiles when passed the same file', function() 131 eq(cimp.kEqualFiles, (path_full_compare(f1, f1))) 132 end) 133 134 itp('returns kEqualFileNames when files that dont exist and have same name', function() 135 eq(cimp.kEqualFileNames, (path_full_compare('null.txt', 'null.txt', true))) 136 end) 137 138 itp('returns kBothFilesMissing when files that dont exist', function() 139 eq(cimp.kBothFilesMissing, (path_full_compare('null.txt', 'null.txt'))) 140 end) 141 142 itp('returns kDifferentFiles when passed different files', function() 143 eq(cimp.kDifferentFiles, (path_full_compare(f1, f2))) 144 eq(cimp.kDifferentFiles, (path_full_compare(f2, f1))) 145 end) 146 147 itp('returns kOneFileMissing if only one does not exist', function() 148 eq(cimp.kOneFileMissing, (path_full_compare(f1, 'null.txt'))) 149 eq(cimp.kOneFileMissing, (path_full_compare('null.txt', f1))) 150 end) 151 end) 152 153 describe('path_tail', function() 154 local function path_tail(file) 155 local res = cimp.path_tail((to_cstr(file))) 156 neq(NULL, res) 157 return ffi.string(res) 158 end 159 160 itp('returns the tail of a given file path', function() 161 eq('file.txt', path_tail('directory/file.txt')) 162 end) 163 164 itp('returns an empty string if file ends in a slash', function() 165 eq('', path_tail('directory/')) 166 end) 167 end) 168 169 describe('path_tail_with_sep', function() 170 local function path_tail_with_sep(file) 171 local res = cimp.path_tail_with_sep((to_cstr(file))) 172 neq(NULL, res) 173 return ffi.string(res) 174 end 175 176 itp('returns the tail of a file together with its separator', function() 177 eq('///file.txt', path_tail_with_sep('directory///file.txt')) 178 end) 179 180 itp('returns an empty string when given an empty file name', function() 181 eq('', path_tail_with_sep('')) 182 end) 183 184 itp('returns only the separator if there is a trailing separator', function() 185 eq('/', path_tail_with_sep('some/directory/')) 186 end) 187 188 itp('cuts a leading separator', function() 189 eq('file.txt', path_tail_with_sep('/file.txt')) 190 eq('', path_tail_with_sep('/')) 191 end) 192 193 itp('returns the whole file name if there is no separator', function() 194 eq('file.txt', path_tail_with_sep('file.txt')) 195 end) 196 end) 197 198 describe('invocation_path_tail', function() 199 -- Returns the path tail and length (out param) of the tail. 200 -- Does not convert the tail from C-pointer to lua string for use with 201 -- strcmp. 202 local function invocation_path_tail(invk) 203 local plen = ffi.new('size_t[?]', 1) 204 local ptail = cimp.invocation_path_tail((to_cstr(invk)), plen) 205 neq(NULL, ptail) 206 207 -- it does not change the output if len==NULL 208 local tail2 = cimp.invocation_path_tail((to_cstr(invk)), NULL) 209 neq(NULL, tail2) 210 eq((ffi.string(ptail)), (ffi.string(tail2))) 211 return ptail, plen[0] 212 end 213 214 -- This test mimics the intended use in C. 215 local function compare(base, pinvk, len) 216 return eq(0, (ffi.C.strncmp((to_cstr(base)), pinvk, len))) 217 end 218 219 itp('returns the executable name of an invocation given a relative invocation', function() 220 local invk, len = invocation_path_tail('directory/exe a b c') 221 compare('exe a b c', invk, len) 222 eq(3, len) 223 end) 224 225 itp('returns the executable name of an invocation given an absolute invocation', function() 226 if ffi.os == 'Windows' then 227 local invk, len = invocation_path_tail('C:\\Users\\anyone\\Program Files\\z a b') 228 compare('z a b', invk, len) 229 eq(1, len) 230 else 231 local invk, len = invocation_path_tail('/usr/bin/z a b') 232 compare('z a b', invk, len) 233 eq(1, len) 234 end 235 end) 236 237 itp('does not count arguments to the executable as part of its path', function() 238 local invk, len = invocation_path_tail('exe a/b\\c') 239 compare('exe a/b\\c', invk, len) 240 eq(3, len) 241 end) 242 243 itp('only accepts whitespace as a terminator for the executable name', function() 244 local invk, _ = invocation_path_tail('exe-a+b_c[]()|#!@$%^&*') 245 eq('exe-a+b_c[]()|#!@$%^&*', (ffi.string(invk))) 246 end) 247 248 itp('is equivalent to path_tail when args do not contain a path separator', function() 249 local ptail = cimp.path_tail(to_cstr('a/b/c x y z')) 250 neq(NULL, ptail) 251 local tail = ffi.string(ptail) 252 local invk, _ = invocation_path_tail('a/b/c x y z') 253 eq(tail, ffi.string(invk)) 254 end) 255 256 itp('is not equivalent to path_tail when args contain a path separator', function() 257 local ptail = cimp.path_tail(to_cstr('a/b/c x y/z')) 258 neq(NULL, ptail) 259 local invk, _ = invocation_path_tail('a/b/c x y/z') 260 neq((ffi.string(ptail)), (ffi.string(invk))) 261 end) 262 end) 263 264 describe('path_next_component', function() 265 local function path_next_component(file) 266 local res = cimp.path_next_component((to_cstr(file))) 267 neq(NULL, res) 268 return ffi.string(res) 269 end 270 271 itp('returns', function() 272 eq('directory/file.txt', path_next_component('some/directory/file.txt')) 273 end) 274 275 itp('returns empty string if given file contains no separator', function() 276 eq('', path_next_component('file.txt')) 277 end) 278 end) 279 280 describe('path_shorten_fname', function() 281 itp('returns NULL if `full_path` is NULL', function() 282 local dir = to_cstr('some/directory/file.txt') 283 eq(NULL, (cimp.path_shorten_fname(NULL, dir))) 284 end) 285 286 itp('returns NULL if the path and dir does not match', function() 287 local dir = to_cstr('not/the/same') 288 local full = to_cstr('as/this.txt') 289 eq(NULL, (cimp.path_shorten_fname(full, dir))) 290 end) 291 292 itp('returns NULL if the path is not separated properly', function() 293 local dir = to_cstr('some/very/long/') 294 local full = to_cstr('some/very/long/directory/file.txt') 295 eq(NULL, (cimp.path_shorten_fname(full, dir))) 296 end) 297 298 itp('shortens the filename if `dir_name` is the start of `full_path`', function() 299 local full = to_cstr('some/very/long/directory/file.txt') 300 local dir = to_cstr('some/very/long') 301 eq('directory/file.txt', (ffi.string(cimp.path_shorten_fname(full, dir)))) 302 -- Also works with duplicate slashes. #37080 303 full = to_cstr('some/very/long//directory/file.txt') 304 eq('directory/file.txt', (ffi.string(cimp.path_shorten_fname(full, dir)))) 305 full = to_cstr('some/very/long///directory/file.txt') 306 eq('directory/file.txt', (ffi.string(cimp.path_shorten_fname(full, dir)))) 307 end) 308 end) 309 end) 310 311 describe('path_try_shorten_fname', function() 312 local cwd = uv.cwd() 313 314 before_each(function() 315 mkdir('ut_directory') 316 end) 317 318 after_each(function() 319 uv.chdir(cwd) 320 uv.fs_rmdir('ut_directory') 321 end) 322 323 describe('path_try_shorten_fname', function() 324 itp('returns shortened path if possible', function() 325 uv.chdir('ut_directory') 326 local full = to_cstr(uv.cwd() .. '/subdir/file.txt') 327 eq('subdir/file.txt', (ffi.string(cimp.path_try_shorten_fname(full)))) 328 end) 329 330 itp('returns `full_path` if a shorter version is not possible', function() 331 local old = uv.cwd() 332 uv.chdir('ut_directory') 333 local full = old .. '/subdir/file.txt' 334 eq(full, (ffi.string(cimp.path_try_shorten_fname(to_cstr(full))))) 335 end) 336 337 itp('returns NULL if `full_path` is NULL', function() 338 eq(NULL, (cimp.path_try_shorten_fname(NULL))) 339 end) 340 end) 341 end) 342 343 describe('path.c path_guess_exepath', function() 344 local cwd = uv.cwd() 345 346 for _, name in ipairs({ './nvim', '.nvim', 'foo/nvim' }) do 347 itp('"' .. name .. '" returns name catenated with CWD', function() 348 local bufsize = 255 349 local buf = cstr(bufsize, '') 350 cimp.path_guess_exepath(name, buf, bufsize) 351 eq(cwd .. '/' .. name, ffi.string(buf)) 352 end) 353 end 354 355 itp('absolute path returns the name unmodified', function() 356 local name = '/foo/bar/baz' 357 local bufsize = 255 358 local buf = cstr(bufsize, '') 359 cimp.path_guess_exepath(name, buf, bufsize) 360 eq(name, ffi.string(buf)) 361 end) 362 363 itp('returns the name unmodified if not found in $PATH', function() 364 local name = '23u0293_not_in_path' 365 local bufsize = 255 366 local buf = cstr(bufsize, '') 367 cimp.path_guess_exepath(name, buf, bufsize) 368 eq(name, ffi.string(buf)) 369 end) 370 371 itp('does not crash if $PATH item exceeds MAXPATHL', function() 372 local orig_path_env = os.getenv('PATH') 373 local name = 'cat' -- Some executable in $PATH. 374 local bufsize = 255 375 local buf = cstr(bufsize, '') 376 local insane_path = orig_path_env .. ':' .. (('x/'):rep(4097)) 377 378 cimp.os_setenv('PATH', insane_path, true) 379 cimp.path_guess_exepath(name, buf, bufsize) 380 eq('bin/' .. name, ffi.string(buf):sub(-#('bin/' .. name), -1)) 381 382 -- Restore $PATH. 383 cimp.os_setenv('PATH', orig_path_env, true) 384 end) 385 386 itp('returns full path found in $PATH', function() 387 local name = 'cat' -- Some executable in $PATH. 388 local bufsize = 255 389 local buf = cstr(bufsize, '') 390 cimp.path_guess_exepath(name, buf, bufsize) 391 -- Usually "/bin/cat" on unix, "/path/to/nvim/cat" on Windows. 392 eq('bin/' .. name, ffi.string(buf):sub(-#('bin/' .. name), -1)) 393 end) 394 end) 395 396 describe('path.c', function() 397 setup(function() 398 mkdir('unit-test-directory') 399 io.open('unit-test-directory/test.file', 'w'):close() 400 401 -- Since the tests are executed, they are called by an executable. We use 402 -- that executable for several asserts. 403 local absolute_executable = arg[0] 404 405 -- Split absolute_executable into a directory and the actual file name for 406 -- later usage. 407 local directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$') -- luacheck: ignore 408 end) 409 410 teardown(function() 411 os.remove('unit-test-directory/test.file') 412 uv.fs_rmdir('unit-test-directory') 413 end) 414 415 describe('vim_FullName', function() 416 local function vim_FullName(filename, buflen, do_expand) 417 local buf = cstr(buflen, '') 418 local result = cimp.vim_FullName(to_cstr(filename), buf, buflen, do_expand) 419 return buf, result 420 end 421 422 local function get_buf_len(s, q) 423 return math.max(string.len(s), string.len(q)) + 1 424 end 425 426 itp('fails if given filename is NULL', function() 427 local do_expand = 1 428 local buflen = 10 429 local buf = cstr(buflen, '') 430 local result = cimp.vim_FullName(NULL, buf, buflen, do_expand) 431 eq(FAIL, result) 432 end) 433 434 itp('fails safely if given length is wrong #5737', function() 435 local filename = 'foo/bar/bazzzzzzz/buz/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a' 436 local too_short_len = 8 437 local buf = cstr(too_short_len, '') 438 local do_expand = 1 439 local result = cimp.vim_FullName(filename, buf, too_short_len, do_expand) 440 local expected = string.sub(filename, 1, (too_short_len - 1)) 441 eq(expected, ffi.string(buf)) 442 eq(FAIL, result) 443 end) 444 445 itp('uses the filename if the filename is a URL', function() 446 local filename = 'http://www.neovim.org' 447 local buflen = string.len(filename) + 1 448 local do_expand = 1 449 local buf, result = vim_FullName(filename, buflen, do_expand) 450 eq(filename, ffi.string(buf)) 451 eq(OK, result) 452 end) 453 454 itp('fails and uses filename if given filename contains non-existing directory', function() 455 -- test with different filename lengths 456 for rep = 1, 10 do 457 local filename = ('non_existing_'):rep(rep) .. 'dir/test.file' 458 local buflen = string.len(filename) + 1 459 local do_expand = 1 460 local buf, result = vim_FullName(filename, buflen, do_expand) 461 eq(filename, ffi.string(buf)) 462 eq(FAIL, result) 463 end 464 end) 465 466 itp('concatenates filename if it does not contain a slash', function() 467 local expected = uv.cwd() .. '/test.file' 468 local filename = 'test.file' 469 local buflen = get_buf_len(expected, filename) 470 local do_expand = 1 471 local buf, result = vim_FullName(filename, buflen, do_expand) 472 eq(expected, ffi.string(buf)) 473 eq(OK, result) 474 end) 475 476 itp('produces absolute path for .. without a slash', function() 477 local old_dir = uv.cwd() 478 uv.chdir('..') 479 local expected = uv.cwd() 480 uv.chdir(old_dir) 481 local filename = '..' 482 local buflen = get_buf_len(expected, filename) 483 local do_expand = 1 484 local buf, result = vim_FullName(filename, buflen, do_expand) 485 eq(expected, ffi.string(buf)) 486 eq(OK, result) 487 end) 488 489 itp('produces absolute path if possible and if path contains a slash', function() 490 local old_dir = uv.cwd() 491 uv.chdir('..') 492 local expected = uv.cwd() .. '/test.file' 493 uv.chdir(old_dir) 494 local filename = '../test.file' 495 local buflen = get_buf_len(expected, filename) 496 local do_expand = 1 497 local buf, result = vim_FullName(filename, buflen, do_expand) 498 eq(expected, ffi.string(buf)) 499 eq(OK, result) 500 end) 501 502 itp('just copies the path if it is already absolute and force=0', function() 503 local absolute_path = '/absolute/path' 504 local buflen = string.len(absolute_path) + 1 505 local do_expand = 0 506 local buf, result = vim_FullName(absolute_path, buflen, do_expand) 507 eq(absolute_path, ffi.string(buf)) 508 eq(OK, result) 509 end) 510 511 itp('fails and uses filename when the path is relative to HOME', function() 512 eq(false, cimp.os_isdir('~'), 'sanity check: no literal "~" directory') 513 local absolute_path = '~/home.file' 514 local buflen = string.len(absolute_path) + 1 515 local do_expand = 1 516 local buf, result = vim_FullName(absolute_path, buflen, do_expand) 517 eq(absolute_path, ffi.string(buf)) 518 eq(FAIL, result) 519 end) 520 521 itp('works with some "normal" relative path with directories', function() 522 local expected = uv.cwd() .. '/unit-test-directory/test.file' 523 local filename = 'unit-test-directory/test.file' 524 local buflen = get_buf_len(expected, filename) 525 local do_expand = 1 526 local buf, result = vim_FullName(filename, buflen, do_expand) 527 eq(expected, ffi.string(buf)) 528 eq(OK, result) 529 end) 530 531 itp('does not modify the given filename', function() 532 local expected = uv.cwd() .. '/unit-test-directory/test.file' 533 local filename = to_cstr('unit-test-directory/test.file') 534 local buflen = string.len(expected) + 1 535 local buf = cstr(buflen, '') 536 local do_expand = 1 537 -- Don't use the wrapper but pass a cstring directly to the c function. 538 eq('unit-test-directory/test.file', ffi.string(filename)) 539 local result = cimp.vim_FullName(filename, buf, buflen, do_expand) 540 eq(expected, ffi.string(buf)) 541 eq(OK, result) 542 end) 543 544 itp('works with directories that have one path component', function() 545 local filename = '/tmp' 546 local expected = filename 547 local buflen = get_buf_len(expected, filename) 548 local do_expand = 1 549 local buf, result = vim_FullName(filename, buflen, do_expand) 550 eq('/tmp', ffi.string(buf)) 551 eq(OK, result) 552 end) 553 554 itp('does not remove trailing slash from non-existing relative directory #20847', function() 555 local expected = uv.cwd() .. '/non_existing_dir/' 556 local filename = 'non_existing_dir/' 557 local buflen = get_buf_len(expected, filename) 558 local do_expand = 1 559 local buf, result = vim_FullName(filename, buflen, do_expand) 560 eq(expected, ffi.string(buf)) 561 eq(OK, result) 562 end) 563 564 itp('expands "./" to the current directory #7117', function() 565 local expected = uv.cwd() .. '/unit-test-directory/test.file' 566 local filename = './unit-test-directory/test.file' 567 local buflen = get_buf_len(expected, filename) 568 local do_expand = 1 569 local buf, result = vim_FullName(filename, buflen, do_expand) 570 eq(OK, result) 571 eq(expected, ffi.string(buf)) 572 end) 573 574 itp('collapses "foo/../foo" to "foo" #7117', function() 575 local expected = uv.cwd() .. '/unit-test-directory/test.file' 576 local filename = 'unit-test-directory/../unit-test-directory/test.file' 577 local buflen = get_buf_len(expected, filename) 578 local do_expand = 1 579 local buf, result = vim_FullName(filename, buflen, do_expand) 580 eq(OK, result) 581 eq(expected, ffi.string(buf)) 582 end) 583 end) 584 585 describe('path_fix_case', function() 586 local function fix_case(file) 587 local c_file = to_cstr(file) 588 cimp.path_fix_case(c_file) 589 return ffi.string(c_file) 590 end 591 592 before_each(function() 593 mkdir('CamelCase') 594 end) 595 after_each(function() 596 uv.fs_rmdir('CamelCase') 597 end) 598 599 if ffi.os == 'Windows' or ffi.os == 'OSX' then 600 itp('Corrects the case of file names in Mac and Windows', function() 601 eq('CamelCase', fix_case('camelcase')) 602 eq('CamelCase', fix_case('cAMELcASE')) 603 end) 604 else 605 itp('does nothing on Linux', function() 606 eq('camelcase', fix_case('camelcase')) 607 eq('cAMELcASE', fix_case('cAMELcASE')) 608 end) 609 end 610 end) 611 612 describe('append_path', function() 613 itp('joins given paths with a slash', function() 614 local path1 = cstr(100, 'path1') 615 local to_append = to_cstr('path2') 616 eq(OK, (cimp.append_path(path1, to_append, 100))) 617 eq('path1/path2', (ffi.string(path1))) 618 end) 619 620 itp('joins given paths without adding an unnecessary slash', function() 621 local path1 = cstr(100, 'path1/') 622 local to_append = to_cstr('path2') 623 eq(OK, cimp.append_path(path1, to_append, 100)) 624 eq('path1/path2', (ffi.string(path1))) 625 end) 626 627 itp('fails and uses filename if there is not enough space left for to_append', function() 628 local path1 = cstr(11, 'path1/') 629 local to_append = to_cstr('path2') 630 eq(FAIL, (cimp.append_path(path1, to_append, 11))) 631 end) 632 633 itp('does not append a slash if to_append is empty', function() 634 local path1 = cstr(6, 'path1') 635 local to_append = to_cstr('') 636 eq(OK, (cimp.append_path(path1, to_append, 6))) 637 eq('path1', (ffi.string(path1))) 638 end) 639 640 itp('does not append unnecessary dots', function() 641 local path1 = cstr(6, 'path1') 642 local to_append = to_cstr('.') 643 eq(OK, (cimp.append_path(path1, to_append, 6))) 644 eq('path1', (ffi.string(path1))) 645 end) 646 647 itp('copies to_append to path, if path is empty', function() 648 local path1 = cstr(7, '') 649 local to_append = to_cstr('/path2') 650 eq(OK, (cimp.append_path(path1, to_append, 7))) 651 eq('/path2', (ffi.string(path1))) 652 end) 653 end) 654 655 describe('path_is_absolute', function() 656 local function path_is_absolute(filename) 657 filename = to_cstr(filename) 658 return cimp.path_is_absolute(filename) 659 end 660 661 itp('returns true if filename starts with a slash', function() 662 eq(true, path_is_absolute('/some/directory/')) 663 end) 664 665 itp('returns true if filename starts with a tilde', function() 666 eq(true, path_is_absolute('~/in/my/home~/directory')) 667 end) 668 669 itp('returns false if filename starts not with slash nor tilde', function() 670 eq(false, path_is_absolute('not/in/my/home~/directory')) 671 end) 672 end) 673 674 describe('path_with_extension', function() 675 local function path_with_extension(filename, extension) 676 local c_filename = to_cstr(filename) 677 local c_extension = to_cstr(extension) 678 return cimp.path_with_extension(c_filename, c_extension) 679 end 680 681 itp('returns true if filename includes a provided extension', function() 682 eq(true, path_with_extension('/some/path/file.lua', 'lua')) 683 end) 684 685 itp('returns false if filename does not include a provided extension', function() 686 eq(false, path_with_extension('/some/path/file.vim', 'lua')) 687 eq(false, path_with_extension('/some/path/file', 'lua')) 688 end) 689 690 itp("respects 'fileignorecase' option", function() 691 options.p_fic = false 692 eq(false, path_with_extension('/some/path/file.VIM', 'vim')) 693 eq(false, path_with_extension('/some/path/file.LUA', 'lua')) 694 options.p_fic = true 695 eq(true, path_with_extension('/some/path/file.VIM', 'vim')) 696 eq(true, path_with_extension('/some/path/file.LUA', 'lua')) 697 end) 698 end) 699 700 describe('path_with_url', function() 701 itp('scheme is alpha and inner numeric, "+", "-", "." only', function() 702 local function path_with_url(fname) 703 return cimp.path_with_url(to_cstr(fname)) 704 end 705 706 -- Check normal scheme with just alphabetic 707 eq(1, path_with_url([[test://xyz/foo/b0]])) 708 eq(2, path_with_url([[test:\\xyz\foo\b0]])) 709 710 -- Check valid scheme with just alphanumeric 711 eq(1, path_with_url([[test123://xyz/foo/b0]])) 712 eq(2, path_with_url([[test123:\\xyz\foo\b0]])) 713 714 -- Check invalid scheme (contains invalid character) 715 eq(0, path_with_url([[test_abc://xyz/foo/b2]])) 716 717 -- Check valid scheme containing '+', '-', or '.' 718 eq(1, path_with_url([[test+abc://xyz/foo/b1]])) 719 eq(2, path_with_url([[test+abc:\\xyz\foo\b1]])) 720 eq(1, path_with_url([[test-abc://xyz/foo/b3]])) 721 eq(2, path_with_url([[test-abc:\\xyz\foo\b3]])) 722 eq(1, path_with_url([[test.abc://xyz/foo/b1]])) 723 eq(2, path_with_url([[test.abc:\\xyz\foo\b1]])) 724 725 -- Check valid scheme with full suite of allowed characters 726 eq(1, path_with_url([[test+abc-123.ghi://xyz/foo/b1]])) 727 eq(2, path_with_url([[test+abc-123.ghi:\\xyz\foo\b1]])) 728 729 -- Check invalid scheme starting or ending wiht '+', '-', or '.' 730 eq(0, path_with_url([[-test://xyz/foo/b4]])) 731 eq(0, path_with_url([[test-://xyz/foo/b5]])) 732 eq(0, path_with_url([[+test://xyz/foo/b4]])) 733 eq(0, path_with_url([[test+://xyz/foo/b5]])) 734 eq(0, path_with_url([[.test://xyz/foo/b4]])) 735 eq(0, path_with_url([[test.://xyz/foo/b5]])) 736 737 -- Check additional valid scheme containing '+', '-', or '.' 738 eq(1, path_with_url([[test-C:/xyz/foo/b5]])) 739 eq(1, path_with_url([[test-custom:/xyz/foo/b5]])) 740 eq(1, path_with_url([[test+C:/xyz/foo/b5]])) 741 eq(1, path_with_url([[test+custom:/xyz/foo/b5]])) 742 eq(1, path_with_url([[test.C:/xyz/foo/b5]])) 743 eq(1, path_with_url([[test.custom:/xyz/foo/b5]])) 744 745 -- Check invalid scheme representing drive letter 746 eq(0, path_with_url([[c:/xyz/foo/b5]])) 747 eq(0, path_with_url([[C:/xyz/foo/b5]])) 748 end) 749 end) 750 end)