xref: /vim-8.2.3635/src/testdir/runtest.vim (revision 4b2c8047)
143345546SBram Moolenaar" This script is sourced while editing the .vim file with the tests.
243345546SBram Moolenaar" When the script is successful the .res file will be created.
343345546SBram Moolenaar" Errors are appended to the test.log file.
443345546SBram Moolenaar"
5befb366aSBram Moolenaar" To execute only specific test functions, add a second argument.  It will be
6e219f73eSBram Moolenaar" matched against the names of the Test_ function.  E.g.:
7befb366aSBram Moolenaar"	../vim -u NONE -S runtest.vim test_channel.vim open_delay
8befb366aSBram Moolenaar" The output can be found in the "messages" file.
9befb366aSBram Moolenaar"
10ce436de5SBram Moolenaar" If the environment variable $TEST_FILTER is set then only test functions
11ce436de5SBram Moolenaar" matching this pattern are executed.  E.g. for sh/bash:
12ce436de5SBram Moolenaar"     export TEST_FILTER=Test_channel
13ce436de5SBram Moolenaar" For csh:
14ce436de5SBram Moolenaar"     setenv TEST_FILTER Test_channel
15ce436de5SBram Moolenaar"
16dae453f3SBram Moolenaar" If the environment variable $TEST_SKIP_PAT is set then test functions
17dae453f3SBram Moolenaar" matching this pattern will be skipped.  It's the opposite of $TEST_FILTER.
18dae453f3SBram Moolenaar"
196ca6ca48SBram Moolenaar" While working on a test you can make $TEST_NO_RETRY non-empty to not retry:
206ca6ca48SBram Moolenaar"     export TEST_NO_RETRY=yes
216ca6ca48SBram Moolenaar"
22ce436de5SBram Moolenaar" To ignore failure for tests that are known to fail in a certain environment,
23ce436de5SBram Moolenaar" set $TEST_MAY_FAIL to a comma separated list of function names.  E.g. for
24ce436de5SBram Moolenaar" sh/bash:
25ce436de5SBram Moolenaar"     export TEST_MAY_FAIL=Test_channel_one,Test_channel_other
26ce436de5SBram Moolenaar" The failure report will then not be included in the test.log file and
27ce436de5SBram Moolenaar" "make test" will not fail.
28ce436de5SBram Moolenaar"
2943345546SBram Moolenaar" The test script may contain anything, only functions that start with
3043345546SBram Moolenaar" "Test_" are special.  These will be invoked and should contain assert
3143345546SBram Moolenaar" functions.  See test_assert.vim for an example.
3243345546SBram Moolenaar"
3343345546SBram Moolenaar" It is possible to source other files that contain "Test_" functions.  This
3443345546SBram Moolenaar" can speed up testing, since Vim does not need to restart.  But be careful
3543345546SBram Moolenaar" that the tests do not interfere with each other.
3643345546SBram Moolenaar"
3743345546SBram Moolenaar" If an error cannot be detected properly with an assert function add the
3843345546SBram Moolenaar" error to the v:errors list:
3943345546SBram Moolenaar"   call add(v:errors, 'test foo failed: Cannot find xyz')
4043345546SBram Moolenaar"
4143345546SBram Moolenaar" If preparation for each Test_ function is needed, define a SetUp function.
4243345546SBram Moolenaar" It will be called before each Test_ function.
4343345546SBram Moolenaar"
4443345546SBram Moolenaar" If cleanup after each Test_ function is needed, define a TearDown function.
4543345546SBram Moolenaar" It will be called after each Test_ function.
4600af60bbSBram Moolenaar"
4700af60bbSBram Moolenaar" When debugging a test it can be useful to add messages to v:errors:
4800af60bbSBram Moolenaar"	call add(v:errors, "this happened")
4900af60bbSBram Moolenaar
5043345546SBram Moolenaar
5143345546SBram Moolenaar" Without the +eval feature we can't run these tests, bail out.
52b96a32efSBram Moolenaarsilent! while 0
53b96a32efSBram Moolenaar  qa!
54b96a32efSBram Moolenaarsilent! endwhile
5543345546SBram Moolenaar
5618aa13d1SBram Moolenaar" In the GUI we can always change the screen size.
5718aa13d1SBram Moolenaarif has('gui_running')
5818aa13d1SBram Moolenaar  set columns=80 lines=25
5918aa13d1SBram Moolenaarendif
6018aa13d1SBram Moolenaar
6143345546SBram Moolenaar" Check that the screen size is at least 24 x 80 characters.
6243345546SBram Moolenaarif &lines < 24 || &columns < 80
630b5dc644SBram Moolenaar  let error = 'Screen size too small! Tests require at least 24 lines with 80 characters, got ' .. &lines .. ' lines with ' .. &columns .. ' characters'
6443345546SBram Moolenaar  echoerr error
6543345546SBram Moolenaar  split test.log
6643345546SBram Moolenaar  $put =error
6745aa07d3SBram Moolenaar  write
6845aa07d3SBram Moolenaar  split messages
690b5dc644SBram Moolenaar  call append(line('$'), '')
700b5dc644SBram Moolenaar  call append(line('$'), 'From ' . expand('%') . ':')
7145aa07d3SBram Moolenaar  call append(line('$'), error)
7245aa07d3SBram Moolenaar  write
7345aa07d3SBram Moolenaar  qa!
7443345546SBram Moolenaarendif
7543345546SBram Moolenaar
7675ee544fSBram Moolenaarif has('reltime')
7775ee544fSBram Moolenaar  let s:start_time = reltime()
7875ee544fSBram Moolenaarendif
7975ee544fSBram Moolenaar
8089b10421SBram Moolenaar" Common with all tests on all systems.
8189b10421SBram Moolenaarsource setup.vim
8289b10421SBram Moolenaar
83c0662466SBram Moolenaar" For consistency run all tests with 'nocompatible' set.
84c0662466SBram Moolenaar" This also enables use of line continuation.
85c0662466SBram Moolenaarset nocp viminfo+=nviminfo
86c0662466SBram Moolenaar
8730276f2bSBram Moolenaar" Use utf-8 by default, instead of whatever the system default happens to be.
88ed79d1e3SBram Moolenaar" Individual tests can overrule this at the top of the file and use
89ed79d1e3SBram Moolenaar" g:orig_encoding if needed.
90ed79d1e3SBram Moolenaarlet g:orig_encoding = &encoding
91ac105ed3SBram Moolenaarset encoding=utf-8
92ac105ed3SBram Moolenaar
93d8f27b30SBram Moolenaar" REDIR_TEST_TO_NULL has a very permissive SwapExists autocommand which is for
94d8f27b30SBram Moolenaar" the test_name.vim file itself. Replace it here with a more restrictive one,
95d8f27b30SBram Moolenaar" so we still catch mistakes.
96*4b2c8047SChristian Brabandtif has("win32")
97*4b2c8047SChristian Brabandt  " replace any '/' directory separators by '\\'
98*4b2c8047SChristian Brabandt  let s:test_script_fname = substitute(expand('%'), '/', '\\', 'g')
99*4b2c8047SChristian Brabandtelse
100d8f27b30SBram Moolenaar  let s:test_script_fname = expand('%')
101*4b2c8047SChristian Brabandtendif
102d8f27b30SBram Moolenaarau! SwapExists * call HandleSwapExists()
103d8f27b30SBram Moolenaarfunc HandleSwapExists()
1048ce4b7edSBram Moolenaar  if exists('g:ignoreSwapExists')
1058ce4b7edSBram Moolenaar    return
1068ce4b7edSBram Moolenaar  endif
107b073da89SBram Moolenaar  " Ignore finding a swap file for the test script (the user might be
108d8f27b30SBram Moolenaar  " editing it and do ":make test_name") and the output file.
109b073da89SBram Moolenaar  " Report finding another swap file and chose 'q' to avoid getting stuck.
110d8f27b30SBram Moolenaar  if expand('<afile>') == 'messages' || expand('<afile>') =~ s:test_script_fname
111d8f27b30SBram Moolenaar    let v:swapchoice = 'e'
112b073da89SBram Moolenaar  else
113b073da89SBram Moolenaar    call assert_report('Unexpected swap file: ' .. v:swapname)
114b073da89SBram Moolenaar    let v:swapchoice = 'q'
115d8f27b30SBram Moolenaar  endif
116d8f27b30SBram Moolenaarendfunc
117d8f27b30SBram Moolenaar
1187a073549SBram Moolenaar" Avoid stopping at the "hit enter" prompt
1197a073549SBram Moolenaarset nomore
1207a073549SBram Moolenaar
121c0662466SBram Moolenaar" Output all messages in English.
122c0662466SBram Moolenaarlang mess C
123c0662466SBram Moolenaar
12410e1d01aSBram Moolenaar" suppress menu translation
12510e1d01aSBram Moolenaarif has('gui_running') && exists('did_install_default_menus')
12610e1d01aSBram Moolenaar  source $VIMRUNTIME/delmenu.vim
12710e1d01aSBram Moolenaar  set langmenu=none
12810e1d01aSBram Moolenaar  source $VIMRUNTIME/menu.vim
12910e1d01aSBram Moolenaarendif
13010e1d01aSBram Moolenaar
131f60b796fSBram Moolenaar" Always use forward slashes.
132f60b796fSBram Moolenaarset shellslash
133f60b796fSBram Moolenaar
13428fb79dbSBram Moolenaarlet s:srcdir = expand('%:p:h:h')
13528fb79dbSBram Moolenaar
1362690b5aeSBram Moolenaarif has('win32')
1372690b5aeSBram Moolenaar  " avoid prompt that is long or contains a line break
1382690b5aeSBram Moolenaar  let $PROMPT = '$P$G'
13945df2a01SBram Moolenaar  " On MS-Windows t_md and t_me are Vim specific escape sequences.
14045df2a01SBram Moolenaar  let s:t_bold = "\x1b[1m"
14145df2a01SBram Moolenaar  let s:t_normal = "\x1b[m"
14245df2a01SBram Moolenaarelse
14345df2a01SBram Moolenaar  let s:t_bold = &t_md
14445df2a01SBram Moolenaar  let s:t_normal = &t_me
1452690b5aeSBram Moolenaarendif
1462690b5aeSBram Moolenaar
1474b2ce129SBram Moolenaarif has('mac')
1484b2ce129SBram Moolenaar  " In MacOS, when starting a shell in a terminal, a bash deprecation warning
1494b2ce129SBram Moolenaar  " message is displayed. This breaks the terminal test. Disable the warning
1504b2ce129SBram Moolenaar  " message.
1514b2ce129SBram Moolenaar  let $BASH_SILENCE_DEPRECATION_WARNING = 1
1524b2ce129SBram Moolenaarendif
1534b2ce129SBram Moolenaar
1548e8df251SBram Moolenaar" Prepare for calling test_garbagecollect_now().
155ebf7dfa6SBram Moolenaarlet v:testing = 1
156ebf7dfa6SBram Moolenaar
15728fb79dbSBram Moolenaar" Support function: get the alloc ID by name.
15828fb79dbSBram Moolenaarfunction GetAllocId(name)
15928fb79dbSBram Moolenaar  exe 'split ' . s:srcdir . '/alloc.h'
160065ee9aeSBram Moolenaar  let top = search('typedef enum')
161065ee9aeSBram Moolenaar  if top == 0
162065ee9aeSBram Moolenaar    call add(v:errors, 'typedef not found in alloc.h')
163065ee9aeSBram Moolenaar  endif
16428fb79dbSBram Moolenaar  let lnum = search('aid_' . a:name . ',')
16528fb79dbSBram Moolenaar  if lnum == 0
16628fb79dbSBram Moolenaar    call add(v:errors, 'Alloc ID ' . a:name . ' not defined')
16728fb79dbSBram Moolenaar  endif
16828fb79dbSBram Moolenaar  close
169065ee9aeSBram Moolenaar  return lnum - top - 1
17028fb79dbSBram Moolenaarendfunc
17128fb79dbSBram Moolenaar
17242205551SBram Moolenaarfunc RunTheTest(test)
1734c86830fSBram Moolenaar  echoconsole 'Executing ' . a:test
17475ee544fSBram Moolenaar  if has('reltime')
17575ee544fSBram Moolenaar    let func_start = reltime()
17675ee544fSBram Moolenaar  endif
177e5f2a075SBram Moolenaar
178e5f2a075SBram Moolenaar  " Avoid stopping at the "hit enter" prompt
179e5f2a075SBram Moolenaar  set nomore
180e5f2a075SBram Moolenaar
181e5f2a075SBram Moolenaar  " Avoid a three second wait when a message is about to be overwritten by the
182e5f2a075SBram Moolenaar  " mode message.
183e5f2a075SBram Moolenaar  set noshowmode
184e5f2a075SBram Moolenaar
185eb992cb9SBram Moolenaar  " Clear any overrides.
186eb992cb9SBram Moolenaar  call test_override('ALL', 0)
187eb992cb9SBram Moolenaar
188cf1ba35fSBram Moolenaar  " Some tests wipe out buffers.  To be consistent, always wipe out all
189cf1ba35fSBram Moolenaar  " buffers.
190cf1ba35fSBram Moolenaar  %bwipe!
191cf1ba35fSBram Moolenaar
192209d3874SBram Moolenaar  " The test may change the current directory. Save and restore the
193209d3874SBram Moolenaar  " directory after executing the test.
194209d3874SBram Moolenaar  let save_cwd = getcwd()
195209d3874SBram Moolenaar
196b5760a1cSBram Moolenaar  if exists("*SetUp")
197cc28e2d0SBram Moolenaar    try
198b5760a1cSBram Moolenaar      call SetUp()
199cc28e2d0SBram Moolenaar    catch
200cc28e2d0SBram Moolenaar      call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
201cc28e2d0SBram Moolenaar    endtry
202b5760a1cSBram Moolenaar  endif
203b5760a1cSBram Moolenaar
204f204e05aSBram Moolenaar  if a:test =~ 'Test_nocatch_'
205f204e05aSBram Moolenaar    " Function handles errors itself.  This avoids skipping commands after the
206f204e05aSBram Moolenaar    " error.
207776b9546SBram Moolenaar    let g:skipped_reason = ''
208f204e05aSBram Moolenaar    exe 'call ' . a:test
209776b9546SBram Moolenaar    if g:skipped_reason != ''
210776b9546SBram Moolenaar      call add(s:messages, '    Skipped')
211776b9546SBram Moolenaar      call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason)
212776b9546SBram Moolenaar    endif
213f204e05aSBram Moolenaar  else
214b5760a1cSBram Moolenaar    try
215bfe13cccSBram Moolenaar      au VimLeavePre * call EarlyExit(g:testfunc)
216b5760a1cSBram Moolenaar      exe 'call ' . a:test
2178903676dSBram Moolenaar      au! VimLeavePre
218dac1947bSBram Moolenaar    catch /^\cskipped/
219dac1947bSBram Moolenaar      call add(s:messages, '    Skipped')
220dac1947bSBram Moolenaar      call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '',  ''))
221b5760a1cSBram Moolenaar    catch
222b5760a1cSBram Moolenaar      call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
223b5760a1cSBram Moolenaar    endtry
224f204e05aSBram Moolenaar  endif
2258ad16da7SBram Moolenaar
2268ad16da7SBram Moolenaar  " In case 'insertmode' was set and something went wrong, make sure it is
2278ad16da7SBram Moolenaar  " reset to avoid trouble with anything else.
2288ad16da7SBram Moolenaar  set noinsertmode
229b5760a1cSBram Moolenaar
230b5760a1cSBram Moolenaar  if exists("*TearDown")
231cc28e2d0SBram Moolenaar    try
232b5760a1cSBram Moolenaar      call TearDown()
233cc28e2d0SBram Moolenaar    catch
234cc28e2d0SBram Moolenaar      call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
235cc28e2d0SBram Moolenaar    endtry
236b5760a1cSBram Moolenaar  endif
2377cba71d7SBram Moolenaar
2380b5dc644SBram Moolenaar  " Clear any autocommands and put back the catch-all for SwapExists.
239cf1ba35fSBram Moolenaar  au!
240d8f27b30SBram Moolenaar  au SwapExists * call HandleSwapExists()
241cf1ba35fSBram Moolenaar
242ef6b979bSBram Moolenaar  " Check for and close any stray popup windows.
24305ad5ff0SBram Moolenaar  if has('popupwin')
244ef6b979bSBram Moolenaar    call assert_equal([], popup_list())
24503a9f848SBram Moolenaar    call popup_clear(1)
246ae943150SBram Moolenaar  endif
247ae943150SBram Moolenaar
248ce11de87SBram Moolenaar  " Close any extra tab pages and windows and make the current one not modified.
249ce11de87SBram Moolenaar  while tabpagenr('$') > 1
250bdf931c2SBram Moolenaar    let winid = win_getid()
251cf1ba35fSBram Moolenaar    quit!
252bdf931c2SBram Moolenaar    if winid == win_getid()
253bdf931c2SBram Moolenaar      echoerr 'Could not quit window'
254bdf931c2SBram Moolenaar      break
255bdf931c2SBram Moolenaar    endif
256ce11de87SBram Moolenaar  endwhile
257ce11de87SBram Moolenaar
258358308ddSBram Moolenaar  while 1
259358308ddSBram Moolenaar    let wincount = winnr('$')
260358308ddSBram Moolenaar    if wincount == 1
261358308ddSBram Moolenaar      break
262358308ddSBram Moolenaar    endif
2637cba71d7SBram Moolenaar    bwipe!
264358308ddSBram Moolenaar    if wincount == winnr('$')
265358308ddSBram Moolenaar      " Did not manage to close a window.
266358308ddSBram Moolenaar      only!
267358308ddSBram Moolenaar      break
268358308ddSBram Moolenaar    endif
2697cba71d7SBram Moolenaar  endwhile
270209d3874SBram Moolenaar
271209d3874SBram Moolenaar  exe 'cd ' . save_cwd
272640d4f0cSBram Moolenaar
273640d4f0cSBram Moolenaar  let message = 'Executed ' . a:test
274640d4f0cSBram Moolenaar  if has('reltime')
2758d943796SBram Moolenaar    let message ..= repeat(' ', 50 - len(message))
2768d943796SBram Moolenaar    let time = reltime(func_start)
2778d943796SBram Moolenaar    if has('float') && reltimefloat(time) > 0.1
27845df2a01SBram Moolenaar      let message = s:t_bold .. message
2798d943796SBram Moolenaar    endif
2808d943796SBram Moolenaar    let message ..= ' in ' .. reltimestr(time) .. ' seconds'
2818d943796SBram Moolenaar    if has('float') && reltimefloat(time) > 0.1
28245df2a01SBram Moolenaar      let message ..= s:t_normal
2838d943796SBram Moolenaar    endif
284640d4f0cSBram Moolenaar  endif
285640d4f0cSBram Moolenaar  call add(s:messages, message)
286640d4f0cSBram Moolenaar  let s:done += 1
287b5760a1cSBram Moolenaarendfunc
28828fb79dbSBram Moolenaar
289ce436de5SBram Moolenaarfunc AfterTheTest(func_name)
29043345546SBram Moolenaar  if len(v:errors) > 0
291ce436de5SBram Moolenaar    if match(s:may_fail_list, '^' .. a:func_name) >= 0
292ce436de5SBram Moolenaar      let s:fail_expected += 1
293bfe13cccSBram Moolenaar      call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
294ce436de5SBram Moolenaar      call extend(s:errors_expected, v:errors)
295ce436de5SBram Moolenaar    else
29600af60bbSBram Moolenaar      let s:fail += 1
297bfe13cccSBram Moolenaar      call add(s:errors, 'Found errors in ' . g:testfunc . ':')
29800af60bbSBram Moolenaar      call extend(s:errors, v:errors)
299ce436de5SBram Moolenaar    endif
30043345546SBram Moolenaar    let v:errors = []
30143345546SBram Moolenaar  endif
30242205551SBram Moolenaarendfunc
30342205551SBram Moolenaar
3048903676dSBram Moolenaarfunc EarlyExit(test)
3058903676dSBram Moolenaar  " It's OK for the test we use to test the quit detection.
3068903676dSBram Moolenaar  if a:test != 'Test_zz_quit_detected()'
3078903676dSBram Moolenaar    call add(v:errors, 'Test caused Vim to exit: ' . a:test)
3088903676dSBram Moolenaar  endif
3098903676dSBram Moolenaar
3108903676dSBram Moolenaar  call FinishTesting()
3118903676dSBram Moolenaarendfunc
3128903676dSBram Moolenaar
31342205551SBram Moolenaar" This function can be called by a test if it wants to abort testing.
31442205551SBram Moolenaarfunc FinishTesting()
315ce436de5SBram Moolenaar  call AfterTheTest('')
31643345546SBram Moolenaar
317fc4ad616SBram Moolenaar  " Don't write viminfo on exit.
318fc4ad616SBram Moolenaar  set viminfo=
319fc4ad616SBram Moolenaar
320d1ee0043SBram Moolenaar  " Clean up files created by setup.vim
321d1ee0043SBram Moolenaar  call delete('XfakeHOME', 'rf')
322d1ee0043SBram Moolenaar
323ce436de5SBram Moolenaar  if s:fail == 0 && s:fail_expected == 0
32443345546SBram Moolenaar    " Success, create the .res file so that make knows it's done.
32500af60bbSBram Moolenaar    exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
32643345546SBram Moolenaar    write
32743345546SBram Moolenaar  endif
32843345546SBram Moolenaar
32900af60bbSBram Moolenaar  if len(s:errors) > 0
33043345546SBram Moolenaar    " Append errors to test.log
33143345546SBram Moolenaar    split test.log
33243345546SBram Moolenaar    call append(line('$'), '')
33300af60bbSBram Moolenaar    call append(line('$'), 'From ' . g:testname . ':')
33400af60bbSBram Moolenaar    call append(line('$'), s:errors)
33543345546SBram Moolenaar    write
33643345546SBram Moolenaar  endif
33743345546SBram Moolenaar
33829f9ed20SBram Moolenaar  if s:done == 0
3397b666c78SBram Moolenaar    if s:filtered > 0
340dae453f3SBram Moolenaar      if $TEST_FILTER != ''
3417b666c78SBram Moolenaar        let message = "NO tests match $TEST_FILTER: '" .. $TEST_FILTER .. "'"
34229f9ed20SBram Moolenaar      else
343dae453f3SBram Moolenaar        let message = "ALL tests match $TEST_SKIP_PAT: '" .. $TEST_SKIP_PAT .. "'"
344dae453f3SBram Moolenaar      endif
345dae453f3SBram Moolenaar    else
3467b666c78SBram Moolenaar      let message = 'NO tests executed'
3477b666c78SBram Moolenaar    endif
3487b666c78SBram Moolenaar  else
3497b666c78SBram Moolenaar    if s:filtered > 0
350dae453f3SBram Moolenaar      call add(s:messages, "Filtered " .. s:filtered .. " tests with $TEST_FILTER and $TEST_SKIP_PAT")
3517b666c78SBram Moolenaar    endif
35200af60bbSBram Moolenaar    let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
35329f9ed20SBram Moolenaar  endif
3547b666c78SBram Moolenaar  if s:done > 0 && has('reltime')
35545df2a01SBram Moolenaar    let message = s:t_bold .. message .. repeat(' ', 40 - len(message))
35675ee544fSBram Moolenaar    let message ..= ' in ' .. reltimestr(reltime(s:start_time)) .. ' seconds'
35745df2a01SBram Moolenaar    let message ..= s:t_normal
35875ee544fSBram Moolenaar  endif
359096c8bb4SBram Moolenaar  echo message
36000af60bbSBram Moolenaar  call add(s:messages, message)
36100af60bbSBram Moolenaar  if s:fail > 0
36200af60bbSBram Moolenaar    let message = s:fail . ' FAILED:'
363096c8bb4SBram Moolenaar    echo message
36400af60bbSBram Moolenaar    call add(s:messages, message)
36500af60bbSBram Moolenaar    call extend(s:messages, s:errors)
36643345546SBram Moolenaar  endif
367ce436de5SBram Moolenaar  if s:fail_expected > 0
368ce436de5SBram Moolenaar    let message = s:fail_expected . ' FAILED (matching $TEST_MAY_FAIL):'
369ce436de5SBram Moolenaar    echo message
370ce436de5SBram Moolenaar    call add(s:messages, message)
371ce436de5SBram Moolenaar    call extend(s:messages, s:errors_expected)
372ce436de5SBram Moolenaar  endif
37343345546SBram Moolenaar
374dac1947bSBram Moolenaar  " Add SKIPPED messages
375dac1947bSBram Moolenaar  call extend(s:messages, s:skipped)
376dac1947bSBram Moolenaar
377dac1947bSBram Moolenaar  " Append messages to the file "messages"
378096c8bb4SBram Moolenaar  split messages
379096c8bb4SBram Moolenaar  call append(line('$'), '')
38000af60bbSBram Moolenaar  call append(line('$'), 'From ' . g:testname . ':')
38100af60bbSBram Moolenaar  call append(line('$'), s:messages)
382096c8bb4SBram Moolenaar  write
383096c8bb4SBram Moolenaar
38443345546SBram Moolenaar  qall!
38542205551SBram Moolenaarendfunc
38642205551SBram Moolenaar
38742205551SBram Moolenaar" Source the test script.  First grab the file name, in case the script
38842205551SBram Moolenaar" navigates away.  g:testname can be used by the tests.
38942205551SBram Moolenaarlet g:testname = expand('%')
39042205551SBram Moolenaarlet s:done = 0
39142205551SBram Moolenaarlet s:fail = 0
392ce436de5SBram Moolenaarlet s:fail_expected = 0
39342205551SBram Moolenaarlet s:errors = []
394ce436de5SBram Moolenaarlet s:errors_expected = []
39542205551SBram Moolenaarlet s:messages = []
39642205551SBram Moolenaarlet s:skipped = []
39742205551SBram Moolenaarif expand('%') =~ 'test_vimscript.vim'
398ce436de5SBram Moolenaar  " this test has intentional errors, don't use try/catch.
39942205551SBram Moolenaar  source %
40042205551SBram Moolenaarelse
40142205551SBram Moolenaar  try
40242205551SBram Moolenaar    source %
4039c0cec65SBram Moolenaar  catch /^\cskipped/
4049c0cec65SBram Moolenaar    call add(s:messages, '    Skipped')
4059c0cec65SBram Moolenaar    call add(s:skipped, 'SKIPPED ' . expand('%') . ': ' . substitute(v:exception, '^\S*\s\+', '',  ''))
40642205551SBram Moolenaar  catch
40742205551SBram Moolenaar    let s:fail += 1
40842205551SBram Moolenaar    call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
40942205551SBram Moolenaar  endtry
41042205551SBram Moolenaarendif
41142205551SBram Moolenaar
41242205551SBram Moolenaar" Locate Test_ functions and execute them.
41342205551SBram Moolenaarredir @q
41442205551SBram Moolenaarsilent function /^Test_
41542205551SBram Moolenaarredir END
41661a6d4e4SBram Moolenaarlet s:tests = split(substitute(@q, '\(function\|def\) \(\k*()\)', '\2', 'g'))
41742205551SBram Moolenaar
41842205551SBram Moolenaar" If there is an extra argument filter the function names against it.
41942205551SBram Moolenaarif argc() > 1
42042205551SBram Moolenaar  let s:tests = filter(s:tests, 'v:val =~ argv(1)')
42142205551SBram Moolenaarendif
42242205551SBram Moolenaar
423a7f6c3cfSBram Moolenaar" If the environment variable $TEST_FILTER is set then filter the function
424a7f6c3cfSBram Moolenaar" names against it.
4257b666c78SBram Moolenaarlet s:filtered = 0
426a7f6c3cfSBram Moolenaarif $TEST_FILTER != ''
4277b666c78SBram Moolenaar  let s:filtered = len(s:tests)
428a7f6c3cfSBram Moolenaar  let s:tests = filter(s:tests, 'v:val =~ $TEST_FILTER')
4297b666c78SBram Moolenaar  let s:filtered -= len(s:tests)
430a7f6c3cfSBram Moolenaarendif
431a7f6c3cfSBram Moolenaar
432ce436de5SBram Moolenaarlet s:may_fail_list = []
433ce436de5SBram Moolenaarif $TEST_MAY_FAIL != ''
434bfe13cccSBram Moolenaar  " Split the list at commas and add () to make it match g:testfunc.
435ce436de5SBram Moolenaar  let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
436ce436de5SBram Moolenaarendif
437ce436de5SBram Moolenaar
43842205551SBram Moolenaar" Execute the tests in alphabetical order.
439bfe13cccSBram Moolenaarfor g:testfunc in sort(s:tests)
440dae453f3SBram Moolenaar  if $TEST_SKIP_PAT != '' && g:testfunc =~ $TEST_SKIP_PAT
441dae453f3SBram Moolenaar    call add(s:messages, g:testfunc .. ' matches $TEST_SKIP_PAT')
442dae453f3SBram Moolenaar    let s:filtered += 1
443dae453f3SBram Moolenaar    continue
444dae453f3SBram Moolenaar  endif
445dae453f3SBram Moolenaar
4464a6fcf80SBram Moolenaar  " Silence, please!
4474a6fcf80SBram Moolenaar  set belloff=all
448f77af0e6SBram Moolenaar  let prev_error = ''
449f77af0e6SBram Moolenaar  let total_errors = []
4503ed9efc2SBram Moolenaar  let g:run_nr = 1
4514a6fcf80SBram Moolenaar
452bfe13cccSBram Moolenaar  " A test can set g:test_is_flaky to retry running the test.
453bfe13cccSBram Moolenaar  let g:test_is_flaky = 0
4543cdcb090SBram Moolenaar
455bfe13cccSBram Moolenaar  call RunTheTest(g:testfunc)
45642205551SBram Moolenaar
457f77af0e6SBram Moolenaar  " Repeat a flaky test.  Give up when:
4586ca6ca48SBram Moolenaar  " - $TEST_NO_RETRY is not empty
459f77af0e6SBram Moolenaar  " - it fails again with the same message
4601bc353b6SBram Moolenaar  " - it fails five times (with a different message)
461dbc0d216SBram Moolenaar  if len(v:errors) > 0
462622b3568SBram Moolenaar        \ && $TEST_NO_RETRY == ''
463f08b0eb8SBram Moolenaar        \ && g:test_is_flaky
464f77af0e6SBram Moolenaar    while 1
465bfe13cccSBram Moolenaar      call add(s:messages, 'Found errors in ' . g:testfunc . ':')
4666caf606bSBram Moolenaar      call extend(s:messages, v:errors)
46715e737f7SBram Moolenaar
4683ed9efc2SBram Moolenaar      call add(total_errors, 'Run ' . g:run_nr . ':')
469f77af0e6SBram Moolenaar      call extend(total_errors, v:errors)
470f77af0e6SBram Moolenaar
4713ed9efc2SBram Moolenaar      if g:run_nr == 5 || prev_error == v:errors[0]
472f77af0e6SBram Moolenaar        call add(total_errors, 'Flaky test failed too often, giving up')
473f77af0e6SBram Moolenaar        let v:errors = total_errors
474f77af0e6SBram Moolenaar        break
475f77af0e6SBram Moolenaar      endif
476f77af0e6SBram Moolenaar
477f77af0e6SBram Moolenaar      call add(s:messages, 'Flaky test failed, running it again')
478f77af0e6SBram Moolenaar
479f77af0e6SBram Moolenaar      " Flakiness is often caused by the system being very busy.  Sleep a
480f77af0e6SBram Moolenaar      " couple of seconds to have a higher chance of succeeding the second
481f77af0e6SBram Moolenaar      " time.
48255058601SBram Moolenaar      sleep 2
48355058601SBram Moolenaar
484f77af0e6SBram Moolenaar      let prev_error = v:errors[0]
48542205551SBram Moolenaar      let v:errors = []
4863ed9efc2SBram Moolenaar      let g:run_nr += 1
487f77af0e6SBram Moolenaar
488bfe13cccSBram Moolenaar      call RunTheTest(g:testfunc)
489f77af0e6SBram Moolenaar
490f77af0e6SBram Moolenaar      if len(v:errors) == 0
491f77af0e6SBram Moolenaar        " Test passed on rerun.
492f77af0e6SBram Moolenaar        break
49315e737f7SBram Moolenaar      endif
494f77af0e6SBram Moolenaar    endwhile
49542205551SBram Moolenaar  endif
49642205551SBram Moolenaar
497bfe13cccSBram Moolenaar  call AfterTheTest(g:testfunc)
49842205551SBram Moolenaarendfor
49942205551SBram Moolenaar
50042205551SBram Moolenaarcall FinishTesting()
501cc28e2d0SBram Moolenaar
502cc28e2d0SBram Moolenaar" vim: shiftwidth=2 sts=2 expandtab
503