1" Test for user functions.
2" Also test an <expr> mapping calling a function.
3" Also test that a builtin function cannot be replaced.
4" Also test for regression when calling arbitrary expression.
5
6source check.vim
7source shared.vim
8
9func Table(title, ...)
10  let ret = a:title
11  let idx = 1
12  while idx <= a:0
13    exe "let ret = ret . a:" . idx
14    let idx = idx + 1
15  endwhile
16  return ret
17endfunc
18
19func Compute(n1, n2, divname)
20  if a:n2 == 0
21    return "fail"
22  endif
23  exe "let g:" . a:divname . " = ". a:n1 / a:n2
24  return "ok"
25endfunc
26
27func Expr1()
28  silent! normal! v
29  return "111"
30endfunc
31
32func Expr2()
33  call search('XX', 'b')
34  return "222"
35endfunc
36
37func ListItem()
38  let g:counter += 1
39  return g:counter . '. '
40endfunc
41
42func ListReset()
43  let g:counter = 0
44  return ''
45endfunc
46
47func FuncWithRef(a)
48  unlet g:FuncRef
49  return a:a
50endfunc
51
52func Test_user_func()
53  let g:FuncRef = function("FuncWithRef")
54  let g:counter = 0
55  inoremap <expr> ( ListItem()
56  inoremap <expr> [ ListReset()
57  imap <expr> + Expr1()
58  imap <expr> * Expr2()
59  let g:retval = "nop"
60
61  call assert_equal('xxx4asdf', Table("xxx", 4, "asdf"))
62  call assert_equal('fail', Compute(45, 0, "retval"))
63  call assert_equal('nop', g:retval)
64  call assert_equal('ok', Compute(45, 5, "retval"))
65  call assert_equal(9, g:retval)
66  call assert_equal(333, g:FuncRef(333))
67
68  let g:retval = "nop"
69  call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf"))
70  call assert_equal('fail', 45->Compute(0, "retval"))
71  call assert_equal('nop', g:retval)
72  call assert_equal('ok', 45->Compute(5, "retval"))
73  call assert_equal(9, g:retval)
74  " call assert_equal(333, 333->g:FuncRef())
75
76  enew
77
78  normal oXX+-XX
79  call assert_equal('XX111-XX', getline('.'))
80  normal o---*---
81  call assert_equal('---222---', getline('.'))
82  normal o(one
83  call assert_equal('1. one', getline('.'))
84  normal o(two
85  call assert_equal('2. two', getline('.'))
86  normal o[(one again
87  call assert_equal('1. one again', getline('.'))
88
89  " Try to overwrite a function in the global (g:) scope
90  call assert_equal(3, max([1, 2, 3]))
91  call assert_fails("call extend(g:, {'max': function('min')})", 'E704')
92  call assert_equal(3, max([1, 2, 3]))
93
94  " Try to overwrite an user defined function with a function reference
95  call assert_fails("let Expr1 = function('min')", 'E705:')
96
97  " Regression: the first line below used to throw ?E110: Missing ')'?
98  " Second is here just to prove that this line is correct when not skipping
99  " rhs of &&.
100  call assert_equal(0, (0 && (function('tr'))(1, 2, 3)))
101  call assert_equal(1, (1 && (function('tr'))(1, 2, 3)))
102
103  delfunc Table
104  delfunc Compute
105  delfunc Expr1
106  delfunc Expr2
107  delfunc ListItem
108  delfunc ListReset
109  unlet g:retval g:counter
110  enew!
111endfunc
112
113func Log(val, base = 10)
114  return log(a:val) / log(a:base)
115endfunc
116
117func Args(mandatory, optional = v:null, ...)
118  return deepcopy(a:)
119endfunc
120
121func Args2(a = 1, b = 2, c = 3)
122  return deepcopy(a:)
123endfunc
124
125func MakeBadFunc()
126  func s:fcn(a, b=1, c)
127  endfunc
128endfunc
129
130func Test_default_arg()
131  if has('float')
132    call assert_equal(1.0, Log(10))
133    call assert_equal(log(10), Log(10, exp(1)))
134    call assert_fails("call Log(1,2,3)", 'E118')
135  endif
136
137  let res = Args(1)
138  call assert_equal(res.mandatory, 1)
139  call assert_equal(res.optional, v:null)
140  call assert_equal(res['0'], 0)
141
142  let res = Args(1,2)
143  call assert_equal(res.mandatory, 1)
144  call assert_equal(res.optional, 2)
145  call assert_equal(res['0'], 0)
146
147  let res = Args(1,2,3)
148  call assert_equal(res.mandatory, 1)
149  call assert_equal(res.optional, 2)
150  call assert_equal(res['0'], 1)
151
152  call assert_fails("call MakeBadFunc()", 'E989')
153  call assert_fails("fu F(a=1 ,) | endf", 'E475')
154
155  let d = Args2(7, v:none, 9)
156  call assert_equal([7, 2, 9], [d.a, d.b, d.c])
157
158  call assert_equal("\n"
159	\ .. "   function Args2(a = 1, b = 2, c = 3)\n"
160	\ .. "1    return deepcopy(a:)\n"
161	\ .. "   endfunction",
162	\ execute('func Args2'))
163endfunc
164
165func s:addFoo(lead)
166  return a:lead .. 'foo'
167endfunc
168
169func Test_user_method()
170  eval 'bar'->s:addFoo()->assert_equal('barfoo')
171endfunc
172
173func Test_failed_call_in_try()
174  try | call UnknownFunc() | catch | endtry
175endfunc
176
177" Test for listing user-defined functions
178func Test_function_list()
179  call assert_fails("function Xabc", 'E123:')
180endfunc
181
182" Test for <sfile>, <slnum> in a function
183func Test_sfile_in_function()
184  func Xfunc()
185    call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>'))
186    call assert_equal('2', expand('<slnum>'))
187  endfunc
188  call Xfunc()
189  delfunc Xfunc
190endfunc
191
192" Test trailing text after :endfunction				    {{{1
193func Test_endfunction_trailing()
194  call assert_false(exists('*Xtest'))
195
196  exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'"
197  call assert_true(exists('*Xtest'))
198  call assert_equal('yes', done)
199  delfunc Xtest
200  unlet done
201
202  exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'"
203  call assert_true(exists('*Xtest'))
204  call assert_equal('yes', done)
205  delfunc Xtest
206  unlet done
207
208  " trailing line break
209  exe "func Xtest()\necho 'hello'\nendfunc\n"
210  call assert_true(exists('*Xtest'))
211  delfunc Xtest
212
213  set verbose=1
214  exe "func Xtest()\necho 'hello'\nendfunc \" garbage"
215  call assert_notmatch('W22:', split(execute('1messages'), "\n")[0])
216  call assert_true(exists('*Xtest'))
217  delfunc Xtest
218
219  exe "func Xtest()\necho 'hello'\nendfunc garbage"
220  call assert_match('W22:', split(execute('1messages'), "\n")[0])
221  call assert_true(exists('*Xtest'))
222  delfunc Xtest
223  set verbose=0
224
225  function Foo()
226    echo 'hello'
227  endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
228  delfunc Foo
229endfunc
230
231func Test_delfunction_force()
232  delfunc! Xtest
233  delfunc! Xtest
234  func Xtest()
235    echo 'nothing'
236  endfunc
237  delfunc! Xtest
238  delfunc! Xtest
239
240  " Try deleting the current function
241  call assert_fails('delfunc Test_delfunction_force', 'E131:')
242endfunc
243
244func Test_function_defined_line()
245  CheckNotGui
246
247  let lines =<< trim [CODE]
248  " F1
249  func F1()
250    " F2
251    func F2()
252      "
253      "
254      "
255      return
256    endfunc
257    " F3
258    execute "func F3()\n\n\n\nreturn\nendfunc"
259    " F4
260    execute "func F4()\n
261                \\n
262                \\n
263                \\n
264                \return\n
265                \endfunc"
266  endfunc
267  " F5
268  execute "func F5()\n\n\n\nreturn\nendfunc"
269  " F6
270  execute "func F6()\n
271              \\n
272              \\n
273              \\n
274              \return\n
275              \endfunc"
276  call F1()
277  verbose func F1
278  verbose func F2
279  verbose func F3
280  verbose func F4
281  verbose func F5
282  verbose func F6
283  qall!
284  [CODE]
285
286  call writefile(lines, 'Xtest.vim')
287  let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim')
288  call assert_equal(0, v:shell_error)
289
290  let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*')
291  call assert_match(' line 2$', m)
292
293  let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*')
294  call assert_match(' line 4$', m)
295
296  let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*')
297  call assert_match(' line 11$', m)
298
299  let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*')
300  call assert_match(' line 13$', m)
301
302  let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*')
303  call assert_match(' line 21$', m)
304
305  let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*')
306  call assert_match(' line 23$', m)
307
308  call delete('Xtest.vim')
309endfunc
310
311" Test for defining a function reference in the global scope
312func Test_add_funcref_to_global_scope()
313  let x = g:
314  let caught_E862 = 0
315  try
316    func x.Xfunc()
317      return 1
318    endfunc
319  catch /E862:/
320    let caught_E862 = 1
321  endtry
322  call assert_equal(1, caught_E862)
323endfunc
324
325func Test_funccall_garbage_collect()
326  func Func(x, ...)
327    call add(a:x, a:000)
328  endfunc
329  call Func([], [])
330  " Must not crash cause by invalid freeing
331  call test_garbagecollect_now()
332  call assert_true(v:true)
333  delfunc Func
334endfunc
335
336" Test for script-local function
337func <SID>DoLast()
338  call append(line('$'), "last line")
339endfunc
340
341func s:DoNothing()
342  call append(line('$'), "nothing line")
343endfunc
344
345func Test_script_local_func()
346  set nocp nomore viminfo+=nviminfo
347  new
348  nnoremap <buffer> _x	:call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr>
349
350  normal _x
351  call assert_equal('nothing line', getline(2))
352  call assert_equal('last line', getline(3))
353  close!
354
355  " Try to call a script local function in global scope
356  let lines =<< trim [CODE]
357    :call assert_fails('call s:Xfunc()', 'E81:')
358    :call assert_fails('let x = call("<SID>Xfunc", [])', 'E120:')
359    :call writefile(v:errors, 'Xresult')
360    :qall
361
362  [CODE]
363  call writefile(lines, 'Xscript')
364  if RunVim([], [], '-s Xscript')
365    call assert_equal([], readfile('Xresult'))
366  endif
367  call delete('Xresult')
368  call delete('Xscript')
369endfunc
370
371" Test for errors in defining new functions
372func Test_func_def_error()
373  call assert_fails('func Xfunc abc ()', 'E124:')
374  call assert_fails('func Xfunc(', 'E125:')
375  call assert_fails('func xfunc()', 'E128:')
376
377  " Try to redefine a function that is in use
378  let caught_E127 = 0
379  try
380    func! Test_func_def_error()
381    endfunc
382  catch /E127:/
383    let caught_E127 = 1
384  endtry
385  call assert_equal(1, caught_E127)
386
387  " Try to define a function in a dict twice
388  let d = {}
389  let lines =<< trim END
390    func d.F1()
391      return 1
392    endfunc
393  END
394  let l = join(lines, "\n") . "\n"
395  exe l
396  call assert_fails('exe l', 'E717:')
397
398  " Define an autoload function with an incorrect file name
399  call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript')
400  call assert_fails('source Xscript', 'E746:')
401  call delete('Xscript')
402endfunc
403
404" Test for deleting a function
405func Test_del_func()
406  call assert_fails('delfunction Xabc', 'E130:')
407  let d = {'a' : 10}
408  call assert_fails('delfunc d.a', 'E718:')
409endfunc
410
411" Test for calling return outside of a function
412func Test_return_outside_func()
413  call writefile(['return 10'], 'Xscript')
414  call assert_fails('source Xscript', 'E133:')
415  call delete('Xscript')
416endfunc
417
418" Test for errors in calling a function
419func Test_func_arg_error()
420  " Too many arguments
421  call assert_fails("call call('min', range(1,20))", 'E118:')
422  call assert_fails("call call('min', range(1,21))", 'E699:')
423  call assert_fails('echo min(0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,0,1)',
424        \ 'E740:')
425
426  " Missing dict argument
427  func Xfunc() dict
428    return 1
429  endfunc
430  call assert_fails('call Xfunc()', 'E725:')
431  delfunc Xfunc
432endfunc
433
434" vim: shiftwidth=2 sts=2 expandtab
435