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)