neovim

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

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)