1" Tests for the Vim script debug commands
2
3source shared.vim
4source screendump.vim
5
6" Run a Vim debugger command
7" If the expected output argument is supplied, then check for it.
8func RunDbgCmd(buf, cmd, ...)
9  call term_sendkeys(a:buf, a:cmd . "\r")
10  call term_wait(a:buf)
11
12  if a:0 != 0
13    " Verify the expected output
14    let lnum = 20 - len(a:1)
15    for l in a:1
16      call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))})
17      let lnum += 1
18    endfor
19  endif
20endfunc
21
22" Debugger tests
23func Test_Debugger()
24  if !CanRunVimInTerminal()
25    throw 'Skipped: cannot run Vim in a terminal window'
26  endif
27
28  " Create a Vim script with some functions
29  call writefile([
30	      \ 'func Foo()',
31	      \ '  let var1 = 1',
32	      \ '  let var2 = Bar(var1) + 9',
33	      \ '  return var2',
34	      \ 'endfunc',
35	      \ 'func Bar(var)',
36	      \ '  let var1 = 2 + a:var',
37	      \ '  let var2 = Bazz(var1) + 4',
38	      \ '  return var2',
39	      \ 'endfunc',
40	      \ 'func Bazz(var)',
41	      \ '  try',
42	      \ '    let var1 = 3 + a:var',
43	      \ '    let var3 = "another var"',
44	      \ '    let var3 = "value2"',
45	      \ '  catch',
46	      \ '    let var4 = "exception"',
47	      \ '  endtry',
48	      \ '  return var1',
49	      \ 'endfunc'], 'Xtest.vim')
50
51  " Start Vim in a terminal
52  let buf = RunVimInTerminal('-S Xtest.vim', {})
53
54  " Start the Vim debugger
55  call RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()'])
56
57  " Create a few stack frames by stepping through functions
58  call RunDbgCmd(buf, 'step', ['line 1: let var1 = 1'])
59  call RunDbgCmd(buf, 'step', ['line 2: let var2 = Bar(var1) + 9'])
60  call RunDbgCmd(buf, 'step', ['line 1: let var1 = 2 + a:var'])
61  call RunDbgCmd(buf, 'step', ['line 2: let var2 = Bazz(var1) + 4'])
62  call RunDbgCmd(buf, 'step', ['line 1: try'])
63  call RunDbgCmd(buf, 'step', ['line 2: let var1 = 3 + a:var'])
64  call RunDbgCmd(buf, 'step', ['line 3: let var3 = "another var"'])
65
66  " check backtrace
67  call RunDbgCmd(buf, 'backtrace', [
68	      \ '  2 function Foo[2]',
69	      \ '  1 Bar[2]',
70	      \ '->0 Bazz',
71	      \ 'line 3: let var3 = "another var"'])
72
73  " Check variables in different stack frames
74  call RunDbgCmd(buf, 'echo var1', ['6'])
75
76  call RunDbgCmd(buf, 'up')
77  call RunDbgCmd(buf, 'back', [
78	      \ '  2 function Foo[2]',
79	      \ '->1 Bar[2]',
80	      \ '  0 Bazz',
81	      \ 'line 3: let var3 = "another var"'])
82  call RunDbgCmd(buf, 'echo var1', ['3'])
83
84  call RunDbgCmd(buf, 'u')
85  call RunDbgCmd(buf, 'bt', [
86	      \ '->2 function Foo[2]',
87	      \ '  1 Bar[2]',
88	      \ '  0 Bazz',
89	      \ 'line 3: let var3 = "another var"'])
90  call RunDbgCmd(buf, 'echo var1', ['1'])
91
92  " Undefined variables
93  call RunDbgCmd(buf, 'step')
94  call RunDbgCmd(buf, 'frame 2')
95  call RunDbgCmd(buf, 'echo var3', [
96	\ 'Error detected while processing function Foo[2]..Bar[2]..Bazz:',
97	\ 'line    4:',
98	\ 'E121: Undefined variable: var3'])
99
100  " var3 is defined in this level with some other value
101  call RunDbgCmd(buf, 'fr 0')
102  call RunDbgCmd(buf, 'echo var3', ['another var'])
103
104  call RunDbgCmd(buf, 'step')
105  call RunDbgCmd(buf, '')
106  call RunDbgCmd(buf, '')
107  call RunDbgCmd(buf, '')
108  call RunDbgCmd(buf, '')
109  call RunDbgCmd(buf, 'step', [
110	      \ 'function Foo[2]..Bar',
111	      \ 'line 3: End of function'])
112  call RunDbgCmd(buf, 'up')
113
114  " Undefined var2
115  call RunDbgCmd(buf, 'echo var2', [
116	      \ 'Error detected while processing function Foo[2]..Bar:',
117	      \ 'line    3:',
118	      \ 'E121: Undefined variable: var2'])
119
120  " Var2 is defined with 10
121  call RunDbgCmd(buf, 'down')
122  call RunDbgCmd(buf, 'echo var2', ['10'])
123
124  " Backtrace movements
125  call RunDbgCmd(buf, 'b', [
126	      \ '  1 function Foo[2]',
127	      \ '->0 Bar',
128	      \ 'line 3: End of function'])
129
130  " next command cannot go down, we are on bottom
131  call RunDbgCmd(buf, 'down', ['frame is zero'])
132  call RunDbgCmd(buf, 'up')
133
134  " next command cannot go up, we are on top
135  call RunDbgCmd(buf, 'up', ['frame at highest level: 1'])
136  call RunDbgCmd(buf, 'where', [
137	      \ '->1 function Foo[2]',
138	      \ '  0 Bar',
139	      \ 'line 3: End of function'])
140
141  " fil is not frame or finish, it is file
142  call RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--'])
143
144  " relative backtrace movement
145  call RunDbgCmd(buf, 'fr -1')
146  call RunDbgCmd(buf, 'frame', [
147	      \ '  1 function Foo[2]',
148	      \ '->0 Bar',
149	      \ 'line 3: End of function'])
150
151  call RunDbgCmd(buf, 'fr +1')
152  call RunDbgCmd(buf, 'fram', [
153	      \ '->1 function Foo[2]',
154	      \ '  0 Bar',
155	      \ 'line 3: End of function'])
156
157  " go beyond limits does not crash
158  call RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1'])
159  call RunDbgCmd(buf, 'fra', [
160	      \ '->1 function Foo[2]',
161	      \ '  0 Bar',
162	      \ 'line 3: End of function'])
163
164  call RunDbgCmd(buf, 'frame -40', ['frame is zero'])
165  call RunDbgCmd(buf, 'fram', [
166	      \ '  1 function Foo[2]',
167	      \ '->0 Bar',
168	      \ 'line 3: End of function'])
169
170  " final result 19
171  call RunDbgCmd(buf, 'cont', ['19'])
172
173  " breakpoints tests
174
175  " Start a debug session, so that reading the last line from the terminal
176  " works properly.
177  call RunDbgCmd(buf, ':debug echo Foo()')
178
179  " No breakpoints
180  call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
181
182  " Place some breakpoints
183  call RunDbgCmd(buf, 'breaka func Bar')
184  call RunDbgCmd(buf, 'breaklis', ['  1  func Bar  line 1'])
185  call RunDbgCmd(buf, 'breakadd func 3 Bazz')
186  call RunDbgCmd(buf, 'breaklist', ['  1  func Bar  line 1',
187	      \ '  2  func Bazz  line 3'])
188
189  " Check whether the breakpoints are hit
190  call RunDbgCmd(buf, 'cont', [
191	      \ 'Breakpoint in "Bar" line 1',
192	      \ 'function Foo[2]..Bar',
193	      \ 'line 1: let var1 = 2 + a:var'])
194  call RunDbgCmd(buf, 'cont', [
195	      \ 'Breakpoint in "Bazz" line 3',
196	      \ 'function Foo[2]..Bar[2]..Bazz',
197	      \ 'line 3: let var3 = "another var"'])
198
199  " Delete the breakpoints
200  call RunDbgCmd(buf, 'breakd 1')
201  call RunDbgCmd(buf, 'breakli', ['  2  func Bazz  line 3'])
202  call RunDbgCmd(buf, 'breakdel func 3 Bazz')
203  call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
204
205  call RunDbgCmd(buf, 'cont')
206
207  " Make sure the breakpoints are removed
208  call RunDbgCmd(buf, ':echo Foo()', ['19'])
209
210  " Delete a non-existing breakpoint
211  call RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2'])
212
213  " Expression breakpoint
214  call RunDbgCmd(buf, ':breakadd func 2 Bazz')
215  call RunDbgCmd(buf, ':echo Bazz(1)', [
216	      \ 'Entering Debug mode.  Type "cont" to continue.',
217	      \ 'function Bazz',
218	      \ 'line 2: let var1 = 3 + a:var'])
219  call RunDbgCmd(buf, 'step')
220  call RunDbgCmd(buf, 'step')
221  call RunDbgCmd(buf, 'breaka expr var3')
222  call RunDbgCmd(buf, 'breakl', ['  3  func Bazz  line 2',
223	      \ '  4  expr var3'])
224  call RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 5',
225	      \ 'Oldval = "''another var''"',
226	      \ 'Newval = "''value2''"',
227	      \ 'function Bazz',
228	      \ 'line 5: catch'])
229
230  call RunDbgCmd(buf, 'breakdel *')
231  call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
232
233  " Check for error cases
234  call RunDbgCmd(buf, 'breakadd abcd', [
235	      \ 'Error detected while processing function Bazz:',
236	      \ 'line    5:',
237	      \ 'E475: Invalid argument: abcd'])
238  call RunDbgCmd(buf, 'breakadd func', ['E475: Invalid argument: func'])
239  call RunDbgCmd(buf, 'breakadd func 2', ['E475: Invalid argument: func 2'])
240  call RunDbgCmd(buf, 'breaka func a()', ['E475: Invalid argument: func a()'])
241  call RunDbgCmd(buf, 'breakd abcd', ['E475: Invalid argument: abcd'])
242  call RunDbgCmd(buf, 'breakd func', ['E475: Invalid argument: func'])
243  call RunDbgCmd(buf, 'breakd func a()', ['E475: Invalid argument: func a()'])
244  call RunDbgCmd(buf, 'breakd func a', ['E161: Breakpoint not found: func a'])
245  call RunDbgCmd(buf, 'breakd expr', ['E475: Invalid argument: expr'])
246  call RunDbgCmd(buf, 'breakd expr x', [
247	      \ 'E121: Undefined variable: x',
248	      \ 'E161: Breakpoint not found: expr x'])
249
250  " finish the current function
251  call RunDbgCmd(buf, 'finish', [
252	      \ 'function Bazz',
253	      \ 'line 8: End of function'])
254  call RunDbgCmd(buf, 'cont')
255
256  " Test for :next
257  call RunDbgCmd(buf, ':debug echo Bar(1)')
258  call RunDbgCmd(buf, 'step')
259  call RunDbgCmd(buf, 'next')
260  call RunDbgCmd(buf, '', [
261	      \ 'function Bar',
262	      \ 'line 3: return var2'])
263  call RunDbgCmd(buf, 'c')
264
265  " Test for :interrupt
266  call RunDbgCmd(buf, ':debug echo Bazz(1)')
267  call RunDbgCmd(buf, 'step')
268  call RunDbgCmd(buf, 'step')
269  call RunDbgCmd(buf, 'interrupt', [
270	      \ 'Exception thrown: Vim:Interrupt',
271	      \ 'function Bazz',
272	      \ 'line 5: catch'])
273  call RunDbgCmd(buf, 'c')
274
275  " Test for :quit
276  call RunDbgCmd(buf, ':debug echo Foo()')
277  call RunDbgCmd(buf, 'breakdel *')
278  call RunDbgCmd(buf, 'breakadd func 3 Foo')
279  call RunDbgCmd(buf, 'breakadd func 3 Bazz')
280  call RunDbgCmd(buf, 'cont', [
281	      \ 'Breakpoint in "Bazz" line 3',
282	      \ 'function Foo[2]..Bar[2]..Bazz',
283	      \ 'line 3: let var3 = "another var"'])
284  call RunDbgCmd(buf, 'quit', [
285	      \ 'Breakpoint in "Foo" line 3',
286	      \ 'function Foo',
287	      \ 'line 3: return var2'])
288  call RunDbgCmd(buf, 'breakdel *')
289  call RunDbgCmd(buf, 'quit')
290  call RunDbgCmd(buf, 'enew! | only!')
291
292  call StopVimInTerminal(buf)
293
294  " Tests for :breakadd file and :breakadd here
295  " Breakpoints should be set before sourcing the file
296
297  call writefile([
298	      \ 'let var1 = 10',
299	      \ 'let var2 = 20',
300	      \ 'let var3 = 30',
301	      \ 'let var4 = 40'], 'Xtest.vim')
302
303  " Start Vim in a terminal
304  let buf = RunVimInTerminal('Xtest.vim', {})
305  call RunDbgCmd(buf, ':breakadd file 2 Xtest.vim')
306  call RunDbgCmd(buf, ':4 | breakadd here')
307  call RunDbgCmd(buf, ':source Xtest.vim', ['line 2: let var2 = 20'])
308  call RunDbgCmd(buf, 'cont', ['line 4: let var4 = 40'])
309  call RunDbgCmd(buf, 'cont')
310
311  call StopVimInTerminal(buf)
312
313  call delete('Xtest.vim')
314endfunc
315