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