xref: /vim-8.2.3635/src/testdir/runtest.vim (revision 8ce4b7ed)
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"
166ca6ca48SBram Moolenaar" While working on a test you can make $TEST_NO_RETRY non-empty to not retry:
176ca6ca48SBram Moolenaar"     export TEST_NO_RETRY=yes
186ca6ca48SBram Moolenaar"
19ce436de5SBram Moolenaar" To ignore failure for tests that are known to fail in a certain environment,
20ce436de5SBram Moolenaar" set $TEST_MAY_FAIL to a comma separated list of function names.  E.g. for
21ce436de5SBram Moolenaar" sh/bash:
22ce436de5SBram Moolenaar"     export TEST_MAY_FAIL=Test_channel_one,Test_channel_other
23ce436de5SBram Moolenaar" The failure report will then not be included in the test.log file and
24ce436de5SBram Moolenaar" "make test" will not fail.
25ce436de5SBram Moolenaar"
2643345546SBram Moolenaar" The test script may contain anything, only functions that start with
2743345546SBram Moolenaar" "Test_" are special.  These will be invoked and should contain assert
2843345546SBram Moolenaar" functions.  See test_assert.vim for an example.
2943345546SBram Moolenaar"
3043345546SBram Moolenaar" It is possible to source other files that contain "Test_" functions.  This
3143345546SBram Moolenaar" can speed up testing, since Vim does not need to restart.  But be careful
3243345546SBram Moolenaar" that the tests do not interfere with each other.
3343345546SBram Moolenaar"
3443345546SBram Moolenaar" If an error cannot be detected properly with an assert function add the
3543345546SBram Moolenaar" error to the v:errors list:
3643345546SBram Moolenaar"   call add(v:errors, 'test foo failed: Cannot find xyz')
3743345546SBram Moolenaar"
3843345546SBram Moolenaar" If preparation for each Test_ function is needed, define a SetUp function.
3943345546SBram Moolenaar" It will be called before each Test_ function.
4043345546SBram Moolenaar"
4143345546SBram Moolenaar" If cleanup after each Test_ function is needed, define a TearDown function.
4243345546SBram Moolenaar" It will be called after each Test_ function.
4300af60bbSBram Moolenaar"
4400af60bbSBram Moolenaar" When debugging a test it can be useful to add messages to v:errors:
4500af60bbSBram Moolenaar"	call add(v:errors, "this happened")
4600af60bbSBram Moolenaar
4743345546SBram Moolenaar
4843345546SBram Moolenaar" Without the +eval feature we can't run these tests, bail out.
494686b323SBram Moolenaarso small.vim
5043345546SBram Moolenaar
5118aa13d1SBram Moolenaar" In the GUI we can always change the screen size.
5218aa13d1SBram Moolenaarif has('gui_running')
5318aa13d1SBram Moolenaar  set columns=80 lines=25
5418aa13d1SBram Moolenaarendif
5518aa13d1SBram Moolenaar
5643345546SBram Moolenaar" Check that the screen size is at least 24 x 80 characters.
5743345546SBram Moolenaarif &lines < 24 || &columns < 80
580b5dc644SBram Moolenaar  let error = 'Screen size too small! Tests require at least 24 lines with 80 characters, got ' .. &lines .. ' lines with ' .. &columns .. ' characters'
5943345546SBram Moolenaar  echoerr error
6043345546SBram Moolenaar  split test.log
6143345546SBram Moolenaar  $put =error
6245aa07d3SBram Moolenaar  write
6345aa07d3SBram Moolenaar  split messages
640b5dc644SBram Moolenaar  call append(line('$'), '')
650b5dc644SBram Moolenaar  call append(line('$'), 'From ' . expand('%') . ':')
6645aa07d3SBram Moolenaar  call append(line('$'), error)
6745aa07d3SBram Moolenaar  write
6845aa07d3SBram Moolenaar  qa!
6943345546SBram Moolenaarendif
7043345546SBram Moolenaar
7175ee544fSBram Moolenaarif has('reltime')
7275ee544fSBram Moolenaar  let s:start_time = reltime()
7375ee544fSBram Moolenaarendif
7475ee544fSBram Moolenaar
7589b10421SBram Moolenaar" Common with all tests on all systems.
7689b10421SBram Moolenaarsource setup.vim
7789b10421SBram Moolenaar
78c0662466SBram Moolenaar" For consistency run all tests with 'nocompatible' set.
79c0662466SBram Moolenaar" This also enables use of line continuation.
80c0662466SBram Moolenaarset nocp viminfo+=nviminfo
81c0662466SBram Moolenaar
8230276f2bSBram Moolenaar" Use utf-8 by default, instead of whatever the system default happens to be.
83ed79d1e3SBram Moolenaar" Individual tests can overrule this at the top of the file and use
84ed79d1e3SBram Moolenaar" g:orig_encoding if needed.
85ed79d1e3SBram Moolenaarlet g:orig_encoding = &encoding
86ac105ed3SBram Moolenaarset encoding=utf-8
87ac105ed3SBram Moolenaar
88d8f27b30SBram Moolenaar" REDIR_TEST_TO_NULL has a very permissive SwapExists autocommand which is for
89d8f27b30SBram Moolenaar" the test_name.vim file itself. Replace it here with a more restrictive one,
90d8f27b30SBram Moolenaar" so we still catch mistakes.
91d8f27b30SBram Moolenaarlet s:test_script_fname = expand('%')
92d8f27b30SBram Moolenaarau! SwapExists * call HandleSwapExists()
93d8f27b30SBram Moolenaarfunc HandleSwapExists()
94*8ce4b7edSBram Moolenaar  if exists('g:ignoreSwapExists')
95*8ce4b7edSBram Moolenaar    return
96*8ce4b7edSBram Moolenaar  endif
97b073da89SBram Moolenaar  " Ignore finding a swap file for the test script (the user might be
98d8f27b30SBram Moolenaar  " editing it and do ":make test_name") and the output file.
99b073da89SBram Moolenaar  " Report finding another swap file and chose 'q' to avoid getting stuck.
100d8f27b30SBram Moolenaar  if expand('<afile>') == 'messages' || expand('<afile>') =~ s:test_script_fname
101d8f27b30SBram Moolenaar    let v:swapchoice = 'e'
102b073da89SBram Moolenaar  else
103b073da89SBram Moolenaar    call assert_report('Unexpected swap file: ' .. v:swapname)
104b073da89SBram Moolenaar    let v:swapchoice = 'q'
105d8f27b30SBram Moolenaar  endif
106d8f27b30SBram Moolenaarendfunc
107d8f27b30SBram Moolenaar
1087a073549SBram Moolenaar" Avoid stopping at the "hit enter" prompt
1097a073549SBram Moolenaarset nomore
1107a073549SBram Moolenaar
111c0662466SBram Moolenaar" Output all messages in English.
112c0662466SBram Moolenaarlang mess C
113c0662466SBram Moolenaar
11410e1d01aSBram Moolenaar" suppress menu translation
11510e1d01aSBram Moolenaarif has('gui_running') && exists('did_install_default_menus')
11610e1d01aSBram Moolenaar  source $VIMRUNTIME/delmenu.vim
11710e1d01aSBram Moolenaar  set langmenu=none
11810e1d01aSBram Moolenaar  source $VIMRUNTIME/menu.vim
11910e1d01aSBram Moolenaarendif
12010e1d01aSBram Moolenaar
121f60b796fSBram Moolenaar" Always use forward slashes.
122f60b796fSBram Moolenaarset shellslash
123f60b796fSBram Moolenaar
12428fb79dbSBram Moolenaarlet s:srcdir = expand('%:p:h:h')
12528fb79dbSBram Moolenaar
1262690b5aeSBram Moolenaarif has('win32')
1272690b5aeSBram Moolenaar  " avoid prompt that is long or contains a line break
1282690b5aeSBram Moolenaar  let $PROMPT = '$P$G'
12945df2a01SBram Moolenaar  " On MS-Windows t_md and t_me are Vim specific escape sequences.
13045df2a01SBram Moolenaar  let s:t_bold = "\x1b[1m"
13145df2a01SBram Moolenaar  let s:t_normal = "\x1b[m"
13245df2a01SBram Moolenaarelse
13345df2a01SBram Moolenaar  let s:t_bold = &t_md
13445df2a01SBram Moolenaar  let s:t_normal = &t_me
1352690b5aeSBram Moolenaarendif
1362690b5aeSBram Moolenaar
1378e8df251SBram Moolenaar" Prepare for calling test_garbagecollect_now().
138ebf7dfa6SBram Moolenaarlet v:testing = 1
139ebf7dfa6SBram Moolenaar
14028fb79dbSBram Moolenaar" Support function: get the alloc ID by name.
14128fb79dbSBram Moolenaarfunction GetAllocId(name)
14228fb79dbSBram Moolenaar  exe 'split ' . s:srcdir . '/alloc.h'
143065ee9aeSBram Moolenaar  let top = search('typedef enum')
144065ee9aeSBram Moolenaar  if top == 0
145065ee9aeSBram Moolenaar    call add(v:errors, 'typedef not found in alloc.h')
146065ee9aeSBram Moolenaar  endif
14728fb79dbSBram Moolenaar  let lnum = search('aid_' . a:name . ',')
14828fb79dbSBram Moolenaar  if lnum == 0
14928fb79dbSBram Moolenaar    call add(v:errors, 'Alloc ID ' . a:name . ' not defined')
15028fb79dbSBram Moolenaar  endif
15128fb79dbSBram Moolenaar  close
152065ee9aeSBram Moolenaar  return lnum - top - 1
15328fb79dbSBram Moolenaarendfunc
15428fb79dbSBram Moolenaar
15542205551SBram Moolenaarfunc RunTheTest(test)
156b5760a1cSBram Moolenaar  echo 'Executing ' . a:test
15775ee544fSBram Moolenaar  if has('reltime')
15875ee544fSBram Moolenaar    let func_start = reltime()
15975ee544fSBram Moolenaar  endif
160e5f2a075SBram Moolenaar
161e5f2a075SBram Moolenaar  " Avoid stopping at the "hit enter" prompt
162e5f2a075SBram Moolenaar  set nomore
163e5f2a075SBram Moolenaar
164e5f2a075SBram Moolenaar  " Avoid a three second wait when a message is about to be overwritten by the
165e5f2a075SBram Moolenaar  " mode message.
166e5f2a075SBram Moolenaar  set noshowmode
167e5f2a075SBram Moolenaar
168eb992cb9SBram Moolenaar  " Clear any overrides.
169eb992cb9SBram Moolenaar  call test_override('ALL', 0)
170eb992cb9SBram Moolenaar
171cf1ba35fSBram Moolenaar  " Some tests wipe out buffers.  To be consistent, always wipe out all
172cf1ba35fSBram Moolenaar  " buffers.
173cf1ba35fSBram Moolenaar  %bwipe!
174cf1ba35fSBram Moolenaar
175209d3874SBram Moolenaar  " The test may change the current directory. Save and restore the
176209d3874SBram Moolenaar  " directory after executing the test.
177209d3874SBram Moolenaar  let save_cwd = getcwd()
178209d3874SBram Moolenaar
179b5760a1cSBram Moolenaar  if exists("*SetUp")
180cc28e2d0SBram Moolenaar    try
181b5760a1cSBram Moolenaar      call SetUp()
182cc28e2d0SBram Moolenaar    catch
183cc28e2d0SBram Moolenaar      call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
184cc28e2d0SBram Moolenaar    endtry
185b5760a1cSBram Moolenaar  endif
186b5760a1cSBram Moolenaar
187f204e05aSBram Moolenaar  if a:test =~ 'Test_nocatch_'
188f204e05aSBram Moolenaar    " Function handles errors itself.  This avoids skipping commands after the
189f204e05aSBram Moolenaar    " error.
190f204e05aSBram Moolenaar    exe 'call ' . a:test
191f204e05aSBram Moolenaar  else
192b5760a1cSBram Moolenaar    try
193bfe13cccSBram Moolenaar      au VimLeavePre * call EarlyExit(g:testfunc)
194b5760a1cSBram Moolenaar      exe 'call ' . a:test
1958903676dSBram Moolenaar      au! VimLeavePre
196dac1947bSBram Moolenaar    catch /^\cskipped/
197dac1947bSBram Moolenaar      call add(s:messages, '    Skipped')
198dac1947bSBram Moolenaar      call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '',  ''))
199b5760a1cSBram Moolenaar    catch
200b5760a1cSBram Moolenaar      call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
201b5760a1cSBram Moolenaar    endtry
202f204e05aSBram Moolenaar  endif
2038ad16da7SBram Moolenaar
2048ad16da7SBram Moolenaar  " In case 'insertmode' was set and something went wrong, make sure it is
2058ad16da7SBram Moolenaar  " reset to avoid trouble with anything else.
2068ad16da7SBram Moolenaar  set noinsertmode
207b5760a1cSBram Moolenaar
208b5760a1cSBram Moolenaar  if exists("*TearDown")
209cc28e2d0SBram Moolenaar    try
210b5760a1cSBram Moolenaar      call TearDown()
211cc28e2d0SBram Moolenaar    catch
212cc28e2d0SBram Moolenaar      call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
213cc28e2d0SBram Moolenaar    endtry
214b5760a1cSBram Moolenaar  endif
2157cba71d7SBram Moolenaar
2160b5dc644SBram Moolenaar  " Clear any autocommands and put back the catch-all for SwapExists.
217cf1ba35fSBram Moolenaar  au!
218d8f27b30SBram Moolenaar  au SwapExists * call HandleSwapExists()
219cf1ba35fSBram Moolenaar
220ef6b979bSBram Moolenaar  " Check for and close any stray popup windows.
22105ad5ff0SBram Moolenaar  if has('popupwin')
222ef6b979bSBram Moolenaar    call assert_equal([], popup_list())
22303a9f848SBram Moolenaar    call popup_clear(1)
224ae943150SBram Moolenaar  endif
225ae943150SBram Moolenaar
226ce11de87SBram Moolenaar  " Close any extra tab pages and windows and make the current one not modified.
227ce11de87SBram Moolenaar  while tabpagenr('$') > 1
228cf1ba35fSBram Moolenaar    quit!
229ce11de87SBram Moolenaar  endwhile
230ce11de87SBram Moolenaar
231358308ddSBram Moolenaar  while 1
232358308ddSBram Moolenaar    let wincount = winnr('$')
233358308ddSBram Moolenaar    if wincount == 1
234358308ddSBram Moolenaar      break
235358308ddSBram Moolenaar    endif
2367cba71d7SBram Moolenaar    bwipe!
237358308ddSBram Moolenaar    if wincount == winnr('$')
238358308ddSBram Moolenaar      " Did not manage to close a window.
239358308ddSBram Moolenaar      only!
240358308ddSBram Moolenaar      break
241358308ddSBram Moolenaar    endif
2427cba71d7SBram Moolenaar  endwhile
243209d3874SBram Moolenaar
244209d3874SBram Moolenaar  exe 'cd ' . save_cwd
245640d4f0cSBram Moolenaar
246640d4f0cSBram Moolenaar  let message = 'Executed ' . a:test
247640d4f0cSBram Moolenaar  if has('reltime')
2488d943796SBram Moolenaar    let message ..= repeat(' ', 50 - len(message))
2498d943796SBram Moolenaar    let time = reltime(func_start)
2508d943796SBram Moolenaar    if has('float') && reltimefloat(time) > 0.1
25145df2a01SBram Moolenaar      let message = s:t_bold .. message
2528d943796SBram Moolenaar    endif
2538d943796SBram Moolenaar    let message ..= ' in ' .. reltimestr(time) .. ' seconds'
2548d943796SBram Moolenaar    if has('float') && reltimefloat(time) > 0.1
25545df2a01SBram Moolenaar      let message ..= s:t_normal
2568d943796SBram Moolenaar    endif
257640d4f0cSBram Moolenaar  endif
258640d4f0cSBram Moolenaar  call add(s:messages, message)
259640d4f0cSBram Moolenaar  let s:done += 1
260b5760a1cSBram Moolenaarendfunc
26128fb79dbSBram Moolenaar
262ce436de5SBram Moolenaarfunc AfterTheTest(func_name)
26343345546SBram Moolenaar  if len(v:errors) > 0
264ce436de5SBram Moolenaar    if match(s:may_fail_list, '^' .. a:func_name) >= 0
265ce436de5SBram Moolenaar      let s:fail_expected += 1
266bfe13cccSBram Moolenaar      call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
267ce436de5SBram Moolenaar      call extend(s:errors_expected, v:errors)
268ce436de5SBram Moolenaar    else
26900af60bbSBram Moolenaar      let s:fail += 1
270bfe13cccSBram Moolenaar      call add(s:errors, 'Found errors in ' . g:testfunc . ':')
27100af60bbSBram Moolenaar      call extend(s:errors, v:errors)
272ce436de5SBram Moolenaar    endif
27343345546SBram Moolenaar    let v:errors = []
27443345546SBram Moolenaar  endif
27542205551SBram Moolenaarendfunc
27642205551SBram Moolenaar
2778903676dSBram Moolenaarfunc EarlyExit(test)
2788903676dSBram Moolenaar  " It's OK for the test we use to test the quit detection.
2798903676dSBram Moolenaar  if a:test != 'Test_zz_quit_detected()'
2808903676dSBram Moolenaar    call add(v:errors, 'Test caused Vim to exit: ' . a:test)
2818903676dSBram Moolenaar  endif
2828903676dSBram Moolenaar
2838903676dSBram Moolenaar  call FinishTesting()
2848903676dSBram Moolenaarendfunc
2858903676dSBram Moolenaar
28642205551SBram Moolenaar" This function can be called by a test if it wants to abort testing.
28742205551SBram Moolenaarfunc FinishTesting()
288ce436de5SBram Moolenaar  call AfterTheTest('')
28943345546SBram Moolenaar
290fc4ad616SBram Moolenaar  " Don't write viminfo on exit.
291fc4ad616SBram Moolenaar  set viminfo=
292fc4ad616SBram Moolenaar
293d1ee0043SBram Moolenaar  " Clean up files created by setup.vim
294d1ee0043SBram Moolenaar  call delete('XfakeHOME', 'rf')
295d1ee0043SBram Moolenaar
296ce436de5SBram Moolenaar  if s:fail == 0 && s:fail_expected == 0
29743345546SBram Moolenaar    " Success, create the .res file so that make knows it's done.
29800af60bbSBram Moolenaar    exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
29943345546SBram Moolenaar    write
30043345546SBram Moolenaar  endif
30143345546SBram Moolenaar
30200af60bbSBram Moolenaar  if len(s:errors) > 0
30343345546SBram Moolenaar    " Append errors to test.log
30443345546SBram Moolenaar    split test.log
30543345546SBram Moolenaar    call append(line('$'), '')
30600af60bbSBram Moolenaar    call append(line('$'), 'From ' . g:testname . ':')
30700af60bbSBram Moolenaar    call append(line('$'), s:errors)
30843345546SBram Moolenaar    write
30943345546SBram Moolenaar  endif
31043345546SBram Moolenaar
31129f9ed20SBram Moolenaar  if s:done == 0
3127b666c78SBram Moolenaar    if s:filtered > 0
3137b666c78SBram Moolenaar      let message = "NO tests match $TEST_FILTER: '" .. $TEST_FILTER .. "'"
31429f9ed20SBram Moolenaar    else
3157b666c78SBram Moolenaar      let message = 'NO tests executed'
3167b666c78SBram Moolenaar    endif
3177b666c78SBram Moolenaar  else
3187b666c78SBram Moolenaar    if s:filtered > 0
3197b666c78SBram Moolenaar      call add(s:messages, "Filtered " .. s:filtered .. " tests with $TEST_FILTER")
3207b666c78SBram Moolenaar    endif
32100af60bbSBram Moolenaar    let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
32229f9ed20SBram Moolenaar  endif
3237b666c78SBram Moolenaar  if s:done > 0 && has('reltime')
32445df2a01SBram Moolenaar    let message = s:t_bold .. message .. repeat(' ', 40 - len(message))
32575ee544fSBram Moolenaar    let message ..= ' in ' .. reltimestr(reltime(s:start_time)) .. ' seconds'
32645df2a01SBram Moolenaar    let message ..= s:t_normal
32775ee544fSBram Moolenaar  endif
328096c8bb4SBram Moolenaar  echo message
32900af60bbSBram Moolenaar  call add(s:messages, message)
33000af60bbSBram Moolenaar  if s:fail > 0
33100af60bbSBram Moolenaar    let message = s:fail . ' FAILED:'
332096c8bb4SBram Moolenaar    echo message
33300af60bbSBram Moolenaar    call add(s:messages, message)
33400af60bbSBram Moolenaar    call extend(s:messages, s:errors)
33543345546SBram Moolenaar  endif
336ce436de5SBram Moolenaar  if s:fail_expected > 0
337ce436de5SBram Moolenaar    let message = s:fail_expected . ' FAILED (matching $TEST_MAY_FAIL):'
338ce436de5SBram Moolenaar    echo message
339ce436de5SBram Moolenaar    call add(s:messages, message)
340ce436de5SBram Moolenaar    call extend(s:messages, s:errors_expected)
341ce436de5SBram Moolenaar  endif
34243345546SBram Moolenaar
343dac1947bSBram Moolenaar  " Add SKIPPED messages
344dac1947bSBram Moolenaar  call extend(s:messages, s:skipped)
345dac1947bSBram Moolenaar
346dac1947bSBram Moolenaar  " Append messages to the file "messages"
347096c8bb4SBram Moolenaar  split messages
348096c8bb4SBram Moolenaar  call append(line('$'), '')
34900af60bbSBram Moolenaar  call append(line('$'), 'From ' . g:testname . ':')
35000af60bbSBram Moolenaar  call append(line('$'), s:messages)
351096c8bb4SBram Moolenaar  write
352096c8bb4SBram Moolenaar
35343345546SBram Moolenaar  qall!
35442205551SBram Moolenaarendfunc
35542205551SBram Moolenaar
35642205551SBram Moolenaar" Source the test script.  First grab the file name, in case the script
35742205551SBram Moolenaar" navigates away.  g:testname can be used by the tests.
35842205551SBram Moolenaarlet g:testname = expand('%')
35942205551SBram Moolenaarlet s:done = 0
36042205551SBram Moolenaarlet s:fail = 0
361ce436de5SBram Moolenaarlet s:fail_expected = 0
36242205551SBram Moolenaarlet s:errors = []
363ce436de5SBram Moolenaarlet s:errors_expected = []
36442205551SBram Moolenaarlet s:messages = []
36542205551SBram Moolenaarlet s:skipped = []
36642205551SBram Moolenaarif expand('%') =~ 'test_vimscript.vim'
367ce436de5SBram Moolenaar  " this test has intentional errors, don't use try/catch.
36842205551SBram Moolenaar  source %
36942205551SBram Moolenaarelse
37042205551SBram Moolenaar  try
37142205551SBram Moolenaar    source %
3729c0cec65SBram Moolenaar  catch /^\cskipped/
3739c0cec65SBram Moolenaar    call add(s:messages, '    Skipped')
3749c0cec65SBram Moolenaar    call add(s:skipped, 'SKIPPED ' . expand('%') . ': ' . substitute(v:exception, '^\S*\s\+', '',  ''))
37542205551SBram Moolenaar  catch
37642205551SBram Moolenaar    let s:fail += 1
37742205551SBram Moolenaar    call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
37842205551SBram Moolenaar  endtry
37942205551SBram Moolenaarendif
38042205551SBram Moolenaar
38142205551SBram Moolenaar" Names of flaky tests.
382dbc0d216SBram Moolenaarlet s:flaky_tests = [
3830d0c3ca0SBram Moolenaar      \ 'Test_autocmd_SafeState()',
38442205551SBram Moolenaar      \ 'Test_client_server()',
38542205551SBram Moolenaar      \ 'Test_close_and_exit_cb()',
386d80232beSBram Moolenaar      \ 'Test_close_output_buffer()',
38742205551SBram Moolenaar      \ 'Test_collapse_buffers()',
38865873846SBram Moolenaar      \ 'Test_cwd()',
389218959bcSBram Moolenaar      \ 'Test_diff_screen()',
39042205551SBram Moolenaar      \ 'Test_exit_callback_interval()',
391ea94c855SBram Moolenaar      \ 'Test_map_timeout_with_timer_interrupt()',
3921eca6f13SBram Moolenaar      \ 'Test_out_cb()',
39342205551SBram Moolenaar      \ 'Test_pipe_through_sort_all()',
39442205551SBram Moolenaar      \ 'Test_pipe_through_sort_some()',
39544f0bd87SBram Moolenaar      \ 'Test_popup_and_window_resize()',
39642205551SBram Moolenaar      \ 'Test_quoteplus()',
3977dd48506SBram Moolenaar      \ 'Test_quotestar()',
39842205551SBram Moolenaar      \ 'Test_reltime()',
3993c8cd4a1SBram Moolenaar      \ 'Test_state()',
400f204e05aSBram Moolenaar      \ 'Test_terminal_composing_unicode()',
4013876789bSBram Moolenaar      \ 'Test_terminal_does_not_truncate_last_newlines()',
4023615abb6SBram Moolenaar      \ 'Test_terminal_no_cmd()',
40375a60f74SBram Moolenaar      \ 'Test_terminal_noblock()',
4047dd88c51SBram Moolenaar      \ 'Test_terminal_redir_file()',
405e219f73eSBram Moolenaar      \ 'Test_termwinscroll()',
4069a2fddcfSBram Moolenaar      \ 'Test_timer_oneshot()',
4079a2fddcfSBram Moolenaar      \ 'Test_timer_paused()',
4089a2fddcfSBram Moolenaar      \ 'Test_timer_repeat_many()',
4099a2fddcfSBram Moolenaar      \ 'Test_timer_repeat_three()',
4109a2fddcfSBram Moolenaar      \ 'Test_timer_stop_all_in_callback()',
4119a2fddcfSBram Moolenaar      \ 'Test_timer_stop_in_callback()',
4129a2fddcfSBram Moolenaar      \ 'Test_timer_with_partial_callback()',
41342205551SBram Moolenaar      \ ]
41442205551SBram Moolenaar
41542205551SBram Moolenaar" Locate Test_ functions and execute them.
41642205551SBram Moolenaarredir @q
41742205551SBram Moolenaarsilent function /^Test_
41842205551SBram Moolenaarredir END
41961a6d4e4SBram Moolenaarlet s:tests = split(substitute(@q, '\(function\|def\) \(\k*()\)', '\2', 'g'))
42042205551SBram Moolenaar
42142205551SBram Moolenaar" If there is an extra argument filter the function names against it.
42242205551SBram Moolenaarif argc() > 1
42342205551SBram Moolenaar  let s:tests = filter(s:tests, 'v:val =~ argv(1)')
42442205551SBram Moolenaarendif
42542205551SBram Moolenaar
426a7f6c3cfSBram Moolenaar" If the environment variable $TEST_FILTER is set then filter the function
427a7f6c3cfSBram Moolenaar" names against it.
4287b666c78SBram Moolenaarlet s:filtered = 0
429a7f6c3cfSBram Moolenaarif $TEST_FILTER != ''
4307b666c78SBram Moolenaar  let s:filtered = len(s:tests)
431a7f6c3cfSBram Moolenaar  let s:tests = filter(s:tests, 'v:val =~ $TEST_FILTER')
4327b666c78SBram Moolenaar  let s:filtered -= len(s:tests)
433a7f6c3cfSBram Moolenaarendif
434a7f6c3cfSBram Moolenaar
435ce436de5SBram Moolenaarlet s:may_fail_list = []
436ce436de5SBram Moolenaarif $TEST_MAY_FAIL != ''
437bfe13cccSBram Moolenaar  " Split the list at commas and add () to make it match g:testfunc.
438ce436de5SBram Moolenaar  let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
439ce436de5SBram Moolenaarendif
440ce436de5SBram Moolenaar
44142205551SBram Moolenaar" Execute the tests in alphabetical order.
442bfe13cccSBram Moolenaarfor g:testfunc in sort(s:tests)
4434a6fcf80SBram Moolenaar  " Silence, please!
4444a6fcf80SBram Moolenaar  set belloff=all
445f77af0e6SBram Moolenaar  let prev_error = ''
446f77af0e6SBram Moolenaar  let total_errors = []
4473ed9efc2SBram Moolenaar  let g:run_nr = 1
4484a6fcf80SBram Moolenaar
449bfe13cccSBram Moolenaar  " A test can set g:test_is_flaky to retry running the test.
450bfe13cccSBram Moolenaar  let g:test_is_flaky = 0
4513cdcb090SBram Moolenaar
452bfe13cccSBram Moolenaar  call RunTheTest(g:testfunc)
45342205551SBram Moolenaar
454f77af0e6SBram Moolenaar  " Repeat a flaky test.  Give up when:
4556ca6ca48SBram Moolenaar  " - $TEST_NO_RETRY is not empty
456f77af0e6SBram Moolenaar  " - it fails again with the same message
4571bc353b6SBram Moolenaar  " - it fails five times (with a different message)
458dbc0d216SBram Moolenaar  if len(v:errors) > 0
459622b3568SBram Moolenaar        \ && $TEST_NO_RETRY == ''
460bfe13cccSBram Moolenaar        \ && (index(s:flaky_tests, g:testfunc) >= 0
461bfe13cccSBram Moolenaar        \      || g:test_is_flaky)
462f77af0e6SBram Moolenaar    while 1
463bfe13cccSBram Moolenaar      call add(s:messages, 'Found errors in ' . g:testfunc . ':')
4646caf606bSBram Moolenaar      call extend(s:messages, v:errors)
46515e737f7SBram Moolenaar
4663ed9efc2SBram Moolenaar      call add(total_errors, 'Run ' . g:run_nr . ':')
467f77af0e6SBram Moolenaar      call extend(total_errors, v:errors)
468f77af0e6SBram Moolenaar
4693ed9efc2SBram Moolenaar      if g:run_nr == 5 || prev_error == v:errors[0]
470f77af0e6SBram Moolenaar        call add(total_errors, 'Flaky test failed too often, giving up')
471f77af0e6SBram Moolenaar        let v:errors = total_errors
472f77af0e6SBram Moolenaar        break
473f77af0e6SBram Moolenaar      endif
474f77af0e6SBram Moolenaar
475f77af0e6SBram Moolenaar      call add(s:messages, 'Flaky test failed, running it again')
476f77af0e6SBram Moolenaar
477f77af0e6SBram Moolenaar      " Flakiness is often caused by the system being very busy.  Sleep a
478f77af0e6SBram Moolenaar      " couple of seconds to have a higher chance of succeeding the second
479f77af0e6SBram Moolenaar      " time.
48055058601SBram Moolenaar      sleep 2
48155058601SBram Moolenaar
482f77af0e6SBram Moolenaar      let prev_error = v:errors[0]
48342205551SBram Moolenaar      let v:errors = []
4843ed9efc2SBram Moolenaar      let g:run_nr += 1
485f77af0e6SBram Moolenaar
486bfe13cccSBram Moolenaar      call RunTheTest(g:testfunc)
487f77af0e6SBram Moolenaar
488f77af0e6SBram Moolenaar      if len(v:errors) == 0
489f77af0e6SBram Moolenaar        " Test passed on rerun.
490f77af0e6SBram Moolenaar        break
49115e737f7SBram Moolenaar      endif
492f77af0e6SBram Moolenaar    endwhile
49342205551SBram Moolenaar  endif
49442205551SBram Moolenaar
495bfe13cccSBram Moolenaar  call AfterTheTest(g:testfunc)
49642205551SBram Moolenaarendfor
49742205551SBram Moolenaar
49842205551SBram Moolenaarcall FinishTesting()
499cc28e2d0SBram Moolenaar
500cc28e2d0SBram Moolenaar" vim: shiftwidth=2 sts=2 expandtab
501