xref: /vim-8.2.3635/src/testdir/runtest.vim (revision f0b03c4e)
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      exe 'call ' . a:test
128    catch /^\cskipped/
129      call add(s:messages, '    Skipped')
130      call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '',  ''))
131    catch
132      call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
133    endtry
134  endif
135
136  if exists("*TearDown")
137    try
138      call TearDown()
139    catch
140      call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
141    endtry
142  endif
143
144  " Clear any autocommands
145  au!
146
147  " Close any extra tab pages and windows and make the current one not modified.
148  while tabpagenr('$') > 1
149    quit!
150  endwhile
151
152  while 1
153    let wincount = winnr('$')
154    if wincount == 1
155      break
156    endif
157    bwipe!
158    if wincount == winnr('$')
159      " Did not manage to close a window.
160      only!
161      break
162    endif
163  endwhile
164
165  exe 'cd ' . save_cwd
166endfunc
167
168func AfterTheTest()
169  if len(v:errors) > 0
170    let s:fail += 1
171    call add(s:errors, 'Found errors in ' . s:test . ':')
172    call extend(s:errors, v:errors)
173    let v:errors = []
174  endif
175endfunc
176
177" This function can be called by a test if it wants to abort testing.
178func FinishTesting()
179  call AfterTheTest()
180
181  " Don't write viminfo on exit.
182  set viminfo=
183
184  " Clean up files created by setup.vim
185  call delete('XfakeHOME', 'rf')
186
187  if s:fail == 0
188    " Success, create the .res file so that make knows it's done.
189    exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
190    write
191  endif
192
193  if len(s:errors) > 0
194    " Append errors to test.log
195    split test.log
196    call append(line('$'), '')
197    call append(line('$'), 'From ' . g:testname . ':')
198    call append(line('$'), s:errors)
199    write
200  endif
201
202  let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
203  echo message
204  call add(s:messages, message)
205  if s:fail > 0
206    let message = s:fail . ' FAILED:'
207    echo message
208    call add(s:messages, message)
209    call extend(s:messages, s:errors)
210  endif
211
212  " Add SKIPPED messages
213  call extend(s:messages, s:skipped)
214
215  " Append messages to the file "messages"
216  split messages
217  call append(line('$'), '')
218  call append(line('$'), 'From ' . g:testname . ':')
219  call append(line('$'), s:messages)
220  write
221
222  qall!
223endfunc
224
225" Source the test script.  First grab the file name, in case the script
226" navigates away.  g:testname can be used by the tests.
227let g:testname = expand('%')
228let s:done = 0
229let s:fail = 0
230let s:errors = []
231let s:messages = []
232let s:skipped = []
233if expand('%') =~ 'test_vimscript.vim'
234  " this test has intentional s:errors, don't use try/catch.
235  source %
236else
237  try
238    source %
239  catch
240    let s:fail += 1
241    call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
242  endtry
243endif
244
245" Names of flaky tests.
246let s:flaky = [
247      \ 'Test_client_server()',
248      \ 'Test_close_and_exit_cb()',
249      \ 'Test_collapse_buffers()',
250      \ 'Test_communicate()',
251      \ 'Test_exit_callback_interval()',
252      \ 'Test_nb_basic()',
253      \ 'Test_oneshot()',
254      \ 'Test_out_cb()',
255      \ 'Test_paused()',
256      \ 'Test_pipe_through_sort_all()',
257      \ 'Test_pipe_through_sort_some()',
258      \ 'Test_quoteplus()',
259      \ 'Test_quotestar()',
260      \ 'Test_reltime()',
261      \ 'Test_terminal_composing_unicode()',
262      \ 'Test_terminal_noblock()',
263      \ 'Test_terminal_redir_file()',
264      \ 'Test_terminal_tmap()',
265      \ 'Test_with_partial_callback()',
266      \ ]
267
268" Locate Test_ functions and execute them.
269redir @q
270silent function /^Test_
271redir END
272let s:tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g'))
273
274" If there is an extra argument filter the function names against it.
275if argc() > 1
276  let s:tests = filter(s:tests, 'v:val =~ argv(1)')
277endif
278
279" Execute the tests in alphabetical order.
280for s:test in sort(s:tests)
281  " Silence, please!
282  set belloff=all
283
284  call RunTheTest(s:test)
285
286  if len(v:errors) > 0 && index(s:flaky, s:test) >= 0
287    call add(s:messages, 'Found errors in ' . s:test . ':')
288    call extend(s:messages, v:errors)
289    call add(s:messages, 'Flaky test failed, running it again')
290    let first_run = v:errors
291
292    " Flakiness is often caused by the system being very busy.  Sleep a couple
293    " of seconds to have a higher chance of succeeding the second time.
294    sleep 2
295
296    let v:errors = []
297    call RunTheTest(s:test)
298    if len(v:errors) > 0
299      let second_run = v:errors
300      let v:errors = ['First run:']
301      call extend(v:errors, first_run)
302      call add(v:errors, 'Second run:')
303      call extend(v:errors, second_run)
304    endif
305  endif
306
307  call AfterTheTest()
308endfor
309
310call FinishTesting()
311
312" vim: shiftwidth=2 sts=2 expandtab
313