ctx_functions_spec.lua (13195B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 4 local call = n.call 5 local clear = n.clear 6 local command = n.command 7 local eq = t.eq 8 local eval = n.eval 9 local feed = n.feed 10 local map = vim.tbl_map 11 local api = n.api 12 local parse_context = n.parse_context 13 local exec_capture = n.exec_capture 14 local source = n.source 15 local trim = vim.trim 16 local write_file = t.write_file 17 local pcall_err = t.pcall_err 18 19 describe('context functions', function() 20 local fname1 = 'Xtest-functional-eval-ctx1' 21 local fname2 = 'Xtest-functional-eval-ctx2' 22 local outofbounds = 'Vim:E475: Invalid value for argument index: out of bounds' 23 24 before_each(function() 25 clear() 26 write_file(fname1, '1\n2\n3') 27 write_file(fname2, 'a\nb\nc') 28 end) 29 30 after_each(function() 31 os.remove(fname1) 32 os.remove(fname2) 33 end) 34 35 describe('ctxpush/ctxpop', function() 36 it('saves and restores registers properly', function() 37 local regs = { '1', '2', '3', 'a' } 38 local vals = { '1', '2', '3', 'hjkl' } 39 feed('i1<cr>2<cr>3<c-[>ddddddqahjklq') 40 eq( 41 vals, 42 map(function(r) 43 return trim(call('getreg', r)) 44 end, regs) 45 ) 46 call('ctxpush') 47 call('ctxpush', { 'regs' }) 48 49 map(function(r) 50 call('setreg', r, {}) 51 end, regs) 52 eq( 53 { '', '', '', '' }, 54 map(function(r) 55 return trim(call('getreg', r)) 56 end, regs) 57 ) 58 59 call('ctxpop') 60 eq( 61 vals, 62 map(function(r) 63 return trim(call('getreg', r)) 64 end, regs) 65 ) 66 67 map(function(r) 68 call('setreg', r, {}) 69 end, regs) 70 eq( 71 { '', '', '', '' }, 72 map(function(r) 73 return trim(call('getreg', r)) 74 end, regs) 75 ) 76 77 call('ctxpop') 78 eq( 79 vals, 80 map(function(r) 81 return trim(call('getreg', r)) 82 end, regs) 83 ) 84 end) 85 86 it('saves and restores jumplist properly', function() 87 command('edit ' .. fname1) 88 feed('G') 89 feed('gg') 90 command('edit ' .. fname2) 91 local jumplist = call('getjumplist') 92 call('ctxpush') 93 call('ctxpush', { 'jumps' }) 94 95 command('clearjumps') 96 eq({ {}, 0 }, call('getjumplist')) 97 98 call('ctxpop') 99 eq(jumplist, call('getjumplist')) 100 101 command('clearjumps') 102 eq({ {}, 0 }, call('getjumplist')) 103 104 call('ctxpop') 105 eq(jumplist, call('getjumplist')) 106 end) 107 108 it('saves and restores buffer list properly', function() 109 command('edit ' .. fname1) 110 command('edit ' .. fname2) 111 command('edit TEST') 112 local bufs = call('map', call('getbufinfo'), 'v:val.name') 113 call('ctxpush') 114 call('ctxpush', { 'bufs' }) 115 116 command('%bwipeout') 117 eq({ '' }, call('map', call('getbufinfo'), 'v:val.name')) 118 119 call('ctxpop') 120 eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name')) 121 122 command('%bwipeout') 123 eq({ '' }, call('map', call('getbufinfo'), 'v:val.name')) 124 125 call('ctxpop') 126 eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name')) 127 end) 128 129 it('saves and restores global variables properly', function() 130 api.nvim_set_var('one', 1) 131 api.nvim_set_var('Two', 2) 132 api.nvim_set_var('THREE', 3) 133 eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]')) 134 call('ctxpush') 135 call('ctxpush', { 'gvars' }) 136 137 api.nvim_del_var('one') 138 api.nvim_del_var('Two') 139 api.nvim_del_var('THREE') 140 eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one')) 141 eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two')) 142 eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE')) 143 144 call('ctxpop') 145 eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]')) 146 147 api.nvim_del_var('one') 148 api.nvim_del_var('Two') 149 api.nvim_del_var('THREE') 150 eq('Vim:E121: Undefined variable: g:one', pcall_err(eval, 'g:one')) 151 eq('Vim:E121: Undefined variable: g:Two', pcall_err(eval, 'g:Two')) 152 eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE')) 153 154 call('ctxpop') 155 eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]')) 156 end) 157 158 it('saves and restores script functions properly', function() 159 source([[ 160 function s:greet(name) 161 echom 'Hello, '.a:name.'!' 162 endfunction 163 164 function s:greet_all(name, ...) 165 echom 'Hello, '.a:name.'!' 166 for more in a:000 167 echom 'Hello, '.more.'!' 168 endfor 169 endfunction 170 171 function Greet(name) 172 call call('s:greet', [a:name]) 173 endfunction 174 175 function GreetAll(name, ...) 176 call call('s:greet_all', extend([a:name], a:000)) 177 endfunction 178 179 function SaveSFuncs() 180 call ctxpush(['sfuncs']) 181 endfunction 182 183 function DeleteSFuncs() 184 delfunction s:greet 185 delfunction s:greet_all 186 endfunction 187 188 function RestoreFuncs() 189 call ctxpop() 190 endfunction 191 192 let g:sid = expand('<SID>') 193 ]]) 194 local sid = api.nvim_get_var('sid') 195 196 eq('Hello, World!', exec_capture([[call Greet('World')]])) 197 eq( 198 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!', 199 exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]) 200 ) 201 202 call('SaveSFuncs') 203 call('DeleteSFuncs') 204 205 eq( 206 ('function Greet, line 1: Vim(call):E117: Unknown function: %sgreet'):format(sid), 207 pcall_err(command, [[call Greet('World')]]) 208 ) 209 eq( 210 ('function GreetAll, line 1: Vim(call):E117: Unknown function: %sgreet_all'):format(sid), 211 pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]]) 212 ) 213 214 call('RestoreFuncs') 215 216 eq('Hello, World!', exec_capture([[call Greet('World')]])) 217 eq( 218 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!', 219 exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]) 220 ) 221 end) 222 223 it('saves and restores functions properly', function() 224 source([[ 225 function Greet(name) 226 echom 'Hello, '.a:name.'!' 227 endfunction 228 229 function GreetAll(name, ...) 230 echom 'Hello, '.a:name.'!' 231 for more in a:000 232 echom 'Hello, '.more.'!' 233 endfor 234 endfunction 235 ]]) 236 237 eq('Hello, World!', exec_capture([[call Greet('World')]])) 238 eq( 239 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!', 240 exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]) 241 ) 242 243 call('ctxpush', { 'funcs' }) 244 command('delfunction Greet') 245 command('delfunction GreetAll') 246 247 eq('Vim:E117: Unknown function: Greet', pcall_err(call, 'Greet', 'World')) 248 eq( 249 'Vim:E117: Unknown function: GreetAll', 250 pcall_err(call, 'GreetAll', 'World', 'One', 'Two', 'Three') 251 ) 252 253 call('ctxpop') 254 255 eq('Hello, World!', exec_capture([[call Greet('World')]])) 256 eq( 257 'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!', 258 exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]) 259 ) 260 end) 261 262 it('errors out when context stack is empty', function() 263 local err = 'Vim:Context stack is empty' 264 eq(err, pcall_err(call, 'ctxpop')) 265 eq(err, pcall_err(call, 'ctxpop')) 266 call('ctxpush') 267 call('ctxpush') 268 call('ctxpop') 269 call('ctxpop') 270 eq(err, pcall_err(call, 'ctxpop')) 271 end) 272 end) 273 274 describe('ctxsize()', function() 275 it('returns context stack size', function() 276 eq(0, call('ctxsize')) 277 call('ctxpush') 278 eq(1, call('ctxsize')) 279 call('ctxpush') 280 eq(2, call('ctxsize')) 281 call('ctxpush') 282 eq(3, call('ctxsize')) 283 call('ctxpop') 284 eq(2, call('ctxsize')) 285 call('ctxpop') 286 eq(1, call('ctxsize')) 287 call('ctxpop') 288 eq(0, call('ctxsize')) 289 end) 290 end) 291 292 describe('ctxget()', function() 293 it('errors out when index is out of bounds', function() 294 eq(outofbounds, pcall_err(call, 'ctxget')) 295 call('ctxpush') 296 eq(outofbounds, pcall_err(call, 'ctxget', 1)) 297 call('ctxpop') 298 eq(outofbounds, pcall_err(call, 'ctxget', 0)) 299 end) 300 301 it('returns context dict at index in context stack', function() 302 feed('i1<cr>2<cr>3<c-[>ddddddqahjklq') 303 command('edit! ' .. fname1) 304 feed('G') 305 feed('gg') 306 command('edit ' .. fname2) 307 api.nvim_set_var('one', 1) 308 api.nvim_set_var('Two', 2) 309 api.nvim_set_var('THREE', 3) 310 311 local with_regs = { 312 ['regs'] = { 313 { ['rt'] = 1, ['rc'] = { '1' }, ['n'] = 49, ['ru'] = true }, 314 { ['rt'] = 1, ['rc'] = { '2' }, ['n'] = 50 }, 315 { ['rt'] = 1, ['rc'] = { '3' }, ['n'] = 51 }, 316 { ['rc'] = { 'hjkl' }, ['n'] = 97 }, 317 }, 318 } 319 320 local with_jumps = { 321 ['jumps'] = eval((([[ 322 filter(map(add( 323 getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }), 324 'filter( 325 { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, 326 { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') 327 ]]):gsub('\n', ''))), 328 } 329 330 local with_bufs = { 331 ['bufs'] = eval([[ 332 filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)') 333 ]]), 334 } 335 336 local with_gvars = { 337 ['gvars'] = { { 'one', 1 }, { 'Two', 2 }, { 'THREE', 3 } }, 338 } 339 340 local with_all = { 341 ['regs'] = with_regs['regs'], 342 ['jumps'] = with_jumps['jumps'], 343 ['bufs'] = with_bufs['bufs'], 344 ['gvars'] = with_gvars['gvars'], 345 } 346 347 call('ctxpush') 348 eq(with_all, parse_context(call('ctxget'))) 349 eq(with_all, parse_context(call('ctxget', 0))) 350 351 call('ctxpush', { 'gvars' }) 352 eq(with_gvars, parse_context(call('ctxget'))) 353 eq(with_gvars, parse_context(call('ctxget', 0))) 354 eq(with_all, parse_context(call('ctxget', 1))) 355 356 call('ctxpush', { 'bufs' }) 357 eq(with_bufs, parse_context(call('ctxget'))) 358 eq(with_bufs, parse_context(call('ctxget', 0))) 359 eq(with_gvars, parse_context(call('ctxget', 1))) 360 eq(with_all, parse_context(call('ctxget', 2))) 361 362 call('ctxpush', { 'jumps' }) 363 eq(with_jumps, parse_context(call('ctxget'))) 364 eq(with_jumps, parse_context(call('ctxget', 0))) 365 eq(with_bufs, parse_context(call('ctxget', 1))) 366 eq(with_gvars, parse_context(call('ctxget', 2))) 367 eq(with_all, parse_context(call('ctxget', 3))) 368 369 call('ctxpush', { 'regs' }) 370 eq(with_regs, parse_context(call('ctxget'))) 371 eq(with_regs, parse_context(call('ctxget', 0))) 372 eq(with_jumps, parse_context(call('ctxget', 1))) 373 eq(with_bufs, parse_context(call('ctxget', 2))) 374 eq(with_gvars, parse_context(call('ctxget', 3))) 375 eq(with_all, parse_context(call('ctxget', 4))) 376 377 call('ctxpop') 378 eq(with_jumps, parse_context(call('ctxget'))) 379 eq(with_jumps, parse_context(call('ctxget', 0))) 380 eq(with_bufs, parse_context(call('ctxget', 1))) 381 eq(with_gvars, parse_context(call('ctxget', 2))) 382 eq(with_all, parse_context(call('ctxget', 3))) 383 384 call('ctxpop') 385 eq(with_bufs, parse_context(call('ctxget'))) 386 eq(with_bufs, parse_context(call('ctxget', 0))) 387 eq(with_gvars, parse_context(call('ctxget', 1))) 388 eq(with_all, parse_context(call('ctxget', 2))) 389 390 call('ctxpop') 391 eq(with_gvars, parse_context(call('ctxget'))) 392 eq(with_gvars, parse_context(call('ctxget', 0))) 393 eq(with_all, parse_context(call('ctxget', 1))) 394 395 call('ctxpop') 396 eq(with_all, parse_context(call('ctxget'))) 397 eq(with_all, parse_context(call('ctxget', 0))) 398 end) 399 end) 400 401 describe('ctxset()', function() 402 it('errors out when index is out of bounds', function() 403 eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 })) 404 call('ctxpush') 405 eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 1)) 406 call('ctxpop') 407 eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 0)) 408 end) 409 410 it('errors when context dict is invalid', function() 411 call('ctxpush') 412 eq( 413 'Vim:E474: Failed to convert list to msgpack string buffer', 414 pcall_err(call, 'ctxset', { regs = { {} }, jumps = { {} } }) 415 ) 416 end) 417 418 it('sets context dict at index in context stack', function() 419 api.nvim_set_var('one', 1) 420 api.nvim_set_var('Two', 2) 421 api.nvim_set_var('THREE', 3) 422 call('ctxpush') 423 local ctx1 = call('ctxget') 424 api.nvim_set_var('one', 'a') 425 api.nvim_set_var('Two', 'b') 426 api.nvim_set_var('THREE', 'c') 427 call('ctxpush') 428 call('ctxpush') 429 local ctx2 = call('ctxget') 430 431 eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]')) 432 call('ctxset', ctx1) 433 call('ctxset', ctx2, 2) 434 call('ctxpop') 435 eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]')) 436 call('ctxpop') 437 eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]')) 438 api.nvim_set_var('one', 1.5) 439 eq({ 1.5, 'b', 'c' }, eval('[g:one, g:Two, g:THREE]')) 440 call('ctxpop') 441 eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]')) 442 end) 443 end) 444 end)