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