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