blob: 6fc0dcc669987f4f3d1a71cd26997984905590c4 [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')
matveytad3b6a32024-12-08 10:26:51 +010058 if has('gui_gtk')
59 " to keep screendump size unchanged
60 set guifont=Monospace\ 10
61 endif
Bram Moolenaar18aa13d2020-07-11 13:09:36 +020062 set columns=80 lines=25
63endif
64
Bram Moolenaar43345542015-11-29 17:35:35 +010065" Check that the screen size is at least 24 x 80 characters.
Bram Moolenaar94722c52023-01-28 19:19:03 +000066if &lines < 24 || &columns < 80
Bram Moolenaar0b5dc642019-08-11 22:56:15 +020067 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 +010068 echoerr error
69 split test.log
70 $put =error
Bram Moolenaar45aa07d2019-06-15 18:20:38 +020071 write
72 split messages
Bram Moolenaar0b5dc642019-08-11 22:56:15 +020073 call append(line('$'), '')
74 call append(line('$'), 'From ' . expand('%') . ':')
Bram Moolenaar45aa07d2019-06-15 18:20:38 +020075 call append(line('$'), error)
76 write
77 qa!
Bram Moolenaar43345542015-11-29 17:35:35 +010078endif
79
Bram Moolenaar75ee5442019-06-06 18:05:25 +020080if has('reltime')
Bram Moolenaarb9093d52022-09-23 19:42:31 +010081 let s:run_start_time = reltime()
82
83 if !filereadable('starttime')
84 " first test, store the overall test starting time
85 let s:test_start_time = localtime()
86 call writefile([string(s:test_start_time)], 'starttime')
87 else
88 " second or later test, read the overall test starting time
89 let s:test_start_time = readfile('starttime')[0]->str2nr()
90 endif
Bram Moolenaar75ee5442019-06-06 18:05:25 +020091endif
92
Bakudankun29f3a452021-12-11 12:28:08 +000093" Always use forward slashes.
94set shellslash
95
Bram Moolenaar89b10422016-07-12 22:51:22 +020096" Common with all tests on all systems.
97source setup.vim
98
Bram Moolenaar7c2beb42023-07-08 00:25:56 +010099" Needed for RunningWithValgrind().
100source shared.vim
101
Bram Moolenaarc0662462015-12-30 15:49:05 +0100102" For consistency run all tests with 'nocompatible' set.
103" This also enables use of line continuation.
104set nocp viminfo+=nviminfo
105
Bram Moolenaar30276f22019-01-24 17:59:39 +0100106" Use utf-8 by default, instead of whatever the system default happens to be.
Bram Moolenaared79d1e2019-02-22 14:38:58 +0100107" Individual tests can overrule this at the top of the file and use
108" g:orig_encoding if needed.
109let g:orig_encoding = &encoding
Bram Moolenaar30276f22019-01-24 17:59:39 +0100110set encoding=utf-8
Bram Moolenaarac105ed2016-07-21 20:33:32 +0200111
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200112" REDIR_TEST_TO_NULL has a very permissive SwapExists autocommand which is for
113" the test_name.vim file itself. Replace it here with a more restrictive one,
114" so we still catch mistakes.
Christian Brabandt4b2c8042021-11-03 22:31:44 +0000115if has("win32")
116 " replace any '/' directory separators by '\\'
117 let s:test_script_fname = substitute(expand('%'), '/', '\\', 'g')
118else
119 let s:test_script_fname = expand('%')
120endif
Bram Moolenaarabc81302023-06-04 16:55:27 +0100121
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200122au! SwapExists * call HandleSwapExists()
123func HandleSwapExists()
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +0200124 if exists('g:ignoreSwapExists')
Bram Moolenaarabc81302023-06-04 16:55:27 +0100125 if type(g:ignoreSwapExists) == v:t_string
126 let v:swapchoice = g:ignoreSwapExists
127 endif
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +0200128 return
129 endif
Bram Moolenaarb073da82019-07-13 14:47:26 +0200130 " Ignore finding a swap file for the test script (the user might be
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200131 " editing it and do ":make test_name") and the output file.
Bram Moolenaarb073da82019-07-13 14:47:26 +0200132 " Report finding another swap file and chose 'q' to avoid getting stuck.
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200133 if expand('<afile>') == 'messages' || expand('<afile>') =~ s:test_script_fname
134 let v:swapchoice = 'e'
Bram Moolenaarb073da82019-07-13 14:47:26 +0200135 else
136 call assert_report('Unexpected swap file: ' .. v:swapname)
137 let v:swapchoice = 'q'
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200138 endif
139endfunc
140
Bram Moolenaar7a073542017-02-01 23:17:36 +0100141" Avoid stopping at the "hit enter" prompt
142set nomore
143
Bram Moolenaarc0662462015-12-30 15:49:05 +0100144" Output all messages in English.
145lang mess C
146
Bram Moolenaar10e1d012020-07-18 22:03:11 +0200147" suppress menu translation
148if has('gui_running') && exists('did_install_default_menus')
149 source $VIMRUNTIME/delmenu.vim
150 set langmenu=none
151 source $VIMRUNTIME/menu.vim
152endif
153
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100154let s:srcdir = expand('%:p:h:h')
155
Bram Moolenaar2690b5a2020-07-22 18:14:58 +0200156if has('win32')
157 " avoid prompt that is long or contains a line break
158 let $PROMPT = '$P$G'
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200159 " On MS-Windows t_md and t_me are Vim specific escape sequences.
160 let s:t_bold = "\x1b[1m"
161 let s:t_normal = "\x1b[m"
162else
163 let s:t_bold = &t_md
164 let s:t_normal = &t_me
Bram Moolenaar2690b5a2020-07-22 18:14:58 +0200165endif
166
Bram Moolenaar4b2ce122020-11-21 21:41:41 +0100167if has('mac')
Dominique Pelle923dce22021-11-21 11:36:04 +0000168 " In macOS, when starting a shell in a terminal, a bash deprecation warning
Bram Moolenaar4b2ce122020-11-21 21:41:41 +0100169 " message is displayed. This breaks the terminal test. Disable the warning
170 " message.
171 let $BASH_SILENCE_DEPRECATION_WARNING = 1
172endif
173
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000174
Bram Moolenaar8e8df252016-05-25 21:23:21 +0200175" Prepare for calling test_garbagecollect_now().
Bram Moolenaarebf7dfa2016-04-14 12:46:51 +0200176let v:testing = 1
177
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100178" By default, copy each buffer line into allocated memory, so that valgrind can
179" detect accessing memory before and after it.
180call test_override('alloc_lines', 1)
181
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100182" Support function: get the alloc ID by name.
183function GetAllocId(name)
184 exe 'split ' . s:srcdir . '/alloc.h'
Bram Moolenaar065ee9a2016-01-15 20:53:38 +0100185 let top = search('typedef enum')
186 if top == 0
187 call add(v:errors, 'typedef not found in alloc.h')
188 endif
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100189 let lnum = search('aid_' . a:name . ',')
190 if lnum == 0
191 call add(v:errors, 'Alloc ID ' . a:name . ' not defined')
192 endif
193 close
Bram Moolenaar065ee9a2016-01-15 20:53:38 +0100194 return lnum - top - 1
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100195endfunc
196
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000197" Get the list of swap files in the current directory.
198func s:GetSwapFileList()
199 let save_dir = &directory
200 let &directory = '.'
201 let files = swapfilelist()
202 let &directory = save_dir
203
204 " remove a match with runtest.vim
205 let idx = indexof(files, 'v:val =~ "runtest.vim."')
206 if idx >= 0
207 call remove(files, idx)
208 endif
209
210 return files
211endfunc
212
Bram Moolenaar6572a902022-12-06 14:21:09 +0000213" A previous (failed) test run may have left swap files behind. Delete them
214" before running tests again, they might interfere.
215for name in s:GetSwapFileList()
216 call delete(name)
217endfor
zeertzjqa36acb72023-10-21 11:50:26 +0200218unlet! name
Bram Moolenaar6572a902022-12-06 14:21:09 +0000219
220
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100221" Invoked when a test takes too much time.
222func TestTimeout(id)
223 split test.log
224 call append(line('$'), '')
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100225
226 let text = 'Test timed out: ' .. g:testfunc
227 if g:timeout_start > 0
228 let text ..= strftime(' after %s seconds', localtime() - g:timeout_start)
229 endif
230 call append(line('$'), text)
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100231 write
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100232 call add(v:errors, text)
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100233
234 cquit! 42
235endfunc
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100236let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100237
Bram Moolenaar42205552017-03-18 19:42:22 +0100238func RunTheTest(test)
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100239 let prefix = ''
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200240 if has('reltime')
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100241 let prefix = strftime('%M:%S', localtime() - s:test_start_time) .. ' '
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100242 let g:func_start = reltime()
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200243 endif
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100244 echoconsole prefix .. 'Executing ' .. a:test
Bram Moolenaare5f2a072017-02-01 22:31:49 +0100245
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100246 if has('timers')
247 " No test should take longer than 30 seconds. If it takes longer we
248 " assume we are stuck and need to break out.
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100249 let test_timeout_timer =
250 \ timer_start(RunningWithValgrind() ? 50000 : 30000, 'TestTimeout')
251 let g:timeout_start = localtime()
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100252 endif
253
Bram Moolenaare5f2a072017-02-01 22:31:49 +0100254 " Avoid stopping at the "hit enter" prompt
255 set nomore
256
257 " Avoid a three second wait when a message is about to be overwritten by the
258 " mode message.
259 set noshowmode
260
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100261 " Clear any overrides, except "alloc_lines".
Bram Moolenaareb992cb2017-03-09 18:20:16 +0100262 call test_override('ALL', 0)
263
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200264 " Some tests wipe out buffers. To be consistent, always wipe out all
265 " buffers.
266 %bwipe!
267
Bram Moolenaar209d3872017-11-16 21:52:51 +0100268 " The test may change the current directory. Save and restore the
269 " directory after executing the test.
270 let save_cwd = getcwd()
271
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100272 if exists("*SetUp")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100273 try
274 call SetUp()
275 catch
276 call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
277 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100278 endif
279
zeertzjqa0e1f062023-10-18 11:50:37 +0200280 let skipped = v:false
281
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100282 au VimLeavePre * call EarlyExit(g:testfunc)
Bram Moolenaarf204e052017-10-26 17:14:01 +0200283 if a:test =~ 'Test_nocatch_'
284 " Function handles errors itself. This avoids skipping commands after the
285 " error.
Bram Moolenaar776b9542021-03-10 22:27:48 +0100286 let g:skipped_reason = ''
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100287 exe 'call ' . a:test
Bram Moolenaar776b9542021-03-10 22:27:48 +0100288 if g:skipped_reason != ''
289 call add(s:messages, ' Skipped')
290 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason)
zeertzjqa0e1f062023-10-18 11:50:37 +0200291 let skipped = v:true
Bram Moolenaar776b9542021-03-10 22:27:48 +0100292 endif
Bram Moolenaarf204e052017-10-26 17:14:01 +0200293 else
294 try
295 exe 'call ' . a:test
296 catch /^\cskipped/
297 call add(s:messages, ' Skipped')
298 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
zeertzjqa0e1f062023-10-18 11:50:37 +0200299 let skipped = v:true
Bram Moolenaarf204e052017-10-26 17:14:01 +0200300 catch
301 call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
302 endtry
303 endif
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100304 au! VimLeavePre
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100305
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100306 if a:test =~ '_terminal_'
307 " Terminal tests sometimes hang, give extra information
308 echoconsole 'After executing ' .. a:test
309 endif
310
Bram Moolenaar8ad16da2019-01-06 15:29:57 +0100311 " In case 'insertmode' was set and something went wrong, make sure it is
312 " reset to avoid trouble with anything else.
313 set noinsertmode
314
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100315 if exists("*TearDown")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100316 try
317 call TearDown()
318 catch
319 call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
320 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100321 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200322
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100323 if has('timers')
324 call timer_stop(test_timeout_timer)
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100325 let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100326 endif
327
Bram Moolenaar0b5dc642019-08-11 22:56:15 +0200328 " Clear any autocommands and put back the catch-all for SwapExists.
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200329 au!
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200330 au SwapExists * call HandleSwapExists()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200331
Bram Moolenaaref6b9792020-05-13 16:34:15 +0200332 " Check for and close any stray popup windows.
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100333 if has('popupwin')
Bram Moolenaarf05a1e52022-08-02 11:48:53 +0100334 call assert_equal([], popup_list(), 'Popup is still present')
Bram Moolenaar03a9f842020-05-13 13:40:16 +0200335 call popup_clear(1)
Bram Moolenaarae943152019-06-16 22:54:14 +0200336 endif
337
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100338 if filereadable('guidialogfile')
Bram Moolenaar217ea512022-06-14 17:13:59 +0100339 call add(v:errors, "Unexpected dialog: " .. readfile('guidialogfile')->join('<NL>'))
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100340 call delete('guidialogfile')
341 endif
342
Bram Moolenaarce11de82017-10-26 22:00:00 +0200343 " Close any extra tab pages and windows and make the current one not modified.
344 while tabpagenr('$') > 1
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200345 let winid = win_getid()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200346 quit!
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200347 if winid == win_getid()
348 echoerr 'Could not quit window'
349 break
350 endif
Bram Moolenaarce11de82017-10-26 22:00:00 +0200351 endwhile
352
Bram Moolenaar358308d2016-08-24 21:21:26 +0200353 while 1
354 let wincount = winnr('$')
355 if wincount == 1
356 break
357 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200358 bwipe!
Bram Moolenaar358308d2016-08-24 21:21:26 +0200359 if wincount == winnr('$')
360 " Did not manage to close a window.
361 only!
362 break
363 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200364 endwhile
Bram Moolenaar209d3872017-11-16 21:52:51 +0100365
366 exe 'cd ' . save_cwd
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200367
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100368 if a:test =~ '_terminal_'
369 " Terminal tests sometimes hang, give extra information
370 echoconsole 'Finished ' . a:test
371 endif
372
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200373 let message = 'Executed ' . a:test
374 if has('reltime')
Bram Moolenaar8d943792020-06-21 20:39:37 +0200375 let message ..= repeat(' ', 50 - len(message))
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100376 let time = reltime(g:func_start)
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_bold .. message
Bram Moolenaar8d943792020-06-21 20:39:37 +0200379 endif
380 let message ..= ' in ' .. reltimestr(time) .. ' seconds'
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100381 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200382 let message ..= s:t_normal
Bram Moolenaar8d943792020-06-21 20:39:37 +0200383 endif
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200384 endif
385 call add(s:messages, message)
386 let s:done += 1
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000387
Bram Moolenaare5eae822022-12-08 16:30:16 +0000388 " close any split windows
389 while winnr('$') > 1
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100390 noswapfile bwipe!
Bram Moolenaare5eae822022-12-08 16:30:16 +0000391 endwhile
392
Bram Moolenaar23526d22022-12-05 15:50:41 +0000393 " May be editing some buffer, wipe it out. Then we may end up in another
394 " buffer, continue until we end up in an empty no-name buffer without a swap
395 " file.
396 while bufname() != '' || execute('swapname') !~ 'No swap file'
Bram Moolenaarfa2533c2022-12-05 20:58:04 +0000397 let bn = bufnr()
398
399 noswapfile bwipe!
400
401 if bn == bufnr()
402 " avoid getting stuck in the same buffer
403 break
404 endif
Bram Moolenaar23526d22022-12-05 15:50:41 +0000405 endwhile
406
zeertzjqa0e1f062023-10-18 11:50:37 +0200407 if !skipped
408 " Check if the test has left any swap files behind. Delete them before
409 " running tests again, they might interfere.
410 let swapfiles = s:GetSwapFileList()
411 if len(swapfiles) > 0
412 call add(s:messages, "Found swap files: " .. string(swapfiles))
413 for name in swapfiles
414 call delete(name)
415 endfor
416 endif
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000417 endif
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100418endfunc
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100419
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200420function Delete_Xtest_Files()
421 for file in glob('X*', v:false, v:true)
422 if file ==? 'XfakeHOME'
423 " Clean up files created by setup.vim
424 call delete('XfakeHOME', 'rf')
425 continue
426 endif
427 " call add(v:errors, file .. " exists when it shouldn't, trying to delete it!")
428 call delete(file)
429 if !empty(glob(file, v:false, v:true))
430 " call add(v:errors, file .. " still exists after trying to delete it!")
431 if has('unix')
432 call system('rm -rf ' .. file)
433 endif
434 endif
435 endfor
436endfunc
437
Bram Moolenaarce436de2020-03-21 15:17:20 +0100438func AfterTheTest(func_name)
Bram Moolenaar42205552017-03-18 19:42:22 +0100439 if len(v:errors) > 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100440 if match(s:may_fail_list, '^' .. a:func_name) >= 0
441 let s:fail_expected += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200442 call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100443 call extend(s:errors_expected, v:errors)
444 else
445 let s:fail += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200446 call add(s:errors, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100447 call extend(s:errors, v:errors)
448 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100449 let v:errors = []
450 endif
451endfunc
452
Bram Moolenaar89036762018-06-12 14:58:39 +0200453func EarlyExit(test)
454 " It's OK for the test we use to test the quit detection.
455 if a:test != 'Test_zz_quit_detected()'
Bram Moolenaar1c67f3a2021-12-30 13:32:09 +0000456 call add(v:errors, v:errmsg)
Bram Moolenaar89036762018-06-12 14:58:39 +0200457 call add(v:errors, 'Test caused Vim to exit: ' . a:test)
458 endif
459
460 call FinishTesting()
461endfunc
462
Bram Moolenaar42205552017-03-18 19:42:22 +0100463" This function can be called by a test if it wants to abort testing.
464func FinishTesting()
Bram Moolenaarce436de2020-03-21 15:17:20 +0100465 call AfterTheTest('')
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200466 call Delete_Xtest_Files()
Bram Moolenaar42205552017-03-18 19:42:22 +0100467
468 " Don't write viminfo on exit.
469 set viminfo=
470
Bram Moolenaarce436de2020-03-21 15:17:20 +0100471 if s:fail == 0 && s:fail_expected == 0
Bram Moolenaar42205552017-03-18 19:42:22 +0100472 " Success, create the .res file so that make knows it's done.
473 exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
474 write
475 endif
476
477 if len(s:errors) > 0
478 " Append errors to test.log
479 split test.log
480 call append(line('$'), '')
481 call append(line('$'), 'From ' . g:testname . ':')
482 call append(line('$'), s:errors)
483 write
484 endif
485
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200486 if s:done == 0
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200487 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200488 if $TEST_FILTER != ''
489 let message = "NO tests match $TEST_FILTER: '" .. $TEST_FILTER .. "'"
490 else
491 let message = "ALL tests match $TEST_SKIP_PAT: '" .. $TEST_SKIP_PAT .. "'"
492 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200493 else
494 let message = 'NO tests executed'
495 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200496 else
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200497 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200498 call add(s:messages, "Filtered " .. s:filtered .. " tests with $TEST_FILTER and $TEST_SKIP_PAT")
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200499 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200500 let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
501 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200502 if s:done > 0 && has('reltime')
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200503 let message = s:t_bold .. message .. repeat(' ', 40 - len(message))
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100504 let message ..= ' in ' .. reltimestr(reltime(s:run_start_time)) .. ' seconds'
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200505 let message ..= s:t_normal
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200506 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100507 echo message
508 call add(s:messages, message)
509 if s:fail > 0
510 let message = s:fail . ' FAILED:'
511 echo message
512 call add(s:messages, message)
513 call extend(s:messages, s:errors)
514 endif
Bram Moolenaarce436de2020-03-21 15:17:20 +0100515 if s:fail_expected > 0
516 let message = s:fail_expected . ' FAILED (matching $TEST_MAY_FAIL):'
517 echo message
518 call add(s:messages, message)
519 call extend(s:messages, s:errors_expected)
520 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100521
522 " Add SKIPPED messages
523 call extend(s:messages, s:skipped)
524
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200525 " Append messages to the file "messages", but remove ANSI Escape sequences
Bram Moolenaar42205552017-03-18 19:42:22 +0100526 split messages
527 call append(line('$'), '')
528 call append(line('$'), 'From ' . g:testname . ':')
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200529 call append(line('$'), s:messages->map({_, val -> substitute(val, '\%x1b\[\d\?m', '', 'g')}))
Bram Moolenaar42205552017-03-18 19:42:22 +0100530 write
531
532 qall!
533endfunc
534
Bram Moolenaar43345542015-11-29 17:35:35 +0100535" Source the test script. First grab the file name, in case the script
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100536" navigates away. g:testname can be used by the tests.
537let g:testname = expand('%')
538let s:done = 0
539let s:fail = 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100540let s:fail_expected = 0
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100541let s:errors = []
Bram Moolenaarce436de2020-03-21 15:17:20 +0100542let s:errors_expected = []
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100543let s:messages = []
Bram Moolenaardac19472016-09-03 22:35:40 +0200544let s:skipped = []
Bram Moolenaarb544f3c2017-02-23 19:03:28 +0100545if expand('%') =~ 'test_vimscript.vim'
Bram Moolenaarce436de2020-03-21 15:17:20 +0100546 " this test has intentional errors, don't use try/catch.
Bram Moolenaar4686b322015-12-28 14:44:10 +0100547 source %
Bram Moolenaara2cce862016-01-02 19:50:04 +0100548else
549 try
550 source %
Bram Moolenaar9c0cec62019-06-06 13:38:15 +0200551 catch /^\cskipped/
552 call add(s:messages, ' Skipped')
553 call add(s:skipped, 'SKIPPED ' . expand('%') . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
Bram Moolenaara2cce862016-01-02 19:50:04 +0100554 catch
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100555 let s:fail += 1
556 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
Bram Moolenaara2cce862016-01-02 19:50:04 +0100557 endtry
558endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100559
Bram Moolenaar7e0be3e2022-03-20 13:40:41 +0000560" Delete the .res file, it may change behavior for completion
561call delete(fnamemodify(g:testname, ':r') .. '.res')
562
Bram Moolenaar43345542015-11-29 17:35:35 +0100563" Locate Test_ functions and execute them.
564redir @q
Bram Moolenaar93bf5582016-02-18 22:25:47 +0100565silent function /^Test_
Bram Moolenaar43345542015-11-29 17:35:35 +0100566redir END
Bram Moolenaar61a6d4e2020-03-01 23:32:25 +0100567let s:tests = split(substitute(@q, '\(function\|def\) \(\k*()\)', '\2', 'g'))
Bram Moolenaar43345542015-11-29 17:35:35 +0100568
Bram Moolenaarbefb3662016-02-20 14:41:40 +0100569" If there is an extra argument filter the function names against it.
570if argc() > 1
571 let s:tests = filter(s:tests, 'v:val =~ argv(1)')
572endif
573
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200574" If the environment variable $TEST_FILTER is set then filter the function
575" names against it.
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200576let s:filtered = 0
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200577if $TEST_FILTER != ''
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200578 let s:filtered = len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200579 let s:tests = filter(s:tests, 'v:val =~ $TEST_FILTER')
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200580 let s:filtered -= len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200581endif
582
Bram Moolenaarce436de2020-03-21 15:17:20 +0100583let s:may_fail_list = []
584if $TEST_MAY_FAIL != ''
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200585 " Split the list at commas and add () to make it match g:testfunc.
Bram Moolenaarce436de2020-03-21 15:17:20 +0100586 let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
587endif
588
Bram Moolenaarcfc0a352016-01-09 20:23:00 +0100589" Execute the tests in alphabetical order.
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200590for g:testfunc in sort(s:tests)
Bram Moolenaardae453f2021-08-07 17:20:16 +0200591 if $TEST_SKIP_PAT != '' && g:testfunc =~ $TEST_SKIP_PAT
592 call add(s:messages, g:testfunc .. ' matches $TEST_SKIP_PAT')
593 let s:filtered += 1
594 continue
595 endif
596
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200597 " Silence, please!
598 set belloff=all
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100599 let prev_error = ''
600 let total_errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100601 let g:run_nr = 1
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200602
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200603 " A test can set g:test_is_flaky to retry running the test.
604 let g:test_is_flaky = 0
Bram Moolenaar3cdcb092020-03-18 19:18:10 +0100605
Millybaab7c02024-10-28 21:56:14 +0100606 " A test can set g:max_run_nr to change the max retry count.
607 let g:max_run_nr = 5
608 if has('mac')
609 let g:max_run_nr = 10
610 endif
611
612 " By default, give up if the same error occurs. A test can set
613 " g:giveup_same_error to 0 to not give up on the same error and keep trying.
614 let g:giveup_same_error = 1
615
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100616 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200617 call RunTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100618
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100619 " Repeat a flaky test. Give up when:
Bram Moolenaar6ca6ca42020-07-27 19:47:07 +0200620 " - $TEST_NO_RETRY is not empty
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100621 " - it fails again with the same message
Bram Moolenaar1bc353b2019-09-01 14:45:28 +0200622 " - it fails five times (with a different message)
Bram Moolenaardbc0d212018-11-16 18:22:45 +0100623 if len(v:errors) > 0
Bram Moolenaar622b3562020-07-27 20:02:41 +0200624 \ && $TEST_NO_RETRY == ''
Bram Moolenaarf08b0eb2021-10-16 13:00:14 +0100625 \ && g:test_is_flaky
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100626 while 1
Bram Moolenaar06d32a02022-09-03 13:58:47 +0100627 call add(s:messages, 'Found errors in ' .. g:testfunc .. ':')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100628 call extend(s:messages, v:errors)
Bram Moolenaar15e737f2017-03-18 21:22:47 +0100629
Bram Moolenaar65258d32022-09-09 15:09:59 +0100630 let endtime = strftime("%H:%M:%S")
Millybaab7c02024-10-28 21:56:14 +0100631 if has('reltime')
632 let suffix = $' in{reltimestr(reltime(g:func_start))} seconds'
633 else
634 let suffix = ''
635 endif
636 call add(total_errors, $'Run {g:run_nr}, {starttime} - {endtime}{suffix}:')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100637 call extend(total_errors, v:errors)
Bram Moolenaar55058602017-11-21 15:14:51 +0100638
Millybaab7c02024-10-28 21:56:14 +0100639 if g:run_nr >= g:max_run_nr || g:giveup_same_error && prev_error == v:errors[0]
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100640 call add(total_errors, 'Flaky test failed too often, giving up')
641 let v:errors = total_errors
642 break
643 endif
644
645 call add(s:messages, 'Flaky test failed, running it again')
646
647 " Flakiness is often caused by the system being very busy. Sleep a
648 " couple of seconds to have a higher chance of succeeding the second
649 " time.
Millybaab7c02024-10-28 21:56:14 +0100650 let delay = g:run_nr * 2
651 exe 'sleep' delay
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100652
653 let prev_error = v:errors[0]
654 let v:errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100655 let g:run_nr += 1
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100656
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100657 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200658 call RunTheTest(g:testfunc)
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100659
660 if len(v:errors) == 0
661 " Test passed on rerun.
662 break
663 endif
664 endwhile
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100665 endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100666
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200667 call AfterTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100668endfor
669
Bram Moolenaar42205552017-03-18 19:42:22 +0100670call FinishTesting()
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100671
672" vim: shiftwidth=2 sts=2 expandtab