helpers.lua 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871
  1. local ffi = require('ffi')
  2. local formatc = require('test.unit.formatc')
  3. local Set = require('test.unit.set')
  4. local Preprocess = require('test.unit.preprocess')
  5. local Paths = require('test.cmakeconfig.paths')
  6. local global_helpers = require('test.helpers')
  7. local assert = require('luassert')
  8. local say = require('say')
  9. local posix = nil
  10. local syscall = nil
  11. local check_cores = global_helpers.check_cores
  12. local dedent = global_helpers.dedent
  13. local neq = global_helpers.neq
  14. local map = global_helpers.tbl_map
  15. local eq = global_helpers.eq
  16. local trim = global_helpers.trim
  17. -- C constants.
  18. local NULL = ffi.cast('void*', 0)
  19. local OK = 1
  20. local FAIL = 0
  21. local cimport
  22. -- add some standard header locations
  23. for _, p in ipairs(Paths.include_paths) do
  24. Preprocess.add_to_include_path(p)
  25. end
  26. local child_pid = nil
  27. local function only_separate(func)
  28. return function(...)
  29. if child_pid ~= 0 then
  30. error('This function must be run in a separate process only')
  31. end
  32. return func(...)
  33. end
  34. end
  35. local child_calls_init = {}
  36. local child_calls_mod = nil
  37. local child_calls_mod_once = nil
  38. local function child_call(func, ret)
  39. return function(...)
  40. local child_calls = child_calls_mod or child_calls_init
  41. if child_pid ~= 0 then
  42. child_calls[#child_calls + 1] = {func=func, args={...}}
  43. return ret
  44. else
  45. return func(...)
  46. end
  47. end
  48. end
  49. -- Run some code at the start of the child process, before running the test
  50. -- itself. Is supposed to be run in `before_each`.
  51. local function child_call_once(func, ...)
  52. if child_pid ~= 0 then
  53. child_calls_mod_once[#child_calls_mod_once + 1] = {
  54. func=func, args={...}}
  55. else
  56. func(...)
  57. end
  58. end
  59. local child_cleanups_mod_once = nil
  60. -- Run some code at the end of the child process, before exiting. Is supposed to
  61. -- be run in `before_each` because `after_each` is run after child has exited.
  62. local function child_cleanup_once(func, ...)
  63. local child_cleanups = child_cleanups_mod_once
  64. if child_pid ~= 0 then
  65. child_cleanups[#child_cleanups + 1] = {func=func, args={...}}
  66. else
  67. func(...)
  68. end
  69. end
  70. local libnvim = nil
  71. local lib = setmetatable({}, {
  72. __index = only_separate(function(_, idx)
  73. return libnvim[idx]
  74. end),
  75. __newindex = child_call(function(_, idx, val)
  76. libnvim[idx] = val
  77. end),
  78. })
  79. local init = only_separate(function()
  80. -- load neovim shared library
  81. libnvim = ffi.load(Paths.test_libnvim_path)
  82. for _, c in ipairs(child_calls_init) do
  83. c.func(unpack(c.args))
  84. end
  85. libnvim.time_init()
  86. libnvim.fs_init()
  87. libnvim.event_init()
  88. libnvim.early_init(nil)
  89. if child_calls_mod then
  90. for _, c in ipairs(child_calls_mod) do
  91. c.func(unpack(c.args))
  92. end
  93. end
  94. if child_calls_mod_once then
  95. for _, c in ipairs(child_calls_mod_once) do
  96. c.func(unpack(c.args))
  97. end
  98. child_calls_mod_once = nil
  99. end
  100. end)
  101. local deinit = only_separate(function()
  102. if child_cleanups_mod_once then
  103. for _, c in ipairs(child_cleanups_mod_once) do
  104. c.func(unpack(c.args))
  105. end
  106. child_cleanups_mod_once = nil
  107. end
  108. end)
  109. -- a Set that keeps around the lines we've already seen
  110. local cdefs_init = Set:new()
  111. local cdefs_mod = nil
  112. local imported = Set:new()
  113. local pragma_pack_id = 1
  114. -- some things are just too complex for the LuaJIT C parser to digest. We
  115. -- usually don't need them anyway.
  116. local function filter_complex_blocks(body)
  117. local result = {}
  118. for line in body:gmatch("[^\r\n]+") do
  119. if not (string.find(line, "(^)", 1, true) ~= nil
  120. or string.find(line, "_ISwupper", 1, true)
  121. or string.find(line, "_Float")
  122. or string.find(line, "msgpack_zone_push_finalizer")
  123. or string.find(line, "msgpack_unpacker_reserve_buffer")
  124. or string.find(line, "UUID_NULL") -- static const uuid_t UUID_NULL = {...}
  125. or string.find(line, "inline _Bool")) then
  126. result[#result + 1] = line
  127. end
  128. end
  129. return table.concat(result, "\n")
  130. end
  131. local cdef = ffi.cdef
  132. local cimportstr
  133. local previous_defines_init = ''
  134. local preprocess_cache_init = {}
  135. local previous_defines_mod = ''
  136. local preprocess_cache_mod = nil
  137. local function is_child_cdefs()
  138. return (os.getenv('NVIM_TEST_MAIN_CDEFS') ~= '1')
  139. end
  140. -- use this helper to import C files, you can pass multiple paths at once,
  141. -- this helper will return the C namespace of the nvim library.
  142. cimport = function(...)
  143. local previous_defines, preprocess_cache, cdefs
  144. if is_child_cdefs() and preprocess_cache_mod then
  145. preprocess_cache = preprocess_cache_mod
  146. previous_defines = previous_defines_mod
  147. cdefs = cdefs_mod
  148. else
  149. preprocess_cache = preprocess_cache_init
  150. previous_defines = previous_defines_init
  151. cdefs = cdefs_init
  152. end
  153. for _, path in ipairs({...}) do
  154. if not (path:sub(1, 1) == '/' or path:sub(1, 1) == '.'
  155. or path:sub(2, 2) == ':') then
  156. path = './' .. path
  157. end
  158. if not preprocess_cache[path] then
  159. local body
  160. body, previous_defines = Preprocess.preprocess(previous_defines, path)
  161. -- format it (so that the lines are "unique" statements), also filter out
  162. -- Objective-C blocks
  163. if os.getenv('NVIM_TEST_PRINT_I') == '1' then
  164. local lnum = 0
  165. for line in body:gmatch('[^\n]+') do
  166. lnum = lnum + 1
  167. print(lnum, line)
  168. end
  169. end
  170. body = formatc(body)
  171. body = filter_complex_blocks(body)
  172. -- add the formatted lines to a set
  173. local new_cdefs = Set:new()
  174. for line in body:gmatch("[^\r\n]+") do
  175. line = trim(line)
  176. -- give each #pragma pack an unique id, so that they don't get removed
  177. -- if they are inserted into the set
  178. -- (they are needed in the right order with the struct definitions,
  179. -- otherwise luajit has wrong memory layouts for the sturcts)
  180. if line:match("#pragma%s+pack") then
  181. line = line .. " // " .. pragma_pack_id
  182. pragma_pack_id = pragma_pack_id + 1
  183. end
  184. new_cdefs:add(line)
  185. end
  186. -- subtract the lines we've already imported from the new lines, then add
  187. -- the new unique lines to the old lines (so they won't be imported again)
  188. new_cdefs:diff(cdefs)
  189. cdefs:union(new_cdefs)
  190. -- request a sorted version of the new lines (same relative order as the
  191. -- original preprocessed file) and feed that to the LuaJIT ffi
  192. local new_lines = new_cdefs:to_table()
  193. if os.getenv('NVIM_TEST_PRINT_CDEF') == '1' then
  194. for lnum, line in ipairs(new_lines) do
  195. print(lnum, line)
  196. end
  197. end
  198. body = table.concat(new_lines, '\n')
  199. preprocess_cache[path] = body
  200. end
  201. cimportstr(preprocess_cache, path)
  202. end
  203. return lib
  204. end
  205. local cimport_immediate = function(...)
  206. local saved_pid = child_pid
  207. child_pid = 0
  208. local err, emsg = pcall(cimport, ...)
  209. child_pid = saved_pid
  210. if not err then
  211. emsg = tostring(emsg)
  212. io.stderr:write(emsg .. '\n')
  213. assert(false)
  214. else
  215. return lib
  216. end
  217. end
  218. local function _cimportstr(preprocess_cache, path)
  219. if imported:contains(path) then
  220. return lib
  221. end
  222. local body = preprocess_cache[path]
  223. if body == '' then
  224. return lib
  225. end
  226. cdef(body)
  227. imported:add(path)
  228. return lib
  229. end
  230. if is_child_cdefs() then
  231. cimportstr = child_call(_cimportstr, lib)
  232. else
  233. cimportstr = _cimportstr
  234. end
  235. local function alloc_log_new()
  236. local log = {
  237. log={},
  238. lib=cimport('./src/nvim/memory.h'),
  239. original_functions={},
  240. null={['\0:is_null']=true},
  241. }
  242. local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'}
  243. function log:save_original_functions()
  244. for _, funcname in ipairs(allocator_functions) do
  245. if not self.original_functions[funcname] then
  246. self.original_functions[funcname] = self.lib['mem_' .. funcname]
  247. end
  248. end
  249. end
  250. log.save_original_functions = child_call(log.save_original_functions)
  251. function log:set_mocks()
  252. for _, k in ipairs(allocator_functions) do
  253. do
  254. local kk = k
  255. self.lib['mem_' .. k] = function(...)
  256. local log_entry = {func=kk, args={...}}
  257. self.log[#self.log + 1] = log_entry
  258. if kk == 'free' then
  259. self.original_functions[kk](...)
  260. else
  261. log_entry.ret = self.original_functions[kk](...)
  262. end
  263. for i, v in ipairs(log_entry.args) do
  264. if v == nil then
  265. -- XXX This thing thinks that {NULL} ~= {NULL}.
  266. log_entry.args[i] = self.null
  267. end
  268. end
  269. if self.hook then self:hook(log_entry) end
  270. if log_entry.ret then
  271. return log_entry.ret
  272. end
  273. end
  274. end
  275. end
  276. end
  277. log.set_mocks = child_call(log.set_mocks)
  278. function log:clear()
  279. self.log = {}
  280. end
  281. function log:check(exp)
  282. eq(exp, self.log)
  283. self:clear()
  284. end
  285. function log:clear_tmp_allocs(clear_null_frees)
  286. local toremove = {}
  287. local allocs = {}
  288. for i, v in ipairs(self.log) do
  289. if v.func == 'malloc' or v.func == 'calloc' then
  290. allocs[tostring(v.ret)] = i
  291. elseif v.func == 'realloc' or v.func == 'free' then
  292. if allocs[tostring(v.args[1])] then
  293. toremove[#toremove + 1] = allocs[tostring(v.args[1])]
  294. if v.func == 'free' then
  295. toremove[#toremove + 1] = i
  296. end
  297. elseif clear_null_frees and v.args[1] == self.null then
  298. toremove[#toremove + 1] = i
  299. end
  300. if v.func == 'realloc' then
  301. allocs[tostring(v.ret)] = i
  302. end
  303. end
  304. end
  305. table.sort(toremove)
  306. for i = #toremove,1,-1 do
  307. table.remove(self.log, toremove[i])
  308. end
  309. end
  310. function log:restore_original_functions()
  311. -- Do nothing: set mocks live in a separate process
  312. return
  313. --[[
  314. [ for k, v in pairs(self.original_functions) do
  315. [ self.lib['mem_' .. k] = v
  316. [ end
  317. ]]
  318. end
  319. function log:setup()
  320. log:save_original_functions()
  321. log:set_mocks()
  322. end
  323. function log:before_each()
  324. return
  325. end
  326. function log:after_each()
  327. log:restore_original_functions()
  328. end
  329. log:setup()
  330. return log
  331. end
  332. -- take a pointer to a C-allocated string and return an interned
  333. -- version while also freeing the memory
  334. local function internalize(cdata, len)
  335. ffi.gc(cdata, ffi.C.free)
  336. return ffi.string(cdata, len)
  337. end
  338. local cstr = ffi.typeof('char[?]')
  339. local function to_cstr(string)
  340. return cstr(#string + 1, string)
  341. end
  342. local sc
  343. if posix ~= nil then
  344. sc = {
  345. fork = posix.fork,
  346. pipe = posix.pipe,
  347. read = posix.read,
  348. write = posix.write,
  349. close = posix.close,
  350. wait = posix.wait,
  351. exit = posix._exit,
  352. }
  353. elseif syscall ~= nil then
  354. sc = {
  355. fork = syscall.fork,
  356. pipe = function()
  357. local ret = {syscall.pipe()}
  358. return ret[3], ret[4]
  359. end,
  360. read = function(rd, len)
  361. return rd:read(nil, len)
  362. end,
  363. write = function(wr, s)
  364. return wr:write(s)
  365. end,
  366. close = function(p)
  367. return p:close()
  368. end,
  369. wait = syscall.wait,
  370. exit = syscall.exit,
  371. }
  372. else
  373. cimport_immediate('./test/unit/fixtures/posix.h')
  374. sc = {
  375. fork = function()
  376. return tonumber(ffi.C.fork())
  377. end,
  378. pipe = function()
  379. local ret = ffi.new('int[2]', {-1, -1})
  380. ffi.errno(0)
  381. local res = ffi.C.pipe(ret)
  382. if (res ~= 0) then
  383. local err = ffi.errno(0)
  384. assert(res == 0, ("pipe() error: %u: %s"):format(
  385. err, ffi.string(ffi.C.strerror(err))))
  386. end
  387. assert(ret[0] ~= -1 and ret[1] ~= -1)
  388. return ret[0], ret[1]
  389. end,
  390. read = function(rd, len)
  391. local ret = ffi.new('char[?]', len, {0})
  392. local total_bytes_read = 0
  393. ffi.errno(0)
  394. while total_bytes_read < len do
  395. local bytes_read = tonumber(ffi.C.read(
  396. rd,
  397. ffi.cast('void*', ret + total_bytes_read),
  398. len - total_bytes_read))
  399. if bytes_read == -1 then
  400. local err = ffi.errno(0)
  401. if err ~= ffi.C.kPOSIXErrnoEINTR then
  402. assert(false, ("read() error: %u: %s"):format(
  403. err, ffi.string(ffi.C.strerror(err))))
  404. end
  405. elseif bytes_read == 0 then
  406. break
  407. else
  408. total_bytes_read = total_bytes_read + bytes_read
  409. end
  410. end
  411. return ffi.string(ret, total_bytes_read)
  412. end,
  413. write = function(wr, s)
  414. local wbuf = to_cstr(s)
  415. local total_bytes_written = 0
  416. ffi.errno(0)
  417. while total_bytes_written < #s do
  418. local bytes_written = tonumber(ffi.C.write(
  419. wr,
  420. ffi.cast('void*', wbuf + total_bytes_written),
  421. #s - total_bytes_written))
  422. if bytes_written == -1 then
  423. local err = ffi.errno(0)
  424. if err ~= ffi.C.kPOSIXErrnoEINTR then
  425. assert(false, ("write() error: %u: %s ('%s')"):format(
  426. err, ffi.string(ffi.C.strerror(err)), s))
  427. end
  428. elseif bytes_written == 0 then
  429. break
  430. else
  431. total_bytes_written = total_bytes_written + bytes_written
  432. end
  433. end
  434. return total_bytes_written
  435. end,
  436. close = ffi.C.close,
  437. wait = function(pid)
  438. ffi.errno(0)
  439. while true do
  440. local r = ffi.C.waitpid(pid, nil, ffi.C.kPOSIXWaitWUNTRACED)
  441. if r == -1 then
  442. local err = ffi.errno(0)
  443. if err == ffi.C.kPOSIXErrnoECHILD then
  444. break
  445. elseif err ~= ffi.C.kPOSIXErrnoEINTR then
  446. assert(false, ("waitpid() error: %u: %s"):format(
  447. err, ffi.string(ffi.C.strerror(err))))
  448. end
  449. else
  450. assert(r == pid)
  451. end
  452. end
  453. end,
  454. exit = ffi.C._exit,
  455. }
  456. end
  457. local function format_list(lst)
  458. local ret = ''
  459. for _, v in ipairs(lst) do
  460. if ret ~= '' then ret = ret .. ', ' end
  461. ret = ret .. assert:format({v, n=1})[1]
  462. end
  463. return ret
  464. end
  465. if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then
  466. for k_, v_ in pairs(sc) do
  467. (function(k, v)
  468. sc[k] = function(...)
  469. local rets = {v(...)}
  470. io.stderr:write(('%s(%s) = %s\n'):format(k, format_list({...}),
  471. format_list(rets)))
  472. return unpack(rets)
  473. end
  474. end)(k_, v_)
  475. end
  476. end
  477. local function just_fail(_)
  478. return false
  479. end
  480. say:set('assertion.just_fail.positive', '%s')
  481. say:set('assertion.just_fail.negative', '%s')
  482. assert:register('assertion', 'just_fail', just_fail,
  483. 'assertion.just_fail.positive',
  484. 'assertion.just_fail.negative')
  485. local hook_fnamelen = 30
  486. local hook_sfnamelen = 30
  487. local hook_numlen = 5
  488. local hook_msglen = 1 + 1 + 1 + (1 + hook_fnamelen) + (1 + hook_sfnamelen) + (1 + hook_numlen) + 1
  489. local tracehelp = dedent([[
  490. Trace: either in the format described below or custom debug output starting
  491. with `>`. Latter lines still have the same width in byte.
  492. ┌ Trace type: _r_eturn from function , function _c_all, _l_ine executed,
  493. │ _t_ail return, _C_ount (should not actually appear),
  494. │ _s_aved from previous run for reference, _>_ for custom debug
  495. │ output.
  496. │┏ Function type: _L_ua function, _C_ function, _m_ain part of chunk,
  497. │┃ function that did _t_ail call.
  498. │┃┌ Function name type: _g_lobal, _l_ocal, _m_ethod, _f_ield, _u_pvalue,
  499. │┃│ space for unknown.
  500. │┃│ ┏ Source file name ┌ Function name ┏ Line
  501. │┃│ ┃ (trunc to 30 bytes, no .lua) │ (truncated to last 30 bytes) ┃ number
  502. CWN SSSSSSSSSSSSSSSSSSSSSSSSSSSSSS:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:LLLLL\n
  503. ]])
  504. local function child_sethook(wr)
  505. local trace_level = os.getenv('NVIM_TEST_TRACE_LEVEL')
  506. if not trace_level or trace_level == '' then
  507. trace_level = 0
  508. else
  509. trace_level = tonumber(trace_level)
  510. end
  511. if trace_level <= 0 then
  512. return
  513. end
  514. local trace_only_c = trace_level <= 1
  515. local prev_info, prev_reason, prev_lnum
  516. local function hook(reason, lnum, use_prev)
  517. local info = nil
  518. if use_prev then
  519. info = prev_info
  520. elseif reason ~= 'tail return' then -- tail return
  521. info = debug.getinfo(2, 'nSl')
  522. end
  523. if trace_only_c and (not info or info.what ~= 'C') and not use_prev then
  524. if info.source:sub(-9) == '_spec.lua' then
  525. prev_info = info
  526. prev_reason = 'saved'
  527. prev_lnum = lnum
  528. end
  529. return
  530. end
  531. if trace_only_c and not use_prev and prev_reason then
  532. hook(prev_reason, prev_lnum, true)
  533. prev_reason = nil
  534. end
  535. local whatchar = ' '
  536. local namewhatchar = ' '
  537. local funcname = ''
  538. local source = ''
  539. local msgchar = reason:sub(1, 1)
  540. if reason == 'count' then
  541. msgchar = 'C'
  542. end
  543. if info then
  544. funcname = (info.name or ''):sub(1, hook_fnamelen)
  545. whatchar = info.what:sub(1, 1)
  546. namewhatchar = info.namewhat:sub(1, 1)
  547. if namewhatchar == '' then
  548. namewhatchar = ' '
  549. end
  550. source = info.source
  551. if source:sub(1, 1) == '@' then
  552. if source:sub(-4, -1) == '.lua' then
  553. source = source:sub(1, -5)
  554. end
  555. source = source:sub(-hook_sfnamelen, -1)
  556. end
  557. lnum = lnum or info.currentline
  558. end
  559. -- assert(-1 <= lnum and lnum <= 99999)
  560. local lnum_s
  561. if lnum == -1 then
  562. lnum_s = 'nknwn'
  563. else
  564. lnum_s = ('%u'):format(lnum)
  565. end
  566. local msg = ( -- lua does not support %*
  567. ''
  568. .. msgchar
  569. .. whatchar
  570. .. namewhatchar
  571. .. ' '
  572. .. source .. (' '):rep(hook_sfnamelen - #source)
  573. .. ':'
  574. .. funcname .. (' '):rep(hook_fnamelen - #funcname)
  575. .. ':'
  576. .. ('0'):rep(hook_numlen - #lnum_s) .. lnum_s
  577. .. '\n'
  578. )
  579. -- eq(hook_msglen, #msg)
  580. sc.write(wr, msg)
  581. end
  582. debug.sethook(hook, 'crl')
  583. end
  584. local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2))
  585. local _debug_log
  586. local debug_log = only_separate(function(...)
  587. return _debug_log(...)
  588. end)
  589. local function itp_child(wr, func)
  590. _debug_log = function(s)
  591. s = s:sub(1, hook_msglen - 2)
  592. sc.write(wr, '>' .. s .. (' '):rep(hook_msglen - 2 - #s) .. '\n')
  593. end
  594. local status, result = pcall(init)
  595. if status then
  596. collectgarbage('stop')
  597. child_sethook(wr)
  598. status, result = pcall(func)
  599. debug.sethook()
  600. end
  601. sc.write(wr, trace_end_msg)
  602. if not status then
  603. local emsg = tostring(result)
  604. if #emsg > 99999 then
  605. emsg = emsg:sub(1, 99999)
  606. end
  607. sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg))
  608. deinit()
  609. else
  610. sc.write(wr, '+\n')
  611. deinit()
  612. end
  613. collectgarbage('restart')
  614. collectgarbage()
  615. sc.write(wr, '$\n')
  616. sc.close(wr)
  617. sc.exit(status and 0 or 1)
  618. end
  619. local function check_child_err(rd)
  620. local trace = {}
  621. local did_traceline = false
  622. local maxtrace = tonumber(os.getenv('NVIM_TEST_MAXTRACE')) or 1024
  623. while true do
  624. local traceline = sc.read(rd, hook_msglen)
  625. if #traceline ~= hook_msglen then
  626. if #traceline == 0 then
  627. break
  628. else
  629. trace[#trace + 1] = 'Partial read: <' .. trace .. '>\n'
  630. end
  631. end
  632. if traceline == trace_end_msg then
  633. did_traceline = true
  634. break
  635. end
  636. trace[#trace + 1] = traceline
  637. if #trace > maxtrace then
  638. table.remove(trace, 1)
  639. end
  640. end
  641. local res = sc.read(rd, 2)
  642. if #res == 2 then
  643. local err = ''
  644. if res ~= '+\n' then
  645. eq('-\n', res)
  646. local len_s = sc.read(rd, 5)
  647. local len = tonumber(len_s)
  648. neq(0, len)
  649. if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then
  650. err = '\nTest failed, trace:\n' .. tracehelp
  651. for _, traceline in ipairs(trace) do
  652. err = err .. traceline
  653. end
  654. end
  655. err = err .. sc.read(rd, len + 1)
  656. end
  657. local eres = sc.read(rd, 2)
  658. if eres ~= '$\n' then
  659. if #trace == 0 then
  660. err = '\nTest crashed, no trace available (check NVIM_TEST_TRACE_LEVEL)\n'
  661. else
  662. err = '\nTest crashed, trace:\n' .. tracehelp
  663. for i = 1, #trace do
  664. err = err .. trace[i]
  665. end
  666. end
  667. if not did_traceline then
  668. err = err .. '\nNo end of trace occurred'
  669. end
  670. local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true)
  671. if not cc_err then
  672. err = err .. '\ncheck_cores failed: ' .. cc_emsg
  673. end
  674. end
  675. if err ~= '' then
  676. assert.just_fail(err)
  677. end
  678. end
  679. end
  680. local function itp_parent(rd, pid, allow_failure)
  681. local err, emsg = pcall(check_child_err, rd)
  682. sc.wait(pid)
  683. sc.close(rd)
  684. if not err then
  685. if allow_failure then
  686. io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n')
  687. os.execute([[
  688. sh -c "source ci/common/test.sh
  689. check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]])
  690. else
  691. error(emsg)
  692. end
  693. end
  694. end
  695. local function gen_itp(it)
  696. child_calls_mod = {}
  697. child_calls_mod_once = {}
  698. child_cleanups_mod_once = {}
  699. preprocess_cache_mod = map(function(v) return v end, preprocess_cache_init)
  700. previous_defines_mod = previous_defines_init
  701. cdefs_mod = cdefs_init:copy()
  702. local function itp(name, func, allow_failure)
  703. if allow_failure and os.getenv('NVIM_TEST_RUN_FAILING_TESTS') ~= '1' then
  704. -- FIXME Fix tests with this true
  705. return
  706. end
  707. it(name, function()
  708. local rd, wr = sc.pipe()
  709. child_pid = sc.fork()
  710. if child_pid == 0 then
  711. sc.close(rd)
  712. itp_child(wr, func)
  713. else
  714. sc.close(wr)
  715. local saved_child_pid = child_pid
  716. child_pid = nil
  717. itp_parent(rd, saved_child_pid, allow_failure)
  718. end
  719. end)
  720. end
  721. return itp
  722. end
  723. local function cppimport(path)
  724. return cimport(Paths.test_source_path .. '/test/includes/pre/' .. path)
  725. end
  726. cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h',
  727. './src/nvim/os/fs.h')
  728. local function conv_enum(etab, eval)
  729. local n = tonumber(eval)
  730. return etab[n] or n
  731. end
  732. local function array_size(arr)
  733. return ffi.sizeof(arr) / ffi.sizeof(arr[0])
  734. end
  735. local function kvi_size(kvi)
  736. return array_size(kvi.init_array)
  737. end
  738. local function kvi_init(kvi)
  739. kvi.capacity = kvi_size(kvi)
  740. kvi.items = kvi.init_array
  741. return kvi
  742. end
  743. local function kvi_destroy(kvi)
  744. if kvi.items ~= kvi.init_array then
  745. lib.xfree(kvi.items)
  746. end
  747. end
  748. local function kvi_new(ct)
  749. return kvi_init(ffi.new(ct))
  750. end
  751. local function make_enum_conv_tab(m, values, skip_pref, set_cb)
  752. child_call_once(function()
  753. local ret = {}
  754. for _, v in ipairs(values) do
  755. local str_v = v
  756. if v:sub(1, #skip_pref) == skip_pref then
  757. str_v = v:sub(#skip_pref + 1)
  758. end
  759. ret[tonumber(m[v])] = str_v
  760. end
  761. set_cb(ret)
  762. end)
  763. end
  764. local function ptr2addr(ptr)
  765. return tonumber(ffi.cast('intptr_t', ffi.cast('void *', ptr)))
  766. end
  767. local s = ffi.new('char[64]', {0})
  768. local function ptr2key(ptr)
  769. ffi.C.snprintf(s, ffi.sizeof(s), '%p', ffi.cast('void *', ptr))
  770. return ffi.string(s)
  771. end
  772. local module = {
  773. cimport = cimport,
  774. cppimport = cppimport,
  775. internalize = internalize,
  776. ffi = ffi,
  777. lib = lib,
  778. cstr = cstr,
  779. to_cstr = to_cstr,
  780. NULL = NULL,
  781. OK = OK,
  782. FAIL = FAIL,
  783. alloc_log_new = alloc_log_new,
  784. gen_itp = gen_itp,
  785. only_separate = only_separate,
  786. child_call_once = child_call_once,
  787. child_cleanup_once = child_cleanup_once,
  788. sc = sc,
  789. conv_enum = conv_enum,
  790. array_size = array_size,
  791. kvi_destroy = kvi_destroy,
  792. kvi_size = kvi_size,
  793. kvi_init = kvi_init,
  794. kvi_new = kvi_new,
  795. make_enum_conv_tab = make_enum_conv_tab,
  796. ptr2addr = ptr2addr,
  797. ptr2key = ptr2key,
  798. debug_log = debug_log,
  799. }
  800. module = global_helpers.tbl_extend('error', module, global_helpers)
  801. return function()
  802. return module
  803. end