diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/README.md | 13 | ||||
-rw-r--r-- | test/functional/helpers.lua | 23 | ||||
-rw-r--r-- | test/functional/options/pastetoggle_spec.lua | 37 | ||||
-rw-r--r-- | test/helpers.lua | 23 | ||||
-rw-r--r-- | test/unit/helpers.lua | 236 | ||||
-rw-r--r-- | test/unit/os/fileio_spec.lua | 19 | ||||
-rw-r--r-- | test/unit/testtest_spec.lua | 19 |
7 files changed, 298 insertions, 72 deletions
diff --git a/test/README.md b/test/README.md index df66f24626..2857cc0ecf 100644 --- a/test/README.md +++ b/test/README.md @@ -27,8 +27,8 @@ groups by the semantic component they are testing. 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): +treated as Integer; when defined, treated as String; when defined, treated as +Number; !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 @@ -99,3 +99,12 @@ get backtrace from). 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. + +`NVIM_TEST_RUN_TESTTEST` (U) (1): allows running `test/unit/testtest_spec.lua` +used to check how testing infrastructure works. + +`NVIM_TEST_TRACE_LEVEL` (U) (N): specifies unit tests tracing level: `0` +disables tracing (the fastest, but you get no data if tests crash and there was +no core dump generated), `1` or empty/undefined leaves only C function cals and +returns in the trace (faster then recording everything), `2` records all +function calls, returns and lua source lines exuecuted. diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index ab36508262..335cf3c3ff 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -16,6 +16,7 @@ local eq = global_helpers.eq local ok = global_helpers.ok local map = global_helpers.map local filter = global_helpers.filter +local dedent = global_helpers.dedent local start_dir = lfs.currentdir() -- XXX: NVIM_PROG takes precedence, QuickBuild sets it. @@ -191,28 +192,6 @@ local function nvim_feed(input) end end -local function dedent(str) - -- find minimum common indent across lines - local indent = nil - for line in str:gmatch('[^\n]+') do - local line_indent = line:match('^%s+') or '' - if indent == nil or #line_indent < #indent then - indent = line_indent - end - end - if indent == nil or #indent == 0 then - -- no minimum common indent - return str - end - -- create a pattern for the indent - indent = indent:gsub('%s', '[ \t]') - -- strip it from the first line - str = str:gsub('^'..indent, '') - -- strip it from the remaining lines - str = str:gsub('[\n]'..indent, '\n') - return str -end - local function feed(...) for _, v in ipairs({...}) do nvim_feed(dedent(v)) diff --git a/test/functional/options/pastetoggle_spec.lua b/test/functional/options/pastetoggle_spec.lua new file mode 100644 index 0000000000..e449df31f5 --- /dev/null +++ b/test/functional/options/pastetoggle_spec.lua @@ -0,0 +1,37 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local feed = helpers.feed +local execute = helpers.execute +local eq = helpers.eq +local eval = helpers.eval +local sleep = helpers.sleep + +describe("'pastetoggle' option", function() + before_each(function() + clear() + execute('set nopaste') + execute('set pastetoggle=a') + end) + it("toggles 'paste'", function() + eq(eval('&paste'), 0) + feed('a') + -- Need another key so that the vgetorpeek() function returns. + feed('j') + eq(eval('&paste'), 1) + end) + it("multiple key 'pastetoggle' is waited for", function() + eq(eval('&paste'), 0) + local pastetoggle = 'lllll' + execute('set pastetoggle=' .. pastetoggle) + execute('set timeoutlen=1', 'set ttimoutlen=10000') + feed(pastetoggle:sub(0, 2)) + -- sleep() for long enough that vgetorpeek() is gotten into, but short + -- enough that ttimeoutlen is not reached. + sleep(200) + feed(pastetoggle:sub(3, -1)) + -- Need another key so that the vgetorpeek() function returns. + feed('j') + eq(eval('&paste'), 1) + end) +end) diff --git a/test/helpers.lua b/test/helpers.lua index 82451bc61d..3fc10e9e30 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -251,6 +251,28 @@ local function concat_tables(...) return ret end +local function dedent(str) + -- find minimum common indent across lines + local indent = nil + for line in str:gmatch('[^\n]+') do + local line_indent = line:match('^%s+') or '' + if indent == nil or #line_indent < #indent then + indent = line_indent + end + end + if indent == nil or #indent == 0 then + -- no minimum common indent + return str + end + -- create a pattern for the indent + indent = indent:gsub('%s', '[ \t]') + -- strip it from the first line + str = str:gsub('^'..indent, '') + -- strip it from the remaining lines + str = str:gsub('[\n]'..indent, '\n') + return str +end + return { eq = eq, neq = neq, @@ -265,4 +287,5 @@ return { hasenv = hasenv, which = which, concat_tables = concat_tables, + dedent = dedent, } diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index f4e6194fc7..93e1e91173 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -11,6 +11,7 @@ local posix = nil local syscall = nil local check_cores = global_helpers.check_cores +local dedent = global_helpers.dedent local neq = global_helpers.neq local map = global_helpers.map local eq = global_helpers.eq @@ -511,6 +512,189 @@ if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then end end +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 hook_fnamelen = 30 +local hook_sfnamelen = 30 +local hook_numlen = 5 +local hook_msglen = 1 + 1 + 1 + (1 + hook_fnamelen) + (1 + hook_sfnamelen) + (1 + hook_numlen) + 1 + +local tracehelp = dedent([[ + ┌ Trace type: _r_eturn from function , function _c_all, _l_ine executed, + │ _t_ail return, _C_ount (should not actually appear). + │┏ Function type: _L_ua function, _C_ function, _m_ain part of chunk, + │┃ function that did _t_ail call. + │┃┌ Function name type: _g_lobal, _l_ocal, _m_ethod, _f_ield, _u_pvalue, + │┃│ space for unknown. + │┃│ ┏ Source file name ┌ Function name ┏ Line + │┃│ ┃ (trunc to 30 bytes, no .lua) │ (truncated to last 30 bytes) ┃ number + CWN SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:LLLLL\n +]]) + +local function child_sethook(wr) + local trace_level = os.getenv('NVIM_TEST_TRACE_LEVEL') + if not trace_level or trace_level == '' then + trace_level = 1 + else + trace_level = tonumber(trace_level) + end + if trace_level <= 0 then + return + end + local trace_only_c = trace_level <= 1 + local function hook(reason, lnum) + local info = nil + if reason ~= 'tail return' then -- tail return + info = debug.getinfo(2, 'nSl') + end + + if trace_only_c and (not info or info.what ~= 'C') then + return + end + + local whatchar = ' ' + local namewhatchar = ' ' + local funcname = '' + local source = '' + local msgchar = reason:sub(1, 1) + + if reason == 'count' then + msgchar = 'C' + end + + if info then + funcname = (info.name or ''):sub(1, hook_fnamelen) + whatchar = info.what:sub(1, 1) + namewhatchar = info.namewhat:sub(1, 1) + if namewhatchar == '' then + namewhatchar = ' ' + end + source = info.source + if source:sub(1, 1) == '@' then + if source:sub(-4, -1) == '.lua' then + source = source:sub(1, -5) + end + source = source:sub(-hook_sfnamelen, -1) + end + lnum = lnum or info.currentline + end + + -- assert(-1 <= lnum and lnum <= 99999) + local lnum_s + if lnum == -1 then + lnum_s = 'nknwn' + else + lnum_s = ('%u'):format(lnum) + end + local msg = ( -- lua does not support %* + '' + .. msgchar + .. whatchar + .. namewhatchar + .. ' ' + .. source .. (' '):rep(hook_sfnamelen - #source) + .. ':' + .. funcname .. (' '):rep(hook_fnamelen - #funcname) + .. ':' + .. ('0'):rep(hook_numlen - #lnum_s) .. lnum_s + .. '\n' + ) + -- eq(hook_msglen, #msg) + sc.write(wr, msg) + end + debug.sethook(hook, trace_only_c and 'cr' or 'crl') +end + +local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2)) + +local function itp_child(wr, func) + init() + collectgarbage('stop') + child_sethook(wr) + local err, emsg = pcall(func) + debug.sethook() + collectgarbage('restart') + emsg = tostring(emsg) + sc.write(wr, trace_end_msg) + if not err then + if #emsg > 99999 then + emsg = emsg:sub(1, 99999) + end + 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 +end + +local function check_child_err(rd) + local trace = {} + while true do + local traceline = sc.read(rd, hook_msglen) + if #traceline ~= hook_msglen then + if #traceline == 0 then + break + else + trace[#trace + 1] = 'Partial read: <' .. trace .. '>\n' + end + end + if traceline == trace_end_msg then + break + end + trace[#trace + 1] = traceline + end + local res = sc.read(rd, 2) + if #res ~= 2 then + local error + if #trace == 0 then + error = '\nTest crashed, no trace available\n' + else + error = '\nTest crashed, trace:\n' .. tracehelp + for i = 1, #trace do + error = error .. trace[i] + end + end + assert.just_fail(error) + end + 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 function itp_parent(rd, pid, allow_failure) + local err, emsg = pcall(check_child_err, rd) + sc.wait(pid) + 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 + local function gen_itp(it) child_calls_mod = {} child_calls_mod_once = {} @@ -518,14 +702,6 @@ local function gen_itp(it) 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 @@ -535,50 +711,13 @@ local function gen_itp(it) 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 + itp_child(wr, func) else sc.close(wr) - sc.wait(child_pid) + local saved_child_pid = 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 + itp_parent(rd, saved_child_pid, allow_failure) end end) end @@ -610,6 +749,7 @@ local module = { only_separate = only_separate, child_call_once = child_call_once, child_cleanup_once = child_cleanup_once, + sc = sc, } return function(after_each) if after_each then diff --git a/test/unit/os/fileio_spec.lua b/test/unit/os/fileio_spec.lua index 7a738ce85c..5e1b2523fa 100644 --- a/test/unit/os/fileio_spec.lua +++ b/test/unit/os/fileio_spec.lua @@ -80,6 +80,10 @@ local function file_read(fp, size) return ret1, ret2 end +local function file_flush(fp) + return m.file_flush(fp) +end + local function file_fsync(fp) return m.file_fsync(fp) end @@ -244,6 +248,21 @@ describe('file_fsync', function() end) end) +describe('file_flush', function() + itp('can flush writes to disk', function() + local err, fp = file_open(filec, m.kFileCreateOnly, 384) + eq(0, file_flush(fp)) + eq(0, err) + eq(0, lfs.attributes(filec).size) + local wsize = file_write(fp, 'test') + eq(4, wsize) + eq(0, lfs.attributes(filec).size) + eq(0, file_flush(fp)) + eq(wsize, lfs.attributes(filec).size) + eq(0, m.file_close(fp)) + end) +end) + describe('file_read', function() itp('can read small chunks of input until eof', function() local err, fp = file_open(file1, 0, 384) diff --git a/test/unit/testtest_spec.lua b/test/unit/testtest_spec.lua new file mode 100644 index 0000000000..d2f3632b6f --- /dev/null +++ b/test/unit/testtest_spec.lua @@ -0,0 +1,19 @@ +local helpers = require('test.unit.helpers')(after_each) +local assert = require('luassert') + +local itp = helpers.gen_itp(it) + +local sc = helpers.sc + +-- All of the below tests must fail. Check how exactly they fail. +if os.getenv('NVIM_TEST_RUN_TESTTEST') ~= '1' then + return +end +describe('test code', function() + itp('does not hang when working with lengthy errors', function() + assert.just_fail(('x'):rep(65536)) + end) + itp('shows trace after exiting abnormally', function() + sc.exit(0) + end) +end) |