blob: 0d531215574d379d33fc8383035aebc991c63ac6 [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')
Christian Brabandtec7a4e42025-02-09 17:16:36 +0100247 " No test should take longer than 45 seconds. If it takes longer we
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100248 " assume we are stuck and need to break out.
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100249 let test_timeout_timer =
Christian Brabandtec7a4e42025-02-09 17:16:36 +0100250 \ timer_start(RunningWithValgrind() ? 90000 : 45000, 'TestTimeout')
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100251 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
Yee Cheng Chine70587d2025-02-13 20:55:45 +0100268 " Clear all children notifications in case there are stale ones left
269 let g:child_notification = 0
270
Bram Moolenaar209d3872017-11-16 21:52:51 +0100271 " The test may change the current directory. Save and restore the
272 " directory after executing the test.
273 let save_cwd = getcwd()
274
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100275 if exists("*SetUp")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100276 try
277 call SetUp()
278 catch
279 call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
280 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100281 endif
282
zeertzjqa0e1f062023-10-18 11:50:37 +0200283 let skipped = v:false
284
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100285 au VimLeavePre * call EarlyExit(g:testfunc)
Bram Moolenaarf204e052017-10-26 17:14:01 +0200286 if a:test =~ 'Test_nocatch_'
287 " Function handles errors itself. This avoids skipping commands after the
288 " error.
Bram Moolenaar776b9542021-03-10 22:27:48 +0100289 let g:skipped_reason = ''
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100290 exe 'call ' . a:test
Bram Moolenaar776b9542021-03-10 22:27:48 +0100291 if g:skipped_reason != ''
292 call add(s:messages, ' Skipped')
293 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason)
zeertzjqa0e1f062023-10-18 11:50:37 +0200294 let skipped = v:true
Bram Moolenaar776b9542021-03-10 22:27:48 +0100295 endif
Bram Moolenaarf204e052017-10-26 17:14:01 +0200296 else
297 try
298 exe 'call ' . a:test
299 catch /^\cskipped/
300 call add(s:messages, ' Skipped')
301 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
zeertzjqa0e1f062023-10-18 11:50:37 +0200302 let skipped = v:true
Bram Moolenaarf204e052017-10-26 17:14:01 +0200303 catch
304 call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
305 endtry
306 endif
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100307 au! VimLeavePre
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100308
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100309 if a:test =~ '_terminal_'
310 " Terminal tests sometimes hang, give extra information
311 echoconsole 'After executing ' .. a:test
312 endif
313
Bram Moolenaar8ad16da2019-01-06 15:29:57 +0100314 " In case 'insertmode' was set and something went wrong, make sure it is
315 " reset to avoid trouble with anything else.
316 set noinsertmode
317
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100318 if exists("*TearDown")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100319 try
320 call TearDown()
321 catch
322 call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
323 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100324 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200325
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100326 if has('timers')
327 call timer_stop(test_timeout_timer)
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100328 let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100329 endif
330
Bram Moolenaar0b5dc642019-08-11 22:56:15 +0200331 " Clear any autocommands and put back the catch-all for SwapExists.
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200332 au!
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200333 au SwapExists * call HandleSwapExists()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200334
Bram Moolenaaref6b9792020-05-13 16:34:15 +0200335 " Check for and close any stray popup windows.
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100336 if has('popupwin')
Bram Moolenaarf05a1e52022-08-02 11:48:53 +0100337 call assert_equal([], popup_list(), 'Popup is still present')
Bram Moolenaar03a9f842020-05-13 13:40:16 +0200338 call popup_clear(1)
Bram Moolenaarae943152019-06-16 22:54:14 +0200339 endif
340
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100341 if filereadable('guidialogfile')
Bram Moolenaar217ea512022-06-14 17:13:59 +0100342 call add(v:errors, "Unexpected dialog: " .. readfile('guidialogfile')->join('<NL>'))
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100343 call delete('guidialogfile')
344 endif
345
Bram Moolenaarce11de82017-10-26 22:00:00 +0200346 " Close any extra tab pages and windows and make the current one not modified.
347 while tabpagenr('$') > 1
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200348 let winid = win_getid()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200349 quit!
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200350 if winid == win_getid()
351 echoerr 'Could not quit window'
352 break
353 endif
Bram Moolenaarce11de82017-10-26 22:00:00 +0200354 endwhile
355
Bram Moolenaar358308d2016-08-24 21:21:26 +0200356 while 1
357 let wincount = winnr('$')
358 if wincount == 1
359 break
360 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200361 bwipe!
Bram Moolenaar358308d2016-08-24 21:21:26 +0200362 if wincount == winnr('$')
363 " Did not manage to close a window.
364 only!
365 break
366 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200367 endwhile
Bram Moolenaar209d3872017-11-16 21:52:51 +0100368
369 exe 'cd ' . save_cwd
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200370
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100371 if a:test =~ '_terminal_'
372 " Terminal tests sometimes hang, give extra information
373 echoconsole 'Finished ' . a:test
374 endif
375
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200376 let message = 'Executed ' . a:test
377 if has('reltime')
Bram Moolenaar8d943792020-06-21 20:39:37 +0200378 let message ..= repeat(' ', 50 - len(message))
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100379 let time = reltime(g:func_start)
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100380 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200381 let message = s:t_bold .. message
Bram Moolenaar8d943792020-06-21 20:39:37 +0200382 endif
383 let message ..= ' in ' .. reltimestr(time) .. ' seconds'
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100384 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200385 let message ..= s:t_normal
Bram Moolenaar8d943792020-06-21 20:39:37 +0200386 endif
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200387 endif
388 call add(s:messages, message)
389 let s:done += 1
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000390
Bram Moolenaare5eae822022-12-08 16:30:16 +0000391 " close any split windows
392 while winnr('$') > 1
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100393 noswapfile bwipe!
Bram Moolenaare5eae822022-12-08 16:30:16 +0000394 endwhile
395
Bram Moolenaar23526d22022-12-05 15:50:41 +0000396 " May be editing some buffer, wipe it out. Then we may end up in another
397 " buffer, continue until we end up in an empty no-name buffer without a swap
398 " file.
399 while bufname() != '' || execute('swapname') !~ 'No swap file'
Bram Moolenaarfa2533c2022-12-05 20:58:04 +0000400 let bn = bufnr()
401
402 noswapfile bwipe!
403
404 if bn == bufnr()
405 " avoid getting stuck in the same buffer
406 break
407 endif
Bram Moolenaar23526d22022-12-05 15:50:41 +0000408 endwhile
409
zeertzjqa0e1f062023-10-18 11:50:37 +0200410 if !skipped
411 " Check if the test has left any swap files behind. Delete them before
412 " running tests again, they might interfere.
413 let swapfiles = s:GetSwapFileList()
414 if len(swapfiles) > 0
415 call add(s:messages, "Found swap files: " .. string(swapfiles))
416 for name in swapfiles
417 call delete(name)
418 endfor
419 endif
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000420 endif
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100421endfunc
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100422
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200423function Delete_Xtest_Files()
424 for file in glob('X*', v:false, v:true)
425 if file ==? 'XfakeHOME'
426 " Clean up files created by setup.vim
427 call delete('XfakeHOME', 'rf')
428 continue
429 endif
430 " call add(v:errors, file .. " exists when it shouldn't, trying to delete it!")
431 call delete(file)
432 if !empty(glob(file, v:false, v:true))
433 " call add(v:errors, file .. " still exists after trying to delete it!")
434 if has('unix')
435 call system('rm -rf ' .. file)
436 endif
437 endif
438 endfor
439endfunc
440
Bram Moolenaarce436de2020-03-21 15:17:20 +0100441func AfterTheTest(func_name)
Bram Moolenaar42205552017-03-18 19:42:22 +0100442 if len(v:errors) > 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100443 if match(s:may_fail_list, '^' .. a:func_name) >= 0
444 let s:fail_expected += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200445 call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100446 call extend(s:errors_expected, v:errors)
447 else
448 let s:fail += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200449 call add(s:errors, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100450 call extend(s:errors, v:errors)
451 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100452 let v:errors = []
453 endif
454endfunc
455
Bram Moolenaar89036762018-06-12 14:58:39 +0200456func EarlyExit(test)
457 " It's OK for the test we use to test the quit detection.
458 if a:test != 'Test_zz_quit_detected()'
Bram Moolenaar1c67f3a2021-12-30 13:32:09 +0000459 call add(v:errors, v:errmsg)
Bram Moolenaar89036762018-06-12 14:58:39 +0200460 call add(v:errors, 'Test caused Vim to exit: ' . a:test)
461 endif
462
463 call FinishTesting()
464endfunc
465
Bram Moolenaar42205552017-03-18 19:42:22 +0100466" This function can be called by a test if it wants to abort testing.
467func FinishTesting()
Bram Moolenaarce436de2020-03-21 15:17:20 +0100468 call AfterTheTest('')
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200469 call Delete_Xtest_Files()
Bram Moolenaar42205552017-03-18 19:42:22 +0100470
471 " Don't write viminfo on exit.
472 set viminfo=
473
Bram Moolenaarce436de2020-03-21 15:17:20 +0100474 if s:fail == 0 && s:fail_expected == 0
Bram Moolenaar42205552017-03-18 19:42:22 +0100475 " Success, create the .res file so that make knows it's done.
476 exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
477 write
478 endif
479
480 if len(s:errors) > 0
481 " Append errors to test.log
482 split test.log
483 call append(line('$'), '')
484 call append(line('$'), 'From ' . g:testname . ':')
485 call append(line('$'), s:errors)
486 write
487 endif
488
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200489 if s:done == 0
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200490 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200491 if $TEST_FILTER != ''
492 let message = "NO tests match $TEST_FILTER: '" .. $TEST_FILTER .. "'"
493 else
494 let message = "ALL tests match $TEST_SKIP_PAT: '" .. $TEST_SKIP_PAT .. "'"
495 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200496 else
497 let message = 'NO tests executed'
498 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200499 else
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200500 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200501 call add(s:messages, "Filtered " .. s:filtered .. " tests with $TEST_FILTER and $TEST_SKIP_PAT")
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200502 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200503 let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
504 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200505 if s:done > 0 && has('reltime')
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200506 let message = s:t_bold .. message .. repeat(' ', 40 - len(message))
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100507 let message ..= ' in ' .. reltimestr(reltime(s:run_start_time)) .. ' seconds'
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200508 let message ..= s:t_normal
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200509 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100510 echo message
511 call add(s:messages, message)
512 if s:fail > 0
513 let message = s:fail . ' FAILED:'
514 echo message
515 call add(s:messages, message)
516 call extend(s:messages, s:errors)
517 endif
Bram Moolenaarce436de2020-03-21 15:17:20 +0100518 if s:fail_expected > 0
519 let message = s:fail_expected . ' FAILED (matching $TEST_MAY_FAIL):'
520 echo message
521 call add(s:messages, message)
522 call extend(s:messages, s:errors_expected)
523 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100524
525 " Add SKIPPED messages
526 call extend(s:messages, s:skipped)
527
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200528 " Append messages to the file "messages", but remove ANSI Escape sequences
Bram Moolenaar42205552017-03-18 19:42:22 +0100529 split messages
530 call append(line('$'), '')
531 call append(line('$'), 'From ' . g:testname . ':')
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200532 call append(line('$'), s:messages->map({_, val -> substitute(val, '\%x1b\[\d\?m', '', 'g')}))
Bram Moolenaar42205552017-03-18 19:42:22 +0100533 write
534
535 qall!
536endfunc
537
Bram Moolenaar43345542015-11-29 17:35:35 +0100538" Source the test script. First grab the file name, in case the script
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100539" navigates away. g:testname can be used by the tests.
540let g:testname = expand('%')
541let s:done = 0
542let s:fail = 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100543let s:fail_expected = 0
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100544let s:errors = []
Bram Moolenaarce436de2020-03-21 15:17:20 +0100545let s:errors_expected = []
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100546let s:messages = []
Bram Moolenaardac19472016-09-03 22:35:40 +0200547let s:skipped = []
Bram Moolenaarb544f3c2017-02-23 19:03:28 +0100548if expand('%') =~ 'test_vimscript.vim'
Bram Moolenaarce436de2020-03-21 15:17:20 +0100549 " this test has intentional errors, don't use try/catch.
Bram Moolenaar4686b322015-12-28 14:44:10 +0100550 source %
Bram Moolenaara2cce862016-01-02 19:50:04 +0100551else
552 try
553 source %
Bram Moolenaar9c0cec62019-06-06 13:38:15 +0200554 catch /^\cskipped/
555 call add(s:messages, ' Skipped')
556 call add(s:skipped, 'SKIPPED ' . expand('%') . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
Bram Moolenaara2cce862016-01-02 19:50:04 +0100557 catch
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100558 let s:fail += 1
559 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
Bram Moolenaara2cce862016-01-02 19:50:04 +0100560 endtry
561endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100562
Bram Moolenaar7e0be3e2022-03-20 13:40:41 +0000563" Delete the .res file, it may change behavior for completion
564call delete(fnamemodify(g:testname, ':r') .. '.res')
565
Bram Moolenaar43345542015-11-29 17:35:35 +0100566" Locate Test_ functions and execute them.
567redir @q
Bram Moolenaar93bf5582016-02-18 22:25:47 +0100568silent function /^Test_
Bram Moolenaar43345542015-11-29 17:35:35 +0100569redir END
Bram Moolenaar61a6d4e2020-03-01 23:32:25 +0100570let s:tests = split(substitute(@q, '\(function\|def\) \(\k*()\)', '\2', 'g'))
Bram Moolenaar43345542015-11-29 17:35:35 +0100571
Bram Moolenaarbefb3662016-02-20 14:41:40 +0100572" If there is an extra argument filter the function names against it.
573if argc() > 1
574 let s:tests = filter(s:tests, 'v:val =~ argv(1)')
575endif
576
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200577" If the environment variable $TEST_FILTER is set then filter the function
578" names against it.
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200579let s:filtered = 0
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200580if $TEST_FILTER != ''
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200581 let s:filtered = len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200582 let s:tests = filter(s:tests, 'v:val =~ $TEST_FILTER')
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200583 let s:filtered -= len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200584endif
585
Bram Moolenaarce436de2020-03-21 15:17:20 +0100586let s:may_fail_list = []
587if $TEST_MAY_FAIL != ''
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200588 " Split the list at commas and add () to make it match g:testfunc.
Bram Moolenaarce436de2020-03-21 15:17:20 +0100589 let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
590endif
591
Bram Moolenaarcfc0a352016-01-09 20:23:00 +0100592" Execute the tests in alphabetical order.
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200593for g:testfunc in sort(s:tests)
Bram Moolenaardae453f2021-08-07 17:20:16 +0200594 if $TEST_SKIP_PAT != '' && g:testfunc =~ $TEST_SKIP_PAT
595 call add(s:messages, g:testfunc .. ' matches $TEST_SKIP_PAT')
596 let s:filtered += 1
597 continue
598 endif
599
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200600 " Silence, please!
601 set belloff=all
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100602 let prev_error = ''
603 let total_errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100604 let g:run_nr = 1
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200605
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200606 " A test can set g:test_is_flaky to retry running the test.
607 let g:test_is_flaky = 0
Bram Moolenaar3cdcb092020-03-18 19:18:10 +0100608
Drew Vogelea67ba72025-05-07 22:05:17 +0200609 let g:check_screendump_called = v:false
610
Millybaab7c02024-10-28 21:56:14 +0100611 " A test can set g:max_run_nr to change the max retry count.
612 let g:max_run_nr = 5
613 if has('mac')
614 let g:max_run_nr = 10
615 endif
616
617 " By default, give up if the same error occurs. A test can set
618 " g:giveup_same_error to 0 to not give up on the same error and keep trying.
619 let g:giveup_same_error = 1
620
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100621 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200622 call RunTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100623
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100624 " Repeat a flaky test. Give up when:
Bram Moolenaar6ca6ca42020-07-27 19:47:07 +0200625 " - $TEST_NO_RETRY is not empty
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100626 " - it fails again with the same message
Bram Moolenaar1bc353b2019-09-01 14:45:28 +0200627 " - it fails five times (with a different message)
Bram Moolenaardbc0d212018-11-16 18:22:45 +0100628 if len(v:errors) > 0
Bram Moolenaar622b3562020-07-27 20:02:41 +0200629 \ && $TEST_NO_RETRY == ''
Bram Moolenaarf08b0eb2021-10-16 13:00:14 +0100630 \ && g:test_is_flaky
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100631 while 1
Bram Moolenaar06d32a02022-09-03 13:58:47 +0100632 call add(s:messages, 'Found errors in ' .. g:testfunc .. ':')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100633 call extend(s:messages, v:errors)
Bram Moolenaar15e737f2017-03-18 21:22:47 +0100634
Bram Moolenaar65258d32022-09-09 15:09:59 +0100635 let endtime = strftime("%H:%M:%S")
Millybaab7c02024-10-28 21:56:14 +0100636 if has('reltime')
637 let suffix = $' in{reltimestr(reltime(g:func_start))} seconds'
638 else
639 let suffix = ''
640 endif
641 call add(total_errors, $'Run {g:run_nr}, {starttime} - {endtime}{suffix}:')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100642 call extend(total_errors, v:errors)
Bram Moolenaar55058602017-11-21 15:14:51 +0100643
Millybaab7c02024-10-28 21:56:14 +0100644 if g:run_nr >= g:max_run_nr || g:giveup_same_error && prev_error == v:errors[0]
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100645 call add(total_errors, 'Flaky test failed too often, giving up')
646 let v:errors = total_errors
647 break
648 endif
649
650 call add(s:messages, 'Flaky test failed, running it again')
651
652 " Flakiness is often caused by the system being very busy. Sleep a
653 " couple of seconds to have a higher chance of succeeding the second
654 " time.
Millybaab7c02024-10-28 21:56:14 +0100655 let delay = g:run_nr * 2
656 exe 'sleep' delay
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100657
658 let prev_error = v:errors[0]
659 let v:errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100660 let g:run_nr += 1
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100661
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100662 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200663 call RunTheTest(g:testfunc)
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100664
665 if len(v:errors) == 0
666 " Test passed on rerun.
667 break
668 endif
669 endwhile
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100670 endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100671
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200672 call AfterTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100673endfor
674
Bram Moolenaar42205552017-03-18 19:42:22 +0100675call FinishTesting()
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100676
677" vim: shiftwidth=2 sts=2 expandtab