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