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