neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

test_debugger.vim (49507B)


      1 " Tests for the Vim script debug commands
      2 
      3 source shared.vim
      4 source screendump.vim
      5 source check.vim
      6 
      7 func CheckCWD()
      8  " Check that the longer lines don't wrap due to the length of the script name
      9  " in cwd. Need to subtract by 1 since Vim will still wrap the message if it
     10  " just fits.
     11  let script_len = len( getcwd() .. '/Xtest1.vim' )
     12  let longest_line = len( 'Breakpoint in "" line 1' )
     13  if script_len > ( 75 - longest_line - 1 )
     14    throw 'Skipped: Your CWD has too many characters'
     15  endif
     16 endfunc
     17 command! -nargs=0 -bar CheckCWD call CheckCWD()
     18 
     19 " "options" argument can contain:
     20 " 'msec' - time to wait for a match
     21 " 'match' - "pattern" to use "lines" as pattern instead of text
     22 func CheckDbgOutput(buf, lines, options = {})
     23  " Verify the expected output
     24  let lnum = 20 - len(a:lines)
     25  let msec = get(a:options, 'msec', 1000)
     26  for l in a:lines
     27    if get(a:options, 'match', 'equal') ==# 'pattern'
     28      call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, msec)
     29    else
     30      call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, msec)
     31    endif
     32    let lnum += 1
     33  endfor
     34 endfunc
     35 
     36 " Run a Vim debugger command
     37 " If the expected output argument is supplied, then check for it.
     38 func s:RunDbgCmd(buf, cmd, ...)
     39  call term_sendkeys(a:buf, a:cmd . "\r")
     40  call TermWait(a:buf)
     41 
     42  if a:0 != 0
     43    let options = #{match: 'equal'}
     44    if a:0 > 1
     45      call extend(options, a:2)
     46    endif
     47    call CheckDbgOutput(a:buf, a:1, options)
     48  endif
     49 endfunc
     50 
     51 " Debugger tests
     52 func Test_Debugger()
     53  CheckRunVimInTerminal
     54 
     55  " Create a Vim script with some functions
     56  let lines =<< trim END
     57 func Foo()
     58   let var1 = 1
     59   let var2 = Bar(var1) + 9
     60   return var2
     61 endfunc
     62 func Bar(var)
     63   let var1 = 2 + a:var
     64   let var2 = Bazz(var1) + 4
     65   return var2
     66 endfunc
     67 func Bazz(var)
     68   try
     69     let var1 = 3 + a:var
     70     let var3 = "another var"
     71     let var3 = "value2"
     72   catch
     73     let var4 = "exception"
     74   endtry
     75   return var1
     76 endfunc
     77        def Vim9Func()
     78          for cmd in ['confirm', 'xxxxxxx']
     79            for _ in [1, 2]
     80              echo cmd
     81            endfor
     82          endfor
     83        enddef
     84  END
     85  call writefile(lines, 'XtestDebug.vim', 'D')
     86 
     87  " Start Vim in a terminal
     88  let buf = RunVimInTerminal('-S XtestDebug.vim', {})
     89 
     90  " Start the Vim debugger
     91  call s:RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()'])
     92 
     93  " Create a few stack frames by stepping through functions
     94  call s:RunDbgCmd(buf, 'step', ['line 1: let var1 = 1'])
     95  call s:RunDbgCmd(buf, 'step', ['line 2: let var2 = Bar(var1) + 9'])
     96  call s:RunDbgCmd(buf, 'step', ['line 1: let var1 = 2 + a:var'])
     97  call s:RunDbgCmd(buf, 'step', ['line 2: let var2 = Bazz(var1) + 4'])
     98  call s:RunDbgCmd(buf, 'step', ['line 1: try'])
     99  call s:RunDbgCmd(buf, 'step', ['line 2: let var1 = 3 + a:var'])
    100  call s:RunDbgCmd(buf, 'step', ['line 3: let var3 = "another var"'])
    101 
    102  " check backtrace
    103  call s:RunDbgCmd(buf, 'backtrace', [
    104       \ '  2 function Foo[2]',
    105       \ '  1 Bar[2]',
    106       \ '->0 Bazz',
    107       \ 'line 3: let var3 = "another var"'])
    108 
    109  " Check variables in different stack frames
    110  call s:RunDbgCmd(buf, 'echo var1', ['6'])
    111 
    112  call s:RunDbgCmd(buf, 'up')
    113  call s:RunDbgCmd(buf, 'back', [
    114       \ '  2 function Foo[2]',
    115       \ '->1 Bar[2]',
    116       \ '  0 Bazz',
    117       \ 'line 3: let var3 = "another var"'])
    118  call s:RunDbgCmd(buf, 'echo var1', ['3'])
    119 
    120  call s:RunDbgCmd(buf, 'u')
    121  call s:RunDbgCmd(buf, 'bt', [
    122       \ '->2 function Foo[2]',
    123       \ '  1 Bar[2]',
    124       \ '  0 Bazz',
    125       \ 'line 3: let var3 = "another var"'])
    126  call s:RunDbgCmd(buf, 'echo var1', ['1'])
    127 
    128  " Undefined variables
    129  call s:RunDbgCmd(buf, 'step')
    130  call s:RunDbgCmd(buf, 'frame 2')
    131  call s:RunDbgCmd(buf, 'echo var3', [
    132 \ 'Error in function Foo[2]..Bar[2]..Bazz:',
    133 \ 'line    4:',
    134 \ 'E121: Undefined variable: var3'])
    135 
    136  " var3 is defined in this level with some other value
    137  call s:RunDbgCmd(buf, 'fr 0')
    138  call s:RunDbgCmd(buf, 'echo var3', ['another var'])
    139 
    140  call s:RunDbgCmd(buf, 'step')
    141  call s:RunDbgCmd(buf, '')
    142  call s:RunDbgCmd(buf, '')
    143  call s:RunDbgCmd(buf, '')
    144  call s:RunDbgCmd(buf, '')
    145  call s:RunDbgCmd(buf, 'step', [
    146       \ 'function Foo[2]..Bar',
    147       \ 'line 3: End of function'])
    148  call s:RunDbgCmd(buf, 'up')
    149 
    150  " Undefined var2
    151  call s:RunDbgCmd(buf, 'echo var2', [
    152       \ 'Error in function Foo[2]..Bar:',
    153       \ 'line    3:',
    154       \ 'E121: Undefined variable: var2'])
    155 
    156  " Var2 is defined with 10
    157  call s:RunDbgCmd(buf, 'down')
    158  call s:RunDbgCmd(buf, 'echo var2', ['10'])
    159 
    160  " Backtrace movements
    161  call s:RunDbgCmd(buf, 'b', [
    162       \ '  1 function Foo[2]',
    163       \ '->0 Bar',
    164       \ 'line 3: End of function'])
    165 
    166  " next command cannot go down, we are on bottom
    167  call s:RunDbgCmd(buf, 'down', ['frame is zero'])
    168  call s:RunDbgCmd(buf, 'up')
    169 
    170  " next command cannot go up, we are on top
    171  call s:RunDbgCmd(buf, 'up', ['frame at highest level: 1'])
    172  call s:RunDbgCmd(buf, 'where', [
    173       \ '->1 function Foo[2]',
    174       \ '  0 Bar',
    175       \ 'line 3: End of function'])
    176 
    177  " fil is not frame or finish, it is file
    178  call s:RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--'])
    179 
    180  " relative backtrace movement
    181  call s:RunDbgCmd(buf, 'fr -1')
    182  call s:RunDbgCmd(buf, 'frame', [
    183       \ '  1 function Foo[2]',
    184       \ '->0 Bar',
    185       \ 'line 3: End of function'])
    186 
    187  call s:RunDbgCmd(buf, 'fr +1')
    188  call s:RunDbgCmd(buf, 'fram', [
    189       \ '->1 function Foo[2]',
    190       \ '  0 Bar',
    191       \ 'line 3: End of function'])
    192 
    193  " go beyond limits does not crash
    194  call s:RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1'])
    195  call s:RunDbgCmd(buf, 'fra', [
    196       \ '->1 function Foo[2]',
    197       \ '  0 Bar',
    198       \ 'line 3: End of function'])
    199 
    200  call s:RunDbgCmd(buf, 'frame -40', ['frame is zero'])
    201  call s:RunDbgCmd(buf, 'fram', [
    202       \ '  1 function Foo[2]',
    203       \ '->0 Bar',
    204       \ 'line 3: End of function'])
    205 
    206  " final result 19
    207  call s:RunDbgCmd(buf, 'cont', ['19'])
    208 
    209  " breakpoints tests
    210 
    211  " Start a debug session, so that reading the last line from the terminal
    212  " works properly.
    213  call s:RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()'])
    214 
    215  " No breakpoints
    216  call s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
    217 
    218  " Place some breakpoints
    219  call s:RunDbgCmd(buf, 'breaka func Bar')
    220  call s:RunDbgCmd(buf, 'breaklis', ['  1  func Bar  line 1'])
    221  call s:RunDbgCmd(buf, 'breakadd func 3 Bazz')
    222  call s:RunDbgCmd(buf, 'breaklist', ['  1  func Bar  line 1',
    223       \ '  2  func Bazz  line 3'])
    224 
    225  " Check whether the breakpoints are hit
    226  call s:RunDbgCmd(buf, 'cont', [
    227       \ 'Breakpoint in "Bar" line 1',
    228       \ 'function Foo[2]..Bar',
    229       \ 'line 1: let var1 = 2 + a:var'])
    230  call s:RunDbgCmd(buf, 'cont', [
    231       \ 'Breakpoint in "Bazz" line 3',
    232       \ 'function Foo[2]..Bar[2]..Bazz',
    233       \ 'line 3: let var3 = "another var"'])
    234 
    235  " Delete the breakpoints
    236  call s:RunDbgCmd(buf, 'breakd 1')
    237  call s:RunDbgCmd(buf, 'breakli', ['  2  func Bazz  line 3'])
    238  call s:RunDbgCmd(buf, 'breakdel func 3 Bazz')
    239  call s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
    240 
    241  call s:RunDbgCmd(buf, 'cont')
    242 
    243  " Make sure the breakpoints are removed
    244  call s:RunDbgCmd(buf, ':echo Foo()', ['19'])
    245 
    246  " Delete a non-existing breakpoint
    247  call s:RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2'])
    248 
    249  " Expression breakpoint
    250  call s:RunDbgCmd(buf, ':breakadd func 2 Bazz')
    251  call s:RunDbgCmd(buf, ':echo Bazz(1)', [
    252       \ 'Entering Debug mode.  Type "cont" to continue.',
    253       \ 'function Bazz',
    254       \ 'line 2: let var1 = 3 + a:var'])
    255  call s:RunDbgCmd(buf, 'step')
    256  call s:RunDbgCmd(buf, 'step')
    257  call s:RunDbgCmd(buf, 'breaka expr var3')
    258  call s:RunDbgCmd(buf, 'breakl', ['  3  func Bazz  line 2',
    259       \ '  4  expr var3'])
    260  call s:RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 5',
    261       \ 'Oldval = "''another var''"',
    262       \ 'Newval = "''value2''"',
    263       \ 'function Bazz',
    264       \ 'line 5: catch'])
    265 
    266  call s:RunDbgCmd(buf, 'breakdel *')
    267  call s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
    268 
    269  " Check for error cases
    270  call s:RunDbgCmd(buf, 'breakadd abcd', [
    271       \ 'Error in function Bazz:',
    272       \ 'line    5:',
    273       \ 'E475: Invalid argument: abcd'])
    274  call s:RunDbgCmd(buf, 'breakadd func', ['E475: Invalid argument: func'])
    275  call s:RunDbgCmd(buf, 'breakadd func 2', ['E475: Invalid argument: func 2'])
    276  call s:RunDbgCmd(buf, 'breaka func a()', ['E475: Invalid argument: func a()'])
    277  call s:RunDbgCmd(buf, 'breakd abcd', ['E475: Invalid argument: abcd'])
    278  call s:RunDbgCmd(buf, 'breakd func', ['E475: Invalid argument: func'])
    279  call s:RunDbgCmd(buf, 'breakd func a()', ['E475: Invalid argument: func a()'])
    280  call s:RunDbgCmd(buf, 'breakd func a', ['E161: Breakpoint not found: func a'])
    281  call s:RunDbgCmd(buf, 'breakd expr', ['E475: Invalid argument: expr'])
    282  call s:RunDbgCmd(buf, 'breakd expr x', ['E161: Breakpoint not found: expr x'])
    283 
    284  " finish the current function
    285  call s:RunDbgCmd(buf, 'finish', [
    286       \ 'function Bazz',
    287       \ 'line 8: End of function'])
    288  call s:RunDbgCmd(buf, 'cont')
    289 
    290  " Test for :next
    291  call s:RunDbgCmd(buf, ':debug echo Bar(1)')
    292  call s:RunDbgCmd(buf, 'step')
    293  call s:RunDbgCmd(buf, 'next')
    294  call s:RunDbgCmd(buf, '', [
    295       \ 'function Bar',
    296       \ 'line 3: return var2'])
    297  call s:RunDbgCmd(buf, 'c')
    298 
    299  " Test for :interrupt
    300  call s:RunDbgCmd(buf, ':debug echo Bazz(1)')
    301  call s:RunDbgCmd(buf, 'step')
    302  call s:RunDbgCmd(buf, 'step')
    303  call s:RunDbgCmd(buf, 'interrupt', [
    304       \ 'Exception thrown: Vim:Interrupt',
    305       \ 'function Bazz',
    306       \ 'line 5: catch'])
    307  call s:RunDbgCmd(buf, 'c')
    308 
    309  " Test showing local variable in :def function
    310  call s:RunDbgCmd(buf, ':breakadd func 2 Vim9Func')
    311  call s:RunDbgCmd(buf, ':call Vim9Func()', ['line 2:             for _ in [1, 2]'])
    312  call s:RunDbgCmd(buf, 'next', ['line 2: for _ in [1, 2]'])
    313  call s:RunDbgCmd(buf, 'echo cmd', ['confirm'])
    314  call s:RunDbgCmd(buf, 'breakdel *')
    315  call s:RunDbgCmd(buf, 'cont')
    316 
    317  " Test for :quit
    318  call s:RunDbgCmd(buf, ':debug echo Foo()')
    319  call s:RunDbgCmd(buf, 'breakdel *')
    320  call s:RunDbgCmd(buf, 'breakadd func 3 Foo')
    321  call s:RunDbgCmd(buf, 'breakadd func 3 Bazz')
    322  call s:RunDbgCmd(buf, 'cont', [
    323       \ 'Breakpoint in "Bazz" line 3',
    324       \ 'function Foo[2]..Bar[2]..Bazz',
    325       \ 'line 3: let var3 = "another var"'])
    326  call s:RunDbgCmd(buf, 'quit', [
    327       \ 'Breakpoint in "Foo" line 3',
    328       \ 'function Foo',
    329       \ 'line 3: return var2'])
    330  call s:RunDbgCmd(buf, 'breakdel *')
    331  call s:RunDbgCmd(buf, 'quit')
    332  call s:RunDbgCmd(buf, 'enew! | only!')
    333 
    334  call StopVimInTerminal(buf)
    335 endfunc
    336 
    337 func Test_Debugger_breakadd()
    338  " Tests for :breakadd file and :breakadd here
    339  " Breakpoints should be set before sourcing the file
    340  CheckRunVimInTerminal
    341 
    342  let lines =<< trim END
    343 let var1 = 10
    344 let var2 = 20
    345 let var3 = 30
    346 let var4 = 40
    347  END
    348  call writefile(lines, 'XdebugBreakadd.vim', 'D')
    349 
    350  " Start Vim in a terminal
    351  let buf = RunVimInTerminal('XdebugBreakadd.vim', {})
    352  call s:RunDbgCmd(buf, ':breakadd file 2 XdebugBreakadd.vim')
    353  call s:RunDbgCmd(buf, ':4 | breakadd here')
    354  call s:RunDbgCmd(buf, ':source XdebugBreakadd.vim', ['line 2: let var2 = 20'])
    355  call s:RunDbgCmd(buf, 'cont', ['line 4: let var4 = 40'])
    356  call s:RunDbgCmd(buf, 'cont')
    357 
    358  call StopVimInTerminal(buf)
    359 
    360  %bw!
    361 
    362  call assert_fails('breakadd here', 'E32:')
    363  call assert_fails('breakadd file Xtest.vim /\)/', 'E55:')
    364 endfunc
    365 
    366 " Test for expression breakpoint set using ":breakadd expr <expr>"
    367 " FIXME: This doesn't seem to work as documented. The breakpoint is not
    368 " triggered until the next function call.
    369 func Test_Debugger_breakadd_expr()
    370  CheckRunVimInTerminal
    371  CheckCWD
    372 
    373  let lines =<< trim END
    374    func Foo()
    375      eval 1
    376      eval 2
    377    endfunc
    378 
    379    let g:Xtest_var += 1
    380    call Foo()
    381    let g:Xtest_var += 1
    382    call Foo()
    383  END
    384  call writefile(lines, 'XbreakExpr.vim', 'D')
    385 
    386  " Start Vim in a terminal
    387  let buf = RunVimInTerminal('XbreakExpr.vim', {})
    388  call s:RunDbgCmd(buf, ':let g:Xtest_var = 10')
    389  call s:RunDbgCmd(buf, ':breakadd expr g:Xtest_var')
    390  let expected =<< trim eval END
    391    Oldval = "10"
    392    Newval = "11"
    393    {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
    394    line 1: eval 1
    395  END
    396  call s:RunDbgCmd(buf, ':source %', expected)
    397  let expected =<< trim eval END
    398    Oldval = "11"
    399    Newval = "12"
    400    {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
    401    line 1: eval 1
    402  END
    403  call s:RunDbgCmd(buf, 'cont', expected)
    404  call s:RunDbgCmd(buf, 'cont')
    405 
    406  " Check the behavior without the g: prefix.
    407  " FIXME: The Oldval and Newval don't look right here.
    408  call s:RunDbgCmd(buf, ':breakdel *')
    409  call s:RunDbgCmd(buf, ':breakadd expr Xtest_var')
    410  let expected =<< trim eval END
    411    Oldval = "13"
    412    Newval = "(does not exist)"
    413    {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
    414    line 1: eval 1
    415  END
    416  call s:RunDbgCmd(buf, ':source %', expected)
    417  let expected =<< trim eval END
    418    {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
    419    line 2: eval 2
    420  END
    421  call s:RunDbgCmd(buf, 'cont', expected)
    422  let expected =<< trim eval END
    423    Oldval = "14"
    424    Newval = "(does not exist)"
    425    {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
    426    line 1: eval 1
    427  END
    428  call s:RunDbgCmd(buf, 'cont', expected)
    429  let expected =<< trim eval END
    430    {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
    431    line 2: eval 2
    432  END
    433  call s:RunDbgCmd(buf, 'cont', expected)
    434  call s:RunDbgCmd(buf, 'cont')
    435 
    436  call StopVimInTerminal(buf)
    437 endfunc
    438 
    439 " def Test_Debugger_break_at_return()
    440 "   var lines =<< trim END
    441 "       vim9script
    442 "       def g:GetNum(): number
    443 "         return 1
    444 "           + 2
    445 "           + 3
    446 "       enddef
    447 "       breakadd func GetNum
    448 "   END
    449 "   writefile(lines, 'Xtest.vim')
    450 "
    451 "   # Start Vim in a terminal
    452 "   var buf = RunVimInTerminal('-S Xtest.vim', {wait_for_ruler: 0})
    453 "   call TermWait(buf)
    454 "
    455 "   RunDbgCmd(buf, ':call GetNum()',
    456 "      ['line 1: return 1  + 2  + 3'], {match: 'pattern'})
    457 "
    458 "   call StopVimInTerminal(buf)
    459 "   call delete('Xtest.vim')
    460 " enddef
    461 
    462 func Test_Backtrace_Through_Source()
    463  CheckRunVimInTerminal
    464  CheckCWD
    465  let file1 =<< trim END
    466    func SourceAnotherFile()
    467      source Xtest2.vim
    468    endfunc
    469 
    470    func CallAFunction()
    471      call SourceAnotherFile()
    472      call File2Function()
    473    endfunc
    474 
    475    func GlobalFunction()
    476      call CallAFunction()
    477    endfunc
    478  END
    479  call writefile(file1, 'Xtest1.vim', 'D')
    480 
    481  let file2 =<< trim END
    482    func DoAThing()
    483      echo "DoAThing"
    484    endfunc
    485 
    486    func File2Function()
    487      call DoAThing()
    488    endfunc
    489 
    490    call File2Function()
    491  END
    492  call writefile(file2, 'Xtest2.vim', 'D')
    493 
    494  let buf = RunVimInTerminal('-S Xtest1.vim', {})
    495 
    496  call s:RunDbgCmd(buf,
    497                \ ':debug call GlobalFunction()',
    498                \ ['cmd: call GlobalFunction()'])
    499  call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
    500 
    501  call s:RunDbgCmd(buf, 'backtrace', ['>backtrace',
    502                                    \ '->0 function GlobalFunction',
    503                                    \ 'line 1: call CallAFunction()'])
    504 
    505  call s:RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
    506  call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
    507 
    508  call s:RunDbgCmd(buf, 'backtrace', ['>backtrace',
    509                                    \ '  2 function GlobalFunction[1]',
    510                                    \ '  1 CallAFunction[1]',
    511                                    \ '->0 SourceAnotherFile',
    512                                    \ 'line 1: source Xtest2.vim'])
    513 
    514  " Step into the 'source' command. Note that we print the full trace all the
    515  " way though the source command.
    516  call s:RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
    517  call s:RunDbgCmd(buf, 'backtrace', [
    518        \ '>backtrace',
    519        \ '  3 function GlobalFunction[1]',
    520        \ '  2 CallAFunction[1]',
    521        \ '  1 SourceAnotherFile[1]',
    522        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
    523        \ 'line 1: func DoAThing()'])
    524 
    525  call s:RunDbgCmd( buf, 'up' )
    526  call s:RunDbgCmd( buf, 'backtrace', [
    527        \ '>backtrace',
    528        \ '  3 function GlobalFunction[1]',
    529        \ '  2 CallAFunction[1]',
    530        \ '->1 SourceAnotherFile[1]',
    531        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    532        \ 'line 1: func DoAThing()' ] )
    533 
    534  call s:RunDbgCmd( buf, 'up' )
    535  call s:RunDbgCmd( buf, 'backtrace', [
    536        \ '>backtrace',
    537        \ '  3 function GlobalFunction[1]',
    538        \ '->2 CallAFunction[1]',
    539        \ '  1 SourceAnotherFile[1]',
    540        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    541        \ 'line 1: func DoAThing()' ] )
    542 
    543  call s:RunDbgCmd( buf, 'up' )
    544  call s:RunDbgCmd( buf, 'backtrace', [
    545        \ '>backtrace',
    546        \ '->3 function GlobalFunction[1]',
    547        \ '  2 CallAFunction[1]',
    548        \ '  1 SourceAnotherFile[1]',
    549        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    550        \ 'line 1: func DoAThing()' ] )
    551 
    552  call s:RunDbgCmd( buf, 'up', [ 'frame at highest level: 3' ] )
    553  call s:RunDbgCmd( buf, 'backtrace', [
    554        \ '>backtrace',
    555        \ '->3 function GlobalFunction[1]',
    556        \ '  2 CallAFunction[1]',
    557        \ '  1 SourceAnotherFile[1]',
    558        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    559        \ 'line 1: func DoAThing()' ] )
    560 
    561  call s:RunDbgCmd( buf, 'down' )
    562  call s:RunDbgCmd( buf, 'backtrace', [
    563        \ '>backtrace',
    564        \ '  3 function GlobalFunction[1]',
    565        \ '->2 CallAFunction[1]',
    566        \ '  1 SourceAnotherFile[1]',
    567        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    568        \ 'line 1: func DoAThing()' ] )
    569 
    570  call s:RunDbgCmd( buf, 'down' )
    571  call s:RunDbgCmd( buf, 'backtrace', [
    572        \ '>backtrace',
    573        \ '  3 function GlobalFunction[1]',
    574        \ '  2 CallAFunction[1]',
    575        \ '->1 SourceAnotherFile[1]',
    576        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    577        \ 'line 1: func DoAThing()' ] )
    578 
    579  call s:RunDbgCmd( buf, 'down' )
    580  call s:RunDbgCmd( buf, 'backtrace', [
    581        \ '>backtrace',
    582        \ '  3 function GlobalFunction[1]',
    583        \ '  2 CallAFunction[1]',
    584        \ '  1 SourceAnotherFile[1]',
    585        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
    586        \ 'line 1: func DoAThing()' ] )
    587 
    588  call s:RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
    589 
    590  " step until we have another meaningful trace
    591  call s:RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
    592  call s:RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
    593  call s:RunDbgCmd(buf, 'backtrace', [
    594        \ '>backtrace',
    595        \ '  3 function GlobalFunction[1]',
    596        \ '  2 CallAFunction[1]',
    597        \ '  1 SourceAnotherFile[1]',
    598        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
    599        \ 'line 9: call File2Function()'])
    600 
    601  call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
    602  call s:RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
    603  call s:RunDbgCmd(buf, 'backtrace', [
    604        \ '>backtrace',
    605        \ '  5 function GlobalFunction[1]',
    606        \ '  4 CallAFunction[1]',
    607        \ '  3 SourceAnotherFile[1]',
    608        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
    609        \ '  1 function File2Function[1]',
    610        \ '->0 DoAThing',
    611        \ 'line 1: echo "DoAThing"'])
    612 
    613  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
    614  call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
    615  call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
    616  call s:RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
    617  call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
    618  call s:RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
    619  call s:RunDbgCmd(buf, 'backtrace', [
    620        \ '>backtrace',
    621        \ '  1 function GlobalFunction[1]',
    622        \ '->0 CallAFunction',
    623        \ 'line 2: call File2Function()'])
    624 
    625  call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
    626  call s:RunDbgCmd(buf, 'backtrace', [
    627        \ '>backtrace',
    628        \ '  2 function GlobalFunction[1]',
    629        \ '  1 CallAFunction[2]',
    630        \ '->0 File2Function',
    631        \ 'line 1: call DoAThing()'])
    632 
    633  call StopVimInTerminal(buf)
    634 endfunc
    635 
    636 func Test_Backtrace_Autocmd()
    637  CheckRunVimInTerminal
    638  CheckCWD
    639  let file1 =<< trim END
    640    func SourceAnotherFile()
    641      source Xtest2.vim
    642    endfunc
    643 
    644    func CallAFunction()
    645      call SourceAnotherFile()
    646      call File2Function()
    647    endfunc
    648 
    649    func GlobalFunction()
    650      call CallAFunction()
    651    endfunc
    652 
    653    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
    654  END
    655  call writefile(file1, 'Xtest1.vim', 'D')
    656 
    657  let file2 =<< trim END
    658    func DoAThing()
    659      echo "DoAThing"
    660    endfunc
    661 
    662    func File2Function()
    663      call DoAThing()
    664    endfunc
    665 
    666    call File2Function()
    667  END
    668  call writefile(file2, 'Xtest2.vim', 'D')
    669 
    670  let buf = RunVimInTerminal('-S Xtest1.vim', {})
    671 
    672  call s:RunDbgCmd(buf,
    673                \ ':debug doautocmd User TestGlobalFunction',
    674                \ ['cmd: doautocmd User TestGlobalFunction'])
    675  call s:RunDbgCmd(buf, 'step', ['cmd: call GlobalFunction() | echo "Done"'])
    676 
    677  " At this point the only thing in the stack is the autocommand
    678  call s:RunDbgCmd(buf, 'backtrace', [
    679        \ '>backtrace',
    680        \ '->0 User Autocommands for "TestGlobalFunction"',
    681        \ 'cmd: call GlobalFunction() | echo "Done"'])
    682 
    683  " And now we're back into the call stack
    684  call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
    685  call s:RunDbgCmd(buf, 'backtrace', [
    686        \ '>backtrace',
    687        \ '  1 User Autocommands for "TestGlobalFunction"',
    688        \ '->0 function GlobalFunction',
    689        \ 'line 1: call CallAFunction()'])
    690 
    691  call s:RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
    692  call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
    693 
    694  call s:RunDbgCmd(buf, 'backtrace', [
    695        \ '>backtrace',
    696        \ '  3 User Autocommands for "TestGlobalFunction"',
    697        \ '  2 function GlobalFunction[1]',
    698        \ '  1 CallAFunction[1]',
    699        \ '->0 SourceAnotherFile',
    700        \ 'line 1: source Xtest2.vim'])
    701 
    702  " Step into the 'source' command. Note that we print the full trace all the
    703  " way though the source command.
    704  call s:RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
    705  call s:RunDbgCmd(buf, 'backtrace', [
    706        \ '>backtrace',
    707        \ '  4 User Autocommands for "TestGlobalFunction"',
    708        \ '  3 function GlobalFunction[1]',
    709        \ '  2 CallAFunction[1]',
    710        \ '  1 SourceAnotherFile[1]',
    711        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
    712        \ 'line 1: func DoAThing()'])
    713 
    714  call s:RunDbgCmd( buf, 'up' )
    715  call s:RunDbgCmd( buf, 'backtrace', [
    716        \ '>backtrace',
    717        \ '  4 User Autocommands for "TestGlobalFunction"',
    718        \ '  3 function GlobalFunction[1]',
    719        \ '  2 CallAFunction[1]',
    720        \ '->1 SourceAnotherFile[1]',
    721        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    722        \ 'line 1: func DoAThing()' ] )
    723 
    724  call s:RunDbgCmd( buf, 'up' )
    725  call s:RunDbgCmd( buf, 'backtrace', [
    726        \ '>backtrace',
    727        \ '  4 User Autocommands for "TestGlobalFunction"',
    728        \ '  3 function GlobalFunction[1]',
    729        \ '->2 CallAFunction[1]',
    730        \ '  1 SourceAnotherFile[1]',
    731        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    732        \ 'line 1: func DoAThing()' ] )
    733 
    734  call s:RunDbgCmd( buf, 'up' )
    735  call s:RunDbgCmd( buf, 'backtrace', [
    736        \ '>backtrace',
    737        \ '  4 User Autocommands for "TestGlobalFunction"',
    738        \ '->3 function GlobalFunction[1]',
    739        \ '  2 CallAFunction[1]',
    740        \ '  1 SourceAnotherFile[1]',
    741        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    742        \ 'line 1: func DoAThing()' ] )
    743 
    744  call s:RunDbgCmd( buf, 'up' )
    745  call s:RunDbgCmd( buf, 'backtrace', [
    746        \ '>backtrace',
    747        \ '->4 User Autocommands for "TestGlobalFunction"',
    748        \ '  3 function GlobalFunction[1]',
    749        \ '  2 CallAFunction[1]',
    750        \ '  1 SourceAnotherFile[1]',
    751        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    752        \ 'line 1: func DoAThing()' ] )
    753 
    754  call s:RunDbgCmd( buf, 'up', [ 'frame at highest level: 4' ] )
    755  call s:RunDbgCmd( buf, 'backtrace', [
    756        \ '>backtrace',
    757        \ '->4 User Autocommands for "TestGlobalFunction"',
    758        \ '  3 function GlobalFunction[1]',
    759        \ '  2 CallAFunction[1]',
    760        \ '  1 SourceAnotherFile[1]',
    761        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    762        \ 'line 1: func DoAThing()' ] )
    763 
    764  call s:RunDbgCmd( buf, 'down' )
    765  call s:RunDbgCmd( buf, 'backtrace', [
    766        \ '>backtrace',
    767        \ '  4 User Autocommands for "TestGlobalFunction"',
    768        \ '->3 function GlobalFunction[1]',
    769        \ '  2 CallAFunction[1]',
    770        \ '  1 SourceAnotherFile[1]',
    771        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    772        \ 'line 1: func DoAThing()' ] )
    773 
    774 
    775  call s:RunDbgCmd( buf, 'down' )
    776  call s:RunDbgCmd( buf, 'backtrace', [
    777        \ '>backtrace',
    778        \ '  4 User Autocommands for "TestGlobalFunction"',
    779        \ '  3 function GlobalFunction[1]',
    780        \ '->2 CallAFunction[1]',
    781        \ '  1 SourceAnotherFile[1]',
    782        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    783        \ 'line 1: func DoAThing()' ] )
    784 
    785  call s:RunDbgCmd( buf, 'down' )
    786  call s:RunDbgCmd( buf, 'backtrace', [
    787        \ '>backtrace',
    788        \ '  4 User Autocommands for "TestGlobalFunction"',
    789        \ '  3 function GlobalFunction[1]',
    790        \ '  2 CallAFunction[1]',
    791        \ '->1 SourceAnotherFile[1]',
    792        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
    793        \ 'line 1: func DoAThing()' ] )
    794 
    795  call s:RunDbgCmd( buf, 'down' )
    796  call s:RunDbgCmd( buf, 'backtrace', [
    797        \ '>backtrace',
    798        \ '  4 User Autocommands for "TestGlobalFunction"',
    799        \ '  3 function GlobalFunction[1]',
    800        \ '  2 CallAFunction[1]',
    801        \ '  1 SourceAnotherFile[1]',
    802        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
    803        \ 'line 1: func DoAThing()' ] )
    804 
    805  call s:RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
    806 
    807  " step until we have another meaningful trace
    808  call s:RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
    809  call s:RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
    810  call s:RunDbgCmd(buf, 'backtrace', [
    811        \ '>backtrace',
    812        \ '  4 User Autocommands for "TestGlobalFunction"',
    813        \ '  3 function GlobalFunction[1]',
    814        \ '  2 CallAFunction[1]',
    815        \ '  1 SourceAnotherFile[1]',
    816        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
    817        \ 'line 9: call File2Function()'])
    818 
    819  call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
    820  call s:RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
    821  call s:RunDbgCmd(buf, 'backtrace', [
    822        \ '>backtrace',
    823        \ '  6 User Autocommands for "TestGlobalFunction"',
    824        \ '  5 function GlobalFunction[1]',
    825        \ '  4 CallAFunction[1]',
    826        \ '  3 SourceAnotherFile[1]',
    827        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
    828        \ '  1 function File2Function[1]',
    829        \ '->0 DoAThing',
    830        \ 'line 1: echo "DoAThing"'])
    831 
    832  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
    833  call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
    834  call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
    835  call s:RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
    836  call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
    837  call s:RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
    838  call s:RunDbgCmd(buf, 'backtrace', [
    839        \ '>backtrace',
    840        \ '  2 User Autocommands for "TestGlobalFunction"',
    841        \ '  1 function GlobalFunction[1]',
    842        \ '->0 CallAFunction',
    843        \ 'line 2: call File2Function()'])
    844 
    845  call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
    846  call s:RunDbgCmd(buf, 'backtrace', [
    847        \ '>backtrace',
    848        \ '  3 User Autocommands for "TestGlobalFunction"',
    849        \ '  2 function GlobalFunction[1]',
    850        \ '  1 CallAFunction[2]',
    851        \ '->0 File2Function',
    852        \ 'line 1: call DoAThing()'])
    853 
    854 
    855  " Now unwind so that we get back to the original autocommand (and the second
    856  " cmd echo "Done")
    857  call s:RunDbgCmd(buf, 'finish', ['line 1: End of function'])
    858  call s:RunDbgCmd(buf, 'backtrace', [
    859        \ '>backtrace',
    860        \ '  3 User Autocommands for "TestGlobalFunction"',
    861        \ '  2 function GlobalFunction[1]',
    862        \ '  1 CallAFunction[2]',
    863        \ '->0 File2Function',
    864        \ 'line 1: End of function'])
    865 
    866  call s:RunDbgCmd(buf, 'finish', ['line 2: End of function'])
    867  call s:RunDbgCmd(buf, 'backtrace', [
    868        \ '>backtrace',
    869        \ '  2 User Autocommands for "TestGlobalFunction"',
    870        \ '  1 function GlobalFunction[1]',
    871        \ '->0 CallAFunction',
    872        \ 'line 2: End of function'])
    873 
    874  call s:RunDbgCmd(buf, 'finish', ['line 1: End of function'])
    875  call s:RunDbgCmd(buf, 'backtrace', [
    876        \ '>backtrace',
    877        \ '  1 User Autocommands for "TestGlobalFunction"',
    878        \ '->0 function GlobalFunction',
    879        \ 'line 1: End of function'])
    880 
    881  call s:RunDbgCmd(buf, 'step', ['cmd: echo "Done"'])
    882  call s:RunDbgCmd(buf, 'backtrace', [
    883        \ '>backtrace',
    884        \ '->0 User Autocommands for "TestGlobalFunction"',
    885        \ 'cmd: echo "Done"'])
    886 
    887  call StopVimInTerminal(buf)
    888 endfunc
    889 
    890 func Test_Backtrace_CmdLine()
    891  CheckRunVimInTerminal
    892  CheckCWD
    893  let file1 =<< trim END
    894    func SourceAnotherFile()
    895      source Xtest2.vim
    896    endfunc
    897 
    898    func CallAFunction()
    899      call SourceAnotherFile()
    900      call File2Function()
    901    endfunc
    902 
    903    func GlobalFunction()
    904      call CallAFunction()
    905    endfunc
    906 
    907    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
    908  END
    909  call writefile(file1, 'Xtest1.vim', 'D')
    910 
    911  let file2 =<< trim END
    912    func DoAThing()
    913      echo "DoAThing"
    914    endfunc
    915 
    916    func File2Function()
    917      call DoAThing()
    918    endfunc
    919 
    920    call File2Function()
    921  END
    922  call writefile(file2, 'Xtest2.vim', 'D')
    923 
    924  let buf = RunVimInTerminal(
    925        \ '-S Xtest1.vim -c "debug call GlobalFunction()"',
    926        \ {'wait_for_ruler': 0})
    927 
    928  " Need to wait for the vim-in-terminal to be ready.
    929  " With valgrind this can take quite long.
    930  call CheckDbgOutput(buf, ['command line',
    931                            \ 'cmd: call GlobalFunction()'], #{msec: 5000})
    932 
    933  " At this point the only thing in the stack is the cmdline
    934  call s:RunDbgCmd(buf, 'backtrace', [
    935        \ '>backtrace',
    936        \ '->0 command line',
    937        \ 'cmd: call GlobalFunction()'])
    938 
    939  " And now we're back into the call stack
    940  call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
    941  call s:RunDbgCmd(buf, 'backtrace', [
    942        \ '>backtrace',
    943        \ '  1 command line',
    944        \ '->0 function GlobalFunction',
    945        \ 'line 1: call CallAFunction()'])
    946 
    947  call StopVimInTerminal(buf)
    948 endfunc
    949 
    950 func Test_Backtrace_DefFunction()
    951  CheckRunVimInTerminal
    952  CheckCWD
    953  let file1 =<< trim END
    954    vim9script
    955    import './Xtest2.vim' as imp
    956 
    957    def SourceAnotherFile()
    958      source Xtest2.vim
    959    enddef
    960 
    961    def CallAFunction()
    962      SourceAnotherFile()
    963      imp.File2Function()
    964    enddef
    965 
    966    def g:GlobalFunction()
    967      var some = "some var"
    968      CallAFunction()
    969    enddef
    970 
    971    defcompile
    972  END
    973  call writefile(file1, 'Xtest1.vim', 'D')
    974 
    975  let file2 =<< trim END
    976    vim9script
    977 
    978    def DoAThing(): number
    979      var a = 100 * 2
    980      a += 3
    981      return a
    982    enddef
    983 
    984    export def File2Function()
    985      DoAThing()
    986    enddef
    987 
    988    defcompile
    989    File2Function()
    990  END
    991  call writefile(file2, 'Xtest2.vim', 'D')
    992 
    993  let buf = RunVimInTerminal('-S Xtest1.vim', {})
    994 
    995  call s:RunDbgCmd(buf,
    996                \ ':debug call GlobalFunction()',
    997                \ ['cmd: call GlobalFunction()'])
    998 
    999  call s:RunDbgCmd(buf, 'step', ['line 1: var some = "some var"'])
   1000  call s:RunDbgCmd(buf, 'step', ['line 2: CallAFunction()'])
   1001  call s:RunDbgCmd(buf, 'echo some', ['some var'])
   1002 
   1003  call s:RunDbgCmd(buf, 'backtrace', [
   1004        \ '\V>backtrace',
   1005        \ '\V->0 function GlobalFunction',
   1006        \ '\Vline 2: CallAFunction()',
   1007        \ ],
   1008        \ #{match: 'pattern'})
   1009 
   1010  call s:RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()'])
   1011  call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
   1012  " Repeated line, because we fist are in the compiled function before the
   1013  " EXEC and then in do_cmdline() before the :source command.
   1014  call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
   1015  call s:RunDbgCmd(buf, 'step', ['line 1: vim9script'])
   1016  call s:RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number'])
   1017  call s:RunDbgCmd(buf, 'step', ['line 9: export def File2Function()'])
   1018  call s:RunDbgCmd(buf, 'step', ['line 13: defcompile'])
   1019  call s:RunDbgCmd(buf, 'step', ['line 14: File2Function()'])
   1020  call s:RunDbgCmd(buf, 'backtrace', [
   1021        \ '\V>backtrace',
   1022        \ '\V  3 function GlobalFunction[2]',
   1023        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
   1024        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
   1025        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
   1026        \ '\Vline 14: File2Function()'],
   1027        \ #{match: 'pattern'})
   1028 
   1029  " Don't step into compiled functions...
   1030  call s:RunDbgCmd(buf, 'next', ['line 15: End of sourced file'])
   1031  call s:RunDbgCmd(buf, 'backtrace', [
   1032        \ '\V>backtrace',
   1033        \ '\V  3 function GlobalFunction[2]',
   1034        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
   1035        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
   1036        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
   1037        \ '\Vline 15: End of sourced file'],
   1038        \ #{match: 'pattern'})
   1039 
   1040  call StopVimInTerminal(buf)
   1041 endfunc
   1042 
   1043 func Test_DefFunction_expr()
   1044  CheckRunVimInTerminal
   1045  CheckCWD
   1046  let file3 =<< trim END
   1047      vim9script
   1048      g:someVar = "foo"
   1049      def g:ChangeVar()
   1050        g:someVar = "bar"
   1051        echo "changed"
   1052      enddef
   1053      defcompile
   1054  END
   1055  call writefile(file3, 'Xtest3.vim', 'D')
   1056  let buf = RunVimInTerminal('-S Xtest3.vim', {})
   1057 
   1058  call s:RunDbgCmd(buf, ':breakadd expr g:someVar')
   1059  call s:RunDbgCmd(buf, ':call g:ChangeVar()', ['Oldval = "''foo''"', 'Newval = "''bar''"', 'function ChangeVar', 'line 2: echo "changed"'])
   1060 
   1061  call StopVimInTerminal(buf)
   1062 endfunc
   1063 
   1064 func Test_debug_def_and_legacy_function()
   1065  CheckRunVimInTerminal
   1066  CheckCWD
   1067  let file =<< trim END
   1068    vim9script
   1069    def g:SomeFunc()
   1070      echo "here"
   1071      echo "and"
   1072      echo "there"
   1073      breakadd func 2 LocalFunc
   1074      LocalFunc()
   1075    enddef
   1076 
   1077    def LocalFunc()
   1078      echo "first"
   1079      echo "second"
   1080      breakadd func LegacyFunc
   1081      LegacyFunc()
   1082    enddef
   1083 
   1084    func LegacyFunc()
   1085      echo "legone"
   1086      echo "legtwo"
   1087    endfunc
   1088 
   1089    breakadd func 2 g:SomeFunc
   1090  END
   1091  call writefile(file, 'XtestDebug.vim', 'D')
   1092 
   1093  let buf = RunVimInTerminal('-S XtestDebug.vim', {})
   1094 
   1095  call s:RunDbgCmd(buf,':call SomeFunc()', ['line 2: echo "and"'])
   1096  call s:RunDbgCmd(buf,'next', ['line 3: echo "there"'])
   1097  call s:RunDbgCmd(buf,'next', ['line 4: breakadd func 2 LocalFunc'])
   1098 
   1099  " continue, next breakpoint is in LocalFunc()
   1100  call s:RunDbgCmd(buf,'cont', ['line 2: echo "second"'])
   1101 
   1102  " continue, next breakpoint is in LegacyFunc()
   1103  call s:RunDbgCmd(buf,'cont', ['line 1: echo "legone"'])
   1104 
   1105  call s:RunDbgCmd(buf, 'cont')
   1106 
   1107  call StopVimInTerminal(buf)
   1108 endfunc
   1109 
   1110 func Test_debug_def_function()
   1111  CheckRunVimInTerminal
   1112  CheckCWD
   1113  let file =<< trim END
   1114    vim9script
   1115    def g:Func()
   1116      var n: number
   1117      def Closure(): number
   1118          return n + 3
   1119      enddef
   1120      n += Closure()
   1121      echo 'result: ' .. n
   1122    enddef
   1123 
   1124    def g:FuncWithArgs(text: string, nr: number, ...items: list<number>)
   1125      echo text .. nr
   1126      for it in items
   1127        echo it
   1128      endfor
   1129      echo "done"
   1130    enddef
   1131 
   1132    def g:FuncWithDict()
   1133      var d = {
   1134         a: 1,
   1135         b: 2,
   1136         }
   1137         # comment
   1138         def Inner()
   1139           eval 1 + 2
   1140         enddef
   1141    enddef
   1142 
   1143    def g:FuncComment()
   1144      # comment
   1145      echo "first"
   1146         .. "one"
   1147      # comment
   1148      echo "second"
   1149    enddef
   1150 
   1151    def g:FuncForLoop()
   1152      eval 1 + 2
   1153      for i in [11, 22, 33]
   1154        eval i + 2
   1155      endfor
   1156      echo "done"
   1157    enddef
   1158 
   1159    def g:FuncWithSplitLine()
   1160        eval 1 + 2
   1161           | eval 2 + 3
   1162    enddef
   1163  END
   1164  call writefile(file, 'Xtest.vim', 'D')
   1165 
   1166  let buf = RunVimInTerminal('-S Xtest.vim', {})
   1167 
   1168  call s:RunDbgCmd(buf,
   1169                \ ':debug call Func()',
   1170                \ ['cmd: call Func()'])
   1171  call s:RunDbgCmd(buf, 'next', ['result: 3'])
   1172  call term_sendkeys(buf, "\r")
   1173  call s:RunDbgCmd(buf, 'cont')
   1174 
   1175  call s:RunDbgCmd(buf,
   1176                \ ':debug call FuncWithArgs("asdf", 42, 1, 2, 3)',
   1177                \ ['cmd: call FuncWithArgs("asdf", 42, 1, 2, 3)'])
   1178  call s:RunDbgCmd(buf, 'step', ['line 1: echo text .. nr'])
   1179  call s:RunDbgCmd(buf, 'echo text', ['asdf'])
   1180  call s:RunDbgCmd(buf, 'echo nr', ['42'])
   1181  call s:RunDbgCmd(buf, 'echo items', ['[1, 2, 3]'])
   1182  call s:RunDbgCmd(buf, 'step', ['asdf42', 'function FuncWithArgs', 'line 2:   for it in items'])
   1183  call s:RunDbgCmd(buf, 'step', ['function FuncWithArgs', 'line 2: for it in items'])
   1184  call s:RunDbgCmd(buf, 'echo it', ['0'])
   1185  call s:RunDbgCmd(buf, 'step', ['line 3: echo it'])
   1186  call s:RunDbgCmd(buf, 'echo it', ['1'])
   1187  call s:RunDbgCmd(buf, 'step', ['1', 'function FuncWithArgs', 'line 4: endfor'])
   1188  call s:RunDbgCmd(buf, 'step', ['line 2: for it in items'])
   1189  call s:RunDbgCmd(buf, 'echo it', ['1'])
   1190  call s:RunDbgCmd(buf, 'step', ['line 3: echo it'])
   1191  call s:RunDbgCmd(buf, 'step', ['2', 'function FuncWithArgs', 'line 4: endfor'])
   1192  call s:RunDbgCmd(buf, 'step', ['line 2: for it in items'])
   1193  call s:RunDbgCmd(buf, 'echo it', ['2'])
   1194  call s:RunDbgCmd(buf, 'step', ['line 3: echo it'])
   1195  call s:RunDbgCmd(buf, 'step', ['3', 'function FuncWithArgs', 'line 4: endfor'])
   1196  call s:RunDbgCmd(buf, 'step', ['line 2: for it in items'])
   1197  call s:RunDbgCmd(buf, 'step', ['line 5: echo "done"'])
   1198  call s:RunDbgCmd(buf, 'cont')
   1199 
   1200  call s:RunDbgCmd(buf,
   1201                \ ':debug call FuncWithDict()',
   1202                \ ['cmd: call FuncWithDict()'])
   1203  call s:RunDbgCmd(buf, 'step', ['line 1: var d = {  a: 1,  b: 2,  }'])
   1204  call s:RunDbgCmd(buf, 'step', ['line 6: def Inner()'])
   1205  call s:RunDbgCmd(buf, 'cont')
   1206 
   1207  call s:RunDbgCmd(buf, ':breakadd func 1 FuncComment')
   1208  call s:RunDbgCmd(buf, ':call FuncComment()', ['function FuncComment', 'line 2: echo "first"  .. "one"'])
   1209  call s:RunDbgCmd(buf, ':breakadd func 3 FuncComment')
   1210  call s:RunDbgCmd(buf, 'cont', ['function FuncComment', 'line 5: echo "second"'])
   1211  call s:RunDbgCmd(buf, 'cont')
   1212 
   1213  call s:RunDbgCmd(buf, ':breakadd func 2 FuncForLoop')
   1214  call s:RunDbgCmd(buf, ':call FuncForLoop()', ['function FuncForLoop', 'line 2:   for i in [11, 22, 33]'])
   1215  call s:RunDbgCmd(buf, 'step', ['line 2: for i in [11, 22, 33]'])
   1216  call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 3: eval i + 2'])
   1217  call s:RunDbgCmd(buf, 'echo i', ['11'])
   1218  call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 4: endfor'])
   1219  call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 2: for i in [11, 22, 33]'])
   1220  call s:RunDbgCmd(buf, 'next', ['line 3: eval i + 2'])
   1221  call s:RunDbgCmd(buf, 'echo i', ['22'])
   1222 
   1223  call s:RunDbgCmd(buf, 'breakdel *')
   1224  call s:RunDbgCmd(buf, 'cont')
   1225 
   1226  call s:RunDbgCmd(buf, ':breakadd func FuncWithSplitLine')
   1227  call s:RunDbgCmd(buf, ':call FuncWithSplitLine()', ['function FuncWithSplitLine', 'line 1: eval 1 + 2 | eval 2 + 3'])
   1228 
   1229  call s:RunDbgCmd(buf, 'cont')
   1230  call StopVimInTerminal(buf)
   1231 endfunc
   1232 
   1233 func Test_debug_def_function_with_lambda()
   1234  CheckRunVimInTerminal
   1235  CheckCWD
   1236  let lines =<< trim END
   1237     vim9script
   1238     def g:Func()
   1239       var s = 'a'
   1240       ['b']->map((_, v) => s)
   1241       echo "done"
   1242     enddef
   1243     breakadd func 2 g:Func
   1244  END
   1245  call writefile(lines, 'XtestLambda.vim', 'D')
   1246 
   1247  let buf = RunVimInTerminal('-S XtestLambda.vim', {})
   1248 
   1249  call s:RunDbgCmd(buf,
   1250                \ ':call g:Func()',
   1251                \ ['function Func', 'line 2: [''b'']->map((_, v) => s)'])
   1252  call s:RunDbgCmd(buf,
   1253                \ 'next',
   1254                \ ['function Func', 'line 3: echo "done"'])
   1255 
   1256  call s:RunDbgCmd(buf, 'cont')
   1257  call StopVimInTerminal(buf)
   1258 endfunc
   1259 
   1260 func Test_debug_backtrace_level()
   1261  CheckRunVimInTerminal
   1262  CheckCWD
   1263  let lines =<< trim END
   1264    let s:file1_var = 'file1'
   1265    let g:global_var = 'global'
   1266 
   1267    func s:File1Func( arg )
   1268      let s:file1_var .= a:arg
   1269      let local_var = s:file1_var .. ' test1'
   1270      let g:global_var .= local_var
   1271      source Xtest2.vim
   1272    endfunc
   1273 
   1274    call s:File1Func( 'arg1' )
   1275  END
   1276  call writefile(lines, 'Xtest1.vim', 'D')
   1277 
   1278  let lines =<< trim END
   1279    let s:file2_var = 'file2'
   1280 
   1281    func s:File2Func( arg )
   1282      let s:file2_var .= a:arg
   1283      let local_var = s:file2_var .. ' test2'
   1284      let g:global_var .= local_var
   1285    endfunc
   1286 
   1287    call s:File2Func( 'arg2' )
   1288  END
   1289  call writefile(lines, 'Xtest2.vim', 'D')
   1290 
   1291  let file1 = getcwd() .. '/Xtest1.vim'
   1292  let file2 = getcwd() .. '/Xtest2.vim'
   1293 
   1294  " set a breakpoint and source file1.vim
   1295  let buf = RunVimInTerminal(
   1296        \ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim',
   1297        \ #{wait_for_ruler: 0})
   1298 
   1299  call CheckDbgOutput(buf, [
   1300        \ 'Breakpoint in "' .. file1 .. '" line 1',
   1301        \ 'Entering Debug mode.  Type "cont" to continue.',
   1302        \ 'command line..script ' .. file1,
   1303        \ 'line 1: let s:file1_var = ''file1'''
   1304        \ ], #{msec: 5000})
   1305 
   1306  " step through the initial declarations
   1307  call s:RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
   1308  call s:RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] )
   1309  call s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
   1310  call s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
   1311  call s:RunDbgCmd(buf, 'echo global_var', [ 'global' ] )
   1312 
   1313  " step in to the first function
   1314  call s:RunDbgCmd(buf, 'step', [ 'line 11: call s:File1Func( ''arg1'' )' ] )
   1315  call s:RunDbgCmd(buf, 'step', [ 'line 1: let s:file1_var .= a:arg' ] )
   1316  call s:RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
   1317  call s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
   1318  call s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
   1319  call s:RunDbgCmd(buf,
   1320                \'echo global_var',
   1321                \[ 'E121: Undefined variable: global_var' ] )
   1322  call s:RunDbgCmd(buf,
   1323                \'echo local_var',
   1324                \[ 'E121: Undefined variable: local_var' ] )
   1325  call s:RunDbgCmd(buf,
   1326                \'echo l:local_var',
   1327                \[ 'E121: Undefined variable: l:local_var' ] )
   1328 
   1329  " backtrace up
   1330  call s:RunDbgCmd(buf, 'backtrace', [
   1331        \ '\V>backtrace',
   1332        \ '\V  2 command line',
   1333        \ '\V  1 script ' .. file1 .. '[11]',
   1334        \ '\V->0 function <SNR>\.\*_File1Func',
   1335        \ '\Vline 1: let s:file1_var .= a:arg',
   1336        \ ],
   1337        \ #{ match: 'pattern' } )
   1338  call s:RunDbgCmd(buf, 'up', [ '>up' ] )
   1339 
   1340  call s:RunDbgCmd(buf, 'backtrace', [
   1341        \ '\V>backtrace',
   1342        \ '\V  2 command line',
   1343        \ '\V->1 script ' .. file1 .. '[11]',
   1344        \ '\V  0 function <SNR>\.\*_File1Func',
   1345        \ '\Vline 1: let s:file1_var .= a:arg',
   1346        \ ],
   1347        \ #{ match: 'pattern' } )
   1348 
   1349  " Expression evaluation in the script frame (not the function frame)
   1350  " FIXME: Unexpected in this scope (a: should not be visible)
   1351  call s:RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
   1352  call s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
   1353  call s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
   1354  " FIXME: Unexpected in this scope (global should be found)
   1355  call s:RunDbgCmd(buf,
   1356                \'echo global_var',
   1357                \[ 'E121: Undefined variable: global_var' ] )
   1358  call s:RunDbgCmd(buf,
   1359                \'echo local_var',
   1360                \[ 'E121: Undefined variable: local_var' ] )
   1361  call s:RunDbgCmd(buf,
   1362                \'echo l:local_var',
   1363                \[ 'E121: Undefined variable: l:local_var' ] )
   1364 
   1365 
   1366  " step while backtraced jumps to the latest frame
   1367  call s:RunDbgCmd(buf, 'step', [
   1368        \ 'line 2: let local_var = s:file1_var .. '' test1''' ] )
   1369  call s:RunDbgCmd(buf, 'backtrace', [
   1370        \ '\V>backtrace',
   1371        \ '\V  2 command line',
   1372        \ '\V  1 script ' .. file1 .. '[11]',
   1373        \ '\V->0 function <SNR>\.\*_File1Func',
   1374        \ '\Vline 2: let local_var = s:file1_var .. '' test1''',
   1375        \ ],
   1376        \ #{ match: 'pattern' } )
   1377 
   1378  call s:RunDbgCmd(buf, 'step', [ 'line 3: let g:global_var .= local_var' ] )
   1379  call s:RunDbgCmd(buf, 'echo local_var', [ 'file1arg1 test1' ] )
   1380  call s:RunDbgCmd(buf, 'echo l:local_var', [ 'file1arg1 test1' ] )
   1381 
   1382  call s:RunDbgCmd(buf, 'step', [ 'line 4: source Xtest2.vim' ] )
   1383  call s:RunDbgCmd(buf, 'step', [ 'line 1: let s:file2_var = ''file2''' ] )
   1384  call s:RunDbgCmd(buf, 'backtrace', [
   1385        \ '\V>backtrace',
   1386        \ '\V  3 command line',
   1387        \ '\V  2 script ' .. file1 .. '[11]',
   1388        \ '\V  1 function <SNR>\.\*_File1Func[4]',
   1389        \ '\V->0 script ' .. file2,
   1390        \ '\Vline 1: let s:file2_var = ''file2''',
   1391        \ ],
   1392        \ #{ match: 'pattern' } )
   1393 
   1394  " Expression evaluation in the script frame file2 (not the function frame)
   1395  call s:RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
   1396  call s:RunDbgCmd(buf,
   1397        \ 'echo s:file1_var',
   1398        \ [ 'E121: Undefined variable: s:file1_var' ] )
   1399  call s:RunDbgCmd(buf, 'echo g:global_var', [ 'globalfile1arg1 test1' ] )
   1400  call s:RunDbgCmd(buf, 'echo global_var', [ 'globalfile1arg1 test1' ] )
   1401  call s:RunDbgCmd(buf,
   1402                \'echo local_var',
   1403                \[ 'E121: Undefined variable: local_var' ] )
   1404  call s:RunDbgCmd(buf,
   1405                \'echo l:local_var',
   1406                \[ 'E121: Undefined variable: l:local_var' ] )
   1407  call s:RunDbgCmd(buf,
   1408        \ 'echo s:file2_var',
   1409        \ [ 'E121: Undefined variable: s:file2_var' ] )
   1410 
   1411  call s:RunDbgCmd(buf, 'step', [ 'line 3: func s:File2Func( arg )' ] )
   1412  call s:RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
   1413 
   1414  " Up the stack to the other script context
   1415  call s:RunDbgCmd(buf, 'up')
   1416  call s:RunDbgCmd(buf, 'backtrace', [
   1417        \ '\V>backtrace',
   1418        \ '\V  3 command line',
   1419        \ '\V  2 script ' .. file1 .. '[11]',
   1420        \ '\V->1 function <SNR>\.\*_File1Func[4]',
   1421        \ '\V  0 script ' .. file2,
   1422        \ '\Vline 3: func s:File2Func( arg )',
   1423        \ ],
   1424        \ #{ match: 'pattern' } )
   1425  " FIXME: Unexpected. Should see the a: and l: dicts from File1Func
   1426  call s:RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
   1427  call s:RunDbgCmd(buf,
   1428        \ 'echo l:local_var',
   1429        \ [ 'E121: Undefined variable: l:local_var' ] )
   1430 
   1431  call s:RunDbgCmd(buf, 'up')
   1432  call s:RunDbgCmd(buf, 'backtrace', [
   1433        \ '\V>backtrace',
   1434        \ '\V  3 command line',
   1435        \ '\V->2 script ' .. file1 .. '[11]',
   1436        \ '\V  1 function <SNR>\.\*_File1Func[4]',
   1437        \ '\V  0 script ' .. file2,
   1438        \ '\Vline 3: func s:File2Func( arg )',
   1439        \ ],
   1440        \ #{ match: 'pattern' } )
   1441 
   1442  " FIXME: Unexpected (wrong script vars are used)
   1443  call s:RunDbgCmd(buf,
   1444        \ 'echo s:file1_var',
   1445        \ [ 'E121: Undefined variable: s:file1_var' ] )
   1446  call s:RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
   1447 
   1448  call s:RunDbgCmd(buf, 'cont')
   1449  call StopVimInTerminal(buf)
   1450 endfunc
   1451 
   1452 " Test for setting a breakpoint on a :endif where the :if condition is false
   1453 " and then quit the script. This should generate an interrupt.
   1454 func Test_breakpt_endif_intr()
   1455  func F()
   1456    let g:Xpath ..= 'a'
   1457    if v:false
   1458      let g:Xpath ..= 'b'
   1459    endif
   1460    invalid_command
   1461  endfunc
   1462 
   1463  let g:Xpath = ''
   1464  breakadd func 4 F
   1465  try
   1466    let caught_intr = 0
   1467    debuggreedy
   1468    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
   1469  catch /^Vim:Interrupt$/
   1470    call assert_match('\.F, line 4', v:throwpoint)
   1471    let caught_intr = 1
   1472  endtry
   1473  0debuggreedy
   1474  call assert_equal(1, caught_intr)
   1475  call assert_equal('a', g:Xpath)
   1476  breakdel *
   1477  delfunc F
   1478 endfunc
   1479 
   1480 " Test for setting a breakpoint on a :else where the :if condition is false
   1481 " and then quit the script. This should generate an interrupt.
   1482 func Test_breakpt_else_intr()
   1483  func F()
   1484    let g:Xpath ..= 'a'
   1485    if v:false
   1486      let g:Xpath ..= 'b'
   1487    else
   1488      invalid_command
   1489    endif
   1490    invalid_command
   1491  endfunc
   1492 
   1493  let g:Xpath = ''
   1494  breakadd func 4 F
   1495  try
   1496    let caught_intr = 0
   1497    debuggreedy
   1498    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
   1499  catch /^Vim:Interrupt$/
   1500    call assert_match('\.F, line 4', v:throwpoint)
   1501    let caught_intr = 1
   1502  endtry
   1503  0debuggreedy
   1504  call assert_equal(1, caught_intr)
   1505  call assert_equal('a', g:Xpath)
   1506  breakdel *
   1507  delfunc F
   1508 endfunc
   1509 
   1510 " Test for setting a breakpoint on a :endwhile where the :while condition is
   1511 " false and then quit the script. This should generate an interrupt.
   1512 func Test_breakpt_endwhile_intr()
   1513  func F()
   1514    let g:Xpath ..= 'a'
   1515    while v:false
   1516      let g:Xpath ..= 'b'
   1517    endwhile
   1518    invalid_command
   1519  endfunc
   1520 
   1521  let g:Xpath = ''
   1522  breakadd func 4 F
   1523  try
   1524    let caught_intr = 0
   1525    debuggreedy
   1526    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
   1527  catch /^Vim:Interrupt$/
   1528    call assert_match('\.F, line 4', v:throwpoint)
   1529    let caught_intr = 1
   1530  endtry
   1531  0debuggreedy
   1532  call assert_equal(1, caught_intr)
   1533  call assert_equal('a', g:Xpath)
   1534  breakdel *
   1535  delfunc F
   1536 endfunc
   1537 
   1538 " Test for setting a breakpoint on a script local function
   1539 func Test_breakpt_scriptlocal_func()
   1540  let g:Xpath = ''
   1541  func s:G()
   1542    let g:Xpath ..= 'a'
   1543  endfunc
   1544 
   1545  let funcname = expand("<SID>") .. "G"
   1546  exe "breakadd func 1 " .. funcname
   1547  debuggreedy
   1548  redir => output
   1549  call feedkeys(":call " .. funcname .. "()\<CR>c\<CR>", "xt")
   1550  redir END
   1551  0debuggreedy
   1552  call assert_match('Breakpoint in "' .. funcname .. '" line 1', output)
   1553  call assert_equal('a', g:Xpath)
   1554  breakdel *
   1555  exe "delfunc " .. funcname
   1556 endfunc
   1557 
   1558 " vim: shiftwidth=2 sts=2 expandtab