marktree_spec.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. local t = require('test.unit.testutil')
  2. local itp = t.gen_itp(it)
  3. local ffi = t.ffi
  4. local eq = t.eq
  5. local ok = t.ok
  6. local lib = t.cimport('./src/nvim/marktree.h')
  7. local function tablelength(tbl)
  8. local count = 0
  9. for _ in pairs(tbl) do
  10. count = count + 1
  11. end
  12. return count
  13. end
  14. local function pos_leq(a, b)
  15. return a[1] < b[1] or (a[1] == b[1] and a[2] <= b[2])
  16. end
  17. -- Checks that shadow and tree is consistent, and optionally
  18. -- return the order
  19. local function shadoworder(tree, shadow, iter, giveorder)
  20. ok(iter ~= nil)
  21. local status = lib.marktree_itr_first(tree, iter)
  22. local count = 0
  23. local pos2id, id2pos = {}, {}
  24. local last
  25. if not status and next(shadow) == nil then
  26. return pos2id, id2pos
  27. end
  28. repeat
  29. local mark = lib.marktree_itr_current(iter)
  30. local id = tonumber(mark.id)
  31. local spos = shadow[id]
  32. eq(mark.pos.row, spos[1], mark.id)
  33. eq(mark.pos.col, spos[2], mark.id)
  34. if lib.mt_right_test(mark) ~= spos[3] then
  35. error('invalid gravity for ' .. id .. ':(' .. mark.pos.row .. ', ' .. mark.pos.col .. ')')
  36. end
  37. if count > 0 then
  38. if not pos_leq(last, spos) then
  39. error('DISORDER')
  40. end
  41. end
  42. count = count + 1
  43. last = spos
  44. if giveorder then
  45. pos2id[count] = id
  46. id2pos[id] = count
  47. end
  48. until not lib.marktree_itr_next(tree, iter)
  49. local shadowlen = tablelength(shadow)
  50. if shadowlen ~= count then
  51. error('missed some keys? (shadow ' .. shadowlen .. ', tree ' .. count .. ')')
  52. end
  53. return id2pos, pos2id
  54. end
  55. local function shadowsplice(shadow, start, old_extent, new_extent)
  56. local old_end = {
  57. start[1] + old_extent[1],
  58. (old_extent[1] == 0 and start[2] or 0) + old_extent[2],
  59. }
  60. local new_end = {
  61. start[1] + new_extent[1],
  62. (new_extent[1] == 0 and start[2] or 0) + new_extent[2],
  63. }
  64. local delta = { new_end[1] - old_end[1], new_end[2] - old_end[2] }
  65. for _, pos in pairs(shadow) do
  66. if pos_leq(start, pos) then
  67. if pos_leq(pos, old_end) then
  68. -- delete region
  69. if pos[3] then -- right gravity
  70. pos[1], pos[2] = new_end[1], new_end[2]
  71. else
  72. pos[1], pos[2] = start[1], start[2]
  73. end
  74. else
  75. if pos[1] == old_end[1] then
  76. pos[2] = pos[2] + delta[2]
  77. end
  78. pos[1] = pos[1] + delta[1]
  79. end
  80. end
  81. end
  82. end
  83. local function dosplice(tree, shadow, start, old, new)
  84. lib.marktree_splice(tree, start[1], start[2], old[1], old[2], new[1], new[2])
  85. shadowsplice(shadow, start, old, new)
  86. end
  87. local ns = 10
  88. local last_id = nil
  89. local function put(tree, row, col, gravity, end_row, end_col, end_gravity)
  90. last_id = last_id + 1
  91. local my_id = last_id
  92. end_row = end_row or -1
  93. end_col = end_col or -1
  94. end_gravity = end_gravity or false
  95. lib.marktree_put_test(tree, ns, my_id, row, col, gravity, end_row, end_col, end_gravity, false)
  96. return my_id
  97. end
  98. local function put_meta(tree, row, col, gravitate, meta)
  99. last_id = last_id + 1
  100. local my_id = last_id
  101. lib.marktree_put_test(tree, ns, my_id, row, col, gravitate, -1, -1, false, meta)
  102. return my_id
  103. end
  104. describe('marktree', function()
  105. before_each(function()
  106. last_id = 0
  107. end)
  108. itp('works', function()
  109. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  110. local shadow = {}
  111. local iter = ffi.new('MarkTreeIter[1]')
  112. local iter2 = ffi.new('MarkTreeIter[1]')
  113. for i = 1, 100 do
  114. for j = 1, 100 do
  115. local gravitate = (i % 2) > 0
  116. local id = put(tree, j, i, gravitate)
  117. ok(id > 0)
  118. eq(nil, shadow[id])
  119. shadow[id] = { j, i, gravitate }
  120. end
  121. -- checking every insert is too slow, but this is ok
  122. lib.marktree_check(tree)
  123. end
  124. -- ss = lib.mt_inspect_rec(tree)
  125. -- io.stdout:write(ffi.string(ss))
  126. -- io.stdout:flush()
  127. local id2pos, pos2id = shadoworder(tree, shadow, iter)
  128. eq({}, pos2id) -- not set if not requested
  129. eq({}, id2pos)
  130. for i, ipos in pairs(shadow) do
  131. local p = lib.marktree_lookup_ns(tree, ns, i, false, iter)
  132. eq(ipos[1], p.pos.row)
  133. eq(ipos[2], p.pos.col)
  134. local k = lib.marktree_itr_current(iter)
  135. eq(ipos[1], k.pos.row)
  136. eq(ipos[2], k.pos.col, ipos[1])
  137. lib.marktree_itr_next(tree, iter)
  138. -- TODO(bfredl): use id2pos to check neighbour?
  139. -- local k2 = lib.marktree_itr_current(iter)
  140. end
  141. for i, ipos in pairs(shadow) do
  142. lib.marktree_itr_get(tree, ipos[1], ipos[2], iter)
  143. local k = lib.marktree_itr_current(iter)
  144. eq(i, tonumber(k.id))
  145. eq(ipos[1], k.pos.row)
  146. eq(ipos[2], k.pos.col)
  147. end
  148. ok(lib.marktree_itr_first(tree, iter))
  149. local del = lib.marktree_itr_current(iter)
  150. lib.marktree_del_itr(tree, iter, false)
  151. shadow[tonumber(del.id)] = nil
  152. shadoworder(tree, shadow, iter)
  153. for _, ci in ipairs({ 0, -1, 1, -2, 2, -10, 10 }) do
  154. for i = 1, 100 do
  155. lib.marktree_itr_get(tree, i, 50 + ci, iter)
  156. local k = lib.marktree_itr_current(iter)
  157. local id = tonumber(k.id)
  158. eq(shadow[id][1], k.pos.row)
  159. eq(shadow[id][2], k.pos.col)
  160. lib.marktree_del_itr(tree, iter, false)
  161. shadow[id] = nil
  162. end
  163. lib.marktree_check(tree)
  164. shadoworder(tree, shadow, iter)
  165. end
  166. -- NB: this is quite rudimentary. We rely on
  167. -- functional tests exercising splicing quite a bit
  168. lib.marktree_check(tree)
  169. dosplice(tree, shadow, { 2, 2 }, { 0, 5 }, { 1, 2 })
  170. lib.marktree_check(tree)
  171. shadoworder(tree, shadow, iter)
  172. dosplice(tree, shadow, { 30, 2 }, { 30, 5 }, { 1, 2 })
  173. lib.marktree_check(tree)
  174. shadoworder(tree, shadow, iter)
  175. dosplice(tree, shadow, { 5, 3 }, { 0, 2 }, { 0, 5 })
  176. shadoworder(tree, shadow, iter)
  177. lib.marktree_check(tree)
  178. -- build then burn (HOORAY! HOORAY!)
  179. while next(shadow) do
  180. lib.marktree_itr_first(tree, iter)
  181. -- delete every other key for fun and profit
  182. while true do
  183. local k = lib.marktree_itr_current(iter)
  184. lib.marktree_del_itr(tree, iter, false)
  185. ok(shadow[tonumber(k.id)] ~= nil)
  186. shadow[tonumber(k.id)] = nil
  187. local stat = lib.marktree_itr_next(tree, iter)
  188. if not stat then
  189. break
  190. end
  191. end
  192. lib.marktree_check(tree)
  193. shadoworder(tree, shadow, iter2)
  194. end
  195. -- Check iterator validity for 2 specific edge cases:
  196. -- https://github.com/neovim/neovim/pull/14719
  197. lib.marktree_clear(tree)
  198. for i = 1, 20 do
  199. put(tree, i, i, false)
  200. end
  201. lib.marktree_itr_get(tree, 10, 10, iter)
  202. lib.marktree_del_itr(tree, iter, false)
  203. eq(11, iter[0].x.key[iter[0].i].pos.col)
  204. lib.marktree_itr_get(tree, 11, 11, iter)
  205. lib.marktree_del_itr(tree, iter, false)
  206. eq(12, iter[0].x.key[iter[0].i].pos.col)
  207. end)
  208. itp("'intersect_mov' function works correctly", function()
  209. local function mov(x, y, w)
  210. local xa = ffi.new('uint64_t[?]', #x)
  211. for i, xi in ipairs(x) do
  212. xa[i - 1] = xi
  213. end
  214. local ya = ffi.new('uint64_t[?]', #y)
  215. for i, yi in ipairs(y) do
  216. ya[i - 1] = yi
  217. end
  218. local wa = ffi.new('uint64_t[?]', #w)
  219. for i, wi in ipairs(w) do
  220. wa[i - 1] = wi
  221. end
  222. local dummy_size = #x + #y + #w
  223. local wouta = ffi.new('uint64_t[?]', dummy_size)
  224. local douta = ffi.new('uint64_t[?]', dummy_size)
  225. local wsize = ffi.new('size_t[1]')
  226. wsize[0] = dummy_size
  227. local dsize = ffi.new('size_t[1]')
  228. dsize[0] = dummy_size
  229. local status = lib.intersect_mov_test(xa, #x, ya, #y, wa, #w, wouta, wsize, douta, dsize)
  230. if status == 0 then
  231. error 'wowza'
  232. end
  233. local wout, dout = {}, {}
  234. for i = 0, tonumber(wsize[0]) - 1 do
  235. table.insert(wout, tonumber(wouta[i]))
  236. end
  237. for i = 0, tonumber(dsize[0]) - 1 do
  238. table.insert(dout, tonumber(douta[i]))
  239. end
  240. return { wout, dout }
  241. end
  242. eq({ {}, {} }, mov({}, { 2, 3 }, { 2, 3 }))
  243. eq({ { 2, 3 }, {} }, mov({}, {}, { 2, 3 }))
  244. eq({ { 2, 3 }, {} }, mov({ 2, 3 }, {}, {}))
  245. eq({ {}, { 2, 3 } }, mov({}, { 2, 3 }, {}))
  246. eq({ { 1, 5 }, {} }, mov({ 1, 2, 5 }, { 2, 3 }, { 3 }))
  247. eq({ { 1, 2 }, {} }, mov({ 1, 2, 5 }, { 5, 10 }, { 10 }))
  248. eq({ { 1, 2 }, { 5 } }, mov({ 1, 2 }, { 5, 10 }, { 10 }))
  249. eq({ { 1, 3, 5, 7, 9 }, { 2, 4, 6, 8, 10 } }, mov({ 1, 3, 5, 7, 9 }, { 2, 4, 6, 8, 10 }, {}))
  250. eq({ { 1, 3, 5, 7, 9 }, { 2, 6, 10 } }, mov({ 1, 3, 5, 7, 9 }, { 2, 4, 6, 8, 10 }, { 4, 8 }))
  251. eq({ { 1, 4, 7 }, { 2, 5, 8 } }, mov({ 1, 3, 4, 6, 7, 9 }, { 2, 3, 5, 6, 8, 9 }, {}))
  252. eq({ { 1, 4, 7 }, {} }, mov({ 1, 3, 4, 6, 7, 9 }, { 2, 3, 5, 6, 8, 9 }, { 2, 5, 8 }))
  253. eq(
  254. { { 0, 1, 4, 7, 10 }, {} },
  255. mov({ 1, 3, 4, 6, 7, 9 }, { 2, 3, 5, 6, 8, 9 }, { 0, 2, 5, 8, 10 })
  256. )
  257. end)
  258. local function check_intersections(tree)
  259. lib.marktree_check(tree)
  260. -- to debug stuff disable this branch
  261. if true == true then
  262. ok(lib.marktree_check_intersections(tree))
  263. return
  264. end
  265. local str1 = lib.mt_inspect(tree, true, true)
  266. local dot1 = ffi.string(str1.data, str1.size)
  267. local val = lib.marktree_check_intersections(tree)
  268. if not val then
  269. local str2 = lib.mt_inspect(tree, true, true)
  270. local dot2 = ffi.string(str2.data, str2.size)
  271. print('actual:\n\n' .. 'Xafile.dot' .. '\n\nexpected:\n\n' .. 'Xefile.dot' .. '\n')
  272. print('nivå', tree[0].root.level)
  273. io.stdout:flush()
  274. local afil = io.open('Xafile.dot', 'wb')
  275. afil:write(dot1)
  276. afil:close()
  277. local efil = io.open('Xefile.dot', 'wb')
  278. efil:write(dot2)
  279. efil:close()
  280. ok(false)
  281. else
  282. ffi.C.xfree(str1.data)
  283. end
  284. end
  285. itp('works with intersections', function()
  286. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  287. local ids = {}
  288. for i = 1, 80 do
  289. table.insert(ids, put(tree, 1, i, false, 2, 100 - i, false))
  290. check_intersections(tree)
  291. end
  292. for i = 1, 80 do
  293. lib.marktree_del_pair_test(tree, ns, ids[i])
  294. check_intersections(tree)
  295. end
  296. ids = {}
  297. for i = 1, 80 do
  298. table.insert(ids, put(tree, 1, i, false, 2, 100 - i, false))
  299. check_intersections(tree)
  300. end
  301. for i = 1, 10 do
  302. for j = 1, 8 do
  303. local ival = (j - 1) * 10 + i
  304. lib.marktree_del_pair_test(tree, ns, ids[ival])
  305. check_intersections(tree)
  306. end
  307. end
  308. end)
  309. itp('works with intersections with a big tree', function()
  310. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  311. local ids = {}
  312. for i = 1, 1000 do
  313. table.insert(ids, put(tree, 1, i, false, 2, 1000 - i, false))
  314. if i % 10 == 1 then
  315. check_intersections(tree)
  316. end
  317. end
  318. check_intersections(tree)
  319. eq(2000, tree[0].n_keys)
  320. ok(tree[0].root.level >= 2)
  321. local iter = ffi.new('MarkTreeIter[1]')
  322. local k = 0
  323. for i = 1, 20 do
  324. for j = 1, 50 do
  325. k = k + 1
  326. local ival = (j - 1) * 20 + i
  327. if false == true then -- if there actually is a failure, this branch will fail out at the actual spot of the error
  328. lib.marktree_lookup_ns(tree, ns, ids[ival], false, iter)
  329. lib.marktree_del_itr(tree, iter, false)
  330. check_intersections(tree)
  331. lib.marktree_lookup_ns(tree, ns, ids[ival], true, iter)
  332. lib.marktree_del_itr(tree, iter, false)
  333. check_intersections(tree)
  334. else
  335. lib.marktree_del_pair_test(tree, ns, ids[ival])
  336. if k % 5 == 1 then
  337. check_intersections(tree)
  338. end
  339. end
  340. end
  341. end
  342. eq(0, tree[0].n_keys)
  343. end)
  344. itp('works with intersections and marktree_splice', function()
  345. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  346. for i = 1, 1000 do
  347. put(tree, 1, i, false, 2, 1000 - i, false)
  348. if i % 10 == 1 then
  349. check_intersections(tree)
  350. end
  351. end
  352. check_intersections(tree)
  353. eq(2000, tree[0].n_keys)
  354. ok(tree[0].root.level >= 2)
  355. for _ = 1, 10 do
  356. lib.marktree_splice(tree, 0, 0, 0, 100, 0, 0)
  357. check_intersections(tree)
  358. end
  359. end)
  360. itp('marktree_move should preserve key order', function()
  361. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  362. local iter = ffi.new('MarkTreeIter[1]')
  363. local ids = {}
  364. -- new index and old index look the same, but still have to move because
  365. -- pos will get updated
  366. table.insert(ids, put(tree, 1, 1, false, 1, 3, false))
  367. table.insert(ids, put(tree, 1, 3, false, 1, 3, false))
  368. table.insert(ids, put(tree, 1, 3, false, 1, 3, false))
  369. table.insert(ids, put(tree, 1, 3, false, 1, 3, false))
  370. lib.marktree_lookup_ns(tree, ns, ids[3], false, iter)
  371. lib.marktree_move(tree, iter, 1, 2)
  372. check_intersections(tree)
  373. end)
  374. itp('works with intersections and marktree_move', function()
  375. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  376. local ids = {}
  377. for i = 1, 1000 do
  378. table.insert(ids, put(tree, 1, i, false, 2, 1000 - i, false))
  379. if i % 10 == 1 then
  380. check_intersections(tree)
  381. end
  382. end
  383. local iter = ffi.new('MarkTreeIter[1]')
  384. for i = 1, 1000 do
  385. local which = i % 2
  386. lib.marktree_lookup_ns(tree, ns, ids[i], which, iter)
  387. lib.marktree_move(tree, iter, 1 + which, 500 + i)
  388. if i % 10 == 1 then
  389. check_intersections(tree)
  390. end
  391. end
  392. end)
  393. itp('works with intersections with a even bigger tree', function()
  394. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  395. local ids = {}
  396. -- too much overhead on ASAN
  397. local size_factor = t.is_asan() and 3 or 10
  398. local at_row = {}
  399. for i = 1, 10 do
  400. at_row[i] = {}
  401. end
  402. local size = 1000 * size_factor
  403. local k = 1
  404. while k <= size do
  405. for row1 = 1, 9 do
  406. for row2 = row1, 10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2
  407. if k > size then
  408. break
  409. end
  410. local id = put(tree, row1, k, false, row2, size - k, false)
  411. table.insert(ids, id)
  412. for i = row1 + 1, row2 do
  413. table.insert(at_row[i], id)
  414. end
  415. --if tree[0].root.level == 4 then error("kk"..k) end
  416. if k % 100 * size_factor == 1 or (k < 2000 and k % 100 == 1) then
  417. check_intersections(tree)
  418. end
  419. k = k + 1
  420. end
  421. end
  422. end
  423. eq(2 * size, tree[0].n_keys)
  424. ok(tree[0].root.level >= 3)
  425. check_intersections(tree)
  426. local iter = ffi.new('MarkTreeIter[1]')
  427. local pair = ffi.new('MTPair[1]')
  428. for i = 1, 10 do
  429. -- use array as set and not {[id]=true} map, to detect duplicates
  430. local set = {}
  431. eq(true, ffi.C.marktree_itr_get_overlap(tree, i, 0, iter))
  432. while ffi.C.marktree_itr_step_overlap(tree, iter, pair) do
  433. local id = tonumber(pair[0].start.id)
  434. table.insert(set, id)
  435. end
  436. table.sort(set)
  437. eq(at_row[i], set)
  438. end
  439. k = 0
  440. for i = 1, 100 do
  441. for j = 1, (10 * size_factor) do
  442. k = k + 1
  443. local ival = (j - 1) * 100 + i
  444. lib.marktree_del_pair_test(tree, ns, ids[ival])
  445. -- just a few stickprov, if there is trouble we need to check
  446. -- everyone using the code in the "big tree" case above
  447. if k % 100 * size_factor == 0 or (k > 3000 and k % 200 == 0) then
  448. check_intersections(tree)
  449. end
  450. end
  451. end
  452. eq(0, tree[0].n_keys)
  453. end)
  454. itp('works with intersections with a even bigger tree and splice', function()
  455. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  456. -- too much overhead on ASAN
  457. local size_factor = t.is_asan() and 3 or 10
  458. local at_row = {}
  459. for i = 1, 10 do
  460. at_row[i] = {}
  461. end
  462. local size = 1000 * size_factor
  463. local k = 1
  464. while k <= size do
  465. for row1 = 1, 9 do
  466. for row2 = row1, 10 do -- note row2 can be == row1, leads to empty ranges being tested when k > size/2
  467. if k > size then
  468. break
  469. end
  470. local id = put(tree, row1, k, false, row2, size - k, false)
  471. for i = row1 + 1, row2 do
  472. table.insert(at_row[i], id)
  473. end
  474. --if tree[0].root.level == 4 then error("kk"..k) end
  475. if k % 100 * size_factor == 1 or (k < 2000 and k % 100 == 1) then
  476. check_intersections(tree)
  477. end
  478. k = k + 1
  479. end
  480. end
  481. end
  482. eq(2 * size, tree[0].n_keys)
  483. ok(tree[0].root.level >= 3)
  484. check_intersections(tree)
  485. for _ = 1, 10 do
  486. for j = 3, 8 do
  487. lib.marktree_splice(tree, j, 0, 0, 200, 0, 0)
  488. check_intersections(tree)
  489. end
  490. end
  491. end)
  492. itp('works with meta counts', function()
  493. local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
  494. -- add
  495. local shadow = {}
  496. for i = 1, 100 do
  497. for j = 1, 100 do
  498. local gravitate = (i % 2) > 0
  499. local inline = (j == 3 or j == 50 or j == 51 or j == 55) and i % 11 == 1
  500. inline = inline or ((j >= 80 and j < 85) and i % 3 == 1)
  501. local id = put_meta(tree, j, i, gravitate, inline)
  502. if inline then
  503. shadow[id] = { j, i, gravitate }
  504. end
  505. end
  506. -- checking every insert is too slow, but this is ok
  507. lib.marktree_check(tree)
  508. end
  509. lib.marktree_check(tree)
  510. local iter = ffi.new('MarkTreeIter[1]')
  511. local filter = ffi.new('uint32_t[4]')
  512. filter[0] = -1
  513. ok(lib.marktree_itr_get_filter(tree, 0, 0, 101, 0, filter, iter))
  514. local seen = {}
  515. repeat
  516. local mark = lib.marktree_itr_current(iter)
  517. eq(nil, seen[mark.id])
  518. seen[mark.id] = true
  519. eq(mark.pos.row, shadow[mark.id][1])
  520. eq(mark.pos.col, shadow[mark.id][2])
  521. until not lib.marktree_itr_next_filter(tree, iter, 101, 0, filter)
  522. eq(tablelength(seen), tablelength(shadow))
  523. -- test skipping subtrees to find the filtered mark at line 50
  524. for i = 4, 50 do
  525. ok(lib.marktree_itr_get_filter(tree, i, 0, 60, 0, filter, iter))
  526. local mark = lib.marktree_itr_current(iter)
  527. eq({ 50, 50, 1 }, { mark.id, mark.pos.row, mark.pos.col })
  528. end
  529. -- delete
  530. for id = 1, 10000, 2 do
  531. lib.marktree_lookup_ns(tree, ns, id, false, iter)
  532. if shadow[id] then
  533. local mark = lib.marktree_itr_current(iter)
  534. eq(mark.pos.row, shadow[id][1])
  535. eq(mark.pos.col, shadow[id][2])
  536. shadow[id] = nil
  537. end
  538. lib.marktree_del_itr(tree, iter, false)
  539. if id % 100 == 1 then
  540. lib.marktree_check(tree)
  541. end
  542. end
  543. -- Splice!
  544. dosplice(tree, shadow, { 82, 0 }, { 0, 50 }, { 0, 0 })
  545. lib.marktree_check(tree)
  546. dosplice(tree, shadow, { 81, 50 }, { 2, 50 }, { 1, 0 })
  547. lib.marktree_check(tree)
  548. dosplice(tree, shadow, { 2, 50 }, { 1, 50 }, { 0, 10 })
  549. lib.marktree_check(tree)
  550. ok(lib.marktree_itr_get_filter(tree, 0, 0, 101, 0, filter, iter))
  551. seen = {}
  552. repeat
  553. local mark = lib.marktree_itr_current(iter)
  554. eq(nil, seen[mark.id])
  555. seen[mark.id] = true
  556. eq(mark.pos.row, shadow[mark.id][1])
  557. eq(mark.pos.col, shadow[mark.id][2])
  558. until not lib.marktree_itr_next_filter(tree, iter, 101, 0, filter)
  559. eq(tablelength(seen), tablelength(shadow))
  560. end)
  561. end)