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:')
342  call assert_fails('breakadd file Xtest.vim /\)/', 'E55:')
343endfunc
344
345func Test_Backtrace_Through_Source()
346  CheckCWD
347  let file1 =<< trim END
348    func SourceAnotherFile()
349      source Xtest2.vim
350    endfunc
351
352    func CallAFunction()
353      call SourceAnotherFile()
354      call File2Function()
355    endfunc
356
357    func GlobalFunction()
358      call CallAFunction()
359    endfunc
360  END
361  call writefile(file1, 'Xtest1.vim')
362
363  let file2 =<< trim END
364    func DoAThing()
365      echo "DoAThing"
366    endfunc
367
368    func File2Function()
369      call DoAThing()
370    endfunc
371
372    call File2Function()
373  END
374  call writefile(file2, 'Xtest2.vim')
375
376  let buf = RunVimInTerminal('-S Xtest1.vim', {})
377
378  call RunDbgCmd(buf,
379                \ ':debug call GlobalFunction()',
380                \ ['cmd: call GlobalFunction()'])
381  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
382
383  call RunDbgCmd(buf, 'backtrace', ['>backtrace',
384                                    \ '->0 function GlobalFunction',
385                                    \ 'line 1: call CallAFunction()'])
386
387  call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
388  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
389
390  call RunDbgCmd(buf, 'backtrace', ['>backtrace',
391                                    \ '  2 function GlobalFunction[1]',
392                                    \ '  1 CallAFunction[1]',
393                                    \ '->0 SourceAnotherFile',
394                                    \ 'line 1: source Xtest2.vim'])
395
396  " Step into the 'source' command. Note that we print the full trace all the
397  " way though the source command.
398  call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
399  call RunDbgCmd(buf, 'backtrace', [
400        \ '>backtrace',
401        \ '  3 function GlobalFunction[1]',
402        \ '  2 CallAFunction[1]',
403        \ '  1 SourceAnotherFile[1]',
404        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
405        \ 'line 1: func DoAThing()'])
406
407  call RunDbgCmd( buf, 'up' )
408  call RunDbgCmd( buf, 'backtrace', [
409        \ '>backtrace',
410        \ '  3 function GlobalFunction[1]',
411        \ '  2 CallAFunction[1]',
412        \ '->1 SourceAnotherFile[1]',
413        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
414        \ 'line 1: func DoAThing()' ] )
415
416  call RunDbgCmd( buf, 'up' )
417  call RunDbgCmd( buf, 'backtrace', [
418        \ '>backtrace',
419        \ '  3 function GlobalFunction[1]',
420        \ '->2 CallAFunction[1]',
421        \ '  1 SourceAnotherFile[1]',
422        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
423        \ 'line 1: func DoAThing()' ] )
424
425  call RunDbgCmd( buf, 'up' )
426  call RunDbgCmd( buf, 'backtrace', [
427        \ '>backtrace',
428        \ '->3 function GlobalFunction[1]',
429        \ '  2 CallAFunction[1]',
430        \ '  1 SourceAnotherFile[1]',
431        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
432        \ 'line 1: func DoAThing()' ] )
433
434  call RunDbgCmd( buf, 'up', [ 'frame at highest level: 3' ] )
435  call RunDbgCmd( buf, 'backtrace', [
436        \ '>backtrace',
437        \ '->3 function GlobalFunction[1]',
438        \ '  2 CallAFunction[1]',
439        \ '  1 SourceAnotherFile[1]',
440        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
441        \ 'line 1: func DoAThing()' ] )
442
443  call RunDbgCmd( buf, 'down' )
444  call RunDbgCmd( buf, 'backtrace', [
445        \ '>backtrace',
446        \ '  3 function GlobalFunction[1]',
447        \ '->2 CallAFunction[1]',
448        \ '  1 SourceAnotherFile[1]',
449        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
450        \ 'line 1: func DoAThing()' ] )
451
452  call RunDbgCmd( buf, 'down' )
453  call RunDbgCmd( buf, 'backtrace', [
454        \ '>backtrace',
455        \ '  3 function GlobalFunction[1]',
456        \ '  2 CallAFunction[1]',
457        \ '->1 SourceAnotherFile[1]',
458        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
459        \ 'line 1: func DoAThing()' ] )
460
461  call RunDbgCmd( buf, 'down' )
462  call RunDbgCmd( buf, 'backtrace', [
463        \ '>backtrace',
464        \ '  3 function GlobalFunction[1]',
465        \ '  2 CallAFunction[1]',
466        \ '  1 SourceAnotherFile[1]',
467        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
468        \ 'line 1: func DoAThing()' ] )
469
470  call RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
471
472  " step until we have another meaninfgul trace
473  call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
474  call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
475  call RunDbgCmd(buf, 'backtrace', [
476        \ '>backtrace',
477        \ '  3 function GlobalFunction[1]',
478        \ '  2 CallAFunction[1]',
479        \ '  1 SourceAnotherFile[1]',
480        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
481        \ 'line 9: call File2Function()'])
482
483  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
484  call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
485  call RunDbgCmd(buf, 'backtrace', [
486        \ '>backtrace',
487        \ '  5 function GlobalFunction[1]',
488        \ '  4 CallAFunction[1]',
489        \ '  3 SourceAnotherFile[1]',
490        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
491        \ '  1 function File2Function[1]',
492        \ '->0 DoAThing',
493        \ 'line 1: echo "DoAThing"'])
494
495  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
496  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
497  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
498  call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
499  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
500  call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
501  call RunDbgCmd(buf, 'backtrace', [
502        \ '>backtrace',
503        \ '  1 function GlobalFunction[1]',
504        \ '->0 CallAFunction',
505        \ 'line 2: call File2Function()'])
506
507  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
508  call RunDbgCmd(buf, 'backtrace', [
509        \ '>backtrace',
510        \ '  2 function GlobalFunction[1]',
511        \ '  1 CallAFunction[2]',
512        \ '->0 File2Function',
513        \ 'line 1: call DoAThing()'])
514
515  call StopVimInTerminal(buf)
516  call delete('Xtest1.vim')
517  call delete('Xtest2.vim')
518endfunc
519
520func Test_Backtrace_Autocmd()
521  CheckCWD
522  let file1 =<< trim END
523    func SourceAnotherFile()
524      source Xtest2.vim
525    endfunc
526
527    func CallAFunction()
528      call SourceAnotherFile()
529      call File2Function()
530    endfunc
531
532    func GlobalFunction()
533      call CallAFunction()
534    endfunc
535
536    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
537  END
538  call writefile(file1, 'Xtest1.vim')
539
540  let file2 =<< trim END
541    func DoAThing()
542      echo "DoAThing"
543    endfunc
544
545    func File2Function()
546      call DoAThing()
547    endfunc
548
549    call File2Function()
550  END
551  call writefile(file2, 'Xtest2.vim')
552
553  let buf = RunVimInTerminal('-S Xtest1.vim', {})
554
555  call RunDbgCmd(buf,
556                \ ':debug doautocmd User TestGlobalFunction',
557                \ ['cmd: doautocmd User TestGlobalFunction'])
558  call RunDbgCmd(buf, 'step', ['cmd: call GlobalFunction() | echo "Done"'])
559
560  " At this point the ontly thing in the stack is the autocommand
561  call RunDbgCmd(buf, 'backtrace', [
562        \ '>backtrace',
563        \ '->0 User Autocommands for "TestGlobalFunction"',
564        \ 'cmd: call GlobalFunction() | echo "Done"'])
565
566  " And now we're back into the call stack
567  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
568  call RunDbgCmd(buf, 'backtrace', [
569        \ '>backtrace',
570        \ '  1 User Autocommands for "TestGlobalFunction"',
571        \ '->0 function GlobalFunction',
572        \ 'line 1: call CallAFunction()'])
573
574  call RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
575  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
576
577  call RunDbgCmd(buf, 'backtrace', [
578        \ '>backtrace',
579        \ '  3 User Autocommands for "TestGlobalFunction"',
580        \ '  2 function GlobalFunction[1]',
581        \ '  1 CallAFunction[1]',
582        \ '->0 SourceAnotherFile',
583        \ 'line 1: source Xtest2.vim'])
584
585  " Step into the 'source' command. Note that we print the full trace all the
586  " way though the source command.
587  call RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
588  call RunDbgCmd(buf, 'backtrace', [
589        \ '>backtrace',
590        \ '  4 User Autocommands for "TestGlobalFunction"',
591        \ '  3 function GlobalFunction[1]',
592        \ '  2 CallAFunction[1]',
593        \ '  1 SourceAnotherFile[1]',
594        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
595        \ 'line 1: func DoAThing()'])
596
597  call RunDbgCmd( buf, 'up' )
598  call RunDbgCmd( buf, 'backtrace', [
599        \ '>backtrace',
600        \ '  4 User Autocommands for "TestGlobalFunction"',
601        \ '  3 function GlobalFunction[1]',
602        \ '  2 CallAFunction[1]',
603        \ '->1 SourceAnotherFile[1]',
604        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
605        \ 'line 1: func DoAThing()' ] )
606
607  call RunDbgCmd( buf, 'up' )
608  call RunDbgCmd( buf, 'backtrace', [
609        \ '>backtrace',
610        \ '  4 User Autocommands for "TestGlobalFunction"',
611        \ '  3 function GlobalFunction[1]',
612        \ '->2 CallAFunction[1]',
613        \ '  1 SourceAnotherFile[1]',
614        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
615        \ 'line 1: func DoAThing()' ] )
616
617  call RunDbgCmd( buf, 'up' )
618  call RunDbgCmd( buf, 'backtrace', [
619        \ '>backtrace',
620        \ '  4 User Autocommands for "TestGlobalFunction"',
621        \ '->3 function GlobalFunction[1]',
622        \ '  2 CallAFunction[1]',
623        \ '  1 SourceAnotherFile[1]',
624        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
625        \ 'line 1: func DoAThing()' ] )
626
627  call RunDbgCmd( buf, 'up' )
628  call RunDbgCmd( buf, 'backtrace', [
629        \ '>backtrace',
630        \ '->4 User Autocommands for "TestGlobalFunction"',
631        \ '  3 function GlobalFunction[1]',
632        \ '  2 CallAFunction[1]',
633        \ '  1 SourceAnotherFile[1]',
634        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
635        \ 'line 1: func DoAThing()' ] )
636
637  call RunDbgCmd( buf, 'up', [ 'frame at highest level: 4' ] )
638  call RunDbgCmd( buf, 'backtrace', [
639        \ '>backtrace',
640        \ '->4 User Autocommands for "TestGlobalFunction"',
641        \ '  3 function GlobalFunction[1]',
642        \ '  2 CallAFunction[1]',
643        \ '  1 SourceAnotherFile[1]',
644        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
645        \ 'line 1: func DoAThing()' ] )
646
647  call RunDbgCmd( buf, 'down' )
648  call RunDbgCmd( buf, 'backtrace', [
649        \ '>backtrace',
650        \ '  4 User Autocommands for "TestGlobalFunction"',
651        \ '->3 function GlobalFunction[1]',
652        \ '  2 CallAFunction[1]',
653        \ '  1 SourceAnotherFile[1]',
654        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
655        \ 'line 1: func DoAThing()' ] )
656
657
658  call RunDbgCmd( buf, 'down' )
659  call RunDbgCmd( buf, 'backtrace', [
660        \ '>backtrace',
661        \ '  4 User Autocommands for "TestGlobalFunction"',
662        \ '  3 function GlobalFunction[1]',
663        \ '->2 CallAFunction[1]',
664        \ '  1 SourceAnotherFile[1]',
665        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
666        \ 'line 1: func DoAThing()' ] )
667
668  call RunDbgCmd( buf, 'down' )
669  call RunDbgCmd( buf, 'backtrace', [
670        \ '>backtrace',
671        \ '  4 User Autocommands for "TestGlobalFunction"',
672        \ '  3 function GlobalFunction[1]',
673        \ '  2 CallAFunction[1]',
674        \ '->1 SourceAnotherFile[1]',
675        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
676        \ 'line 1: func DoAThing()' ] )
677
678  call RunDbgCmd( buf, 'down' )
679  call RunDbgCmd( buf, 'backtrace', [
680        \ '>backtrace',
681        \ '  4 User Autocommands for "TestGlobalFunction"',
682        \ '  3 function GlobalFunction[1]',
683        \ '  2 CallAFunction[1]',
684        \ '  1 SourceAnotherFile[1]',
685        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
686        \ 'line 1: func DoAThing()' ] )
687
688  call RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
689
690  " step until we have another meaninfgul trace
691  call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
692  call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
693  call RunDbgCmd(buf, 'backtrace', [
694        \ '>backtrace',
695        \ '  4 User Autocommands for "TestGlobalFunction"',
696        \ '  3 function GlobalFunction[1]',
697        \ '  2 CallAFunction[1]',
698        \ '  1 SourceAnotherFile[1]',
699        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
700        \ 'line 9: call File2Function()'])
701
702  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
703  call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
704  call RunDbgCmd(buf, 'backtrace', [
705        \ '>backtrace',
706        \ '  6 User Autocommands for "TestGlobalFunction"',
707        \ '  5 function GlobalFunction[1]',
708        \ '  4 CallAFunction[1]',
709        \ '  3 SourceAnotherFile[1]',
710        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
711        \ '  1 function File2Function[1]',
712        \ '->0 DoAThing',
713        \ 'line 1: echo "DoAThing"'])
714
715  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
716  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
717  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
718  call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
719  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
720  call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
721  call RunDbgCmd(buf, 'backtrace', [
722        \ '>backtrace',
723        \ '  2 User Autocommands for "TestGlobalFunction"',
724        \ '  1 function GlobalFunction[1]',
725        \ '->0 CallAFunction',
726        \ 'line 2: call File2Function()'])
727
728  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
729  call RunDbgCmd(buf, 'backtrace', [
730        \ '>backtrace',
731        \ '  3 User Autocommands for "TestGlobalFunction"',
732        \ '  2 function GlobalFunction[1]',
733        \ '  1 CallAFunction[2]',
734        \ '->0 File2Function',
735        \ 'line 1: call DoAThing()'])
736
737
738  " Now unwind so that we get back to the original autocommand (and the second
739  " cmd echo "Done")
740  call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
741  call RunDbgCmd(buf, 'backtrace', [
742        \ '>backtrace',
743        \ '  3 User Autocommands for "TestGlobalFunction"',
744        \ '  2 function GlobalFunction[1]',
745        \ '  1 CallAFunction[2]',
746        \ '->0 File2Function',
747        \ 'line 1: End of function'])
748
749  call RunDbgCmd(buf, 'finish', ['line 2: End of function'])
750  call RunDbgCmd(buf, 'backtrace', [
751        \ '>backtrace',
752        \ '  2 User Autocommands for "TestGlobalFunction"',
753        \ '  1 function GlobalFunction[1]',
754        \ '->0 CallAFunction',
755        \ 'line 2: End of function'])
756
757  call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
758  call RunDbgCmd(buf, 'backtrace', [
759        \ '>backtrace',
760        \ '  1 User Autocommands for "TestGlobalFunction"',
761        \ '->0 function GlobalFunction',
762        \ 'line 1: End of function'])
763
764  call RunDbgCmd(buf, 'step', ['cmd: echo "Done"'])
765  call RunDbgCmd(buf, 'backtrace', [
766        \ '>backtrace',
767        \ '->0 User Autocommands for "TestGlobalFunction"',
768        \ 'cmd: echo "Done"'])
769
770  call StopVimInTerminal(buf)
771  call delete('Xtest1.vim')
772  call delete('Xtest2.vim')
773endfunc
774
775func Test_Backtrace_CmdLine()
776  CheckCWD
777  let file1 =<< trim END
778    func SourceAnotherFile()
779      source Xtest2.vim
780    endfunc
781
782    func CallAFunction()
783      call SourceAnotherFile()
784      call File2Function()
785    endfunc
786
787    func GlobalFunction()
788      call CallAFunction()
789    endfunc
790
791    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
792  END
793  call writefile(file1, 'Xtest1.vim')
794
795  let file2 =<< trim END
796    func DoAThing()
797      echo "DoAThing"
798    endfunc
799
800    func File2Function()
801      call DoAThing()
802    endfunc
803
804    call File2Function()
805  END
806  call writefile(file2, 'Xtest2.vim')
807
808  let buf = RunVimInTerminal(
809        \ '-S Xtest1.vim -c "debug call GlobalFunction()"',
810        \ {'wait_for_ruler': 0})
811
812  " Need to wait for the vim-in-terminal to be ready
813  call CheckDbgOutput(buf, ['command line',
814                            \ 'cmd: call GlobalFunction()'])
815
816  " At this point the ontly thing in the stack is the cmdline
817  call RunDbgCmd(buf, 'backtrace', [
818        \ '>backtrace',
819        \ '->0 command line',
820        \ 'cmd: call GlobalFunction()'])
821
822  " And now we're back into the call stack
823  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
824  call RunDbgCmd(buf, 'backtrace', [
825        \ '>backtrace',
826        \ '  1 command line',
827        \ '->0 function GlobalFunction',
828        \ 'line 1: call CallAFunction()'])
829
830  call StopVimInTerminal(buf)
831  call delete('Xtest1.vim')
832  call delete('Xtest2.vim')
833endfunc
834
835func Test_Backtrace_DefFunction()
836  CheckCWD
837  let file1 =<< trim END
838    vim9script
839    import File2Function from './Xtest2.vim'
840
841    def SourceAnotherFile()
842      source Xtest2.vim
843    enddef
844
845    def CallAFunction()
846      SourceAnotherFile()
847      File2Function()
848    enddef
849
850    def g:GlobalFunction()
851      CallAFunction()
852    enddef
853
854    defcompile
855  END
856  call writefile(file1, 'Xtest1.vim')
857
858  let file2 =<< trim END
859    vim9script
860
861    def DoAThing(): number
862      var a = 100 * 2
863      a += 3
864      return a
865    enddef
866
867    export def File2Function()
868      DoAThing()
869    enddef
870
871    defcompile
872    File2Function()
873  END
874  call writefile(file2, 'Xtest2.vim')
875
876  let buf = RunVimInTerminal('-S Xtest1.vim', {})
877
878  call RunDbgCmd(buf,
879                \ ':debug call GlobalFunction()',
880                \ ['cmd: call GlobalFunction()'])
881
882  " FIXME: Vim9 lines are not debugged!
883  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
884
885  " But they do appear in the backtrace
886  call RunDbgCmd(buf, 'backtrace', [
887        \ '\V>backtrace',
888        \ '\V  2 function GlobalFunction[1]',
889        \ '\V  1 <SNR>\.\*_CallAFunction[1]',
890        \ '\V->0 <SNR>\.\*_SourceAnotherFile',
891        \ '\Vline 1: source Xtest2.vim'],
892        \ #{match: 'pattern'})
893
894
895  call RunDbgCmd(buf, 'step', ['line 1: vim9script'])
896  call RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number'])
897  call RunDbgCmd(buf, 'step', ['line 9: export def File2Function()'])
898  call RunDbgCmd(buf, 'step', ['line 9: def File2Function()'])
899  call RunDbgCmd(buf, 'step', ['line 13: defcompile'])
900  call RunDbgCmd(buf, 'step', ['line 14: File2Function()'])
901  call RunDbgCmd(buf, 'backtrace', [
902        \ '\V>backtrace',
903        \ '\V  3 function GlobalFunction[1]',
904        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
905        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
906        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
907        \ '\Vline 14: File2Function()'],
908        \ #{match: 'pattern'})
909
910  " Don't step into compiled functions...
911  call RunDbgCmd(buf, 'step', ['line 15: End of sourced file'])
912  call RunDbgCmd(buf, 'backtrace', [
913        \ '\V>backtrace',
914        \ '\V  3 function GlobalFunction[1]',
915        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
916        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
917        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
918        \ '\Vline 15: End of sourced file'],
919        \ #{match: 'pattern'})
920
921
922  call StopVimInTerminal(buf)
923  call delete('Xtest1.vim')
924  call delete('Xtest2.vim')
925endfunc
926
927func Test_debug_backtrace_level()
928  CheckCWD
929  let lines =<< trim END
930    let s:file1_var = 'file1'
931    let g:global_var = 'global'
932
933    func s:File1Func( arg )
934      let s:file1_var .= a:arg
935      let local_var = s:file1_var .. ' test1'
936      let g:global_var .= local_var
937      source Xtest2.vim
938    endfunc
939
940    call s:File1Func( 'arg1' )
941  END
942  call writefile(lines, 'Xtest1.vim')
943
944  let lines =<< trim END
945    let s:file2_var = 'file2'
946
947    func s:File2Func( arg )
948      let s:file2_var .= a:arg
949      let local_var = s:file2_var .. ' test2'
950      let g:global_var .= local_var
951    endfunc
952
953    call s:File2Func( 'arg2' )
954  END
955  call writefile(lines, 'Xtest2.vim')
956
957  let file1 = getcwd() .. '/Xtest1.vim'
958  let file2 = getcwd() .. '/Xtest2.vim'
959
960  " set a breakpoint and source file1.vim
961  let buf = RunVimInTerminal(
962        \ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim',
963        \ #{ wait_for_ruler: 0 } )
964
965  call CheckDbgOutput(buf, [
966        \ 'Breakpoint in "' .. file1 .. '" line 1',
967        \ 'Entering Debug mode.  Type "cont" to continue.',
968        \ 'command line..script ' .. file1,
969        \ 'line 1: let s:file1_var = ''file1'''
970        \ ])
971
972  " step throught the initial declarations
973  call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
974  call RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] )
975  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
976  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
977  call RunDbgCmd(buf, 'echo global_var', [ 'global' ] )
978
979  " step in to the first function
980  call RunDbgCmd(buf, 'step', [ 'line 11: call s:File1Func( ''arg1'' )' ] )
981  call RunDbgCmd(buf, 'step', [ 'line 1: let s:file1_var .= a:arg' ] )
982  call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
983  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
984  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
985  call RunDbgCmd(buf,
986                \'echo global_var',
987                \[ 'E121: Undefined variable: global_var' ] )
988  call RunDbgCmd(buf,
989                \'echo local_var',
990                \[ 'E121: Undefined variable: local_var' ] )
991  call RunDbgCmd(buf,
992                \'echo l:local_var',
993                \[ 'E121: Undefined variable: l:local_var' ] )
994
995  " backtrace up
996  call RunDbgCmd(buf, 'backtrace', [
997        \ '\V>backtrace',
998        \ '\V  2 command line',
999        \ '\V  1 script ' .. file1 .. '[11]',
1000        \ '\V->0 function <SNR>\.\*_File1Func',
1001        \ '\Vline 1: let s:file1_var .= a:arg',
1002        \ ],
1003        \ #{ match: 'pattern' } )
1004  call RunDbgCmd(buf, 'up', [ '>up' ] )
1005
1006  call RunDbgCmd(buf, 'backtrace', [
1007        \ '\V>backtrace',
1008        \ '\V  2 command line',
1009        \ '\V->1 script ' .. file1 .. '[11]',
1010        \ '\V  0 function <SNR>\.\*_File1Func',
1011        \ '\Vline 1: let s:file1_var .= a:arg',
1012        \ ],
1013        \ #{ match: 'pattern' } )
1014
1015  " Expression evaluation in the script frame (not the function frame)
1016  " FIXME: Unexpected in this scope (a: should not be visibnle)
1017  call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
1018  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
1019  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
1020  " FIXME: Unexpected in this scope (global should be found)
1021  call RunDbgCmd(buf,
1022                \'echo global_var',
1023                \[ 'E121: Undefined variable: global_var' ] )
1024  call RunDbgCmd(buf,
1025                \'echo local_var',
1026                \[ 'E121: Undefined variable: local_var' ] )
1027  call RunDbgCmd(buf,
1028                \'echo l:local_var',
1029                \[ 'E121: Undefined variable: l:local_var' ] )
1030
1031
1032  " step while backtraced jumps to the latest frame
1033  call RunDbgCmd(buf, 'step', [
1034        \ 'line 2: let local_var = s:file1_var .. '' test1''' ] )
1035  call RunDbgCmd(buf, 'backtrace', [
1036        \ '\V>backtrace',
1037        \ '\V  2 command line',
1038        \ '\V  1 script ' .. file1 .. '[11]',
1039        \ '\V->0 function <SNR>\.\*_File1Func',
1040        \ '\Vline 2: let local_var = s:file1_var .. '' test1''',
1041        \ ],
1042        \ #{ match: 'pattern' } )
1043
1044  call RunDbgCmd(buf, 'step', [ 'line 3: let g:global_var .= local_var' ] )
1045  call RunDbgCmd(buf, 'echo local_var', [ 'file1arg1 test1' ] )
1046  call RunDbgCmd(buf, 'echo l:local_var', [ 'file1arg1 test1' ] )
1047
1048  call RunDbgCmd(buf, 'step', [ 'line 4: source Xtest2.vim' ] )
1049  call RunDbgCmd(buf, 'step', [ 'line 1: let s:file2_var = ''file2''' ] )
1050  call RunDbgCmd(buf, 'backtrace', [
1051        \ '\V>backtrace',
1052        \ '\V  3 command line',
1053        \ '\V  2 script ' .. file1 .. '[11]',
1054        \ '\V  1 function <SNR>\.\*_File1Func[4]',
1055        \ '\V->0 script ' .. file2,
1056        \ '\Vline 1: let s:file2_var = ''file2''',
1057        \ ],
1058        \ #{ match: 'pattern' } )
1059
1060  " Expression evaluation in the script frame file2 (not the function frame)
1061  call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
1062  call RunDbgCmd(buf,
1063        \ 'echo s:file1_var',
1064        \ [ 'E121: Undefined variable: s:file1_var' ] )
1065  call RunDbgCmd(buf, 'echo g:global_var', [ 'globalfile1arg1 test1' ] )
1066  call RunDbgCmd(buf, 'echo global_var', [ 'globalfile1arg1 test1' ] )
1067  call RunDbgCmd(buf,
1068                \'echo local_var',
1069                \[ 'E121: Undefined variable: local_var' ] )
1070  call RunDbgCmd(buf,
1071                \'echo l:local_var',
1072                \[ 'E121: Undefined variable: l:local_var' ] )
1073  call RunDbgCmd(buf,
1074        \ 'echo s:file2_var',
1075        \ [ 'E121: Undefined variable: s:file2_var' ] )
1076
1077  call RunDbgCmd(buf, 'step', [ 'line 3: func s:File2Func( arg )' ] )
1078  call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
1079
1080  " Up the stack to the other script context
1081  call RunDbgCmd(buf, 'up')
1082  call RunDbgCmd(buf, 'backtrace', [
1083        \ '\V>backtrace',
1084        \ '\V  3 command line',
1085        \ '\V  2 script ' .. file1 .. '[11]',
1086        \ '\V->1 function <SNR>\.\*_File1Func[4]',
1087        \ '\V  0 script ' .. file2,
1088        \ '\Vline 3: func s:File2Func( arg )',
1089        \ ],
1090        \ #{ match: 'pattern' } )
1091  " FIXME: Unexpected. Should see the a: and l: dicts from File1Func
1092  call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
1093  call RunDbgCmd(buf,
1094        \ 'echo l:local_var',
1095        \ [ 'E121: Undefined variable: l:local_var' ] )
1096
1097  call RunDbgCmd(buf, 'up')
1098  call RunDbgCmd(buf, 'backtrace', [
1099        \ '\V>backtrace',
1100        \ '\V  3 command line',
1101        \ '\V->2 script ' .. file1 .. '[11]',
1102        \ '\V  1 function <SNR>\.\*_File1Func[4]',
1103        \ '\V  0 script ' .. file2,
1104        \ '\Vline 3: func s:File2Func( arg )',
1105        \ ],
1106        \ #{ match: 'pattern' } )
1107
1108  " FIXME: Unexpected (wrong script vars are used)
1109  call RunDbgCmd(buf,
1110        \ 'echo s:file1_var',
1111        \ [ 'E121: Undefined variable: s:file1_var' ] )
1112  call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
1113
1114  call StopVimInTerminal(buf)
1115  call delete('Xtest1.vim')
1116  call delete('Xtest2.vim')
1117endfunc
1118
1119" Test for setting a breakpoint on a :endif where the :if condition is false
1120" and then quit the script. This should generate an interrupt.
1121func Test_breakpt_endif_intr()
1122  func F()
1123    let g:Xpath ..= 'a'
1124    if v:false
1125      let g:Xpath ..= 'b'
1126    endif
1127    invalid_command
1128  endfunc
1129
1130  let g:Xpath = ''
1131  breakadd func 4 F
1132  try
1133    let caught_intr = 0
1134    debuggreedy
1135    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
1136  catch /^Vim:Interrupt$/
1137    call assert_match('\.F, line 4', v:throwpoint)
1138    let caught_intr = 1
1139  endtry
1140  0debuggreedy
1141  call assert_equal(1, caught_intr)
1142  call assert_equal('a', g:Xpath)
1143  breakdel *
1144  delfunc F
1145endfunc
1146
1147" Test for setting a breakpoint on a :else where the :if condition is false
1148" and then quit the script. This should generate an interrupt.
1149func Test_breakpt_else_intr()
1150  func F()
1151    let g:Xpath ..= 'a'
1152    if v:false
1153      let g:Xpath ..= 'b'
1154    else
1155      invalid_command
1156    endif
1157    invalid_command
1158  endfunc
1159
1160  let g:Xpath = ''
1161  breakadd func 4 F
1162  try
1163    let caught_intr = 0
1164    debuggreedy
1165    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
1166  catch /^Vim:Interrupt$/
1167    call assert_match('\.F, line 4', v:throwpoint)
1168    let caught_intr = 1
1169  endtry
1170  0debuggreedy
1171  call assert_equal(1, caught_intr)
1172  call assert_equal('a', g:Xpath)
1173  breakdel *
1174  delfunc F
1175endfunc
1176
1177" Test for setting a breakpoint on a :endwhile where the :while condition is
1178" false and then quit the script. This should generate an interrupt.
1179func Test_breakpt_endwhile_intr()
1180  func F()
1181    let g:Xpath ..= 'a'
1182    while v:false
1183      let g:Xpath ..= 'b'
1184    endwhile
1185    invalid_command
1186  endfunc
1187
1188  let g:Xpath = ''
1189  breakadd func 4 F
1190  try
1191    let caught_intr = 0
1192    debuggreedy
1193    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
1194  catch /^Vim:Interrupt$/
1195    call assert_match('\.F, line 4', v:throwpoint)
1196    let caught_intr = 1
1197  endtry
1198  0debuggreedy
1199  call assert_equal(1, caught_intr)
1200  call assert_equal('a', g:Xpath)
1201  breakdel *
1202  delfunc F
1203endfunc
1204
1205" Test for setting a breakpoint on a script local function
1206func Test_breakpt_scriptlocal_func()
1207  let g:Xpath = ''
1208  func s:G()
1209    let g:Xpath ..= 'a'
1210  endfunc
1211
1212  let funcname = expand("<SID>") .. "G"
1213  exe "breakadd func 1 " .. funcname
1214  debuggreedy
1215  redir => output
1216  call feedkeys(":call " .. funcname .. "()\<CR>c\<CR>", "xt")
1217  redir END
1218  0debuggreedy
1219  call assert_match('Breakpoint in "' .. funcname .. '" line 1', output)
1220  call assert_equal('a', g:Xpath)
1221  breakdel *
1222  exe "delfunc " .. funcname
1223endfunc
1224
1225" vim: shiftwidth=2 sts=2 expandtab
1226