blob: f03fe44499e1b0db3afb745ec53fc38364f3784b [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().
Bram Moolenaarebf7dfa2016-04-14 12:46:51 +0200183let v:testing = 1
184
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100185" By default, copy each buffer line into allocated memory, so that valgrind can
186" detect accessing memory before and after it.
187call test_override('alloc_lines', 1)
188
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100189" Support function: get the alloc ID by name.
190function GetAllocId(name)
191 exe 'split ' . s:srcdir . '/alloc.h'
Bram Moolenaar065ee9a2016-01-15 20:53:38 +0100192 let top = search('typedef enum')
193 if top == 0
194 call add(v:errors, 'typedef not found in alloc.h')
195 endif
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100196 let lnum = search('aid_' . a:name . ',')
197 if lnum == 0
198 call add(v:errors, 'Alloc ID ' . a:name . ' not defined')
199 endif
200 close
Bram Moolenaar065ee9a2016-01-15 20:53:38 +0100201 return lnum - top - 1
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100202endfunc
203
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000204" Get the list of swap files in the current directory.
205func s:GetSwapFileList()
206 let save_dir = &directory
207 let &directory = '.'
208 let files = swapfilelist()
209 let &directory = save_dir
210
211 " remove a match with runtest.vim
212 let idx = indexof(files, 'v:val =~ "runtest.vim."')
213 if idx >= 0
214 call remove(files, idx)
215 endif
216
217 return files
218endfunc
219
Bram Moolenaar6572a902022-12-06 14:21:09 +0000220" A previous (failed) test run may have left swap files behind. Delete them
221" before running tests again, they might interfere.
222for name in s:GetSwapFileList()
223 call delete(name)
224endfor
zeertzjqa36acb72023-10-21 11:50:26 +0200225unlet! name
Bram Moolenaar6572a902022-12-06 14:21:09 +0000226
227
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100228" Invoked when a test takes too much time.
229func TestTimeout(id)
230 split test.log
231 call append(line('$'), '')
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100232
233 let text = 'Test timed out: ' .. g:testfunc
234 if g:timeout_start > 0
235 let text ..= strftime(' after %s seconds', localtime() - g:timeout_start)
236 endif
237 call append(line('$'), text)
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100238 write
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100239 call add(v:errors, text)
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100240
241 cquit! 42
242endfunc
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100243let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100244
Bram Moolenaar42205552017-03-18 19:42:22 +0100245func RunTheTest(test)
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100246 let prefix = ''
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200247 if has('reltime')
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100248 let prefix = strftime('%M:%S', localtime() - s:test_start_time) .. ' '
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100249 let g:func_start = reltime()
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200250 endif
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100251 echoconsole prefix .. 'Executing ' .. a:test
Bram Moolenaare5f2a072017-02-01 22:31:49 +0100252
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100253 if has('timers')
Christian Brabandtec7a4e42025-02-09 17:16:36 +0100254 " No test should take longer than 45 seconds. If it takes longer we
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100255 " assume we are stuck and need to break out.
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100256 let test_timeout_timer =
Christian Brabandtec7a4e42025-02-09 17:16:36 +0100257 \ timer_start(RunningWithValgrind() ? 90000 : 45000, 'TestTimeout')
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100258 let g:timeout_start = localtime()
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100259 endif
260
Bram Moolenaare5f2a072017-02-01 22:31:49 +0100261 " Avoid stopping at the "hit enter" prompt
262 set nomore
263
264 " Avoid a three second wait when a message is about to be overwritten by the
265 " mode message.
266 set noshowmode
267
Bram Moolenaarfa4873c2022-06-30 22:13:59 +0100268 " Clear any overrides, except "alloc_lines".
Bram Moolenaareb992cb2017-03-09 18:20:16 +0100269 call test_override('ALL', 0)
270
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200271 " Some tests wipe out buffers. To be consistent, always wipe out all
272 " buffers.
273 %bwipe!
274
Yee Cheng Chine70587d2025-02-13 20:55:45 +0100275 " Clear all children notifications in case there are stale ones left
276 let g:child_notification = 0
277
Bram Moolenaar209d3872017-11-16 21:52:51 +0100278 " The test may change the current directory. Save and restore the
279 " directory after executing the test.
280 let save_cwd = getcwd()
281
Aliaksei Budaveic4eb1cb2025-06-08 15:52:42 +0200282 " Permit "SetUp()" implementations to override default settings.
283 call s:SetDefaultOptionsForGUIBuilds()
284
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100285 if exists("*SetUp")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100286 try
287 call SetUp()
288 catch
289 call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
290 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100291 endif
292
zeertzjqa0e1f062023-10-18 11:50:37 +0200293 let skipped = v:false
294
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100295 au VimLeavePre * call EarlyExit(g:testfunc)
Bram Moolenaarf204e052017-10-26 17:14:01 +0200296 if a:test =~ 'Test_nocatch_'
297 " Function handles errors itself. This avoids skipping commands after the
298 " error.
Bram Moolenaar776b9542021-03-10 22:27:48 +0100299 let g:skipped_reason = ''
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100300 exe 'call ' . a:test
Bram Moolenaar776b9542021-03-10 22:27:48 +0100301 if g:skipped_reason != ''
302 call add(s:messages, ' Skipped')
303 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason)
zeertzjqa0e1f062023-10-18 11:50:37 +0200304 let skipped = v:true
Bram Moolenaar776b9542021-03-10 22:27:48 +0100305 endif
Bram Moolenaarf204e052017-10-26 17:14:01 +0200306 else
307 try
308 exe 'call ' . a:test
309 catch /^\cskipped/
310 call add(s:messages, ' Skipped')
311 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
zeertzjqa0e1f062023-10-18 11:50:37 +0200312 let skipped = v:true
Bram Moolenaarf204e052017-10-26 17:14:01 +0200313 catch
314 call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
315 endtry
316 endif
Bram Moolenaar8bea1712022-06-15 20:49:35 +0100317 au! VimLeavePre
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100318
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100319 if a:test =~ '_terminal_'
320 " Terminal tests sometimes hang, give extra information
321 echoconsole 'After executing ' .. a:test
322 endif
323
Bram Moolenaar8ad16da2019-01-06 15:29:57 +0100324 " In case 'insertmode' was set and something went wrong, make sure it is
325 " reset to avoid trouble with anything else.
326 set noinsertmode
327
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100328 if exists("*TearDown")
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100329 try
330 call TearDown()
331 catch
332 call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
333 endtry
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100334 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200335
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100336 if has('timers')
337 call timer_stop(test_timeout_timer)
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100338 let g:timeout_start = 0
Bram Moolenaar3bcd0dd2022-09-23 20:25:55 +0100339 endif
340
Bram Moolenaar0b5dc642019-08-11 22:56:15 +0200341 " Clear any autocommands and put back the catch-all for SwapExists.
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200342 au!
Bram Moolenaard8f27b32018-10-07 15:42:07 +0200343 au SwapExists * call HandleSwapExists()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200344
Bram Moolenaaref6b9792020-05-13 16:34:15 +0200345 " Check for and close any stray popup windows.
Bram Moolenaar05ad5ff2019-11-30 22:48:27 +0100346 if has('popupwin')
Bram Moolenaarf05a1e52022-08-02 11:48:53 +0100347 call assert_equal([], popup_list(), 'Popup is still present')
Bram Moolenaar03a9f842020-05-13 13:40:16 +0200348 call popup_clear(1)
Bram Moolenaarae943152019-06-16 22:54:14 +0200349 endif
350
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100351 if filereadable('guidialogfile')
Bram Moolenaar217ea512022-06-14 17:13:59 +0100352 call add(v:errors, "Unexpected dialog: " .. readfile('guidialogfile')->join('<NL>'))
Bram Moolenaar2d12c252022-06-13 21:42:45 +0100353 call delete('guidialogfile')
354 endif
355
Bram Moolenaarce11de82017-10-26 22:00:00 +0200356 " Close any extra tab pages and windows and make the current one not modified.
357 while tabpagenr('$') > 1
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200358 let winid = win_getid()
Bram Moolenaarcf1ba352017-10-27 00:55:04 +0200359 quit!
Bram Moolenaarbdf931c2020-10-01 22:37:40 +0200360 if winid == win_getid()
361 echoerr 'Could not quit window'
362 break
363 endif
Bram Moolenaarce11de82017-10-26 22:00:00 +0200364 endwhile
365
Bram Moolenaar358308d2016-08-24 21:21:26 +0200366 while 1
367 let wincount = winnr('$')
368 if wincount == 1
369 break
370 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200371 bwipe!
Bram Moolenaar358308d2016-08-24 21:21:26 +0200372 if wincount == winnr('$')
373 " Did not manage to close a window.
374 only!
375 break
376 endif
Bram Moolenaar7cba71d2016-08-02 23:04:49 +0200377 endwhile
Bram Moolenaar209d3872017-11-16 21:52:51 +0100378
379 exe 'cd ' . save_cwd
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200380
Bram Moolenaara22c56a2022-09-20 15:10:31 +0100381 if a:test =~ '_terminal_'
382 " Terminal tests sometimes hang, give extra information
383 echoconsole 'Finished ' . a:test
384 endif
385
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200386 let message = 'Executed ' . a:test
387 if has('reltime')
Bram Moolenaar8d943792020-06-21 20:39:37 +0200388 let message ..= repeat(' ', 50 - len(message))
Bram Moolenaardaaa3d92022-09-22 15:13:00 +0100389 let time = reltime(g:func_start)
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100390 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200391 let message = s:t_bold .. message
Bram Moolenaar8d943792020-06-21 20:39:37 +0200392 endif
393 let message ..= ' in ' .. reltimestr(time) .. ' seconds'
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100394 if reltimefloat(time) > 0.1
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200395 let message ..= s:t_normal
Bram Moolenaar8d943792020-06-21 20:39:37 +0200396 endif
Bram Moolenaar640d4f02019-06-10 17:43:46 +0200397 endif
398 call add(s:messages, message)
399 let s:done += 1
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000400
Bram Moolenaare5eae822022-12-08 16:30:16 +0000401 " close any split windows
402 while winnr('$') > 1
Bram Moolenaar7c2beb42023-07-08 00:25:56 +0100403 noswapfile bwipe!
Bram Moolenaare5eae822022-12-08 16:30:16 +0000404 endwhile
405
Bram Moolenaar23526d22022-12-05 15:50:41 +0000406 " May be editing some buffer, wipe it out. Then we may end up in another
407 " buffer, continue until we end up in an empty no-name buffer without a swap
408 " file.
409 while bufname() != '' || execute('swapname') !~ 'No swap file'
Bram Moolenaarfa2533c2022-12-05 20:58:04 +0000410 let bn = bufnr()
411
412 noswapfile bwipe!
413
414 if bn == bufnr()
415 " avoid getting stuck in the same buffer
416 break
417 endif
Bram Moolenaar23526d22022-12-05 15:50:41 +0000418 endwhile
419
zeertzjqa0e1f062023-10-18 11:50:37 +0200420 if !skipped
421 " Check if the test has left any swap files behind. Delete them before
422 " running tests again, they might interfere.
423 let swapfiles = s:GetSwapFileList()
424 if len(swapfiles) > 0
425 call add(s:messages, "Found swap files: " .. string(swapfiles))
426 for name in swapfiles
427 call delete(name)
428 endfor
429 endif
Bram Moolenaarc216a7a2022-12-05 13:50:55 +0000430 endif
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100431endfunc
Bram Moolenaar28fb79d2016-01-09 22:28:33 +0100432
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200433function Delete_Xtest_Files()
434 for file in glob('X*', v:false, v:true)
435 if file ==? 'XfakeHOME'
436 " Clean up files created by setup.vim
437 call delete('XfakeHOME', 'rf')
438 continue
439 endif
440 " call add(v:errors, file .. " exists when it shouldn't, trying to delete it!")
441 call delete(file)
442 if !empty(glob(file, v:false, v:true))
443 " call add(v:errors, file .. " still exists after trying to delete it!")
444 if has('unix')
445 call system('rm -rf ' .. file)
446 endif
447 endif
448 endfor
449endfunc
450
Bram Moolenaarce436de2020-03-21 15:17:20 +0100451func AfterTheTest(func_name)
Bram Moolenaar42205552017-03-18 19:42:22 +0100452 if len(v:errors) > 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100453 if match(s:may_fail_list, '^' .. a:func_name) >= 0
454 let s:fail_expected += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200455 call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100456 call extend(s:errors_expected, v:errors)
457 else
458 let s:fail += 1
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200459 call add(s:errors, 'Found errors in ' . g:testfunc . ':')
Bram Moolenaarce436de2020-03-21 15:17:20 +0100460 call extend(s:errors, v:errors)
461 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100462 let v:errors = []
463 endif
464endfunc
465
Bram Moolenaar89036762018-06-12 14:58:39 +0200466func EarlyExit(test)
467 " It's OK for the test we use to test the quit detection.
468 if a:test != 'Test_zz_quit_detected()'
Bram Moolenaar1c67f3a2021-12-30 13:32:09 +0000469 call add(v:errors, v:errmsg)
Bram Moolenaar89036762018-06-12 14:58:39 +0200470 call add(v:errors, 'Test caused Vim to exit: ' . a:test)
471 endif
472
473 call FinishTesting()
474endfunc
475
Bram Moolenaar42205552017-03-18 19:42:22 +0100476" This function can be called by a test if it wants to abort testing.
477func FinishTesting()
Bram Moolenaarce436de2020-03-21 15:17:20 +0100478 call AfterTheTest('')
Christian Brabandt84bc00e2023-07-13 11:45:54 +0200479 call Delete_Xtest_Files()
Bram Moolenaar42205552017-03-18 19:42:22 +0100480
481 " Don't write viminfo on exit.
482 set viminfo=
483
Bram Moolenaarce436de2020-03-21 15:17:20 +0100484 if s:fail == 0 && s:fail_expected == 0
Bram Moolenaar42205552017-03-18 19:42:22 +0100485 " Success, create the .res file so that make knows it's done.
486 exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
487 write
488 endif
489
490 if len(s:errors) > 0
491 " Append errors to test.log
492 split test.log
493 call append(line('$'), '')
494 call append(line('$'), 'From ' . g:testname . ':')
495 call append(line('$'), s:errors)
496 write
497 endif
498
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200499 if s:done == 0
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200500 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200501 if $TEST_FILTER != ''
502 let message = "NO tests match $TEST_FILTER: '" .. $TEST_FILTER .. "'"
503 else
504 let message = "ALL tests match $TEST_SKIP_PAT: '" .. $TEST_SKIP_PAT .. "'"
505 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200506 else
507 let message = 'NO tests executed'
508 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200509 else
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200510 if s:filtered > 0
Bram Moolenaardae453f2021-08-07 17:20:16 +0200511 call add(s:messages, "Filtered " .. s:filtered .. " tests with $TEST_FILTER and $TEST_SKIP_PAT")
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200512 endif
Bram Moolenaar29f9ed22018-04-10 19:20:31 +0200513 let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
514 endif
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200515 if s:done > 0 && has('reltime')
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200516 let message = s:t_bold .. message .. repeat(' ', 40 - len(message))
Bram Moolenaarb9093d52022-09-23 19:42:31 +0100517 let message ..= ' in ' .. reltimestr(reltime(s:run_start_time)) .. ' seconds'
Bram Moolenaar45df2a02020-07-29 15:03:01 +0200518 let message ..= s:t_normal
Bram Moolenaar75ee5442019-06-06 18:05:25 +0200519 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100520 echo message
521 call add(s:messages, message)
522 if s:fail > 0
523 let message = s:fail . ' FAILED:'
524 echo message
525 call add(s:messages, message)
526 call extend(s:messages, s:errors)
527 endif
Bram Moolenaarce436de2020-03-21 15:17:20 +0100528 if s:fail_expected > 0
529 let message = s:fail_expected . ' FAILED (matching $TEST_MAY_FAIL):'
530 echo message
531 call add(s:messages, message)
532 call extend(s:messages, s:errors_expected)
533 endif
Bram Moolenaar42205552017-03-18 19:42:22 +0100534
535 " Add SKIPPED messages
536 call extend(s:messages, s:skipped)
537
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200538 " Append messages to the file "messages", but remove ANSI Escape sequences
Bram Moolenaar42205552017-03-18 19:42:22 +0100539 split messages
540 call append(line('$'), '')
541 call append(line('$'), 'From ' . g:testname . ':')
Christian Brabandt075ab5a2024-10-03 16:38:52 +0200542 call append(line('$'), s:messages->map({_, val -> substitute(val, '\%x1b\[\d\?m', '', 'g')}))
Bram Moolenaar42205552017-03-18 19:42:22 +0100543 write
544
545 qall!
546endfunc
547
Bram Moolenaar43345542015-11-29 17:35:35 +0100548" Source the test script. First grab the file name, in case the script
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100549" navigates away. g:testname can be used by the tests.
550let g:testname = expand('%')
551let s:done = 0
552let s:fail = 0
Bram Moolenaarce436de2020-03-21 15:17:20 +0100553let s:fail_expected = 0
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100554let s:errors = []
Bram Moolenaarce436de2020-03-21 15:17:20 +0100555let s:errors_expected = []
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100556let s:messages = []
Bram Moolenaardac19472016-09-03 22:35:40 +0200557let s:skipped = []
Bram Moolenaarb544f3c2017-02-23 19:03:28 +0100558if expand('%') =~ 'test_vimscript.vim'
Bram Moolenaarce436de2020-03-21 15:17:20 +0100559 " this test has intentional errors, don't use try/catch.
Bram Moolenaar4686b322015-12-28 14:44:10 +0100560 source %
Bram Moolenaara2cce862016-01-02 19:50:04 +0100561else
562 try
563 source %
Bram Moolenaar9c0cec62019-06-06 13:38:15 +0200564 catch /^\cskipped/
565 call add(s:messages, ' Skipped')
566 call add(s:skipped, 'SKIPPED ' . expand('%') . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
Bram Moolenaara2cce862016-01-02 19:50:04 +0100567 catch
Bram Moolenaar00af60b2016-02-13 14:06:14 +0100568 let s:fail += 1
569 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint)
Bram Moolenaara2cce862016-01-02 19:50:04 +0100570 endtry
571endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100572
Bram Moolenaar7e0be3e2022-03-20 13:40:41 +0000573" Delete the .res file, it may change behavior for completion
574call delete(fnamemodify(g:testname, ':r') .. '.res')
575
Bram Moolenaar43345542015-11-29 17:35:35 +0100576" Locate Test_ functions and execute them.
577redir @q
Bram Moolenaar93bf5582016-02-18 22:25:47 +0100578silent function /^Test_
Bram Moolenaar43345542015-11-29 17:35:35 +0100579redir END
Bram Moolenaar61a6d4e2020-03-01 23:32:25 +0100580let s:tests = split(substitute(@q, '\(function\|def\) \(\k*()\)', '\2', 'g'))
Bram Moolenaar43345542015-11-29 17:35:35 +0100581
Bram Moolenaarbefb3662016-02-20 14:41:40 +0100582" If there is an extra argument filter the function names against it.
583if argc() > 1
584 let s:tests = filter(s:tests, 'v:val =~ argv(1)')
585endif
586
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200587" If the environment variable $TEST_FILTER is set then filter the function
588" names against it.
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200589let s:filtered = 0
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200590if $TEST_FILTER != ''
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200591 let s:filtered = len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200592 let s:tests = filter(s:tests, 'v:val =~ $TEST_FILTER')
Bram Moolenaar7b666c72019-09-27 21:25:00 +0200593 let s:filtered -= len(s:tests)
Bram Moolenaara7f6c3c2019-09-27 15:34:16 +0200594endif
595
Bram Moolenaarce436de2020-03-21 15:17:20 +0100596let s:may_fail_list = []
597if $TEST_MAY_FAIL != ''
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200598 " Split the list at commas and add () to make it match g:testfunc.
Bram Moolenaarce436de2020-03-21 15:17:20 +0100599 let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
600endif
601
Bram Moolenaarcfc0a352016-01-09 20:23:00 +0100602" Execute the tests in alphabetical order.
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200603for g:testfunc in sort(s:tests)
Bram Moolenaardae453f2021-08-07 17:20:16 +0200604 if $TEST_SKIP_PAT != '' && g:testfunc =~ $TEST_SKIP_PAT
605 call add(s:messages, g:testfunc .. ' matches $TEST_SKIP_PAT')
606 let s:filtered += 1
607 continue
608 endif
609
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200610 " Silence, please!
611 set belloff=all
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100612 let prev_error = ''
613 let total_errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100614 let g:run_nr = 1
Bram Moolenaar4a6fcf82017-10-12 21:29:22 +0200615
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200616 " A test can set g:test_is_flaky to retry running the test.
617 let g:test_is_flaky = 0
Bram Moolenaar3cdcb092020-03-18 19:18:10 +0100618
Drew Vogelea67ba72025-05-07 22:05:17 +0200619 let g:check_screendump_called = v:false
620
Millybaab7c02024-10-28 21:56:14 +0100621 " A test can set g:max_run_nr to change the max retry count.
622 let g:max_run_nr = 5
623 if has('mac')
624 let g:max_run_nr = 10
625 endif
626
627 " By default, give up if the same error occurs. A test can set
628 " g:giveup_same_error to 0 to not give up on the same error and keep trying.
629 let g:giveup_same_error = 1
630
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100631 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200632 call RunTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100633
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100634 " Repeat a flaky test. Give up when:
Bram Moolenaar6ca6ca42020-07-27 19:47:07 +0200635 " - $TEST_NO_RETRY is not empty
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100636 " - it fails again with the same message
Bram Moolenaar1bc353b2019-09-01 14:45:28 +0200637 " - it fails five times (with a different message)
Bram Moolenaardbc0d212018-11-16 18:22:45 +0100638 if len(v:errors) > 0
Bram Moolenaar622b3562020-07-27 20:02:41 +0200639 \ && $TEST_NO_RETRY == ''
Bram Moolenaarf08b0eb2021-10-16 13:00:14 +0100640 \ && g:test_is_flaky
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100641 while 1
Bram Moolenaar06d32a02022-09-03 13:58:47 +0100642 call add(s:messages, 'Found errors in ' .. g:testfunc .. ':')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100643 call extend(s:messages, v:errors)
Bram Moolenaar15e737f2017-03-18 21:22:47 +0100644
Bram Moolenaar65258d32022-09-09 15:09:59 +0100645 let endtime = strftime("%H:%M:%S")
Millybaab7c02024-10-28 21:56:14 +0100646 if has('reltime')
647 let suffix = $' in{reltimestr(reltime(g:func_start))} seconds'
648 else
649 let suffix = ''
650 endif
651 call add(total_errors, $'Run {g:run_nr}, {starttime} - {endtime}{suffix}:')
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100652 call extend(total_errors, v:errors)
Bram Moolenaar55058602017-11-21 15:14:51 +0100653
Millybaab7c02024-10-28 21:56:14 +0100654 if g:run_nr >= g:max_run_nr || g:giveup_same_error && prev_error == v:errors[0]
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100655 call add(total_errors, 'Flaky test failed too often, giving up')
656 let v:errors = total_errors
657 break
658 endif
659
660 call add(s:messages, 'Flaky test failed, running it again')
661
662 " Flakiness is often caused by the system being very busy. Sleep a
663 " couple of seconds to have a higher chance of succeeding the second
664 " time.
Millybaab7c02024-10-28 21:56:14 +0100665 let delay = g:run_nr * 2
666 exe 'sleep' delay
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100667
668 let prev_error = v:errors[0]
669 let v:errors = []
Bram Moolenaar3ed9efc2020-03-26 16:50:57 +0100670 let g:run_nr += 1
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100671
Bram Moolenaar5fbbec12022-09-03 22:08:11 +0100672 let starttime = strftime("%H:%M:%S")
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200673 call RunTheTest(g:testfunc)
Bram Moolenaarf77af0e2018-11-16 16:52:16 +0100674
675 if len(v:errors) == 0
676 " Test passed on rerun.
677 break
678 endif
679 endwhile
Bram Moolenaarb5760a12016-03-03 13:10:44 +0100680 endif
Bram Moolenaar43345542015-11-29 17:35:35 +0100681
Bram Moolenaarbfe13cc2020-04-12 17:53:12 +0200682 call AfterTheTest(g:testfunc)
Bram Moolenaar43345542015-11-29 17:35:35 +0100683endfor
684
Bram Moolenaar42205552017-03-18 19:42:22 +0100685call FinishTesting()
Bram Moolenaarcc28e2d2016-11-17 17:56:13 +0100686
687" vim: shiftwidth=2 sts=2 expandtab