1" Tests for the Vim script debug commands 2 3source shared.vim 4source screendump.vim 5source check.vim 6 7CheckRunVimInTerminal 8 9func CheckCWD() 10 " Check that the longer lines don't wrap due to the length of the script name 11 " in cwd 12 let script_len = len( getcwd() .. '/Xtest1.vim' ) 13 let longest_line = len( 'Breakpoint in "" line 1' ) 14 if script_len > ( 75 - longest_line ) 15 throw 'Skipped: Your CWD has too many characters' 16 endif 17endfunc 18command! -nargs=0 -bar CheckCWD call CheckCWD() 19 20func CheckDbgOutput(buf, lines, options = {}) 21 " Verify the expected output 22 let lnum = 20 - len(a:lines) 23 for l in a:lines 24 if get(a:options, 'match', 'equal') ==# 'pattern' 25 call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, 200) 26 else 27 call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, 200) 28 endif 29 let lnum += 1 30 endfor 31endfunc 32 33" Run a Vim debugger command 34" If the expected output argument is supplied, then check for it. 35func RunDbgCmd(buf, cmd, ...) 36 call term_sendkeys(a:buf, a:cmd . "\r") 37 call TermWait(a:buf) 38 39 if a:0 != 0 40 let options = #{match: 'equal'} 41 if a:0 > 1 42 call extend(options, a:2) 43 endif 44 call CheckDbgOutput(a:buf, a:1, options) 45 endif 46endfunc 47 48" Debugger tests 49func Test_Debugger() 50 " Create a Vim script with some functions 51 let lines =<< trim END 52 func Foo() 53 let var1 = 1 54 let var2 = Bar(var1) + 9 55 return var2 56 endfunc 57 func Bar(var) 58 let var1 = 2 + a:var 59 let var2 = Bazz(var1) + 4 60 return var2 61 endfunc 62 func Bazz(var) 63 try 64 let var1 = 3 + a:var 65 let var3 = "another var" 66 let var3 = "value2" 67 catch 68 let var4 = "exception" 69 endtry 70 return var1 71 endfunc 72 END 73 call writefile(lines, 'Xtest.vim') 74 75 " Start Vim in a terminal 76 let buf = RunVimInTerminal('-S Xtest.vim', {}) 77 78 " Start the Vim debugger 79 call RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()']) 80 81 " Create a few stack frames by stepping through functions 82 call RunDbgCmd(buf, 'step', ['line 1: let var1 = 1']) 83 call RunDbgCmd(buf, 'step', ['line 2: let var2 = Bar(var1) + 9']) 84 call RunDbgCmd(buf, 'step', ['line 1: let var1 = 2 + a:var']) 85 call RunDbgCmd(buf, 'step', ['line 2: let var2 = Bazz(var1) + 4']) 86 call RunDbgCmd(buf, 'step', ['line 1: try']) 87 call RunDbgCmd(buf, 'step', ['line 2: let var1 = 3 + a:var']) 88 call RunDbgCmd(buf, 'step', ['line 3: let var3 = "another var"']) 89 90 " check backtrace 91 call RunDbgCmd(buf, 'backtrace', [ 92 \ ' 2 function Foo[2]', 93 \ ' 1 Bar[2]', 94 \ '->0 Bazz', 95 \ 'line 3: let var3 = "another var"']) 96 97 " Check variables in different stack frames 98 call RunDbgCmd(buf, 'echo var1', ['6']) 99 100 call RunDbgCmd(buf, 'up') 101 call RunDbgCmd(buf, 'back', [ 102 \ ' 2 function Foo[2]', 103 \ '->1 Bar[2]', 104 \ ' 0 Bazz', 105 \ 'line 3: let var3 = "another var"']) 106 call RunDbgCmd(buf, 'echo var1', ['3']) 107 108 call RunDbgCmd(buf, 'u') 109 call RunDbgCmd(buf, 'bt', [ 110 \ '->2 function Foo[2]', 111 \ ' 1 Bar[2]', 112 \ ' 0 Bazz', 113 \ 'line 3: let var3 = "another var"']) 114 call RunDbgCmd(buf, 'echo var1', ['1']) 115 116 " Undefined variables 117 call RunDbgCmd(buf, 'step') 118 call RunDbgCmd(buf, 'frame 2') 119 call RunDbgCmd(buf, 'echo var3', [ 120 \ 'Error detected while processing function Foo[2]..Bar[2]..Bazz:', 121 \ 'line 4:', 122 \ 'E121: Undefined variable: var3']) 123 124 " var3 is defined in this level with some other value 125 call RunDbgCmd(buf, 'fr 0') 126 call RunDbgCmd(buf, 'echo var3', ['another var']) 127 128 call RunDbgCmd(buf, 'step') 129 call RunDbgCmd(buf, '') 130 call RunDbgCmd(buf, '') 131 call RunDbgCmd(buf, '') 132 call RunDbgCmd(buf, '') 133 call RunDbgCmd(buf, 'step', [ 134 \ 'function Foo[2]..Bar', 135 \ 'line 3: End of function']) 136 call RunDbgCmd(buf, 'up') 137 138 " Undefined var2 139 call RunDbgCmd(buf, 'echo var2', [ 140 \ 'Error detected while processing function Foo[2]..Bar:', 141 \ 'line 3:', 142 \ 'E121: Undefined variable: var2']) 143 144 " Var2 is defined with 10 145 call RunDbgCmd(buf, 'down') 146 call RunDbgCmd(buf, 'echo var2', ['10']) 147 148 " Backtrace movements 149 call RunDbgCmd(buf, 'b', [ 150 \ ' 1 function Foo[2]', 151 \ '->0 Bar', 152 \ 'line 3: End of function']) 153 154 " next command cannot go down, we are on bottom 155 call RunDbgCmd(buf, 'down', ['frame is zero']) 156 call RunDbgCmd(buf, 'up') 157 158 " next command cannot go up, we are on top 159 call RunDbgCmd(buf, 'up', ['frame at highest level: 1']) 160 call RunDbgCmd(buf, 'where', [ 161 \ '->1 function Foo[2]', 162 \ ' 0 Bar', 163 \ 'line 3: End of function']) 164 165 " fil is not frame or finish, it is file 166 call RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--']) 167 168 " relative backtrace movement 169 call RunDbgCmd(buf, 'fr -1') 170 call RunDbgCmd(buf, 'frame', [ 171 \ ' 1 function Foo[2]', 172 \ '->0 Bar', 173 \ 'line 3: End of function']) 174 175 call RunDbgCmd(buf, 'fr +1') 176 call RunDbgCmd(buf, 'fram', [ 177 \ '->1 function Foo[2]', 178 \ ' 0 Bar', 179 \ 'line 3: End of function']) 180 181 " go beyond limits does not crash 182 call RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1']) 183 call RunDbgCmd(buf, 'fra', [ 184 \ '->1 function Foo[2]', 185 \ ' 0 Bar', 186 \ 'line 3: End of function']) 187 188 call RunDbgCmd(buf, 'frame -40', ['frame is zero']) 189 call RunDbgCmd(buf, 'fram', [ 190 \ ' 1 function Foo[2]', 191 \ '->0 Bar', 192 \ 'line 3: End of function']) 193 194 " final result 19 195 call RunDbgCmd(buf, 'cont', ['19']) 196 197 " breakpoints tests 198 199 " Start a debug session, so that reading the last line from the terminal 200 " works properly. 201 call RunDbgCmd(buf, ':debug echo Foo()') 202 203 " No breakpoints 204 call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 205 206 " Place some breakpoints 207 call RunDbgCmd(buf, 'breaka func Bar') 208 call RunDbgCmd(buf, 'breaklis', [' 1 func Bar line 1']) 209 call RunDbgCmd(buf, 'breakadd func 3 Bazz') 210 call RunDbgCmd(buf, 'breaklist', [' 1 func Bar line 1', 211 \ ' 2 func Bazz line 3']) 212 213 " Check whether the breakpoints are hit 214 call RunDbgCmd(buf, 'cont', [ 215 \ 'Breakpoint in "Bar" line 1', 216 \ 'function Foo[2]..Bar', 217 \ 'line 1: let var1 = 2 + a:var']) 218 call RunDbgCmd(buf, 'cont', [ 219 \ 'Breakpoint in "Bazz" line 3', 220 \ 'function Foo[2]..Bar[2]..Bazz', 221 \ 'line 3: let var3 = "another var"']) 222 223 " Delete the breakpoints 224 call RunDbgCmd(buf, 'breakd 1') 225 call RunDbgCmd(buf, 'breakli', [' 2 func Bazz line 3']) 226 call RunDbgCmd(buf, 'breakdel func 3 Bazz') 227 call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 228 229 call RunDbgCmd(buf, 'cont') 230 231 " Make sure the breakpoints are removed 232 call RunDbgCmd(buf, ':echo Foo()', ['19']) 233 234 " Delete a non-existing breakpoint 235 call RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2']) 236 237 " Expression breakpoint 238 call RunDbgCmd(buf, ':breakadd func 2 Bazz') 239 call RunDbgCmd(buf, ':echo Bazz(1)', [ 240 \ 'Entering Debug mode. Type "cont" to continue.', 241 \ 'function Bazz', 242 \ 'line 2: let var1 = 3 + a:var']) 243 call RunDbgCmd(buf, 'step') 244 call RunDbgCmd(buf, 'step') 245 call RunDbgCmd(buf, 'breaka expr var3') 246 call RunDbgCmd(buf, 'breakl', [' 3 func Bazz line 2', 247 \ ' 4 expr var3']) 248 call RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 5', 249 \ 'Oldval = "''another var''"', 250 \ 'Newval = "''value2''"', 251 \ 'function Bazz', 252 \ 'line 5: catch']) 253 254 call RunDbgCmd(buf, 'breakdel *') 255 call RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 256 257 " Check for error cases 258 call RunDbgCmd(buf, 'breakadd abcd', [ 259 \ 'Error detected while processing function Bazz:', 260 \ 'line 5:', 261 \ 'E475: Invalid argument: abcd']) 262 call RunDbgCmd(buf, 'breakadd func', ['E475: Invalid argument: func']) 263 call RunDbgCmd(buf, 'breakadd func 2', ['E475: Invalid argument: func 2']) 264 call RunDbgCmd(buf, 'breaka func a()', ['E475: Invalid argument: func a()']) 265 call RunDbgCmd(buf, 'breakd abcd', ['E475: Invalid argument: abcd']) 266 call RunDbgCmd(buf, 'breakd func', ['E475: Invalid argument: func']) 267 call RunDbgCmd(buf, 'breakd func a()', ['E475: Invalid argument: func a()']) 268 call RunDbgCmd(buf, 'breakd func a', ['E161: Breakpoint not found: func a']) 269 call RunDbgCmd(buf, 'breakd expr', ['E475: Invalid argument: expr']) 270 call RunDbgCmd(buf, 'breakd expr x', [ 271 \ 'E121: Undefined variable: x', 272 \ 'E161: Breakpoint not found: expr x']) 273 274 " finish the current function 275 call RunDbgCmd(buf, 'finish', [ 276 \ 'function Bazz', 277 \ 'line 8: End of function']) 278 call RunDbgCmd(buf, 'cont') 279 280 " Test for :next 281 call RunDbgCmd(buf, ':debug echo Bar(1)') 282 call RunDbgCmd(buf, 'step') 283 call RunDbgCmd(buf, 'next') 284 call RunDbgCmd(buf, '', [ 285 \ 'function Bar', 286 \ 'line 3: return var2']) 287 call RunDbgCmd(buf, 'c') 288 289 " Test for :interrupt 290 call RunDbgCmd(buf, ':debug echo Bazz(1)') 291 call RunDbgCmd(buf, 'step') 292 call RunDbgCmd(buf, 'step') 293 call RunDbgCmd(buf, 'interrupt', [ 294 \ 'Exception thrown: Vim:Interrupt', 295 \ 'function Bazz', 296 \ 'line 5: catch']) 297 call RunDbgCmd(buf, 'c') 298 299 " Test for :quit 300 call RunDbgCmd(buf, ':debug echo Foo()') 301 call RunDbgCmd(buf, 'breakdel *') 302 call RunDbgCmd(buf, 'breakadd func 3 Foo') 303 call RunDbgCmd(buf, 'breakadd func 3 Bazz') 304 call RunDbgCmd(buf, 'cont', [ 305 \ 'Breakpoint in "Bazz" line 3', 306 \ 'function Foo[2]..Bar[2]..Bazz', 307 \ 'line 3: let var3 = "another var"']) 308 call RunDbgCmd(buf, 'quit', [ 309 \ 'Breakpoint in "Foo" line 3', 310 \ 'function Foo', 311 \ 'line 3: return var2']) 312 call RunDbgCmd(buf, 'breakdel *') 313 call RunDbgCmd(buf, 'quit') 314 call RunDbgCmd(buf, 'enew! | only!') 315 316 call StopVimInTerminal(buf) 317 318 " Tests for :breakadd file and :breakadd here 319 " Breakpoints should be set before sourcing the file 320 321 let lines =<< trim END 322 let var1 = 10 323 let var2 = 20 324 let var3 = 30 325 let var4 = 40 326 END 327 call writefile(lines, 'Xtest.vim') 328 329 " Start Vim in a terminal 330 let buf = RunVimInTerminal('Xtest.vim', {}) 331 call RunDbgCmd(buf, ':breakadd file 2 Xtest.vim') 332 call RunDbgCmd(buf, ':4 | breakadd here') 333 call RunDbgCmd(buf, ':source Xtest.vim', ['line 2: let var2 = 20']) 334 call RunDbgCmd(buf, 'cont', ['line 4: let var4 = 40']) 335 call RunDbgCmd(buf, 'cont') 336 337 call StopVimInTerminal(buf) 338 339 call delete('Xtest.vim') 340 %bw! 341 call assert_fails('breakadd here', 'E32:') 342endfunc 343 344func Test_Backtrace_Through_Source() 345 CheckCWD 346 let file1 =<< trim END 347 func SourceAnotherFile() 348 source Xtest2.vim 349 endfunc 350 351 func CallAFunction() 352 call SourceAnotherFile() 353 call File2Function() 354 endfunc 355 356 func GlobalFunction() 357 call CallAFunction() 358 endfunc 359 END 360 call writefile(file1, 'Xtest1.vim') 361 362 let file2 =<< trim END 363 func DoAThing() 364 echo "DoAThing" 365 endfunc 366 367 func File2Function() 368 call DoAThing() 369 endfunc 370 371 call File2Function() 372 END 373 call writefile(file2, 'Xtest2.vim') 374 375 let buf = RunVimInTerminal('-S Xtest1.vim', {}) 376 377 call RunDbgCmd(buf, 378 \ ':debug call GlobalFunction()', 379 \ ['cmd: call GlobalFunction()']) 380 call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()']) 381 382 call RunDbgCmd(buf, 'backtrace', ['>backtrace', 383 \ '->0 function GlobalFunction', 384 \ 'line 1: call CallAFunction()']) 385 386 call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()']) 387 call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) 388 389 call RunDbgCmd(buf, 'backtrace', ['>backtrace', 390 \ ' 2 function GlobalFunction[1]', 391 \ ' 1 CallAFunction[1]', 392 \ '->0 SourceAnotherFile', 393 \ 'line 1: source Xtest2.vim']) 394 395 " Step into the 'source' command. Note that we print the full trace all the 396 " way though the source command. 397 call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()']) 398 call RunDbgCmd(buf, 'backtrace', [ 399 \ '>backtrace', 400 \ ' 3 function GlobalFunction[1]', 401 \ ' 2 CallAFunction[1]', 402 \ ' 1 SourceAnotherFile[1]', 403 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 404 \ 'line 1: func DoAThing()']) 405 406 call RunDbgCmd( buf, 'up' ) 407 call RunDbgCmd( buf, 'backtrace', [ 408 \ '>backtrace', 409 \ ' 3 function GlobalFunction[1]', 410 \ ' 2 CallAFunction[1]', 411 \ '->1 SourceAnotherFile[1]', 412 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 413 \ 'line 1: func DoAThing()' ] ) 414 415 call RunDbgCmd( buf, 'up' ) 416 call RunDbgCmd( buf, 'backtrace', [ 417 \ '>backtrace', 418 \ ' 3 function GlobalFunction[1]', 419 \ '->2 CallAFunction[1]', 420 \ ' 1 SourceAnotherFile[1]', 421 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 422 \ 'line 1: func DoAThing()' ] ) 423 424 call RunDbgCmd( buf, 'up' ) 425 call RunDbgCmd( buf, 'backtrace', [ 426 \ '>backtrace', 427 \ '->3 function GlobalFunction[1]', 428 \ ' 2 CallAFunction[1]', 429 \ ' 1 SourceAnotherFile[1]', 430 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 431 \ 'line 1: func DoAThing()' ] ) 432 433 call RunDbgCmd( buf, 'up', [ 'frame at highest level: 3' ] ) 434 call RunDbgCmd( buf, 'backtrace', [ 435 \ '>backtrace', 436 \ '->3 function GlobalFunction[1]', 437 \ ' 2 CallAFunction[1]', 438 \ ' 1 SourceAnotherFile[1]', 439 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 440 \ 'line 1: func DoAThing()' ] ) 441 442 call RunDbgCmd( buf, 'down' ) 443 call RunDbgCmd( buf, 'backtrace', [ 444 \ '>backtrace', 445 \ ' 3 function GlobalFunction[1]', 446 \ '->2 CallAFunction[1]', 447 \ ' 1 SourceAnotherFile[1]', 448 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 449 \ 'line 1: func DoAThing()' ] ) 450 451 call RunDbgCmd( buf, 'down' ) 452 call RunDbgCmd( buf, 'backtrace', [ 453 \ '>backtrace', 454 \ ' 3 function GlobalFunction[1]', 455 \ ' 2 CallAFunction[1]', 456 \ '->1 SourceAnotherFile[1]', 457 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 458 \ 'line 1: func DoAThing()' ] ) 459 460 call RunDbgCmd( buf, 'down' ) 461 call RunDbgCmd( buf, 'backtrace', [ 462 \ '>backtrace', 463 \ ' 3 function GlobalFunction[1]', 464 \ ' 2 CallAFunction[1]', 465 \ ' 1 SourceAnotherFile[1]', 466 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 467 \ 'line 1: func DoAThing()' ] ) 468 469 call RunDbgCmd( buf, 'down', [ 'frame is zero' ] ) 470 471 " step until we have another meaninfgul trace 472 call RunDbgCmd(buf, 'step', ['line 5: func File2Function()']) 473 call RunDbgCmd(buf, 'step', ['line 9: call File2Function()']) 474 call RunDbgCmd(buf, 'backtrace', [ 475 \ '>backtrace', 476 \ ' 3 function GlobalFunction[1]', 477 \ ' 2 CallAFunction[1]', 478 \ ' 1 SourceAnotherFile[1]', 479 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 480 \ 'line 9: call File2Function()']) 481 482 call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()']) 483 call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"']) 484 call RunDbgCmd(buf, 'backtrace', [ 485 \ '>backtrace', 486 \ ' 5 function GlobalFunction[1]', 487 \ ' 4 CallAFunction[1]', 488 \ ' 3 SourceAnotherFile[1]', 489 \ ' 2 script ' .. getcwd() .. '/Xtest2.vim[9]', 490 \ ' 1 function File2Function[1]', 491 \ '->0 DoAThing', 492 \ 'line 1: echo "DoAThing"']) 493 494 " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim 495 call RunDbgCmd(buf, 'step', ['line 1: End of function']) 496 call RunDbgCmd(buf, 'step', ['line 1: End of function']) 497 call RunDbgCmd(buf, 'step', ['line 10: End of sourced file']) 498 call RunDbgCmd(buf, 'step', ['line 1: End of function']) 499 call RunDbgCmd(buf, 'step', ['line 2: call File2Function()']) 500 call RunDbgCmd(buf, 'backtrace', [ 501 \ '>backtrace', 502 \ ' 1 function GlobalFunction[1]', 503 \ '->0 CallAFunction', 504 \ 'line 2: call File2Function()']) 505 506 call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()']) 507 call RunDbgCmd(buf, 'backtrace', [ 508 \ '>backtrace', 509 \ ' 2 function GlobalFunction[1]', 510 \ ' 1 CallAFunction[2]', 511 \ '->0 File2Function', 512 \ 'line 1: call DoAThing()']) 513 514 call StopVimInTerminal(buf) 515 call delete('Xtest1.vim') 516 call delete('Xtest2.vim') 517endfunc 518 519func Test_Backtrace_Autocmd() 520 CheckCWD 521 let file1 =<< trim END 522 func SourceAnotherFile() 523 source Xtest2.vim 524 endfunc 525 526 func CallAFunction() 527 call SourceAnotherFile() 528 call File2Function() 529 endfunc 530 531 func GlobalFunction() 532 call CallAFunction() 533 endfunc 534 535 au User TestGlobalFunction :call GlobalFunction() | echo "Done" 536 END 537 call writefile(file1, 'Xtest1.vim') 538 539 let file2 =<< trim END 540 func DoAThing() 541 echo "DoAThing" 542 endfunc 543 544 func File2Function() 545 call DoAThing() 546 endfunc 547 548 call File2Function() 549 END 550 call writefile(file2, 'Xtest2.vim') 551 552 let buf = RunVimInTerminal('-S Xtest1.vim', {}) 553 554 call RunDbgCmd(buf, 555 \ ':debug doautocmd User TestGlobalFunction', 556 \ ['cmd: doautocmd User TestGlobalFunction']) 557 call RunDbgCmd(buf, 'step', ['cmd: call GlobalFunction() | echo "Done"']) 558 559 " At this point the ontly thing in the stack is the autocommand 560 call RunDbgCmd(buf, 'backtrace', [ 561 \ '>backtrace', 562 \ '->0 User Autocommands for "TestGlobalFunction"', 563 \ 'cmd: call GlobalFunction() | echo "Done"']) 564 565 " And now we're back into the call stack 566 call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()']) 567 call RunDbgCmd(buf, 'backtrace', [ 568 \ '>backtrace', 569 \ ' 1 User Autocommands for "TestGlobalFunction"', 570 \ '->0 function GlobalFunction', 571 \ 'line 1: call CallAFunction()']) 572 573 call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()']) 574 call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) 575 576 call RunDbgCmd(buf, 'backtrace', [ 577 \ '>backtrace', 578 \ ' 3 User Autocommands for "TestGlobalFunction"', 579 \ ' 2 function GlobalFunction[1]', 580 \ ' 1 CallAFunction[1]', 581 \ '->0 SourceAnotherFile', 582 \ 'line 1: source Xtest2.vim']) 583 584 " Step into the 'source' command. Note that we print the full trace all the 585 " way though the source command. 586 call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()']) 587 call RunDbgCmd(buf, 'backtrace', [ 588 \ '>backtrace', 589 \ ' 4 User Autocommands for "TestGlobalFunction"', 590 \ ' 3 function GlobalFunction[1]', 591 \ ' 2 CallAFunction[1]', 592 \ ' 1 SourceAnotherFile[1]', 593 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 594 \ 'line 1: func DoAThing()']) 595 596 " step until we have another meaninfgul trace 597 call RunDbgCmd(buf, 'step', ['line 5: func File2Function()']) 598 call RunDbgCmd(buf, 'step', ['line 9: call File2Function()']) 599 call RunDbgCmd(buf, 'backtrace', [ 600 \ '>backtrace', 601 \ ' 4 User Autocommands for "TestGlobalFunction"', 602 \ ' 3 function GlobalFunction[1]', 603 \ ' 2 CallAFunction[1]', 604 \ ' 1 SourceAnotherFile[1]', 605 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 606 \ 'line 9: call File2Function()']) 607 608 call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()']) 609 call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"']) 610 call RunDbgCmd(buf, 'backtrace', [ 611 \ '>backtrace', 612 \ ' 6 User Autocommands for "TestGlobalFunction"', 613 \ ' 5 function GlobalFunction[1]', 614 \ ' 4 CallAFunction[1]', 615 \ ' 3 SourceAnotherFile[1]', 616 \ ' 2 script ' .. getcwd() .. '/Xtest2.vim[9]', 617 \ ' 1 function File2Function[1]', 618 \ '->0 DoAThing', 619 \ 'line 1: echo "DoAThing"']) 620 621 " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim 622 call RunDbgCmd(buf, 'step', ['line 1: End of function']) 623 call RunDbgCmd(buf, 'step', ['line 1: End of function']) 624 call RunDbgCmd(buf, 'step', ['line 10: End of sourced file']) 625 call RunDbgCmd(buf, 'step', ['line 1: End of function']) 626 call RunDbgCmd(buf, 'step', ['line 2: call File2Function()']) 627 call RunDbgCmd(buf, 'backtrace', [ 628 \ '>backtrace', 629 \ ' 2 User Autocommands for "TestGlobalFunction"', 630 \ ' 1 function GlobalFunction[1]', 631 \ '->0 CallAFunction', 632 \ 'line 2: call File2Function()']) 633 634 call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()']) 635 call RunDbgCmd(buf, 'backtrace', [ 636 \ '>backtrace', 637 \ ' 3 User Autocommands for "TestGlobalFunction"', 638 \ ' 2 function GlobalFunction[1]', 639 \ ' 1 CallAFunction[2]', 640 \ '->0 File2Function', 641 \ 'line 1: call DoAThing()']) 642 643 644 " Now unwind so that we get back to the original autocommand (and the second 645 " cmd echo "Done") 646 call RunDbgCmd(buf, 'finish', ['line 1: End of function']) 647 call RunDbgCmd(buf, 'backtrace', [ 648 \ '>backtrace', 649 \ ' 3 User Autocommands for "TestGlobalFunction"', 650 \ ' 2 function GlobalFunction[1]', 651 \ ' 1 CallAFunction[2]', 652 \ '->0 File2Function', 653 \ 'line 1: End of function']) 654 655 call RunDbgCmd(buf, 'finish', ['line 2: End of function']) 656 call RunDbgCmd(buf, 'backtrace', [ 657 \ '>backtrace', 658 \ ' 2 User Autocommands for "TestGlobalFunction"', 659 \ ' 1 function GlobalFunction[1]', 660 \ '->0 CallAFunction', 661 \ 'line 2: End of function']) 662 663 call RunDbgCmd(buf, 'finish', ['line 1: End of function']) 664 call RunDbgCmd(buf, 'backtrace', [ 665 \ '>backtrace', 666 \ ' 1 User Autocommands for "TestGlobalFunction"', 667 \ '->0 function GlobalFunction', 668 \ 'line 1: End of function']) 669 670 call RunDbgCmd(buf, 'step', ['cmd: echo "Done"']) 671 call RunDbgCmd(buf, 'backtrace', [ 672 \ '>backtrace', 673 \ '->0 User Autocommands for "TestGlobalFunction"', 674 \ 'cmd: echo "Done"']) 675 676 call StopVimInTerminal(buf) 677 call delete('Xtest1.vim') 678 call delete('Xtest2.vim') 679endfunc 680 681func Test_Backtrace_CmdLine() 682 CheckCWD 683 let file1 =<< trim END 684 func SourceAnotherFile() 685 source Xtest2.vim 686 endfunc 687 688 func CallAFunction() 689 call SourceAnotherFile() 690 call File2Function() 691 endfunc 692 693 func GlobalFunction() 694 call CallAFunction() 695 endfunc 696 697 au User TestGlobalFunction :call GlobalFunction() | echo "Done" 698 END 699 call writefile(file1, 'Xtest1.vim') 700 701 let file2 =<< trim END 702 func DoAThing() 703 echo "DoAThing" 704 endfunc 705 706 func File2Function() 707 call DoAThing() 708 endfunc 709 710 call File2Function() 711 END 712 call writefile(file2, 'Xtest2.vim') 713 714 let buf = RunVimInTerminal( 715 \ '-S Xtest1.vim -c "debug call GlobalFunction()"', 716 \ {'wait_for_ruler': 0}) 717 718 " Need to wait for the vim-in-terminal to be ready 719 call CheckDbgOutput(buf, ['command line', 720 \ 'cmd: call GlobalFunction()']) 721 722 " At this point the ontly thing in the stack is the cmdline 723 call RunDbgCmd(buf, 'backtrace', [ 724 \ '>backtrace', 725 \ '->0 command line', 726 \ 'cmd: call GlobalFunction()']) 727 728 " And now we're back into the call stack 729 call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()']) 730 call RunDbgCmd(buf, 'backtrace', [ 731 \ '>backtrace', 732 \ ' 1 command line', 733 \ '->0 function GlobalFunction', 734 \ 'line 1: call CallAFunction()']) 735 736 call StopVimInTerminal(buf) 737 call delete('Xtest1.vim') 738 call delete('Xtest2.vim') 739endfunc 740 741func Test_Backtrace_DefFunction() 742 CheckCWD 743 let file1 =<< trim END 744 vim9script 745 import File2Function from './Xtest2.vim' 746 747 def SourceAnotherFile() 748 source Xtest2.vim 749 enddef 750 751 def CallAFunction() 752 SourceAnotherFile() 753 File2Function() 754 enddef 755 756 def g:GlobalFunction() 757 CallAFunction() 758 enddef 759 760 defcompile 761 END 762 call writefile(file1, 'Xtest1.vim') 763 764 let file2 =<< trim END 765 vim9script 766 767 def DoAThing(): number 768 let a = 100 * 2 769 a += 3 770 return a 771 enddef 772 773 export def File2Function() 774 DoAThing() 775 enddef 776 777 defcompile 778 File2Function() 779 END 780 call writefile(file2, 'Xtest2.vim') 781 782 let buf = RunVimInTerminal('-S Xtest1.vim', {}) 783 784 call RunDbgCmd(buf, 785 \ ':debug call GlobalFunction()', 786 \ ['cmd: call GlobalFunction()']) 787 788 " FIXME: Vim9 lines are not debugged! 789 call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) 790 791 " But they do appear in the backtrace 792 call RunDbgCmd(buf, 'backtrace', [ 793 \ '\V>backtrace', 794 \ '\V 2 function GlobalFunction[1]', 795 \ '\V 1 <SNR>\.\*_CallAFunction[1]', 796 \ '\V->0 <SNR>\.\*_SourceAnotherFile', 797 \ '\Vline 1: source Xtest2.vim'], 798 \ #{match: 'pattern'}) 799 800 801 call RunDbgCmd(buf, 'step', ['line 1: vim9script']) 802 call RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number']) 803 call RunDbgCmd(buf, 'step', ['line 9: export def File2Function()']) 804 call RunDbgCmd(buf, 'step', ['line 9: def File2Function()']) 805 call RunDbgCmd(buf, 'step', ['line 13: defcompile']) 806 call RunDbgCmd(buf, 'step', ['line 14: File2Function()']) 807 call RunDbgCmd(buf, 'backtrace', [ 808 \ '\V>backtrace', 809 \ '\V 3 function GlobalFunction[1]', 810 \ '\V 2 <SNR>\.\*_CallAFunction[1]', 811 \ '\V 1 <SNR>\.\*_SourceAnotherFile[1]', 812 \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim', 813 \ '\Vline 14: File2Function()'], 814 \ #{match: 'pattern'}) 815 816 " Don't step into compiled functions... 817 call RunDbgCmd(buf, 'step', ['line 15: End of sourced file']) 818 call RunDbgCmd(buf, 'backtrace', [ 819 \ '\V>backtrace', 820 \ '\V 3 function GlobalFunction[1]', 821 \ '\V 2 <SNR>\.\*_CallAFunction[1]', 822 \ '\V 1 <SNR>\.\*_SourceAnotherFile[1]', 823 \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim', 824 \ '\Vline 15: End of sourced file'], 825 \ #{match: 'pattern'}) 826 827 828 call StopVimInTerminal(buf) 829 call delete('Xtest1.vim') 830 call delete('Xtest2.vim') 831endfunc 832 833func Test_debug_backtrace_level() 834 CheckCWD 835 let lines =<< trim END 836 let s:file1_var = 'file1' 837 let g:global_var = 'global' 838 839 func s:File1Func( arg ) 840 let s:file1_var .= a:arg 841 let local_var = s:file1_var .. ' test1' 842 let g:global_var .= local_var 843 source Xtest2.vim 844 endfunc 845 846 call s:File1Func( 'arg1' ) 847 END 848 call writefile(lines, 'Xtest1.vim') 849 850 let lines =<< trim END 851 let s:file2_var = 'file2' 852 853 func s:File2Func( arg ) 854 let s:file2_var .= a:arg 855 let local_var = s:file2_var .. ' test2' 856 let g:global_var .= local_var 857 endfunc 858 859 call s:File2Func( 'arg2' ) 860 END 861 call writefile(lines, 'Xtest2.vim') 862 863 let file1 = getcwd() .. '/Xtest1.vim' 864 let file2 = getcwd() .. '/Xtest2.vim' 865 866 " set a breakpoint and source file1.vim 867 let buf = RunVimInTerminal( 868 \ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim', 869 \ #{ wait_for_ruler: 0 } ) 870 871 call CheckDbgOutput(buf, [ 872 \ 'Breakpoint in "' .. file1 .. '" line 1', 873 \ 'Entering Debug mode. Type "cont" to continue.', 874 \ 'command line..script ' .. file1, 875 \ 'line 1: let s:file1_var = ''file1''' 876 \ ]) 877 878 " step throught the initial declarations 879 call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] ) 880 call RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] ) 881 call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] ) 882 call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] ) 883 call RunDbgCmd(buf, 'echo global_var', [ 'global' ] ) 884 885 " step in to the first function 886 call RunDbgCmd(buf, 'step', [ 'line 11: call s:File1Func( ''arg1'' )' ] ) 887 call RunDbgCmd(buf, 'step', [ 'line 1: let s:file1_var .= a:arg' ] ) 888 call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] ) 889 call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] ) 890 call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] ) 891 call RunDbgCmd(buf, 892 \'echo global_var', 893 \[ 'E121: Undefined variable: global_var' ] ) 894 call RunDbgCmd(buf, 895 \'echo local_var', 896 \[ 'E121: Undefined variable: local_var' ] ) 897 call RunDbgCmd(buf, 898 \'echo l:local_var', 899 \[ 'E121: Undefined variable: l:local_var' ] ) 900 901 " backtrace up 902 call RunDbgCmd(buf, 'backtrace', [ 903 \ '\V>backtrace', 904 \ '\V 2 command line', 905 \ '\V 1 script ' .. file1 .. '[11]', 906 \ '\V->0 function <SNR>\.\*_File1Func', 907 \ '\Vline 1: let s:file1_var .= a:arg', 908 \ ], 909 \ #{ match: 'pattern' } ) 910 call RunDbgCmd(buf, 'up', [ '>up' ] ) 911 912 call RunDbgCmd(buf, 'backtrace', [ 913 \ '\V>backtrace', 914 \ '\V 2 command line', 915 \ '\V->1 script ' .. file1 .. '[11]', 916 \ '\V 0 function <SNR>\.\*_File1Func', 917 \ '\Vline 1: let s:file1_var .= a:arg', 918 \ ], 919 \ #{ match: 'pattern' } ) 920 921 " Expression evaluation in the script frame (not the function frame) 922 " FIXME: Unexpected in this scope (a: should not be visibnle) 923 call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] ) 924 call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] ) 925 call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] ) 926 " FIXME: Unexpected in this scope (global should be found) 927 call RunDbgCmd(buf, 928 \'echo global_var', 929 \[ 'E121: Undefined variable: global_var' ] ) 930 call RunDbgCmd(buf, 931 \'echo local_var', 932 \[ 'E121: Undefined variable: local_var' ] ) 933 call RunDbgCmd(buf, 934 \'echo l:local_var', 935 \[ 'E121: Undefined variable: l:local_var' ] ) 936 937 938 " step while backtraced jumps to the latest frame 939 call RunDbgCmd(buf, 'step', [ 940 \ 'line 2: let local_var = s:file1_var .. '' test1''' ] ) 941 call RunDbgCmd(buf, 'backtrace', [ 942 \ '\V>backtrace', 943 \ '\V 2 command line', 944 \ '\V 1 script ' .. file1 .. '[11]', 945 \ '\V->0 function <SNR>\.\*_File1Func', 946 \ '\Vline 2: let local_var = s:file1_var .. '' test1''', 947 \ ], 948 \ #{ match: 'pattern' } ) 949 950 call RunDbgCmd(buf, 'step', [ 'line 3: let g:global_var .= local_var' ] ) 951 call RunDbgCmd(buf, 'echo local_var', [ 'file1arg1 test1' ] ) 952 call RunDbgCmd(buf, 'echo l:local_var', [ 'file1arg1 test1' ] ) 953 954 call RunDbgCmd(buf, 'step', [ 'line 4: source Xtest2.vim' ] ) 955 call RunDbgCmd(buf, 'step', [ 'line 1: let s:file2_var = ''file2''' ] ) 956 call RunDbgCmd(buf, 'backtrace', [ 957 \ '\V>backtrace', 958 \ '\V 3 command line', 959 \ '\V 2 script ' .. file1 .. '[11]', 960 \ '\V 1 function <SNR>\.\*_File1Func[4]', 961 \ '\V->0 script ' .. file2, 962 \ '\Vline 1: let s:file2_var = ''file2''', 963 \ ], 964 \ #{ match: 'pattern' } ) 965 966 " Expression evaluation in the script frame file2 (not the function frame) 967 call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] ) 968 call RunDbgCmd(buf, 969 \ 'echo s:file1_var', 970 \ [ 'E121: Undefined variable: s:file1_var' ] ) 971 call RunDbgCmd(buf, 'echo g:global_var', [ 'globalfile1arg1 test1' ] ) 972 call RunDbgCmd(buf, 'echo global_var', [ 'globalfile1arg1 test1' ] ) 973 call RunDbgCmd(buf, 974 \'echo local_var', 975 \[ 'E121: Undefined variable: local_var' ] ) 976 call RunDbgCmd(buf, 977 \'echo l:local_var', 978 \[ 'E121: Undefined variable: l:local_var' ] ) 979 call RunDbgCmd(buf, 980 \ 'echo s:file2_var', 981 \ [ 'E121: Undefined variable: s:file2_var' ] ) 982 983 call RunDbgCmd(buf, 'step', [ 'line 3: func s:File2Func( arg )' ] ) 984 call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] ) 985 986 " Up the stack to the other script context 987 call RunDbgCmd(buf, 'up') 988 call RunDbgCmd(buf, 'backtrace', [ 989 \ '\V>backtrace', 990 \ '\V 3 command line', 991 \ '\V 2 script ' .. file1 .. '[11]', 992 \ '\V->1 function <SNR>\.\*_File1Func[4]', 993 \ '\V 0 script ' .. file2, 994 \ '\Vline 3: func s:File2Func( arg )', 995 \ ], 996 \ #{ match: 'pattern' } ) 997 " FIXME: Unexpected. Should see the a: and l: dicts from File1Func 998 call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] ) 999 call RunDbgCmd(buf, 1000 \ 'echo l:local_var', 1001 \ [ 'E121: Undefined variable: l:local_var' ] ) 1002 1003 call RunDbgCmd(buf, 'up') 1004 call RunDbgCmd(buf, 'backtrace', [ 1005 \ '\V>backtrace', 1006 \ '\V 3 command line', 1007 \ '\V->2 script ' .. file1 .. '[11]', 1008 \ '\V 1 function <SNR>\.\*_File1Func[4]', 1009 \ '\V 0 script ' .. file2, 1010 \ '\Vline 3: func s:File2Func( arg )', 1011 \ ], 1012 \ #{ match: 'pattern' } ) 1013 1014 " FIXME: Unexpected (wrong script vars are used) 1015 call RunDbgCmd(buf, 1016 \ 'echo s:file1_var', 1017 \ [ 'E121: Undefined variable: s:file1_var' ] ) 1018 call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] ) 1019 1020 call StopVimInTerminal(buf) 1021 call delete('Xtest1.vim') 1022 call delete('Xtest2.vim') 1023endfunc 1024 1025" Test for setting a breakpoint on a :endif where the :if condition is false 1026" and then quit the script. This should generate an interrupt. 1027func Test_breakpt_endif_intr() 1028 func F() 1029 let g:Xpath ..= 'a' 1030 if v:false 1031 let g:Xpath ..= 'b' 1032 endif 1033 invalid_command 1034 endfunc 1035 1036 let g:Xpath = '' 1037 breakadd func 4 F 1038 try 1039 let caught_intr = 0 1040 debuggreedy 1041 call feedkeys(":call F()\<CR>quit\<CR>", "xt") 1042 catch /^Vim:Interrupt$/ 1043 call assert_match('\.F, line 4', v:throwpoint) 1044 let caught_intr = 1 1045 endtry 1046 0debuggreedy 1047 call assert_equal(1, caught_intr) 1048 call assert_equal('a', g:Xpath) 1049 breakdel * 1050 delfunc F 1051endfunc 1052 1053" Test for setting a breakpoint on a :else where the :if condition is false 1054" and then quit the script. This should generate an interrupt. 1055func Test_breakpt_else_intr() 1056 func F() 1057 let g:Xpath ..= 'a' 1058 if v:false 1059 let g:Xpath ..= 'b' 1060 else 1061 invalid_command 1062 endif 1063 invalid_command 1064 endfunc 1065 1066 let g:Xpath = '' 1067 breakadd func 4 F 1068 try 1069 let caught_intr = 0 1070 debuggreedy 1071 call feedkeys(":call F()\<CR>quit\<CR>", "xt") 1072 catch /^Vim:Interrupt$/ 1073 call assert_match('\.F, line 4', v:throwpoint) 1074 let caught_intr = 1 1075 endtry 1076 0debuggreedy 1077 call assert_equal(1, caught_intr) 1078 call assert_equal('a', g:Xpath) 1079 breakdel * 1080 delfunc F 1081endfunc 1082 1083" Test for setting a breakpoint on a :endwhile where the :while condition is 1084" false and then quit the script. This should generate an interrupt. 1085func Test_breakpt_endwhile_intr() 1086 func F() 1087 let g:Xpath ..= 'a' 1088 while v:false 1089 let g:Xpath ..= 'b' 1090 endwhile 1091 invalid_command 1092 endfunc 1093 1094 let g:Xpath = '' 1095 breakadd func 4 F 1096 try 1097 let caught_intr = 0 1098 debuggreedy 1099 call feedkeys(":call F()\<CR>quit\<CR>", "xt") 1100 catch /^Vim:Interrupt$/ 1101 call assert_match('\.F, line 4', v:throwpoint) 1102 let caught_intr = 1 1103 endtry 1104 0debuggreedy 1105 call assert_equal(1, caught_intr) 1106 call assert_equal('a', g:Xpath) 1107 breakdel * 1108 delfunc F 1109endfunc 1110 1111" Test for setting a breakpoint on a script local function 1112func Test_breakpt_scriptlocal_func() 1113 let g:Xpath = '' 1114 func s:G() 1115 let g:Xpath ..= 'a' 1116 endfunc 1117 1118 let funcname = expand("<SID>") .. "G" 1119 exe "breakadd func 1 " .. funcname 1120 debuggreedy 1121 redir => output 1122 call feedkeys(":call " .. funcname .. "()\<CR>c\<CR>", "xt") 1123 redir END 1124 0debuggreedy 1125 call assert_match('Breakpoint in "' .. funcname .. '" line 1', output) 1126 call assert_equal('a', g:Xpath) 1127 breakdel * 1128 exe "delfunc " .. funcname 1129endfunc 1130 1131" vim: shiftwidth=2 sts=2 expandtab 1132