env_spec.lua (14791B)
1 local t = require('test.unit.testutil') 2 local itp = t.gen_itp(it) 3 4 local cimport = t.cimport 5 local eq = t.eq 6 local neq = t.neq 7 local ffi = t.ffi 8 local cstr = t.cstr 9 local to_cstr = t.to_cstr 10 local NULL = t.NULL 11 local OK = 0 12 13 local cimp = cimport('./src/nvim/os/os.h') 14 15 describe('env.c', function() 16 local function os_env_exists(name, nonempty) 17 return cimp.os_env_exists(to_cstr(name), nonempty) 18 end 19 20 local function os_setenv(name, value, override) 21 return cimp.os_setenv(to_cstr(name), to_cstr(value), override) 22 end 23 24 local function os_unsetenv(name) 25 return cimp.os_unsetenv(to_cstr(name)) 26 end 27 28 local function os_getenv(name) 29 local rval = cimp.os_getenv(to_cstr(name)) 30 if rval ~= NULL then 31 return ffi.string(rval) 32 else 33 return NULL 34 end 35 end 36 37 local function os_getenv_buf(name, buf, bufsize) 38 local rval = cimp.os_getenv_buf(to_cstr(name), buf, bufsize) 39 if rval ~= NULL then 40 return ffi.string(rval) 41 else 42 return NULL 43 end 44 end 45 46 local function os_getenv_noalloc(name) 47 local rval = cimp.os_getenv_noalloc(to_cstr(name)) 48 if rval ~= NULL then 49 return ffi.string(rval) 50 else 51 return NULL 52 end 53 end 54 55 itp('os_env_exists(..., false)', function() 56 eq(false, os_env_exists('', false)) 57 eq(false, os_env_exists(' ', false)) 58 eq(false, os_env_exists('\t', false)) 59 eq(false, os_env_exists('\n', false)) 60 eq(false, os_env_exists('AaあB <= very weird name...', false)) 61 62 local varname = 'NVIM_UNIT_TEST_os_env_exists' 63 eq(false, os_env_exists(varname, false)) 64 eq(OK, os_setenv(varname, 'foo bar baz ...', 1)) 65 eq(true, os_env_exists(varname, false)) 66 eq(OK, os_setenv(varname, 'f', 1)) 67 eq(true, os_env_exists(varname, true)) 68 end) 69 70 itp('os_env_exists(..., true)', function() 71 eq(false, os_env_exists('', true)) 72 eq(false, os_env_exists(' ', true)) 73 eq(false, os_env_exists('\t', true)) 74 eq(false, os_env_exists('\n', true)) 75 eq(false, os_env_exists('AaあB <= very weird name...', true)) 76 77 local varname = 'NVIM_UNIT_TEST_os_env_defined' 78 eq(false, os_env_exists(varname, true)) 79 eq(OK, os_setenv(varname, '', 1)) 80 eq(false, os_env_exists(varname, true)) 81 eq(OK, os_setenv(varname, 'foo bar baz ...', 1)) 82 eq(true, os_env_exists(varname, true)) 83 eq(OK, os_setenv(varname, 'f', 1)) 84 eq(true, os_env_exists(varname, true)) 85 end) 86 87 describe('os_setenv', function() 88 itp('sets an env var and returns success', function() 89 local name = 'NVIM_UNIT_TEST_SETENV_1N' 90 local value = 'NVIM_UNIT_TEST_SETENV_1V' 91 eq(nil, os.getenv(name)) 92 eq(OK, os_setenv(name, value, 1)) 93 eq(value, os.getenv(name)) 94 95 -- Set empty, then set non-empty, then retrieve. 96 eq(OK, os_setenv(name, '', 1)) 97 eq('', os.getenv(name)) 98 eq(OK, os_setenv(name, 'non-empty', 1)) 99 eq('non-empty', os.getenv(name)) 100 end) 101 102 itp('`overwrite` behavior', function() 103 local name = 'NVIM_UNIT_TEST_SETENV_2N' 104 local value = 'NVIM_UNIT_TEST_SETENV_2V' 105 local value_updated = 'NVIM_UNIT_TEST_SETENV_2V_UPDATED' 106 eq(OK, os_setenv(name, value, 0)) 107 eq(value, os.getenv(name)) 108 eq(OK, os_setenv(name, value_updated, 0)) 109 eq(value, os.getenv(name)) 110 eq(OK, os_setenv(name, value_updated, 1)) 111 eq(value_updated, os.getenv(name)) 112 end) 113 end) 114 115 describe('os_setenv_append_path', function() 116 itp('appends :/foo/bar to $PATH', function() 117 local original_path = os.getenv('PATH') 118 eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz.exe'))) 119 eq(original_path .. ':/foo/bar', os.getenv('PATH')) 120 end) 121 122 itp('avoids redundant separator when appending to $PATH #7377', function() 123 os_setenv('PATH', '/a/b/c:', true) 124 eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz.exe'))) 125 -- Must not have duplicate separators. #7377 126 eq('/a/b/c:/foo/bar', os.getenv('PATH')) 127 end) 128 129 itp('returns false if `fname` is not absolute', function() 130 local original_path = os.getenv('PATH') 131 eq(false, cimp.os_setenv_append_path(to_cstr('foo/bar/baz.exe'))) 132 eq(original_path, os.getenv('PATH')) 133 end) 134 end) 135 136 describe('os_shell_is_cmdexe', function() 137 itp('returns true for expected names', function() 138 eq(true, cimp.os_shell_is_cmdexe(to_cstr('cmd.exe'))) 139 eq(true, cimp.os_shell_is_cmdexe(to_cstr('cmd'))) 140 eq(true, cimp.os_shell_is_cmdexe(to_cstr('CMD.EXE'))) 141 eq(true, cimp.os_shell_is_cmdexe(to_cstr('CMD'))) 142 143 os_setenv('COMSPEC', '/foo/bar/cmd.exe', 0) 144 eq(true, cimp.os_shell_is_cmdexe(to_cstr('$COMSPEC'))) 145 os_setenv('COMSPEC', [[C:\system32\cmd.exe]], 0) 146 eq(true, cimp.os_shell_is_cmdexe(to_cstr('$COMSPEC'))) 147 end) 148 itp('returns false for unexpected names', function() 149 eq(false, cimp.os_shell_is_cmdexe(to_cstr(''))) 150 eq(false, cimp.os_shell_is_cmdexe(to_cstr('powershell'))) 151 eq(false, cimp.os_shell_is_cmdexe(to_cstr(' cmd.exe '))) 152 eq(false, cimp.os_shell_is_cmdexe(to_cstr('cm'))) 153 eq(false, cimp.os_shell_is_cmdexe(to_cstr('md'))) 154 eq(false, cimp.os_shell_is_cmdexe(to_cstr('cmd.ex'))) 155 156 os_setenv('COMSPEC', '/foo/bar/cmd', 0) 157 eq(false, cimp.os_shell_is_cmdexe(to_cstr('$COMSPEC'))) 158 end) 159 end) 160 161 describe('os_getenv', function() 162 itp('reads an env var', function() 163 local name = 'NVIM_UNIT_TEST_GETENV_1N' 164 local value = 'NVIM_UNIT_TEST_GETENV_1V' 165 eq(NULL, os_getenv(name)) 166 -- Use os_setenv because Lua doesn't have setenv. 167 os_setenv(name, value, 1) 168 eq(value, os_getenv(name)) 169 170 -- Shortest non-empty value 171 os_setenv(name, 'z', 1) 172 eq('z', os_getenv(name)) 173 174 -- Get a big value. 175 local bigval = ('x'):rep(256) 176 eq(OK, os_setenv(name, bigval, 1)) 177 eq(bigval, os_getenv(name)) 178 179 -- Set non-empty, then set empty. 180 eq(OK, os_setenv(name, 'non-empty', 1)) 181 eq('non-empty', os_getenv(name)) 182 eq(OK, os_setenv(name, '', 1)) 183 eq(NULL, os_getenv(name)) 184 end) 185 186 itp('returns NULL if the env var is not found', function() 187 eq(NULL, os_getenv('NVIM_UNIT_TEST_GETENV_NOTFOUND')) 188 end) 189 end) 190 191 describe('os_getenv_buf', function() 192 itp('reads an env var into given buffer', function() 193 local name = 'NVIM_UNIT_TEST_GETENV_1N' 194 local value = 'NVIM_UNIT_TEST_GETENV_1V' 195 local bufsize = 200 196 local buf = cstr(bufsize, '') 197 eq(NULL, os_getenv_buf(name, buf, bufsize)) 198 -- Use os_setenv because Lua doesn't have setenv. 199 os_setenv(name, value, 1) 200 eq(value, os_getenv_buf(name, buf, bufsize)) 201 202 -- Shortest non-empty value 203 os_setenv(name, 'z', 1) 204 eq('z', os_getenv_buf(name, buf, bufsize)) 205 206 -- Variable size above `bufsize` gets truncated 207 local verybigval = ('y'):rep(bufsize + 10) 208 local trunc = string.sub(verybigval, 0, bufsize - 1) 209 eq(OK, os_setenv(name, verybigval, 1)) 210 eq(trunc, os_getenv_buf(name, buf, bufsize)) 211 end) 212 end) 213 214 describe('os_getenv_noalloc', function() 215 itp('reads an env var without memory allocation', function() 216 local name = 'NVIM_UNIT_TEST_GETENV_1N' 217 local value = 'NVIM_UNIT_TEST_GETENV_1V' 218 eq(NULL, os_getenv_noalloc(name)) 219 -- Use os_setenv because Lua doesn't have setenv. 220 os_setenv(name, value, 1) 221 eq(value, os_getenv_noalloc(name)) 222 223 -- Shortest non-empty value 224 os_setenv(name, 'z', 1) 225 eq('z', os_getenv_noalloc(name)) 226 227 local bigval = ('x'):rep(256) 228 eq(OK, os_setenv(name, bigval, 1)) 229 eq(bigval, os_getenv_noalloc(name)) 230 231 -- Variable size above NameBuff size gets truncated 232 -- This assumes MAXPATHL is 4096 bytes. 233 local verybigval = ('y'):rep(4096) 234 local trunc = string.sub(verybigval, 0, 4095) 235 eq(OK, os_setenv(name, verybigval, 1)) 236 eq(trunc, os_getenv_noalloc(name)) 237 238 -- Set non-empty, then set empty. 239 eq(OK, os_setenv(name, 'non-empty', 1)) 240 eq('non-empty', os_getenv_noalloc(name)) 241 eq(OK, os_setenv(name, '', 1)) 242 eq(NULL, os_getenv_noalloc(name)) 243 end) 244 245 itp('returns NULL if the env var is not found', function() 246 eq(NULL, os_getenv_noalloc('NVIM_UNIT_TEST_GETENV_NOTFOUND')) 247 end) 248 end) 249 250 itp('os_unsetenv', function() 251 local name = 'TEST_UNSETENV' 252 local value = 'TESTVALUE' 253 os_setenv(name, value, 1) 254 eq(OK, os_unsetenv(name)) 255 neq(value, os_getenv(name)) 256 -- Depending on the platform the var might be unset or set as '' 257 assert.True(os_getenv(name) == nil or os_getenv(name) == '') 258 if os_getenv(name) == nil then 259 eq(false, os_env_exists(name, false)) 260 end 261 end) 262 263 describe('os_getenvname_at_index', function() 264 itp('returns names of environment variables', function() 265 local test_name = 'NVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1N' 266 local test_value = 'NVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1V' 267 os_setenv(test_name, test_value, 1) 268 local i = 0 269 local names = {} 270 local found_name = false 271 local name = cimp.os_getenvname_at_index(i) 272 while name ~= NULL do 273 table.insert(names, ffi.string(name)) 274 if (ffi.string(name)) == test_name then 275 found_name = true 276 end 277 i = i + 1 278 name = cimp.os_getenvname_at_index(i) 279 end 280 eq(true, #names > 0) 281 eq(true, found_name) 282 end) 283 284 itp('returns NULL if the index is out of bounds', function() 285 local huge = ffi.new('size_t', 10000) 286 local maxuint32 = ffi.new('size_t', 4294967295) 287 eq(NULL, cimp.os_getenvname_at_index(huge)) 288 eq(NULL, cimp.os_getenvname_at_index(maxuint32)) 289 290 if ffi.abi('64bit') then 291 -- couldn't use a bigger number because it gets converted to 292 -- double somewhere, should be big enough anyway 293 -- maxuint64 = ffi.new 'size_t', 18446744073709551615 294 local maxuint64 = ffi.new('size_t', 18446744073709000000) 295 eq(NULL, cimp.os_getenvname_at_index(maxuint64)) 296 end 297 end) 298 end) 299 300 describe('os_get_pid', function() 301 itp('returns the process ID', function() 302 local stat_file = io.open('/proc/self/stat') 303 if stat_file then 304 local stat_str = stat_file:read('*l') 305 stat_file:close() 306 local pid = tonumber((stat_str:match('%d+'))) 307 eq(pid, tonumber(cimp.os_get_pid())) 308 else 309 -- /proc is not available on all systems, test if pid is nonzero. 310 eq(true, (cimp.os_get_pid() > 0)) 311 end 312 end) 313 end) 314 315 describe('os_get_hostname', function() 316 itp('returns the hostname', function() 317 local handle = io.popen('hostname') 318 local hostname = handle:read('*l') 319 handle:close() 320 local hostname_buf = cstr(255, '') 321 cimp.os_get_hostname(hostname_buf, 255) 322 eq(hostname, (ffi.string(hostname_buf))) 323 end) 324 end) 325 326 describe('expand_env_esc', function() 327 itp('expands environment variables', function() 328 local name = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCN' 329 local value = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCV' 330 os_setenv(name, value, 1) 331 -- TODO(bobtwinkles) This only tests Unix expansions. There should be a 332 -- test for Windows as well 333 local input1 = to_cstr('$NVIM_UNIT_TEST_EXPAND_ENV_ESCN/test') 334 local input2 = to_cstr('${NVIM_UNIT_TEST_EXPAND_ENV_ESCN}/test') 335 local output_buff1 = cstr(255, '') 336 local output_buff2 = cstr(255, '') 337 local output_expected = 'NVIM_UNIT_TEST_EXPAND_ENV_ESCV/test' 338 cimp.expand_env_esc(input1, output_buff1, 255, false, true, NULL) 339 cimp.expand_env_esc(input2, output_buff2, 255, false, true, NULL) 340 eq(output_expected, ffi.string(output_buff1)) 341 eq(output_expected, ffi.string(output_buff2)) 342 end) 343 344 itp('expands ~ once when `one` is true', function() 345 local input = '~/foo ~ foo' 346 local homedir = cstr(255, '') 347 cimp.expand_env_esc(to_cstr('~'), homedir, 255, false, true, NULL) 348 local output_expected = ffi.string(homedir) .. '/foo ~ foo' 349 local output = cstr(255, '') 350 cimp.expand_env_esc(to_cstr(input), output, 255, false, true, NULL) 351 eq(ffi.string(output), ffi.string(output_expected)) 352 end) 353 354 itp('expands ~ every time when `one` is false', function() 355 local input = to_cstr('~/foo ~ foo') 356 local dst = cstr(255, '') 357 cimp.expand_env_esc(to_cstr('~'), dst, 255, false, true, NULL) 358 local homedir = ffi.string(dst) 359 local output_expected = homedir .. '/foo ' .. homedir .. ' foo' 360 local output = cstr(255, '') 361 cimp.expand_env_esc(input, output, 255, false, false, NULL) 362 eq(output_expected, ffi.string(output)) 363 end) 364 365 itp('does not crash #3725', function() 366 local name_out = ffi.new('char[100]') 367 cimp.os_get_username(name_out, 100) 368 local curuser = ffi.string(name_out) 369 370 local src = 371 to_cstr('~' .. curuser .. '/Vcs/django-rest-framework/rest_framework/renderers.py') 372 local dst = cstr(256, '~' .. curuser) 373 cimp.expand_env_esc(src, dst, 256, false, false, NULL) 374 local len = string.len(ffi.string(dst)) 375 assert.True(len > 56) 376 assert.True(len < 256) 377 end) 378 379 itp('respects `dstlen` without expansion', function() 380 local input = to_cstr('this is a very long thing that will not fit') 381 -- The buffer is long enough to actually contain the full input in case the 382 -- test fails, but we don't tell expand_env_esc that 383 local output = cstr(255, '') 384 cimp.expand_env_esc(input, output, 5, false, true, NULL) 385 -- Make sure the first few characters are copied properly and that there is a 386 -- terminating null character 387 for i = 0, 3 do 388 eq(input[i], output[i]) 389 end 390 eq(0, output[4]) 391 end) 392 393 itp('respects `dstlen` with expansion', function() 394 local varname = to_cstr('NVIM_UNIT_TEST_EXPAND_ENV_ESC_DSTLENN') 395 local varval = to_cstr('NVIM_UNIT_TEST_EXPAND_ENV_ESC_DSTLENV') 396 cimp.os_setenv(varname, varval, 1) 397 -- TODO(bobtwinkles) This test uses unix-specific environment variable accessing, 398 -- should have some alternative for windows 399 local input = to_cstr('$NVIM_UNIT_TEST_EXPAND_ENV_ESC_DSTLENN/even more stuff') 400 -- The buffer is long enough to actually contain the full input in case the 401 -- test fails, but we don't tell expand_env_esc that 402 local output = cstr(255, '') 403 cimp.expand_env_esc(input, output, 5, false, true, NULL) 404 -- Make sure the first few characters are copied properly and that there is a 405 -- terminating null character 406 -- expand_env_esc SHOULD NOT expand the variable if there is not enough space to 407 -- contain the result 408 for i = 0, 3 do 409 eq(input[i], output[i]) 410 end 411 eq(0, output[4]) 412 end) 413 end) 414 end)