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    return
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()')
56
57  " Create a few stack frames by stepping through functions
58  call RunDbgCmd(buf, 'step')
59  call RunDbgCmd(buf, 'step')
60  call RunDbgCmd(buf, 'step')
61  call RunDbgCmd(buf, 'step')
62  call RunDbgCmd(buf, 'step')
63  call RunDbgCmd(buf, 'step')
64  call RunDbgCmd(buf, 'step')
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