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')
340endfunc
341
342func Test_Backtrace_Through_Source()
343  CheckCWD
344  let file1 =<< trim END
345    func SourceAnotherFile()
346      source Xtest2.vim
347    endfunc
348
349    func CallAFunction()
350      call SourceAnotherFile()
351      call File2Function()
352    endfunc
353
354    func GlobalFunction()
355      call CallAFunction()
356    endfunc
357  END
358  call writefile(file1, 'Xtest1.vim')
359
360  let file2 =<< trim END
361    func DoAThing()
362      echo "DoAThing"
363    endfunc
364
365    func File2Function()
366      call DoAThing()
367    endfunc
368
369    call File2Function()
370  END
371  call writefile(file2, 'Xtest2.vim')
372
373  let buf = RunVimInTerminal('-S Xtest1.vim', {})
374
375  call RunDbgCmd(buf,
376                \ ':debug call GlobalFunction()',
377                \ ['cmd: call GlobalFunction()'])
378  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
379
380  call RunDbgCmd(buf, 'backtrace', ['>backtrace',
381                                    \ '->0 function GlobalFunction',
382                                    \ 'line 1: call CallAFunction()'])
383
384  call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
385  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
386
387  call RunDbgCmd(buf, 'backtrace', ['>backtrace',
388                                    \ '  2 function GlobalFunction[1]',
389                                    \ '  1 CallAFunction[1]',
390                                    \ '->0 SourceAnotherFile',
391                                    \ 'line 1: source Xtest2.vim'])
392
393  " Step into the 'source' command. Note that we print the full trace all the
394  " way though the source command.
395  call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
396  call RunDbgCmd(buf, 'backtrace', [
397        \ '>backtrace',
398        \ '  3 function GlobalFunction[1]',
399        \ '  2 CallAFunction[1]',
400        \ '  1 SourceAnotherFile[1]',
401        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
402        \ 'line 1: func DoAThing()'])
403
404  call RunDbgCmd( buf, 'up' )
405  call RunDbgCmd( buf, 'backtrace', [
406        \ '>backtrace',
407        \ '  3 function GlobalFunction[1]',
408        \ '  2 CallAFunction[1]',
409        \ '->1 SourceAnotherFile[1]',
410        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
411        \ 'line 1: func DoAThing()' ] )
412
413  call RunDbgCmd( buf, 'up' )
414  call RunDbgCmd( buf, 'backtrace', [
415        \ '>backtrace',
416        \ '  3 function GlobalFunction[1]',
417        \ '->2 CallAFunction[1]',
418        \ '  1 SourceAnotherFile[1]',
419        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
420        \ 'line 1: func DoAThing()' ] )
421
422  call RunDbgCmd( buf, 'up' )
423  call RunDbgCmd( buf, 'backtrace', [
424        \ '>backtrace',
425        \ '->3 function GlobalFunction[1]',
426        \ '  2 CallAFunction[1]',
427        \ '  1 SourceAnotherFile[1]',
428        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
429        \ 'line 1: func DoAThing()' ] )
430
431  call RunDbgCmd( buf, 'up', [ 'frame at highest level: 3' ] )
432  call RunDbgCmd( buf, 'backtrace', [
433        \ '>backtrace',
434        \ '->3 function GlobalFunction[1]',
435        \ '  2 CallAFunction[1]',
436        \ '  1 SourceAnotherFile[1]',
437        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
438        \ 'line 1: func DoAThing()' ] )
439
440  call RunDbgCmd( buf, 'down' )
441  call RunDbgCmd( buf, 'backtrace', [
442        \ '>backtrace',
443        \ '  3 function GlobalFunction[1]',
444        \ '->2 CallAFunction[1]',
445        \ '  1 SourceAnotherFile[1]',
446        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
447        \ 'line 1: func DoAThing()' ] )
448
449  call RunDbgCmd( buf, 'down' )
450  call RunDbgCmd( buf, 'backtrace', [
451        \ '>backtrace',
452        \ '  3 function GlobalFunction[1]',
453        \ '  2 CallAFunction[1]',
454        \ '->1 SourceAnotherFile[1]',
455        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
456        \ 'line 1: func DoAThing()' ] )
457
458  call RunDbgCmd( buf, 'down' )
459  call RunDbgCmd( buf, 'backtrace', [
460        \ '>backtrace',
461        \ '  3 function GlobalFunction[1]',
462        \ '  2 CallAFunction[1]',
463        \ '  1 SourceAnotherFile[1]',
464        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
465        \ 'line 1: func DoAThing()' ] )
466
467  call RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
468
469  " step until we have another meaninfgul trace
470  call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
471  call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
472  call RunDbgCmd(buf, 'backtrace', [
473        \ '>backtrace',
474        \ '  3 function GlobalFunction[1]',
475        \ '  2 CallAFunction[1]',
476        \ '  1 SourceAnotherFile[1]',
477        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
478        \ 'line 9: call File2Function()'])
479
480  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
481  call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
482  call RunDbgCmd(buf, 'backtrace', [
483        \ '>backtrace',
484        \ '  5 function GlobalFunction[1]',
485        \ '  4 CallAFunction[1]',
486        \ '  3 SourceAnotherFile[1]',
487        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
488        \ '  1 function File2Function[1]',
489        \ '->0 DoAThing',
490        \ 'line 1: echo "DoAThing"'])
491
492  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
493  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
494  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
495  call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
496  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
497  call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
498  call RunDbgCmd(buf, 'backtrace', [
499        \ '>backtrace',
500        \ '  1 function GlobalFunction[1]',
501        \ '->0 CallAFunction',
502        \ 'line 2: call File2Function()'])
503
504  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
505  call RunDbgCmd(buf, 'backtrace', [
506        \ '>backtrace',
507        \ '  2 function GlobalFunction[1]',
508        \ '  1 CallAFunction[2]',
509        \ '->0 File2Function',
510        \ 'line 1: call DoAThing()'])
511
512  call StopVimInTerminal(buf)
513  call delete('Xtest1.vim')
514  call delete('Xtest2.vim')
515endfunc
516
517func Test_Backtrace_Autocmd()
518  CheckCWD
519  let file1 =<< trim END
520    func SourceAnotherFile()
521      source Xtest2.vim
522    endfunc
523
524    func CallAFunction()
525      call SourceAnotherFile()
526      call File2Function()
527    endfunc
528
529    func GlobalFunction()
530      call CallAFunction()
531    endfunc
532
533    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
534  END
535  call writefile(file1, 'Xtest1.vim')
536
537  let file2 =<< trim END
538    func DoAThing()
539      echo "DoAThing"
540    endfunc
541
542    func File2Function()
543      call DoAThing()
544    endfunc
545
546    call File2Function()
547  END
548  call writefile(file2, 'Xtest2.vim')
549
550  let buf = RunVimInTerminal('-S Xtest1.vim', {})
551
552  call RunDbgCmd(buf,
553                \ ':debug doautocmd User TestGlobalFunction',
554                \ ['cmd: doautocmd User TestGlobalFunction'])
555  call RunDbgCmd(buf, 'step', ['cmd: call GlobalFunction() | echo "Done"'])
556
557  " At this point the ontly thing in the stack is the autocommand
558  call RunDbgCmd(buf, 'backtrace', [
559        \ '>backtrace',
560        \ '->0 User Autocommands for "TestGlobalFunction"',
561        \ 'cmd: call GlobalFunction() | echo "Done"'])
562
563  " And now we're back into the call stack
564  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
565  call RunDbgCmd(buf, 'backtrace', [
566        \ '>backtrace',
567        \ '  1 User Autocommands for "TestGlobalFunction"',
568        \ '->0 function GlobalFunction',
569        \ 'line 1: call CallAFunction()'])
570
571  call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
572  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
573
574  call RunDbgCmd(buf, 'backtrace', [
575        \ '>backtrace',
576        \ '  3 User Autocommands for "TestGlobalFunction"',
577        \ '  2 function GlobalFunction[1]',
578        \ '  1 CallAFunction[1]',
579        \ '->0 SourceAnotherFile',
580        \ 'line 1: source Xtest2.vim'])
581
582  " Step into the 'source' command. Note that we print the full trace all the
583  " way though the source command.
584  call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
585  call RunDbgCmd(buf, 'backtrace', [
586        \ '>backtrace',
587        \ '  4 User Autocommands for "TestGlobalFunction"',
588        \ '  3 function GlobalFunction[1]',
589        \ '  2 CallAFunction[1]',
590        \ '  1 SourceAnotherFile[1]',
591        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
592        \ 'line 1: func DoAThing()'])
593
594  " step until we have another meaninfgul trace
595  call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
596  call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
597  call RunDbgCmd(buf, 'backtrace', [
598        \ '>backtrace',
599        \ '  4 User Autocommands for "TestGlobalFunction"',
600        \ '  3 function GlobalFunction[1]',
601        \ '  2 CallAFunction[1]',
602        \ '  1 SourceAnotherFile[1]',
603        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
604        \ 'line 9: call File2Function()'])
605
606  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
607  call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
608  call RunDbgCmd(buf, 'backtrace', [
609        \ '>backtrace',
610        \ '  6 User Autocommands for "TestGlobalFunction"',
611        \ '  5 function GlobalFunction[1]',
612        \ '  4 CallAFunction[1]',
613        \ '  3 SourceAnotherFile[1]',
614        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
615        \ '  1 function File2Function[1]',
616        \ '->0 DoAThing',
617        \ 'line 1: echo "DoAThing"'])
618
619  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
620  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
621  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
622  call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
623  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
624  call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
625  call RunDbgCmd(buf, 'backtrace', [
626        \ '>backtrace',
627        \ '  2 User Autocommands for "TestGlobalFunction"',
628        \ '  1 function GlobalFunction[1]',
629        \ '->0 CallAFunction',
630        \ 'line 2: call File2Function()'])
631
632  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
633  call RunDbgCmd(buf, 'backtrace', [
634        \ '>backtrace',
635        \ '  3 User Autocommands for "TestGlobalFunction"',
636        \ '  2 function GlobalFunction[1]',
637        \ '  1 CallAFunction[2]',
638        \ '->0 File2Function',
639        \ 'line 1: call DoAThing()'])
640
641
642  " Now unwind so that we get back to the original autocommand (and the second
643  " cmd echo "Done")
644  call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
645  call RunDbgCmd(buf, 'backtrace', [
646        \ '>backtrace',
647        \ '  3 User Autocommands for "TestGlobalFunction"',
648        \ '  2 function GlobalFunction[1]',
649        \ '  1 CallAFunction[2]',
650        \ '->0 File2Function',
651        \ 'line 1: End of function'])
652
653  call RunDbgCmd(buf, 'finish', ['line 2: End of function'])
654  call RunDbgCmd(buf, 'backtrace', [
655        \ '>backtrace',
656        \ '  2 User Autocommands for "TestGlobalFunction"',
657        \ '  1 function GlobalFunction[1]',
658        \ '->0 CallAFunction',
659        \ 'line 2: End of function'])
660
661  call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
662  call RunDbgCmd(buf, 'backtrace', [
663        \ '>backtrace',
664        \ '  1 User Autocommands for "TestGlobalFunction"',
665        \ '->0 function GlobalFunction',
666        \ 'line 1: End of function'])
667
668  call RunDbgCmd(buf, 'step', ['cmd: echo "Done"'])
669  call RunDbgCmd(buf, 'backtrace', [
670        \ '>backtrace',
671        \ '->0 User Autocommands for "TestGlobalFunction"',
672        \ 'cmd: echo "Done"'])
673
674  call StopVimInTerminal(buf)
675  call delete('Xtest1.vim')
676  call delete('Xtest2.vim')
677endfunc
678
679func Test_Backtrace_CmdLine()
680  CheckCWD
681  let file1 =<< trim END
682    func SourceAnotherFile()
683      source Xtest2.vim
684    endfunc
685
686    func CallAFunction()
687      call SourceAnotherFile()
688      call File2Function()
689    endfunc
690
691    func GlobalFunction()
692      call CallAFunction()
693    endfunc
694
695    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
696  END
697  call writefile(file1, 'Xtest1.vim')
698
699  let file2 =<< trim END
700    func DoAThing()
701      echo "DoAThing"
702    endfunc
703
704    func File2Function()
705      call DoAThing()
706    endfunc
707
708    call File2Function()
709  END
710  call writefile(file2, 'Xtest2.vim')
711
712  let buf = RunVimInTerminal(
713        \ '-S Xtest1.vim -c "debug call GlobalFunction()"',
714        \ {'wait_for_ruler': 0})
715
716  " Need to wait for the vim-in-terminal to be ready
717  call CheckDbgOutput(buf, ['command line',
718                            \ 'cmd: call GlobalFunction()'])
719
720  " At this point the ontly thing in the stack is the cmdline
721  call RunDbgCmd(buf, 'backtrace', [
722        \ '>backtrace',
723        \ '->0 command line',
724        \ 'cmd: call GlobalFunction()'])
725
726  " And now we're back into the call stack
727  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
728  call RunDbgCmd(buf, 'backtrace', [
729        \ '>backtrace',
730        \ '  1 command line',
731        \ '->0 function GlobalFunction',
732        \ 'line 1: call CallAFunction()'])
733
734  call StopVimInTerminal(buf)
735  call delete('Xtest1.vim')
736  call delete('Xtest2.vim')
737endfunc
738
739func Test_Backtrace_DefFunction()
740  CheckCWD
741  let file1 =<< trim END
742    vim9script
743    import File2Function from './Xtest2.vim'
744
745    def SourceAnotherFile()
746      source Xtest2.vim
747    enddef
748
749    def CallAFunction()
750      SourceAnotherFile()
751      File2Function()
752    enddef
753
754    def g:GlobalFunction()
755      CallAFunction()
756    enddef
757
758    defcompile
759  END
760  call writefile(file1, 'Xtest1.vim')
761
762  let file2 =<< trim END
763    vim9script
764
765    def DoAThing(): number
766      let a = 100 * 2
767      a += 3
768      return a
769    enddef
770
771    export def File2Function()
772      DoAThing()
773    enddef
774
775    defcompile
776    File2Function()
777  END
778  call writefile(file2, 'Xtest2.vim')
779
780  let buf = RunVimInTerminal('-S Xtest1.vim', {})
781
782  call RunDbgCmd(buf,
783                \ ':debug call GlobalFunction()',
784                \ ['cmd: call GlobalFunction()'])
785
786  " FIXME: Vim9 lines are not debugged!
787  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
788
789  " But they do appear in the backtrace
790  call RunDbgCmd(buf, 'backtrace', [
791        \ '\V>backtrace',
792        \ '\V  2 function GlobalFunction[1]',
793        \ '\V  1 <SNR>\.\*_CallAFunction[1]',
794        \ '\V->0 <SNR>\.\*_SourceAnotherFile',
795        \ '\Vline 1: source Xtest2.vim'],
796        \ #{match: 'pattern'})
797
798
799  call RunDbgCmd(buf, 'step', ['line 1: vim9script'])
800  call RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number'])
801  call RunDbgCmd(buf, 'step', ['line 9: export def File2Function()'])
802  call RunDbgCmd(buf, 'step', ['line 9: def File2Function()'])
803  call RunDbgCmd(buf, 'step', ['line 13: defcompile'])
804  call RunDbgCmd(buf, 'step', ['line 14: File2Function()'])
805  call RunDbgCmd(buf, 'backtrace', [
806        \ '\V>backtrace',
807        \ '\V  3 function GlobalFunction[1]',
808        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
809        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
810        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
811        \ '\Vline 14: File2Function()'],
812        \ #{match: 'pattern'})
813
814  " Don't step into compiled functions...
815  call RunDbgCmd(buf, 'step', ['line 15: End of sourced file'])
816  call RunDbgCmd(buf, 'backtrace', [
817        \ '\V>backtrace',
818        \ '\V  3 function GlobalFunction[1]',
819        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
820        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
821        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
822        \ '\Vline 15: End of sourced file'],
823        \ #{match: 'pattern'})
824
825
826  call StopVimInTerminal(buf)
827  call delete('Xtest1.vim')
828  call delete('Xtest2.vim')
829endfunc
830
831func Test_debug_backtrace_level()
832  CheckCWD
833  let lines =<< trim END
834    let s:file1_var = 'file1'
835    let g:global_var = 'global'
836
837    func s:File1Func( arg )
838      let s:file1_var .= a:arg
839      let local_var = s:file1_var .. ' test1'
840      let g:global_var .= local_var
841      source Xtest2.vim
842    endfunc
843
844    call s:File1Func( 'arg1' )
845  END
846  call writefile(lines, 'Xtest1.vim')
847
848  let lines =<< trim END
849    let s:file2_var = 'file2'
850
851    func s:File2Func( arg )
852      let s:file2_var .= a:arg
853      let local_var = s:file2_var .. ' test2'
854      let g:global_var .= local_var
855    endfunc
856
857    call s:File2Func( 'arg2' )
858  END
859  call writefile(lines, 'Xtest2.vim')
860
861  let file1 = getcwd() .. '/Xtest1.vim'
862  let file2 = getcwd() .. '/Xtest2.vim'
863
864  " set a breakpoint and source file1.vim
865  let buf = RunVimInTerminal(
866        \ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim',
867        \ #{ wait_for_ruler: 0 } )
868
869  call CheckDbgOutput(buf, [
870        \ 'Breakpoint in "' .. file1 .. '" line 1',
871        \ 'Entering Debug mode.  Type "cont" to continue.',
872        \ 'command line..script ' .. file1,
873        \ 'line 1: let s:file1_var = ''file1'''
874        \ ])
875
876  " step throught the initial declarations
877  call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
878  call RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] )
879  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
880  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
881  call RunDbgCmd(buf, 'echo global_var', [ 'global' ] )
882
883  " step in to the first function
884  call RunDbgCmd(buf, 'step', [ 'line 11: call s:File1Func( ''arg1'' )' ] )
885  call RunDbgCmd(buf, 'step', [ 'line 1: let s:file1_var .= a:arg' ] )
886  call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
887  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
888  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
889  call RunDbgCmd(buf,
890                \'echo global_var',
891                \[ 'E121: Undefined variable: global_var' ] )
892  call RunDbgCmd(buf,
893                \'echo local_var',
894                \[ 'E121: Undefined variable: local_var' ] )
895  call RunDbgCmd(buf,
896                \'echo l:local_var',
897                \[ 'E121: Undefined variable: l:local_var' ] )
898
899  " backtrace up
900  call RunDbgCmd(buf, 'backtrace', [
901        \ '\V>backtrace',
902        \ '\V  2 command line',
903        \ '\V  1 script ' .. file1 .. '[11]',
904        \ '\V->0 function <SNR>\.\*_File1Func',
905        \ '\Vline 1: let s:file1_var .= a:arg',
906        \ ],
907        \ #{ match: 'pattern' } )
908  call RunDbgCmd(buf, 'up', [ '>up' ] )
909
910  call RunDbgCmd(buf, 'backtrace', [
911        \ '\V>backtrace',
912        \ '\V  2 command line',
913        \ '\V->1 script ' .. file1 .. '[11]',
914        \ '\V  0 function <SNR>\.\*_File1Func',
915        \ '\Vline 1: let s:file1_var .= a:arg',
916        \ ],
917        \ #{ match: 'pattern' } )
918
919  " Expression evaluation in the script frame (not the function frame)
920  " FIXME: Unexpected in this scope (a: should not be visibnle)
921  call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
922  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
923  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
924  " FIXME: Unexpected in this scope (global should be found)
925  call RunDbgCmd(buf,
926                \'echo global_var',
927                \[ 'E121: Undefined variable: global_var' ] )
928  call RunDbgCmd(buf,
929                \'echo local_var',
930                \[ 'E121: Undefined variable: local_var' ] )
931  call RunDbgCmd(buf,
932                \'echo l:local_var',
933                \[ 'E121: Undefined variable: l:local_var' ] )
934
935
936  " step while backtraced jumps to the latest frame
937  call RunDbgCmd(buf, 'step', [
938        \ 'line 2: let local_var = s:file1_var .. '' test1''' ] )
939  call RunDbgCmd(buf, 'backtrace', [
940        \ '\V>backtrace',
941        \ '\V  2 command line',
942        \ '\V  1 script ' .. file1 .. '[11]',
943        \ '\V->0 function <SNR>\.\*_File1Func',
944        \ '\Vline 2: let local_var = s:file1_var .. '' test1''',
945        \ ],
946        \ #{ match: 'pattern' } )
947
948  call RunDbgCmd(buf, 'step', [ 'line 3: let g:global_var .= local_var' ] )
949  call RunDbgCmd(buf, 'echo local_var', [ 'file1arg1 test1' ] )
950  call RunDbgCmd(buf, 'echo l:local_var', [ 'file1arg1 test1' ] )
951
952  call RunDbgCmd(buf, 'step', [ 'line 4: source Xtest2.vim' ] )
953  call RunDbgCmd(buf, 'step', [ 'line 1: let s:file2_var = ''file2''' ] )
954  call RunDbgCmd(buf, 'backtrace', [
955        \ '\V>backtrace',
956        \ '\V  3 command line',
957        \ '\V  2 script ' .. file1 .. '[11]',
958        \ '\V  1 function <SNR>\.\*_File1Func[4]',
959        \ '\V->0 script ' .. file2,
960        \ '\Vline 1: let s:file2_var = ''file2''',
961        \ ],
962        \ #{ match: 'pattern' } )
963
964  " Expression evaluation in the script frame file2 (not the function frame)
965  call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
966  call RunDbgCmd(buf,
967        \ 'echo s:file1_var',
968        \ [ 'E121: Undefined variable: s:file1_var' ] )
969  call RunDbgCmd(buf, 'echo g:global_var', [ 'globalfile1arg1 test1' ] )
970  call RunDbgCmd(buf, 'echo global_var', [ 'globalfile1arg1 test1' ] )
971  call RunDbgCmd(buf,
972                \'echo local_var',
973                \[ 'E121: Undefined variable: local_var' ] )
974  call RunDbgCmd(buf,
975                \'echo l:local_var',
976                \[ 'E121: Undefined variable: l:local_var' ] )
977  call RunDbgCmd(buf,
978        \ 'echo s:file2_var',
979        \ [ 'E121: Undefined variable: s:file2_var' ] )
980
981  call RunDbgCmd(buf, 'step', [ 'line 3: func s:File2Func( arg )' ] )
982  call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
983
984  " Up the stack to the other script context
985  call RunDbgCmd(buf, 'up')
986  call RunDbgCmd(buf, 'backtrace', [
987        \ '\V>backtrace',
988        \ '\V  3 command line',
989        \ '\V  2 script ' .. file1 .. '[11]',
990        \ '\V->1 function <SNR>\.\*_File1Func[4]',
991        \ '\V  0 script ' .. file2,
992        \ '\Vline 3: func s:File2Func( arg )',
993        \ ],
994        \ #{ match: 'pattern' } )
995  " FIXME: Unexpected. Should see the a: and l: dicts from File1Func
996  call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
997  call RunDbgCmd(buf,
998        \ 'echo l:local_var',
999        \ [ 'E121: Undefined variable: l:local_var' ] )
1000
1001  call RunDbgCmd(buf, 'up')
1002  call RunDbgCmd(buf, 'backtrace', [
1003        \ '\V>backtrace',
1004        \ '\V  3 command line',
1005        \ '\V->2 script ' .. file1 .. '[11]',
1006        \ '\V  1 function <SNR>\.\*_File1Func[4]',
1007        \ '\V  0 script ' .. file2,
1008        \ '\Vline 3: func s:File2Func( arg )',
1009        \ ],
1010        \ #{ match: 'pattern' } )
1011
1012  " FIXME: Unexpected (wrong script vars are used)
1013  call RunDbgCmd(buf,
1014        \ 'echo s:file1_var',
1015        \ [ 'E121: Undefined variable: s:file1_var' ] )
1016  call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
1017
1018  call StopVimInTerminal(buf)
1019  call delete('Xtest1.vim')
1020  call delete('Xtest2.vim')
1021endfunc
1022