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