typval_spec.lua (121150B)
1 local bit = require('bit') 2 local t = require('test.unit.testutil') 3 local t_eval = require('test.unit.eval.testutil') 4 5 local itp = t.gen_itp(it) 6 7 local OK = t.OK 8 local eq = t.eq 9 local neq = t.neq 10 local ffi = t.ffi 11 local FAIL = t.FAIL 12 local NULL = t.NULL 13 local cimport = t.cimport 14 local to_cstr = t.to_cstr 15 local alloc_log_new = t.alloc_log_new 16 local concat_tables = t.concat_tables 17 local map = vim.tbl_map 18 19 local a = t_eval.alloc_logging_t 20 local int = t_eval.int 21 local list = t_eval.list 22 local dict = t_eval.dict 23 local eval0 = t_eval.eval0 24 local lst2tbl = t_eval.lst2tbl 25 local dct2tbl = t_eval.dct2tbl 26 local typvalt = t_eval.typvalt 27 local type_key = t_eval.type_key 28 local li_alloc = t_eval.li_alloc 29 local first_di = t_eval.first_di 30 local nil_value = t_eval.nil_value 31 local func_type = t_eval.func_type 32 local null_list = t_eval.null_list 33 local null_dict = t_eval.null_dict 34 local dict_items = t_eval.dict_items 35 local list_items = t_eval.list_items 36 local empty_list = t_eval.empty_list 37 local lua2typvalt = t_eval.lua2typvalt 38 local typvalt2lua = t_eval.typvalt2lua 39 local null_string = t_eval.null_string 40 local callback2tbl = t_eval.callback2tbl 41 local tbl2callback = t_eval.tbl2callback 42 local dict_watchers = t_eval.dict_watchers 43 44 local lib = cimport( 45 './src/nvim/eval/typval.h', 46 './src/nvim/memory.h', 47 './src/nvim/mbyte.h', 48 './src/nvim/garray.h', 49 './src/nvim/eval.h', 50 './src/nvim/vim_defs.h', 51 './src/nvim/globals.h' 52 ) 53 54 local function vimconv_alloc() 55 return ffi.gc(ffi.cast('vimconv_T*', lib.xcalloc(1, ffi.sizeof('vimconv_T'))), function(vc) 56 lib.convert_setup(vc, nil, nil) 57 lib.xfree(vc) 58 end) 59 end 60 61 local function list_watch_alloc(li) 62 return ffi.cast('listwatch_T*', ffi.new('listwatch_T[1]', { { lw_item = li } })) 63 end 64 65 local function list_watch(l, li) 66 local lw = list_watch_alloc(li or l.lv_first) 67 lib.tv_list_watch_add(l, lw) 68 return lw 69 end 70 71 local function get_alloc_rets(exp_log, res) 72 setmetatable(res, { 73 __index = { 74 freed = function(r, n) 75 return { func = 'free', args = { r[n] } } 76 end, 77 }, 78 }) 79 for i = 1, #exp_log do 80 if ({ malloc = true, calloc = true })[exp_log[i].func] then 81 res[#res + 1] = exp_log[i].ret 82 end 83 end 84 return exp_log 85 end 86 87 local alloc_log = alloc_log_new() 88 89 before_each(function() 90 alloc_log:before_each() 91 end) 92 93 after_each(function() 94 alloc_log:after_each() 95 end) 96 97 local function ga_alloc(itemsize, growsize) 98 local ga = ffi.gc(ffi.cast('garray_T*', ffi.new('garray_T[1]', {})), lib.ga_clear) 99 lib.ga_init(ga, itemsize or 1, growsize or 80) 100 return ga 101 end 102 103 local function check_emsg(f, msg) 104 local saved_msg_hist_last = lib.msg_hist_last 105 if saved_msg_hist_last == nil then 106 saved_msg_hist_last = nil 107 end 108 local ret = { f() } 109 if msg ~= nil then 110 eq(msg, ffi.string(lib.msg_hist_last.msg.items[0].text.data)) 111 neq(saved_msg_hist_last, lib.msg_hist_last) 112 else 113 if saved_msg_hist_last ~= lib.msg_hist_last then 114 eq(nil, lib.msg_hist_last) 115 else 116 eq(saved_msg_hist_last, lib.msg_hist_last) 117 end 118 end 119 return unpack(ret) 120 end 121 122 describe('typval.c', function() 123 describe('list', function() 124 describe('item', function() 125 describe('remove()', function() 126 itp('works', function() 127 local l = list(1, 2, 3, 4, 5, 6, 7) 128 neq(nil, l) 129 local lis = list_items(l) 130 alloc_log:check({ 131 a.list(l), 132 a.li(lis[1]), 133 a.li(lis[2]), 134 a.li(lis[3]), 135 a.li(lis[4]), 136 a.li(lis[5]), 137 a.li(lis[6]), 138 a.li(lis[7]), 139 }) 140 141 eq(lis[2], lib.tv_list_item_remove(l, lis[1])) 142 alloc_log:check({ 143 a.freed(table.remove(lis, 1)), 144 }) 145 eq(lis, list_items(l)) 146 147 eq(lis[7], lib.tv_list_item_remove(l, lis[6])) 148 alloc_log:check({ 149 a.freed(table.remove(lis)), 150 }) 151 eq(lis, list_items(l)) 152 153 eq(lis[4], lib.tv_list_item_remove(l, lis[3])) 154 alloc_log:check({ 155 a.freed(table.remove(lis, 3)), 156 }) 157 eq(lis, list_items(l)) 158 end) 159 itp('also frees the value', function() 160 local l = list('a', 'b', 'c', 'd') 161 neq(nil, l) 162 local lis = list_items(l) 163 alloc_log:check({ 164 a.list(l), 165 a.str(lis[1].li_tv.vval.v_string, 1), 166 a.li(lis[1]), 167 a.str(lis[2].li_tv.vval.v_string, 1), 168 a.li(lis[2]), 169 a.str(lis[3].li_tv.vval.v_string, 1), 170 a.li(lis[3]), 171 a.str(lis[4].li_tv.vval.v_string, 1), 172 a.li(lis[4]), 173 }) 174 local strings = map(function(li) 175 return li.li_tv.vval.v_string 176 end, lis) 177 178 eq(lis[2], lib.tv_list_item_remove(l, lis[1])) 179 alloc_log:check({ 180 a.freed(table.remove(strings, 1)), 181 a.freed(table.remove(lis, 1)), 182 }) 183 eq(lis, list_items(l)) 184 185 eq(lis[3], lib.tv_list_item_remove(l, lis[2])) 186 alloc_log:check({ 187 a.freed(table.remove(strings, 2)), 188 a.freed(table.remove(lis, 2)), 189 }) 190 eq(lis, list_items(l)) 191 192 eq(nil, lib.tv_list_item_remove(l, lis[2])) 193 alloc_log:check({ 194 a.freed(table.remove(strings, 2)), 195 a.freed(table.remove(lis, 2)), 196 }) 197 eq(lis, list_items(l)) 198 end) 199 itp('works and adjusts watchers correctly', function() 200 local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil) 201 neq(nil, l) 202 local lis = list_items(l) 203 -- Three watchers: pointing to first, middle and last elements. 204 local lws = { 205 list_watch(l, lis[1]), 206 list_watch(l, lis[4]), 207 list_watch(l, lis[7]), 208 } 209 alloc_log:check({ 210 a.list(l), 211 a.li(lis[1]), 212 a.li(lis[2]), 213 a.li(lis[3]), 214 a.li(lis[4]), 215 a.li(lis[5]), 216 a.li(lis[6]), 217 a.li(lis[7]), 218 }) 219 220 eq(lis[5], lib.tv_list_item_remove(l, lis[4])) 221 alloc_log:check({ a.freed(lis[4]) }) 222 eq({ lis[1], lis[5], lis[7] }, { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item }) 223 224 eq(lis[3], lib.tv_list_item_remove(l, lis[2])) 225 alloc_log:check({ a.freed(lis[2]) }) 226 eq({ lis[1], lis[5], lis[7] }, { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item }) 227 228 eq(nil, lib.tv_list_item_remove(l, lis[7])) 229 alloc_log:check({ a.freed(lis[7]) }) 230 eq( 231 { lis[1], lis[5], nil }, 232 { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil } 233 ) 234 235 eq(lis[3], lib.tv_list_item_remove(l, lis[1])) 236 alloc_log:check({ a.freed(lis[1]) }) 237 eq( 238 { lis[3], lis[5], nil }, 239 { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil } 240 ) 241 242 lib.tv_list_watch_remove(l, lws[2]) 243 lib.tv_list_watch_remove(l, lws[3]) 244 lib.tv_list_watch_remove(l, lws[1]) 245 lib.tv_list_free(l) 246 alloc_log:check({ 247 a.freed(lis[3]), 248 a.freed(lis[5]), 249 a.freed(lis[6]), 250 a.freed(l), 251 }) 252 end) 253 end) 254 end) 255 describe('watch', function() 256 describe('remove()', function() 257 itp('works', function() 258 local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil) 259 eq(nil, l.lv_watch) 260 local lw = list_watch(l) 261 neq(nil, l.lv_watch) 262 alloc_log:clear() 263 lib.tv_list_watch_remove(l, lw) 264 eq(nil, l.lv_watch) 265 alloc_log:check({ 266 -- Does not free anything. 267 }) 268 local lws = { list_watch(l), list_watch(l), list_watch(l) } 269 alloc_log:clear() 270 lib.tv_list_watch_remove(l, lws[2]) 271 eq(lws[3], l.lv_watch) 272 eq(lws[1], l.lv_watch.lw_next) 273 lib.tv_list_watch_remove(l, lws[1]) 274 eq(lws[3], l.lv_watch) 275 eq(nil, l.lv_watch.lw_next) 276 lib.tv_list_watch_remove(l, lws[3]) 277 eq(nil, l.lv_watch) 278 alloc_log:check({ 279 -- Does not free anything. 280 }) 281 end) 282 itp('ignores not found watchers', function() 283 local l = list(1, 2, 3, 4, 5, 6, 7) 284 local lw = list_watch_alloc() 285 lib.tv_list_watch_remove(l, lw) 286 end) 287 end) 288 end) 289 -- add() and fix() were tested when testing tv_list_item_remove() 290 describe('free()', function() 291 itp('recursively frees list', function() 292 local l1 = ffi.gc(list(1, 'abc'), nil) 293 local l2 = ffi.gc(list({}), nil) 294 local l3 = ffi.gc(list(empty_list), nil) 295 local alloc_rets = {} 296 alloc_log:check(get_alloc_rets({ 297 a.list(l1), 298 a.li(l1.lv_first), 299 a.str(l1.lv_last.li_tv.vval.v_string, #'abc'), 300 a.li(l1.lv_last), 301 a.list(l2), 302 a.dict(l2.lv_first.li_tv.vval.v_dict), 303 a.li(l2.lv_first), 304 a.list(l3), 305 a.list(l3.lv_first.li_tv.vval.v_list), 306 a.li(l3.lv_first), 307 }, alloc_rets)) 308 lib.tv_list_free(l1) 309 alloc_log:check({ 310 alloc_rets:freed(2), 311 alloc_rets:freed(3), 312 alloc_rets:freed(4), 313 alloc_rets:freed(1), 314 }) 315 lib.tv_list_free(l2) 316 alloc_log:check({ 317 alloc_rets:freed(6), 318 alloc_rets:freed(7), 319 alloc_rets:freed(5), 320 }) 321 lib.tv_list_free(l3) 322 alloc_log:check({ 323 alloc_rets:freed(9), 324 alloc_rets:freed(10), 325 alloc_rets:freed(8), 326 }) 327 end) 328 end) 329 describe('free_list()', function() 330 itp('does not free list contents', function() 331 local l1 = ffi.gc(list(1, 'abc'), nil) 332 local l2 = ffi.gc(list({}), nil) 333 local l3 = ffi.gc(list(empty_list), nil) 334 local alloc_rets = {} 335 alloc_log:check(get_alloc_rets({ 336 a.list(l1), 337 a.li(l1.lv_first), 338 a.str(l1.lv_last.li_tv.vval.v_string, #'abc'), 339 a.li(l1.lv_last), 340 a.list(l2), 341 a.dict(l2.lv_first.li_tv.vval.v_dict), 342 a.li(l2.lv_first), 343 a.list(l3), 344 a.list(l3.lv_first.li_tv.vval.v_list), 345 a.li(l3.lv_first), 346 }, alloc_rets)) 347 lib.tv_list_free_list(l1) 348 alloc_log:check({ 349 alloc_rets:freed(1), 350 }) 351 lib.tv_list_free_list(l2) 352 alloc_log:check({ 353 alloc_rets:freed(5), 354 }) 355 lib.tv_list_free_list(l3) 356 alloc_log:check({ 357 alloc_rets:freed(8), 358 }) 359 end) 360 end) 361 describe('free_contents()', function() 362 itp('recursively frees list, except for the list structure itself', function() 363 local l1 = ffi.gc(list(1, 'abc'), nil) 364 local l2 = ffi.gc(list({}), nil) 365 local l3 = ffi.gc(list(empty_list), nil) 366 local alloc_rets = {} 367 alloc_log:check(get_alloc_rets({ 368 a.list(l1), 369 a.li(l1.lv_first), 370 a.str(l1.lv_last.li_tv.vval.v_string, #'abc'), 371 a.li(l1.lv_last), 372 a.list(l2), 373 a.dict(l2.lv_first.li_tv.vval.v_dict), 374 a.li(l2.lv_first), 375 a.list(l3), 376 a.list(l3.lv_first.li_tv.vval.v_list), 377 a.li(l3.lv_first), 378 }, alloc_rets)) 379 lib.tv_list_free_contents(l1) 380 alloc_log:check({ 381 alloc_rets:freed(2), 382 alloc_rets:freed(3), 383 alloc_rets:freed(4), 384 }) 385 lib.tv_list_free_contents(l2) 386 alloc_log:check({ 387 alloc_rets:freed(6), 388 alloc_rets:freed(7), 389 }) 390 lib.tv_list_free_contents(l3) 391 alloc_log:check({ 392 alloc_rets:freed(9), 393 alloc_rets:freed(10), 394 }) 395 end) 396 end) 397 describe('unref()', function() 398 itp('recursively frees list when reference count goes to 0', function() 399 local l = ffi.gc(list(empty_list), nil) 400 local alloc_rets = {} 401 alloc_log:check(get_alloc_rets({ 402 a.list(l), 403 a.list(l.lv_first.li_tv.vval.v_list), 404 a.li(l.lv_first), 405 }, alloc_rets)) 406 l.lv_refcount = 2 407 lib.tv_list_unref(l) 408 alloc_log:check({}) 409 lib.tv_list_unref(l) 410 alloc_log:check({ 411 alloc_rets:freed(2), 412 alloc_rets:freed(3), 413 alloc_rets:freed(1), 414 }) 415 end) 416 end) 417 describe('drop_items()', function() 418 itp('works', function() 419 local l_tv = lua2typvalt({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }) 420 local l = l_tv.vval.v_list 421 local lis = list_items(l) 422 -- Three watchers: pointing to first, middle and last elements. 423 local lws = { 424 list_watch(l, lis[1]), 425 list_watch(l, lis[7]), 426 list_watch(l, lis[13]), 427 } 428 alloc_log:clear() 429 430 lib.tv_list_drop_items(l, lis[1], lis[3]) 431 eq({ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }, typvalt2lua(l_tv)) 432 eq({ lis[4], lis[7], lis[13] }, { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item }) 433 434 lib.tv_list_drop_items(l, lis[11], lis[13]) 435 eq({ 4, 5, 6, 7, 8, 9, 10 }, typvalt2lua(l_tv)) 436 eq( 437 { lis[4], lis[7], nil }, 438 { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil } 439 ) 440 441 lib.tv_list_drop_items(l, lis[6], lis[8]) 442 eq({ 4, 5, 9, 10 }, typvalt2lua(l_tv)) 443 eq( 444 { lis[4], lis[9], nil }, 445 { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil } 446 ) 447 448 lib.tv_list_drop_items(l, lis[4], lis[10]) 449 eq(empty_list, typvalt2lua(l_tv)) 450 eq( 451 { true, true, true }, 452 { lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil } 453 ) 454 455 lib.tv_list_watch_remove(l, lws[1]) 456 lib.tv_list_watch_remove(l, lws[2]) 457 lib.tv_list_watch_remove(l, lws[3]) 458 459 alloc_log:check({}) 460 end) 461 end) 462 describe('remove_items()', function() 463 itp('works', function() 464 local l_tv = 465 lua2typvalt({ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13' }) 466 local l = l_tv.vval.v_list 467 local lis = list_items(l) 468 local strings = map(function(li) 469 return li.li_tv.vval.v_string 470 end, lis) 471 -- Three watchers: pointing to first, middle and last elements. 472 local lws = { 473 list_watch(l, lis[1]), 474 list_watch(l, lis[7]), 475 list_watch(l, lis[13]), 476 } 477 alloc_log:clear() 478 479 lib.tv_list_remove_items(l, lis[1], lis[3]) 480 eq({ '4', '5', '6', '7', '8', '9', '10', '11', '12', '13' }, typvalt2lua(l_tv)) 481 eq({ lis[4], lis[7], lis[13] }, { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item }) 482 alloc_log:check({ 483 a.freed(strings[1]), 484 a.freed(lis[1]), 485 a.freed(strings[2]), 486 a.freed(lis[2]), 487 a.freed(strings[3]), 488 a.freed(lis[3]), 489 }) 490 491 lib.tv_list_remove_items(l, lis[11], lis[13]) 492 eq({ '4', '5', '6', '7', '8', '9', '10' }, typvalt2lua(l_tv)) 493 eq( 494 { lis[4], lis[7], nil }, 495 { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil } 496 ) 497 alloc_log:check({ 498 a.freed(strings[11]), 499 a.freed(lis[11]), 500 a.freed(strings[12]), 501 a.freed(lis[12]), 502 a.freed(strings[13]), 503 a.freed(lis[13]), 504 }) 505 506 lib.tv_list_remove_items(l, lis[6], lis[8]) 507 eq({ '4', '5', '9', '10' }, typvalt2lua(l_tv)) 508 eq( 509 { lis[4], lis[9], nil }, 510 { lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil } 511 ) 512 alloc_log:check({ 513 a.freed(strings[6]), 514 a.freed(lis[6]), 515 a.freed(strings[7]), 516 a.freed(lis[7]), 517 a.freed(strings[8]), 518 a.freed(lis[8]), 519 }) 520 521 lib.tv_list_remove_items(l, lis[4], lis[10]) 522 eq(empty_list, typvalt2lua(l_tv)) 523 eq( 524 { true, true, true }, 525 { lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil } 526 ) 527 alloc_log:check({ 528 a.freed(strings[4]), 529 a.freed(lis[4]), 530 a.freed(strings[5]), 531 a.freed(lis[5]), 532 a.freed(strings[9]), 533 a.freed(lis[9]), 534 a.freed(strings[10]), 535 a.freed(lis[10]), 536 }) 537 538 lib.tv_list_watch_remove(l, lws[1]) 539 lib.tv_list_watch_remove(l, lws[2]) 540 lib.tv_list_watch_remove(l, lws[3]) 541 542 alloc_log:check({}) 543 end) 544 end) 545 describe('insert', function() 546 describe('()', function() 547 itp('works', function() 548 local l_tv = lua2typvalt({ 1, 2, 3, 4, 5, 6, 7 }) 549 local l = l_tv.vval.v_list 550 local lis = list_items(l) 551 local li 552 553 li = li_alloc(true) 554 li.li_tv = { v_type = lib.VAR_FLOAT, vval = { v_float = 100500 } } 555 lib.tv_list_insert(l, li, nil) 556 eq(l.lv_last, li) 557 eq({ 1, 2, 3, 4, 5, 6, 7, 100500 }, typvalt2lua(l_tv)) 558 559 li = li_alloc(true) 560 li.li_tv = { v_type = lib.VAR_FLOAT, vval = { v_float = 0 } } 561 lib.tv_list_insert(l, li, lis[1]) 562 eq(l.lv_first, li) 563 eq({ 0, 1, 2, 3, 4, 5, 6, 7, 100500 }, typvalt2lua(l_tv)) 564 565 li = li_alloc(true) 566 li.li_tv = { v_type = lib.VAR_FLOAT, vval = { v_float = 4.5 } } 567 lib.tv_list_insert(l, li, lis[5]) 568 eq(list_items(l)[6], li) 569 eq({ 0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500 }, typvalt2lua(l_tv)) 570 end) 571 itp('works with an empty list', function() 572 local l_tv = lua2typvalt(empty_list) 573 local l = l_tv.vval.v_list 574 575 eq(nil, l.lv_first) 576 eq(nil, l.lv_last) 577 578 local li = li_alloc(true) 579 li.li_tv = { v_type = lib.VAR_FLOAT, vval = { v_float = 100500 } } 580 lib.tv_list_insert(l, li, nil) 581 eq(l.lv_last, li) 582 eq({ 100500 }, typvalt2lua(l_tv)) 583 end) 584 end) 585 describe('tv()', function() 586 itp('works', function() 587 local l_tv = lua2typvalt(empty_list) 588 local l = l_tv.vval.v_list 589 590 local l_l_tv = lua2typvalt(empty_list) 591 alloc_log:clear() 592 local l_l = l_l_tv.vval.v_list 593 eq(1, l_l.lv_refcount) 594 lib.tv_list_insert_tv(l, l_l_tv, nil) 595 eq(2, l_l.lv_refcount) 596 eq(l_l, l.lv_first.li_tv.vval.v_list) 597 alloc_log:check({ 598 a.li(l.lv_first), 599 }) 600 601 local l_s_tv = lua2typvalt('test') 602 alloc_log:check({ 603 a.str(l_s_tv.vval.v_string, 'test'), 604 }) 605 lib.tv_list_insert_tv(l, l_s_tv, l.lv_first) 606 alloc_log:check({ 607 a.li(l.lv_first), 608 a.str(l.lv_first.li_tv.vval.v_string, 'test'), 609 }) 610 611 eq({ 'test', empty_list }, typvalt2lua(l_tv)) 612 end) 613 end) 614 end) 615 describe('append', function() 616 describe('list()', function() 617 itp('works', function() 618 local l_tv = lua2typvalt(empty_list) 619 local l = l_tv.vval.v_list 620 621 local l_l = list(1) 622 alloc_log:clear() 623 eq(1, l_l.lv_refcount) 624 lib.tv_list_append_list(l, l_l) 625 eq(2, l_l.lv_refcount) 626 eq(l_l, l.lv_first.li_tv.vval.v_list) 627 alloc_log:check({ 628 a.li(l.lv_last), 629 }) 630 631 lib.tv_list_append_list(l, nil) 632 alloc_log:check({ 633 a.li(l.lv_last), 634 }) 635 636 eq({ { 1 }, null_list }, typvalt2lua(l_tv)) 637 end) 638 end) 639 describe('dict()', function() 640 itp('works', function() 641 local l_tv = lua2typvalt(empty_list) 642 local l = l_tv.vval.v_list 643 644 local l_d_tv = lua2typvalt({ test = 1 }) 645 local l_d = l_d_tv.vval.v_dict 646 alloc_log:clear() 647 eq(1, l_d.dv_refcount) 648 lib.tv_list_append_dict(l, l_d) 649 eq(2, l_d.dv_refcount) 650 eq(l_d, l.lv_first.li_tv.vval.v_list) 651 alloc_log:check({ 652 a.li(l.lv_last), 653 }) 654 655 lib.tv_list_append_dict(l, nil) 656 alloc_log:check({ 657 a.li(l.lv_last), 658 }) 659 660 eq({ { test = 1 }, null_dict }, typvalt2lua(l_tv)) 661 end) 662 end) 663 describe('string()', function() 664 itp('works', function() 665 local l_tv = lua2typvalt(empty_list) 666 local l = l_tv.vval.v_list 667 668 alloc_log:clear() 669 lib.tv_list_append_string(l, 'test', 3) 670 alloc_log:check({ 671 a.str(l.lv_last.li_tv.vval.v_string, 'tes'), 672 a.li(l.lv_last), 673 }) 674 675 lib.tv_list_append_string(l, nil, 0) 676 alloc_log:check({ 677 a.li(l.lv_last), 678 }) 679 680 lib.tv_list_append_string(l, nil, -1) 681 alloc_log:check({ 682 a.li(l.lv_last), 683 }) 684 685 lib.tv_list_append_string(l, 'test', -1) 686 alloc_log:check({ 687 a.str(l.lv_last.li_tv.vval.v_string, 'test'), 688 a.li(l.lv_last), 689 }) 690 691 eq({ 'tes', null_string, null_string, 'test' }, typvalt2lua(l_tv)) 692 end) 693 end) 694 describe('allocated string()', function() 695 itp('works', function() 696 local l_tv = lua2typvalt(empty_list) 697 local l = l_tv.vval.v_list 698 699 local s = lib.xstrdup('test') 700 alloc_log:clear() 701 lib.tv_list_append_allocated_string(l, s) 702 alloc_log:check({ 703 a.li(l.lv_last), 704 }) 705 706 lib.tv_list_append_allocated_string(l, nil) 707 alloc_log:check({ 708 a.li(l.lv_last), 709 }) 710 711 lib.tv_list_append_allocated_string(l, nil) 712 alloc_log:check({ 713 a.li(l.lv_last), 714 }) 715 716 eq({ 'test', null_string, null_string }, typvalt2lua(l_tv)) 717 end) 718 end) 719 describe('number()', function() 720 itp('works', function() 721 local l_tv = lua2typvalt(empty_list) 722 local l = l_tv.vval.v_list 723 724 alloc_log:clear() 725 lib.tv_list_append_number(l, -100500) 726 alloc_log:check({ 727 a.li(l.lv_last), 728 }) 729 730 lib.tv_list_append_number(l, 100500) 731 alloc_log:check({ 732 a.li(l.lv_last), 733 }) 734 735 eq({ int(-100500), int(100500) }, typvalt2lua(l_tv)) 736 end) 737 end) 738 describe('tv()', function() 739 itp('works', function() 740 local l_tv = lua2typvalt(empty_list) 741 local l = l_tv.vval.v_list 742 743 local l_l_tv = lua2typvalt(empty_list) 744 alloc_log:clear() 745 local l_l = l_l_tv.vval.v_list 746 eq(1, l_l.lv_refcount) 747 lib.tv_list_append_tv(l, l_l_tv) 748 eq(2, l_l.lv_refcount) 749 eq(l_l, l.lv_first.li_tv.vval.v_list) 750 alloc_log:check({ 751 a.li(l.lv_first), 752 }) 753 754 local l_s_tv = lua2typvalt('test') 755 alloc_log:check({ 756 a.str(l_s_tv.vval.v_string, 'test'), 757 }) 758 lib.tv_list_append_tv(l, l_s_tv) 759 alloc_log:check({ 760 a.li(l.lv_last), 761 a.str(l.lv_last.li_tv.vval.v_string, 'test'), 762 }) 763 764 eq({ empty_list, 'test' }, typvalt2lua(l_tv)) 765 end) 766 end) 767 describe('owned tv()', function() 768 itp('works', function() 769 local l_tv = lua2typvalt(empty_list) 770 local l = l_tv.vval.v_list 771 772 local l_l_tv = lua2typvalt(empty_list) 773 alloc_log:clear() 774 local l_l = l_l_tv.vval.v_list 775 eq(1, l_l.lv_refcount) 776 lib.tv_list_append_owned_tv(l, l_l_tv) 777 eq(1, l_l.lv_refcount) 778 l_l.lv_refcount = l_l.lv_refcount + 1 779 eq(l_l, l.lv_first.li_tv.vval.v_list) 780 alloc_log:check({ 781 a.li(l.lv_first), 782 }) 783 784 local l_s_tv = ffi.gc(lua2typvalt('test'), nil) 785 alloc_log:check({ 786 a.str(l_s_tv.vval.v_string, 'test'), 787 }) 788 lib.tv_list_append_owned_tv(l, l_s_tv) 789 eq(l_s_tv.vval.v_string, l.lv_last.li_tv.vval.v_string) 790 l_s_tv.vval.v_string = nil 791 alloc_log:check({ 792 a.li(l.lv_last), 793 }) 794 795 eq({ empty_list, 'test' }, typvalt2lua(l_tv)) 796 end) 797 end) 798 end) 799 describe('copy()', function() 800 local function tv_list_copy(...) 801 return ffi.gc(lib.tv_list_copy(...), lib.tv_list_unref) 802 end 803 itp('copies NULL correctly', function() 804 eq(nil, lib.tv_list_copy(nil, nil, true, 0)) 805 eq(nil, lib.tv_list_copy(nil, nil, false, 0)) 806 eq(nil, lib.tv_list_copy(nil, nil, true, 1)) 807 eq(nil, lib.tv_list_copy(nil, nil, false, 1)) 808 end) 809 itp('copies list correctly without converting items', function() 810 do 811 local v = { { ['«'] = '»' }, { '„' }, 1, '“', null_string, null_list, null_dict } 812 local l_tv = lua2typvalt(v) 813 local l = l_tv.vval.v_list 814 local lis = list_items(l) 815 alloc_log:clear() 816 817 eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) 818 eq(1, lis[2].li_tv.vval.v_list.lv_refcount) 819 local l_copy1 = tv_list_copy(nil, l, false, 0) 820 eq(2, lis[1].li_tv.vval.v_dict.dv_refcount) 821 eq(2, lis[2].li_tv.vval.v_list.lv_refcount) 822 local lis_copy1 = list_items(l_copy1) 823 eq(lis[1].li_tv.vval.v_dict, lis_copy1[1].li_tv.vval.v_dict) 824 eq(lis[2].li_tv.vval.v_list, lis_copy1[2].li_tv.vval.v_list) 825 eq(v, lst2tbl(l_copy1)) 826 alloc_log:check({ 827 a.list(l_copy1), 828 a.li(lis_copy1[1]), 829 a.li(lis_copy1[2]), 830 a.li(lis_copy1[3]), 831 a.li(lis_copy1[4]), 832 a.str(lis_copy1[4].li_tv.vval.v_string, #v[4]), 833 a.li(lis_copy1[5]), 834 a.li(lis_copy1[6]), 835 a.li(lis_copy1[7]), 836 }) 837 lib.tv_list_free(ffi.gc(l_copy1, nil)) 838 alloc_log:clear() 839 840 eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) 841 eq(1, lis[2].li_tv.vval.v_list.lv_refcount) 842 local l_deepcopy1 = tv_list_copy(nil, l, true, 0) 843 neq(nil, l_deepcopy1) 844 eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) 845 eq(1, lis[2].li_tv.vval.v_list.lv_refcount) 846 local lis_deepcopy1 = list_items(l_deepcopy1) 847 neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) 848 neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) 849 eq(v, lst2tbl(l_deepcopy1)) 850 local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) 851 alloc_log:check({ 852 a.list(l_deepcopy1), 853 a.li(lis_deepcopy1[1]), 854 a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), 855 a.di(di_deepcopy1, #'«'), 856 a.str(di_deepcopy1.di_tv.vval.v_string, #v[1]['«']), 857 a.li(lis_deepcopy1[2]), 858 a.list(lis_deepcopy1[2].li_tv.vval.v_list), 859 a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), 860 a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), 861 a.li(lis_deepcopy1[3]), 862 a.li(lis_deepcopy1[4]), 863 a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), 864 a.li(lis_deepcopy1[5]), 865 a.li(lis_deepcopy1[6]), 866 a.li(lis_deepcopy1[7]), 867 }) 868 end 869 collectgarbage() 870 end) 871 itp('copies list correctly and converts items', function() 872 local vc = vimconv_alloc() 873 -- UTF-8 ↔ latin1 conversions needs no iconv 874 eq(OK, lib.convert_setup(vc, to_cstr('utf-8'), to_cstr('latin1'))) 875 876 local v = { { ['«'] = '»' }, { '„' }, 1, '“', null_string, null_list, null_dict } 877 local l_tv = lua2typvalt(v) 878 local l = l_tv.vval.v_list 879 local lis = list_items(l) 880 alloc_log:clear() 881 882 eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) 883 eq(1, lis[2].li_tv.vval.v_list.lv_refcount) 884 local l_deepcopy1 = tv_list_copy(vc, l, true, 0) 885 neq(nil, l_deepcopy1) 886 eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) 887 eq(1, lis[2].li_tv.vval.v_list.lv_refcount) 888 local lis_deepcopy1 = list_items(l_deepcopy1) 889 neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) 890 neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) 891 eq( 892 { { ['\171'] = '\187' }, { '\191' }, 1, '\191', null_string, null_list, null_dict }, 893 lst2tbl(l_deepcopy1) 894 ) 895 local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) 896 alloc_log:clear_tmp_allocs() 897 alloc_log:check({ 898 a.list(l_deepcopy1), 899 a.li(lis_deepcopy1[1]), 900 a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), 901 a.di(di_deepcopy1, 1), 902 a.str(di_deepcopy1.di_tv.vval.v_string, 2), 903 a.li(lis_deepcopy1[2]), 904 a.list(lis_deepcopy1[2].li_tv.vval.v_list), 905 a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), 906 a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), 907 a.li(lis_deepcopy1[3]), 908 a.li(lis_deepcopy1[4]), 909 a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), 910 a.li(lis_deepcopy1[5]), 911 a.li(lis_deepcopy1[6]), 912 a.li(lis_deepcopy1[7]), 913 }) 914 end) 915 itp('returns different/same containers with(out) copyID', function() 916 local l_inner_tv = lua2typvalt(empty_list) 917 local l_tv = lua2typvalt({ l_inner_tv, l_inner_tv }) 918 eq(3, l_inner_tv.vval.v_list.lv_refcount) 919 local l = l_tv.vval.v_list 920 eq(l.lv_first.li_tv.vval.v_list, l.lv_last.li_tv.vval.v_list) 921 922 local l_copy1 = tv_list_copy(nil, l, true, 0) 923 neq(l_copy1.lv_first.li_tv.vval.v_list, l_copy1.lv_last.li_tv.vval.v_list) 924 eq({ empty_list, empty_list }, lst2tbl(l_copy1)) 925 926 local l_copy2 = tv_list_copy(nil, l, true, 2) 927 eq(l_copy2.lv_first.li_tv.vval.v_list, l_copy2.lv_last.li_tv.vval.v_list) 928 eq({ empty_list, empty_list }, lst2tbl(l_copy2)) 929 930 eq(3, l_inner_tv.vval.v_list.lv_refcount) 931 end) 932 itp('works with self-referencing list with copyID', function() 933 local l_tv = lua2typvalt(empty_list) 934 local l = l_tv.vval.v_list 935 eq(1, l.lv_refcount) 936 lib.tv_list_append_list(l, l) 937 eq(2, l.lv_refcount) 938 939 local l_copy1 = tv_list_copy(nil, l, true, 2) 940 eq(2, l_copy1.lv_refcount) 941 local v = {} 942 v[1] = v 943 eq(v, lst2tbl(l_copy1)) 944 945 local lis = list_items(l) 946 lib.tv_list_item_remove(l, lis[1]) 947 eq(1, l.lv_refcount) 948 949 local lis_copy1 = list_items(l_copy1) 950 lib.tv_list_item_remove(l_copy1, lis_copy1[1]) 951 eq(1, l_copy1.lv_refcount) 952 end) 953 end) 954 describe('extend()', function() 955 itp('can extend list with itself', function() 956 local l 957 958 l = list(1, {}) 959 alloc_log:clear() 960 eq(1, l.lv_refcount) 961 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 962 963 lib.tv_list_extend(l, l, nil) 964 alloc_log:check({ 965 a.li(l.lv_last.li_prev), 966 a.li(l.lv_last), 967 }) 968 eq(1, l.lv_refcount) 969 eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) 970 eq({ 1, {}, 1, {} }, lst2tbl(l)) 971 972 l = list(1, {}) 973 alloc_log:clear() 974 eq(1, l.lv_refcount) 975 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 976 977 lib.tv_list_extend(l, l, l.lv_last) 978 alloc_log:check({ 979 a.li(l.lv_last.li_prev.li_prev), 980 a.li(l.lv_last.li_prev), 981 }) 982 eq({ 1, 1, {}, {} }, lst2tbl(l)) 983 eq(1, l.lv_refcount) 984 eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) 985 986 l = list(1, {}) 987 alloc_log:clear() 988 eq(1, l.lv_refcount) 989 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 990 991 lib.tv_list_extend(l, l, l.lv_first) 992 alloc_log:check({ 993 a.li(l.lv_first), 994 a.li(l.lv_first.li_next), 995 }) 996 eq({ 1, {}, 1, {} }, lst2tbl(l)) 997 eq(1, l.lv_refcount) 998 eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) 999 end) 1000 itp('can extend list with an empty list', function() 1001 local l = list(1, {}) 1002 local el = list() 1003 alloc_log:clear() 1004 eq(1, l.lv_refcount) 1005 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1006 eq(1, el.lv_refcount) 1007 1008 lib.tv_list_extend(l, el, nil) 1009 alloc_log:check({}) 1010 eq(1, l.lv_refcount) 1011 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1012 eq(1, el.lv_refcount) 1013 eq({ 1, {} }, lst2tbl(l)) 1014 1015 lib.tv_list_extend(l, el, l.lv_first) 1016 alloc_log:check({}) 1017 eq(1, l.lv_refcount) 1018 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1019 eq(1, el.lv_refcount) 1020 eq({ 1, {} }, lst2tbl(l)) 1021 1022 lib.tv_list_extend(l, el, l.lv_last) 1023 alloc_log:check({}) 1024 eq(1, l.lv_refcount) 1025 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1026 eq(1, el.lv_refcount) 1027 eq({ 1, {} }, lst2tbl(l)) 1028 end) 1029 itp('can extend list with another non-empty list', function() 1030 local l 1031 local l2 = list(42, empty_list) 1032 eq(1, l2.lv_refcount) 1033 eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1034 1035 l = ffi.gc(list(1, {}), nil) 1036 alloc_log:clear() 1037 eq(1, l.lv_refcount) 1038 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1039 1040 lib.tv_list_extend(l, l2, nil) 1041 alloc_log:check({ 1042 a.li(l.lv_last.li_prev), 1043 a.li(l.lv_last), 1044 }) 1045 eq(1, l2.lv_refcount) 1046 eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1047 eq({ 1, {}, 42, empty_list }, lst2tbl(l)) 1048 lib.tv_list_free(l) 1049 eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1050 1051 l = ffi.gc(list(1, {}), nil) 1052 alloc_log:clear() 1053 eq(1, l.lv_refcount) 1054 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1055 1056 lib.tv_list_extend(l, l2, l.lv_first) 1057 alloc_log:check({ 1058 a.li(l.lv_first), 1059 a.li(l.lv_first.li_next), 1060 }) 1061 eq(1, l2.lv_refcount) 1062 eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1063 eq({ 42, empty_list, 1, {} }, lst2tbl(l)) 1064 lib.tv_list_free(l) 1065 eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1066 1067 l = ffi.gc(list(1, {}), nil) 1068 alloc_log:clear() 1069 eq(1, l.lv_refcount) 1070 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1071 1072 lib.tv_list_extend(l, l2, l.lv_last) 1073 alloc_log:check({ 1074 a.li(l.lv_first.li_next), 1075 a.li(l.lv_first.li_next.li_next), 1076 }) 1077 eq(1, l2.lv_refcount) 1078 eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1079 eq({ 1, 42, empty_list, {} }, lst2tbl(l)) 1080 lib.tv_list_free(l) 1081 eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1082 end) 1083 end) 1084 describe('concat()', function() 1085 itp('works with NULL lists', function() 1086 local l = list(1, {}) 1087 alloc_log:clear() 1088 eq(1, l.lv_refcount) 1089 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1090 1091 local rettv1 = typvalt() 1092 eq(OK, lib.tv_list_concat(nil, l, rettv1)) 1093 eq(1, l.lv_refcount) 1094 eq(tonumber(lib.VAR_LIST), tonumber(rettv1.v_type)) 1095 eq({ 1, {} }, typvalt2lua(rettv1)) 1096 eq(1, rettv1.vval.v_list.lv_refcount) 1097 alloc_log:check({ 1098 a.list(rettv1.vval.v_list), 1099 a.li(rettv1.vval.v_list.lv_first), 1100 a.li(rettv1.vval.v_list.lv_last), 1101 }) 1102 eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1103 1104 local rettv2 = typvalt() 1105 eq(OK, lib.tv_list_concat(l, nil, rettv2)) 1106 eq(1, l.lv_refcount) 1107 eq(tonumber(lib.VAR_LIST), tonumber(rettv2.v_type)) 1108 eq({ 1, {} }, typvalt2lua(rettv2)) 1109 eq(1, rettv2.vval.v_list.lv_refcount) 1110 alloc_log:check({ 1111 a.list(rettv2.vval.v_list), 1112 a.li(rettv2.vval.v_list.lv_first), 1113 a.li(rettv2.vval.v_list.lv_last), 1114 }) 1115 eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1116 1117 local rettv3 = typvalt() 1118 eq(OK, lib.tv_list_concat(nil, nil, rettv3)) 1119 eq(tonumber(lib.VAR_LIST), tonumber(rettv3.v_type)) 1120 eq(null_list, typvalt2lua(rettv3)) 1121 alloc_log:check({}) 1122 end) 1123 itp('works with two different lists', function() 1124 local l1 = list(1, {}) 1125 local l2 = list(3, empty_list) 1126 eq(1, l1.lv_refcount) 1127 eq(1, l1.lv_last.li_tv.vval.v_dict.dv_refcount) 1128 eq(1, l2.lv_refcount) 1129 eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1130 alloc_log:clear() 1131 1132 local rettv = typvalt() 1133 eq(OK, lib.tv_list_concat(l1, l2, rettv)) 1134 eq(1, l1.lv_refcount) 1135 eq(2, l1.lv_last.li_tv.vval.v_dict.dv_refcount) 1136 eq(1, l2.lv_refcount) 1137 eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) 1138 alloc_log:check({ 1139 a.list(rettv.vval.v_list), 1140 a.li(rettv.vval.v_list.lv_first), 1141 a.li(rettv.vval.v_list.lv_first.li_next), 1142 a.li(rettv.vval.v_list.lv_last.li_prev), 1143 a.li(rettv.vval.v_list.lv_last), 1144 }) 1145 eq({ 1, {}, 3, empty_list }, typvalt2lua(rettv)) 1146 end) 1147 itp('can concatenate list with itself', function() 1148 local l = list(1, {}) 1149 eq(1, l.lv_refcount) 1150 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1151 alloc_log:clear() 1152 1153 local rettv = typvalt() 1154 eq(OK, lib.tv_list_concat(l, l, rettv)) 1155 eq(1, l.lv_refcount) 1156 eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1157 alloc_log:check({ 1158 a.list(rettv.vval.v_list), 1159 a.li(rettv.vval.v_list.lv_first), 1160 a.li(rettv.vval.v_list.lv_first.li_next), 1161 a.li(rettv.vval.v_list.lv_last.li_prev), 1162 a.li(rettv.vval.v_list.lv_last), 1163 }) 1164 eq({ 1, {}, 1, {} }, typvalt2lua(rettv)) 1165 end) 1166 itp('can concatenate empty non-NULL lists', function() 1167 local l = list(1, {}) 1168 local le = list() 1169 local le2 = list() 1170 eq(1, l.lv_refcount) 1171 eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1172 eq(1, le.lv_refcount) 1173 eq(1, le2.lv_refcount) 1174 alloc_log:clear() 1175 1176 local rettv1 = typvalt() 1177 eq(OK, lib.tv_list_concat(l, le, rettv1)) 1178 eq(1, l.lv_refcount) 1179 eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1180 eq(1, le.lv_refcount) 1181 eq(1, le2.lv_refcount) 1182 alloc_log:check({ 1183 a.list(rettv1.vval.v_list), 1184 a.li(rettv1.vval.v_list.lv_first), 1185 a.li(rettv1.vval.v_list.lv_last), 1186 }) 1187 eq({ 1, {} }, typvalt2lua(rettv1)) 1188 1189 local rettv2 = typvalt() 1190 eq(OK, lib.tv_list_concat(le, l, rettv2)) 1191 eq(1, l.lv_refcount) 1192 eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1193 eq(1, le.lv_refcount) 1194 eq(1, le2.lv_refcount) 1195 alloc_log:check({ 1196 a.list(rettv2.vval.v_list), 1197 a.li(rettv2.vval.v_list.lv_first), 1198 a.li(rettv2.vval.v_list.lv_last), 1199 }) 1200 eq({ 1, {} }, typvalt2lua(rettv2)) 1201 1202 local rettv3 = typvalt() 1203 eq(OK, lib.tv_list_concat(le, le, rettv3)) 1204 eq(1, l.lv_refcount) 1205 eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1206 eq(1, le.lv_refcount) 1207 eq(1, le2.lv_refcount) 1208 alloc_log:check({ 1209 a.list(rettv3.vval.v_list), 1210 }) 1211 eq(empty_list, typvalt2lua(rettv3)) 1212 1213 local rettv4 = typvalt() 1214 eq(OK, lib.tv_list_concat(le, le2, rettv4)) 1215 eq(1, l.lv_refcount) 1216 eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) 1217 eq(1, le.lv_refcount) 1218 eq(1, le2.lv_refcount) 1219 alloc_log:check({ 1220 a.list(rettv4.vval.v_list), 1221 }) 1222 eq(empty_list, typvalt2lua(rettv4)) 1223 end) 1224 end) 1225 describe('join()', function() 1226 local function list_join(l, sep, join_ret) 1227 local ga = ga_alloc() 1228 eq(join_ret or OK, lib.tv_list_join(ga, l, sep)) 1229 local ret = '' 1230 if ga.ga_data ~= nil then 1231 ret = ffi.string(ga.ga_data) 1232 end 1233 -- For some reason this is not working well in GC 1234 lib.ga_clear(ffi.gc(ga, nil)) 1235 return ret 1236 end 1237 itp('works', function() 1238 local l 1239 l = list('boo', 'far') 1240 eq('boo far', list_join(l, ' ')) 1241 eq('boofar', list_join(l, '')) 1242 1243 l = list('boo') 1244 eq('boo', list_join(l, ' ')) 1245 1246 l = list() 1247 eq('', list_join(l, ' ')) 1248 1249 l = list({}, 'far') 1250 eq('{} far', list_join(l, ' ')) 1251 1252 local recursive_list = {} 1253 recursive_list[1] = recursive_list 1254 l = ffi.gc(list(recursive_list, 'far'), nil) 1255 eq('[[...@0]] far', list_join(l, ' ')) 1256 1257 local recursive_l = l.lv_first.li_tv.vval.v_list 1258 local recursive_li = recursive_l.lv_first 1259 lib.tv_list_item_remove(recursive_l, recursive_li) 1260 lib.tv_list_free(l) 1261 end) 1262 end) 1263 describe('equal()', function() 1264 itp('compares empty and NULL lists correctly', function() 1265 local l = list() 1266 local l2 = list() 1267 1268 -- NULL lists are equal to empty lists 1269 eq(true, lib.tv_list_equal(l, nil, true)) 1270 eq(true, lib.tv_list_equal(nil, l, false)) 1271 1272 -- NULL lists are equal themselves 1273 eq(true, lib.tv_list_equal(nil, nil, true)) 1274 eq(true, lib.tv_list_equal(nil, nil, false)) 1275 1276 -- As well as empty lists 1277 eq(true, lib.tv_list_equal(l, l, true)) 1278 eq(true, lib.tv_list_equal(l, l2, false)) 1279 eq(true, lib.tv_list_equal(l2, l, false)) 1280 eq(true, lib.tv_list_equal(l2, l2, true)) 1281 end) 1282 itp('compares lists correctly when case is not ignored', function() 1283 local l1 = list('abc', { 1, 2, 'Abc' }, 'def') 1284 local l2 = list('abc', { 1, 2, 'Abc' }) 1285 local l3 = list('abc', { 1, 2, 'Abc' }, 'Def') 1286 local l4 = list('abc', { 1, 2, 'Abc', 4 }, 'def') 1287 local l5 = list('Abc', { 1, 2, 'Abc' }, 'def') 1288 local l6 = list('abc', { 1, 2, 'Abc' }, 'def') 1289 local l7 = list('abc', { 1, 2, 'abc' }, 'def') 1290 local l8 = list('abc', nil, 'def') 1291 local l9 = list('abc', { 1, 2, nil }, 'def') 1292 1293 eq(true, lib.tv_list_equal(l1, l1, false)) 1294 eq(false, lib.tv_list_equal(l1, l2, false)) 1295 eq(false, lib.tv_list_equal(l1, l3, false)) 1296 eq(false, lib.tv_list_equal(l1, l4, false)) 1297 eq(false, lib.tv_list_equal(l1, l5, false)) 1298 eq(true, lib.tv_list_equal(l1, l6, false)) 1299 eq(false, lib.tv_list_equal(l1, l7, false)) 1300 eq(false, lib.tv_list_equal(l1, l8, false)) 1301 eq(false, lib.tv_list_equal(l1, l9, false)) 1302 end) 1303 itp('compares lists correctly when case is ignored', function() 1304 local l1 = list('abc', { 1, 2, 'Abc' }, 'def') 1305 local l2 = list('abc', { 1, 2, 'Abc' }) 1306 local l3 = list('abc', { 1, 2, 'Abc' }, 'Def') 1307 local l4 = list('abc', { 1, 2, 'Abc', 4 }, 'def') 1308 local l5 = list('Abc', { 1, 2, 'Abc' }, 'def') 1309 local l6 = list('abc', { 1, 2, 'Abc' }, 'def') 1310 local l7 = list('abc', { 1, 2, 'abc' }, 'def') 1311 local l8 = list('abc', nil, 'def') 1312 local l9 = list('abc', { 1, 2, nil }, 'def') 1313 1314 eq(true, lib.tv_list_equal(l1, l1, true)) 1315 eq(false, lib.tv_list_equal(l1, l2, true)) 1316 eq(true, lib.tv_list_equal(l1, l3, true)) 1317 eq(false, lib.tv_list_equal(l1, l4, true)) 1318 eq(true, lib.tv_list_equal(l1, l5, true)) 1319 eq(true, lib.tv_list_equal(l1, l6, true)) 1320 eq(true, lib.tv_list_equal(l1, l7, true)) 1321 eq(false, lib.tv_list_equal(l1, l8, true)) 1322 eq(false, lib.tv_list_equal(l1, l9, true)) 1323 end) 1324 end) 1325 describe('find', function() 1326 describe('()', function() 1327 itp('correctly indexes list', function() 1328 local l = list(1, 2, 3, 4, 5) 1329 local lis = list_items(l) 1330 alloc_log:clear() 1331 1332 eq(nil, lib.tv_list_find(nil, -1)) 1333 eq(nil, lib.tv_list_find(nil, 0)) 1334 eq(nil, lib.tv_list_find(nil, 1)) 1335 1336 eq(nil, lib.tv_list_find(l, 5)) 1337 eq(nil, lib.tv_list_find(l, -6)) 1338 eq(lis[1], lib.tv_list_find(l, -5)) 1339 eq(lis[5], lib.tv_list_find(l, 4)) 1340 eq(lis[3], lib.tv_list_find(l, 2)) 1341 eq(lis[3], lib.tv_list_find(l, -3)) 1342 eq(lis[3], lib.tv_list_find(l, 2)) 1343 eq(lis[3], lib.tv_list_find(l, 2)) 1344 eq(lis[3], lib.tv_list_find(l, -3)) 1345 1346 l.lv_idx_item = nil 1347 eq(lis[1], lib.tv_list_find(l, -5)) 1348 l.lv_idx_item = nil 1349 eq(lis[5], lib.tv_list_find(l, 4)) 1350 l.lv_idx_item = nil 1351 eq(lis[3], lib.tv_list_find(l, 2)) 1352 l.lv_idx_item = nil 1353 eq(lis[3], lib.tv_list_find(l, -3)) 1354 l.lv_idx_item = nil 1355 eq(lis[3], lib.tv_list_find(l, 2)) 1356 l.lv_idx_item = nil 1357 eq(lis[3], lib.tv_list_find(l, 2)) 1358 l.lv_idx_item = nil 1359 eq(lis[3], lib.tv_list_find(l, -3)) 1360 1361 l.lv_idx_item = nil 1362 eq(lis[3], lib.tv_list_find(l, 2)) 1363 eq(lis[1], lib.tv_list_find(l, -5)) 1364 eq(lis[3], lib.tv_list_find(l, 2)) 1365 eq(lis[5], lib.tv_list_find(l, 4)) 1366 eq(lis[3], lib.tv_list_find(l, 2)) 1367 eq(lis[3], lib.tv_list_find(l, 2)) 1368 eq(lis[3], lib.tv_list_find(l, 2)) 1369 eq(lis[3], lib.tv_list_find(l, -3)) 1370 eq(lis[3], lib.tv_list_find(l, 2)) 1371 eq(lis[3], lib.tv_list_find(l, 2)) 1372 eq(lis[3], lib.tv_list_find(l, 2)) 1373 eq(lis[3], lib.tv_list_find(l, -3)) 1374 1375 alloc_log:check({}) 1376 end) 1377 end) 1378 describe('nr()', function() 1379 local function tv_list_find_nr(l, n, msg) 1380 return check_emsg(function() 1381 local err = ffi.new('bool[1]', { false }) 1382 local ret = lib.tv_list_find_nr(l, n, err) 1383 return (err[0] == true), ret 1384 end, msg) 1385 end 1386 itp('returns correct number', function() 1387 local l = list(int(1), int(2), int(3), int(4), int(5)) 1388 alloc_log:clear() 1389 1390 eq({ false, 1 }, { tv_list_find_nr(l, -5) }) 1391 eq({ false, 5 }, { tv_list_find_nr(l, 4) }) 1392 eq({ false, 3 }, { tv_list_find_nr(l, 2) }) 1393 eq({ false, 3 }, { tv_list_find_nr(l, -3) }) 1394 1395 alloc_log:check({}) 1396 end) 1397 itp('returns correct number when given a string', function() 1398 local l = list('1', '2', '3', '4', '5') 1399 alloc_log:clear() 1400 1401 eq({ false, 1 }, { tv_list_find_nr(l, -5) }) 1402 eq({ false, 5 }, { tv_list_find_nr(l, 4) }) 1403 eq({ false, 3 }, { tv_list_find_nr(l, 2) }) 1404 eq({ false, 3 }, { tv_list_find_nr(l, -3) }) 1405 1406 alloc_log:check({}) 1407 end) 1408 itp('returns zero when given a NULL string', function() 1409 local l = list(null_string) 1410 alloc_log:clear() 1411 1412 eq({ false, 0 }, { tv_list_find_nr(l, 0) }) 1413 1414 alloc_log:check({}) 1415 end) 1416 itp('errors out on NULL lists', function() 1417 eq({ true, -1 }, { tv_list_find_nr(nil, -5) }) 1418 eq({ true, -1 }, { tv_list_find_nr(nil, 4) }) 1419 eq({ true, -1 }, { tv_list_find_nr(nil, 2) }) 1420 eq({ true, -1 }, { tv_list_find_nr(nil, -3) }) 1421 1422 alloc_log:check({}) 1423 end) 1424 itp('errors out on out-of-range indexes', function() 1425 local l = list(int(1), int(2), int(3), int(4), int(5)) 1426 alloc_log:clear() 1427 1428 eq({ true, -1 }, { tv_list_find_nr(l, -6) }) 1429 eq({ true, -1 }, { tv_list_find_nr(l, 5) }) 1430 1431 alloc_log:check({}) 1432 end) 1433 itp('errors out on invalid types', function() 1434 local l = list(1, empty_list, {}) 1435 1436 eq({ true, 0 }, { tv_list_find_nr(l, 0, 'E805: Using a Float as a Number') }) 1437 eq({ true, 0 }, { tv_list_find_nr(l, 1, 'E745: Using a List as a Number') }) 1438 eq({ true, 0 }, { tv_list_find_nr(l, 2, 'E728: Using a Dictionary as a Number') }) 1439 eq({ true, 0 }, { tv_list_find_nr(l, -1, 'E728: Using a Dictionary as a Number') }) 1440 eq({ true, 0 }, { tv_list_find_nr(l, -2, 'E745: Using a List as a Number') }) 1441 eq({ true, 0 }, { tv_list_find_nr(l, -3, 'E805: Using a Float as a Number') }) 1442 end) 1443 end) 1444 local function tv_list_find_str(l, n, msg) 1445 return check_emsg(function() 1446 local ret = lib.tv_list_find_str(l, n) 1447 local s = nil 1448 if ret ~= nil then 1449 s = ffi.string(ret) 1450 end 1451 return s 1452 end, msg) 1453 end 1454 describe('str()', function() 1455 itp('returns correct string', function() 1456 local l = list(int(1), 2.5, int(3), int(4), int(5)) 1457 alloc_log:clear() 1458 1459 eq('1', tv_list_find_str(l, -5)) 1460 eq('2.5', tv_list_find_str(l, 1)) 1461 eq('5', tv_list_find_str(l, 4)) 1462 eq('3', tv_list_find_str(l, 2)) 1463 eq('3', tv_list_find_str(l, -3)) 1464 1465 alloc_log:check({ a.freed(alloc_log.null), a.freed(alloc_log.null) }) 1466 end) 1467 itp('returns string when used with VAR_STRING items', function() 1468 local l = list('1', '2', '3', '4', '5') 1469 alloc_log:clear() 1470 1471 eq('1', tv_list_find_str(l, -5)) 1472 eq('5', tv_list_find_str(l, 4)) 1473 eq('3', tv_list_find_str(l, 2)) 1474 eq('3', tv_list_find_str(l, -3)) 1475 1476 alloc_log:check({}) 1477 end) 1478 itp('returns empty when used with NULL string', function() 1479 local l = list(null_string) 1480 alloc_log:clear() 1481 1482 eq('', tv_list_find_str(l, 0)) 1483 1484 alloc_log:check({}) 1485 end) 1486 itp('fails with error message when index is out of range', function() 1487 local l = list(int(1), int(2), int(3), int(4), int(5)) 1488 1489 eq(nil, tv_list_find_str(l, -6, 'E684: List index out of range: -6')) 1490 eq(nil, tv_list_find_str(l, 5, 'E684: List index out of range: 5')) 1491 end) 1492 itp('fails with error message on invalid types', function() 1493 local l = list(empty_list, {}) 1494 1495 eq('', tv_list_find_str(l, 0, 'E730: Using a List as a String')) 1496 eq('', tv_list_find_str(l, 1, 'E731: Using a Dictionary as a String')) 1497 eq('', tv_list_find_str(l, -1, 'E731: Using a Dictionary as a String')) 1498 eq('', tv_list_find_str(l, -2, 'E730: Using a List as a String')) 1499 end) 1500 end) 1501 end) 1502 describe('idx_of_item()', function() 1503 itp('works', function() 1504 local l = list(1, 2, 3, 4, 5) 1505 local l2 = list(42, empty_list) 1506 local lis = list_items(l) 1507 local lis2 = list_items(l2) 1508 1509 for i, li in ipairs(lis) do 1510 eq(i - 1, lib.tv_list_idx_of_item(l, li)) 1511 end 1512 eq(-1, lib.tv_list_idx_of_item(l, lis2[1])) 1513 eq(-1, lib.tv_list_idx_of_item(l, nil)) 1514 eq(-1, lib.tv_list_idx_of_item(nil, nil)) 1515 eq(-1, lib.tv_list_idx_of_item(nil, lis[1])) 1516 end) 1517 end) 1518 end) 1519 describe('dict', function() 1520 describe('watcher', function() 1521 describe('add()/remove()', function() 1522 itp('works with an empty key', function() 1523 local d = dict({}) 1524 eq({}, dict_watchers(d)) 1525 local cb = ffi.gc(tbl2callback({ type = 'none' }), nil) 1526 alloc_log:clear() 1527 lib.tv_dict_watcher_add(d, '*', 0, cb[0]) 1528 local ws, qs = dict_watchers(d) 1529 local key_p = qs[1].key_pattern 1530 alloc_log:check({ 1531 a.dwatcher(qs[1]), 1532 a.str(key_p, 0), 1533 }) 1534 eq({ { busy = false, cb = { type = 'none' }, pat = '' } }, ws) 1535 eq(true, lib.tv_dict_watcher_remove(d, 'x', 0, cb[0])) 1536 alloc_log:check({ 1537 a.freed(key_p), 1538 a.freed(qs[1]), 1539 }) 1540 eq({}, dict_watchers(d)) 1541 end) 1542 itp('works with multiple callbacks', function() 1543 local d = dict({}) 1544 eq({}, dict_watchers(d)) 1545 alloc_log:check({ a.dict(d) }) 1546 local cbs = {} 1547 cbs[1] = { 'te', ffi.gc(tbl2callback({ type = 'none' }), nil) } 1548 alloc_log:check({}) 1549 cbs[2] = { 'foo', ffi.gc(tbl2callback({ type = 'fref', fref = 'tr' }), nil) } 1550 alloc_log:check({ 1551 a.str(cbs[2][2].data.funcref, #'tr'), 1552 }) 1553 cbs[3] = { 1554 'te', 1555 ffi.gc( 1556 tbl2callback({ 1557 type = 'pt', 1558 fref = 'tr', 1559 pt = { 1560 value = 'tr', 1561 args = { 'test' }, 1562 dict = {}, 1563 }, 1564 }), 1565 nil 1566 ), 1567 } 1568 local pt3 = cbs[3][2].data.partial 1569 local pt3_argv = pt3.pt_argv 1570 local pt3_dict = pt3.pt_dict 1571 local pt3_name = pt3.pt_name 1572 local pt3_str_arg = pt3.pt_argv[0].vval.v_string 1573 alloc_log:check({ 1574 a.lua_pt(pt3), 1575 a.lua_tvs(pt3_argv, pt3.pt_argc), 1576 a.str(pt3_str_arg, #'test'), 1577 a.dict(pt3_dict), 1578 a.str(pt3_name, #'tr'), 1579 }) 1580 for _, v in ipairs(cbs) do 1581 lib.tv_dict_watcher_add(d, v[1], #v[1], v[2][0]) 1582 end 1583 local ws, qs, kps = dict_watchers(d) 1584 eq({ 1585 { busy = false, pat = cbs[1][1], cb = { type = 'none' } }, 1586 { busy = false, pat = cbs[2][1], cb = { type = 'fref', fref = 'tr' } }, 1587 { 1588 busy = false, 1589 pat = cbs[3][1], 1590 cb = { 1591 type = 'pt', 1592 fref = 'tr', 1593 pt = { 1594 [type_key] = func_type, 1595 value = 'tr', 1596 args = { 'test' }, 1597 dict = {}, 1598 }, 1599 }, 1600 }, 1601 }, ws) 1602 alloc_log:check({ 1603 a.dwatcher(qs[1]), 1604 a.str(kps[1][1], kps[1][2]), 1605 a.dwatcher(qs[2]), 1606 a.str(kps[2][1], kps[2][2]), 1607 a.dwatcher(qs[3]), 1608 a.str(kps[3][1], kps[3][2]), 1609 }) 1610 eq(true, lib.tv_dict_watcher_remove(d, cbs[2][1], #cbs[2][1], cbs[2][2][0])) 1611 alloc_log:check({ 1612 a.freed(cbs[2][2].data.funcref), 1613 a.freed(kps[2][1]), 1614 a.freed(qs[2]), 1615 }) 1616 eq(false, lib.tv_dict_watcher_remove(d, cbs[2][1], #cbs[2][1], cbs[2][2][0])) 1617 eq({ 1618 { busy = false, pat = cbs[1][1], cb = { type = 'none' } }, 1619 { 1620 busy = false, 1621 pat = cbs[3][1], 1622 cb = { 1623 type = 'pt', 1624 fref = 'tr', 1625 pt = { 1626 [type_key] = func_type, 1627 value = 'tr', 1628 args = { 'test' }, 1629 dict = {}, 1630 }, 1631 }, 1632 }, 1633 }, dict_watchers(d)) 1634 eq(true, lib.tv_dict_watcher_remove(d, cbs[3][1], #cbs[3][1], cbs[3][2][0])) 1635 alloc_log:check({ 1636 a.freed(pt3_str_arg), 1637 a.freed(pt3_argv), 1638 a.freed(pt3_dict), 1639 a.freed(pt3_name), 1640 a.freed(pt3), 1641 a.freed(kps[3][1]), 1642 a.freed(qs[3]), 1643 }) 1644 eq(false, lib.tv_dict_watcher_remove(d, cbs[3][1], #cbs[3][1], cbs[3][2][0])) 1645 eq({ { busy = false, pat = cbs[1][1], cb = { type = 'none' } } }, dict_watchers(d)) 1646 eq(true, lib.tv_dict_watcher_remove(d, cbs[1][1], #cbs[1][1], cbs[1][2][0])) 1647 alloc_log:check({ 1648 a.freed(kps[1][1]), 1649 a.freed(qs[1]), 1650 }) 1651 eq(false, lib.tv_dict_watcher_remove(d, cbs[1][1], #cbs[1][1], cbs[1][2][0])) 1652 eq({}, dict_watchers(d)) 1653 end) 1654 end) 1655 describe('notify', function() 1656 -- Way too hard to test it here, functional tests in 1657 -- dict_notifications_spec.lua. 1658 end) 1659 end) 1660 describe('item', function() 1661 describe('alloc()/free()', function() 1662 local function check_tv_dict_item_alloc_len(s, len, tv, more_frees) 1663 local di 1664 if len == nil then 1665 di = ffi.gc(lib.tv_dict_item_alloc(s), nil) 1666 len = #s 1667 else 1668 di = ffi.gc(lib.tv_dict_item_alloc_len(s, len or #s), nil) 1669 end 1670 eq(s:sub(1, len), ffi.string(di.di_key)) 1671 alloc_log:check({ a.di(di, len) }) 1672 if tv then 1673 di.di_tv = ffi.gc(tv, nil) 1674 else 1675 di.di_tv.v_type = lib.VAR_UNKNOWN 1676 end 1677 lib.tv_dict_item_free(di) 1678 alloc_log:check(concat_tables(more_frees, { a.freed(di) })) 1679 end 1680 local function check_tv_dict_item_alloc(s, tv, more_frees) 1681 return check_tv_dict_item_alloc_len(s, nil, tv, more_frees) 1682 end 1683 itp('works', function() 1684 check_tv_dict_item_alloc('') 1685 check_tv_dict_item_alloc('t') 1686 check_tv_dict_item_alloc('TEST') 1687 check_tv_dict_item_alloc_len('', 0) 1688 check_tv_dict_item_alloc_len('TEST', 2) 1689 local tv = lua2typvalt('test') 1690 alloc_log:check({ a.str(tv.vval.v_string, #'test') }) 1691 check_tv_dict_item_alloc('', tv, { a.freed(tv.vval.v_string) }) 1692 tv = lua2typvalt('test') 1693 alloc_log:check({ a.str(tv.vval.v_string, #'test') }) 1694 check_tv_dict_item_alloc_len('', 0, tv, { a.freed(tv.vval.v_string) }) 1695 end) 1696 end) 1697 describe('add()/remove()', function() 1698 itp('works', function() 1699 local d = dict() 1700 eq({}, dct2tbl(d)) 1701 alloc_log:check({ a.dict(d) }) 1702 local di = ffi.gc(lib.tv_dict_item_alloc(''), nil) 1703 local tv = lua2typvalt('test') 1704 di.di_tv = ffi.gc(tv, nil) 1705 alloc_log:check({ a.di(di, ''), a.str(tv.vval.v_string, 'test') }) 1706 eq(OK, lib.tv_dict_add(d, di)) 1707 alloc_log:check({}) 1708 eq( 1709 FAIL, 1710 check_emsg(function() 1711 return lib.tv_dict_add(d, di) 1712 end, 'E685: Internal error: hash_add(): duplicate key ""') 1713 ) 1714 alloc_log:clear() 1715 lib.tv_dict_item_remove(d, di) 1716 alloc_log:check({ 1717 a.freed(tv.vval.v_string), 1718 a.freed(di), 1719 }) 1720 end) 1721 end) 1722 end) 1723 describe('indexing', function() 1724 describe('find()', function() 1725 local function tv_dict_find(d, key, key_len) 1726 local di = lib.tv_dict_find(d, key, key_len or #key) 1727 if di == nil then 1728 return nil, nil, nil 1729 end 1730 return typvalt2lua(di.di_tv), ffi.string(di.di_key), di 1731 end 1732 itp('works with NULL dict', function() 1733 eq(nil, lib.tv_dict_find(nil, '', 0)) 1734 eq(nil, lib.tv_dict_find(nil, 'test', -1)) 1735 eq(nil, lib.tv_dict_find(nil, nil, 0)) 1736 end) 1737 itp('works with empty key', function() 1738 local lua_d = { 1739 [''] = 0, 1740 t = 1, 1741 te = 2, 1742 tes = 3, 1743 test = 4, 1744 testt = 5, 1745 } 1746 local d = dict(lua_d) 1747 alloc_log:clear() 1748 eq(lua_d, dct2tbl(d)) 1749 alloc_log:check({}) 1750 local dis = dict_items(d) 1751 eq({ 0, '', dis[''] }, { tv_dict_find(d, '', 0) }) 1752 end) 1753 itp('works with len properly', function() 1754 local lua_d = { 1755 [''] = 0, 1756 t = 1, 1757 te = 2, 1758 tes = 3, 1759 test = 4, 1760 testt = 5, 1761 } 1762 local d = dict(lua_d) 1763 alloc_log:clear() 1764 eq(lua_d, dct2tbl(d)) 1765 alloc_log:check({}) 1766 for i = 0, 5 do 1767 local v, k = tv_dict_find(d, 'testt', i) 1768 eq({ i, ('testt'):sub(1, i) }, { v, k }) 1769 end 1770 eq(nil, tv_dict_find(d, 'testt', 6)) -- Should take NUL byte 1771 eq(5, tv_dict_find(d, 'testt', -1)) 1772 alloc_log:check({}) 1773 end) 1774 end) 1775 describe('get_number()', function() 1776 itp('works with NULL dict', function() 1777 eq( 1778 0, 1779 check_emsg(function() 1780 return lib.tv_dict_get_number(nil, 'test') 1781 end, nil) 1782 ) 1783 end) 1784 itp('works', function() 1785 local d = ffi.gc(dict({ test = {} }), nil) 1786 eq( 1787 0, 1788 check_emsg(function() 1789 return lib.tv_dict_get_number(d, 'test') 1790 end, 'E728: Using a Dictionary as a Number') 1791 ) 1792 d = ffi.gc(dict({ tes = int(42), t = 44, te = '43' }), nil) 1793 alloc_log:clear() 1794 eq( 1795 0, 1796 check_emsg(function() 1797 return lib.tv_dict_get_number(d, 'test') 1798 end, nil) 1799 ) 1800 eq( 1801 42, 1802 check_emsg(function() 1803 return lib.tv_dict_get_number(d, 'tes') 1804 end, nil) 1805 ) 1806 eq( 1807 43, 1808 check_emsg(function() 1809 return lib.tv_dict_get_number(d, 'te') 1810 end, nil) 1811 ) 1812 alloc_log:check({}) 1813 eq( 1814 0, 1815 check_emsg(function() 1816 return lib.tv_dict_get_number(d, 't') 1817 end, 'E805: Using a Float as a Number') 1818 ) 1819 end) 1820 end) 1821 describe('get_string()', function() 1822 itp('works with NULL dict', function() 1823 eq( 1824 nil, 1825 check_emsg(function() 1826 return lib.tv_dict_get_string(nil, 'test', false) 1827 end, nil) 1828 ) 1829 end) 1830 itp('works', function() 1831 local d = ffi.gc(dict({ test = {} }), nil) 1832 eq( 1833 '', 1834 ffi.string(check_emsg(function() 1835 return lib.tv_dict_get_string(d, 'test', false) 1836 end, 'E731: Using a Dictionary as a String')) 1837 ) 1838 d = ffi.gc(dict({ tes = int(42), t = 44, te = '43', xx = int(45) }), nil) 1839 alloc_log:clear() 1840 local dis = dict_items(d) 1841 eq( 1842 nil, 1843 check_emsg(function() 1844 return lib.tv_dict_get_string(d, 'test', false) 1845 end, nil) 1846 ) 1847 local s42 = check_emsg(function() 1848 return lib.tv_dict_get_string(d, 'tes', false) 1849 end, nil) 1850 eq('42', ffi.string(s42)) 1851 local s45 = check_emsg(function() 1852 return lib.tv_dict_get_string(d, 'xx', false) 1853 end, nil) 1854 eq(s42, s45) 1855 eq('45', ffi.string(s45)) 1856 eq('45', ffi.string(s42)) 1857 local s43 = check_emsg(function() 1858 return lib.tv_dict_get_string(d, 'te', false) 1859 end, nil) 1860 eq('43', ffi.string(s43)) 1861 neq(s42, s43) 1862 eq(s43, dis.te.di_tv.vval.v_string) 1863 alloc_log:check({}) 1864 local s44 = check_emsg(function() 1865 return lib.tv_dict_get_string(d, 't', false) 1866 end, nil) 1867 eq('44.0', ffi.string(s44)) 1868 alloc_log:check({ a.freed(alloc_log.null), a.freed(alloc_log.null) }) 1869 end) 1870 itp('allocates a string copy when requested', function() 1871 local function tv_dict_get_string_alloc(d, key, emsg, is_float) 1872 alloc_log:clear() 1873 local ret = check_emsg(function() 1874 return lib.tv_dict_get_string(d, key, true) 1875 end, emsg) 1876 local s_ret = (ret ~= nil) and ffi.string(ret) or nil 1877 if not emsg then 1878 if s_ret then 1879 if is_float then 1880 alloc_log:check({ 1881 a.freed(alloc_log.null), 1882 a.freed(alloc_log.null), 1883 a.str(ret, s_ret), 1884 }) 1885 else 1886 alloc_log:check({ a.str(ret, s_ret) }) 1887 end 1888 else 1889 alloc_log:check({}) 1890 end 1891 end 1892 lib.xfree(ret) 1893 return s_ret 1894 end 1895 local d = ffi.gc(dict({ test = {} }), nil) 1896 eq('', tv_dict_get_string_alloc(d, 'test', 'E731: Using a Dictionary as a String')) 1897 d = ffi.gc(dict({ tes = int(42), t = 44, te = '43', xx = int(45) }), nil) 1898 alloc_log:clear() 1899 eq(nil, tv_dict_get_string_alloc(d, 'test')) 1900 eq('42', tv_dict_get_string_alloc(d, 'tes')) 1901 eq('45', tv_dict_get_string_alloc(d, 'xx')) 1902 eq('43', tv_dict_get_string_alloc(d, 'te')) 1903 eq('44.0', tv_dict_get_string_alloc(d, 't', nil, true)) 1904 end) 1905 end) 1906 describe('get_string_buf()', function() 1907 local function tv_dict_get_string_buf(d, key, is_float, buf, emsg) 1908 buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) 1909 alloc_log:clear() 1910 local ret = check_emsg(function() 1911 return lib.tv_dict_get_string_buf(d, key, buf) 1912 end, emsg) 1913 local s_ret = (ret ~= nil) and ffi.string(ret) or nil 1914 if not emsg then 1915 if is_float then 1916 alloc_log:check({ a.freed(alloc_log.null), a.freed(alloc_log.null) }) 1917 else 1918 alloc_log:check({}) 1919 end 1920 end 1921 return s_ret, ret, buf 1922 end 1923 itp('works with NULL dict', function() 1924 eq(nil, tv_dict_get_string_buf(nil, 'test')) 1925 end) 1926 itp('works', function() 1927 local lua_d = { 1928 [''] = {}, 1929 t = 1, 1930 te = int(2), 1931 tes = empty_list, 1932 test = 'tset', 1933 testt = 5, 1934 } 1935 local d = dict(lua_d) 1936 alloc_log:clear() 1937 eq(lua_d, dct2tbl(d)) 1938 alloc_log:check({}) 1939 local s, r, b 1940 s, r, b = tv_dict_get_string_buf(d, 'test') 1941 neq(r, b) 1942 eq('tset', s) 1943 s, r, b = tv_dict_get_string_buf(d, 't', true) 1944 eq(r, b) 1945 eq('1.0', s) 1946 s, r, b = tv_dict_get_string_buf(d, 'te') 1947 eq(r, b) 1948 eq('2', s) 1949 end) 1950 end) 1951 describe('get_string_buf_chk()', function() 1952 local function tv_dict_get_string_buf_chk(d, key, is_float, len, buf, def, emsg) 1953 buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) 1954 def = def or ffi.gc(lib.xstrdup('DEFAULT'), lib.xfree) 1955 len = len or #key 1956 alloc_log:clear() 1957 local ret = check_emsg(function() 1958 return lib.tv_dict_get_string_buf_chk(d, key, len, buf, def) 1959 end, emsg) 1960 local s_ret = (ret ~= nil) and ffi.string(ret) or nil 1961 if not emsg then 1962 if is_float then 1963 alloc_log:check({ a.freed(alloc_log.null), a.freed(alloc_log.null) }) 1964 else 1965 alloc_log:check({}) 1966 end 1967 end 1968 return s_ret, ret, buf, def 1969 end 1970 itp('works with NULL dict', function() 1971 eq('DEFAULT', tv_dict_get_string_buf_chk(nil, 'test')) 1972 end) 1973 itp('works', function() 1974 local lua_d = { 1975 [''] = {}, 1976 t = 1, 1977 te = int(2), 1978 tes = empty_list, 1979 test = 'tset', 1980 testt = 5, 1981 } 1982 local d = dict(lua_d) 1983 alloc_log:clear() 1984 eq(lua_d, dct2tbl(d)) 1985 alloc_log:check({}) 1986 local s, r, b, def 1987 s, r, b, def = tv_dict_get_string_buf_chk(d, 'test') 1988 neq(r, b) 1989 neq(r, def) 1990 eq('tset', s) 1991 s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', true, 1) 1992 eq(r, b) 1993 neq(r, def) 1994 eq('1.0', s) 1995 s, r, b, def = tv_dict_get_string_buf_chk(d, 'te') 1996 eq(r, b) 1997 neq(r, def) 1998 eq('2', s) 1999 s, r, b, def = tv_dict_get_string_buf_chk(d, 'TEST') 2000 eq(r, def) 2001 neq(r, b) 2002 eq('DEFAULT', s) 2003 end) 2004 end) 2005 describe('get_callback()', function() 2006 local function tv_dict_get_callback(d, key, key_len, emsg) 2007 key_len = key_len or #key 2008 local cb = 2009 ffi.gc(ffi.cast('Callback*', lib.xmalloc(ffi.sizeof('Callback'))), lib.callback_free) 2010 alloc_log:clear() 2011 local ret = check_emsg(function() 2012 return lib.tv_dict_get_callback(d, key, key_len, cb) 2013 end, emsg) 2014 local cb_lua = callback2tbl(cb[0]) 2015 return cb_lua, ret 2016 end 2017 itp('works with NULL dict', function() 2018 eq({ { type = 'none' }, true }, { tv_dict_get_callback(nil, '') }) 2019 end) 2020 itp('works', function() 2021 local lua_d = { 2022 [''] = 'tr', 2023 t = int(1), 2024 te = { [type_key] = func_type, value = 'tr' }, 2025 tes = { [type_key] = func_type, value = 'tr', args = { 'a', 'b' } }, 2026 test = { [type_key] = func_type, value = 'Test', dict = { test = 1 }, args = {} }, 2027 testt = { [type_key] = func_type, value = 'Test', dict = { test = 1 }, args = { 1 } }, 2028 } 2029 local d = dict(lua_d) 2030 eq(lua_d, dct2tbl(d)) 2031 eq({ { type = 'fref', fref = 'tr' }, true }, { tv_dict_get_callback(d, '', -1) }) 2032 eq({ { type = 'none' }, true }, { tv_dict_get_callback(d, 'x', -1) }) 2033 eq({ { type = 'fref', fref = 'tr' }, true }, { tv_dict_get_callback(d, 'testt', 0) }) 2034 eq({ { type = 'none' }, false }, { 2035 tv_dict_get_callback( 2036 d, 2037 'test', 2038 1, 2039 'E6000: Argument is not a function or function name' 2040 ), 2041 }) 2042 eq({ { type = 'fref', fref = 'tr' }, true }, { tv_dict_get_callback(d, 'testt', 2) }) 2043 eq({ 2044 { 2045 type = 'pt', 2046 fref = 'tr', 2047 pt = { 2048 [type_key] = func_type, 2049 value = 'tr', 2050 args = { 'a', 'b' }, 2051 }, 2052 }, 2053 true, 2054 }, { tv_dict_get_callback(d, 'testt', 3) }) 2055 eq({ 2056 { 2057 type = 'pt', 2058 fref = 'Test', 2059 pt = { [type_key] = func_type, value = 'Test', dict = { test = 1 }, args = {} }, 2060 }, 2061 true, 2062 }, { tv_dict_get_callback(d, 'testt', 4) }) 2063 eq({ 2064 { 2065 type = 'pt', 2066 fref = 'Test', 2067 pt = { [type_key] = func_type, value = 'Test', dict = { test = 1 }, args = { 1 } }, 2068 }, 2069 true, 2070 }, { tv_dict_get_callback(d, 'testt', 5) }) 2071 end) 2072 end) 2073 end) 2074 describe('add', function() 2075 describe('()', function() 2076 itp('works', function() 2077 local di = lib.tv_dict_item_alloc_len('t-est', 5) 2078 alloc_log:check({ a.di(di, 't-est') }) 2079 di.di_tv.v_type = lib.VAR_NUMBER 2080 di.di_tv.vval.v_number = 42 2081 local d = dict({ test = 10 }) 2082 local dis = dict_items(d) 2083 alloc_log:check({ 2084 a.dict(d), 2085 a.di(dis.test, 'test'), 2086 }) 2087 eq({ test = 10 }, dct2tbl(d)) 2088 alloc_log:clear() 2089 eq(OK, lib.tv_dict_add(d, di)) 2090 alloc_log:check({}) 2091 eq({ test = 10, ['t-est'] = int(42) }, dct2tbl(d)) 2092 eq( 2093 FAIL, 2094 check_emsg(function() 2095 return lib.tv_dict_add(d, di) 2096 end, 'E685: Internal error: hash_add(): duplicate key "t-est"') 2097 ) 2098 end) 2099 end) 2100 describe('list()', function() 2101 itp('works', function() 2102 local l = list(1, 2, 3) 2103 alloc_log:clear() 2104 eq(1, l.lv_refcount) 2105 local d = dict({ test = 10 }) 2106 alloc_log:clear() 2107 eq({ test = 10 }, dct2tbl(d)) 2108 eq(OK, lib.tv_dict_add_list(d, 'testt', 3, l)) 2109 local dis = dict_items(d) 2110 alloc_log:check({ a.di(dis.tes, 'tes') }) 2111 eq({ test = 10, tes = { 1, 2, 3 } }, dct2tbl(d)) 2112 eq(2, l.lv_refcount) 2113 eq( 2114 FAIL, 2115 check_emsg(function() 2116 return lib.tv_dict_add_list(d, 'testt', 3, l) 2117 end, 'E685: Internal error: hash_add(): duplicate key "tes"') 2118 ) 2119 eq(2, l.lv_refcount) 2120 alloc_log:clear() 2121 lib.emsg_skip = lib.emsg_skip + 1 2122 eq( 2123 FAIL, 2124 check_emsg(function() 2125 return lib.tv_dict_add_list(d, 'testt', 3, l) 2126 end, nil) 2127 ) 2128 eq(2, l.lv_refcount) 2129 lib.emsg_skip = lib.emsg_skip - 1 2130 alloc_log:clear_tmp_allocs() 2131 alloc_log:check({}) 2132 end) 2133 end) 2134 describe('dict()', function() 2135 itp('works', function() 2136 local d2 = dict({ foo = 42 }) 2137 alloc_log:clear() 2138 eq(1, d2.dv_refcount) 2139 local d = dict({ test = 10 }) 2140 alloc_log:clear() 2141 eq({ test = 10 }, dct2tbl(d)) 2142 eq(OK, lib.tv_dict_add_dict(d, 'testt', 3, d2)) 2143 local dis = dict_items(d) 2144 alloc_log:check({ a.di(dis.tes, 'tes') }) 2145 eq({ test = 10, tes = { foo = 42 } }, dct2tbl(d)) 2146 eq(2, d2.dv_refcount) 2147 eq( 2148 FAIL, 2149 check_emsg(function() 2150 return lib.tv_dict_add_dict(d, 'testt', 3, d2) 2151 end, 'E685: Internal error: hash_add(): duplicate key "tes"') 2152 ) 2153 eq(2, d2.dv_refcount) 2154 alloc_log:clear() 2155 lib.emsg_skip = lib.emsg_skip + 1 2156 eq( 2157 FAIL, 2158 check_emsg(function() 2159 return lib.tv_dict_add_dict(d, 'testt', 3, d2) 2160 end, nil) 2161 ) 2162 eq(2, d2.dv_refcount) 2163 lib.emsg_skip = lib.emsg_skip - 1 2164 alloc_log:clear_tmp_allocs() 2165 alloc_log:check({}) 2166 end) 2167 end) 2168 describe('nr()', function() 2169 itp('works', function() 2170 local d = dict({ test = 10 }) 2171 alloc_log:clear() 2172 eq({ test = 10 }, dct2tbl(d)) 2173 eq(OK, lib.tv_dict_add_nr(d, 'testt', 3, 2)) 2174 local dis = dict_items(d) 2175 alloc_log:check({ a.di(dis.tes, 'tes') }) 2176 eq({ test = 10, tes = int(2) }, dct2tbl(d)) 2177 eq( 2178 FAIL, 2179 check_emsg(function() 2180 return lib.tv_dict_add_nr(d, 'testt', 3, 2) 2181 end, 'E685: Internal error: hash_add(): duplicate key "tes"') 2182 ) 2183 alloc_log:clear() 2184 lib.emsg_skip = lib.emsg_skip + 1 2185 eq( 2186 FAIL, 2187 check_emsg(function() 2188 return lib.tv_dict_add_nr(d, 'testt', 3, 2) 2189 end, nil) 2190 ) 2191 lib.emsg_skip = lib.emsg_skip - 1 2192 alloc_log:clear_tmp_allocs() 2193 alloc_log:check({}) 2194 end) 2195 end) 2196 describe('float()', function() 2197 itp('works', function() 2198 local d = dict({ test = 10 }) 2199 alloc_log:clear() 2200 eq({ test = 10 }, dct2tbl(d)) 2201 eq(OK, lib.tv_dict_add_float(d, 'testt', 3, 1.5)) 2202 local dis = dict_items(d) 2203 alloc_log:check({ a.di(dis.tes, 'tes') }) 2204 eq({ test = 10, tes = 1.5 }, dct2tbl(d)) 2205 eq( 2206 FAIL, 2207 check_emsg(function() 2208 return lib.tv_dict_add_float(d, 'testt', 3, 1.5) 2209 end, 'E685: Internal error: hash_add(): duplicate key "tes"') 2210 ) 2211 alloc_log:clear() 2212 lib.emsg_skip = lib.emsg_skip + 1 2213 eq( 2214 FAIL, 2215 check_emsg(function() 2216 return lib.tv_dict_add_float(d, 'testt', 3, 1.5) 2217 end, nil) 2218 ) 2219 lib.emsg_skip = lib.emsg_skip - 1 2220 alloc_log:clear_tmp_allocs() 2221 alloc_log:check({}) 2222 end) 2223 end) 2224 describe('str()', function() 2225 itp('works', function() 2226 local d = dict({ test = 10 }) 2227 alloc_log:clear() 2228 eq({ test = 10 }, dct2tbl(d)) 2229 eq(OK, lib.tv_dict_add_str(d, 'testt', 3, 'TEST')) 2230 local dis = dict_items(d) 2231 alloc_log:check({ 2232 a.str(dis.tes.di_tv.vval.v_string, 'TEST'), 2233 a.di(dis.tes, 'tes'), 2234 }) 2235 eq({ test = 10, tes = 'TEST' }, dct2tbl(d)) 2236 eq( 2237 FAIL, 2238 check_emsg(function() 2239 return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') 2240 end, 'E685: Internal error: hash_add(): duplicate key "tes"') 2241 ) 2242 alloc_log:clear() 2243 lib.emsg_skip = lib.emsg_skip + 1 2244 eq( 2245 FAIL, 2246 check_emsg(function() 2247 return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') 2248 end, nil) 2249 ) 2250 lib.emsg_skip = lib.emsg_skip - 1 2251 alloc_log:clear_tmp_allocs() 2252 alloc_log:check({}) 2253 end) 2254 end) 2255 describe('allocated_str()', function() 2256 itp('works', function() 2257 local d = dict({ test = 10 }) 2258 eq({ test = 10 }, dct2tbl(d)) 2259 alloc_log:clear() 2260 local s1 = lib.xstrdup('TEST') 2261 local s2 = lib.xstrdup('TEST') 2262 local s3 = lib.xstrdup('TEST') 2263 alloc_log:check({ 2264 a.str(s1, 'TEST'), 2265 a.str(s2, 'TEST'), 2266 a.str(s3, 'TEST'), 2267 }) 2268 eq(OK, lib.tv_dict_add_allocated_str(d, 'testt', 3, s1)) 2269 local dis = dict_items(d) 2270 alloc_log:check({ 2271 a.di(dis.tes, 'tes'), 2272 }) 2273 eq({ test = 10, tes = 'TEST' }, dct2tbl(d)) 2274 eq( 2275 FAIL, 2276 check_emsg(function() 2277 return lib.tv_dict_add_allocated_str(d, 'testt', 3, s2) 2278 end, 'E685: Internal error: hash_add(): duplicate key "tes"') 2279 ) 2280 alloc_log:clear() 2281 lib.emsg_skip = lib.emsg_skip + 1 2282 eq( 2283 FAIL, 2284 check_emsg(function() 2285 return lib.tv_dict_add_allocated_str(d, 'testt', 3, s3) 2286 end, nil) 2287 ) 2288 lib.emsg_skip = lib.emsg_skip - 1 2289 alloc_log:clear_tmp_allocs() 2290 alloc_log:check({ 2291 a.freed(s3), 2292 }) 2293 end) 2294 end) 2295 end) 2296 describe('clear()', function() 2297 itp('works', function() 2298 local d = dict() 2299 alloc_log:check({ a.dict(d) }) 2300 eq({}, dct2tbl(d)) 2301 lib.tv_dict_clear(d) 2302 eq({}, dct2tbl(d)) 2303 lib.tv_dict_add_str(d, 'TEST', 3, 'tEsT') 2304 local dis = dict_items(d) 2305 local di = dis.TES 2306 local di_s = di.di_tv.vval.v_string 2307 alloc_log:check({ a.str(di_s), a.di(di) }) 2308 eq({ TES = 'tEsT' }, dct2tbl(d)) 2309 lib.tv_dict_clear(d) 2310 alloc_log:check({ a.freed(di_s), a.freed(di) }) 2311 eq({}, dct2tbl(d)) 2312 end) 2313 end) 2314 describe('extend()', function() 2315 local function tv_dict_extend(d1, d2, action, emsg) 2316 action = action or 'force' 2317 check_emsg(function() 2318 return lib.tv_dict_extend(d1, d2, action) 2319 end, emsg) 2320 end 2321 itp('works', function() 2322 local d1 = dict() 2323 alloc_log:check({ a.dict(d1) }) 2324 eq({}, dct2tbl(d1)) 2325 local d2 = dict() 2326 alloc_log:check({ a.dict(d2) }) 2327 eq({}, dct2tbl(d2)) 2328 tv_dict_extend(d1, d2, 'error') 2329 tv_dict_extend(d1, d2, 'keep') 2330 tv_dict_extend(d1, d2, 'force') 2331 alloc_log:check({}) 2332 2333 d1 = dict({ a = 'TEST' }) 2334 eq({ a = 'TEST' }, dct2tbl(d1)) 2335 local dis1 = dict_items(d1) 2336 local a1_s = dis1.a.di_tv.vval.v_string 2337 alloc_log:clear_tmp_allocs() 2338 alloc_log:check({ 2339 a.dict(d1), 2340 a.di(dis1.a), 2341 a.str(a1_s), 2342 }) 2343 d2 = dict({ a = 'TSET' }) 2344 eq({ a = 'TSET' }, dct2tbl(d2)) 2345 local dis2 = dict_items(d2) 2346 local a2_s = dis2.a.di_tv.vval.v_string 2347 alloc_log:clear_tmp_allocs() 2348 alloc_log:check({ 2349 a.dict(d2), 2350 a.di(dis2.a), 2351 a.str(a2_s), 2352 }) 2353 2354 tv_dict_extend(d1, d2, 'error', 'E737: Key already exists: a') 2355 eq({ a = 'TEST' }, dct2tbl(d1)) 2356 eq({ a = 'TSET' }, dct2tbl(d2)) 2357 alloc_log:clear() 2358 2359 tv_dict_extend(d1, d2, 'keep') 2360 alloc_log:check({}) 2361 eq({ a = 'TEST' }, dct2tbl(d1)) 2362 eq({ a = 'TSET' }, dct2tbl(d2)) 2363 2364 tv_dict_extend(d1, d2, 'force') 2365 alloc_log:check({ 2366 a.freed(a1_s), 2367 a.str(dis1.a.di_tv.vval.v_string), 2368 }) 2369 eq({ a = 'TSET' }, dct2tbl(d1)) 2370 eq({ a = 'TSET' }, dct2tbl(d2)) 2371 end) 2372 pending('disallows overriding builtin or user functions: here be the dragons', function() 2373 -- pending: see TODO below 2374 local d = dict() 2375 d.dv_scope = lib.VAR_DEF_SCOPE 2376 local f_lua = { 2377 [type_key] = func_type, 2378 value = 'tr', 2379 } 2380 local f_tv = lua2typvalt(f_lua) 2381 local p_lua = { 2382 [type_key] = func_type, 2383 value = 'tr', 2384 args = { 1 }, 2385 } 2386 local p_tv = lua2typvalt(p_lua) 2387 eq(lib.VAR_PARTIAL, p_tv.v_type) 2388 local d2 = dict({ tr = f_tv }) 2389 local d3 = dict({ tr = p_tv }) 2390 local d4 = dict({ ['TEST:THIS'] = p_tv }) 2391 local d5 = dict({ Test = f_tv }) 2392 local d6 = dict({ Test = p_tv }) 2393 eval0([[execute("function Test()\nendfunction")]]) 2394 -- TODO: test breaks at this point 2395 tv_dict_extend(d, d2, 'force', 'E704: Funcref variable name must start with a capital: tr') 2396 tv_dict_extend(d, d3, 'force', 'E704: Funcref variable name must start with a capital: tr') 2397 tv_dict_extend(d, d4, 'force', 'E461: Illegal variable name: TEST:THIS') 2398 tv_dict_extend(d, d5, 'force', 'E705: Variable name conflicts with existing function: Test') 2399 tv_dict_extend(d, d6, 'force', 'E705: Variable name conflicts with existing function: Test') 2400 eq({}, dct2tbl(d)) 2401 d.dv_scope = lib.VAR_SCOPE 2402 tv_dict_extend(d, d4, 'force', 'E461: Illegal variable name: TEST:THIS') 2403 eq({}, dct2tbl(d)) 2404 tv_dict_extend(d, d2, 'force') 2405 eq({ tr = f_lua }, dct2tbl(d)) 2406 tv_dict_extend(d, d3, 'force') 2407 eq({ tr = p_lua }, dct2tbl(d)) 2408 tv_dict_extend(d, d5, 'force') 2409 eq({ tr = p_lua, Test = f_lua }, dct2tbl(d)) 2410 tv_dict_extend(d, d6, 'force') 2411 eq({ tr = p_lua, Test = p_lua }, dct2tbl(d)) 2412 end) 2413 itp('cares about locks and read-only items', function() 2414 local d_lua = { tv_locked = 1, tv_fixed = 2, di_ro = 3, di_ro_sbx = 4 } 2415 local d = dict(d_lua) 2416 local dis = dict_items(d) 2417 dis.tv_locked.di_tv.v_lock = lib.VAR_LOCKED 2418 dis.tv_fixed.di_tv.v_lock = lib.VAR_FIXED 2419 dis.di_ro.di_flags = bit.bor(dis.di_ro.di_flags, lib.DI_FLAGS_RO) 2420 dis.di_ro_sbx.di_flags = bit.bor(dis.di_ro_sbx.di_flags, lib.DI_FLAGS_RO_SBX) 2421 lib.sandbox = true 2422 local d1 = dict({ tv_locked = 41 }) 2423 local d2 = dict({ tv_fixed = 42 }) 2424 local d3 = dict({ di_ro = 43 }) 2425 local d4 = dict({ di_ro_sbx = 44 }) 2426 tv_dict_extend(d, d1, 'force', 'E741: Value is locked: extend() argument') 2427 tv_dict_extend(d, d2, 'force', 'E742: Cannot change value of extend() argument') 2428 tv_dict_extend(d, d3, 'force', 'E46: Cannot change read-only variable "extend() argument"') 2429 tv_dict_extend( 2430 d, 2431 d4, 2432 'force', 2433 'E794: Cannot set variable in the sandbox: "extend() argument"' 2434 ) 2435 eq(d_lua, dct2tbl(d)) 2436 lib.sandbox = false 2437 tv_dict_extend(d, d4, 'force') 2438 d_lua.di_ro_sbx = 44 2439 eq(d_lua, dct2tbl(d)) 2440 end) 2441 end) 2442 describe('equal()', function() 2443 local function tv_dict_equal(d1, d2, ic) 2444 return lib.tv_dict_equal(d1, d2, ic or false) 2445 end 2446 itp('works', function() 2447 eq(true, tv_dict_equal(nil, nil)) 2448 local d1 = dict() 2449 alloc_log:check({ a.dict(d1) }) 2450 eq(1, d1.dv_refcount) 2451 eq(true, tv_dict_equal(nil, d1)) 2452 eq(true, tv_dict_equal(d1, nil)) 2453 eq(true, tv_dict_equal(d1, d1)) 2454 eq(1, d1.dv_refcount) 2455 alloc_log:check({}) 2456 local d_upper = dict({ a = 'TEST' }) 2457 local dis_upper = dict_items(d_upper) 2458 local d_lower = dict({ a = 'test' }) 2459 local dis_lower = dict_items(d_lower) 2460 local d_kupper_upper = dict({ A = 'TEST' }) 2461 local dis_kupper_upper = dict_items(d_kupper_upper) 2462 local d_kupper_lower = dict({ A = 'test' }) 2463 local dis_kupper_lower = dict_items(d_kupper_lower) 2464 alloc_log:clear_tmp_allocs() 2465 alloc_log:check({ 2466 a.dict(d_upper), 2467 a.di(dis_upper.a), 2468 a.str(dis_upper.a.di_tv.vval.v_string), 2469 2470 a.dict(d_lower), 2471 a.di(dis_lower.a), 2472 a.str(dis_lower.a.di_tv.vval.v_string), 2473 2474 a.dict(d_kupper_upper), 2475 a.di(dis_kupper_upper.A), 2476 a.str(dis_kupper_upper.A.di_tv.vval.v_string), 2477 2478 a.dict(d_kupper_lower), 2479 a.di(dis_kupper_lower.A), 2480 a.str(dis_kupper_lower.A.di_tv.vval.v_string), 2481 }) 2482 eq(true, tv_dict_equal(d_upper, d_upper)) 2483 eq(true, tv_dict_equal(d_upper, d_upper, true)) 2484 eq(false, tv_dict_equal(d_upper, d_lower, false)) 2485 eq(true, tv_dict_equal(d_upper, d_lower, true)) 2486 eq(true, tv_dict_equal(d_kupper_upper, d_kupper_lower, true)) 2487 eq(false, tv_dict_equal(d_kupper_upper, d_lower, true)) 2488 eq(false, tv_dict_equal(d_kupper_upper, d_upper, true)) 2489 alloc_log:check({}) 2490 end) 2491 end) 2492 describe('copy()', function() 2493 local function tv_dict_copy(...) 2494 return ffi.gc(lib.tv_dict_copy(...), lib.tv_dict_unref) 2495 end 2496 itp('copies NULL correctly', function() 2497 eq(nil, lib.tv_dict_copy(nil, nil, true, 0)) 2498 eq(nil, lib.tv_dict_copy(nil, nil, false, 0)) 2499 eq(nil, lib.tv_dict_copy(nil, nil, true, 1)) 2500 eq(nil, lib.tv_dict_copy(nil, nil, false, 1)) 2501 end) 2502 itp('copies dict correctly without converting items', function() 2503 do 2504 local v = { 2505 a = { ['«'] = '»' }, 2506 b = { '„' }, 2507 ['1'] = 1, 2508 ['«»'] = '“', 2509 ns = null_string, 2510 nl = null_list, 2511 nd = null_dict, 2512 } 2513 local d_tv = lua2typvalt(v) 2514 local d = d_tv.vval.v_dict 2515 local dis = dict_items(d) 2516 alloc_log:clear() 2517 2518 eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) 2519 eq(1, dis.b.di_tv.vval.v_list.lv_refcount) 2520 local d_copy1 = tv_dict_copy(nil, d, false, 0) 2521 eq(2, dis.a.di_tv.vval.v_dict.dv_refcount) 2522 eq(2, dis.b.di_tv.vval.v_list.lv_refcount) 2523 local dis_copy1 = dict_items(d_copy1) 2524 eq(dis.a.di_tv.vval.v_dict, dis_copy1.a.di_tv.vval.v_dict) 2525 eq(dis.b.di_tv.vval.v_list, dis_copy1.b.di_tv.vval.v_list) 2526 eq(v, dct2tbl(d_copy1)) 2527 alloc_log:clear() 2528 lib.tv_dict_free(ffi.gc(d_copy1, nil)) 2529 alloc_log:clear() 2530 2531 eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) 2532 eq(1, dis.b.di_tv.vval.v_list.lv_refcount) 2533 local d_deepcopy1 = tv_dict_copy(nil, d, true, 0) 2534 neq(nil, d_deepcopy1) 2535 eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) 2536 eq(1, dis.b.di_tv.vval.v_list.lv_refcount) 2537 local dis_deepcopy1 = dict_items(d_deepcopy1) 2538 neq(dis.a.di_tv.vval.v_dict, dis_deepcopy1.a.di_tv.vval.v_dict) 2539 neq(dis.b.di_tv.vval.v_list, dis_deepcopy1.b.di_tv.vval.v_list) 2540 eq(v, dct2tbl(d_deepcopy1)) 2541 alloc_log:clear() 2542 end 2543 collectgarbage() 2544 end) 2545 itp('copies dict correctly and converts items', function() 2546 local vc = vimconv_alloc() 2547 -- UTF-8 ↔ latin1 conversions need no iconv 2548 eq(OK, lib.convert_setup(vc, to_cstr('utf-8'), to_cstr('latin1'))) 2549 2550 local v = { 2551 a = { ['«'] = '»' }, 2552 b = { '„' }, 2553 ['1'] = 1, 2554 ['«»'] = '“', 2555 ns = null_string, 2556 nl = null_list, 2557 nd = null_dict, 2558 } 2559 local d_tv = lua2typvalt(v) 2560 local d = d_tv.vval.v_dict 2561 local dis = dict_items(d) 2562 alloc_log:clear() 2563 2564 eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) 2565 eq(1, dis.b.di_tv.vval.v_list.lv_refcount) 2566 local d_deepcopy1 = tv_dict_copy(vc, d, true, 0) 2567 neq(nil, d_deepcopy1) 2568 eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) 2569 eq(1, dis.b.di_tv.vval.v_list.lv_refcount) 2570 local dis_deepcopy1 = dict_items(d_deepcopy1) 2571 neq(dis.a.di_tv.vval.v_dict, dis_deepcopy1.a.di_tv.vval.v_dict) 2572 neq(dis.b.di_tv.vval.v_list, dis_deepcopy1.b.di_tv.vval.v_list) 2573 eq({ 2574 a = { ['\171'] = '\187' }, 2575 b = { '\191' }, 2576 ['1'] = 1, 2577 ['\171\187'] = '\191', 2578 ns = null_string, 2579 nl = null_list, 2580 nd = null_dict, 2581 }, dct2tbl(d_deepcopy1)) 2582 alloc_log:clear_tmp_allocs() 2583 alloc_log:clear() 2584 end) 2585 itp('returns different/same containers with(out) copyID', function() 2586 local d_inner_tv = lua2typvalt({}) 2587 local d_tv = lua2typvalt({ a = d_inner_tv, b = d_inner_tv }) 2588 eq(3, d_inner_tv.vval.v_dict.dv_refcount) 2589 local d = d_tv.vval.v_dict 2590 local dis = dict_items(d) 2591 eq(dis.a.di_tv.vval.v_dict, dis.b.di_tv.vval.v_dict) 2592 2593 local d_copy1 = tv_dict_copy(nil, d, true, 0) 2594 local dis_copy1 = dict_items(d_copy1) 2595 neq(dis_copy1.a.di_tv.vval.v_dict, dis_copy1.b.di_tv.vval.v_dict) 2596 eq({ a = {}, b = {} }, dct2tbl(d_copy1)) 2597 2598 local d_copy2 = tv_dict_copy(nil, d, true, 2) 2599 local dis_copy2 = dict_items(d_copy2) 2600 eq(dis_copy2.a.di_tv.vval.v_dict, dis_copy2.b.di_tv.vval.v_dict) 2601 eq({ a = {}, b = {} }, dct2tbl(d_copy2)) 2602 2603 eq(3, d_inner_tv.vval.v_dict.dv_refcount) 2604 end) 2605 itp('works with self-referencing dict with copyID', function() 2606 local d_tv = lua2typvalt({}) 2607 local d = d_tv.vval.v_dict 2608 eq(1, d.dv_refcount) 2609 lib.tv_dict_add_dict(d, 'test', 4, d) 2610 eq(2, d.dv_refcount) 2611 2612 local d_copy1 = tv_dict_copy(nil, d, true, 2) 2613 eq(2, d_copy1.dv_refcount) 2614 local v = {} 2615 v.test = v 2616 eq(v, dct2tbl(d_copy1)) 2617 2618 lib.tv_dict_clear(d) 2619 eq(1, d.dv_refcount) 2620 2621 lib.tv_dict_clear(d_copy1) 2622 eq(1, d_copy1.dv_refcount) 2623 end) 2624 end) 2625 describe('set_keys_readonly()', function() 2626 itp('works', function() 2627 local d = dict({ a = true }) 2628 local dis = dict_items(d) 2629 alloc_log:check({ a.dict(d), a.di(dis.a) }) 2630 eq(0, bit.band(dis.a.di_flags, lib.DI_FLAGS_RO)) 2631 eq(0, bit.band(dis.a.di_flags, lib.DI_FLAGS_FIX)) 2632 lib.tv_dict_set_keys_readonly(d) 2633 alloc_log:check({}) 2634 eq(lib.DI_FLAGS_RO, bit.band(dis.a.di_flags, lib.DI_FLAGS_RO)) 2635 eq(lib.DI_FLAGS_FIX, bit.band(dis.a.di_flags, lib.DI_FLAGS_FIX)) 2636 end) 2637 end) 2638 end) 2639 describe('tv', function() 2640 describe('alloc', function() 2641 describe('list ret()', function() 2642 itp('works', function() 2643 local rettv = typvalt(lib.VAR_UNKNOWN) 2644 local l = lib.tv_list_alloc_ret(rettv, 0) 2645 eq(empty_list, typvalt2lua(rettv)) 2646 eq(rettv.vval.v_list, l) 2647 end) 2648 end) 2649 describe('dict ret()', function() 2650 itp('works', function() 2651 local rettv = typvalt(lib.VAR_UNKNOWN) 2652 lib.tv_dict_alloc_ret(rettv) 2653 eq({}, typvalt2lua(rettv)) 2654 end) 2655 end) 2656 end) 2657 local function defalloc() 2658 return {} 2659 end 2660 describe('clear()', function() 2661 itp('works', function() 2662 local function deffrees(alloc_rets) 2663 local ret = {} 2664 for i = #alloc_rets, 1, -1 do 2665 ret[#alloc_rets - i + 1] = alloc_rets:freed(i) 2666 end 2667 return ret 2668 end 2669 alloc_log:check({}) 2670 lib.tv_clear(nil) 2671 alloc_log:check({}) 2672 local ll = {} 2673 local ll_l = nil 2674 ll[1] = ll 2675 local dd = {} 2676 local dd_d = nil 2677 dd.dd = dd 2678 for _, v in ipairs({ 2679 { nil_value }, 2680 { 2681 null_string, 2682 nil, 2683 function() 2684 return { a.freed(alloc_log.null) } 2685 end, 2686 }, 2687 { 0 }, 2688 { int(0) }, 2689 { true }, 2690 { false }, 2691 { 2692 'true', 2693 function(tv) 2694 return { a.str(tv.vval.v_string) } 2695 end, 2696 }, 2697 { 2698 {}, 2699 function(tv) 2700 return { a.dict(tv.vval.v_dict) } 2701 end, 2702 }, 2703 { 2704 empty_list, 2705 function(tv) 2706 return { a.list(tv.vval.v_list) } 2707 end, 2708 }, 2709 { 2710 ll, 2711 function(tv) 2712 ll_l = tv.vval.v_list 2713 return { a.list(tv.vval.v_list), a.li(tv.vval.v_list.lv_first) } 2714 end, 2715 defalloc, 2716 }, 2717 { 2718 dd, 2719 function(tv) 2720 dd_d = tv.vval.v_dict 2721 return { a.dict(tv.vval.v_dict), a.di(first_di(tv.vval.v_dict)) } 2722 end, 2723 defalloc, 2724 }, 2725 }) do 2726 local tv = lua2typvalt(v[1]) 2727 local alloc_rets = {} 2728 alloc_log:check(get_alloc_rets((v[2] or defalloc)(tv), alloc_rets)) 2729 lib.tv_clear(tv) 2730 alloc_log:check((v[3] or deffrees)(alloc_rets)) 2731 end 2732 eq(1, ll_l.lv_refcount) 2733 eq(1, dd_d.dv_refcount) 2734 end) 2735 end) 2736 describe('copy()', function() 2737 itp('works', function() 2738 local function strallocs(tv) 2739 return { a.str(tv.vval.v_string) } 2740 end 2741 for _, v in ipairs({ 2742 { nil_value }, 2743 { null_string }, 2744 { 0 }, 2745 { int(0) }, 2746 { true }, 2747 { false }, 2748 { 2749 {}, 2750 function(tv) 2751 return { a.dict(tv.vval.v_dict) } 2752 end, 2753 nil, 2754 function(from, to) 2755 eq(2, to.vval.v_dict.dv_refcount) 2756 eq(to.vval.v_dict, from.vval.v_dict) 2757 end, 2758 }, 2759 { 2760 empty_list, 2761 function(tv) 2762 return { a.list(tv.vval.v_list) } 2763 end, 2764 nil, 2765 function(from, to) 2766 eq(2, to.vval.v_list.lv_refcount) 2767 eq(to.vval.v_list, from.vval.v_list) 2768 end, 2769 }, 2770 { 2771 'test', 2772 strallocs, 2773 strallocs, 2774 function(from, to) 2775 neq(to.vval.v_string, from.vval.v_string) 2776 end, 2777 }, 2778 }) do 2779 local from = lua2typvalt(v[1]) 2780 alloc_log:check((v[2] or defalloc)(from)) 2781 local to = typvalt(lib.VAR_UNKNOWN) 2782 lib.tv_copy(from, to) 2783 local res = v[1] 2784 eq(res, typvalt2lua(to)) 2785 alloc_log:check((v[3] or defalloc)(to)) 2786 if v[4] then 2787 v[4](from, to) 2788 end 2789 end 2790 end) 2791 end) 2792 describe('item_lock()', function() 2793 itp('does not alter VAR_PARTIAL', function() 2794 local p_tv = lua2typvalt({ 2795 [type_key] = func_type, 2796 value = 'tr', 2797 dict = {}, 2798 }) 2799 lib.tv_item_lock(p_tv, -1, true, false) 2800 eq(lib.VAR_UNLOCKED, p_tv.vval.v_partial.pt_dict.dv_lock) 2801 end) 2802 itp('does not change VAR_FIXED values', function() 2803 local d_tv = lua2typvalt({}) 2804 local l_tv = lua2typvalt(empty_list) 2805 alloc_log:clear() 2806 d_tv.v_lock = lib.VAR_FIXED 2807 d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED 2808 l_tv.v_lock = lib.VAR_FIXED 2809 l_tv.vval.v_list.lv_lock = lib.VAR_FIXED 2810 lib.tv_item_lock(d_tv, 1, true, false) 2811 lib.tv_item_lock(l_tv, 1, true, false) 2812 eq(lib.VAR_FIXED, d_tv.v_lock) 2813 eq(lib.VAR_FIXED, l_tv.v_lock) 2814 eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock) 2815 eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock) 2816 lib.tv_item_lock(d_tv, 1, false, false) 2817 lib.tv_item_lock(l_tv, 1, false, false) 2818 eq(lib.VAR_FIXED, d_tv.v_lock) 2819 eq(lib.VAR_FIXED, l_tv.v_lock) 2820 eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock) 2821 eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock) 2822 alloc_log:check({}) 2823 end) 2824 itp('works with NULL values', function() 2825 local l_tv = lua2typvalt(null_list) 2826 local d_tv = lua2typvalt(null_dict) 2827 local s_tv = lua2typvalt(null_string) 2828 alloc_log:clear() 2829 lib.tv_item_lock(l_tv, 1, true, false) 2830 lib.tv_item_lock(d_tv, 1, true, false) 2831 lib.tv_item_lock(s_tv, 1, true, false) 2832 eq(null_list, typvalt2lua(l_tv)) 2833 eq(null_dict, typvalt2lua(d_tv)) 2834 eq(null_string, typvalt2lua(s_tv)) 2835 eq(lib.VAR_LOCKED, d_tv.v_lock) 2836 eq(lib.VAR_LOCKED, l_tv.v_lock) 2837 eq(lib.VAR_LOCKED, s_tv.v_lock) 2838 alloc_log:check({}) 2839 end) 2840 end) 2841 describe('islocked()', function() 2842 itp('works with NULL values', function() 2843 local l_tv = lua2typvalt(null_list) 2844 local d_tv = lua2typvalt(null_dict) 2845 eq(false, lib.tv_islocked(l_tv)) 2846 eq(false, lib.tv_islocked(d_tv)) 2847 end) 2848 itp('works', function() 2849 local tv = lua2typvalt() 2850 local d_tv = lua2typvalt({}) 2851 local l_tv = lua2typvalt(empty_list) 2852 alloc_log:clear() 2853 eq(false, lib.tv_islocked(tv)) 2854 eq(false, lib.tv_islocked(l_tv)) 2855 eq(false, lib.tv_islocked(d_tv)) 2856 d_tv.vval.v_dict.dv_lock = lib.VAR_LOCKED 2857 l_tv.vval.v_list.lv_lock = lib.VAR_LOCKED 2858 eq(true, lib.tv_islocked(l_tv)) 2859 eq(true, lib.tv_islocked(d_tv)) 2860 tv.v_lock = lib.VAR_LOCKED 2861 d_tv.v_lock = lib.VAR_LOCKED 2862 l_tv.v_lock = lib.VAR_LOCKED 2863 eq(true, lib.tv_islocked(tv)) 2864 eq(true, lib.tv_islocked(l_tv)) 2865 eq(true, lib.tv_islocked(d_tv)) 2866 d_tv.vval.v_dict.dv_lock = lib.VAR_UNLOCKED 2867 l_tv.vval.v_list.lv_lock = lib.VAR_UNLOCKED 2868 eq(true, lib.tv_islocked(tv)) 2869 eq(true, lib.tv_islocked(l_tv)) 2870 eq(true, lib.tv_islocked(d_tv)) 2871 tv.v_lock = lib.VAR_FIXED 2872 d_tv.v_lock = lib.VAR_FIXED 2873 l_tv.v_lock = lib.VAR_FIXED 2874 eq(false, lib.tv_islocked(tv)) 2875 eq(false, lib.tv_islocked(l_tv)) 2876 eq(false, lib.tv_islocked(d_tv)) 2877 d_tv.vval.v_dict.dv_lock = lib.VAR_LOCKED 2878 l_tv.vval.v_list.lv_lock = lib.VAR_LOCKED 2879 eq(true, lib.tv_islocked(l_tv)) 2880 eq(true, lib.tv_islocked(d_tv)) 2881 d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED 2882 l_tv.vval.v_list.lv_lock = lib.VAR_FIXED 2883 eq(false, lib.tv_islocked(l_tv)) 2884 eq(false, lib.tv_islocked(d_tv)) 2885 alloc_log:check({}) 2886 end) 2887 end) 2888 describe('check_lock()', function() 2889 local function tv_check_lock(lock, name, name_len, emsg) 2890 return check_emsg(function() 2891 return lib.value_check_lock(lock, name, name_len) 2892 end, emsg) 2893 end 2894 itp('works', function() 2895 eq(false, tv_check_lock(lib.VAR_UNLOCKED, 'test', 3)) 2896 eq(true, tv_check_lock(lib.VAR_LOCKED, 'test', 3, 'E741: Value is locked: tes')) 2897 eq(true, tv_check_lock(lib.VAR_FIXED, 'test', 3, 'E742: Cannot change value of tes')) 2898 eq(true, tv_check_lock(lib.VAR_LOCKED, nil, 0, 'E741: Value is locked')) 2899 eq(true, tv_check_lock(lib.VAR_FIXED, nil, 0, 'E742: Cannot change value')) 2900 eq(true, tv_check_lock(lib.VAR_LOCKED, nil, lib.kTVCstring, 'E741: Value is locked')) 2901 eq( 2902 true, 2903 tv_check_lock(lib.VAR_FIXED, 'test', lib.kTVCstring, 'E742: Cannot change value of test') 2904 ) 2905 end) 2906 end) 2907 describe('equal()', function() 2908 itp('compares empty and NULL lists correctly', function() 2909 local l = lua2typvalt(empty_list) 2910 local l2 = lua2typvalt(empty_list) 2911 local nl = lua2typvalt(null_list) 2912 2913 -- NULL lists are equal to empty lists 2914 eq(true, lib.tv_equal(l, nl, true)) 2915 eq(true, lib.tv_equal(nl, l, false)) 2916 2917 -- NULL lists are equal themselves 2918 eq(true, lib.tv_equal(nl, nl, true)) 2919 eq(true, lib.tv_equal(nl, nl, false)) 2920 2921 -- As well as empty lists 2922 eq(true, lib.tv_equal(l, l, true)) 2923 eq(true, lib.tv_equal(l, l2, false)) 2924 eq(true, lib.tv_equal(l2, l, false)) 2925 eq(true, lib.tv_equal(l2, l2, true)) 2926 end) 2927 itp('compares lists correctly when case is not ignored', function() 2928 local l1 = lua2typvalt({ 'abc', { 1, 2, 'Abc' }, 'def' }) 2929 local l2 = lua2typvalt({ 'abc', { 1, 2, 'Abc' } }) 2930 local l3 = lua2typvalt({ 'abc', { 1, 2, 'Abc' }, 'Def' }) 2931 local l4 = lua2typvalt({ 'abc', { 1, 2, 'Abc', 4 }, 'def' }) 2932 local l5 = lua2typvalt({ 'Abc', { 1, 2, 'Abc' }, 'def' }) 2933 local l6 = lua2typvalt({ 'abc', { 1, 2, 'Abc' }, 'def' }) 2934 local l7 = lua2typvalt({ 'abc', { 1, 2, 'abc' }, 'def' }) 2935 local l8 = lua2typvalt({ 'abc', nil, 'def' }) 2936 local l9 = lua2typvalt({ 'abc', { 1, 2, nil }, 'def' }) 2937 2938 eq(true, lib.tv_equal(l1, l1, false)) 2939 eq(false, lib.tv_equal(l1, l2, false)) 2940 eq(false, lib.tv_equal(l1, l3, false)) 2941 eq(false, lib.tv_equal(l1, l4, false)) 2942 eq(false, lib.tv_equal(l1, l5, false)) 2943 eq(true, lib.tv_equal(l1, l6, false)) 2944 eq(false, lib.tv_equal(l1, l7, false)) 2945 eq(false, lib.tv_equal(l1, l8, false)) 2946 eq(false, lib.tv_equal(l1, l9, false)) 2947 end) 2948 itp('compares lists correctly when case is ignored', function() 2949 local l1 = lua2typvalt({ 'abc', { 1, 2, 'Abc' }, 'def' }) 2950 local l2 = lua2typvalt({ 'abc', { 1, 2, 'Abc' } }) 2951 local l3 = lua2typvalt({ 'abc', { 1, 2, 'Abc' }, 'Def' }) 2952 local l4 = lua2typvalt({ 'abc', { 1, 2, 'Abc', 4 }, 'def' }) 2953 local l5 = lua2typvalt({ 'Abc', { 1, 2, 'Abc' }, 'def' }) 2954 local l6 = lua2typvalt({ 'abc', { 1, 2, 'Abc' }, 'def' }) 2955 local l7 = lua2typvalt({ 'abc', { 1, 2, 'abc' }, 'def' }) 2956 local l8 = lua2typvalt({ 'abc', nil, 'def' }) 2957 local l9 = lua2typvalt({ 'abc', { 1, 2, nil }, 'def' }) 2958 2959 eq(true, lib.tv_equal(l1, l1, true)) 2960 eq(false, lib.tv_equal(l1, l2, true)) 2961 eq(true, lib.tv_equal(l1, l3, true)) 2962 eq(false, lib.tv_equal(l1, l4, true)) 2963 eq(true, lib.tv_equal(l1, l5, true)) 2964 eq(true, lib.tv_equal(l1, l6, true)) 2965 eq(true, lib.tv_equal(l1, l7, true)) 2966 eq(false, lib.tv_equal(l1, l8, true)) 2967 eq(false, lib.tv_equal(l1, l9, true)) 2968 end) 2969 local function tv_equal(d1, d2, ic) 2970 return lib.tv_equal(d1, d2, ic or false) 2971 end 2972 itp('works with dictionaries', function() 2973 local nd = lua2typvalt(null_dict) 2974 eq(true, tv_equal(nd, nd)) 2975 alloc_log:check({}) 2976 local d1 = lua2typvalt({}) 2977 alloc_log:check({ a.dict(d1.vval.v_dict) }) 2978 eq(1, d1.vval.v_dict.dv_refcount) 2979 eq(true, tv_equal(nd, d1)) 2980 eq(true, tv_equal(d1, nd)) 2981 eq(true, tv_equal(d1, d1)) 2982 eq(1, d1.vval.v_dict.dv_refcount) 2983 alloc_log:check({}) 2984 local d_upper = lua2typvalt({ a = 'TEST' }) 2985 local dis_upper = dict_items(d_upper.vval.v_dict) 2986 local d_lower = lua2typvalt({ a = 'test' }) 2987 local dis_lower = dict_items(d_lower.vval.v_dict) 2988 local d_kupper_upper = lua2typvalt({ A = 'TEST' }) 2989 local dis_kupper_upper = dict_items(d_kupper_upper.vval.v_dict) 2990 local d_kupper_lower = lua2typvalt({ A = 'test' }) 2991 local dis_kupper_lower = dict_items(d_kupper_lower.vval.v_dict) 2992 alloc_log:clear_tmp_allocs() 2993 alloc_log:check({ 2994 a.dict(d_upper.vval.v_dict), 2995 a.di(dis_upper.a), 2996 a.str(dis_upper.a.di_tv.vval.v_string), 2997 2998 a.dict(d_lower.vval.v_dict), 2999 a.di(dis_lower.a), 3000 a.str(dis_lower.a.di_tv.vval.v_string), 3001 3002 a.dict(d_kupper_upper.vval.v_dict), 3003 a.di(dis_kupper_upper.A), 3004 a.str(dis_kupper_upper.A.di_tv.vval.v_string), 3005 3006 a.dict(d_kupper_lower.vval.v_dict), 3007 a.di(dis_kupper_lower.A), 3008 a.str(dis_kupper_lower.A.di_tv.vval.v_string), 3009 }) 3010 eq(true, tv_equal(d_upper, d_upper)) 3011 eq(true, tv_equal(d_upper, d_upper, true)) 3012 eq(false, tv_equal(d_upper, d_lower, false)) 3013 eq(true, tv_equal(d_upper, d_lower, true)) 3014 eq(true, tv_equal(d_kupper_upper, d_kupper_lower, true)) 3015 eq(false, tv_equal(d_kupper_upper, d_lower, true)) 3016 eq(false, tv_equal(d_kupper_upper, d_upper, true)) 3017 alloc_log:check({}) 3018 end) 3019 end) 3020 describe('check', function() 3021 describe('str_or_nr()', function() 3022 itp('works', function() 3023 local tv = typvalt() 3024 local mem = lib.xmalloc(1) 3025 tv.vval.v_list = mem -- Should crash when actually accessed 3026 alloc_log:clear() 3027 for _, v in ipairs({ 3028 { lib.VAR_NUMBER, nil }, 3029 { lib.VAR_FLOAT, 'E805: Expected a Number or a String, Float found' }, 3030 { lib.VAR_PARTIAL, 'E703: Expected a Number or a String, Funcref found' }, 3031 { lib.VAR_FUNC, 'E703: Expected a Number or a String, Funcref found' }, 3032 { lib.VAR_LIST, 'E745: Expected a Number or a String, List found' }, 3033 { lib.VAR_DICT, 'E728: Expected a Number or a String, Dictionary found' }, 3034 { lib.VAR_SPECIAL, 'E5300: Expected a Number or a String' }, 3035 { lib.VAR_UNKNOWN, 'E685: Internal error: tv_check_str_or_nr(UNKNOWN)' }, 3036 }) do 3037 local typ = v[1] 3038 local emsg = v[2] 3039 local ret = true 3040 if emsg then 3041 ret = false 3042 end 3043 tv.v_type = typ 3044 eq( 3045 ret, 3046 check_emsg(function() 3047 return lib.tv_check_str_or_nr(tv) 3048 end, emsg) 3049 ) 3050 if emsg then 3051 alloc_log:clear() 3052 else 3053 alloc_log:check({}) 3054 end 3055 end 3056 end) 3057 end) 3058 describe('num()', function() 3059 itp('works', function() 3060 local tv = typvalt() 3061 local mem = lib.xmalloc(1) 3062 tv.vval.v_list = mem -- Should crash when actually accessed 3063 alloc_log:clear() 3064 for _, v in ipairs({ 3065 { lib.VAR_NUMBER, nil }, 3066 { lib.VAR_FLOAT, 'E805: Using a Float as a Number' }, 3067 { lib.VAR_PARTIAL, 'E703: Using a Funcref as a Number' }, 3068 { lib.VAR_FUNC, 'E703: Using a Funcref as a Number' }, 3069 { lib.VAR_LIST, 'E745: Using a List as a Number' }, 3070 { lib.VAR_DICT, 'E728: Using a Dictionary as a Number' }, 3071 { lib.VAR_SPECIAL, nil }, 3072 { lib.VAR_UNKNOWN, 'E685: using an invalid value as a Number' }, 3073 }) do 3074 local typ = v[1] 3075 local emsg = v[2] 3076 local ret = true 3077 if emsg then 3078 ret = false 3079 end 3080 tv.v_type = typ 3081 eq( 3082 ret, 3083 check_emsg(function() 3084 return lib.tv_check_num(tv) 3085 end, emsg) 3086 ) 3087 if emsg then 3088 alloc_log:clear() 3089 else 3090 alloc_log:check({}) 3091 end 3092 end 3093 end) 3094 end) 3095 describe('str()', function() 3096 itp('works', function() 3097 local tv = typvalt() 3098 local mem = lib.xmalloc(1) 3099 tv.vval.v_list = mem -- Should crash when actually accessed 3100 alloc_log:clear() 3101 for _, v in ipairs({ 3102 { lib.VAR_NUMBER, nil }, 3103 { lib.VAR_FLOAT, nil }, 3104 { lib.VAR_PARTIAL, 'E729: Using a Funcref as a String' }, 3105 { lib.VAR_FUNC, 'E729: Using a Funcref as a String' }, 3106 { lib.VAR_LIST, 'E730: Using a List as a String' }, 3107 { lib.VAR_DICT, 'E731: Using a Dictionary as a String' }, 3108 { lib.VAR_BOOL, nil }, 3109 { lib.VAR_SPECIAL, nil }, 3110 { lib.VAR_UNKNOWN, 'E908: Using an invalid value as a String' }, 3111 }) do 3112 local typ = v[1] 3113 local emsg = v[2] 3114 local ret = true 3115 if emsg then 3116 ret = false 3117 end 3118 tv.v_type = typ 3119 eq( 3120 ret, 3121 check_emsg(function() 3122 return lib.tv_check_str(tv) 3123 end, emsg) 3124 ) 3125 if emsg then 3126 alloc_log:clear() 3127 else 3128 alloc_log:check({}) 3129 end 3130 end 3131 end) 3132 end) 3133 end) 3134 describe('get', function() 3135 describe('number()', function() 3136 itp('works', function() 3137 for _, v in ipairs({ 3138 { lib.VAR_NUMBER, { v_number = 42 }, nil, 42 }, 3139 { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, 100500 }, 3140 { lib.VAR_FLOAT, { v_float = 42.53 }, 'E805: Using a Float as a Number', 0 }, 3141 { lib.VAR_PARTIAL, { v_partial = NULL }, 'E703: Using a Funcref as a Number', 0 }, 3142 { lib.VAR_FUNC, { v_string = NULL }, 'E703: Using a Funcref as a Number', 0 }, 3143 { lib.VAR_LIST, { v_list = NULL }, 'E745: Using a List as a Number', 0 }, 3144 { lib.VAR_DICT, { v_dict = NULL }, 'E728: Using a Dictionary as a Number', 0 }, 3145 { lib.VAR_SPECIAL, { v_special = lib.kSpecialVarNull }, nil, 0 }, 3146 { lib.VAR_BOOL, { v_bool = lib.kBoolVarTrue }, nil, 1 }, 3147 { lib.VAR_BOOL, { v_bool = lib.kBoolVarFalse }, nil, 0 }, 3148 { lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0 }, 3149 }) do 3150 -- Using to_cstr, cannot free with tv_clear 3151 local tv = ffi.gc(typvalt(v[1], v[2]), nil) 3152 alloc_log:check({}) 3153 local emsg = v[3] 3154 local ret = v[4] 3155 eq( 3156 ret, 3157 check_emsg(function() 3158 return lib.tv_get_number(tv) 3159 end, emsg) 3160 ) 3161 if emsg then 3162 alloc_log:clear() 3163 else 3164 alloc_log:check({}) 3165 end 3166 end 3167 end) 3168 end) 3169 describe('number_chk()', function() 3170 itp('works', function() 3171 for _, v in ipairs({ 3172 { lib.VAR_NUMBER, { v_number = 42 }, nil, 42 }, 3173 { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, 100500 }, 3174 { lib.VAR_FLOAT, { v_float = 42.53 }, 'E805: Using a Float as a Number', 0 }, 3175 { lib.VAR_PARTIAL, { v_partial = NULL }, 'E703: Using a Funcref as a Number', 0 }, 3176 { lib.VAR_FUNC, { v_string = NULL }, 'E703: Using a Funcref as a Number', 0 }, 3177 { lib.VAR_LIST, { v_list = NULL }, 'E745: Using a List as a Number', 0 }, 3178 { lib.VAR_DICT, { v_dict = NULL }, 'E728: Using a Dictionary as a Number', 0 }, 3179 { lib.VAR_SPECIAL, { v_special = lib.kSpecialVarNull }, nil, 0 }, 3180 { lib.VAR_BOOL, { v_bool = lib.kBoolVarTrue }, nil, 1 }, 3181 { lib.VAR_BOOL, { v_bool = lib.kBoolVarFalse }, nil, 0 }, 3182 { lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0 }, 3183 }) do 3184 -- Using to_cstr, cannot free with tv_clear 3185 local tv = ffi.gc(typvalt(v[1], v[2]), nil) 3186 alloc_log:check({}) 3187 local emsg = v[3] 3188 local ret = { v[4], not not emsg } 3189 eq( 3190 ret, 3191 check_emsg(function() 3192 local err = ffi.new('bool[1]', { false }) 3193 local res = lib.tv_get_number_chk(tv, err) 3194 return { res, err[0] } 3195 end, emsg) 3196 ) 3197 if emsg then 3198 alloc_log:clear() 3199 else 3200 alloc_log:check({}) 3201 end 3202 end 3203 end) 3204 end) 3205 describe('lnum()', function() 3206 itp('works', function() 3207 for _, v in ipairs({ 3208 { lib.VAR_NUMBER, { v_number = 42 }, nil, 42 }, 3209 { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, 100500 }, 3210 { lib.VAR_STRING, { v_string = to_cstr('.') }, nil, 46 }, 3211 { lib.VAR_FLOAT, { v_float = 42.53 }, 'E805: Using a Float as a Number', -1 }, 3212 { lib.VAR_PARTIAL, { v_partial = NULL }, 'E703: Using a Funcref as a Number', -1 }, 3213 { lib.VAR_FUNC, { v_string = NULL }, 'E703: Using a Funcref as a Number', -1 }, 3214 { lib.VAR_LIST, { v_list = NULL }, 'E745: Using a List as a Number', -1 }, 3215 { lib.VAR_DICT, { v_dict = NULL }, 'E728: Using a Dictionary as a Number', -1 }, 3216 { lib.VAR_SPECIAL, { v_special = lib.kSpecialVarNull }, nil, 0 }, 3217 { lib.VAR_BOOL, { v_bool = lib.kBoolVarTrue }, nil, 1 }, 3218 { lib.VAR_BOOL, { v_bool = lib.kBoolVarFalse }, nil, 0 }, 3219 { lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', -1 }, 3220 }) do 3221 lib.curwin.w_cursor.lnum = 46 3222 -- Using to_cstr, cannot free with tv_clear 3223 local tv = ffi.gc(typvalt(v[1], v[2]), nil) 3224 alloc_log:check({}) 3225 local emsg = v[3] 3226 local ret = v[4] 3227 eq( 3228 ret, 3229 check_emsg(function() 3230 return lib.tv_get_lnum(tv) 3231 end, emsg) 3232 ) 3233 if emsg then 3234 alloc_log:clear() 3235 else 3236 alloc_log:check({}) 3237 end 3238 end 3239 end) 3240 end) 3241 describe('float()', function() 3242 itp('works', function() 3243 for _, v in ipairs({ 3244 { lib.VAR_NUMBER, { v_number = 42 }, nil, 42 }, 3245 { 3246 lib.VAR_STRING, 3247 { v_string = to_cstr('100500') }, 3248 'E892: Using a String as a Float', 3249 0, 3250 }, 3251 { lib.VAR_FLOAT, { v_float = 42.53 }, nil, 42.53 }, 3252 { lib.VAR_PARTIAL, { v_partial = NULL }, 'E891: Using a Funcref as a Float', 0 }, 3253 { lib.VAR_FUNC, { v_string = NULL }, 'E891: Using a Funcref as a Float', 0 }, 3254 { lib.VAR_LIST, { v_list = NULL }, 'E893: Using a List as a Float', 0 }, 3255 { lib.VAR_DICT, { v_dict = NULL }, 'E894: Using a Dictionary as a Float', 0 }, 3256 { 3257 lib.VAR_SPECIAL, 3258 { v_special = lib.kSpecialVarNull }, 3259 'E907: Using a special value as a Float', 3260 0, 3261 }, 3262 { 3263 lib.VAR_BOOL, 3264 { v_bool = lib.kBoolVarTrue }, 3265 'E362: Using a boolean value as a Float', 3266 0, 3267 }, 3268 { 3269 lib.VAR_BOOL, 3270 { v_bool = lib.kBoolVarFalse }, 3271 'E362: Using a boolean value as a Float', 3272 0, 3273 }, 3274 { lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_float(UNKNOWN)', 0 }, 3275 }) do 3276 -- Using to_cstr, cannot free with tv_clear 3277 local tv = ffi.gc(typvalt(v[1], v[2]), nil) 3278 alloc_log:check({}) 3279 local emsg = v[3] 3280 local ret = v[4] 3281 eq( 3282 ret, 3283 check_emsg(function() 3284 return lib.tv_get_float(tv) 3285 end, emsg) 3286 ) 3287 if emsg then 3288 alloc_log:clear() 3289 else 3290 alloc_log:check({}) 3291 end 3292 end 3293 end) 3294 end) 3295 3296 local function test_string_fn(input, fn) 3297 for _, v in ipairs(input) do 3298 -- Using to_cstr in place of Neovim allocated string, cannot 3299 -- tv_clear() that. 3300 local tv = ffi.gc(typvalt(v[1], v[2]), nil) 3301 alloc_log:check({}) 3302 local emsg = v[3] 3303 local ret = v[4] 3304 eq( 3305 ret, 3306 check_emsg(function() 3307 local res, buf = fn(tv) 3308 if 3309 tv.v_type == lib.VAR_NUMBER 3310 or tv.v_type == lib.VAR_FLOAT 3311 or tv.v_type == lib.VAR_SPECIAL 3312 or tv.v_type == lib.VAR_BOOL 3313 then 3314 eq(buf, res) 3315 else 3316 neq(buf, res) 3317 end 3318 if res ~= nil then 3319 return ffi.string(res) 3320 else 3321 return nil 3322 end 3323 end, emsg) 3324 ) 3325 if emsg then 3326 alloc_log:clear() 3327 elseif tv.v_type == lib.VAR_FLOAT then 3328 alloc_log:check({ a.freed(alloc_log.null), a.freed(alloc_log.null) }) 3329 else 3330 alloc_log:check({}) 3331 end 3332 end 3333 end 3334 describe('string()', function() 3335 itp('works', function() 3336 local buf = lib.tv_get_string(lua2typvalt(int(1))) 3337 local buf_chk = lib.tv_get_string_chk(lua2typvalt(int(1))) 3338 neq(buf, buf_chk) 3339 test_string_fn({ 3340 { lib.VAR_NUMBER, { v_number = 42 }, nil, '42' }, 3341 { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, '100500' }, 3342 { lib.VAR_FLOAT, { v_float = 42.53 }, nil, '42.53' }, 3343 { lib.VAR_PARTIAL, { v_partial = NULL }, 'E729: Using a Funcref as a String', '' }, 3344 { lib.VAR_FUNC, { v_string = NULL }, 'E729: Using a Funcref as a String', '' }, 3345 { lib.VAR_LIST, { v_list = NULL }, 'E730: Using a List as a String', '' }, 3346 { lib.VAR_DICT, { v_dict = NULL }, 'E731: Using a Dictionary as a String', '' }, 3347 { lib.VAR_SPECIAL, { v_special = lib.kSpecialVarNull }, nil, 'v:null' }, 3348 { lib.VAR_BOOL, { v_bool = lib.kBoolVarTrue }, nil, 'v:true' }, 3349 { lib.VAR_BOOL, { v_bool = lib.kBoolVarFalse }, nil, 'v:false' }, 3350 { lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', '' }, 3351 }, function(tv) 3352 return lib.tv_get_string(tv), buf 3353 end) 3354 end) 3355 end) 3356 describe('string_chk()', function() 3357 itp('works', function() 3358 local buf = lib.tv_get_string_chk(lua2typvalt(int(1))) 3359 test_string_fn({ 3360 { lib.VAR_NUMBER, { v_number = 42 }, nil, '42' }, 3361 { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, '100500' }, 3362 { lib.VAR_FLOAT, { v_float = 42.53 }, nil, '42.53' }, 3363 { lib.VAR_PARTIAL, { v_partial = NULL }, 'E729: Using a Funcref as a String', nil }, 3364 { lib.VAR_FUNC, { v_string = NULL }, 'E729: Using a Funcref as a String', nil }, 3365 { lib.VAR_LIST, { v_list = NULL }, 'E730: Using a List as a String', nil }, 3366 { lib.VAR_DICT, { v_dict = NULL }, 'E731: Using a Dictionary as a String', nil }, 3367 { lib.VAR_SPECIAL, { v_special = lib.kSpecialVarNull }, nil, 'v:null' }, 3368 { lib.VAR_BOOL, { v_bool = lib.kBoolVarTrue }, nil, 'v:true' }, 3369 { lib.VAR_BOOL, { v_bool = lib.kBoolVarFalse }, nil, 'v:false' }, 3370 { lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil }, 3371 }, function(tv) 3372 return lib.tv_get_string_chk(tv), buf 3373 end) 3374 end) 3375 end) 3376 describe('string_buf()', function() 3377 itp('works', function() 3378 test_string_fn({ 3379 { lib.VAR_NUMBER, { v_number = 42 }, nil, '42' }, 3380 { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, '100500' }, 3381 { lib.VAR_FLOAT, { v_float = 42.53 }, nil, '42.53' }, 3382 { lib.VAR_PARTIAL, { v_partial = NULL }, 'E729: Using a Funcref as a String', '' }, 3383 { lib.VAR_FUNC, { v_string = NULL }, 'E729: Using a Funcref as a String', '' }, 3384 { lib.VAR_LIST, { v_list = NULL }, 'E730: Using a List as a String', '' }, 3385 { lib.VAR_DICT, { v_dict = NULL }, 'E731: Using a Dictionary as a String', '' }, 3386 { lib.VAR_SPECIAL, { v_special = lib.kSpecialVarNull }, nil, 'v:null' }, 3387 { lib.VAR_BOOL, { v_bool = lib.kBoolVarTrue }, nil, 'v:true' }, 3388 { lib.VAR_BOOL, { v_bool = lib.kBoolVarFalse }, nil, 'v:false' }, 3389 { lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', '' }, 3390 }, function(tv) 3391 local buf = ffi.new('char[?]', lib.NUMBUFLEN, { 0 }) 3392 return lib.tv_get_string_buf(tv, buf), buf 3393 end) 3394 end) 3395 end) 3396 describe('string_buf_chk()', function() 3397 itp('works', function() 3398 test_string_fn({ 3399 { lib.VAR_NUMBER, { v_number = 42 }, nil, '42' }, 3400 { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, '100500' }, 3401 { lib.VAR_FLOAT, { v_float = 42.53 }, nil, '42.53' }, 3402 { lib.VAR_PARTIAL, { v_partial = NULL }, 'E729: Using a Funcref as a String', nil }, 3403 { lib.VAR_FUNC, { v_string = NULL }, 'E729: Using a Funcref as a String', nil }, 3404 { lib.VAR_LIST, { v_list = NULL }, 'E730: Using a List as a String', nil }, 3405 { lib.VAR_DICT, { v_dict = NULL }, 'E731: Using a Dictionary as a String', nil }, 3406 { lib.VAR_SPECIAL, { v_special = lib.kSpecialVarNull }, nil, 'v:null' }, 3407 { lib.VAR_BOOL, { v_bool = lib.kBoolVarTrue }, nil, 'v:true' }, 3408 { lib.VAR_BOOL, { v_bool = lib.kBoolVarFalse }, nil, 'v:false' }, 3409 { lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil }, 3410 }, function(tv) 3411 local buf = ffi.new('char[?]', lib.NUMBUFLEN, { 0 }) 3412 return lib.tv_get_string_buf_chk(tv, buf), buf 3413 end) 3414 end) 3415 end) 3416 end) 3417 end) 3418 end)