blob: f97c432930b69ccfbaf5189b47acf4ce706f238c [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')
Aliaksei Budaveic4eb1cb2025-06-08 15:52:42 +020059 " Use e.g. SetUp() and TearDown() to change "&guifont" when needed;
60 " otherwise, keep the following value to match current screendumps.
matveytad3b6a32024-12-08 10:26:51 +010061 set guifont=Monospace\ 10
62 endif
Aliaksei Budaveic4eb1cb2025-06-08 15:52:42 +020063
64 func s:SetDefaultOptionsForGUIBuilds()
65 set columns=80 lines=25
66 endfunc
67else
68 func s:SetDefaultOptionsForGUIBuilds()
69 endfunc
Bram Moolenaar18aa13d2020-07-11 13:09:36 +020070endif
71
Bram Moolenaar43345542015-11-29 17:35:35 +010072" Check that the screen size is at least 24 x 80 characters.
Bram Moolenaar94722c52023-01-28 19:19:03 +000073if &lines < 24 || &columns < 80
Bram Moolenaar0b5dc642019-08-11 22:56:15 +020074 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 +010075 echoerr error
76 split test.log
77 $put =error
Bram Moolenaar45aa07d2019-06-15 18:20:38 +020078 write
79 split messages
Bram Moolenaar0b5dc642019-08-11 22:56:15 +020080 call append(line('$'), '')
81 call append(line('$'), 'From ' . expand('%') . ':')
Bram Moolenaar45aa07d2019-06-15 18:20:38 +020082 call append(line('$'), error)
83 write
84 qa!
Bram Moolenaar43345542015-11-29 17:35:35 +010085endif
86
Bram Moolenaar75ee5442019-06-06 18:05:25 +020087if has('reltime')
Bram Moolenaarb9093d52022-09-23 19:42:31 +010088 let s:run_start_time = reltime()
89
90 if !filereadable('starttime')
91 " first test, store the overall test starting time
92 let s:test_start_time = localtime()
93 call writefile([string(s:test_start_time)], 'starttime')
94 else
95 " second or later test, read the overall test starting time
96 let s:test_start_time = readfile('starttime')[0]->str2nr()
97 endif
Bram Moolenaar75ee5442019-06-06 18:05:25 +020098endif
99
Bakudankun29f3a452021-12-11 12:28:08 +0000100" Always use forward slashes.
101set shellslash
102
Bram Moolenaar89b10422016-07-12 22:51:22 +0200103" Common with all tests on all systems.
104source setup.vim
105
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100106" Needed for RunningWithValgrind().
107source shared.vim
108
Bram Moolenaarc0662462015-12-30 15:49:05 +0100109" For consistency run all tests with 'nocompatible' set.
110" This also enables use of line continuation.
111set nocp viminfo+=nviminfo
112
Bram Moolenaar30276f22019-01-24 17:59:39 +0100113" Use utf-8 by default, instead of whatever the system default happens to be.
Bram Moolenaared79d1e2019-02-22 14:38:58 +0100114" Individual tests can overrule this at the top of the file and use
115" g:orig_encoding if needed.
116let g:orig_encoding = &encoding
Bram Moolenaar30276f22019-01-24 17:59:39 +0100117set encoding=utf-8
Bram Moolenaarac105ed2016-07-21 20:33:32 +0200118
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200119" REDIR_TEST_TO_NULL has a very permissive SwapExists autocommand which is for
120" the test_name.vim file itself. Replace it here with a more restrictive one,
121" so we still catch mistakes.
Christian Brabandt4b2c8042021-11-03 22:31:44 +0000122if has("win32")
123 " replace any '/' directory separators by '\\'
124 let s:test_script_fname = substitute(expand('%'), '/', '\\', 'g')
125else
126 let s:test_script_fname = expand('%')
127endif
Bram Moolenaarabc81302023-06-04 16:55:27 +0100128
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200129au! SwapExists * call HandleSwapExists()
130func HandleSwapExists()
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +0200131 if exists('g:ignoreSwapExists')
Bram Moolenaarabc81302023-06-04 16:55:27 +0100132 if type(g:ignoreSwapExists) == v:t_string
133 let v:swapchoice = g:ignoreSwapExists
134 endif
Bram Moolenaar8ce4b7e2020-08-07 18:12:18 +0200135 return
136 endif
Bram Moolenaarb073da82019-07-13 14:47:26 +0200137 " Ignore finding a swap file for the test script (the user might be
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200138 " editing it and do ":make test_name") and the output file.
Bram Moolenaarb073da82019-07-13 14:47:26 +0200139 " Report finding another swap file and chose 'q' to avoid getting stuck.
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200140 if expand('<afile>') == 'messages' || expand('<afile>') =~ s:test_script_fname
141 let v:swapchoice = 'e'
Bram Moolenaarb073da82019-07-13 14:47:26 +0200142 else
143 call assert_report('Unexpected swap file: ' .. v:swapname)
144 let v:swapchoice = 'q'
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200145 endif
146endfunc
147
Bram Moolenaar7a073542017-02-01 23:17:36 +0100148" Avoid stopping at the "hit enter" prompt
149set nomore
150
Bram Moolenaarc0662462015-12-30 15:49:05 +0100151" Output all messages in English.
152lang mess C
153
Bram Moolenaar10e1d012020-07-18 22:03:11 +0200154" suppress menu translation
155if has('gui_running') && exists('did_install_default_menus')
156 source $VIMRUNTIME/delmenu.vim
157 set langmenu=none
158 source $VIMRUNTIME/menu.vim
159endif
160
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100161let s:srcdir = expand('%:p:h:h')
162
Bram Moolenaar2690b5a2020-07-22 18:14:58 +0200163if has('win32')
164 " avoid prompt that is long or contains a line break
165 let $PROMPT = '$P$G'
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200166 " On MS-Windows t_md and t_me are Vim specific escape sequences.
167 let s:t_bold = "\x1b[1m"
168 let s:t_normal = "\x1b[m"
169else
170 let s:t_bold = &t_md
171 let s:t_normal = &t_me
Bram Moolenaar2690b5a2020-07-22 18:14:58 +0200172endif
173
Bram Moolenaar4b2ce122020-11-21 21:41:41 +0100174if has('mac')
Dominique Pelle923dce22021-11-21 11:36:04 +0000175 " In macOS, when starting a shell in a terminal, a bash deprecation warning
Bram Moolenaar4b2ce122020-11-21 21:41:41 +0100176 " message is displayed. This breaks the terminal test. Disable the warning
177 " message.
178 let $BASH_SILENCE_DEPRECATION_WARNING = 1
179endif
180
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000181
Bram Moolenaar8e8df252016-05-25 21:23:21 +0200182" Prepare for calling test_garbagecollect_now().
zeertzjq7a734b72025-07-05 17:02:04 +0200183" Also avoids some delays in Insert mode completion.
Bram Moolenaarebf7dfa2016-04-14 12:46:51 +0200184let v:testing = 1
185
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100186" By default, copy each buffer line into allocated memory, so that valgrind can
187" detect accessing memory before and after it.
188call test_override('alloc_lines', 1)
189
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100190" Support function: get the alloc ID by name.
191function GetAllocId(name)
192 exe 'split ' . s:srcdir . '/alloc.h'
Bram Moolenaar065ee9a2016-01-15 20:53:38 +0100193 let top = search('typedef enum')
194 if top == 0
195 call add(v:errors, 'typedef not found in alloc.h')
196 endif
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100197 let lnum = search('aid_' . a:name . ',')
198 if lnum == 0
199 call add(v:errors, 'Alloc ID ' . a:name . ' not defined')
200 endif
201 close
Bram Moolenaar065ee9a2016-01-15 20:53:38 +0100202 return lnum - top - 1
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100203endfunc
204
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000205" Get the list of swap files in the current directory.
206func s:GetSwapFileList()
207 let save_dir = &directory
208 let &directory = '.'
209 let files = swapfilelist()
210 let &directory = save_dir
211
212 " remove a match with runtest.vim
213 let idx = indexof(files, 'v:val =~ "runtest.vim."')
214 if idx >= 0
215 call remove(files, idx)
216 endif
217
218 return files
219endfunc
220
Bram Moolenaar6572a902022-12-06 14:21:09 +0000221" A previous (failed) test run may have left swap files behind. Delete them
222" before running tests again, they might interfere.
223for name in s:GetSwapFileList()
224 call delete(name)
225endfor
zeertzjqa36acb72023-10-21 11:50:26 +0200226unlet! name
Bram Moolenaar6572a902022-12-06 14:21:09 +0000227
228
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100229" Invoked when a test takes too much time.
230func TestTimeout(id)
231 split test.log
232 call append(line('$'), '')
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100233
234 let text = 'Test timed out: ' .. g:testfunc
235 if g:timeout_start > 0
236 let text ..= strftime(' after %s seconds', localtime() - g:timeout_start)
237 endif
238 call append(line('$'), text)
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100239 write
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100240 call add(v:errors, text)
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100241
242 cquit! 42
243endfunc
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100244let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100245
Bram Moolenaar42205552017-03-18 19:42:22 +0100246func RunTheTest(test)
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100247 let prefix = ''
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200248 if has('reltime')
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100249 let prefix = strftime('%M:%S', localtime() - s:test_start_time) .. ' '
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100250 let g:func_start = reltime()
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200251 endif
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100252 echoconsole prefix .. 'Executing ' .. a:test
Bram Moolenaare5f2a072017-02-01 22:31:49 +0100253
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100254 if has('timers')
Christian Brabandtec7a4e42025-02-09 17:16:36 +0100255 " No test should take longer than 45 seconds. If it takes longer we
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100256 " assume we are stuck and need to break out.
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100257 let test_timeout_timer =
Christian Brabandtec7a4e42025-02-09 17:16:36 +0100258 \ timer_start(RunningWithValgrind() ? 90000 : 45000, 'TestTimeout')
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100259 let g:timeout_start = localtime()
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100260 endif
261
Bram Moolenaare5f2a072017-02-01 22:31:49 +0100262 " Avoid stopping at the "hit enter" prompt
263 set nomore
264
265 " Avoid a three second wait when a message is about to be overwritten by the
266 " mode message.
267 set noshowmode
268
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100269 " Clear any overrides, except "alloc_lines".
Bram Moolenaareb992cb2017-03-09 18:20:16 +0100270 call test_override('ALL', 0)
271
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200272 " Some tests wipe out buffers. To be consistent, always wipe out all
273 " buffers.
274 %bwipe!
275
Yee Cheng Chine70587d2025-02-13 20:55:45 +0100276 " Clear all children notifications in case there are stale ones left
277 let g:child_notification = 0
278
Bram Moolenaar209d3872017-11-16 21:52:51 +0100279 " The test may change the current directory. Save and restore the
280 " directory after executing the test.
281 let save_cwd = getcwd()
282
Aliaksei Budaveic4eb1cb2025-06-08 15:52:42 +0200283 " Permit "SetUp()" implementations to override default settings.
284 call s:SetDefaultOptionsForGUIBuilds()
285
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100286 if exists("*SetUp")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100287 try
288 call SetUp()
289 catch
290 call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
291 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100292 endif
293
zeertzjqa0e1f062023-10-18 11:50:37 +0200294 let skipped = v:false
295
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100296 au VimLeavePre * call EarlyExit(g:testfunc)
Bram Moolenaarf204e052017-10-26 17:14:01 +0200297 if a:test =~ 'Test_nocatch_'
298 " Function handles errors itself. This avoids skipping commands after the
299 " error.
Bram Moolenaar776b9542021-03-10 22:27:48 +0100300 let g:skipped_reason = ''
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100301 exe 'call ' . a:test
Bram Moolenaar776b9542021-03-10 22:27:48 +0100302 if g:skipped_reason != ''
303 call add(s:messages, ' Skipped')
304 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason)
zeertzjqa0e1f062023-10-18 11:50:37 +0200305 let skipped = v:true
Bram Moolenaar776b9542021-03-10 22:27:48 +0100306 endif
Bram Moolenaarf204e052017-10-26 17:14:01 +0200307 else
308 try
309 exe 'call ' . a:test
310 catch /^\cskipped/
311 call add(s:messages, ' Skipped')
312 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
zeertzjqa0e1f062023-10-18 11:50:37 +0200313 let skipped = v:true
Bram Moolenaarf204e052017-10-26 17:14:01 +0200314 catch
315 call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
316 endtry
317 endif
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100318 au! VimLeavePre
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100319
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100320 if a:test =~ '_terminal_'
321 " Terminal tests sometimes hang, give extra information
322 echoconsole 'After executing ' .. a:test
323 endif
324
Bram Moolenaar8ad16da2019-01-06 15:29:57 +0100325 " In case 'insertmode' was set and something went wrong, make sure it is
326 " reset to avoid trouble with anything else.
327 set noinsertmode
328
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100329 if exists("*TearDown")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100330 try
331 call TearDown()
332 catch
333 call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
334 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100335 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200336
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100337 if has('timers')
338 call timer_stop(test_timeout_timer)
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100339 let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100340 endif
341
Bram Moolenaar0b5dc642019-08-11 22:56:15 +0200342 " Clear any autocommands and put back the catch-all for SwapExists.
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200343 au!
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200344 au SwapExists * call HandleSwapExists()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200345
Bram Moolenaaref6b9792020-05-13 16:34:15 +0200346 " Check for and close any stray popup windows.
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100347 if has('popupwin')
Bram Moolenaarf05a1e52022-08-02 11:48:53 +0100348 call assert_equal([], popup_list(), 'Popup is still present')
Bram Moolenaar03a9f842020-05-13 13:40:16 +0200349 call popup_clear(1)
Bram Moolenaarae943152019-06-16 22:54:14 +0200350 endif
351
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100352 if filereadable('guidialogfile')
Bram Moolenaar217ea512022-06-14 17:13:59 +0100353 call add(v:errors, "Unexpected dialog: " .. readfile('guidialogfile')->join('<NL>'))
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100354 call delete('guidialogfile')
355 endif
356
Bram Moolenaarce11de82017-10-26 22:00:00 +0200357 " Close any extra tab pages and windows and make the current one not modified.
358 while tabpagenr('$') > 1
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200359 let winid = win_getid()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200360 quit!
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200361 if winid == win_getid()
362 echoerr 'Could not quit window'
363 break
364 endif
Bram Moolenaarce11de82017-10-26 22:00:00 +0200365 endwhile
366
Bram Moolenaar358308d2016-08-24 21:21:26 +0200367 while 1
368 let wincount = winnr('$')
369 if wincount == 1
370 break
371 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200372 bwipe!
Bram Moolenaar358308d2016-08-24 21:21:26 +0200373 if wincount == winnr('$')
374 " Did not manage to close a window.
375 only!
376 break
377 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200378 endwhile
Bram Moolenaar209d3872017-11-16 21:52:51 +0100379
380 exe 'cd ' . save_cwd
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200381
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100382 if a:test =~ '_terminal_'
383 " Terminal tests sometimes hang, give extra information
384 echoconsole 'Finished ' . a:test
385 endif
386
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200387 let message = 'Executed ' . a:test
388 if has('reltime')
Bram Moolenaar8d943792020-06-21 20:39:37 +0200389 let message ..= repeat(' ', 50 - len(message))
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100390 let time = reltime(g:func_start)
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100391 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200392 let message = s:t_bold .. message
Bram Moolenaar8d943792020-06-21 20:39:37 +0200393 endif
394 let message ..= ' in ' .. reltimestr(time) .. ' seconds'
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100395 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200396 let message ..= s:t_normal
Bram Moolenaar8d943792020-06-21 20:39:37 +0200397 endif
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200398 endif
399 call add(s:messages, message)
400 let s:done += 1
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000401
Bram Moolenaare5eae822022-12-08 16:30:16 +0000402 " close any split windows
403 while winnr('$') > 1
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100404 noswapfile bwipe!
Bram Moolenaare5eae822022-12-08 16:30:16 +0000405 endwhile
406
Bram Moolenaar23526d22022-12-05 15:50:41 +0000407 " May be editing some buffer, wipe it out. Then we may end up in another
408 " buffer, continue until we end up in an empty no-name buffer without a swap
409 " file.
410 while bufname() != '' || execute('swapname') !~ 'No swap file'
Bram Moolenaarfa2533c2022-12-05 20:58:04 +0000411 let bn = bufnr()
412
413 noswapfile bwipe!
414
415 if bn == bufnr()
416 " avoid getting stuck in the same buffer
417 break
418 endif
Bram Moolenaar23526d22022-12-05 15:50:41 +0000419 endwhile
420
zeertzjqa0e1f062023-10-18 11:50:37 +0200421 if !skipped
422 " Check if the test has left any swap files behind. Delete them before
423 " running tests again, they might interfere.
424 let swapfiles = s:GetSwapFileList()
425 if len(swapfiles) > 0
426 call add(s:messages, "Found swap files: " .. string(swapfiles))
427 for name in swapfiles
428 call delete(name)
429 endfor
430 endif
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000431 endif
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100432endfunc
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100433
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200434function Delete_Xtest_Files()
435 for file in glob('X*', v:false, v:true)
436 if file ==? 'XfakeHOME'
437 " Clean up files created by setup.vim
438 call delete('XfakeHOME', 'rf')
439 continue
440 endif
441 " call add(v:errors, file .. " exists when it shouldn't, trying to delete it!")
442 call delete(file)
443 if !empty(glob(file, v:false, v:true))
444 " call add(v:errors, file .. " still exists after trying to delete it!")
445 if has('unix')
446 call system('rm -rf ' .. file)
447 endif
448 endif
449 endfor
450endfunc
451
Bram Moolenaarce436de2020-03-21 15:17:20 +0100452func AfterTheTest(func_name)
Bram Moolenaar42205552017-03-18 19:42:22 +0100453 if len(v:errors) > 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100454 if match(s:may_fail_list, '^' .. a:func_name) >= 0
455 let s:fail_expected += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200456 call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100457 call extend(s:errors_expected, v:errors)
458 else
459 let s:fail += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200460 call add(s:errors, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100461 call extend(s:errors, v:errors)
462 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100463 let v:errors = []
464 endif
465endfunc
466
Bram Moolenaar89036762018-06-12 14:58:39 +0200467func EarlyExit(test)
468 " It's OK for the test we use to test the quit detection.
469 if a:test != 'Test_zz_quit_detected()'
Bram Moolenaar1c67f3a2021-12-30 13:32:09 +0000470 call add(v:errors, v:errmsg)
Bram Moolenaar89036762018-06-12 14:58:39 +0200471 call add(v:errors, 'Test caused Vim to exit: ' . a:test)
472 endif
473
474 call FinishTesting()
475endfunc
476
Bram Moolenaar42205552017-03-18 19:42:22 +0100477" This function can be called by a test if it wants to abort testing.
478func FinishTesting()
Bram Moolenaarce436de2020-03-21 15:17:20 +0100479 call AfterTheTest('')
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200480 call Delete_Xtest_Files()
Bram Moolenaar42205552017-03-18 19:42:22 +0100481
482 " Don't write viminfo on exit.
483 set viminfo=
484
Bram Moolenaarce436de2020-03-21 15:17:20 +0100485 if s:fail == 0 && s:fail_expected == 0
Bram Moolenaar42205552017-03-18 19:42:22 +0100486 " Success, create the .res file so that make knows it's done.
487 exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
488 write
489 endif
490
491 if len(s:errors) > 0
492 " Append errors to test.log
493 split test.log
494 call append(line('$'), '')
495 call append(line('$'), 'From ' . g:testname . ':')
496 call append(line('$'), s:errors)
497 write
498 endif
499
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200500 if s:done == 0
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200501 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200502 if $TEST_FILTER != ''
503 let message = "NO tests match $TEST_FILTER: '" .. $TEST_FILTER .. "'"
504 else
505 let message = "ALL tests match $TEST_SKIP_PAT: '" .. $TEST_SKIP_PAT .. "'"
506 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200507 else
508 let message = 'NO tests executed'
509 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200510 else
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200511 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200512 call add(s:messages, "Filtered " .. s:filtered .. " tests with $TEST_FILTER and $TEST_SKIP_PAT")
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200513 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200514 let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
515 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200516 if s:done > 0 && has('reltime')
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200517 let message = s:t_bold .. message .. repeat(' ', 40 - len(message))
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100518 let message ..= ' in ' .. reltimestr(reltime(s:run_start_time)) .. ' seconds'
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200519 let message ..= s:t_normal
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200520 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100521 echo message
522 call add(s:messages, message)
523 if s:fail > 0
524 let message = s:fail . ' FAILED:'
525 echo message
526 call add(s:messages, message)
527 call extend(s:messages, s:errors)
528 endif
Bram Moolenaarce436de2020-03-21 15:17:20 +0100529 if s:fail_expected > 0
530 let message = s:fail_expected . ' FAILED (matching $TEST_MAY_FAIL):'
531 echo message
532 call add(s:messages, message)
533 call extend(s:messages, s:errors_expected)
534 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100535
536 " Add SKIPPED messages
537 call extend(s:messages, s:skipped)
538
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200539 " Append messages to the file "messages", but remove ANSI Escape sequences
Bram Moolenaar42205552017-03-18 19:42:22 +0100540 split messages
541 call append(line('$'), '')
542 call append(line('$'), 'From ' . g:testname . ':')
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200543 call append(line('$'), s:messages->map({_, val -> substitute(val, '\%x1b\[\d\?m', '', 'g')}))
Bram Moolenaar42205552017-03-18 19:42:22 +0100544 write
545
546 qall!
547endfunc
548
Bram Moolenaar43345542015-11-29 17:35:35 +0100549" Source the test script. First grab the file name, in case the script
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100550" navigates away. g:testname can be used by the tests.
551let g:testname = expand('%')
552let s:done = 0
553let s:fail = 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100554let s:fail_expected = 0
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100555let s:errors = []
Bram Moolenaarce436de2020-03-21 15:17:20 +0100556let s:errors_expected = []
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100557let s:messages = []
Bram Moolenaardac19472016-09-03 22:35:40 +0200558let s:skipped = []
Bram Moolenaarb544f3c2017-02-23 19:03:28 +0100559if expand('%') =~ 'test_vimscript.vim'
Bram Moolenaarce436de2020-03-21 15:17:20 +0100560 " this test has intentional errors, don't use try/catch.
Bram Moolenaar4686b322015-12-28 14:44:10 +0100561 source %
Bram Moolenaara2cce862016-01-02 19:50:04 +0100562else
563 try
564 source %
Bram Moolenaar9c0cec62019-06-06 13:38:15 +0200565 catch /^\cskipped/
566 call add(s:messages, ' Skipped')
567 call add(s:skipped, 'SKIPPED ' . expand('%') . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
Bram Moolenaara2cce862016-01-02 19:50:04 +0100568 catch
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100569 let s:fail += 1
570 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
Bram Moolenaara2cce862016-01-02 19:50:04 +0100571 endtry
572endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100573
Bram Moolenaar7e0be3e2022-03-20 13:40:41 +0000574" Delete the .res file, it may change behavior for completion
575call delete(fnamemodify(g:testname, ':r') .. '.res')
576
Bram Moolenaar43345542015-11-29 17:35:35 +0100577" Locate Test_ functions and execute them.
578redir @q
Bram Moolenaar93bf5582016-02-18 22:25:47 +0100579silent function /^Test_
Bram Moolenaar43345542015-11-29 17:35:35 +0100580redir END
Bram Moolenaar61a6d4e2020-03-01 23:32:25 +0100581let s:tests = split(substitute(@q, '\(function\|def\) \(\k*()\)', '\2', 'g'))
Bram Moolenaar43345542015-11-29 17:35:35 +0100582
Bram Moolenaarbefb3662016-02-20 14:41:40 +0100583" If there is an extra argument filter the function names against it.
584if argc() > 1
585 let s:tests = filter(s:tests, 'v:val =~ argv(1)')
586endif
587
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200588" If the environment variable $TEST_FILTER is set then filter the function
589" names against it.
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200590let s:filtered = 0
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200591if $TEST_FILTER != ''
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200592 let s:filtered = len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200593 let s:tests = filter(s:tests, 'v:val =~ $TEST_FILTER')
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200594 let s:filtered -= len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200595endif
596
Bram Moolenaarce436de2020-03-21 15:17:20 +0100597let s:may_fail_list = []
598if $TEST_MAY_FAIL != ''
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200599 " Split the list at commas and add () to make it match g:testfunc.
Bram Moolenaarce436de2020-03-21 15:17:20 +0100600 let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
601endif
602
Bram Moolenaarcfc0a352016-01-09 20:23:00 +0100603" Execute the tests in alphabetical order.
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200604for g:testfunc in sort(s:tests)
Bram Moolenaardae453f2021-08-07 17:20:16 +0200605 if $TEST_SKIP_PAT != '' && g:testfunc =~ $TEST_SKIP_PAT
606 call add(s:messages, g:testfunc .. ' matches $TEST_SKIP_PAT')
607 let s:filtered += 1
608 continue
609 endif
610
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200611 " Silence, please!
612 set belloff=all
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100613 let prev_error = ''
614 let total_errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100615 let g:run_nr = 1
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200616
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200617 " A test can set g:test_is_flaky to retry running the test.
618 let g:test_is_flaky = 0
Bram Moolenaar3cdcb092020-03-18 19:18:10 +0100619
Drew Vogelea67ba72025-05-07 22:05:17 +0200620 let g:check_screendump_called = v:false
621
Millybaab7c02024-10-28 21:56:14 +0100622 " A test can set g:max_run_nr to change the max retry count.
623 let g:max_run_nr = 5
624 if has('mac')
625 let g:max_run_nr = 10
626 endif
627
628 " By default, give up if the same error occurs. A test can set
629 " g:giveup_same_error to 0 to not give up on the same error and keep trying.
630 let g:giveup_same_error = 1
631
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100632 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200633 call RunTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100634
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100635 " Repeat a flaky test. Give up when:
Bram Moolenaar6ca6ca42020-07-27 19:47:07 +0200636 " - $TEST_NO_RETRY is not empty
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100637 " - it fails again with the same message
Bram Moolenaar1bc353b2019-09-01 14:45:28 +0200638 " - it fails five times (with a different message)
Bram Moolenaardbc0d212018-11-16 18:22:45 +0100639 if len(v:errors) > 0
Bram Moolenaar622b3562020-07-27 20:02:41 +0200640 \ && $TEST_NO_RETRY == ''
Bram Moolenaarf08b0eb2021-10-16 13:00:14 +0100641 \ && g:test_is_flaky
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100642 while 1
Bram Moolenaar06d32a02022-09-03 13:58:47 +0100643 call add(s:messages, 'Found errors in ' .. g:testfunc .. ':')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100644 call extend(s:messages, v:errors)
Bram Moolenaar15e737f2017-03-18 21:22:47 +0100645
Bram Moolenaar65258d32022-09-09 15:09:59 +0100646 let endtime = strftime("%H:%M:%S")
Millybaab7c02024-10-28 21:56:14 +0100647 if has('reltime')
648 let suffix = $' in{reltimestr(reltime(g:func_start))} seconds'
649 else
650 let suffix = ''
651 endif
652 call add(total_errors, $'Run {g:run_nr}, {starttime} - {endtime}{suffix}:')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100653 call extend(total_errors, v:errors)
Bram Moolenaar55058602017-11-21 15:14:51 +0100654
Millybaab7c02024-10-28 21:56:14 +0100655 if g:run_nr >= g:max_run_nr || g:giveup_same_error && prev_error == v:errors[0]
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100656 call add(total_errors, 'Flaky test failed too often, giving up')
657 let v:errors = total_errors
658 break
659 endif
660
661 call add(s:messages, 'Flaky test failed, running it again')
662
663 " Flakiness is often caused by the system being very busy. Sleep a
664 " couple of seconds to have a higher chance of succeeding the second
665 " time.
Millybaab7c02024-10-28 21:56:14 +0100666 let delay = g:run_nr * 2
667 exe 'sleep' delay
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100668
669 let prev_error = v:errors[0]
670 let v:errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100671 let g:run_nr += 1
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100672
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100673 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200674 call RunTheTest(g:testfunc)
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100675
676 if len(v:errors) == 0
677 " Test passed on rerun.
678 break
679 endif
680 endwhile
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100681 endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100682
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200683 call AfterTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100684endfor
685
Bram Moolenaar42205552017-03-18 19:42:22 +0100686call FinishTesting()
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100687
688" vim: shiftwidth=2 sts=2 expandtab