neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

executable_spec.lua (9332B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 
      4 local eq, clear, call, write_file, command = t.eq, n.clear, n.call, t.write_file, n.command
      5 local exc_exec = n.exc_exec
      6 local eval = n.eval
      7 local is_os = t.is_os
      8 
      9 describe('executable()', function()
     10  before_each(clear)
     11 
     12  it('returns 1 for commands in $PATH', function()
     13    local exe = is_os('win') and 'ping' or 'ls'
     14    eq(1, call('executable', exe))
     15    command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
     16    eq(1, call('executable', 'null'))
     17    eq(1, call('executable', 'true'))
     18    eq(1, call('executable', 'false'))
     19  end)
     20 
     21  if is_os('win') then
     22    it('exepath respects shellslash', function()
     23      -- test/ cannot be a symlink in this test.
     24      n.api.nvim_set_current_dir(t.paths.test_source_path)
     25 
     26      command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
     27      eq(
     28        [[test\functional\fixtures\bin\null.CMD]],
     29        call('fnamemodify', call('exepath', 'null'), ':.')
     30      )
     31      command('set shellslash')
     32      eq(
     33        'test/functional/fixtures/bin/null.CMD',
     34        call('fnamemodify', call('exepath', 'null'), ':.')
     35      )
     36    end)
     37 
     38    it('stdpath respects shellslash', function()
     39      -- Needs to check paths relative to repo root dir.
     40      n.api.nvim_set_current_dir(t.paths.test_source_path)
     41 
     42      t.matches(
     43        [[build\Xtest_xdg[%w_]*\share\nvim%-data]],
     44        call('fnamemodify', call('stdpath', 'data'), ':.')
     45      )
     46      command('set shellslash')
     47      t.matches(
     48        'build/Xtest_xdg[%w_]*/share/nvim%-data',
     49        call('fnamemodify', call('stdpath', 'data'), ':.')
     50      )
     51    end)
     52  end
     53 
     54  it('fails for invalid values', function()
     55    for _, input in ipairs({ 'v:null', 'v:true', 'v:false', '{}', '[]' }) do
     56      eq(
     57        'Vim(call):E1174: String required for argument 1',
     58        exc_exec('call executable(' .. input .. ')')
     59      )
     60    end
     61    command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
     62    for _, input in ipairs({ 'v:null', 'v:true', 'v:false' }) do
     63      eq(
     64        'Vim(call):E1174: String required for argument 1',
     65        exc_exec('call executable(' .. input .. ')')
     66      )
     67    end
     68  end)
     69 
     70  it('returns 0 for empty strings', function()
     71    eq(0, call('executable', '""'))
     72  end)
     73 
     74  it('returns 0 for non-existent files', function()
     75    eq(0, call('executable', 'no_such_file_exists_209ufq23f'))
     76  end)
     77 
     78  it('sibling to nvim binary', function()
     79    -- Some executable in build/bin/, *not* in $PATH nor CWD.
     80    local sibling_exe = 'printargs-test'
     81    -- Windows: siblings are in Nvim's "pseudo-$PATH".
     82    local expected = is_os('win') and 1 or 0
     83    if is_os('win') then
     84      eq('arg1=lemon;arg2=sky;arg3=tree;', call('system', sibling_exe .. ' lemon sky tree'))
     85    end
     86    eq(expected, call('executable', sibling_exe))
     87  end)
     88 
     89  describe('exec-bit', function()
     90    setup(function()
     91      clear()
     92      write_file('Xtest_not_executable', 'non-executable file')
     93      write_file('Xtest_executable', 'executable file (exec-bit set)')
     94      if not is_os('win') then -- N/A for Windows.
     95        call('system', { 'chmod', '-x', 'Xtest_not_executable' })
     96        call('system', { 'chmod', '+x', 'Xtest_executable' })
     97      end
     98    end)
     99 
    100    teardown(function()
    101      os.remove('Xtest_not_executable')
    102      os.remove('Xtest_executable')
    103    end)
    104 
    105    it('not set', function()
    106      eq(0, call('executable', 'Xtest_not_executable'))
    107      eq(0, call('executable', './Xtest_not_executable'))
    108    end)
    109 
    110    it('set, unqualified and not in $PATH', function()
    111      eq(0, call('executable', 'Xtest_executable'))
    112    end)
    113 
    114    it('set, qualified as a path', function()
    115      local expected = is_os('win') and 0 or 1
    116      eq(expected, call('executable', './Xtest_executable'))
    117    end)
    118  end)
    119 end)
    120 
    121 describe('executable() (Windows)', function()
    122  if not is_os('win') then
    123    pending('N/A for non-windows')
    124    return
    125  end
    126 
    127  local exts = { 'bat', 'exe', 'com', 'cmd' }
    128  setup(function()
    129    for _, ext in ipairs(exts) do
    130      write_file('test_executable_' .. ext .. '.' .. ext, '')
    131    end
    132    write_file('test_executable_zzz.zzz', '')
    133  end)
    134 
    135  teardown(function()
    136    for _, ext in ipairs(exts) do
    137      os.remove('test_executable_' .. ext .. '.' .. ext)
    138    end
    139    os.remove('test_executable_zzz.zzz')
    140  end)
    141 
    142  it('tries default extensions on a filename if $PATHEXT is empty', function()
    143    -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
    144    clear({ env = { PATHEXT = '' } })
    145    for _, ext in ipairs(exts) do
    146      eq(1, call('executable', 'test_executable_' .. ext))
    147    end
    148    eq(0, call('executable', 'test_executable_zzz'))
    149  end)
    150 
    151  it('tries default extensions on a filepath if $PATHEXT is empty', function()
    152    -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
    153    clear({ env = { PATHEXT = '' } })
    154    for _, ext in ipairs(exts) do
    155      eq(1, call('executable', '.\\test_executable_' .. ext))
    156    end
    157    eq(0, call('executable', '.\\test_executable_zzz'))
    158  end)
    159 
    160  it('system([…]), jobstart([…]) use $PATHEXT #9569', function()
    161    -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
    162    clear({ env = { PATHEXT = '' } })
    163    -- Invoking `cmdscript` should find/execute `cmdscript.cmd`.
    164    eq('much success\n', call('system', { 'test/functional/fixtures/cmdscript' }))
    165    assert(0 < call('jobstart', { 'test/functional/fixtures/cmdscript' }))
    166  end)
    167 
    168  it('full path with extension', function()
    169    -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
    170    clear({ env = { PATHEXT = '' } })
    171    -- Some executable we can expect in the test env.
    172    local exe = 'printargs-test'
    173    local exedir = eval("fnamemodify(v:progpath, ':h')")
    174    local exepath = exedir .. '/' .. exe .. '.exe'
    175    eq(1, call('executable', exepath))
    176    eq('arg1=lemon;arg2=sky;arg3=tree;', call('system', exepath .. ' lemon sky tree'))
    177  end)
    178 
    179  it('full path without extension', function()
    180    -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
    181    clear({ env = { PATHEXT = '' } })
    182    -- Some executable we can expect in the test env.
    183    local exe = 'printargs-test'
    184    local exedir = eval("fnamemodify(v:progpath, ':h')")
    185    local exepath = exedir .. '/' .. exe
    186    eq('arg1=lemon;arg2=sky;arg3=tree;', call('system', exepath .. ' lemon sky tree'))
    187    eq(1, call('executable', exepath))
    188  end)
    189 
    190  it('respects $PATHEXT when trying extensions on a filename', function()
    191    clear({ env = { PATHEXT = '.zzz' } })
    192    for _, ext in ipairs(exts) do
    193      eq(0, call('executable', 'test_executable_' .. ext))
    194    end
    195    eq(1, call('executable', 'test_executable_zzz'))
    196  end)
    197 
    198  it('respects $PATHEXT when trying extensions on a filepath', function()
    199    clear({ env = { PATHEXT = '.zzz' } })
    200    for _, ext in ipairs(exts) do
    201      eq(0, call('executable', '.\\test_executable_' .. ext))
    202    end
    203    eq(1, call('executable', '.\\test_executable_zzz'))
    204  end)
    205 
    206  it('with weird $PATHEXT', function()
    207    clear({ env = { PATHEXT = ';' } })
    208    eq(0, call('executable', '.\\test_executable_zzz'))
    209    clear({ env = { PATHEXT = ';;;.zzz;;' } })
    210    eq(1, call('executable', '.\\test_executable_zzz'))
    211  end)
    212 
    213  it("unqualified filename, Unix-style 'shell'", function()
    214    clear({ env = { PATHEXT = '' } })
    215    command('set shell=sh')
    216    for _, ext in ipairs(exts) do
    217      eq(0, call('executable', 'test_executable_' .. ext .. '.' .. ext))
    218    end
    219    eq(0, call('executable', 'test_executable_zzz.zzz'))
    220  end)
    221 
    222  it("relative path, Unix-style 'shell' (backslashes)", function()
    223    clear({ env = { PATHEXT = '' } })
    224    command('set shell=bash.exe')
    225    for _, ext in ipairs(exts) do
    226      eq(1, call('executable', '.\\test_executable_' .. ext .. '.' .. ext))
    227      eq(1, call('executable', './test_executable_' .. ext .. '.' .. ext))
    228    end
    229    eq(1, call('executable', '.\\test_executable_zzz.zzz'))
    230    eq(1, call('executable', './test_executable_zzz.zzz'))
    231  end)
    232 
    233  it('unqualified filename, $PATHEXT contains dot', function()
    234    clear({ env = { PATHEXT = '.;.zzz' } })
    235    for _, ext in ipairs(exts) do
    236      eq(1, call('executable', 'test_executable_' .. ext .. '.' .. ext))
    237    end
    238    eq(1, call('executable', 'test_executable_zzz.zzz'))
    239    clear({ env = { PATHEXT = '.zzz;.' } })
    240    for _, ext in ipairs(exts) do
    241      eq(1, call('executable', 'test_executable_' .. ext .. '.' .. ext))
    242    end
    243    eq(1, call('executable', 'test_executable_zzz.zzz'))
    244  end)
    245 
    246  it('relative path, $PATHEXT contains dot (backslashes)', function()
    247    clear({ env = { PATHEXT = '.;.zzz' } })
    248    for _, ext in ipairs(exts) do
    249      eq(1, call('executable', '.\\test_executable_' .. ext .. '.' .. ext))
    250      eq(1, call('executable', './test_executable_' .. ext .. '.' .. ext))
    251    end
    252    eq(1, call('executable', '.\\test_executable_zzz.zzz'))
    253    eq(1, call('executable', './test_executable_zzz.zzz'))
    254  end)
    255 
    256  it('ignores case of extension', function()
    257    clear({ env = { PATHEXT = '.ZZZ' } })
    258    eq(1, call('executable', 'test_executable_zzz.zzz'))
    259  end)
    260 
    261  it('relative path does not search $PATH', function()
    262    clear({ env = { PATHEXT = '' } })
    263    eq(0, call('executable', './System32/notepad.exe'))
    264    eq(0, call('executable', '.\\System32\\notepad.exe'))
    265    eq(0, call('executable', '../notepad.exe'))
    266    eq(0, call('executable', '..\\notepad.exe'))
    267  end)
    268 end)