Merge #6214 from ZyX-I/split-eval'/isolated-unittests

Run all unit tests in separate processes
This commit is contained in:
Justin M. Keyes
2017-03-12 10:52:13 +01:00
committed by GitHub
32 changed files with 1226 additions and 732 deletions

101
test/README.md Normal file
View File

@@ -0,0 +1,101 @@
# Tests
Tests are run by `/cmake/RunTests.cmake` file, using busted.
## Directory structure
Directories with tests: `/test/benchmark` for benchmarks, `/test/functional` for
functional tests, `/test/unit` for unit tests. `/test/config` contains `*.in`
files (currently a single one) which are transformed into `*.lua` files using
`configure_file` CMake command: this is for acessing CMake variables in lua
tests. `/test/includes` contains include files for use by luajit `ffi.cdef`
C definitions parser: normally used to make macros not accessible via this
mechanism accessible the other way.
Files `/test/*/preload.lua` contain modules which will be preloaded by busted,
via `--helper` option. `/test/**/helpers.lua` contain various “library”
functions, (intended to be) used by a number of tests and not just a single one.
`/test/*/**/*_spec.lua` are files containing actual tests. Files that do not end
with a `_spec.lua` are libraries like `/test/**/helpers.lua`, except that they
have some common topic.
Tests inside `/test/unit` and `/test/functional` are normally divided into
groups by the semantic component they are testing.
## Environment variables
Test behaviour is affected by environment variables. Currently supported
(Functional, Unit, Benchmarks) (when Defined; when set to _1_; when defined,
treated as Integer; when defined, treated as String; !must be defined to
function properly):
`GDB` (F) (D): makes nvim instances to be run under `gdbserver`. It will be
accessible on `localhost:7777`: use `gdb build/bin/nvim`, type `target remote
:7777` inside.
`GDBSERVER_PORT` (F) (I): overrides port used for `GDB`.
`VALGRIND` (F) (D): makes nvim instances to be run under `valgrind`. Log files
are named `valgrind-%p.log` in this case. Note that non-empty valgrind log may
fail tests. Valgrind arguments may be seen in `/test/functional/helpers.lua`.
May be used in conjunction with `GDB`.
`VALGRIND_LOG` (F) (S): overrides valgrind log file name used for `VALGRIND`.
`TEST_SKIP_FRAGILE` (F) (D): makes test suite skip some fragile tests.
`NVIM_PROG`, `NVIM_PRG` (F) (S): override path to Neovim executable (default to
`build/bin/nvim`).
`CC` (U) (S): specifies which C compiler to use to preprocess files. Currently
only compilers with gcc-compatible arguments are supported.
`NVIM_TEST_MAIN_CDEFS` (U) (1): makes `ffi.cdef` run in main process. This
raises a possibility of bugs due to conflicts in header definitions, despite the
counters, but greatly speeds up unit tests by not requiring `ffi.cdef` to do
parsing of big strings with C definitions.
`NVIM_TEST_PRINT_I` (U) (1): makes `cimport` print preprocessed, but not yet
filtered through `formatc` headers. Used to debug `formatc`. Printing is done
with the line numbers.
`NVIM_TEST_PRINT_CDEF` (U) (1): makes `cimport` print final lines which will be
then passed to `ffi.cdef`. Used to debug errors `ffi.cdef` happens to throw
sometimes.
`NVIM_TEST_PRINT_SYSCALLS` (U) (1): makes it print to stderr when syscall
wrappers are called and what they returned. Used to debug code which makes unit
tests be executed in separate processes.
`NVIM_TEST_RUN_FAILING_TESTS` (U) (1): makes `itp` run tests which are known to
fail (marked by setting third argument to `true`).
`LOG_DIR` (FU) (S!): specifies where to seek for valgrind and ASAN log files.
`NVIM_TEST_CORE_*` (FU) (S): a set of environment variables which specify where
to search for core files. Are supposed to be defined all at once.
`NVIM_TEST_CORE_GLOB_DIRECTORY` (FU) (S): directory where core files are
located. May be `.`. This directory is then recursively searched for core files.
Note: this variable must be defined for any of the following to have any effect.
`NVIM_TEST_CORE_GLOB_RE` (FU) (S): regular expression which must be matched by
core files. E.g. `/core[^/]*$`. May be absent, in which case any file is
considered to be matched.
`NVIM_TEST_CORE_EXC_RE` (FU) (S): regular expression which excludes certain
directories from searching for core files inside. E.g. use `^/%.deps$` to not
search inside `/.deps`. If absent, nothing is excluded.
`NVIM_TEST_CORE_DB_CMD` (FU) (S): command to get backtrace out of the debugger.
E.g. `gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c
"$_NVIM_TEST_CORE"`. Defaults to the example command. This debug command may use
environment variables `_NVIM_TEST_APP` (path to application which is being
debugged: normally either nvim or luajit) and `_NVIM_TEST_CORE` (core file to
get backtrace from).
`NVIM_TEST_CORE_RANDOM_SKIP` (FU) (D): makes `check_cores` not check cores after
approximately 90% of the tests. Should be used when finding cores is too hard
for some reason. Normally (on OS X or when `NVIM_TEST_CORE_GLOB_DIRECTORY` is
defined and this variable is not) cores are checked for after each test.

View File

@@ -8,6 +8,15 @@ end
module.test_include_path = "${CMAKE_BINARY_DIR}/test/includes/post"
module.test_libnvim_path = "${TEST_LIBNVIM_PATH}"
module.test_source_path = "${CMAKE_SOURCE_DIR}"
module.test_lua_prg = "${LUA_PRG}"
module.test_luajit_prg = ""
if module.test_luajit_prg == '' then
if module.test_lua_prg:sub(-6) == 'luajit' then
module.test_luajit_prg = module.test_lua_prg
else
module.test_luajit_prg = nil
end
end
table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include")
return module

View File

@@ -30,13 +30,15 @@ local function glob(initial_path, re, exc_re)
if ((not exc_re or not checked_path:match(exc_re))
and e:sub(1, 1) ~= '.') then
local attrs = lfs.attributes(full_path)
local check_key = attrs.dev .. ':' .. tostring(attrs.ino)
if not checked_files[check_key] then
checked_files[check_key] = true
if attrs.mode == 'directory' then
paths_to_check[#paths_to_check + 1] = full_path
elseif not re or checked_path:match(re) then
ret[#ret + 1] = full_path
if attrs then
local check_key = attrs.dev .. ':' .. tostring(attrs.ino)
if not checked_files[check_key] then
checked_files[check_key] = true
if attrs.mode == 'directory' then
paths_to_check[#paths_to_check + 1] = full_path
elseif not re or checked_path:match(re) then
ret[#ret + 1] = full_path
end
end
end
end
@@ -212,6 +214,17 @@ local function check_cores(app)
end
end
local function which(exe)
local pipe = io.popen('which ' .. exe, 'r')
local ret = pipe:read('*a')
pipe:close()
if ret == '' then
return nil
else
return ret:sub(1, -2)
end
end
return {
eq = eq,
neq = neq,
@@ -224,4 +237,5 @@ return {
glob = glob,
check_cores = check_cores,
hasenv = hasenv,
which = which,
}

View File

@@ -1,4 +1,4 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(nil)
local eval_helpers = require('test.unit.eval.helpers')
local cimport = helpers.cimport
@@ -19,47 +19,55 @@ local api = cimport('./src/nvim/api/private/defs.h',
local obj2lua
local obj2lua_tab = {
[tonumber(api.kObjectTypeArray)] = function(obj)
local ret = {[type_key]=list_type}
for i = 1,tonumber(obj.data.array.size) do
ret[i] = obj2lua(obj.data.array.items[i - 1])
end
if ret[1] then
ret[type_key] = nil
end
return ret
end,
[tonumber(api.kObjectTypeDictionary)] = function(obj)
local ret = {}
for i = 1,tonumber(obj.data.dictionary.size) do
local kv_pair = obj.data.dictionary.items[i - 1]
ret[ffi.string(kv_pair.key.data, kv_pair.key.size)] = obj2lua(kv_pair.value)
end
return ret
end,
[tonumber(api.kObjectTypeBoolean)] = function(obj)
if obj.data.boolean == false then
return false
else
return true
end
end,
[tonumber(api.kObjectTypeNil)] = function(_)
return nil_value
end,
[tonumber(api.kObjectTypeFloat)] = function(obj)
return tonumber(obj.data.floating)
end,
[tonumber(api.kObjectTypeInteger)] = function(obj)
return {[type_key]=int_type, value=tonumber(obj.data.integer)}
end,
[tonumber(api.kObjectTypeString)] = function(obj)
return ffi.string(obj.data.string.data, obj.data.string.size)
end,
}
local obj2lua_tab = nil
local function init_obj2lua_tab()
if obj2lua_tab then
return
end
obj2lua_tab = {
[tonumber(api.kObjectTypeArray)] = function(obj)
local ret = {[type_key]=list_type}
for i = 1,tonumber(obj.data.array.size) do
ret[i] = obj2lua(obj.data.array.items[i - 1])
end
if ret[1] then
ret[type_key] = nil
end
return ret
end,
[tonumber(api.kObjectTypeDictionary)] = function(obj)
local ret = {}
for i = 1,tonumber(obj.data.dictionary.size) do
local kv_pair = obj.data.dictionary.items[i - 1]
ret[ffi.string(kv_pair.key.data, kv_pair.key.size)] = obj2lua(kv_pair.value)
end
return ret
end,
[tonumber(api.kObjectTypeBoolean)] = function(obj)
if obj.data.boolean == false then
return false
else
return true
end
end,
[tonumber(api.kObjectTypeNil)] = function(_)
return nil_value
end,
[tonumber(api.kObjectTypeFloat)] = function(obj)
return tonumber(obj.data.floating)
end,
[tonumber(api.kObjectTypeInteger)] = function(obj)
return {[type_key]=int_type, value=tonumber(obj.data.integer)}
end,
[tonumber(api.kObjectTypeString)] = function(obj)
return ffi.string(obj.data.string.data, obj.data.string.size)
end,
}
end
obj2lua = function(obj)
init_obj2lua_tab()
return ((obj2lua_tab[tonumber(obj['type'])] or function(obj_inner)
assert(false, 'Converting ' .. tostring(tonumber(obj_inner['type'])) .. ' is not implementing yet')
end)(obj))

View File

@@ -1,4 +1,5 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local eval_helpers = require('test.unit.eval.helpers')
local api_helpers = require('test.unit.api.helpers')
@@ -25,7 +26,7 @@ describe('vim_to_object', function()
end
local different_output_test = function(name, input, output)
it(name, function()
itp(name, function()
eq(output, vim_to_object(input))
end)
end
@@ -76,19 +77,19 @@ describe('vim_to_object', function()
different_output_test('outputs nil for nested lists (2 level, in dict)',
lst3, {{lst=nil_value}, true, false, 'ttest'})
it('outputs empty list for NULL list', function()
itp('outputs empty list for NULL list', function()
local tt = typvalt('VAR_LIST', {v_list=NULL})
eq(nil, tt.vval.v_list)
eq({[type_key]=list_type}, obj2lua(api.vim_to_object(tt)))
end)
it('outputs empty dict for NULL dict', function()
itp('outputs empty dict for NULL dict', function()
local tt = typvalt('VAR_DICT', {v_dict=NULL})
eq(nil, tt.vval.v_dict)
eq({}, obj2lua(api.vim_to_object(tt)))
end)
it('regression: partials in a list', function()
itp('regression: partials in a list', function()
local llist = {
{
[type_key]=func_type,

View File

@@ -1,5 +1,6 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local to_cstr = helpers.to_cstr
local get_str = helpers.ffi.string
@@ -39,17 +40,17 @@ describe('buffer functions', function()
describe('buf_valid', function()
it('should view NULL as an invalid buffer', function()
itp('should view NULL as an invalid buffer', function()
eq(false, buffer.buf_valid(NULL))
end)
it('should view an open buffer as valid', function()
itp('should view an open buffer as valid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
eq(true, buffer.buf_valid(buf))
end)
it('should view a closed and hidden buffer as valid', function()
itp('should view a closed and hidden buffer as valid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
close_buffer(NULL, buf, 0, 0)
@@ -57,7 +58,7 @@ describe('buffer functions', function()
eq(true, buffer.buf_valid(buf))
end)
it('should view a closed and unloaded buffer as valid', function()
itp('should view a closed and unloaded buffer as valid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
close_buffer(NULL, buf, buffer.DOBUF_UNLOAD, 0)
@@ -65,7 +66,7 @@ describe('buffer functions', function()
eq(true, buffer.buf_valid(buf))
end)
it('should view a closed and wiped buffer as invalid', function()
itp('should view a closed and wiped buffer as invalid', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0)
@@ -84,7 +85,7 @@ describe('buffer functions', function()
return buffer.buflist_findpat(to_cstr(pat), NULL, allow_unlisted, 0, 0)
end
it('should find exact matches', function()
itp('should find exact matches', function()
local buf = buflist_new(path1, buffer.BLN_LISTED)
eq(buf.handle, buflist_findpat(path1, ONLY_LISTED))
@@ -92,7 +93,7 @@ describe('buffer functions', function()
close_buffer(NULL, buf, buffer.DOBUF_WIPE, 0)
end)
it('should prefer to match the start of a file path', function()
itp('should prefer to match the start of a file path', function()
local buf1 = buflist_new(path1, buffer.BLN_LISTED)
local buf2 = buflist_new(path2, buffer.BLN_LISTED)
local buf3 = buflist_new(path3, buffer.BLN_LISTED)
@@ -106,7 +107,7 @@ describe('buffer functions', function()
close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
end)
it('should prefer to match the end of a file over the middle', function()
itp('should prefer to match the end of a file over the middle', function()
--{ Given: Two buffers, where 'test' appears in both
-- And: 'test' appears at the end of buf3 but in the middle of buf2
local buf2 = buflist_new(path2, buffer.BLN_LISTED)
@@ -130,7 +131,7 @@ describe('buffer functions', function()
close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
end)
it('should match a unique fragment of a file path', function()
itp('should match a unique fragment of a file path', function()
local buf1 = buflist_new(path1, buffer.BLN_LISTED)
local buf2 = buflist_new(path2, buffer.BLN_LISTED)
local buf3 = buflist_new(path3, buffer.BLN_LISTED)
@@ -142,7 +143,7 @@ describe('buffer functions', function()
close_buffer(NULL, buf3, buffer.DOBUF_WIPE, 0)
end)
it('should include / ignore unlisted buffers based on the flag.', function()
itp('should include / ignore unlisted buffers based on the flag.', function()
--{ Given: A buffer
local buf3 = buflist_new(path3, buffer.BLN_LISTED)
@@ -169,7 +170,7 @@ describe('buffer functions', function()
--}
end)
it('should prefer listed buffers to unlisted buffers.', function()
itp('should prefer listed buffers to unlisted buffers.', function()
--{ Given: Two buffers that match a pattern
local buf1 = buflist_new(path1, buffer.BLN_LISTED)
local buf2 = buflist_new(path2, buffer.BLN_LISTED)
@@ -265,7 +266,7 @@ describe('buffer functions', function()
local expected_cell_count = option.expected_cell_count or statusline_cell_count
local expected_byte_length = option.expected_byte_length or expected_cell_count
it(description, function()
itp(description, function()
if option.file_name then
buffer.setfname(globals.curbuf, to_cstr(option.file_name), NULL, 1)
else

View File

@@ -1,4 +1,5 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local to_cstr = helpers.to_cstr
@@ -11,25 +12,11 @@ local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval_defs.h',
'./src/nvim/message.h')
describe('json_decode_string()', function()
local saved_p_enc = nil
before_each(function()
saved_p_enc = decode.p_enc
end)
after_each(function()
decode.emsg_silent = 0
decode.p_enc = saved_p_enc
while decode.delete_first_msg() == 1 do
-- Delete all messages
end
end)
local char = function(c)
return ffi.gc(decode.xmemdup(c, 1), decode.xfree)
end
it('does not overflow when running with `n…`, `t…`, `f…`', function()
itp('does not overflow when running with `n…`, `t…`, `f…`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
decode.emsg_silent = 1
-- This will not crash, but if `len` argument will be ignored it will parse
@@ -56,7 +43,7 @@ describe('json_decode_string()', function()
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
it('does not overflow and crash when running with `n`, `t`, `f`', function()
itp('does not overflow and crash when running with `n`, `t`, `f`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
decode.emsg_silent = 1
eq(0, decode.json_decode_string(char('n'), 1, rettv))
@@ -67,7 +54,7 @@ describe('json_decode_string()', function()
eq(decode.VAR_UNKNOWN, rettv.v_type)
end)
it('does not overflow when running with `"…`', function()
itp('does not overflow when running with `"…`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
decode.emsg_silent = 1
eq(0, decode.json_decode_string('"t"', 2, rettv))
@@ -84,7 +71,8 @@ describe('json_decode_string()', function()
eq(msg, ffi.string(decode.last_msg_hist.msg))
end
it('does not overflow in error messages', function()
itp('does not overflow in error messages', function()
local saved_p_enc = decode.p_enc
check_failure(']test', 1, 'E474: No container to close: ]')
check_failure('[}test', 2, 'E474: Closing list with curly bracket: }')
check_failure('{]test', 2,
@@ -129,11 +117,11 @@ describe('json_decode_string()', function()
check_failure('[1test', 2, 'E474: Unexpected end of input: [1')
end)
it('does not overflow with `-`', function()
itp('does not overflow with `-`', function()
check_failure('-0', 1, 'E474: Missing number after minus sign: -')
end)
it('does not overflow and crash when running with `"`', function()
itp('does not overflow and crash when running with `"`', function()
local rettv = ffi.new('typval_T', {v_type=decode.VAR_UNKNOWN})
decode.emsg_silent = 1
eq(0, decode.json_decode_string(char('"'), 1, rettv))

View File

@@ -1,4 +1,5 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local eval_helpers = require('test.unit.eval.helpers')
local cimport = helpers.cimport
@@ -18,25 +19,25 @@ describe('encode_list_write()', function()
return encode.encode_list_write(l, to_cstr(s), #s)
end
it('writes empty string', function()
itp('writes empty string', function()
local l = list()
eq(0, encode_list_write(l, ''))
eq({[type_key]=list_type}, lst2tbl(l))
end)
it('writes ASCII string literal with printable characters', function()
itp('writes ASCII string literal with printable characters', function()
local l = list()
eq(0, encode_list_write(l, 'abc'))
eq({'abc'}, lst2tbl(l))
end)
it('writes string starting with NL', function()
itp('writes string starting with NL', function()
local l = list()
eq(0, encode_list_write(l, '\nabc'))
eq({null_string, 'abc'}, lst2tbl(l))
end)
it('writes string starting with NL twice', function()
itp('writes string starting with NL twice', function()
local l = list()
eq(0, encode_list_write(l, '\nabc'))
eq({null_string, 'abc'}, lst2tbl(l))
@@ -44,13 +45,13 @@ describe('encode_list_write()', function()
eq({null_string, 'abc', 'abc'}, lst2tbl(l))
end)
it('writes string ending with NL', function()
itp('writes string ending with NL', function()
local l = list()
eq(0, encode_list_write(l, 'abc\n'))
eq({'abc', null_string}, lst2tbl(l))
end)
it('writes string ending with NL twice', function()
itp('writes string ending with NL twice', function()
local l = list()
eq(0, encode_list_write(l, 'abc\n'))
eq({'abc', null_string}, lst2tbl(l))
@@ -58,7 +59,7 @@ describe('encode_list_write()', function()
eq({'abc', 'abc', null_string}, lst2tbl(l))
end)
it('writes string starting, ending and containing NL twice', function()
itp('writes string starting, ending and containing NL twice', function()
local l = list()
eq(0, encode_list_write(l, '\na\nb\n'))
eq({null_string, 'a', 'b', null_string}, lst2tbl(l))
@@ -66,7 +67,7 @@ describe('encode_list_write()', function()
eq({null_string, 'a', 'b', null_string, 'a', 'b', null_string}, lst2tbl(l))
end)
it('writes string starting, ending and containing NUL with NL between twice', function()
itp('writes string starting, ending and containing NUL with NL between twice', function()
local l = list()
eq(0, encode_list_write(l, '\0\n\0\n\0'))
eq({'\n', '\n', '\n'}, lst2tbl(l))
@@ -74,7 +75,7 @@ describe('encode_list_write()', function()
eq({'\n', '\n', '\n\n', '\n', '\n'}, lst2tbl(l))
end)
it('writes string starting, ending and containing NL with NUL between twice', function()
itp('writes string starting, ending and containing NL with NUL between twice', function()
local l = list()
eq(0, encode_list_write(l, '\n\0\n\0\n'))
eq({null_string, '\n', '\n', null_string}, lst2tbl(l))
@@ -82,7 +83,7 @@ describe('encode_list_write()', function()
eq({null_string, '\n', '\n', null_string, '\n', '\n', null_string}, lst2tbl(l))
end)
it('writes string containing a single NL twice', function()
itp('writes string containing a single NL twice', function()
local l = list()
eq(0, encode_list_write(l, '\n'))
eq({null_string, null_string}, lst2tbl(l))
@@ -90,7 +91,7 @@ describe('encode_list_write()', function()
eq({null_string, null_string, null_string}, lst2tbl(l))
end)
it('writes string containing a few NLs twice', function()
itp('writes string containing a few NLs twice', function()
local l = list()
eq(0, encode_list_write(l, '\n\n\n'))
eq({null_string, null_string, null_string, null_string}, lst2tbl(l))

View File

@@ -1,4 +1,4 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(nil)
local cimport = helpers.cimport
local to_cstr = helpers.to_cstr
@@ -46,12 +46,6 @@ local function list(...)
return ret
end
local special_tab = {
[eval.kSpecialVarFalse] = false,
[eval.kSpecialVarNull] = nil_value,
[eval.kSpecialVarTrue] = true,
}
local ptr2key = function(ptr)
return tostring(ptr)
end
@@ -60,64 +54,74 @@ local lst2tbl
local dct2tbl
local typvalt2lua
local typvalt2lua_tab
local typvalt2lua_tab = nil
typvalt2lua_tab = {
[tonumber(eval.VAR_SPECIAL)] = function(t)
return special_tab[t.vval.v_special]
end,
[tonumber(eval.VAR_NUMBER)] = function(t)
return {[type_key]=int_type, value=tonumber(t.vval.v_number)}
end,
[tonumber(eval.VAR_FLOAT)] = function(t)
return tonumber(t.vval.v_float)
end,
[tonumber(eval.VAR_STRING)] = function(t)
local str = t.vval.v_string
if str == nil then
return null_string
else
return ffi.string(str)
end
end,
[tonumber(eval.VAR_LIST)] = function(t, processed)
return lst2tbl(t.vval.v_list, processed)
end,
[tonumber(eval.VAR_DICT)] = function(t, processed)
return dct2tbl(t.vval.v_dict, processed)
end,
[tonumber(eval.VAR_FUNC)] = function(t, processed)
return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t, processed or {})}
end,
[tonumber(eval.VAR_PARTIAL)] = function(t, processed)
local p_key = ptr2key(t)
if processed[p_key] then
return processed[p_key]
end
local pt = t.vval.v_partial
local value, auto, dict, argv = nil, nil, nil, nil
if pt ~= nil then
value = ffi.string(pt.pt_name)
auto = pt.pt_auto and true or nil
argv = {}
for i = 1, pt.pt_argc do
argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed)
local function typvalt2lua_tab_init()
if typvalt2lua_tab then
return
end
typvalt2lua_tab = {
[tonumber(eval.VAR_SPECIAL)] = function(t)
return ({
[eval.kSpecialVarFalse] = false,
[eval.kSpecialVarNull] = nil_value,
[eval.kSpecialVarTrue] = true,
})[t.vval.v_special]
end,
[tonumber(eval.VAR_NUMBER)] = function(t)
return {[type_key]=int_type, value=tonumber(t.vval.v_number)}
end,
[tonumber(eval.VAR_FLOAT)] = function(t)
return tonumber(t.vval.v_float)
end,
[tonumber(eval.VAR_STRING)] = function(t)
local str = t.vval.v_string
if str == nil then
return null_string
else
return ffi.string(str)
end
if pt.pt_dict ~= nil then
dict = dct2tbl(pt.pt_dict)
end,
[tonumber(eval.VAR_LIST)] = function(t, processed)
return lst2tbl(t.vval.v_list, processed)
end,
[tonumber(eval.VAR_DICT)] = function(t, processed)
return dct2tbl(t.vval.v_dict, processed)
end,
[tonumber(eval.VAR_FUNC)] = function(t, processed)
return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t, processed or {})}
end,
[tonumber(eval.VAR_PARTIAL)] = function(t, processed)
local p_key = ptr2key(t)
if processed[p_key] then
return processed[p_key]
end
end
return {
[type_key]=func_type,
value=value,
auto=auto,
args=argv,
dict=dict,
}
end,
}
local pt = t.vval.v_partial
local value, auto, dict, argv = nil, nil, nil, nil
if pt ~= nil then
value = ffi.string(pt.pt_name)
auto = pt.pt_auto and true or nil
argv = {}
for i = 1, pt.pt_argc do
argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed)
end
if pt.pt_dict ~= nil then
dict = dct2tbl(pt.pt_dict)
end
end
return {
[type_key]=func_type,
value=value,
auto=auto,
args=argv,
dict=dict,
}
end,
}
end
typvalt2lua = function(t, processed)
typvalt2lua_tab_init()
return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner)
assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet')
end)(t, processed or {}))
@@ -169,9 +173,10 @@ lst2tbl = function(l, processed)
return ret
end
local hi_key_removed = eval._hash_key_removed()
local hi_key_removed = nil
local function dict_iter(d, return_hi)
hi_key_removed = hi_key_removed or eval._hash_key_removed()
local init_s = {
todo=d.dv_hashtab.ht_used,
hi=d.dv_hashtab.ht_array,
@@ -320,25 +325,28 @@ local lua2typvalt_type_tab = {
end,
}
local special_vals = {
[null_string] = {eval.VAR_STRING, {v_string=ffi.cast('char_u*', nil)}},
[null_list] = {eval.VAR_LIST, {v_list=ffi.cast('list_T*', nil)}},
[null_dict] = {eval.VAR_DICT, {v_dict=ffi.cast('dict_T*', nil)}},
[nil_value] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarNull}},
[true] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarTrue}},
[false] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarFalse}},
}
for k, v in pairs(special_vals) do
local tmp = function(typ, vval)
special_vals[k] = function()
return typvalt(typ, vval)
end
end
tmp(v[1], v[2])
end
local special_vals = nil
lua2typvalt = function(l, processed)
if not special_vals then
special_vals = {
[null_string] = {'VAR_STRING', {v_string=ffi.cast('char_u*', nil)}},
[null_list] = {'VAR_LIST', {v_list=ffi.cast('list_T*', nil)}},
[null_dict] = {'VAR_DICT', {v_dict=ffi.cast('dict_T*', nil)}},
[nil_value] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarNull}},
[true] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarTrue}},
[false] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarFalse}},
}
for k, v in pairs(special_vals) do
local tmp = function(typ, vval)
special_vals[k] = function()
return typvalt(eval[typ], vval)
end
end
tmp(v[1], v[2])
end
end
processed = processed or {}
if l == nil or l == nil_value then
return special_vals[nil_value]()
@@ -360,7 +368,7 @@ lua2typvalt = function(l, processed)
return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)})
elseif type(l) == 'cdata' then
local tv = typvalt(eval.VAR_UNKNOWN)
eval.tv_copy(l, tv)
eval.copy_tv(l, tv)
return tv
end
end

View File

@@ -1,4 +1,5 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local to_cstr = helpers.to_cstr
@@ -15,7 +16,7 @@ local eval_expr = function(expr)
end
describe('NULL typval_T', function()
it('is produced by $XXX_UNEXISTENT_VAR_XXX', function()
itp('is produced by $XXX_UNEXISTENT_VAR_XXX', function()
-- Required for various tests which need to check whether typval_T with NULL
-- string works correctly. This test checks that unexistent environment
-- variable produces NULL string, not that some specific environment
@@ -29,13 +30,13 @@ describe('NULL typval_T', function()
eq(nil, rettv.vval.v_string)
end)
it('is produced by v:_null_list', function()
itp('is produced by v:_null_list', function()
local rettv = eval_expr('v:_null_list')
eq(eval.VAR_LIST, rettv.v_type)
eq(nil, rettv.vval.v_list)
end)
it('is produced by v:_null_dict', function()
itp('is produced by v:_null_dict', function()
local rettv = eval_expr('v:_null_dict')
eq(eval.VAR_DICT, rettv.v_type)
eq(nil, rettv.vval.v_dict)

View File

@@ -1,4 +1,5 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local eval_helpers = require('test.unit.eval.helpers')
local alloc_log_new = helpers.alloc_log_new
@@ -26,7 +27,7 @@ after_each(function()
end)
describe('clear_tv()', function()
it('successfully frees all lists in [&l [1], *l, *l]', function()
itp('successfully frees all lists in [&l [1], *l, *l]', function()
local l_inner = {1}
local list = {l_inner, l_inner, l_inner}
local list_tv = ffi.gc(lua2typvalt(list), nil)
@@ -53,7 +54,7 @@ describe('clear_tv()', function()
a.freed(list_p),
})
end)
it('successfully frees all lists in [&l [], *l, *l]', function()
itp('successfully frees all lists in [&l [], *l, *l]', function()
local l_inner = {[type_key]=list_type}
local list = {l_inner, l_inner, l_inner}
local list_tv = ffi.gc(lua2typvalt(list), nil)
@@ -77,7 +78,7 @@ describe('clear_tv()', function()
a.freed(list_p),
})
end)
it('successfully frees all dictionaries in [&d {}, *d]', function()
itp('successfully frees all dictionaries in [&d {}, *d]', function()
local d_inner = {}
local list = {d_inner, d_inner}
local list_tv = ffi.gc(lua2typvalt(list), nil)
@@ -99,7 +100,7 @@ describe('clear_tv()', function()
a.freed(list_p),
})
end)
it('successfully frees all dictionaries in [&d {a: 1}, *d]', function()
itp('successfully frees all dictionaries in [&d {a: 1}, *d]', function()
local d_inner = {a=1}
local list = {d_inner, d_inner}
local list_tv = ffi.gc(lua2typvalt(list), nil)

View File

@@ -1,4 +1,5 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
--{:cimport, :internalize, :eq, :neq, :ffi, :lib, :cstr, :to_cstr} = require 'test.unit.helpers'
local eq = helpers.eq
@@ -16,67 +17,67 @@ describe('file_pat functions', function()
return ffi.string(res)
end
it('returns ^path$ regex for literal path input', function()
itp('returns ^path$ regex for literal path input', function()
eq( '^path$', file_pat_to_reg_pat('path'))
end)
it('does not prepend ^ when there is a starting glob (*)', function()
itp('does not prepend ^ when there is a starting glob (*)', function()
eq('path$', file_pat_to_reg_pat('*path'))
end)
it('does not append $ when there is an ending glob (*)', function()
itp('does not append $ when there is an ending glob (*)', function()
eq('^path', file_pat_to_reg_pat('path*'))
end)
it('does not include ^ or $ when surrounded by globs (*)', function()
itp('does not include ^ or $ when surrounded by globs (*)', function()
eq('path', file_pat_to_reg_pat('*path*'))
end)
it('replaces the bash any character (?) with the regex any character (.)', function()
itp('replaces the bash any character (?) with the regex any character (.)', function()
eq('^foo.bar$', file_pat_to_reg_pat('foo?bar'))
end)
it('replaces a glob (*) in the middle of a path with regex multiple any character (.*)',
itp('replaces a glob (*) in the middle of a path with regex multiple any character (.*)',
function()
eq('^foo.*bar$', file_pat_to_reg_pat('foo*bar'))
end)
it([[unescapes \? to ?]], function()
itp([[unescapes \? to ?]], function()
eq('^foo?bar$', file_pat_to_reg_pat([[foo\?bar]]))
end)
it([[unescapes \% to %]], function()
itp([[unescapes \% to %]], function()
eq('^foo%bar$', file_pat_to_reg_pat([[foo\%bar]]))
end)
it([[unescapes \, to ,]], function()
itp([[unescapes \, to ,]], function()
eq('^foo,bar$', file_pat_to_reg_pat([[foo\,bar]]))
end)
it([[unescapes '\ ' to ' ']], function()
itp([[unescapes '\ ' to ' ']], function()
eq('^foo bar$', file_pat_to_reg_pat([[foo\ bar]]))
end)
it([[escapes . to \.]], function()
itp([[escapes . to \.]], function()
eq([[^foo\.bar$]], file_pat_to_reg_pat('foo.bar'))
end)
it('Converts bash brace expansion {a,b} to regex options (a|b)', function()
itp('Converts bash brace expansion {a,b} to regex options (a|b)', function()
eq([[^foo\(bar\|baz\)$]], file_pat_to_reg_pat('foo{bar,baz}'))
end)
it('Collapses multiple consecutive * into a single character', function()
itp('Collapses multiple consecutive * into a single character', function()
eq([[^foo.*bar$]], file_pat_to_reg_pat('foo*******bar'))
eq([[foobar$]], file_pat_to_reg_pat('********foobar'))
eq([[^foobar]], file_pat_to_reg_pat('foobar********'))
end)
it('Does not escape ^', function()
itp('Does not escape ^', function()
eq([[^^blah$]], file_pat_to_reg_pat('^blah'))
eq([[^foo^bar$]], file_pat_to_reg_pat('foo^bar'))
end)
it('Does not escape $', function()
itp('Does not escape $', function()
eq([[^blah$$]], file_pat_to_reg_pat('blah$'))
eq([[^foo$bar$]], file_pat_to_reg_pat('foo$bar'))
end)

View File

@@ -0,0 +1,11 @@
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdlib.h>
enum {
kPOSIXErrnoEINTR = EINTR,
kPOSIXErrnoECHILD = ECHILD,
kPOSIXWaitWUNTRACED = WUNTRACED,
};

View File

@@ -1,4 +1,5 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local internalize = helpers.internalize
@@ -8,7 +9,7 @@ local ffi = helpers.ffi
local to_cstr = helpers.to_cstr
local NULL = helpers.NULL
local garray = cimport('stdlib.h', './src/nvim/garray.h')
local garray = cimport('./src/nvim/garray.h')
local itemsize = 14
local growsize = 95
@@ -156,7 +157,7 @@ local ga_append_ints = function(garr, ...)
end
-- enhanced constructors
local garray_ctype = ffi.typeof('garray_T[1]')
local garray_ctype = function(...) return ffi.typeof('garray_T[1]')(...) end
local new_garray = function()
local garr = garray_ctype()
return ffi.gc(garr, ga_clear)
@@ -183,7 +184,7 @@ end
describe('garray', function()
describe('ga_init', function()
it('initializes the values of the garray', function()
itp('initializes the values of the garray', function()
local garr = new_garray()
ga_init(garr, itemsize, growsize)
eq(0, ga_len(garr))
@@ -204,7 +205,7 @@ describe('garray', function()
return garr
end
it('grows by growsize items if num < growsize', function()
itp('grows by growsize items if num < growsize', function()
itemsize = 16
growsize = 4
local grow_by = growsize - 1
@@ -213,7 +214,7 @@ describe('garray', function()
eq(growsize, ga_maxlen(garr)) -- we requested LESS than growsize, so...
end)
it('grows by num items if num > growsize', function()
itp('grows by num items if num > growsize', function()
itemsize = 16
growsize = 4
local grow_by = growsize + 1
@@ -222,7 +223,7 @@ describe('garray', function()
eq(grow_by, ga_maxlen(garr)) -- we requested MORE than growsize, so...
end)
it('does not grow when nothing is requested', function()
itp('does not grow when nothing is requested', function()
local garr = new_and_grow(16, 4, 0)
eq(NULL, ga_data(garr))
eq(0, ga_maxlen(garr))
@@ -230,7 +231,7 @@ describe('garray', function()
end)
describe('ga_clear', function()
it('clears an already allocated array', function()
itp('clears an already allocated array', function()
-- allocate and scramble an array
local garr = garray_ctype()
ga_init(garr, itemsize, growsize)
@@ -247,7 +248,7 @@ describe('garray', function()
end)
describe('ga_append', function()
it('can append bytes', function()
itp('can append bytes', function()
-- this is the actual ga_append, the others are just emulated lua
-- versions
local garr = new_garray()
@@ -262,7 +263,7 @@ describe('garray', function()
eq('hello', ffi.string(bytes))
end)
it('can append integers', function()
itp('can append integers', function()
local garr = new_garray()
ga_init(garr, ffi.sizeof("int"), 1)
local input = {
@@ -279,7 +280,7 @@ describe('garray', function()
end
end)
it('can append strings to a growing array of strings', function()
itp('can append strings to a growing array of strings', function()
local garr = new_string_garray()
local input = {
"some",
@@ -298,7 +299,7 @@ describe('garray', function()
end)
describe('ga_concat', function()
it('concatenates the parameter to the growing byte array', function()
itp('concatenates the parameter to the growing byte array', function()
local garr = new_garray()
ga_init(garr, ffi.sizeof("char"), 1)
local str = "ohwell●●"
@@ -329,11 +330,11 @@ describe('garray', function()
end
describe('ga_concat_strings', function()
it('returns an empty string when concatenating an empty array', function()
itp('returns an empty string when concatenating an empty array', function()
test_concat_fn({ }, ga_concat_strings)
end)
it('can concatenate a non-empty array', function()
itp('can concatenate a non-empty array', function()
test_concat_fn({
'oh',
'my',
@@ -343,11 +344,11 @@ describe('garray', function()
end)
describe('ga_concat_strings_sep', function()
it('returns an empty string when concatenating an empty array', function()
itp('returns an empty string when concatenating an empty array', function()
test_concat_fn({ }, ga_concat_strings_sep, '---')
end)
it('can concatenate a non-empty array', function()
itp('can concatenate a non-empty array', function()
local sep = '-●●-'
test_concat_fn({
'oh',
@@ -358,7 +359,7 @@ describe('garray', function()
end)
describe('ga_remove_duplicate_strings', function()
it('sorts and removes duplicate strings', function()
itp('sorts and removes duplicate strings', function()
local garr = new_string_garray()
local input = {
'ccc',

View File

@@ -4,8 +4,15 @@ local Set = require('test.unit.set')
local Preprocess = require('test.unit.preprocess')
local Paths = require('test.config.paths')
local global_helpers = require('test.helpers')
local assert = require('luassert')
local say = require('say')
local posix = nil
local syscall = nil
local check_cores = global_helpers.check_cores
local neq = global_helpers.neq
local map = global_helpers.map
local eq = global_helpers.eq
local ok = global_helpers.ok
@@ -15,20 +22,110 @@ local NULL = ffi.cast('void*', 0)
local OK = 1
local FAIL = 0
local cimport
-- add some standard header locations
for _, p in ipairs(Paths.include_paths) do
Preprocess.add_to_include_path(p)
end
-- load neovim shared library
local libnvim = ffi.load(Paths.test_libnvim_path)
local child_pid = nil
local function only_separate(func)
return function(...)
if child_pid ~= 0 then
error('This function must be run in a separate process only')
end
return func(...)
end
end
local child_calls_init = {}
local child_calls_mod = nil
local child_calls_mod_once = nil
local function child_call(func, ret)
return function(...)
local child_calls = child_calls_mod or child_calls_init
if child_pid ~= 0 then
child_calls[#child_calls + 1] = {func=func, args={...}}
return ret
else
return func(...)
end
end
end
-- Run some code at the start of the child process, before running the test
-- itself. Is supposed to be run in `before_each`.
local function child_call_once(func, ...)
if child_pid ~= 0 then
child_calls_mod_once[#child_calls_mod_once + 1] = {
func=func, args={...}}
else
func(...)
end
end
local child_cleanups_mod_once = nil
-- Run some code at the end of the child process, before exiting. Is supposed to
-- be run in `before_each` because `after_each` is run after child has exited.
local function child_cleanup_once(func, ...)
local child_cleanups = child_cleanups_mod_once
if child_pid ~= 0 then
child_cleanups[#child_cleanups + 1] = {func=func, args={...}}
else
func(...)
end
end
local libnvim = nil
local lib = setmetatable({}, {
__index = only_separate(function(_, idx)
return libnvim[idx]
end),
__newindex = child_call(function(_, idx, val)
libnvim[idx] = val
end),
})
local init = only_separate(function()
-- load neovim shared library
libnvim = ffi.load(Paths.test_libnvim_path)
for _, c in ipairs(child_calls_init) do
c.func(unpack(c.args))
end
libnvim.time_init()
libnvim.early_init()
libnvim.event_init()
if child_calls_mod then
for _, c in ipairs(child_calls_mod) do
c.func(unpack(c.args))
end
end
if child_calls_mod_once then
for _, c in ipairs(child_calls_mod_once) do
c.func(unpack(c.args))
end
child_calls_mod_once = nil
end
end)
local deinit = only_separate(function()
if child_cleanups_mod_once then
for _, c in ipairs(child_cleanups_mod_once) do
c.func(unpack(c.args))
end
child_cleanups_mod_once = nil
end
end)
local function trim(s)
return s:match('^%s*(.*%S)') or ''
end
-- a Set that keeps around the lines we've already seen
local cdefs = Set:new()
local cdefs_init = Set:new()
local cdefs_mod = nil
local imported = Set:new()
local pragma_pack_id = 1
@@ -51,84 +148,120 @@ local function filter_complex_blocks(body)
return table.concat(result, "\n")
end
local previous_defines = ''
local cdef = ffi.cdef
local cimportstr
local previous_defines_init = ''
local preprocess_cache_init = {}
local previous_defines_mod = ''
local preprocess_cache_mod = nil
local function is_child_cdefs()
return (os.getenv('NVIM_TEST_MAIN_CDEFS') ~= '1')
end
-- use this helper to import C files, you can pass multiple paths at once,
-- this helper will return the C namespace of the nvim library.
local function cimport(...)
local paths = {}
local args = {...}
-- filter out paths we've already imported
for _,path in pairs(args) do
if path ~= nil and not imported:contains(path) then
paths[#paths + 1] = path
cimport = function(...)
local previous_defines, preprocess_cache, cdefs
if is_child_cdefs() and preprocess_cache_mod then
preprocess_cache = preprocess_cache_mod
previous_defines = previous_defines_mod
cdefs = cdefs_mod
else
preprocess_cache = preprocess_cache_init
previous_defines = previous_defines_init
cdefs = cdefs_init
end
for _, path in ipairs({...}) do
if not (path:sub(1, 1) == '/' or path:sub(1, 1) == '.'
or path:sub(2, 2) == ':') then
path = './' .. path
end
end
if not preprocess_cache[path] then
local body
body, previous_defines = Preprocess.preprocess(previous_defines, path)
-- format it (so that the lines are "unique" statements), also filter out
-- Objective-C blocks
if os.getenv('NVIM_TEST_PRINT_I') == '1' then
local lnum = 0
for line in body:gmatch('[^\n]+') do
lnum = lnum + 1
print(lnum, line)
end
end
body = formatc(body)
body = filter_complex_blocks(body)
-- add the formatted lines to a set
local new_cdefs = Set:new()
for line in body:gmatch("[^\r\n]+") do
line = trim(line)
-- give each #pragma pack an unique id, so that they don't get removed
-- if they are inserted into the set
-- (they are needed in the right order with the struct definitions,
-- otherwise luajit has wrong memory layouts for the sturcts)
if line:match("#pragma%s+pack") then
line = line .. " // " .. pragma_pack_id
pragma_pack_id = pragma_pack_id + 1
end
new_cdefs:add(line)
end
for _,path in pairs(paths) do
imported:add(path)
end
-- subtract the lines we've already imported from the new lines, then add
-- the new unique lines to the old lines (so they won't be imported again)
new_cdefs:diff(cdefs)
cdefs:union(new_cdefs)
-- request a sorted version of the new lines (same relative order as the
-- original preprocessed file) and feed that to the LuaJIT ffi
local new_lines = new_cdefs:to_table()
if os.getenv('NVIM_TEST_PRINT_CDEF') == '1' then
for lnum, line in ipairs(new_lines) do
print(lnum, line)
end
end
body = table.concat(new_lines, '\n')
if #paths == 0 then
return libnvim
end
local body
body, previous_defines = Preprocess.preprocess(previous_defines, unpack(paths))
-- format it (so that the lines are "unique" statements), also filter out
-- Objective-C blocks
if os.getenv('NVIM_TEST_PRINT_I') == '1' then
local lnum = 0
for line in body:gmatch('[^\n]+') do
lnum = lnum + 1
print(lnum, line)
preprocess_cache[path] = body
end
cimportstr(preprocess_cache, path)
end
body = formatc(body)
body = filter_complex_blocks(body)
-- add the formatted lines to a set
local new_cdefs = Set:new()
for line in body:gmatch("[^\r\n]+") do
line = trim(line)
-- give each #pragma pack an unique id, so that they don't get removed
-- if they are inserted into the set
-- (they are needed in the right order with the struct definitions,
-- otherwise luajit has wrong memory layouts for the sturcts)
if line:match("#pragma%s+pack") then
line = line .. " // " .. pragma_pack_id
pragma_pack_id = pragma_pack_id + 1
end
new_cdefs:add(line)
end
-- subtract the lines we've already imported from the new lines, then add
-- the new unique lines to the old lines (so they won't be imported again)
new_cdefs:diff(cdefs)
cdefs:union(new_cdefs)
if new_cdefs:size() == 0 then
-- if there's no new lines, just return
return libnvim
end
-- request a sorted version of the new lines (same relative order as the
-- original preprocessed file) and feed that to the LuaJIT ffi
local new_lines = new_cdefs:to_table()
if os.getenv('NVIM_TEST_PRINT_CDEF') == '1' then
for lnum, line in ipairs(new_lines) do
print(lnum, line)
end
end
ffi.cdef(table.concat(new_lines, "\n"))
return libnvim
return lib
end
local function cppimport(path)
return cimport(Paths.test_include_path .. '/' .. path)
local cimport_immediate = function(...)
local saved_pid = child_pid
child_pid = 0
local err, emsg = pcall(cimport, ...)
child_pid = saved_pid
if not err then
emsg = tostring(emsg)
io.stderr:write(emsg .. '\n')
assert(false)
else
return lib
end
end
local function _cimportstr(preprocess_cache, path)
if imported:contains(path) then
return lib
end
local body = preprocess_cache[path]
if body == '' then
return lib
end
cdef(body)
imported:add(path)
return lib
end
if is_child_cdefs() then
cimportstr = child_call(_cimportstr, lib)
else
cimportstr = _cimportstr
end
local function alloc_log_new()
@@ -141,9 +274,12 @@ local function alloc_log_new()
local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'}
function log:save_original_functions()
for _, funcname in ipairs(allocator_functions) do
self.original_functions[funcname] = self.lib['mem_' .. funcname]
if not self.original_functions[funcname] then
self.original_functions[funcname] = self.lib['mem_' .. funcname]
end
end
end
log.save_original_functions = child_call(log.save_original_functions)
function log:set_mocks()
for _, k in ipairs(allocator_functions) do
do
@@ -170,6 +306,7 @@ local function alloc_log_new()
end
end
end
log.set_mocks = child_call(log.set_mocks)
function log:clear()
self.log = {}
end
@@ -178,22 +315,28 @@ local function alloc_log_new()
self:clear()
end
function log:restore_original_functions()
for k, v in pairs(self.original_functions) do
self.lib['mem_' .. k] = v
end
-- Do nothing: set mocks live in a separate process
return
--[[
[ for k, v in pairs(self.original_functions) do
[ self.lib['mem_' .. k] = v
[ end
]]
end
function log:before_each()
function log:setup()
log:save_original_functions()
log:set_mocks()
end
function log:before_each()
return
end
function log:after_each()
log:restore_original_functions()
end
log:setup()
return log
end
cimport('./src/nvim/types.h')
-- take a pointer to a C-allocated string and return an interned
-- version while also freeing the memory
local function internalize(cdata, len)
@@ -206,17 +349,226 @@ local function to_cstr(string)
return cstr(#string + 1, string)
end
-- initialize some global variables, this is still necessary to unit test
-- functions that rely on global state.
do
local main = cimport('./src/nvim/main.h')
local time = cimport('./src/nvim/os/time.h')
time.time_init()
main.early_init()
main.event_init()
local sc
if posix ~= nil then
sc = {
fork = posix.fork,
pipe = posix.pipe,
read = posix.read,
write = posix.write,
close = posix.close,
wait = posix.wait,
exit = posix._exit,
}
elseif syscall ~= nil then
sc = {
fork = syscall.fork,
pipe = function()
local ret = {syscall.pipe()}
return ret[3], ret[4]
end,
read = function(rd, len)
return rd:read(nil, len)
end,
write = function(wr, s)
return wr:write(s)
end,
close = function(p)
return p:close()
end,
wait = syscall.wait,
exit = syscall.exit,
}
else
cimport_immediate('./test/unit/fixtures/posix.h')
sc = {
fork = function()
return tonumber(ffi.C.fork())
end,
pipe = function()
local ret = ffi.new('int[2]', {-1, -1})
ffi.errno(0)
local res = ffi.C.pipe(ret)
if (res ~= 0) then
local err = ffi.errno(0)
assert(res == 0, ("pipe() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err))))
end
assert(ret[0] ~= -1 and ret[1] ~= -1)
return ret[0], ret[1]
end,
read = function(rd, len)
local ret = ffi.new('char[?]', len, {0})
local total_bytes_read = 0
ffi.errno(0)
while total_bytes_read < len do
local bytes_read = tonumber(ffi.C.read(
rd,
ffi.cast('void*', ret + total_bytes_read),
len - total_bytes_read))
if bytes_read == -1 then
local err = ffi.errno(0)
if err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("read() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err))))
end
elseif bytes_read == 0 then
break
else
total_bytes_read = total_bytes_read + bytes_read
end
end
return ffi.string(ret, total_bytes_read)
end,
write = function(wr, s)
local wbuf = to_cstr(s)
local total_bytes_written = 0
ffi.errno(0)
while total_bytes_written < #s do
local bytes_written = tonumber(ffi.C.write(
wr,
ffi.cast('void*', wbuf + total_bytes_written),
#s - total_bytes_written))
if bytes_written == -1 then
local err = ffi.errno(0)
if err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("write() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err))))
end
elseif bytes_written == 0 then
break
else
total_bytes_written = total_bytes_written + bytes_written
end
end
return total_bytes_written
end,
close = ffi.C.close,
wait = function(pid)
ffi.errno(0)
while true do
local r = ffi.C.waitpid(pid, nil, ffi.C.kPOSIXWaitWUNTRACED)
if r == -1 then
local err = ffi.errno(0)
if err == ffi.C.kPOSIXErrnoECHILD then
break
elseif err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("waitpid() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err))))
end
else
assert(r == pid)
end
end
end,
exit = ffi.C._exit,
}
end
return {
local function format_list(lst)
local ret = ''
for _, v in ipairs(lst) do
if ret ~= '' then ret = ret .. ', ' end
ret = ret .. assert:format({v, n=1})[1]
end
return ret
end
if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then
for k_, v_ in pairs(sc) do
(function(k, v)
sc[k] = function(...)
local rets = {v(...)}
io.stderr:write(('%s(%s) = %s\n'):format(k, format_list({...}),
format_list(rets)))
return unpack(rets)
end
end)(k_, v_)
end
end
local function gen_itp(it)
child_calls_mod = {}
child_calls_mod_once = {}
child_cleanups_mod_once = {}
preprocess_cache_mod = map(function(v) return v end, preprocess_cache_init)
previous_defines_mod = previous_defines_init
cdefs_mod = cdefs_init:copy()
local function just_fail(_)
return false
end
say:set('assertion.just_fail.positive', '%s')
say:set('assertion.just_fail.negative', '%s')
assert:register('assertion', 'just_fail', just_fail,
'assertion.just_fail.positive',
'assertion.just_fail.negative')
local function itp(name, func, allow_failure)
if allow_failure and os.getenv('NVIM_TEST_RUN_FAILING_TESTS') ~= '1' then
-- FIXME Fix tests with this true
return
end
it(name, function()
local rd, wr = sc.pipe()
child_pid = sc.fork()
if child_pid == 0 then
init()
sc.close(rd)
collectgarbage('stop')
local err, emsg = pcall(func)
collectgarbage('restart')
emsg = tostring(emsg)
if not err then
sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg))
deinit()
sc.close(wr)
sc.exit(1)
else
sc.write(wr, '+\n')
deinit()
sc.close(wr)
sc.exit(0)
end
else
sc.close(wr)
sc.wait(child_pid)
child_pid = nil
local function check()
local res = sc.read(rd, 2)
eq(2, #res)
if res == '+\n' then
return
end
eq('-\n', res)
local len_s = sc.read(rd, 5)
local len = tonumber(len_s)
neq(0, len)
local err = sc.read(rd, len + 1)
assert.just_fail(err)
end
local err, emsg = pcall(check)
sc.close(rd)
if not err then
if allow_failure then
io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n')
os.execute([[sh -c "source .ci/common/test.sh ; check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]])
else
error(emsg)
end
end
end
end)
end
return itp
end
local function cppimport(path)
return cimport(Paths.test_include_path .. '/' .. path)
end
cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h')
local module = {
cimport = cimport,
cppimport = cppimport,
internalize = internalize,
@@ -224,11 +576,23 @@ return {
eq = eq,
neq = neq,
ffi = ffi,
lib = libnvim,
lib = lib,
cstr = cstr,
to_cstr = to_cstr,
NULL = NULL,
OK = OK,
FAIL = FAIL,
alloc_log_new = alloc_log_new,
gen_itp = gen_itp,
only_separate = only_separate,
child_call_once = child_call_once,
child_cleanup_once = child_cleanup_once,
}
return function(after_each)
if after_each then
after_each(function()
check_cores(Paths.test_luajit_prg)
end)
end
return module
end

View File

@@ -1,4 +1,5 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local ffi = helpers.ffi
local eq = helpers.eq
@@ -26,7 +27,7 @@ describe('mbyte', function()
before_each(function()
end)
it('utf_ptr2char', function()
itp('utf_ptr2char', function()
-- For strings with length 1 the first byte is returned.
for c = 0, 255 do
eq(c, mbyte.utf_ptr2char(to_string({c, 0})))
@@ -44,7 +45,7 @@ describe('mbyte', function()
describe('utfc_ptr2char_len', function()
it('1-byte sequences', function()
itp('1-byte sequences', function()
local pcc = to_intp()
for c = 0, 255 do
eq(c, mbyte.utfc_ptr2char_len(to_string({c}), pcc, 1))
@@ -52,7 +53,7 @@ describe('mbyte', function()
end
end)
it('2-byte sequences', function()
itp('2-byte sequences', function()
local pcc = to_intp()
-- No combining characters
eq(0x007f, mbyte.utfc_ptr2char_len(to_string({0x7f, 0x7f}), pcc, 2))
@@ -76,7 +77,7 @@ describe('mbyte', function()
eq(0, pcc[0])
end)
it('3-byte sequences', function()
itp('3-byte sequences', function()
local pcc = to_intp()
-- No second UTF-8 character
@@ -108,7 +109,7 @@ describe('mbyte', function()
eq(0, pcc[0])
end)
it('4-byte sequences', function()
itp('4-byte sequences', function()
local pcc = to_intp()
-- No following combining character
@@ -145,7 +146,7 @@ describe('mbyte', function()
eq(0, pcc[0])
end)
it('5+-byte sequences', function()
itp('5+-byte sequences', function()
local pcc = to_intp()
-- No following combining character

View File

@@ -1,4 +1,5 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local cstr = helpers.cstr
@@ -26,7 +27,7 @@ describe('xstrlcat()', function()
return ffi.string(dst_cstr)
end
it('concatenates strings', function()
itp('concatenates strings', function()
eq('ab', test_xstrlcat('a', 'b', 3))
eq('ab', test_xstrlcat('a', 'b', 4096))
eq('ABCיהZdefgiיהZ', test_xstrlcat('ABCיהZ', 'defgiיהZ', 4096))
@@ -34,7 +35,7 @@ describe('xstrlcat()', function()
eq('a', test_xstrlcat('a', '', 4096))
end)
it('concatenates overlapping strings', function()
itp('concatenates overlapping strings', function()
eq('abcabc', test_xstrlcat_overlap('abc', 0, 7))
eq('abca', test_xstrlcat_overlap('abc', 0, 5))
eq('abcb', test_xstrlcat_overlap('abc', 1, 5))
@@ -42,7 +43,7 @@ describe('xstrlcat()', function()
eq('abcabc', test_xstrlcat_overlap('abc', 0, 2343))
end)
it('truncates if `dsize` is too small', function()
itp('truncates if `dsize` is too small', function()
eq('a', test_xstrlcat('a', 'b', 2))
eq('', test_xstrlcat('', 'b', 1))
eq('ABCיהZd', test_xstrlcat('ABCיהZ', 'defgiיהZ', 10))

View File

@@ -1,4 +1,5 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local ffi = helpers.ffi
local eq = helpers.eq
@@ -35,23 +36,23 @@ describe('trunc_string', function()
for _,t in ipairs(permutations) do
describe('populates buf '..t.desc, function()
it('with a small string', function()
itp('with a small string', function()
t.func('text', 'text')
end)
it('with a medium string', function()
itp('with a medium string', function()
t.func('a short text', 'a short text')
end)
it('with a string of length == 1/2 room', function()
itp('with a string of length == 1/2 room', function()
t.func('a text that fits', 'a text that fits', 34)
end)
it('with a string exactly the truncate size', function()
itp('with a string exactly the truncate size', function()
t.func('a text tha just fits', 'a text tha just fits')
end)
it('with a string that must be truncated', function()
itp('with a string that must be truncated', function()
t.func('a text that nott fits', 'a text t...nott fits')
end)
end)

View File

@@ -1,9 +1,12 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local ffi = helpers.ffi
local eq = helpers.eq
local child_call_once = helpers.child_call_once
local cimport = helpers.cimport
local ffi = helpers.ffi
local eq = helpers.eq
local multiqueue = helpers.cimport("./test/unit/fixtures/multiqueue.h")
local multiqueue = cimport("./test/unit/fixtures/multiqueue.h")
describe("multiqueue (multi-level event-queue)", function()
local parent, child1, child2, child3
@@ -21,28 +24,30 @@ describe("multiqueue (multi-level event-queue)", function()
end
before_each(function()
parent = multiqueue.multiqueue_new_parent(ffi.NULL, ffi.NULL)
child1 = multiqueue.multiqueue_new_child(parent)
child2 = multiqueue.multiqueue_new_child(parent)
child3 = multiqueue.multiqueue_new_child(parent)
put(child1, 'c1i1')
put(child1, 'c1i2')
put(child2, 'c2i1')
put(child1, 'c1i3')
put(child2, 'c2i2')
put(child2, 'c2i3')
put(child2, 'c2i4')
put(child3, 'c3i1')
put(child3, 'c3i2')
child_call_once(function()
parent = multiqueue.multiqueue_new_parent(ffi.NULL, ffi.NULL)
child1 = multiqueue.multiqueue_new_child(parent)
child2 = multiqueue.multiqueue_new_child(parent)
child3 = multiqueue.multiqueue_new_child(parent)
put(child1, 'c1i1')
put(child1, 'c1i2')
put(child2, 'c2i1')
put(child1, 'c1i3')
put(child2, 'c2i2')
put(child2, 'c2i3')
put(child2, 'c2i4')
put(child3, 'c3i1')
put(child3, 'c3i2')
end)
end)
it('keeps count of added events', function()
itp('keeps count of added events', function()
eq(3, multiqueue.multiqueue_size(child1))
eq(4, multiqueue.multiqueue_size(child2))
eq(2, multiqueue.multiqueue_size(child3))
end)
it('keeps count of removed events', function()
itp('keeps count of removed events', function()
multiqueue.multiqueue_get(child1)
eq(2, multiqueue.multiqueue_size(child1))
multiqueue.multiqueue_get(child1)
@@ -57,7 +62,7 @@ describe("multiqueue (multi-level event-queue)", function()
eq(0, multiqueue.multiqueue_size(child1))
end)
it('removing from parent removes from child', function()
itp('removing from parent removes from child', function()
eq('c1i1', get(parent))
eq('c1i2', get(parent))
eq('c2i1', get(parent))
@@ -67,7 +72,7 @@ describe("multiqueue (multi-level event-queue)", function()
eq('c2i4', get(parent))
end)
it('removing from child removes from parent', function()
itp('removing from child removes from parent', function()
eq('c2i1', get(child2))
eq('c2i2', get(child2))
eq('c1i1', get(child1))
@@ -77,13 +82,13 @@ describe("multiqueue (multi-level event-queue)", function()
eq('c2i4', get(parent))
end)
it('removing from child at the beginning of parent', function()
itp('removing from child at the beginning of parent', function()
eq('c1i1', get(child1))
eq('c1i2', get(child1))
eq('c2i1', get(parent))
end)
it('removing from parent after get from parent and put to child', function()
itp('removing from parent after get from parent and put to child', function()
eq('c1i1', get(parent))
eq('c1i2', get(parent))
eq('c2i1', get(parent))
@@ -99,7 +104,7 @@ describe("multiqueue (multi-level event-queue)", function()
eq('c1i22', get(parent))
end)
it('removing from parent after get and put to child', function()
itp('removing from parent after get and put to child', function()
eq('c1i1', get(child1))
eq('c1i2', get(child1))
eq('c2i1', get(child2))
@@ -117,7 +122,7 @@ describe("multiqueue (multi-level event-queue)", function()
eq('c1i12', get(parent))
end)
it('put after removing from child at the end of parent', function()
itp('put after removing from child at the end of parent', function()
eq('c3i1', get(child3))
eq('c3i2', get(child3))
put(child1, 'c1i11')
@@ -133,7 +138,7 @@ describe("multiqueue (multi-level event-queue)", function()
eq('c2i11', get(parent))
end)
it('removes from parent queue when child is freed', function()
itp('removes from parent queue when child is freed', function()
free(child2)
eq('c1i1', get(parent))
eq('c1i2', get(parent))

View File

@@ -1,4 +1,5 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local to_cstr = helpers.to_cstr
local eq = helpers.eq
@@ -12,23 +13,23 @@ end
describe('check_ff_value', function()
it('views empty string as valid', function()
itp('views empty string as valid', function()
eq(1, check_ff_value(""))
end)
it('views "unix", "dos" and "mac" as valid', function()
itp('views "unix", "dos" and "mac" as valid', function()
eq(1, check_ff_value("unix"))
eq(1, check_ff_value("dos"))
eq(1, check_ff_value("mac"))
end)
it('views "foo" as invalid', function()
itp('views "foo" as invalid', function()
eq(0, check_ff_value("foo"))
end)
end)
describe('get_sts_value', function()
it([[returns 'softtabstop' when it is non-negative]], function()
itp([[returns 'softtabstop' when it is non-negative]], function()
globals.curbuf.b_p_sts = 5
eq(5, option.get_sts_value())
@@ -36,7 +37,7 @@ describe('get_sts_value', function()
eq(0, option.get_sts_value())
end)
it([[returns "effective shiftwidth" when 'softtabstop' is negative]], function()
itp([[returns "effective shiftwidth" when 'softtabstop' is negative]], function()
local shiftwidth = 2
globals.curbuf.b_p_sw = shiftwidth
local tabstop = 5

View File

@@ -1,4 +1,5 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local eq = helpers.eq
@@ -33,7 +34,7 @@ describe('env function', function()
describe('os_setenv', function()
local OK = 0
it('sets an env variable and returns OK', function()
itp('sets an env variable and returns OK', function()
local name = 'NEOVIM_UNIT_TEST_SETENV_1N'
local value = 'NEOVIM_UNIT_TEST_SETENV_1V'
eq(nil, os.getenv(name))
@@ -41,7 +42,7 @@ describe('env function', function()
eq(value, os.getenv(name))
end)
it("dosn't overwrite an env variable if overwrite is 0", function()
itp("dosn't overwrite an env variable if overwrite is 0", function()
local name = 'NEOVIM_UNIT_TEST_SETENV_2N'
local value = 'NEOVIM_UNIT_TEST_SETENV_2V'
local value_updated = 'NEOVIM_UNIT_TEST_SETENV_2V_UPDATED'
@@ -53,13 +54,13 @@ describe('env function', function()
end)
describe('os_setenv_append_path', function()
it('appends /foo/bar to $PATH', function()
itp('appends /foo/bar to $PATH', function()
local original_path = os.getenv('PATH')
eq(true, cimp.os_setenv_append_path(to_cstr('/foo/bar/baz')))
eq(original_path..':/foo/bar', os.getenv('PATH'))
end)
it('returns false if `fname` is not absolute', function()
itp('returns false if `fname` is not absolute', function()
local original_path = os.getenv('PATH')
eq(false, cimp.os_setenv_append_path(to_cstr('foo/bar/baz')))
eq(original_path, os.getenv('PATH'))
@@ -67,7 +68,7 @@ describe('env function', function()
end)
describe('os_getenv', function()
it('reads an env variable', function()
itp('reads an env variable', function()
local name = 'NEOVIM_UNIT_TEST_GETENV_1N'
local value = 'NEOVIM_UNIT_TEST_GETENV_1V'
eq(NULL, os_getenv(name))
@@ -76,14 +77,14 @@ describe('env function', function()
eq(value, os_getenv(name))
end)
it('returns NULL if the env variable is not found', function()
itp('returns NULL if the env variable is not found', function()
local name = 'NEOVIM_UNIT_TEST_GETENV_NOTFOUND'
return eq(NULL, os_getenv(name))
end)
end)
describe('os_unsetenv', function()
it('unsets environment variable', function()
itp('unsets environment variable', function()
local name = 'TEST_UNSETENV'
local value = 'TESTVALUE'
os_setenv(name, value, 1)
@@ -95,7 +96,7 @@ describe('env function', function()
end)
describe('os_getenvname_at_index', function()
it('returns names of environment variables', function()
itp('returns names of environment variables', function()
local test_name = 'NEOVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1N'
local test_value = 'NEOVIM_UNIT_TEST_GETENVNAME_AT_INDEX_1V'
os_setenv(test_name, test_value, 1)
@@ -115,7 +116,7 @@ describe('env function', function()
eq(true, found_name)
end)
it('returns NULL if the index is out of bounds', function()
itp('returns NULL if the index is out of bounds', function()
local huge = ffi.new('size_t', 10000)
local maxuint32 = ffi.new('size_t', 4294967295)
eq(NULL, cimp.os_getenvname_at_index(huge))
@@ -132,7 +133,7 @@ describe('env function', function()
end)
describe('os_get_pid', function()
it('returns the process ID', function()
itp('returns the process ID', function()
local stat_file = io.open('/proc/self/stat')
if stat_file then
local stat_str = stat_file:read('*l')
@@ -147,7 +148,7 @@ describe('env function', function()
end)
describe('os_get_hostname', function()
it('returns the hostname', function()
itp('returns the hostname', function()
local handle = io.popen('hostname')
local hostname = handle:read('*l')
handle:close()
@@ -158,7 +159,7 @@ describe('env function', function()
end)
describe('expand_env_esc', function()
it('expands environment variables', function()
itp('expands environment variables', function()
local name = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCN'
local value = 'NEOVIM_UNIT_TEST_EXPAND_ENV_ESCV'
os_setenv(name, value, 1)
@@ -175,7 +176,7 @@ describe('env function', function()
eq(output_expected, ffi.string(output_buff2))
end)
it('expands ~ once when `one` is true', function()
itp('expands ~ once when `one` is true', function()
local input = '~/foo ~ foo'
local homedir = cstr(255, '')
cimp.expand_env_esc(to_cstr('~'), homedir, 255, false, true, NULL)
@@ -185,7 +186,7 @@ describe('env function', function()
eq(ffi.string(output), ffi.string(output_expected))
end)
it('expands ~ every time when `one` is false', function()
itp('expands ~ every time when `one` is false', function()
local input = to_cstr('~/foo ~ foo')
local dst = cstr(255, '')
cimp.expand_env_esc(to_cstr('~'), dst, 255, false, true, NULL)
@@ -196,7 +197,7 @@ describe('env function', function()
eq(output_expected, ffi.string(output))
end)
it('does not crash #3725', function()
itp('does not crash #3725', function()
local name_out = ffi.new('char[100]')
cimp.os_get_user_name(name_out, 100)
local curuser = ffi.string(name_out)
@@ -209,7 +210,7 @@ describe('env function', function()
assert.True(len < 99)
end)
it('respects `dstlen` without expansion', function()
itp('respects `dstlen` without expansion', function()
local input = to_cstr('this is a very long thing that will not fit')
-- The buffer is long enough to actually contain the full input in case the
-- test fails, but we don't tell expand_env_esc that
@@ -223,7 +224,7 @@ describe('env function', function()
eq(0, output[4])
end)
it('respects `dstlen` with expansion', function()
itp('respects `dstlen` with expansion', function()
local varname = to_cstr('NVIM_UNIT_TEST_EXPAND_ENV_ESC_DSTLENN')
local varval = to_cstr('NVIM_UNIT_TEST_EXPAND_ENV_ESC_DSTLENV')
cimp.os_setenv(varname, varval, 1)

View File

@@ -1,6 +1,7 @@
local lfs = require('lfs')
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local eq = helpers.eq
local ffi = helpers.ffi
@@ -88,7 +89,7 @@ local function file_skip(fp, size)
end
describe('file_open', function()
it('can create a rwx------ file with kFileCreate', function()
itp('can create a rwx------ file with kFileCreate', function()
local err, fp = file_open(filec, m.kFileCreate, 448)
eq(0, err)
local attrs = lfs.attributes(filec)
@@ -96,7 +97,7 @@ describe('file_open', function()
eq(0, m.file_close(fp))
end)
it('can create a rw------- file with kFileCreate', function()
itp('can create a rw------- file with kFileCreate', function()
local err, fp = file_open(filec, m.kFileCreate, 384)
eq(0, err)
local attrs = lfs.attributes(filec)
@@ -104,7 +105,7 @@ describe('file_open', function()
eq(0, m.file_close(fp))
end)
it('can create a rwx------ file with kFileCreateOnly', function()
itp('can create a rwx------ file with kFileCreateOnly', function()
local err, fp = file_open(filec, m.kFileCreateOnly, 448)
eq(0, err)
local attrs = lfs.attributes(filec)
@@ -112,7 +113,7 @@ describe('file_open', function()
eq(0, m.file_close(fp))
end)
it('can create a rw------- file with kFileCreateOnly', function()
itp('can create a rw------- file with kFileCreateOnly', function()
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
eq(0, err)
local attrs = lfs.attributes(filec)
@@ -120,47 +121,47 @@ describe('file_open', function()
eq(0, m.file_close(fp))
end)
it('fails to open an existing file with kFileCreateOnly', function()
itp('fails to open an existing file with kFileCreateOnly', function()
local err, _ = file_open(file1, m.kFileCreateOnly, 384)
eq(m.UV_EEXIST, err)
end)
it('fails to open an symlink with kFileNoSymlink', function()
itp('fails to open an symlink with kFileNoSymlink', function()
local err, _ = file_open(linkf, m.kFileNoSymlink, 384)
-- err is UV_EMLINK in FreeBSD, but if I use `ok(err == m.UV_ELOOP or err ==
-- m.UV_EMLINK)`, then I loose the ability to see actual `err` value.
if err ~= m.UV_ELOOP then eq(m.UV_EMLINK, err) end
end)
it('can open an existing file write-only with kFileCreate', function()
itp('can open an existing file write-only with kFileCreate', function()
local err, fp = file_open(file1, m.kFileCreate, 384)
eq(0, err)
eq(true, fp.wr)
eq(0, m.file_close(fp))
end)
it('can open an existing file read-only with zero', function()
itp('can open an existing file read-only with zero', function()
local err, fp = file_open(file1, 0, 384)
eq(0, err)
eq(false, fp.wr)
eq(0, m.file_close(fp))
end)
it('can open an existing file read-only with kFileReadOnly', function()
itp('can open an existing file read-only with kFileReadOnly', function()
local err, fp = file_open(file1, m.kFileReadOnly, 384)
eq(0, err)
eq(false, fp.wr)
eq(0, m.file_close(fp))
end)
it('can open an existing file read-only with kFileNoSymlink', function()
itp('can open an existing file read-only with kFileNoSymlink', function()
local err, fp = file_open(file1, m.kFileNoSymlink, 384)
eq(0, err)
eq(false, fp.wr)
eq(0, m.file_close(fp))
end)
it('can truncate an existing file with kFileTruncate', function()
itp('can truncate an existing file with kFileTruncate', function()
local err, fp = file_open(file1, m.kFileTruncate, 384)
eq(0, err)
eq(true, fp.wr)
@@ -169,7 +170,7 @@ describe('file_open', function()
eq(0, attrs.size)
end)
it('can open an existing file write-only with kFileWriteOnly', function()
itp('can open an existing file write-only with kFileWriteOnly', function()
local err, fp = file_open(file1, m.kFileWriteOnly, 384)
eq(0, err)
eq(true, fp.wr)
@@ -178,14 +179,14 @@ describe('file_open', function()
eq(4096, attrs.size)
end)
it('fails to create a file with just kFileWriteOnly', function()
itp('fails to create a file with just kFileWriteOnly', function()
local err, _ = file_open(filec, m.kFileWriteOnly, 384)
eq(m.UV_ENOENT, err)
local attrs = lfs.attributes(filec)
eq(nil, attrs)
end)
it('can truncate an existing file with kFileTruncate when opening a symlink',
itp('can truncate an existing file with kFileTruncate when opening a symlink',
function()
local err, fp = file_open(linkf, m.kFileTruncate, 384)
eq(0, err)
@@ -195,31 +196,31 @@ describe('file_open', function()
eq(0, attrs.size)
end)
it('fails to open a directory write-only', function()
itp('fails to open a directory write-only', function()
local err, _ = file_open(dir, m.kFileWriteOnly, 384)
eq(m.UV_EISDIR, err)
end)
it('fails to open a broken symbolic link write-only', function()
itp('fails to open a broken symbolic link write-only', function()
local err, _ = file_open(linkb, m.kFileWriteOnly, 384)
eq(m.UV_ENOENT, err)
end)
it('fails to open a broken symbolic link read-only', function()
itp('fails to open a broken symbolic link read-only', function()
local err, _ = file_open(linkb, m.kFileReadOnly, 384)
eq(m.UV_ENOENT, err)
end)
end)
describe('file_open_new', function()
it('can open a file read-only', function()
itp('can open a file read-only', function()
local err, fp = file_open_new(file1, 0, 384)
eq(0, err)
eq(false, fp.wr)
eq(0, m.file_free(fp))
end)
it('fails to open an existing file with kFileCreateOnly', function()
itp('fails to open an existing file with kFileCreateOnly', function()
local err, fp = file_open_new(file1, m.kFileCreateOnly, 384)
eq(m.UV_EEXIST, err)
eq(nil, fp)
@@ -229,7 +230,7 @@ end)
-- file_close is called above, so it is not tested directly
describe('file_fsync', function()
it('can flush writes to disk', function()
itp('can flush writes to disk', function()
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
eq(0, file_fsync(fp))
eq(0, err)
@@ -244,7 +245,7 @@ describe('file_fsync', function()
end)
describe('file_read', function()
it('can read small chunks of input until eof', function()
itp('can read small chunks of input until eof', function()
local err, fp = file_open(file1, 0, 384)
eq(0, err)
eq(false, fp.wr)
@@ -264,7 +265,7 @@ describe('file_read', function()
eq(0, m.file_close(fp))
end)
it('can read the whole file at once', function()
itp('can read the whole file at once', function()
local err, fp = file_open(file1, 0, 384)
eq(0, err)
eq(false, fp.wr)
@@ -273,7 +274,7 @@ describe('file_read', function()
eq(0, m.file_close(fp))
end)
it('can read more then 1024 bytes after reading a small chunk', function()
itp('can read more then 1024 bytes after reading a small chunk', function()
local err, fp = file_open(file1, 0, 384)
eq(0, err)
eq(false, fp.wr)
@@ -283,7 +284,7 @@ describe('file_read', function()
eq(0, m.file_close(fp))
end)
it('can read file by 768-byte-chunks', function()
itp('can read file by 768-byte-chunks', function()
local err, fp = file_open(file1, 0, 384)
eq(0, err)
eq(false, fp.wr)
@@ -305,7 +306,7 @@ describe('file_read', function()
end)
describe('file_write', function()
it('can write the whole file at once', function()
itp('can write the whole file at once', function()
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
eq(0, err)
eq(true, fp.wr)
@@ -316,7 +317,7 @@ describe('file_write', function()
eq(fcontents, io.open(filec):read('*a'))
end)
it('can write the whole file by small chunks', function()
itp('can write the whole file by small chunks', function()
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
eq(0, err)
eq(true, fp.wr)
@@ -333,7 +334,7 @@ describe('file_write', function()
eq(fcontents, io.open(filec):read('*a'))
end)
it('can write the whole file by 768-byte-chunks', function()
itp('can write the whole file by 768-byte-chunks', function()
local err, fp = file_open(filec, m.kFileCreateOnly, 384)
eq(0, err)
eq(true, fp.wr)
@@ -352,7 +353,7 @@ describe('file_write', function()
end)
describe('file_skip', function()
it('can skip 3 bytes', function()
itp('can skip 3 bytes', function()
local err, fp = file_open(file1, 0, 384)
eq(0, err)
eq(false, fp.wr)

View File

@@ -1,7 +1,8 @@
local lfs = require('lfs')
local bit = require('bit')
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local cppimport = helpers.cppimport
@@ -15,10 +16,10 @@ local to_cstr = helpers.to_cstr
local OK = helpers.OK
local FAIL = helpers.FAIL
local NULL = helpers.NULL
local NODE_NORMAL = 0
local NODE_WRITABLE = 1
cimport('unistd.h')
cimport('./src/nvim/os/shell.h')
cimport('./src/nvim/option_defs.h')
cimport('./src/nvim/main.h')
@@ -65,13 +66,10 @@ local function os_getperm(filename)
end
describe('fs function', function()
local orig_test_file_perm
setup(function()
before_each(function()
lfs.mkdir('unit-test-directory');
io.open('unit-test-directory/test.file', 'w').close()
orig_test_file_perm = os_getperm('unit-test-directory/test.file')
io.open('unit-test-directory/test_2.file', 'w').close()
lfs.link('test.file', 'unit-test-directory/test_link.file', true)
@@ -83,7 +81,7 @@ describe('fs function', function()
directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$')
end)
teardown(function()
after_each(function()
os.remove('unit-test-directory/test.file')
os.remove('unit-test-directory/test_2.file')
os.remove('unit-test-directory/test_link.file')
@@ -104,13 +102,13 @@ describe('fs function', function()
buffer = cstr(length, '')
end)
it('returns OK and writes current directory into the buffer if it is large\n enough', function()
itp('returns OK and writes current directory into the buffer if it is large\n enough', function()
eq(OK, (os_dirname(buffer, length)))
eq(lfs.currentdir(), (ffi.string(buffer)))
end)
-- What kind of other failing cases are possible?
it('returns FAIL if the buffer is too small', function()
itp('returns FAIL if the buffer is too small', function()
local buf = cstr((length - 1), '')
eq(FAIL, (os_dirname(buf, (length - 1))))
end)
@@ -121,35 +119,35 @@ describe('fs function', function()
end
describe('os_isdir', function()
it('returns false if an empty string is given', function()
itp('returns false if an empty string is given', function()
eq(false, (os_isdir('')))
end)
it('returns false if a nonexisting directory is given', function()
itp('returns false if a nonexisting directory is given', function()
eq(false, (os_isdir('non-existing-directory')))
end)
it('returns false if a nonexisting absolute directory is given', function()
itp('returns false if a nonexisting absolute directory is given', function()
eq(false, (os_isdir('/non-existing-directory')))
end)
it('returns false if an existing file is given', function()
itp('returns false if an existing file is given', function()
eq(false, (os_isdir('unit-test-directory/test.file')))
end)
it('returns true if the current directory is given', function()
itp('returns true if the current directory is given', function()
eq(true, (os_isdir('.')))
end)
it('returns true if the parent directory is given', function()
itp('returns true if the parent directory is given', function()
eq(true, (os_isdir('..')))
end)
it('returns true if an arbitrary directory is given', function()
itp('returns true if an arbitrary directory is given', function()
eq(true, (os_isdir('unit-test-directory')))
end)
it('returns true if an absolute directory is given', function()
itp('returns true if an absolute directory is given', function()
eq(true, (os_isdir(directory)))
end)
end)
@@ -179,24 +177,24 @@ describe('fs function', function()
return os_can_exe(name)
end
it('returns false when given a directory', function()
itp('returns false when given a directory', function()
cant_exe('./unit-test-directory')
end)
it('returns false when given a regular file without executable bit set', function()
itp('returns false when given a regular file without executable bit set', function()
cant_exe('unit-test-directory/test.file')
end)
it('returns false when the given file does not exists', function()
itp('returns false when the given file does not exists', function()
cant_exe('does-not-exist.file')
end)
it('returns the absolute path when given an executable inside $PATH', function()
itp('returns the absolute path when given an executable inside $PATH', function()
local fullpath = exe('ls')
eq(1, fs.path_is_absolute_path(to_cstr(fullpath)))
end)
it('returns the absolute path when given an executable relative to the current dir', function()
itp('returns the absolute path when given an executable relative to the current dir', function()
local old_dir = lfs.currentdir()
lfs.chdir(directory)
@@ -216,10 +214,6 @@ describe('fs function', function()
end)
describe('file permissions', function()
before_each(function()
os_setperm('unit-test-directory/test.file', orig_test_file_perm)
end)
local function os_fchown(filename, user_id, group_id)
local fd = ffi.C.open(filename, 0)
local res = fs.os_fchown(fd, user_id, group_id)
@@ -240,22 +234,22 @@ describe('fs function', function()
end
describe('os_getperm', function()
it('returns UV_ENOENT when the given file does not exist', function()
itp('returns UV_ENOENT when the given file does not exist', function()
eq(ffi.C.UV_ENOENT, (os_getperm('non-existing-file')))
end)
it('returns a perm > 0 when given an existing file', function()
itp('returns a perm > 0 when given an existing file', function()
assert.is_true((os_getperm('unit-test-directory')) > 0)
end)
it('returns S_IRUSR when the file is readable', function()
itp('returns S_IRUSR when the file is readable', function()
local perm = os_getperm('unit-test-directory')
assert.is_true((bit_set(perm, ffi.C.kS_IRUSR)))
end)
end)
describe('os_setperm', function()
it('can set and unset the executable bit of a file', function()
itp('can set and unset the executable bit of a file', function()
local perm = os_getperm('unit-test-directory/test.file')
perm = unset_bit(perm, ffi.C.kS_IXUSR)
eq(OK, (os_setperm('unit-test-directory/test.file', perm)))
@@ -267,7 +261,7 @@ describe('fs function', function()
assert.is_true((bit_set(perm, ffi.C.kS_IXUSR)))
end)
it('fails if given file does not exist', function()
itp('fails if given file does not exist', function()
local perm = ffi.C.kS_IXUSR
eq(FAIL, (os_setperm('non-existing-file', perm)))
end)
@@ -275,7 +269,7 @@ describe('fs function', function()
describe('os_fchown', function()
local filename = 'unit-test-directory/test.file'
it('does not change owner and group if respective IDs are equal to -1', function()
itp('does not change owner and group if respective IDs are equal to -1', function()
local uid = lfs.attributes(filename, 'uid')
local gid = lfs.attributes(filename, 'gid')
eq(0, os_fchown(filename, -1, -1))
@@ -287,7 +281,7 @@ describe('fs function', function()
if (os.execute('id -G > /dev/null 2>&1') ~= 0) then
pending('skipped (missing `id` utility)', function() end)
else
it('owner of a file may change the group of the file to any group of which that owner is a member', function()
itp('owner of a file may change the group of the file to any group of which that owner is a member', function()
local file_gid = lfs.attributes(filename, 'gid')
-- Gets ID of any group of which current user is a member except the
@@ -311,7 +305,7 @@ describe('fs function', function()
if (ffi.os == 'Windows' or ffi.C.geteuid() == 0) then
pending('skipped (uv_fs_chown is no-op on Windows)', function() end)
else
it('returns nonzero if process has not enough permissions', function()
itp('returns nonzero if process has not enough permissions', function()
-- chown to root
neq(0, os_fchown(filename, 0, 0))
end)
@@ -320,7 +314,7 @@ describe('fs function', function()
describe('os_file_is_readable', function()
it('returns false if the file is not readable', function()
itp('returns false if the file is not readable', function()
local perm = os_getperm('unit-test-directory/test.file')
perm = unset_bit(perm, ffi.C.kS_IRUSR)
perm = unset_bit(perm, ffi.C.kS_IRGRP)
@@ -329,19 +323,19 @@ describe('fs function', function()
eq(false, os_file_is_readable('unit-test-directory/test.file'))
end)
it('returns false if the file does not exist', function()
itp('returns false if the file does not exist', function()
eq(false, os_file_is_readable(
'unit-test-directory/what_are_you_smoking.gif'))
end)
it('returns true if the file is readable', function()
itp('returns true if the file is readable', function()
eq(true, os_file_is_readable(
'unit-test-directory/test.file'))
end)
end)
describe('os_file_is_writable', function()
it('returns 0 if the file is readonly', function()
itp('returns 0 if the file is readonly', function()
local perm = os_getperm('unit-test-directory/test.file')
perm = unset_bit(perm, ffi.C.kS_IWUSR)
perm = unset_bit(perm, ffi.C.kS_IWGRP)
@@ -350,11 +344,11 @@ describe('fs function', function()
eq(0, os_file_is_writable('unit-test-directory/test.file'))
end)
it('returns 1 if the file is writable', function()
itp('returns 1 if the file is writable', function()
eq(1, os_file_is_writable('unit-test-directory/test.file'))
end)
it('returns 2 when given a folder with rights to write into', function()
itp('returns 2 when given a folder with rights to write into', function()
eq(2, os_file_is_writable('unit-test-directory'))
end)
end)
@@ -420,19 +414,19 @@ describe('fs function', function()
end
describe('os_path_exists', function()
it('returns false when given a non-existing file', function()
itp('returns false when given a non-existing file', function()
eq(false, (os_path_exists('non-existing-file')))
end)
it('returns true when given an existing file', function()
itp('returns true when given an existing file', function()
eq(true, (os_path_exists('unit-test-directory/test.file')))
end)
it('returns false when given a broken symlink', function()
itp('returns false when given a broken symlink', function()
eq(false, (os_path_exists('unit-test-directory/test_broken_link.file')))
end)
it('returns true when given a directory', function()
itp('returns true when given a directory', function()
eq(true, (os_path_exists('unit-test-directory')))
end)
end)
@@ -441,18 +435,18 @@ describe('fs function', function()
local test = 'unit-test-directory/test.file'
local not_exist = 'unit-test-directory/not_exist.file'
it('can rename file if destination file does not exist', function()
itp('can rename file if destination file does not exist', function()
eq(OK, (os_rename(test, not_exist)))
eq(false, (os_path_exists(test)))
eq(true, (os_path_exists(not_exist)))
eq(OK, (os_rename(not_exist, test))) -- restore test file
end)
it('fail if source file does not exist', function()
itp('fail if source file does not exist', function()
eq(FAIL, (os_rename(not_exist, test)))
end)
it('can overwrite destination file if it exists', function()
itp('can overwrite destination file if it exists', function()
local other = 'unit-test-directory/other.file'
local file = io.open(other, 'w')
file:write('other')
@@ -477,11 +471,11 @@ describe('fs function', function()
os.remove('unit-test-directory/test_remove.file')
end)
it('returns non-zero when given a non-existing file', function()
itp('returns non-zero when given a non-existing file', function()
neq(0, (os_remove('non-existing-file')))
end)
it('removes the given file and returns 0', function()
itp('removes the given file and returns 0', function()
local f = 'unit-test-directory/test_remove.file'
assert_file_exists(f)
eq(0, (os_remove(f)))
@@ -502,30 +496,30 @@ describe('fs function', function()
os.remove(new_file)
end)
it('returns UV_ENOENT for O_RDWR on a non-existing file', function()
itp('returns UV_ENOENT for O_RDWR on a non-existing file', function()
eq(ffi.C.UV_ENOENT, (os_open('non-existing-file', ffi.C.kO_RDWR, 0)))
end)
it('returns non-negative for O_CREAT on a non-existing file which then can be closed', function()
itp('returns non-negative for O_CREAT on a non-existing file which then can be closed', function()
assert_file_does_not_exist(new_file)
local fd = os_open(new_file, ffi.C.kO_CREAT, 0)
assert.is_true(0 <= fd)
eq(0, os_close(fd))
end)
it('returns non-negative for O_CREAT on a existing file which then can be closed', function()
itp('returns non-negative for O_CREAT on a existing file which then can be closed', function()
assert_file_exists(existing_file)
local fd = os_open(existing_file, ffi.C.kO_CREAT, 0)
assert.is_true(0 <= fd)
eq(0, os_close(fd))
end)
it('returns UV_EEXIST for O_CREAT|O_EXCL on a existing file', function()
itp('returns UV_EEXIST for O_CREAT|O_EXCL on a existing file', function()
assert_file_exists(existing_file)
eq(ffi.C.kUV_EEXIST, (os_open(existing_file, (bit.bor(ffi.C.kO_CREAT, ffi.C.kO_EXCL)), 0)))
end)
it('sets `rwx` permissions for O_CREAT 700 which then can be closed', function()
itp('sets `rwx` permissions for O_CREAT 700 which then can be closed', function()
assert_file_does_not_exist(new_file)
--create the file
local fd = os_open(new_file, ffi.C.kO_CREAT, tonumber("700", 8))
@@ -534,7 +528,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('sets `rw` permissions for O_CREAT 600 which then can be closed', function()
itp('sets `rw` permissions for O_CREAT 600 which then can be closed', function()
assert_file_does_not_exist(new_file)
--create the file
local fd = os_open(new_file, ffi.C.kO_CREAT, tonumber("600", 8))
@@ -543,7 +537,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('returns a non-negative file descriptor for an existing file which then can be closed', function()
itp('returns a non-negative file descriptor for an existing file which then can be closed', function()
local fd = os_open(existing_file, ffi.C.kO_RDWR, 0)
assert.is_true(0 <= fd)
eq(0, os_close(fd))
@@ -551,7 +545,7 @@ describe('fs function', function()
end)
describe('os_close', function()
it('returns EBADF for negative file descriptors', function()
itp('returns EBADF for negative file descriptors', function()
eq(ffi.C.UV_EBADF, os_close(-1))
eq(ffi.C.UV_EBADF, os_close(-1000))
end)
@@ -570,7 +564,7 @@ describe('fs function', function()
os.remove(file)
end)
it('can read zero bytes from a file', function()
itp('can read zero bytes from a file', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0)
eq({false, 0, ''}, {os_read(fd, nil)})
@@ -578,7 +572,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('can read from a file multiple times', function()
itp('can read from a file multiple times', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0)
eq({false, 2, '\000\001'}, {os_read(fd, 2)})
@@ -586,7 +580,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('can read the whole file at once and then report eof', function()
itp('can read the whole file at once and then report eof', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0)
eq({false, #fcontents, fcontents}, {os_read(fd, #fcontents)})
@@ -594,7 +588,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('can read the whole file in two calls, one partially', function()
itp('can read the whole file in two calls, one partially', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0)
eq({false, #fcontents * 3/4, fcontents:sub(1, #fcontents * 3/4)},
@@ -624,7 +618,7 @@ describe('fs function', function()
os.remove(file)
end)
it('can read zero bytes from a file', function()
itp('can read zero bytes from a file', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0)
eq({false, 0, {}}, {os_readv(fd, {})})
@@ -632,7 +626,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('can read from a file multiple times to a differently-sized buffers', function()
itp('can read from a file multiple times to a differently-sized buffers', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0)
eq({false, 2, {'\000\001'}}, {os_readv(fd, {2})})
@@ -640,7 +634,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('can read the whole file at once and then report eof', function()
itp('can read the whole file at once and then report eof', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0)
eq({false,
@@ -657,7 +651,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('can read the whole file in two calls, one partially', function()
itp('can read the whole file in two calls, one partially', function()
local fd = os_open(file, ffi.C.kO_RDONLY, 0)
ok(fd >= 0)
eq({false, #fcontents * 3/4, {fcontents:sub(1, #fcontents * 3/4)}},
@@ -684,7 +678,7 @@ describe('fs function', function()
os.remove(file)
end)
it('can write zero bytes to a file', function()
itp('can write zero bytes to a file', function()
local fd = os_open(file, ffi.C.kO_WRONLY, 0)
ok(fd >= 0)
eq(0, os_write(fd, ''))
@@ -693,7 +687,7 @@ describe('fs function', function()
eq(0, os_close(fd))
end)
it('can write some data to a file', function()
itp('can write some data to a file', function()
local fd = os_open(file, ffi.C.kO_WRONLY, 0)
ok(fd >= 0)
eq(3, os_write(fd, 'abc'))
@@ -708,11 +702,11 @@ describe('fs function', function()
os.remove('non-existing-file')
end)
it('returns NODE_NORMAL for non-existing file', function()
itp('returns NODE_NORMAL for non-existing file', function()
eq(NODE_NORMAL, fs.os_nodetype(to_cstr('non-existing-file')))
end)
it('returns NODE_WRITABLE for /dev/stderr', function()
itp('returns NODE_WRITABLE for /dev/stderr', function()
eq(NODE_WRITABLE, fs.os_nodetype(to_cstr('/dev/stderr')))
end)
end)
@@ -738,12 +732,12 @@ describe('fs function', function()
end
describe('os_mkdir', function()
it('returns non-zero when given an already existing directory', function()
itp('returns non-zero when given an already existing directory', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
neq(0, (os_mkdir('unit-test-directory', mode)))
end)
it('creates a directory and returns 0', function()
itp('creates a directory and returns 0', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
eq(false, (os_isdir('unit-test-directory/new-dir')))
eq(0, (os_mkdir('unit-test-directory/new-dir', mode)))
@@ -753,14 +747,14 @@ describe('fs function', function()
end)
describe('os_mkdir_recurse', function()
it('returns zero when given an already existing directory', function()
itp('returns zero when given an already existing directory', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_str = os_mkdir_recurse('unit-test-directory', mode)
eq(0, ret)
eq(nil, failed_str)
end)
it('fails to create a directory where there is a file', function()
itp('fails to create a directory where there is a file', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_str = os_mkdir_recurse(
'unit-test-directory/test.file', mode)
@@ -768,7 +762,7 @@ describe('fs function', function()
eq('unit-test-directory/test.file', failed_str)
end)
it('fails to create a directory where there is a file in path', function()
itp('fails to create a directory where there is a file in path', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_str = os_mkdir_recurse(
'unit-test-directory/test.file/test', mode)
@@ -776,7 +770,7 @@ describe('fs function', function()
eq('unit-test-directory/test.file', failed_str)
end)
it('succeeds to create a directory', function()
itp('succeeds to create a directory', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_str = os_mkdir_recurse(
'unit-test-directory/new-dir-recurse', mode)
@@ -787,7 +781,7 @@ describe('fs function', function()
eq(false, os_isdir('unit-test-directory/new-dir-recurse'))
end)
it('succeeds to create a directory ending with ///', function()
itp('succeeds to create a directory ending with ///', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_str = os_mkdir_recurse(
'unit-test-directory/new-dir-recurse///', mode)
@@ -798,7 +792,7 @@ describe('fs function', function()
eq(false, os_isdir('unit-test-directory/new-dir-recurse'))
end)
it('succeeds to create a directory ending with /', function()
itp('succeeds to create a directory ending with /', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_str = os_mkdir_recurse(
'unit-test-directory/new-dir-recurse/', mode)
@@ -809,7 +803,7 @@ describe('fs function', function()
eq(false, os_isdir('unit-test-directory/new-dir-recurse'))
end)
it('succeeds to create a directory tree', function()
itp('succeeds to create a directory tree', function()
local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR
local ret, failed_str = os_mkdir_recurse(
'unit-test-directory/new-dir-recurse/1/2/3', mode)
@@ -828,11 +822,11 @@ describe('fs function', function()
end)
describe('os_rmdir', function()
it('returns non_zero when given a non-existing directory', function()
itp('returns non_zero when given a non-existing directory', function()
neq(0, (os_rmdir('non-existing-directory')))
end)
it('removes the given directory and returns 0', function()
itp('removes the given directory and returns 0', function()
lfs.mkdir('unit-test-directory/new-dir')
eq(0, os_rmdir('unit-test-directory/new-dir'))
eq(false, (os_isdir('unit-test-directory/new-dir')))
@@ -860,19 +854,19 @@ describe('fs function', function()
end
describe('os_fileinfo', function()
it('returns false if given a non-existing file', function()
itp('returns false if given a non-existing file', function()
local file_info = file_info_new()
assert.is_false((fs.os_fileinfo('/non-existent', file_info)))
end)
it('returns true if given an existing file and fills file_info', function()
itp('returns true if given an existing file and fills file_info', function()
local file_info = file_info_new()
local path = 'unit-test-directory/test.file'
assert.is_true((fs.os_fileinfo(path, file_info)))
assert.is_true((is_file_info_filled(file_info)))
end)
it('returns the file info of the linked file, not the link', function()
itp('returns the file info of the linked file, not the link', function()
local file_info = file_info_new()
local path = 'unit-test-directory/test_link.file'
assert.is_true((fs.os_fileinfo(path, file_info)))
@@ -883,19 +877,19 @@ describe('fs function', function()
end)
describe('os_fileinfo_link', function()
it('returns false if given a non-existing file', function()
itp('returns false if given a non-existing file', function()
local file_info = file_info_new()
assert.is_false((fs.os_fileinfo_link('/non-existent', file_info)))
end)
it('returns true if given an existing file and fills file_info', function()
itp('returns true if given an existing file and fills file_info', function()
local file_info = file_info_new()
local path = 'unit-test-directory/test.file'
assert.is_true((fs.os_fileinfo_link(path, file_info)))
assert.is_true((is_file_info_filled(file_info)))
end)
it('returns the file info of the link, not the linked file', function()
itp('returns the file info of the link, not the linked file', function()
local file_info = file_info_new()
local path = 'unit-test-directory/test_link.file'
assert.is_true((fs.os_fileinfo_link(path, file_info)))
@@ -906,12 +900,12 @@ describe('fs function', function()
end)
describe('os_fileinfo_fd', function()
it('returns false if given an invalid file descriptor', function()
itp('returns false if given an invalid file descriptor', function()
local file_info = file_info_new()
assert.is_false((fs.os_fileinfo_fd(-1, file_info)))
end)
it('returns true if given a file descriptor and fills file_info', function()
itp('returns true if given a file descriptor and fills file_info', function()
local file_info = file_info_new()
local path = 'unit-test-directory/test.file'
local fd = ffi.C.open(path, 0)
@@ -922,7 +916,7 @@ describe('fs function', function()
end)
describe('os_fileinfo_id_equal', function()
it('returns false if file infos represent different files', function()
itp('returns false if file infos represent different files', function()
local file_info_1 = file_info_new()
local file_info_2 = file_info_new()
local path_1 = 'unit-test-directory/test.file'
@@ -932,7 +926,7 @@ describe('fs function', function()
assert.is_false((fs.os_fileinfo_id_equal(file_info_1, file_info_2)))
end)
it('returns true if file infos represent the same file', function()
itp('returns true if file infos represent the same file', function()
local file_info_1 = file_info_new()
local file_info_2 = file_info_new()
local path = 'unit-test-directory/test.file'
@@ -941,7 +935,7 @@ describe('fs function', function()
assert.is_true((fs.os_fileinfo_id_equal(file_info_1, file_info_2)))
end)
it('returns true if file infos represent the same file (symlink)', function()
itp('returns true if file infos represent the same file (symlink)', function()
local file_info_1 = file_info_new()
local file_info_2 = file_info_new()
local path_1 = 'unit-test-directory/test.file'
@@ -953,7 +947,7 @@ describe('fs function', function()
end)
describe('os_fileinfo_id', function()
it('extracts ino/dev from file_info into file_id', function()
itp('extracts ino/dev from file_info into file_id', function()
local file_info = file_info_new()
local file_id = file_id_new()
local path = 'unit-test-directory/test.file'
@@ -965,7 +959,7 @@ describe('fs function', function()
end)
describe('os_fileinfo_inode', function()
it('returns the inode from file_info', function()
itp('returns the inode from file_info', function()
local file_info = file_info_new()
local path = 'unit-test-directory/test.file'
assert.is_true((fs.os_fileinfo(path, file_info)))
@@ -975,7 +969,7 @@ describe('fs function', function()
end)
describe('os_fileinfo_size', function()
it('returns the correct size of a file', function()
itp('returns the correct size of a file', function()
local path = 'unit-test-directory/test.file'
local file = io.open(path, 'w')
file:write('some bytes to get filesize != 0')
@@ -989,7 +983,7 @@ describe('fs function', function()
end)
describe('os_fileinfo_hardlinks', function()
it('returns the correct number of hardlinks', function()
itp('returns the correct number of hardlinks', function()
local path = 'unit-test-directory/test.file'
local path_link = 'unit-test-directory/test_hlink.file'
local file_info = file_info_new()
@@ -1002,7 +996,7 @@ describe('fs function', function()
end)
describe('os_fileinfo_blocksize', function()
it('returns the correct blocksize of a file', function()
itp('returns the correct blocksize of a file', function()
local path = 'unit-test-directory/test.file'
-- there is a bug in luafilesystem where
-- `lfs.attributes path, 'blksize'` returns the worng value:
@@ -1023,12 +1017,12 @@ describe('fs function', function()
end)
describe('os_fileid', function()
it('returns false if given an non-existing file', function()
itp('returns false if given an non-existing file', function()
local file_id = file_id_new()
assert.is_false((fs.os_fileid('/non-existent', file_id)))
end)
it('returns true if given an existing file and fills file_id', function()
itp('returns true if given an existing file and fills file_id', function()
local file_id = file_id_new()
local path = 'unit-test-directory/test.file'
assert.is_true((fs.os_fileid(path, file_id)))
@@ -1038,14 +1032,14 @@ describe('fs function', function()
end)
describe('os_fileid_equal', function()
it('returns true if two FileIDs are equal', function()
itp('returns true if two FileIDs are equal', function()
local file_id = file_id_new()
local path = 'unit-test-directory/test.file'
assert.is_true((fs.os_fileid(path, file_id)))
assert.is_true((fs.os_fileid_equal(file_id, file_id)))
end)
it('returns false if two FileIDs are not equal', function()
itp('returns false if two FileIDs are not equal', function()
local file_id_1 = file_id_new()
local file_id_2 = file_id_new()
local path_1 = 'unit-test-directory/test.file'
@@ -1057,7 +1051,7 @@ describe('fs function', function()
end)
describe('os_fileid_equal_fileinfo', function()
it('returns true if file_id and file_info represent the same file', function()
itp('returns true if file_id and file_info represent the same file', function()
local file_id = file_id_new()
local file_info = file_info_new()
local path = 'unit-test-directory/test.file'
@@ -1066,7 +1060,7 @@ describe('fs function', function()
assert.is_true((fs.os_fileid_equal_fileinfo(file_id, file_info)))
end)
it('returns false if file_id and file_info represent different files', function()
itp('returns false if file_id and file_info represent different files', function()
local file_id = file_id_new()
local file_info = file_info_new()
local path_1 = 'unit-test-directory/test.file'

View File

@@ -1,4 +1,5 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local cimported = helpers.cimport(
'./src/nvim/os/shell.h',
'./src/nvim/option_defs.h',
@@ -51,63 +52,51 @@ describe('shell functions', function()
end
describe('os_system', function()
it('can echo some output (shell builtin)', function()
itp('can echo some output (shell builtin)', function()
local cmd, text = 'echo -n', 'some text'
local status, output = os_system(cmd .. ' ' .. text)
eq(text, output)
eq(0, status)
end)
it('can deal with empty output', function()
itp('can deal with empty output', function()
local cmd = 'echo -n'
local status, output = os_system(cmd)
eq('', output)
eq(0, status)
end)
it('can pass input on stdin', function()
itp('can pass input on stdin', function()
local cmd, input = 'cat -', 'some text\nsome other text'
local status, output = os_system(cmd, input)
eq(input, output)
eq(0, status)
end)
it ('returns non-zero exit code', function()
itp('returns non-zero exit code', function()
local status = os_system('exit 2')
eq(2, status)
end)
end)
describe('shell_build_argv', function()
local saved_opts = {}
setup(function()
saved_opts.p_sh = cimported.p_sh
saved_opts.p_shcf = cimported.p_shcf
end)
teardown(function()
cimported.p_sh = saved_opts.p_sh
cimported.p_shcf = saved_opts.p_shcf
end)
it('works with NULL arguments', function()
itp('works with NULL arguments', function()
eq({'/bin/bash'}, shell_build_argv(nil, nil))
end)
it('works with cmd', function()
itp('works with cmd', function()
eq({'/bin/bash', '-c', 'abc def'}, shell_build_argv('abc def', nil))
end)
it('works with extra_args', function()
itp('works with extra_args', function()
eq({'/bin/bash', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl'))
end)
it('works with cmd and extra_args', function()
itp('works with cmd and extra_args', function()
eq({'/bin/bash', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl'))
end)
it('splits and unquotes &shell and &shellcmdflag', function()
itp('splits and unquotes &shell and &shellcmdflag', function()
cimported.p_sh = to_cstr('/Program" "Files/zsh -f')
cimported.p_shcf = to_cstr('-x -o "sh word split" "-"c')
eq({'/Program Files/zsh', '-f',
@@ -117,7 +106,7 @@ describe('shell functions', function()
shell_build_argv('abc def', 'ghi jkl'))
end)
it('applies shellxescape (p_sxe) and shellxquote (p_sxq)', function()
itp('applies shellxescape (p_sxe) and shellxquote (p_sxq)', function()
cimported.p_sxq = to_cstr('(')
cimported.p_sxe = to_cstr('"&|<>()@^')
@@ -129,7 +118,7 @@ describe('shell functions', function()
eq(nil, argv[3])
end)
it('applies shellxquote="(', function()
itp('applies shellxquote="(', function()
cimported.p_sxq = to_cstr('"(')
cimported.p_sxe = to_cstr('"&|<>()@^')
@@ -141,7 +130,7 @@ describe('shell functions', function()
eq(nil, argv[3])
end)
it('applies shellxquote="', function()
itp('applies shellxquote="', function()
cimported.p_sxq = to_cstr('"')
cimported.p_sxe = to_cstr('')
@@ -153,7 +142,7 @@ describe('shell functions', function()
eq(nil, argv[3])
end)
it('with empty shellxquote/shellxescape', function()
itp('with empty shellxquote/shellxescape', function()
local argv = ffi.cast('char**', cimported.shell_build_argv(
to_cstr('echo -n some text'), nil))
eq(ffi.string(argv[0]), '/bin/bash')

View File

@@ -1,4 +1,5 @@
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local eq = helpers.eq
@@ -27,11 +28,11 @@ describe('users function', function()
local current_username = os.getenv('USER')
describe('os_get_usernames', function()
it('returns FAIL if called with NULL', function()
itp('returns FAIL if called with NULL', function()
eq(FAIL, users.os_get_usernames(NULL))
end)
it('fills the names garray with os usernames and returns OK', function()
itp('fills the names garray with os usernames and returns OK', function()
local ga_users = garray_new()
eq(OK, users.os_get_usernames(ga_users))
local user_count = garray_get_len(ga_users)
@@ -48,7 +49,7 @@ describe('users function', function()
end)
describe('os_get_user_name', function()
it('should write the username into the buffer and return OK', function()
itp('should write the username into the buffer and return OK', function()
local name_out = ffi.new('char[100]')
eq(OK, users.os_get_user_name(name_out, 100))
eq(current_username, ffi.string(name_out))
@@ -56,14 +57,14 @@ describe('users function', function()
end)
describe('os_get_uname', function()
it('should write the username into the buffer and return OK', function()
itp('should write the username into the buffer and return OK', function()
local name_out = ffi.new('char[100]')
local user_id = lib.getuid()
eq(OK, users.os_get_uname(user_id, name_out, 100))
eq(current_username, ffi.string(name_out))
end)
it('should FAIL if the userid is not found', function()
itp('should FAIL if the userid is not found', function()
local name_out = ffi.new('char[100]')
-- hoping nobody has this uid
local user_id = 2342
@@ -73,16 +74,16 @@ describe('users function', function()
end)
describe('os_get_user_directory', function()
it('should return NULL if called with NULL', function()
itp('should return NULL if called with NULL', function()
eq(NULL, users.os_get_user_directory(NULL))
end)
it('should return $HOME for the current user', function()
itp('should return $HOME for the current user', function()
local home = os.getenv('HOME')
eq(home, ffi.string((users.os_get_user_directory(current_username))))
end)
it('should return NULL if the user is not found', function()
itp('should return NULL if the user is not found', function()
eq(NULL, users.os_get_user_directory('neovim_user_not_found_test'))
end)
end)

View File

@@ -1,5 +1,6 @@
local lfs = require('lfs')
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local eq = helpers.eq
@@ -14,13 +15,6 @@ local FAIL = helpers.FAIL
cimport('string.h')
local path = cimport('./src/nvim/path.h')
-- import constants parsed by ffi
local kEqualFiles = path.kEqualFiles
local kDifferentFiles = path.kDifferentFiles
local kBothFilesMissing = path.kBothFilesMissing
local kOneFileMissing = path.kOneFileMissing
local kEqualFileNames = path.kEqualFileNames
local length = 0
local buffer = nil
@@ -45,7 +39,7 @@ describe('path function', function()
buffer = cstr(length, '')
end)
it('returns the absolute directory name of a given relative one', function()
itp('returns the absolute directory name of a given relative one', function()
local result = path_full_dir_name('..', buffer, length)
eq(OK, result)
local old_dir = lfs.currentdir()
@@ -55,16 +49,16 @@ describe('path function', function()
eq(expected, (ffi.string(buffer)))
end)
it('returns the current directory name if the given string is empty', function()
itp('returns the current directory name if the given string is empty', function()
eq(OK, (path_full_dir_name('', buffer, length)))
eq(lfs.currentdir(), (ffi.string(buffer)))
end)
it('fails if the given directory does not exist', function()
itp('fails if the given directory does not exist', function()
eq(FAIL, path_full_dir_name('does_not_exist', buffer, length))
end)
it('works with a normal relative dir', function()
itp('works with a normal relative dir', function()
local result = path_full_dir_name('unit-test-directory', buffer, length)
eq(lfs.currentdir() .. '/unit-test-directory', (ffi.string(buffer)))
eq(OK, result)
@@ -91,26 +85,26 @@ describe('path function', function()
os.remove(f2)
end)
it('returns kEqualFiles when passed the same file', function()
eq(kEqualFiles, (path_full_compare(f1, f1)))
itp('returns kEqualFiles when passed the same file', function()
eq(path.kEqualFiles, (path_full_compare(f1, f1)))
end)
it('returns kEqualFileNames when files that dont exist and have same name', function()
eq(kEqualFileNames, (path_full_compare('null.txt', 'null.txt', true)))
itp('returns kEqualFileNames when files that dont exist and have same name', function()
eq(path.kEqualFileNames, (path_full_compare('null.txt', 'null.txt', true)))
end)
it('returns kBothFilesMissing when files that dont exist', function()
eq(kBothFilesMissing, (path_full_compare('null.txt', 'null.txt')))
itp('returns kBothFilesMissing when files that dont exist', function()
eq(path.kBothFilesMissing, (path_full_compare('null.txt', 'null.txt')))
end)
it('returns kDifferentFiles when passed different files', function()
eq(kDifferentFiles, (path_full_compare(f1, f2)))
eq(kDifferentFiles, (path_full_compare(f2, f1)))
itp('returns kDifferentFiles when passed different files', function()
eq(path.kDifferentFiles, (path_full_compare(f1, f2)))
eq(path.kDifferentFiles, (path_full_compare(f2, f1)))
end)
it('returns kOneFileMissing if only one does not exist', function()
eq(kOneFileMissing, (path_full_compare(f1, 'null.txt')))
eq(kOneFileMissing, (path_full_compare('null.txt', f1)))
itp('returns kOneFileMissing if only one does not exist', function()
eq(path.kOneFileMissing, (path_full_compare(f1, 'null.txt')))
eq(path.kOneFileMissing, (path_full_compare('null.txt', f1)))
end)
end)
@@ -121,11 +115,11 @@ describe('path function', function()
return ffi.string(res)
end
it('returns the tail of a given file path', function()
itp('returns the tail of a given file path', function()
eq('file.txt', path_tail('directory/file.txt'))
end)
it('returns an empty string if file ends in a slash', function()
itp('returns an empty string if file ends in a slash', function()
eq('', path_tail('directory/'))
end)
end)
@@ -137,24 +131,24 @@ describe('path function', function()
return ffi.string(res)
end
it('returns the tail of a file together with its separator', function()
itp('returns the tail of a file together with its separator', function()
eq('///file.txt', path_tail_with_sep('directory///file.txt'))
end)
it('returns an empty string when given an empty file name', function()
itp('returns an empty string when given an empty file name', function()
eq('', path_tail_with_sep(''))
end)
it('returns only the separator if there is a trailing separator', function()
itp('returns only the separator if there is a trailing separator', function()
eq('/', path_tail_with_sep('some/directory/'))
end)
it('cuts a leading separator', function()
itp('cuts a leading separator', function()
eq('file.txt', path_tail_with_sep('/file.txt'))
eq('', path_tail_with_sep('/'))
end)
it('returns the whole file name if there is no separator', function()
itp('returns the whole file name if there is no separator', function()
eq('file.txt', path_tail_with_sep('file.txt'))
end)
end)
@@ -180,13 +174,13 @@ describe('path function', function()
return eq(0, (ffi.C.strncmp((to_cstr(base)), pinvk, len)))
end
it('returns the executable name of an invocation given a relative invocation', function()
itp('returns the executable name of an invocation given a relative invocation', function()
local invk, len = invocation_path_tail('directory/exe a b c')
compare("exe a b c", invk, len)
eq(3, len)
end)
it('returns the executable name of an invocation given an absolute invocation', function()
itp('returns the executable name of an invocation given an absolute invocation', function()
if ffi.os == 'Windows' then
local invk, len = invocation_path_tail('C:\\Users\\anyone\\Program Files\\z a b')
compare('z a b', invk, len)
@@ -198,18 +192,18 @@ describe('path function', function()
end
end)
it('does not count arguments to the executable as part of its path', function()
itp('does not count arguments to the executable as part of its path', function()
local invk, len = invocation_path_tail('exe a/b\\c')
compare("exe a/b\\c", invk, len)
eq(3, len)
end)
it('only accepts whitespace as a terminator for the executable name', function()
itp('only accepts whitespace as a terminator for the executable name', function()
local invk, _ = invocation_path_tail('exe-a+b_c[]()|#!@$%^&*')
eq('exe-a+b_c[]()|#!@$%^&*', (ffi.string(invk)))
end)
it('is equivalent to path_tail when args do not contain a path separator', function()
itp('is equivalent to path_tail when args do not contain a path separator', function()
local ptail = path.path_tail(to_cstr("a/b/c x y z"))
neq(NULL, ptail)
local tail = ffi.string(ptail)
@@ -217,7 +211,7 @@ describe('path function', function()
eq(tail, ffi.string(invk))
end)
it('is not equivalent to path_tail when args contain a path separator', function()
itp('is not equivalent to path_tail when args contain a path separator', function()
local ptail = path.path_tail(to_cstr("a/b/c x y/z"))
neq(NULL, ptail)
local invk, _ = invocation_path_tail("a/b/c x y/z")
@@ -232,34 +226,34 @@ describe('path function', function()
return ffi.string(res)
end
it('returns', function()
itp('returns', function()
eq('directory/file.txt', path_next_component('some/directory/file.txt'))
end)
it('returns empty string if given file contains no separator', function()
itp('returns empty string if given file contains no separator', function()
eq('', path_next_component('file.txt'))
end)
end)
describe('path_shorten_fname', function()
it('returns NULL if `full_path` is NULL', function()
itp('returns NULL if `full_path` is NULL', function()
local dir = to_cstr('some/directory/file.txt')
eq(NULL, (path.path_shorten_fname(NULL, dir)))
end)
it('returns NULL if the path and dir does not match', function()
itp('returns NULL if the path and dir does not match', function()
local dir = to_cstr('not/the/same')
local full = to_cstr('as/this.txt')
eq(NULL, (path.path_shorten_fname(full, dir)))
end)
it('returns NULL if the path is not separated properly', function()
itp('returns NULL if the path is not separated properly', function()
local dir = to_cstr('some/very/long/')
local full = to_cstr('some/very/long/directory/file.txt')
eq(NULL, (path.path_shorten_fname(full, dir)))
end)
it('shortens the filename if `dir_name` is the start of `full_path`', function()
itp('shortens the filename if `dir_name` is the start of `full_path`', function()
local full = to_cstr('some/very/long/directory/file.txt')
local dir = to_cstr('some/very/long')
eq('directory/file.txt', (ffi.string(path.path_shorten_fname(full, dir))))
@@ -280,20 +274,20 @@ describe('path_shorten_fname_if_possible', function()
end)
describe('path_shorten_fname_if_possible', function()
it('returns shortened path if possible', function()
itp('returns shortened path if possible', function()
lfs.chdir('ut_directory')
local full = to_cstr(lfs.currentdir() .. '/subdir/file.txt')
eq('subdir/file.txt', (ffi.string(path.path_shorten_fname_if_possible(full))))
end)
it('returns `full_path` if a shorter version is not possible', function()
itp('returns `full_path` if a shorter version is not possible', function()
local old = lfs.currentdir()
lfs.chdir('ut_directory')
local full = old .. '/subdir/file.txt'
eq(full, (ffi.string(path.path_shorten_fname_if_possible(to_cstr(full)))))
end)
it('returns NULL if `full_path` is NULL', function()
itp('returns NULL if `full_path` is NULL', function()
eq(NULL, (path.path_shorten_fname_if_possible(NULL)))
end)
end)
@@ -330,13 +324,13 @@ describe('more path function', function()
buffer = cstr(length, '')
end)
it('fails if given filename is NULL', function()
itp('fails if given filename is NULL', function()
local force_expansion = 1
local result = path.vim_FullName(NULL, buffer, length, force_expansion)
eq(FAIL, result)
end)
it('fails safely if given length is wrong #5737', function()
itp('fails safely if given length is wrong #5737', function()
local force_expansion = 1
local filename = 'foo/bar/bazzzzzzz/buz/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/a'
local too_short_len = 8
@@ -347,7 +341,7 @@ describe('more path function', function()
eq(FAIL, result)
end)
it('uses the filename if the filename is a URL', function()
itp('uses the filename if the filename is a URL', function()
local force_expansion = 1
local filename = 'http://www.neovim.org'
local result = vim_FullName(filename, buffer, length, force_expansion)
@@ -355,7 +349,7 @@ describe('more path function', function()
eq(OK, result)
end)
it('fails and uses filename if given filename contains non-existing directory', function()
itp('fails and uses filename if given filename contains non-existing directory', function()
local force_expansion = 1
local filename = 'non_existing_dir/test.file'
local result = vim_FullName(filename, buffer, length, force_expansion)
@@ -363,7 +357,7 @@ describe('more path function', function()
eq(FAIL, result)
end)
it('concatenates given filename if it does not contain a slash', function()
itp('concatenates given filename if it does not contain a slash', function()
local force_expansion = 1
local result = vim_FullName('test.file', buffer, length, force_expansion)
local expected = lfs.currentdir() .. '/test.file'
@@ -371,7 +365,7 @@ describe('more path function', function()
eq(OK, result)
end)
it('concatenates given filename if it is a directory but does not contain a\n slash', function()
itp('concatenates given filename if it is a directory but does not contain a\n slash', function()
local force_expansion = 1
local result = vim_FullName('..', buffer, length, force_expansion)
local expected = lfs.currentdir() .. '/..'
@@ -381,7 +375,7 @@ describe('more path function', function()
-- Is it possible for every developer to enter '..' directory while running
-- the unit tests? Which other directory would be better?
it('enters given directory (instead of just concatenating the strings) if possible and if path contains a slash', function()
itp('enters given directory (instead of just concatenating the strings) if possible and if path contains a slash', function()
local force_expansion = 1
local result = vim_FullName('../test.file', buffer, length, force_expansion)
local old_dir = lfs.currentdir()
@@ -392,7 +386,7 @@ describe('more path function', function()
eq(OK, result)
end)
it('just copies the path if it is already absolute and force=0', function()
itp('just copies the path if it is already absolute and force=0', function()
local force_expansion = 0
local absolute_path = '/absolute/path'
local result = vim_FullName(absolute_path, buffer, length, force_expansion)
@@ -400,7 +394,7 @@ describe('more path function', function()
eq(OK, result)
end)
it('fails and uses filename when the path is relative to HOME', function()
itp('fails and uses filename when the path is relative to HOME', function()
local force_expansion = 1
local absolute_path = '~/home.file'
local result = vim_FullName(absolute_path, buffer, length, force_expansion)
@@ -408,14 +402,14 @@ describe('more path function', function()
eq(FAIL, result)
end)
it('works with some "normal" relative path with directories', function()
itp('works with some "normal" relative path with directories', function()
local force_expansion = 1
local result = vim_FullName('unit-test-directory/test.file', buffer, length, force_expansion)
eq(OK, result)
eq(lfs.currentdir() .. '/unit-test-directory/test.file', (ffi.string(buffer)))
end)
it('does not modify the given filename', function()
itp('does not modify the given filename', function()
local force_expansion = 1
local filename = to_cstr('unit-test-directory/test.file')
-- Don't use the wrapper here but pass a cstring directly to the c
@@ -426,7 +420,7 @@ describe('more path function', function()
eq(OK, result)
end)
it('works with directories that have one path component', function()
itp('works with directories that have one path component', function()
local force_expansion = 1
local filename = to_cstr('/tmp')
local result = path.vim_FullName(filename, buffer, length, force_expansion)
@@ -446,12 +440,12 @@ describe('more path function', function()
after_each(function() lfs.rmdir('CamelCase') end)
if ffi.os == 'Windows' or ffi.os == 'OSX' then
it('Corrects the case of file names in Mac and Windows', function()
itp('Corrects the case of file names in Mac and Windows', function()
eq('CamelCase', fix_case('camelcase'))
eq('CamelCase', fix_case('cAMELcASE'))
end)
else
it('does nothing on Linux', function()
itp('does nothing on Linux', function()
eq('camelcase', fix_case('camelcase'))
eq('cAMELcASE', fix_case('cAMELcASE'))
end)
@@ -459,41 +453,41 @@ describe('more path function', function()
end)
describe('append_path', function()
it('joins given paths with a slash', function()
itp('joins given paths with a slash', function()
local path1 = cstr(100, 'path1')
local to_append = to_cstr('path2')
eq(OK, (path.append_path(path1, to_append, 100)))
eq("path1/path2", (ffi.string(path1)))
end)
it('joins given paths without adding an unnecessary slash', function()
itp('joins given paths without adding an unnecessary slash', function()
local path1 = cstr(100, 'path1/')
local to_append = to_cstr('path2')
eq(OK, path.append_path(path1, to_append, 100))
eq("path1/path2", (ffi.string(path1)))
end)
it('fails and uses filename if there is not enough space left for to_append', function()
itp('fails and uses filename if there is not enough space left for to_append', function()
local path1 = cstr(11, 'path1/')
local to_append = to_cstr('path2')
eq(FAIL, (path.append_path(path1, to_append, 11)))
end)
it('does not append a slash if to_append is empty', function()
itp('does not append a slash if to_append is empty', function()
local path1 = cstr(6, 'path1')
local to_append = to_cstr('')
eq(OK, (path.append_path(path1, to_append, 6)))
eq('path1', (ffi.string(path1)))
end)
it('does not append unnecessary dots', function()
itp('does not append unnecessary dots', function()
local path1 = cstr(6, 'path1')
local to_append = to_cstr('.')
eq(OK, (path.append_path(path1, to_append, 6)))
eq('path1', (ffi.string(path1)))
end)
it('copies to_append to path, if path is empty', function()
itp('copies to_append to path, if path is empty', function()
local path1 = cstr(7, '')
local to_append = to_cstr('/path2')
eq(OK, (path.append_path(path1, to_append, 7)))
@@ -507,15 +501,15 @@ describe('more path function', function()
return path.path_is_absolute_path(filename)
end
it('returns true if filename starts with a slash', function()
itp('returns true if filename starts with a slash', function()
eq(OK, path_is_absolute_path('/some/directory/'))
end)
it('returns true if filename starts with a tilde', function()
itp('returns true if filename starts with a tilde', function()
eq(OK, path_is_absolute_path('~/in/my/home~/directory'))
end)
it('returns false if filename starts not with slash nor tilde', function()
itp('returns false if filename starts not with slash nor tilde', function()
eq(FAIL, path_is_absolute_path('not/in/my/home~/directory'))
end)
end)

View File

@@ -2,6 +2,6 @@
-- Busted started doing this to help provide more isolation. See issue #62
-- for more information about this.
local ffi = require('ffi')
local helpers = require('test.unit.helpers')
local helpers = require('test.unit.helpers')(nil)
local lfs = require('lfs')
local preprocess = require('test.unit.preprocess')

View File

@@ -1,10 +1,13 @@
local helpers = require 'test.unit.helpers'
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local prof = helpers.cimport './src/nvim/profile.h'
local cimport = helpers.cimport
local ffi = helpers.ffi
local eq = helpers.eq
local neq = helpers.neq
local prof = cimport('./src/nvim/profile.h')
local function split(inputstr, sep)
if sep == nil then
sep = "%s"
@@ -78,7 +81,7 @@ describe('profiling related functions', function()
end
describe('profile_equal', function()
it('times are equal to themselves', function()
itp('times are equal to themselves', function()
local start = profile_start()
assert.is_true(profile_equal(start, start))
@@ -86,7 +89,7 @@ describe('profiling related functions', function()
assert.is_true(profile_equal(e, e))
end)
it('times are unequal to others', function()
itp('times are unequal to others', function()
assert.is_false(profile_equal(profile_start(), profile_start()))
end)
end)
@@ -95,24 +98,24 @@ describe('profiling related functions', function()
-- the profiling package. Those functions in turn will probably be tested
-- using profile_cmp... circular reasoning.
describe('profile_cmp', function()
it('can compare subsequent starts', function()
itp('can compare subsequent starts', function()
local s1, s2 = profile_start(), profile_start()
assert.is_true(profile_cmp(s1, s2) > 0)
assert.is_true(profile_cmp(s2, s1) < 0)
end)
it('can compare the zero element', function()
itp('can compare the zero element', function()
assert.is_true(profile_cmp(profile_zero(), profile_zero()) == 0)
end)
it('correctly orders divisions', function()
itp('correctly orders divisions', function()
local start = profile_start()
assert.is_true(profile_cmp(start, profile_divide(start, 10)) <= 0)
end)
end)
describe('profile_divide', function()
it('actually performs division', function()
itp('actually performs division', function()
-- note: the routine actually performs floating-point division to get
-- better rounding behaviour, we have to take that into account when
-- checking. (check range, not exact number).
@@ -134,14 +137,14 @@ describe('profiling related functions', function()
end)
describe('profile_zero', function()
it('returns the same value on each call', function()
itp('returns the same value on each call', function()
eq(0, profile_zero())
assert.is_true(profile_equal(profile_zero(), profile_zero()))
end)
end)
describe('profile_start', function()
it('increases', function()
itp('increases', function()
local last = profile_start()
for _ = 1, 100 do
local curr = profile_start()
@@ -152,11 +155,11 @@ describe('profiling related functions', function()
end)
describe('profile_end', function()
it('the elapsed time cannot be zero', function()
itp('the elapsed time cannot be zero', function()
neq(profile_zero(), profile_end(profile_start()))
end)
it('outer elapsed >= inner elapsed', function()
itp('outer elapsed >= inner elapsed', function()
for _ = 1, 100 do
local start_outer = profile_start()
local start_inner = profile_start()
@@ -169,11 +172,11 @@ describe('profiling related functions', function()
end)
describe('profile_setlimit', function()
it('sets no limit when 0 is passed', function()
itp('sets no limit when 0 is passed', function()
eq(true, profile_equal(profile_setlimit(0), profile_zero()))
end)
it('sets a limit in the future otherwise', function()
itp('sets a limit in the future otherwise', function()
local future = profile_setlimit(1000)
local now = profile_start()
assert.is_true(profile_cmp(future, now) < 0)
@@ -181,12 +184,12 @@ describe('profiling related functions', function()
end)
describe('profile_passed_limit', function()
it('start is in the past', function()
itp('start is in the past', function()
local start = profile_start()
eq(true, profile_passed_limit(start))
end)
it('start + start is in the future', function()
itp('start + start is in the future', function()
local start = profile_start()
local future = profile_add(start, start)
eq(false, profile_passed_limit(future))
@@ -194,12 +197,12 @@ describe('profiling related functions', function()
end)
describe('profile_msg', function()
it('prints the zero time as 0.00000', function()
itp('prints the zero time as 0.00000', function()
local str = trim(profile_msg(profile_zero()))
eq(str, "0.000000")
end)
it('prints the time passed, in seconds.microsends', function()
itp('prints the time passed, in seconds.microsends', function()
local start = profile_start()
local endt = profile_end(start)
local str = trim(profile_msg(endt))
@@ -221,14 +224,14 @@ describe('profiling related functions', function()
end)
describe('profile_add', function()
it('adds profiling times', function()
itp('adds profiling times', function()
local start = profile_start()
assert.equals(start, profile_add(profile_zero(), start))
end)
end)
describe('profile_sub', function()
it('subtracts profiling times', function()
itp('subtracts profiling times', function()
-- subtracting zero does nothing
local start = profile_start()
assert.equals(start, profile_sub(start, profile_zero()))

View File

@@ -1,9 +1,11 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local ffi = helpers.ffi
local eq = helpers.eq
local cstr = helpers.cstr
local eq = helpers.eq
local ffi = helpers.ffi
local cstr = helpers.cstr
local to_cstr = helpers.to_cstr
local child_call_once = helpers.child_call_once
local rbuffer = helpers.cimport("./test/unit/fixtures/rbuffer.h")
@@ -31,9 +33,11 @@ describe('rbuffer functions', function()
end
before_each(function()
rbuf = ffi.gc(rbuffer.rbuffer_new(capacity), rbuffer.rbuffer_free)
-- fill the internal buffer with the character '0' to simplify inspecting
ffi.C.memset(rbuf.start_ptr, string.byte('0'), capacity)
child_call_once(function()
rbuf = ffi.gc(rbuffer.rbuffer_new(capacity), rbuffer.rbuffer_free)
-- fill the internal buffer with the character '0' to simplify inspecting
ffi.C.memset(rbuf.start_ptr, string.byte('0'), capacity)
end)
end)
describe('RBUFFER_UNTIL_FULL', function()
@@ -50,66 +54,51 @@ describe('rbuffer functions', function()
end)
describe('with empty buffer in one contiguous chunk', function()
it('is called once with the empty chunk', function()
itp('is called once with the empty chunk', function()
collect_write_chunks()
eq({'0000000000000000'}, chunks)
end)
end)
describe('with partially empty buffer in one contiguous chunk', function()
before_each(function()
itp('is called once with the empty chunk', function()
write('string')
end)
it('is called once with the empty chunk', function()
collect_write_chunks()
eq({'0000000000'}, chunks)
end)
end)
describe('with filled buffer in one contiguous chunk', function()
before_each(function()
itp('is not called', function()
write('abcdefghijklmnopq')
end)
it('is not called', function()
collect_write_chunks()
eq({}, chunks)
end)
end)
describe('with buffer partially empty in two contiguous chunks', function()
before_each(function()
itp('is called twice with each filled chunk', function()
write('1234567890')
read(8)
end)
it('is called twice with each filled chunk', function()
collect_write_chunks()
eq({'000000', '12345678'}, chunks)
end)
end)
describe('with buffer empty in two contiguous chunks', function()
before_each(function()
itp('is called twice with each filled chunk', function()
write('12345678')
read(8)
end)
it('is called twice with each filled chunk', function()
collect_write_chunks()
eq({'00000000', '12345678'}, chunks)
end)
end)
describe('with buffer filled in two contiguous chunks', function()
before_each(function()
itp('is not called', function()
write('12345678')
read(8)
write('abcdefghijklmnopq')
end)
it('is not called', function()
collect_write_chunks()
eq({}, chunks)
end)
@@ -130,55 +119,43 @@ describe('rbuffer functions', function()
end)
describe('with empty buffer', function()
it('is not called', function()
itp('is not called', function()
collect_read_chunks()
eq({}, chunks)
end)
end)
describe('with partially filled buffer in one contiguous chunk', function()
before_each(function()
itp('is called once with the filled chunk', function()
write('string')
end)
it('is called once with the filled chunk', function()
collect_read_chunks()
eq({'string'}, chunks)
end)
end)
describe('with filled buffer in one contiguous chunk', function()
before_each(function()
itp('is called once with the filled chunk', function()
write('abcdefghijklmnopq')
end)
it('is called once with the filled chunk', function()
collect_read_chunks()
eq({'abcdefghijklmnop'}, chunks)
end)
end)
describe('with buffer partially filled in two contiguous chunks', function()
before_each(function()
itp('is called twice with each filled chunk', function()
write('1234567890')
read(10)
write('long string')
end)
it('is called twice with each filled chunk', function()
collect_read_chunks()
eq({'long s', 'tring'}, chunks)
end)
end)
describe('with buffer filled in two contiguous chunks', function()
before_each(function()
itp('is called twice with each filled chunk', function()
write('12345678')
read(8)
write('abcdefghijklmnopq')
end)
it('is called twice with each filled chunk', function()
collect_read_chunks()
eq({'abcdefgh', 'ijklmnop'}, chunks)
end)
@@ -198,20 +175,17 @@ describe('rbuffer functions', function()
end)
describe('with empty buffer', function()
it('is not called', function()
itp('is not called', function()
collect_chars()
eq({}, chars)
end)
end)
describe('with buffer filled in two contiguous chunks', function()
before_each(function()
itp('collects each character and index', function()
write('1234567890')
read(10)
write('long string')
end)
it('collects each character and index', function()
collect_chars()
eq({{'l', 0}, {'o', 1}, {'n', 2}, {'g', 3}, {' ', 4}, {'s', 5},
{'t', 6}, {'r', 7}, {'i', 8}, {'n', 9}, {'g', 10}}, chars)
@@ -232,20 +206,17 @@ describe('rbuffer functions', function()
end)
describe('with empty buffer', function()
it('is not called', function()
itp('is not called', function()
collect_chars()
eq({}, chars)
end)
end)
describe('with buffer filled in two contiguous chunks', function()
before_each(function()
itp('collects each character and index', function()
write('1234567890')
read(10)
write('long string')
end)
it('collects each character and index', function()
collect_chars()
eq({{'g', 10}, {'n', 9}, {'i', 8}, {'r', 7}, {'t', 6}, {'s', 5},
{' ', 4}, {'g', 3}, {'n', 2}, {'o', 1}, {'l', 0}}, chars)
@@ -264,13 +235,10 @@ describe('rbuffer functions', function()
end
describe('with buffer filled in two contiguous chunks', function()
before_each(function()
itp('compares the common longest sequence', function()
write('1234567890')
read(10)
write('long string')
end)
it('compares the common longest sequence', function()
eq(0, cmp('long string'))
eq(0, cmp('long strin'))
eq(-1, cmp('long striM'))
@@ -282,31 +250,31 @@ describe('rbuffer functions', function()
end)
describe('with empty buffer', function()
it('returns 0 since no characters are compared', function()
itp('returns 0 since no characters are compared', function()
eq(0, cmp(''))
end)
end)
end)
describe('rbuffer_write', function()
it('fills the internal buffer and returns the write count', function()
itp('fills the internal buffer and returns the write count', function()
eq(12, write('short string'))
eq('short string0000', inspect())
end)
it('wont write beyond capacity', function()
itp('wont write beyond capacity', function()
eq(16, write('very very long string'))
eq('very very long s', inspect())
end)
end)
describe('rbuffer_read', function()
it('reads what was previously written', function()
itp('reads what was previously written', function()
write('to read')
eq('to read', read(20))
end)
it('reads nothing if the buffer is empty', function()
itp('reads nothing if the buffer is empty', function()
eq('', read(20))
write('empty')
eq('empty', read(20))
@@ -315,7 +283,7 @@ describe('rbuffer functions', function()
end)
describe('rbuffer_get', function()
it('fetch the pointer at offset, wrapping if required', function()
itp('fetch the pointer at offset, wrapping if required', function()
write('1234567890')
read(10)
write('long string')
@@ -334,7 +302,7 @@ describe('rbuffer functions', function()
end)
describe('wrapping behavior', function()
it('writing/reading wraps across the end of the internal buffer', function()
itp('writing/reading wraps across the end of the internal buffer', function()
write('1234567890')
eq('1234', read(4))
eq('5678', read(4))

View File

@@ -26,6 +26,22 @@ function Set:new(items)
return obj
end
function Set:copy()
local obj = {}
obj.nelem = self.nelem
obj.tbl = {}
obj.items = {}
for k, v in pairs(self.tbl) do
obj.tbl[k] = v
end
for k, v in pairs(self.items) do
obj.items[k] = v
end
setmetatable(obj, Set)
obj.__index = Set
return obj
end
-- adds the argument Set to this Set
function Set:union(other)
for e in other:iterator() do

View File

@@ -1,4 +1,5 @@
local helpers = require("test.unit.helpers")
local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it)
local cimport = helpers.cimport
local eq = helpers.eq
@@ -19,23 +20,23 @@ describe('vim_strsave_escaped()', function()
return ret
end
it('precedes by a backslash all chars from second argument', function()
itp('precedes by a backslash all chars from second argument', function()
eq([[\a\b\c\d]], vim_strsave_escaped('abcd','abcd'))
end)
it('precedes by a backslash chars only from second argument', function()
itp('precedes by a backslash chars only from second argument', function()
eq([[\a\bcd]], vim_strsave_escaped('abcd','ab'))
end)
it('returns a copy of passed string if second argument is empty', function()
itp('returns a copy of passed string if second argument is empty', function()
eq('text \n text', vim_strsave_escaped('text \n text',''))
end)
it('returns an empty string if first argument is empty string', function()
itp('returns an empty string if first argument is empty string', function()
eq('', vim_strsave_escaped('','\r'))
end)
it('returns a copy of passed string if it does not contain chars from 2nd argument', function()
itp('returns a copy of passed string if it does not contain chars from 2nd argument', function()
eq('some text', vim_strsave_escaped('some text', 'a'))
end)
end)
@@ -50,51 +51,51 @@ describe('vim_strnsave_unquoted()', function()
return ret
end
it('copies unquoted strings as-is', function()
itp('copies unquoted strings as-is', function()
eq('-c', vim_strnsave_unquoted('-c'))
eq('', vim_strnsave_unquoted(''))
end)
it('respects length argument', function()
itp('respects length argument', function()
eq('', vim_strnsave_unquoted('-c', 0))
eq('-', vim_strnsave_unquoted('-c', 1))
eq('-', vim_strnsave_unquoted('"-c', 2))
end)
it('unquotes fully quoted word', function()
itp('unquotes fully quoted word', function()
eq('/bin/sh', vim_strnsave_unquoted('"/bin/sh"'))
end)
it('unquotes partially quoted word', function()
itp('unquotes partially quoted word', function()
eq('/Program Files/sh', vim_strnsave_unquoted('/Program" "Files/sh'))
end)
it('removes ""', function()
itp('removes ""', function()
eq('/Program Files/sh', vim_strnsave_unquoted('/""Program" "Files/sh'))
end)
it('performs unescaping of "', function()
itp('performs unescaping of "', function()
eq('/"Program Files"/sh', vim_strnsave_unquoted('/"\\""Program Files"\\""/sh'))
end)
it('performs unescaping of \\', function()
itp('performs unescaping of \\', function()
eq('/\\Program Files\\foo/sh', vim_strnsave_unquoted('/"\\\\"Program Files"\\\\foo"/sh'))
end)
it('strips quote when there is no pair to it', function()
itp('strips quote when there is no pair to it', function()
eq('/Program Files/sh', vim_strnsave_unquoted('/Program" Files/sh'))
eq('', vim_strnsave_unquoted('"'))
end)
it('allows string to end with one backslash unescaped', function()
itp('allows string to end with one backslash unescaped', function()
eq('/Program Files/sh\\', vim_strnsave_unquoted('/Program" Files/sh\\'))
end)
it('does not perform unescaping out of quotes', function()
itp('does not perform unescaping out of quotes', function()
eq('/Program\\ Files/sh\\', vim_strnsave_unquoted('/Program\\ Files/sh\\'))
end)
it('does not unescape \\n', function()
itp('does not unescape \\n', function()
eq('/Program\\nFiles/sh', vim_strnsave_unquoted('/Program"\\n"Files/sh'))
end)
end)

View File

@@ -1,58 +1,65 @@
local lfs = require 'lfs'
local helpers = require 'test.unit.helpers'
local lfs = require('lfs')
local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it)
local os = helpers.cimport './src/nvim/os/os.h'
local tempfile = helpers.cimport './src/nvim/fileio.h'
local eq = helpers.eq
local neq = helpers.neq
local cimport = helpers.cimport
local child_call_once = helpers.child_call_once
local child_cleanup_once = helpers.child_cleanup_once
local lib = cimport('./src/nvim/os/os.h', './src/nvim/fileio.h')
describe('tempfile related functions', function()
before_each(function()
tempfile.vim_deltempdir()
end)
after_each(function()
tempfile.vim_deltempdir()
local function vim_deltempdir()
lib.vim_deltempdir()
end
child_call_once(vim_deltempdir)
child_cleanup_once(vim_deltempdir)
end)
local vim_gettempdir = function()
return helpers.ffi.string(tempfile.vim_gettempdir())
return helpers.ffi.string(lib.vim_gettempdir())
end
describe('vim_gettempdir', function()
it('returns path to Neovim own temp directory', function()
itp('returns path to Neovim own temp directory', function()
local dir = vim_gettempdir()
assert.True(dir ~= nil and dir:len() > 0)
-- os_file_is_writable returns 2 for a directory which we have rights
-- to write into.
assert.equals(os.os_file_is_writable(helpers.to_cstr(dir)), 2)
eq(lib.os_file_is_writable(helpers.to_cstr(dir)), 2)
for entry in lfs.dir(dir) do
assert.True(entry == '.' or entry == '..')
end
end)
it('returns the same directory on each call', function()
itp('returns the same directory on each call', function()
local dir1 = vim_gettempdir()
local dir2 = vim_gettempdir()
assert.equals(dir1, dir2)
eq(dir1, dir2)
end)
end)
describe('vim_tempname', function()
local vim_tempname = function()
return helpers.ffi.string(tempfile.vim_tempname())
return helpers.ffi.string(lib.vim_tempname())
end
it('generate name of non-existing file', function()
itp('generate name of non-existing file', function()
local file = vim_tempname()
assert.truthy(file)
assert.False(os.os_path_exists(file))
assert.False(lib.os_path_exists(file))
end)
it('generate different names on each call', function()
itp('generate different names on each call', function()
local fst = vim_tempname()
local snd = vim_tempname()
assert.not_equals(fst, snd)
neq(fst, snd)
end)
it('generate file name in Neovim own temp directory', function()
itp('generate file name in Neovim own temp directory', function()
local dir = vim_gettempdir()
local file = vim_tempname()
assert.truthy(file:find('^' .. dir .. '[^/]*$'))