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