xref: /vim-8.2.3635/src/testdir/runtest.vim (revision fc65cabb)
1" This script is sourced while editing the .vim file with the tests.
2" When the script is successful the .res file will be created.
3" Errors are appended to the test.log file.
4"
5" To execute only specific test functions, add a second argument.  It will be
6" matched against the names of the Test_ funtion.  E.g.:
7"	../vim -u NONE -S runtest.vim test_channel.vim open_delay
8" The output can be found in the "messages" file.
9"
10" The test script may contain anything, only functions that start with
11" "Test_" are special.  These will be invoked and should contain assert
12" functions.  See test_assert.vim for an example.
13"
14" It is possible to source other files that contain "Test_" functions.  This
15" can speed up testing, since Vim does not need to restart.  But be careful
16" that the tests do not interfere with each other.
17"
18" If an error cannot be detected properly with an assert function add the
19" error to the v:errors list:
20"   call add(v:errors, 'test foo failed: Cannot find xyz')
21"
22" If preparation for each Test_ function is needed, define a SetUp function.
23" It will be called before each Test_ function.
24"
25" If cleanup after each Test_ function is needed, define a TearDown function.
26" It will be called after each Test_ function.
27"
28" When debugging a test it can be useful to add messages to v:errors:
29" 	call add(v:errors, "this happened")
30
31
32" Without the +eval feature we can't run these tests, bail out.
33so small.vim
34
35" Check that the screen size is at least 24 x 80 characters.
36if &lines < 24 || &columns < 80
37  let error = 'Screen size too small! Tests require at least 24 lines with 80 characters'
38  echoerr error
39  split test.log
40  $put =error
41  w
42  cquit
43endif
44
45" Common with all tests on all systems.
46source setup.vim
47
48" For consistency run all tests with 'nocompatible' set.
49" This also enables use of line continuation.
50set nocp viminfo+=nviminfo
51
52" Use utf-8 or latin1 by default, instead of whatever the system default
53" happens to be.  Individual tests can overrule this at the top of the file.
54if has('multi_byte')
55  set encoding=utf-8
56else
57  set encoding=latin1
58endif
59
60" Avoid stopping at the "hit enter" prompt
61set nomore
62
63" Output all messages in English.
64lang mess C
65
66" Always use forward slashes.
67set shellslash
68
69let s:srcdir = expand('%:p:h:h')
70
71" Prepare for calling test_garbagecollect_now().
72let v:testing = 1
73
74" Support function: get the alloc ID by name.
75function GetAllocId(name)
76  exe 'split ' . s:srcdir . '/alloc.h'
77  let top = search('typedef enum')
78  if top == 0
79    call add(v:errors, 'typedef not found in alloc.h')
80  endif
81  let lnum = search('aid_' . a:name . ',')
82  if lnum == 0
83    call add(v:errors, 'Alloc ID ' . a:name . ' not defined')
84  endif
85  close
86  return lnum - top - 1
87endfunc
88
89func RunTheTest(test)
90  echo 'Executing ' . a:test
91
92  " Avoid stopping at the "hit enter" prompt
93  set nomore
94
95  " Avoid a three second wait when a message is about to be overwritten by the
96  " mode message.
97  set noshowmode
98
99  " Clear any overrides.
100  call test_override('ALL', 0)
101
102  " Some tests wipe out buffers.  To be consistent, always wipe out all
103  " buffers.
104  %bwipe!
105
106  " The test may change the current directory. Save and restore the
107  " directory after executing the test.
108  let save_cwd = getcwd()
109
110  if exists("*SetUp")
111    try
112      call SetUp()
113    catch
114      call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
115    endtry
116  endif
117
118  call add(s:messages, 'Executing ' . a:test)
119  let s:done += 1
120
121  if a:test =~ 'Test_nocatch_'
122    " Function handles errors itself.  This avoids skipping commands after the
123    " error.
124    exe 'call ' . a:test
125  else
126    try
127      let s:test = a:test
128      au VimLeavePre * call EarlyExit(s:test)
129      exe 'call ' . a:test
130      au! VimLeavePre
131    catch /^\cskipped/
132      call add(s:messages, '    Skipped')
133      call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '',  ''))
134    catch
135      call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
136    endtry
137  endif
138
139  if exists("*TearDown")
140    try
141      call TearDown()
142    catch
143      call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
144    endtry
145  endif
146
147  " Clear any autocommands
148  au!
149
150  " Close any extra tab pages and windows and make the current one not modified.
151  while tabpagenr('$') > 1
152    quit!
153  endwhile
154
155  while 1
156    let wincount = winnr('$')
157    if wincount == 1
158      break
159    endif
160    bwipe!
161    if wincount == winnr('$')
162      " Did not manage to close a window.
163      only!
164      break
165    endif
166  endwhile
167
168  exe 'cd ' . save_cwd
169endfunc
170
171func AfterTheTest()
172  if len(v:errors) > 0
173    let s:fail += 1
174    call add(s:errors, 'Found errors in ' . s:test . ':')
175    call extend(s:errors, v:errors)
176    let v:errors = []
177  endif
178endfunc
179
180func EarlyExit(test)
181  " It's OK for the test we use to test the quit detection.
182  if a:test != 'Test_zz_quit_detected()'
183    call add(v:errors, 'Test caused Vim to exit: ' . a:test)
184  endif
185
186  call FinishTesting()
187endfunc
188
189" This function can be called by a test if it wants to abort testing.
190func FinishTesting()
191  call AfterTheTest()
192
193  " Don't write viminfo on exit.
194  set viminfo=
195
196  " Clean up files created by setup.vim
197  call delete('XfakeHOME', 'rf')
198
199  if s:fail == 0
200    " Success, create the .res file so that make knows it's done.
201    exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
202    write
203  endif
204
205  if len(s:errors) > 0
206    " Append errors to test.log
207    split test.log
208    call append(line('$'), '')
209    call append(line('$'), 'From ' . g:testname . ':')
210    call append(line('$'), s:errors)
211    write
212  endif
213
214  if s:done == 0
215    let message = 'NO tests executed'
216  else
217    let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
218  endif
219  echo message
220  call add(s:messages, message)
221  if s:fail > 0
222    let message = s:fail . ' FAILED:'
223    echo message
224    call add(s:messages, message)
225    call extend(s:messages, s:errors)
226  endif
227
228  " Add SKIPPED messages
229  call extend(s:messages, s:skipped)
230
231  " Append messages to the file "messages"
232  split messages
233  call append(line('$'), '')
234  call append(line('$'), 'From ' . g:testname . ':')
235  call append(line('$'), s:messages)
236  write
237
238  qall!
239endfunc
240
241" Source the test script.  First grab the file name, in case the script
242" navigates away.  g:testname can be used by the tests.
243let g:testname = expand('%')
244let s:done = 0
245let s:fail = 0
246let s:errors = []
247let s:messages = []
248let s:skipped = []
249if expand('%') =~ 'test_vimscript.vim'
250  " this test has intentional s:errors, don't use try/catch.
251  source %
252else
253  try
254    source %
255  catch
256    let s:fail += 1
257    call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
258  endtry
259endif
260
261" Names of flaky tests.
262let s:flaky = [
263      \ 'Test_client_server()',
264      \ 'Test_close_and_exit_cb()',
265      \ 'Test_collapse_buffers()',
266      \ 'Test_communicate()',
267      \ 'Test_cwd()',
268      \ 'Test_exit_callback_interval()',
269      \ 'Test_nb_basic()',
270      \ 'Test_oneshot()',
271      \ 'Test_out_cb()',
272      \ 'Test_paused()',
273      \ 'Test_pipe_through_sort_all()',
274      \ 'Test_pipe_through_sort_some()',
275      \ 'Test_popup_and_window_resize()',
276      \ 'Test_quoteplus()',
277      \ 'Test_quotestar()',
278      \ 'Test_reltime()',
279      \ 'Test_repeat_three()',
280      \ 'Test_terminal_composing_unicode()',
281      \ 'Test_terminal_noblock()',
282      \ 'Test_terminal_redir_file()',
283      \ 'Test_terminal_tmap()',
284      \ 'Test_with_partial_callback()',
285      \ ]
286
287" Locate Test_ functions and execute them.
288redir @q
289silent function /^Test_
290redir END
291let s:tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g'))
292
293" If there is an extra argument filter the function names against it.
294if argc() > 1
295  let s:tests = filter(s:tests, 'v:val =~ argv(1)')
296endif
297
298" Execute the tests in alphabetical order.
299for s:test in sort(s:tests)
300  " Silence, please!
301  set belloff=all
302
303  call RunTheTest(s:test)
304
305  if len(v:errors) > 0 && index(s:flaky, s:test) >= 0
306    call add(s:messages, 'Found errors in ' . s:test . ':')
307    call extend(s:messages, v:errors)
308    call add(s:messages, 'Flaky test failed, running it again')
309    let first_run = v:errors
310
311    " Flakiness is often caused by the system being very busy.  Sleep a couple
312    " of seconds to have a higher chance of succeeding the second time.
313    sleep 2
314
315    let v:errors = []
316    call RunTheTest(s:test)
317    if len(v:errors) > 0
318      let second_run = v:errors
319      let v:errors = ['First run:']
320      call extend(v:errors, first_run)
321      call add(v:errors, 'Second run:')
322      call extend(v:errors, second_run)
323    endif
324  endif
325
326  call AfterTheTest()
327endfor
328
329call FinishTesting()
330
331" vim: shiftwidth=2 sts=2 expandtab
332