blob: ded31975b80ba7a76a0a25bb898d3260cf87384c [file] [log] [blame]
Bram Moolenaar43345542015-11-29 17:35:35 +01001" 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"
Bram Moolenaarbefb3662016-02-20 14:41:40 +01005" To execute only specific test functions, add a second argument. It will be
Bram Moolenaare219f732019-11-30 15:34:08 +01006" matched against the names of the Test_ function. E.g.:
Bram Moolenaarbefb3662016-02-20 14:41:40 +01007" ../vim -u NONE -S runtest.vim test_channel.vim open_delay
8" The output can be found in the "messages" file.
9"
Bram Moolenaarce436de2020-03-21 15:17:20 +010010" If the environment variable $TEST_FILTER is set then only test functions
11" matching this pattern are executed. E.g. for sh/bash:
12" export TEST_FILTER=Test_channel
13" For csh:
14" setenv TEST_FILTER Test_channel
15"
Bram Moolenaardae453f2021-08-07 17:20:16 +020016" If the environment variable $TEST_SKIP_PAT is set then test functions
17" matching this pattern will be skipped. It's the opposite of $TEST_FILTER.
18"
Bram Moolenaar6ca6ca42020-07-27 19:47:07 +020019" While working on a test you can make $TEST_NO_RETRY non-empty to not retry:
20" export TEST_NO_RETRY=yes
21"
Bram Moolenaarce436de2020-03-21 15:17:20 +010022" To ignore failure for tests that are known to fail in a certain environment,
23" set $TEST_MAY_FAIL to a comma separated list of function names. E.g. for
24" sh/bash:
25" export TEST_MAY_FAIL=Test_channel_one,Test_channel_other
26" The failure report will then not be included in the test.log file and
27" "make test" will not fail.
28"
Bram Moolenaar43345542015-11-29 17:35:35 +010029" The test script may contain anything, only functions that start with
30" "Test_" are special. These will be invoked and should contain assert
31" functions. See test_assert.vim for an example.
32"
33" It is possible to source other files that contain "Test_" functions. This
34" can speed up testing, since Vim does not need to restart. But be careful
35" that the tests do not interfere with each other.
36"
37" If an error cannot be detected properly with an assert function add the
38" error to the v:errors list:
39" call add(v:errors, 'test foo failed: Cannot find xyz')
40"
41" If preparation for each Test_ function is needed, define a SetUp function.
42" It will be called before each Test_ function.
43"
44" If cleanup after each Test_ function is needed, define a TearDown function.
45" It will be called after each Test_ function.
Bram Moolenaar00af60b2016-02-13 14:06:14 +010046"
47" When debugging a test it can be useful to add messages to v:errors:
Bram Moolenaar8ad16da2019-01-06 15:29:57 +010048" call add(v:errors, "this happened")
Bram Moolenaar00af60b2016-02-13 14:06:14 +010049
Bram Moolenaar43345542015-11-29 17:35:35 +010050
51" Without the +eval feature we can't run these tests, bail out.
Bram Moolenaarb96a32e2020-08-13 18:59:55 +020052silent! while 0
53 qa!
54silent! endwhile
Bram Moolenaar43345542015-11-29 17:35:35 +010055
Bram Moolenaar18aa13d2020-07-11 13:09:36 +020056" In the GUI we can always change the screen size.
57if has('gui_running')
58 set columns=80 lines=25
59endif
60
Bram Moolenaar43345542015-11-29 17:35:35 +010061" Check that the screen size is at least 24 x 80 characters.
Bram Moolenaar94722c52023-01-28 19:19:03 +000062if &lines < 24 || &columns < 80
Bram Moolenaar0b5dc642019-08-11 22:56:15 +020063 let error = 'Screen size too small! Tests require at least 24 lines with 80 characters, got ' .. &lines .. ' lines with ' .. &columns .. ' characters'
Bram Moolenaar43345542015-11-29 17:35:35 +010064 echoerr error
65 split test.log
66 $put =error
Bram Moolenaar45aa07d2019-06-15 18:20:38 +020067 write
68 split messages
Bram Moolenaar0b5dc642019-08-11 22:56:15 +020069 call append(line('$'), '')
70 call append(line('$'), 'From ' . expand('%') . ':')
Bram Moolenaar45aa07d2019-06-15 18:20:38 +020071 call append(line('$'), error)
72 write
73 qa!
Bram Moolenaar43345542015-11-29 17:35:35 +010074endif
75
Bram Moolenaar75ee5442019-06-06 18:05:25 +020076if has('reltime')
Bram Moolenaarb9093d52022-09-23 19:42:31 +010077 let s:run_start_time = reltime()
78
79 if !filereadable('starttime')
80 " first test, store the overall test starting time
81 let s:test_start_time = localtime()
82 call writefile([string(s:test_start_time)], 'starttime')
83 else
84 " second or later test, read the overall test starting time
85 let s:test_start_time = readfile('starttime')[0]->str2nr()
86 endif
Bram Moolenaar75ee5442019-06-06 18:05:25 +020087endif
88
Bakudankun29f3a452021-12-11 12:28:08 +000089" Always use forward slashes.
90set shellslash
91
Bram Moolenaar89b10422016-07-12 22:51:22 +020092" Common with all tests on all systems.
93source setup.vim
94
Bram Moolenaar7c2beb42023-07-08 00:25:56 +010095" Needed for RunningWithValgrind().
96source shared.vim
97
Bram Moolenaarc0662462015-12-30 15:49:05 +010098" For consistency run all tests with 'nocompatible' set.
99" This also enables use of line continuation.
100set nocp viminfo+=nviminfo
101
Bram Moolenaar30276f22019-01-24 17:59:39 +0100102" Use utf-8 by default, instead of whatever the system default happens to be.
Bram Moolenaared79d1e2019-02-22 14:38:58 +0100103" Individual tests can overrule this at the top of the file and use
104" g:orig_encoding if needed.
105let g:orig_encoding = &encoding
Bram Moolenaar30276f22019-01-24 17:59:39 +0100106set encoding=utf-8
Bram Moolenaarac105ed2016-07-21 20:33:32 +0200107
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200108" REDIR_TEST_TO_NULL has a very permissive SwapExists autocommand which is for
109" the test_name.vim file itself. Replace it here with a more restrictive one,
110" so we still catch mistakes.
Christian Brabandt4b2c8042021-11-03 22:31:44 +0000111if has("win32")
112 " replace any '/' directory separators by '\\'
113 let s:test_script_fname = substitute(expand('%'), '/', '\\', 'g')
114else
115 let s:test_script_fname = expand('%')
116endif
Bram Moolenaarabc81302023-06-04 16:55:27 +0100117
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200118au! SwapExists * call HandleSwapExists()
119func HandleSwapExists()
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +0200120 if exists('g:ignoreSwapExists')
Bram Moolenaarabc81302023-06-04 16:55:27 +0100121 if type(g:ignoreSwapExists) == v:t_string
122 let v:swapchoice = g:ignoreSwapExists
123 endif
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +0200124 return
125 endif
Bram Moolenaarb073da82019-07-13 14:47:26 +0200126 " Ignore finding a swap file for the test script (the user might be
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200127 " editing it and do ":make test_name") and the output file.
Bram Moolenaarb073da82019-07-13 14:47:26 +0200128 " Report finding another swap file and chose 'q' to avoid getting stuck.
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200129 if expand('<afile>') == 'messages' || expand('<afile>') =~ s:test_script_fname
130 let v:swapchoice = 'e'
Bram Moolenaarb073da82019-07-13 14:47:26 +0200131 else
132 call assert_report('Unexpected swap file: ' .. v:swapname)
133 let v:swapchoice = 'q'
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200134 endif
135endfunc
136
Bram Moolenaar7a073542017-02-01 23:17:36 +0100137" Avoid stopping at the "hit enter" prompt
138set nomore
139
Bram Moolenaarc0662462015-12-30 15:49:05 +0100140" Output all messages in English.
141lang mess C
142
Bram Moolenaar10e1d012020-07-18 22:03:11 +0200143" suppress menu translation
144if has('gui_running') && exists('did_install_default_menus')
145 source $VIMRUNTIME/delmenu.vim
146 set langmenu=none
147 source $VIMRUNTIME/menu.vim
148endif
149
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100150let s:srcdir = expand('%:p:h:h')
151
Bram Moolenaar2690b5a2020-07-22 18:14:58 +0200152if has('win32')
153 " avoid prompt that is long or contains a line break
154 let $PROMPT = '$P$G'
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200155 " On MS-Windows t_md and t_me are Vim specific escape sequences.
156 let s:t_bold = "\x1b[1m"
157 let s:t_normal = "\x1b[m"
158else
159 let s:t_bold = &t_md
160 let s:t_normal = &t_me
Bram Moolenaar2690b5a2020-07-22 18:14:58 +0200161endif
162
Bram Moolenaar4b2ce122020-11-21 21:41:41 +0100163if has('mac')
Dominique Pelle923dce22021-11-21 11:36:04 +0000164 " In macOS, when starting a shell in a terminal, a bash deprecation warning
Bram Moolenaar4b2ce122020-11-21 21:41:41 +0100165 " message is displayed. This breaks the terminal test. Disable the warning
166 " message.
167 let $BASH_SILENCE_DEPRECATION_WARNING = 1
168endif
169
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000170
Bram Moolenaar8e8df252016-05-25 21:23:21 +0200171" Prepare for calling test_garbagecollect_now().
Bram Moolenaarebf7dfa2016-04-14 12:46:51 +0200172let v:testing = 1
173
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100174" By default, copy each buffer line into allocated memory, so that valgrind can
175" detect accessing memory before and after it.
176call test_override('alloc_lines', 1)
177
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100178" Support function: get the alloc ID by name.
179function GetAllocId(name)
180 exe 'split ' . s:srcdir . '/alloc.h'
Bram Moolenaar065ee9a2016-01-15 20:53:38 +0100181 let top = search('typedef enum')
182 if top == 0
183 call add(v:errors, 'typedef not found in alloc.h')
184 endif
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100185 let lnum = search('aid_' . a:name . ',')
186 if lnum == 0
187 call add(v:errors, 'Alloc ID ' . a:name . ' not defined')
188 endif
189 close
Bram Moolenaar065ee9a2016-01-15 20:53:38 +0100190 return lnum - top - 1
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100191endfunc
192
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000193" Get the list of swap files in the current directory.
194func s:GetSwapFileList()
195 let save_dir = &directory
196 let &directory = '.'
197 let files = swapfilelist()
198 let &directory = save_dir
199
200 " remove a match with runtest.vim
201 let idx = indexof(files, 'v:val =~ "runtest.vim."')
202 if idx >= 0
203 call remove(files, idx)
204 endif
205
206 return files
207endfunc
208
Bram Moolenaar6572a902022-12-06 14:21:09 +0000209" A previous (failed) test run may have left swap files behind. Delete them
210" before running tests again, they might interfere.
211for name in s:GetSwapFileList()
212 call delete(name)
213endfor
zeertzjqa36acb72023-10-21 11:50:26 +0200214unlet! name
Bram Moolenaar6572a902022-12-06 14:21:09 +0000215
216
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100217" Invoked when a test takes too much time.
218func TestTimeout(id)
219 split test.log
220 call append(line('$'), '')
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100221
222 let text = 'Test timed out: ' .. g:testfunc
223 if g:timeout_start > 0
224 let text ..= strftime(' after %s seconds', localtime() - g:timeout_start)
225 endif
226 call append(line('$'), text)
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100227 write
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100228 call add(v:errors, text)
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100229
230 cquit! 42
231endfunc
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100232let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100233
Bram Moolenaar42205552017-03-18 19:42:22 +0100234func RunTheTest(test)
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100235 let prefix = ''
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200236 if has('reltime')
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100237 let prefix = strftime('%M:%S', localtime() - s:test_start_time) .. ' '
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100238 let g:func_start = reltime()
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200239 endif
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100240 echoconsole prefix .. 'Executing ' .. a:test
Bram Moolenaare5f2a072017-02-01 22:31:49 +0100241
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100242 if has('timers')
243 " No test should take longer than 30 seconds. If it takes longer we
244 " assume we are stuck and need to break out.
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100245 let test_timeout_timer =
246 \ timer_start(RunningWithValgrind() ? 50000 : 30000, 'TestTimeout')
247 let g:timeout_start = localtime()
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100248 endif
249
Bram Moolenaare5f2a072017-02-01 22:31:49 +0100250 " Avoid stopping at the "hit enter" prompt
251 set nomore
252
253 " Avoid a three second wait when a message is about to be overwritten by the
254 " mode message.
255 set noshowmode
256
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100257 " Clear any overrides, except "alloc_lines".
Bram Moolenaareb992cb2017-03-09 18:20:16 +0100258 call test_override('ALL', 0)
259
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200260 " Some tests wipe out buffers. To be consistent, always wipe out all
261 " buffers.
262 %bwipe!
263
Bram Moolenaar209d3872017-11-16 21:52:51 +0100264 " The test may change the current directory. Save and restore the
265 " directory after executing the test.
266 let save_cwd = getcwd()
267
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100268 if exists("*SetUp")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100269 try
270 call SetUp()
271 catch
272 call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
273 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100274 endif
275
zeertzjqa0e1f062023-10-18 11:50:37 +0200276 let skipped = v:false
277
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100278 au VimLeavePre * call EarlyExit(g:testfunc)
Bram Moolenaarf204e052017-10-26 17:14:01 +0200279 if a:test =~ 'Test_nocatch_'
280 " Function handles errors itself. This avoids skipping commands after the
281 " error.
Bram Moolenaar776b9542021-03-10 22:27:48 +0100282 let g:skipped_reason = ''
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100283 exe 'call ' . a:test
Bram Moolenaar776b9542021-03-10 22:27:48 +0100284 if g:skipped_reason != ''
285 call add(s:messages, ' Skipped')
286 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason)
zeertzjqa0e1f062023-10-18 11:50:37 +0200287 let skipped = v:true
Bram Moolenaar776b9542021-03-10 22:27:48 +0100288 endif
Bram Moolenaarf204e052017-10-26 17:14:01 +0200289 else
290 try
291 exe 'call ' . a:test
292 catch /^\cskipped/
293 call add(s:messages, ' Skipped')
294 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
zeertzjqa0e1f062023-10-18 11:50:37 +0200295 let skipped = v:true
Bram Moolenaarf204e052017-10-26 17:14:01 +0200296 catch
297 call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
298 endtry
299 endif
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100300 au! VimLeavePre
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100301
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100302 if a:test =~ '_terminal_'
303 " Terminal tests sometimes hang, give extra information
304 echoconsole 'After executing ' .. a:test
305 endif
306
Bram Moolenaar8ad16da2019-01-06 15:29:57 +0100307 " In case 'insertmode' was set and something went wrong, make sure it is
308 " reset to avoid trouble with anything else.
309 set noinsertmode
310
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100311 if exists("*TearDown")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100312 try
313 call TearDown()
314 catch
315 call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
316 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100317 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200318
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100319 if has('timers')
320 call timer_stop(test_timeout_timer)
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100321 let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100322 endif
323
Bram Moolenaar0b5dc642019-08-11 22:56:15 +0200324 " Clear any autocommands and put back the catch-all for SwapExists.
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200325 au!
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200326 au SwapExists * call HandleSwapExists()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200327
Bram Moolenaaref6b9792020-05-13 16:34:15 +0200328 " Check for and close any stray popup windows.
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100329 if has('popupwin')
Bram Moolenaarf05a1e52022-08-02 11:48:53 +0100330 call assert_equal([], popup_list(), 'Popup is still present')
Bram Moolenaar03a9f842020-05-13 13:40:16 +0200331 call popup_clear(1)
Bram Moolenaarae943152019-06-16 22:54:14 +0200332 endif
333
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100334 if filereadable('guidialogfile')
Bram Moolenaar217ea512022-06-14 17:13:59 +0100335 call add(v:errors, "Unexpected dialog: " .. readfile('guidialogfile')->join('<NL>'))
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100336 call delete('guidialogfile')
337 endif
338
Bram Moolenaarce11de82017-10-26 22:00:00 +0200339 " Close any extra tab pages and windows and make the current one not modified.
340 while tabpagenr('$') > 1
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200341 let winid = win_getid()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200342 quit!
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200343 if winid == win_getid()
344 echoerr 'Could not quit window'
345 break
346 endif
Bram Moolenaarce11de82017-10-26 22:00:00 +0200347 endwhile
348
Bram Moolenaar358308d2016-08-24 21:21:26 +0200349 while 1
350 let wincount = winnr('$')
351 if wincount == 1
352 break
353 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200354 bwipe!
Bram Moolenaar358308d2016-08-24 21:21:26 +0200355 if wincount == winnr('$')
356 " Did not manage to close a window.
357 only!
358 break
359 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200360 endwhile
Bram Moolenaar209d3872017-11-16 21:52:51 +0100361
362 exe 'cd ' . save_cwd
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200363
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100364 if a:test =~ '_terminal_'
365 " Terminal tests sometimes hang, give extra information
366 echoconsole 'Finished ' . a:test
367 endif
368
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200369 let message = 'Executed ' . a:test
370 if has('reltime')
Bram Moolenaar8d943792020-06-21 20:39:37 +0200371 let message ..= repeat(' ', 50 - len(message))
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100372 let time = reltime(g:func_start)
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100373 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200374 let message = s:t_bold .. message
Bram Moolenaar8d943792020-06-21 20:39:37 +0200375 endif
376 let message ..= ' in ' .. reltimestr(time) .. ' seconds'
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100377 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200378 let message ..= s:t_normal
Bram Moolenaar8d943792020-06-21 20:39:37 +0200379 endif
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200380 endif
381 call add(s:messages, message)
382 let s:done += 1
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000383
Bram Moolenaare5eae822022-12-08 16:30:16 +0000384 " close any split windows
385 while winnr('$') > 1
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100386 noswapfile bwipe!
Bram Moolenaare5eae822022-12-08 16:30:16 +0000387 endwhile
388
Bram Moolenaar23526d22022-12-05 15:50:41 +0000389 " May be editing some buffer, wipe it out. Then we may end up in another
390 " buffer, continue until we end up in an empty no-name buffer without a swap
391 " file.
392 while bufname() != '' || execute('swapname') !~ 'No swap file'
Bram Moolenaarfa2533c2022-12-05 20:58:04 +0000393 let bn = bufnr()
394
395 noswapfile bwipe!
396
397 if bn == bufnr()
398 " avoid getting stuck in the same buffer
399 break
400 endif
Bram Moolenaar23526d22022-12-05 15:50:41 +0000401 endwhile
402
zeertzjqa0e1f062023-10-18 11:50:37 +0200403 if !skipped
404 " Check if the test has left any swap files behind. Delete them before
405 " running tests again, they might interfere.
406 let swapfiles = s:GetSwapFileList()
407 if len(swapfiles) > 0
408 call add(s:messages, "Found swap files: " .. string(swapfiles))
409 for name in swapfiles
410 call delete(name)
411 endfor
412 endif
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000413 endif
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100414endfunc
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100415
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200416function Delete_Xtest_Files()
417 for file in glob('X*', v:false, v:true)
418 if file ==? 'XfakeHOME'
419 " Clean up files created by setup.vim
420 call delete('XfakeHOME', 'rf')
421 continue
422 endif
423 " call add(v:errors, file .. " exists when it shouldn't, trying to delete it!")
424 call delete(file)
425 if !empty(glob(file, v:false, v:true))
426 " call add(v:errors, file .. " still exists after trying to delete it!")
427 if has('unix')
428 call system('rm -rf ' .. file)
429 endif
430 endif
431 endfor
432endfunc
433
Bram Moolenaarce436de2020-03-21 15:17:20 +0100434func AfterTheTest(func_name)
Bram Moolenaar42205552017-03-18 19:42:22 +0100435 if len(v:errors) > 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100436 if match(s:may_fail_list, '^' .. a:func_name) >= 0
437 let s:fail_expected += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200438 call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100439 call extend(s:errors_expected, v:errors)
440 else
441 let s:fail += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200442 call add(s:errors, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100443 call extend(s:errors, v:errors)
444 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100445 let v:errors = []
446 endif
447endfunc
448
Bram Moolenaar89036762018-06-12 14:58:39 +0200449func EarlyExit(test)
450 " It's OK for the test we use to test the quit detection.
451 if a:test != 'Test_zz_quit_detected()'
Bram Moolenaar1c67f3a2021-12-30 13:32:09 +0000452 call add(v:errors, v:errmsg)
Bram Moolenaar89036762018-06-12 14:58:39 +0200453 call add(v:errors, 'Test caused Vim to exit: ' . a:test)
454 endif
455
456 call FinishTesting()
457endfunc
458
Bram Moolenaar42205552017-03-18 19:42:22 +0100459" This function can be called by a test if it wants to abort testing.
460func FinishTesting()
Bram Moolenaarce436de2020-03-21 15:17:20 +0100461 call AfterTheTest('')
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200462 call Delete_Xtest_Files()
Bram Moolenaar42205552017-03-18 19:42:22 +0100463
464 " Don't write viminfo on exit.
465 set viminfo=
466
Bram Moolenaarce436de2020-03-21 15:17:20 +0100467 if s:fail == 0 && s:fail_expected == 0
Bram Moolenaar42205552017-03-18 19:42:22 +0100468 " Success, create the .res file so that make knows it's done.
469 exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
470 write
471 endif
472
473 if len(s:errors) > 0
474 " Append errors to test.log
475 split test.log
476 call append(line('$'), '')
477 call append(line('$'), 'From ' . g:testname . ':')
478 call append(line('$'), s:errors)
479 write
480 endif
481
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200482 if s:done == 0
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200483 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200484 if $TEST_FILTER != ''
485 let message = "NO tests match $TEST_FILTER: '" .. $TEST_FILTER .. "'"
486 else
487 let message = "ALL tests match $TEST_SKIP_PAT: '" .. $TEST_SKIP_PAT .. "'"
488 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200489 else
490 let message = 'NO tests executed'
491 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200492 else
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200493 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200494 call add(s:messages, "Filtered " .. s:filtered .. " tests with $TEST_FILTER and $TEST_SKIP_PAT")
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200495 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200496 let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
497 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200498 if s:done > 0 && has('reltime')
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200499 let message = s:t_bold .. message .. repeat(' ', 40 - len(message))
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100500 let message ..= ' in ' .. reltimestr(reltime(s:run_start_time)) .. ' seconds'
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200501 let message ..= s:t_normal
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200502 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100503 echo message
504 call add(s:messages, message)
505 if s:fail > 0
506 let message = s:fail . ' FAILED:'
507 echo message
508 call add(s:messages, message)
509 call extend(s:messages, s:errors)
510 endif
Bram Moolenaarce436de2020-03-21 15:17:20 +0100511 if s:fail_expected > 0
512 let message = s:fail_expected . ' FAILED (matching $TEST_MAY_FAIL):'
513 echo message
514 call add(s:messages, message)
515 call extend(s:messages, s:errors_expected)
516 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100517
518 " Add SKIPPED messages
519 call extend(s:messages, s:skipped)
520
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200521 " Append messages to the file "messages", but remove ANSI Escape sequences
Bram Moolenaar42205552017-03-18 19:42:22 +0100522 split messages
523 call append(line('$'), '')
524 call append(line('$'), 'From ' . g:testname . ':')
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200525 call append(line('$'), s:messages->map({_, val -> substitute(val, '\%x1b\[\d\?m', '', 'g')}))
Bram Moolenaar42205552017-03-18 19:42:22 +0100526 write
527
528 qall!
529endfunc
530
Bram Moolenaar43345542015-11-29 17:35:35 +0100531" Source the test script. First grab the file name, in case the script
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100532" navigates away. g:testname can be used by the tests.
533let g:testname = expand('%')
534let s:done = 0
535let s:fail = 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100536let s:fail_expected = 0
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100537let s:errors = []
Bram Moolenaarce436de2020-03-21 15:17:20 +0100538let s:errors_expected = []
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100539let s:messages = []
Bram Moolenaardac19472016-09-03 22:35:40 +0200540let s:skipped = []
Bram Moolenaarb544f3c2017-02-23 19:03:28 +0100541if expand('%') =~ 'test_vimscript.vim'
Bram Moolenaarce436de2020-03-21 15:17:20 +0100542 " this test has intentional errors, don't use try/catch.
Bram Moolenaar4686b322015-12-28 14:44:10 +0100543 source %
Bram Moolenaara2cce862016-01-02 19:50:04 +0100544else
545 try
546 source %
Bram Moolenaar9c0cec62019-06-06 13:38:15 +0200547 catch /^\cskipped/
548 call add(s:messages, ' Skipped')
549 call add(s:skipped, 'SKIPPED ' . expand('%') . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
Bram Moolenaara2cce862016-01-02 19:50:04 +0100550 catch
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100551 let s:fail += 1
552 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
Bram Moolenaara2cce862016-01-02 19:50:04 +0100553 endtry
554endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100555
Bram Moolenaar7e0be3e2022-03-20 13:40:41 +0000556" Delete the .res file, it may change behavior for completion
557call delete(fnamemodify(g:testname, ':r') .. '.res')
558
Bram Moolenaar43345542015-11-29 17:35:35 +0100559" Locate Test_ functions and execute them.
560redir @q
Bram Moolenaar93bf5582016-02-18 22:25:47 +0100561silent function /^Test_
Bram Moolenaar43345542015-11-29 17:35:35 +0100562redir END
Bram Moolenaar61a6d4e2020-03-01 23:32:25 +0100563let s:tests = split(substitute(@q, '\(function\|def\) \(\k*()\)', '\2', 'g'))
Bram Moolenaar43345542015-11-29 17:35:35 +0100564
Bram Moolenaarbefb3662016-02-20 14:41:40 +0100565" If there is an extra argument filter the function names against it.
566if argc() > 1
567 let s:tests = filter(s:tests, 'v:val =~ argv(1)')
568endif
569
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200570" If the environment variable $TEST_FILTER is set then filter the function
571" names against it.
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200572let s:filtered = 0
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200573if $TEST_FILTER != ''
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200574 let s:filtered = len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200575 let s:tests = filter(s:tests, 'v:val =~ $TEST_FILTER')
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200576 let s:filtered -= len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200577endif
578
Bram Moolenaarce436de2020-03-21 15:17:20 +0100579let s:may_fail_list = []
580if $TEST_MAY_FAIL != ''
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200581 " Split the list at commas and add () to make it match g:testfunc.
Bram Moolenaarce436de2020-03-21 15:17:20 +0100582 let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
583endif
584
Bram Moolenaarcfc0a352016-01-09 20:23:00 +0100585" Execute the tests in alphabetical order.
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200586for g:testfunc in sort(s:tests)
Bram Moolenaardae453f2021-08-07 17:20:16 +0200587 if $TEST_SKIP_PAT != '' && g:testfunc =~ $TEST_SKIP_PAT
588 call add(s:messages, g:testfunc .. ' matches $TEST_SKIP_PAT')
589 let s:filtered += 1
590 continue
591 endif
592
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200593 " Silence, please!
594 set belloff=all
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100595 let prev_error = ''
596 let total_errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100597 let g:run_nr = 1
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200598
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200599 " A test can set g:test_is_flaky to retry running the test.
600 let g:test_is_flaky = 0
Bram Moolenaar3cdcb092020-03-18 19:18:10 +0100601
Millybaab7c02024-10-28 21:56:14 +0100602 " A test can set g:max_run_nr to change the max retry count.
603 let g:max_run_nr = 5
604 if has('mac')
605 let g:max_run_nr = 10
606 endif
607
608 " By default, give up if the same error occurs. A test can set
609 " g:giveup_same_error to 0 to not give up on the same error and keep trying.
610 let g:giveup_same_error = 1
611
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100612 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200613 call RunTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100614
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100615 " Repeat a flaky test. Give up when:
Bram Moolenaar6ca6ca42020-07-27 19:47:07 +0200616 " - $TEST_NO_RETRY is not empty
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100617 " - it fails again with the same message
Bram Moolenaar1bc353b2019-09-01 14:45:28 +0200618 " - it fails five times (with a different message)
Bram Moolenaardbc0d212018-11-16 18:22:45 +0100619 if len(v:errors) > 0
Bram Moolenaar622b3562020-07-27 20:02:41 +0200620 \ && $TEST_NO_RETRY == ''
Bram Moolenaarf08b0eb2021-10-16 13:00:14 +0100621 \ && g:test_is_flaky
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100622 while 1
Bram Moolenaar06d32a02022-09-03 13:58:47 +0100623 call add(s:messages, 'Found errors in ' .. g:testfunc .. ':')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100624 call extend(s:messages, v:errors)
Bram Moolenaar15e737f2017-03-18 21:22:47 +0100625
Bram Moolenaar65258d32022-09-09 15:09:59 +0100626 let endtime = strftime("%H:%M:%S")
Millybaab7c02024-10-28 21:56:14 +0100627 if has('reltime')
628 let suffix = $' in{reltimestr(reltime(g:func_start))} seconds'
629 else
630 let suffix = ''
631 endif
632 call add(total_errors, $'Run {g:run_nr}, {starttime} - {endtime}{suffix}:')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100633 call extend(total_errors, v:errors)
Bram Moolenaar55058602017-11-21 15:14:51 +0100634
Millybaab7c02024-10-28 21:56:14 +0100635 if g:run_nr >= g:max_run_nr || g:giveup_same_error && prev_error == v:errors[0]
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100636 call add(total_errors, 'Flaky test failed too often, giving up')
637 let v:errors = total_errors
638 break
639 endif
640
641 call add(s:messages, 'Flaky test failed, running it again')
642
643 " Flakiness is often caused by the system being very busy. Sleep a
644 " couple of seconds to have a higher chance of succeeding the second
645 " time.
Millybaab7c02024-10-28 21:56:14 +0100646 let delay = g:run_nr * 2
647 exe 'sleep' delay
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100648
649 let prev_error = v:errors[0]
650 let v:errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100651 let g:run_nr += 1
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100652
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100653 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200654 call RunTheTest(g:testfunc)
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100655
656 if len(v:errors) == 0
657 " Test passed on rerun.
658 break
659 endif
660 endwhile
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100661 endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100662
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200663 call AfterTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100664endfor
665
Bram Moolenaar42205552017-03-18 19:42:22 +0100666call FinishTesting()
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100667
668" vim: shiftwidth=2 sts=2 expandtab