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  call RunDbgCmd( buf, 'up' )
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 1: func DoAThing()' ] )
605
606  call RunDbgCmd( buf, 'up' )
607  call RunDbgCmd( buf, 'backtrace', [
608        \ '>backtrace',
609        \ '  4 User Autocommands for "TestGlobalFunction"',
610        \ '  3 function GlobalFunction[1]',
611        \ '->2 CallAFunction[1]',
612        \ '  1 SourceAnotherFile[1]',
613        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
614        \ 'line 1: func DoAThing()' ] )
615
616  call RunDbgCmd( buf, 'up' )
617  call RunDbgCmd( buf, 'backtrace', [
618        \ '>backtrace',
619        \ '  4 User Autocommands for "TestGlobalFunction"',
620        \ '->3 function GlobalFunction[1]',
621        \ '  2 CallAFunction[1]',
622        \ '  1 SourceAnotherFile[1]',
623        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
624        \ 'line 1: func DoAThing()' ] )
625
626  call RunDbgCmd( buf, 'up' )
627  call RunDbgCmd( buf, 'backtrace', [
628        \ '>backtrace',
629        \ '->4 User Autocommands for "TestGlobalFunction"',
630        \ '  3 function GlobalFunction[1]',
631        \ '  2 CallAFunction[1]',
632        \ '  1 SourceAnotherFile[1]',
633        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
634        \ 'line 1: func DoAThing()' ] )
635
636  call RunDbgCmd( buf, 'up', [ 'frame at highest level: 4' ] )
637  call RunDbgCmd( buf, 'backtrace', [
638        \ '>backtrace',
639        \ '->4 User Autocommands for "TestGlobalFunction"',
640        \ '  3 function GlobalFunction[1]',
641        \ '  2 CallAFunction[1]',
642        \ '  1 SourceAnotherFile[1]',
643        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
644        \ 'line 1: func DoAThing()' ] )
645
646  call RunDbgCmd( buf, 'down' )
647  call RunDbgCmd( buf, 'backtrace', [
648        \ '>backtrace',
649        \ '  4 User Autocommands for "TestGlobalFunction"',
650        \ '->3 function GlobalFunction[1]',
651        \ '  2 CallAFunction[1]',
652        \ '  1 SourceAnotherFile[1]',
653        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
654        \ 'line 1: func DoAThing()' ] )
655
656
657  call RunDbgCmd( buf, 'down' )
658  call RunDbgCmd( buf, 'backtrace', [
659        \ '>backtrace',
660        \ '  4 User Autocommands for "TestGlobalFunction"',
661        \ '  3 function GlobalFunction[1]',
662        \ '->2 CallAFunction[1]',
663        \ '  1 SourceAnotherFile[1]',
664        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
665        \ 'line 1: func DoAThing()' ] )
666
667  call RunDbgCmd( buf, 'down' )
668  call RunDbgCmd( buf, 'backtrace', [
669        \ '>backtrace',
670        \ '  4 User Autocommands for "TestGlobalFunction"',
671        \ '  3 function GlobalFunction[1]',
672        \ '  2 CallAFunction[1]',
673        \ '->1 SourceAnotherFile[1]',
674        \ '  0 script ' .. getcwd() .. '/Xtest2.vim',
675        \ 'line 1: func DoAThing()' ] )
676
677  call RunDbgCmd( buf, 'down' )
678  call RunDbgCmd( buf, 'backtrace', [
679        \ '>backtrace',
680        \ '  4 User Autocommands for "TestGlobalFunction"',
681        \ '  3 function GlobalFunction[1]',
682        \ '  2 CallAFunction[1]',
683        \ '  1 SourceAnotherFile[1]',
684        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
685        \ 'line 1: func DoAThing()' ] )
686
687  call RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
688
689  " step until we have another meaninfgul trace
690  call RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
691  call RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
692  call RunDbgCmd(buf, 'backtrace', [
693        \ '>backtrace',
694        \ '  4 User Autocommands for "TestGlobalFunction"',
695        \ '  3 function GlobalFunction[1]',
696        \ '  2 CallAFunction[1]',
697        \ '  1 SourceAnotherFile[1]',
698        \ '->0 script ' .. getcwd() .. '/Xtest2.vim',
699        \ 'line 9: call File2Function()'])
700
701  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
702  call RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
703  call RunDbgCmd(buf, 'backtrace', [
704        \ '>backtrace',
705        \ '  6 User Autocommands for "TestGlobalFunction"',
706        \ '  5 function GlobalFunction[1]',
707        \ '  4 CallAFunction[1]',
708        \ '  3 SourceAnotherFile[1]',
709        \ '  2 script ' .. getcwd() .. '/Xtest2.vim[9]',
710        \ '  1 function File2Function[1]',
711        \ '->0 DoAThing',
712        \ 'line 1: echo "DoAThing"'])
713
714  " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
715  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
716  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
717  call RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
718  call RunDbgCmd(buf, 'step', ['line 1: End of function'])
719  call RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
720  call RunDbgCmd(buf, 'backtrace', [
721        \ '>backtrace',
722        \ '  2 User Autocommands for "TestGlobalFunction"',
723        \ '  1 function GlobalFunction[1]',
724        \ '->0 CallAFunction',
725        \ 'line 2: call File2Function()'])
726
727  call RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
728  call RunDbgCmd(buf, 'backtrace', [
729        \ '>backtrace',
730        \ '  3 User Autocommands for "TestGlobalFunction"',
731        \ '  2 function GlobalFunction[1]',
732        \ '  1 CallAFunction[2]',
733        \ '->0 File2Function',
734        \ 'line 1: call DoAThing()'])
735
736
737  " Now unwind so that we get back to the original autocommand (and the second
738  " cmd echo "Done")
739  call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
740  call RunDbgCmd(buf, 'backtrace', [
741        \ '>backtrace',
742        \ '  3 User Autocommands for "TestGlobalFunction"',
743        \ '  2 function GlobalFunction[1]',
744        \ '  1 CallAFunction[2]',
745        \ '->0 File2Function',
746        \ 'line 1: End of function'])
747
748  call RunDbgCmd(buf, 'finish', ['line 2: End of function'])
749  call RunDbgCmd(buf, 'backtrace', [
750        \ '>backtrace',
751        \ '  2 User Autocommands for "TestGlobalFunction"',
752        \ '  1 function GlobalFunction[1]',
753        \ '->0 CallAFunction',
754        \ 'line 2: End of function'])
755
756  call RunDbgCmd(buf, 'finish', ['line 1: End of function'])
757  call RunDbgCmd(buf, 'backtrace', [
758        \ '>backtrace',
759        \ '  1 User Autocommands for "TestGlobalFunction"',
760        \ '->0 function GlobalFunction',
761        \ 'line 1: End of function'])
762
763  call RunDbgCmd(buf, 'step', ['cmd: echo "Done"'])
764  call RunDbgCmd(buf, 'backtrace', [
765        \ '>backtrace',
766        \ '->0 User Autocommands for "TestGlobalFunction"',
767        \ 'cmd: echo "Done"'])
768
769  call StopVimInTerminal(buf)
770  call delete('Xtest1.vim')
771  call delete('Xtest2.vim')
772endfunc
773
774func Test_Backtrace_CmdLine()
775  CheckCWD
776  let file1 =<< trim END
777    func SourceAnotherFile()
778      source Xtest2.vim
779    endfunc
780
781    func CallAFunction()
782      call SourceAnotherFile()
783      call File2Function()
784    endfunc
785
786    func GlobalFunction()
787      call CallAFunction()
788    endfunc
789
790    au User TestGlobalFunction :call GlobalFunction() | echo "Done"
791  END
792  call writefile(file1, 'Xtest1.vim')
793
794  let file2 =<< trim END
795    func DoAThing()
796      echo "DoAThing"
797    endfunc
798
799    func File2Function()
800      call DoAThing()
801    endfunc
802
803    call File2Function()
804  END
805  call writefile(file2, 'Xtest2.vim')
806
807  let buf = RunVimInTerminal(
808        \ '-S Xtest1.vim -c "debug call GlobalFunction()"',
809        \ {'wait_for_ruler': 0})
810
811  " Need to wait for the vim-in-terminal to be ready
812  call CheckDbgOutput(buf, ['command line',
813                            \ 'cmd: call GlobalFunction()'])
814
815  " At this point the ontly thing in the stack is the cmdline
816  call RunDbgCmd(buf, 'backtrace', [
817        \ '>backtrace',
818        \ '->0 command line',
819        \ 'cmd: call GlobalFunction()'])
820
821  " And now we're back into the call stack
822  call RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
823  call RunDbgCmd(buf, 'backtrace', [
824        \ '>backtrace',
825        \ '  1 command line',
826        \ '->0 function GlobalFunction',
827        \ 'line 1: call CallAFunction()'])
828
829  call StopVimInTerminal(buf)
830  call delete('Xtest1.vim')
831  call delete('Xtest2.vim')
832endfunc
833
834func Test_Backtrace_DefFunction()
835  CheckCWD
836  let file1 =<< trim END
837    vim9script
838    import File2Function from './Xtest2.vim'
839
840    def SourceAnotherFile()
841      source Xtest2.vim
842    enddef
843
844    def CallAFunction()
845      SourceAnotherFile()
846      File2Function()
847    enddef
848
849    def g:GlobalFunction()
850      CallAFunction()
851    enddef
852
853    defcompile
854  END
855  call writefile(file1, 'Xtest1.vim')
856
857  let file2 =<< trim END
858    vim9script
859
860    def DoAThing(): number
861      let a = 100 * 2
862      a += 3
863      return a
864    enddef
865
866    export def File2Function()
867      DoAThing()
868    enddef
869
870    defcompile
871    File2Function()
872  END
873  call writefile(file2, 'Xtest2.vim')
874
875  let buf = RunVimInTerminal('-S Xtest1.vim', {})
876
877  call RunDbgCmd(buf,
878                \ ':debug call GlobalFunction()',
879                \ ['cmd: call GlobalFunction()'])
880
881  " FIXME: Vim9 lines are not debugged!
882  call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
883
884  " But they do appear in the backtrace
885  call RunDbgCmd(buf, 'backtrace', [
886        \ '\V>backtrace',
887        \ '\V  2 function GlobalFunction[1]',
888        \ '\V  1 <SNR>\.\*_CallAFunction[1]',
889        \ '\V->0 <SNR>\.\*_SourceAnotherFile',
890        \ '\Vline 1: source Xtest2.vim'],
891        \ #{match: 'pattern'})
892
893
894  call RunDbgCmd(buf, 'step', ['line 1: vim9script'])
895  call RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number'])
896  call RunDbgCmd(buf, 'step', ['line 9: export def File2Function()'])
897  call RunDbgCmd(buf, 'step', ['line 9: def File2Function()'])
898  call RunDbgCmd(buf, 'step', ['line 13: defcompile'])
899  call RunDbgCmd(buf, 'step', ['line 14: File2Function()'])
900  call RunDbgCmd(buf, 'backtrace', [
901        \ '\V>backtrace',
902        \ '\V  3 function GlobalFunction[1]',
903        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
904        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
905        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
906        \ '\Vline 14: File2Function()'],
907        \ #{match: 'pattern'})
908
909  " Don't step into compiled functions...
910  call RunDbgCmd(buf, 'step', ['line 15: End of sourced file'])
911  call RunDbgCmd(buf, 'backtrace', [
912        \ '\V>backtrace',
913        \ '\V  3 function GlobalFunction[1]',
914        \ '\V  2 <SNR>\.\*_CallAFunction[1]',
915        \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
916        \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
917        \ '\Vline 15: End of sourced file'],
918        \ #{match: 'pattern'})
919
920
921  call StopVimInTerminal(buf)
922  call delete('Xtest1.vim')
923  call delete('Xtest2.vim')
924endfunc
925
926func Test_debug_backtrace_level()
927  CheckCWD
928  let lines =<< trim END
929    let s:file1_var = 'file1'
930    let g:global_var = 'global'
931
932    func s:File1Func( arg )
933      let s:file1_var .= a:arg
934      let local_var = s:file1_var .. ' test1'
935      let g:global_var .= local_var
936      source Xtest2.vim
937    endfunc
938
939    call s:File1Func( 'arg1' )
940  END
941  call writefile(lines, 'Xtest1.vim')
942
943  let lines =<< trim END
944    let s:file2_var = 'file2'
945
946    func s:File2Func( arg )
947      let s:file2_var .= a:arg
948      let local_var = s:file2_var .. ' test2'
949      let g:global_var .= local_var
950    endfunc
951
952    call s:File2Func( 'arg2' )
953  END
954  call writefile(lines, 'Xtest2.vim')
955
956  let file1 = getcwd() .. '/Xtest1.vim'
957  let file2 = getcwd() .. '/Xtest2.vim'
958
959  " set a breakpoint and source file1.vim
960  let buf = RunVimInTerminal(
961        \ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim',
962        \ #{ wait_for_ruler: 0 } )
963
964  call CheckDbgOutput(buf, [
965        \ 'Breakpoint in "' .. file1 .. '" line 1',
966        \ 'Entering Debug mode.  Type "cont" to continue.',
967        \ 'command line..script ' .. file1,
968        \ 'line 1: let s:file1_var = ''file1'''
969        \ ])
970
971  " step throught the initial declarations
972  call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
973  call RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] )
974  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
975  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
976  call RunDbgCmd(buf, 'echo global_var', [ 'global' ] )
977
978  " step in to the first function
979  call RunDbgCmd(buf, 'step', [ 'line 11: call s:File1Func( ''arg1'' )' ] )
980  call RunDbgCmd(buf, 'step', [ 'line 1: let s:file1_var .= a:arg' ] )
981  call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
982  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
983  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
984  call RunDbgCmd(buf,
985                \'echo global_var',
986                \[ 'E121: Undefined variable: global_var' ] )
987  call RunDbgCmd(buf,
988                \'echo local_var',
989                \[ 'E121: Undefined variable: local_var' ] )
990  call RunDbgCmd(buf,
991                \'echo l:local_var',
992                \[ 'E121: Undefined variable: l:local_var' ] )
993
994  " backtrace up
995  call RunDbgCmd(buf, 'backtrace', [
996        \ '\V>backtrace',
997        \ '\V  2 command line',
998        \ '\V  1 script ' .. file1 .. '[11]',
999        \ '\V->0 function <SNR>\.\*_File1Func',
1000        \ '\Vline 1: let s:file1_var .= a:arg',
1001        \ ],
1002        \ #{ match: 'pattern' } )
1003  call RunDbgCmd(buf, 'up', [ '>up' ] )
1004
1005  call RunDbgCmd(buf, 'backtrace', [
1006        \ '\V>backtrace',
1007        \ '\V  2 command line',
1008        \ '\V->1 script ' .. file1 .. '[11]',
1009        \ '\V  0 function <SNR>\.\*_File1Func',
1010        \ '\Vline 1: let s:file1_var .= a:arg',
1011        \ ],
1012        \ #{ match: 'pattern' } )
1013
1014  " Expression evaluation in the script frame (not the function frame)
1015  " FIXME: Unexpected in this scope (a: should not be visibnle)
1016  call RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
1017  call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
1018  call RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
1019  " FIXME: Unexpected in this scope (global should be found)
1020  call RunDbgCmd(buf,
1021                \'echo global_var',
1022                \[ 'E121: Undefined variable: global_var' ] )
1023  call RunDbgCmd(buf,
1024                \'echo local_var',
1025                \[ 'E121: Undefined variable: local_var' ] )
1026  call RunDbgCmd(buf,
1027                \'echo l:local_var',
1028                \[ 'E121: Undefined variable: l:local_var' ] )
1029
1030
1031  " step while backtraced jumps to the latest frame
1032  call RunDbgCmd(buf, 'step', [
1033        \ 'line 2: let local_var = s:file1_var .. '' test1''' ] )
1034  call RunDbgCmd(buf, 'backtrace', [
1035        \ '\V>backtrace',
1036        \ '\V  2 command line',
1037        \ '\V  1 script ' .. file1 .. '[11]',
1038        \ '\V->0 function <SNR>\.\*_File1Func',
1039        \ '\Vline 2: let local_var = s:file1_var .. '' test1''',
1040        \ ],
1041        \ #{ match: 'pattern' } )
1042
1043  call RunDbgCmd(buf, 'step', [ 'line 3: let g:global_var .= local_var' ] )
1044  call RunDbgCmd(buf, 'echo local_var', [ 'file1arg1 test1' ] )
1045  call RunDbgCmd(buf, 'echo l:local_var', [ 'file1arg1 test1' ] )
1046
1047  call RunDbgCmd(buf, 'step', [ 'line 4: source Xtest2.vim' ] )
1048  call RunDbgCmd(buf, 'step', [ 'line 1: let s:file2_var = ''file2''' ] )
1049  call RunDbgCmd(buf, 'backtrace', [
1050        \ '\V>backtrace',
1051        \ '\V  3 command line',
1052        \ '\V  2 script ' .. file1 .. '[11]',
1053        \ '\V  1 function <SNR>\.\*_File1Func[4]',
1054        \ '\V->0 script ' .. file2,
1055        \ '\Vline 1: let s:file2_var = ''file2''',
1056        \ ],
1057        \ #{ match: 'pattern' } )
1058
1059  " Expression evaluation in the script frame file2 (not the function frame)
1060  call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
1061  call RunDbgCmd(buf,
1062        \ 'echo s:file1_var',
1063        \ [ 'E121: Undefined variable: s:file1_var' ] )
1064  call RunDbgCmd(buf, 'echo g:global_var', [ 'globalfile1arg1 test1' ] )
1065  call RunDbgCmd(buf, 'echo global_var', [ 'globalfile1arg1 test1' ] )
1066  call RunDbgCmd(buf,
1067                \'echo local_var',
1068                \[ 'E121: Undefined variable: local_var' ] )
1069  call RunDbgCmd(buf,
1070                \'echo l:local_var',
1071                \[ 'E121: Undefined variable: l:local_var' ] )
1072  call RunDbgCmd(buf,
1073        \ 'echo s:file2_var',
1074        \ [ 'E121: Undefined variable: s:file2_var' ] )
1075
1076  call RunDbgCmd(buf, 'step', [ 'line 3: func s:File2Func( arg )' ] )
1077  call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
1078
1079  " Up the stack to the other script context
1080  call RunDbgCmd(buf, 'up')
1081  call RunDbgCmd(buf, 'backtrace', [
1082        \ '\V>backtrace',
1083        \ '\V  3 command line',
1084        \ '\V  2 script ' .. file1 .. '[11]',
1085        \ '\V->1 function <SNR>\.\*_File1Func[4]',
1086        \ '\V  0 script ' .. file2,
1087        \ '\Vline 3: func s:File2Func( arg )',
1088        \ ],
1089        \ #{ match: 'pattern' } )
1090  " FIXME: Unexpected. Should see the a: and l: dicts from File1Func
1091  call RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
1092  call RunDbgCmd(buf,
1093        \ 'echo l:local_var',
1094        \ [ 'E121: Undefined variable: l:local_var' ] )
1095
1096  call RunDbgCmd(buf, 'up')
1097  call RunDbgCmd(buf, 'backtrace', [
1098        \ '\V>backtrace',
1099        \ '\V  3 command line',
1100        \ '\V->2 script ' .. file1 .. '[11]',
1101        \ '\V  1 function <SNR>\.\*_File1Func[4]',
1102        \ '\V  0 script ' .. file2,
1103        \ '\Vline 3: func s:File2Func( arg )',
1104        \ ],
1105        \ #{ match: 'pattern' } )
1106
1107  " FIXME: Unexpected (wrong script vars are used)
1108  call RunDbgCmd(buf,
1109        \ 'echo s:file1_var',
1110        \ [ 'E121: Undefined variable: s:file1_var' ] )
1111  call RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
1112
1113  call StopVimInTerminal(buf)
1114  call delete('Xtest1.vim')
1115  call delete('Xtest2.vim')
1116endfunc
1117
1118" Test for setting a breakpoint on a :endif where the :if condition is false
1119" and then quit the script. This should generate an interrupt.
1120func Test_breakpt_endif_intr()
1121  func F()
1122    let g:Xpath ..= 'a'
1123    if v:false
1124      let g:Xpath ..= 'b'
1125    endif
1126    invalid_command
1127  endfunc
1128
1129  let g:Xpath = ''
1130  breakadd func 4 F
1131  try
1132    let caught_intr = 0
1133    debuggreedy
1134    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
1135  catch /^Vim:Interrupt$/
1136    call assert_match('\.F, line 4', v:throwpoint)
1137    let caught_intr = 1
1138  endtry
1139  0debuggreedy
1140  call assert_equal(1, caught_intr)
1141  call assert_equal('a', g:Xpath)
1142  breakdel *
1143  delfunc F
1144endfunc
1145
1146" Test for setting a breakpoint on a :else where the :if condition is false
1147" and then quit the script. This should generate an interrupt.
1148func Test_breakpt_else_intr()
1149  func F()
1150    let g:Xpath ..= 'a'
1151    if v:false
1152      let g:Xpath ..= 'b'
1153    else
1154      invalid_command
1155    endif
1156    invalid_command
1157  endfunc
1158
1159  let g:Xpath = ''
1160  breakadd func 4 F
1161  try
1162    let caught_intr = 0
1163    debuggreedy
1164    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
1165  catch /^Vim:Interrupt$/
1166    call assert_match('\.F, line 4', v:throwpoint)
1167    let caught_intr = 1
1168  endtry
1169  0debuggreedy
1170  call assert_equal(1, caught_intr)
1171  call assert_equal('a', g:Xpath)
1172  breakdel *
1173  delfunc F
1174endfunc
1175
1176" Test for setting a breakpoint on a :endwhile where the :while condition is
1177" false and then quit the script. This should generate an interrupt.
1178func Test_breakpt_endwhile_intr()
1179  func F()
1180    let g:Xpath ..= 'a'
1181    while v:false
1182      let g:Xpath ..= 'b'
1183    endwhile
1184    invalid_command
1185  endfunc
1186
1187  let g:Xpath = ''
1188  breakadd func 4 F
1189  try
1190    let caught_intr = 0
1191    debuggreedy
1192    call feedkeys(":call F()\<CR>quit\<CR>", "xt")
1193  catch /^Vim:Interrupt$/
1194    call assert_match('\.F, line 4', v:throwpoint)
1195    let caught_intr = 1
1196  endtry
1197  0debuggreedy
1198  call assert_equal(1, caught_intr)
1199  call assert_equal('a', g:Xpath)
1200  breakdel *
1201  delfunc F
1202endfunc
1203
1204" Test for setting a breakpoint on a script local function
1205func Test_breakpt_scriptlocal_func()
1206  let g:Xpath = ''
1207  func s:G()
1208    let g:Xpath ..= 'a'
1209  endfunc
1210
1211  let funcname = expand("<SID>") .. "G"
1212  exe "breakadd func 1 " .. funcname
1213  debuggreedy
1214  redir => output
1215  call feedkeys(":call " .. funcname .. "()\<CR>c\<CR>", "xt")
1216  redir END
1217  0debuggreedy
1218  call assert_match('Breakpoint in "' .. funcname .. '" line 1', output)
1219  call assert_equal('a', g:Xpath)
1220  breakdel *
1221  exe "delfunc " .. funcname
1222endfunc
1223
1224" vim: shiftwidth=2 sts=2 expandtab
1225