patch 9.1.1110: Vim tests are slow and flaky

Problem:  Vim tests are slow and flaky at the same time due to reliance
          on timeouts which are unreliable.
Solution: improve Vim test performance and reduce flakiness
          (Yee Cheng Chin)

A lot of Vim tests currently rely on waiting a specific amount of time
before asserting a condition. This is bad because 1) it is slow, as the
timeout is hardcoded, 2) it's unreliable as a resource-starved runner
may overshoot the timeout. Also, there are a lot of builtin sleep
commands in commonly used utilities like VerifyScreenDump and WaitFor()
which leads to a lot of unnecessary idle time.

Fix these issues by doing the following:
1. Make utilities like VerifyScreenDump and WaitFor use the lowest wait
   time possible (1 ms). This essentially turns it into a spin wait. On
   fast machines, these will finish very quickly. For existing tests
   that had an implicit reliance on the old timeouts (e.g.
   VerifyScreenDump had a 50ms wait before), fix the tests to wait that
   specific amount explicitly.
2. Fix tests that sleep or wait for long amounts of time to instead
   explicitly use a callback mechanism to be notified when a child
   terminal job has finished. This allows the test to only take as much
   time as possible instead of having to hard code an unreliable
   timeout.

With these fixes, tests should 1) completely quickly on fast machines,
and 2) on slow machines they will still run to completion albeit slowly.
Note that previoulsy both were not true. The hardcoded timeouts meant
that on fast machines the tests were mostly idling wasting time, whereas
on slow machines, the timeouts often were not generous enough to allow
them to run to completion.

closes: #16615

Signed-off-by: Yee Cheng Chin <ychin.git@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yee Cheng Chin
2025-02-13 20:55:45 +01:00
committed by Christian Brabandt
parent 977561a719
commit e70587dbdb
17 changed files with 162 additions and 145 deletions

View File

@ -1,4 +1,4 @@
*terminal.txt* For Vim version 9.1. Last change: 2025 Jan 11 *terminal.txt* For Vim version 9.1. Last change: 2025 Feb 13
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -999,7 +999,8 @@ term_wait({buf} [, {time}]) *term_wait()*
Wait for pending updates of {buf} to be handled. Wait for pending updates of {buf} to be handled.
{buf} is used as with |term_getsize()|. {buf} is used as with |term_getsize()|.
{time} is how long to wait for updates to arrive in msec. If {time} is how long to wait for updates to arrive in msec. If
not set then 10 msec will be used. not set then 10 msec will be used. Queued messages will also
be processed similar to |:sleep|.
Can also be used as a |method|: > Can also be used as a |method|: >
GetBufnr()->term_wait() GetBufnr()->term_wait()

View File

@ -1,4 +1,4 @@
*various.txt* For Vim version 9.1. Last change: 2024 Dec 16 *various.txt* For Vim version 9.1. Last change: 2025 Feb 13
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -765,9 +765,8 @@ K Run a program to lookup the keyword under the
MS-Windows). "gs" stands for "goto sleep". MS-Windows). "gs" stands for "goto sleep".
While sleeping the cursor is positioned in the text, While sleeping the cursor is positioned in the text,
if at a visible position. if at a visible position.
Also process the received netbeans messages. {only Queued messages and timers (|+timers|) are processed
available when compiled with the |+netbeans_intg| during the sleep as well.
feature}
*:sl!* *:sleep!* *:sl!* *:sleep!*
:[N]sl[eep]! [N][m] Same as above, but hide the cursor. :[N]sl[eep]! [N][m] Same as above, but hide the cursor.

View File

@ -1 +0,0 @@
vdivvi|gIv|÷<>X<><58>\<5C><>,X<><58>X<EFBFBD><58>\<5C><>#

View File

@ -265,6 +265,9 @@ func RunTheTest(test)
" buffers. " buffers.
%bwipe! %bwipe!
" Clear all children notifications in case there are stale ones left
let g:child_notification = 0
" The test may change the current directory. Save and restore the " The test may change the current directory. Save and restore the
" directory after executing the test. " directory after executing the test.
let save_cwd = getcwd() let save_cwd = getcwd()

View File

@ -52,14 +52,14 @@ func VerifyScreenDump(buf, filename, options, ...)
let filter = 'dumps/' . a:filename . '.vim' let filter = 'dumps/' . a:filename . '.vim'
let testfile = 'failed/' . a:filename . '.dump' let testfile = 'failed/' . a:filename . '.dump'
let max_loops = get(a:options, 'wait', 1000) / 10 let max_loops = get(a:options, 'wait', 1000) / 1
" Starting a terminal to make a screendump is always considered flaky. " Starting a terminal to make a screendump is always considered flaky.
let g:test_is_flaky = 1 let g:test_is_flaky = 1
let g:giveup_same_error = 0 let g:giveup_same_error = 0
" wait for the pending updates to be handled. " wait for the pending updates to be handled.
call TermWait(a:buf) call TermWait(a:buf, 0)
" Redraw to execute the code that updates the screen. Otherwise we get the " Redraw to execute the code that updates the screen. Otherwise we get the
" text and attributes only from the internal buffer. " text and attributes only from the internal buffer.
@ -80,8 +80,8 @@ func VerifyScreenDump(buf, filename, options, ...)
let i = 0 let i = 0
while 1 while 1
" leave some time for updating the original window " leave a bit of time for updating the original window while we spin wait.
sleep 50m sleep 1m
call delete(testfile) call delete(testfile)
call term_dumpwrite(a:buf, testfile, a:options) call term_dumpwrite(a:buf, testfile, a:options)

View File

@ -136,6 +136,34 @@ func s:kill_server(cmd)
endif endif
endfunc endfunc
" Callback function to be invoked by a child terminal job. The parent could
" then wait for the notification using WaitForChildNotification()
let g:child_notification = 0
func Tapi_notify_parent(bufnum, arglist)
let g:child_notification = 1
endfunc
" Generates a command that we can pass to a terminal job that it uses to
" notify us. Argument 'escape' will specify whether to escape the double
" quote.
func TermNotifyParentCmd(escape)
call assert_false(has("win32"), 'Windows does not support terminal API right now. Use another method to synchronize timing.')
let cmd = '\033]51;["call", "Tapi_notify_parent", []]\007'
if a:escape
return escape(cmd, '"')
endif
return cmd
endfunc
" Wait for a child process to notify us. This allows us to sequence events in
" conjunction with the child. Currently the only supported notification method
" is for a terminal job to call Tapi_notify_parent() using terminal API.
func WaitForChildNotification(...)
let timeout = get(a:000, 0, 5000)
call WaitFor({-> g:child_notification == 1}, timeout)
let g:child_notification = 0
endfunc
" Wait for up to five seconds for "expr" to become true. "expr" can be a " Wait for up to five seconds for "expr" to become true. "expr" can be a
" stringified expression to evaluate, or a funcref without arguments. " stringified expression to evaluate, or a funcref without arguments.
" Using a lambda works best. Example: " Using a lambda works best. Example:
@ -162,7 +190,7 @@ endfunc
" A second argument can be used to specify a different timeout in msec. " A second argument can be used to specify a different timeout in msec.
" "
" Return zero for success, one for failure (like the assert function). " Return zero for success, one for failure (like the assert function).
func WaitForAssert(assert, ...) func g:WaitForAssert(assert, ...)
let timeout = get(a:000, 0, 5000) let timeout = get(a:000, 0, 5000)
if s:WaitForCommon(v:null, a:assert, timeout) < 0 if s:WaitForCommon(v:null, a:assert, timeout) < 0
return 1 return 1
@ -200,11 +228,11 @@ func s:WaitForCommon(expr, assert, timeout)
call remove(v:errors, -1) call remove(v:errors, -1)
endif endif
sleep 10m sleep 1m
if exists('*reltimefloat') if exists('*reltimefloat')
let slept = float2nr(reltimefloat(reltime(start)) * 1000) let slept = float2nr(reltimefloat(reltime(start)) * 1000)
else else
let slept += 10 let slept += 1
endif endif
endwhile endwhile

View File

@ -157,14 +157,14 @@ func StopVimInTerminal(buf, kill = 1)
call assert_equal("running", term_getstatus(a:buf)) call assert_equal("running", term_getstatus(a:buf))
" Wait for all the pending updates to terminal to complete " Wait for all the pending updates to terminal to complete
call TermWait(a:buf) call TermWait(a:buf, 1)
" CTRL-O : works both in Normal mode and Insert mode to start a command line. " CTRL-O : works both in Normal mode and Insert mode to start a command line.
" In Command-line it's inserted, the CTRL-U removes it again. " In Command-line it's inserted, the CTRL-U removes it again.
call term_sendkeys(a:buf, "\<C-O>:\<C-U>qa!\<cr>") call term_sendkeys(a:buf, "\<C-O>:\<C-U>qa!\<cr>")
" Wait for all the pending updates to terminal to complete " Wait for all the pending updates to terminal to complete
call TermWait(a:buf) call TermWait(a:buf, 1)
" Wait for the terminal to end. " Wait for the terminal to end.
call WaitForAssert({-> assert_equal("finished", term_getstatus(a:buf))}) call WaitForAssert({-> assert_equal("finished", term_getstatus(a:buf))})

View File

@ -36,6 +36,7 @@ func Test_balloon_eval_term()
call TermWait(buf, 50) call TermWait(buf, 50)
call term_sendkeys(buf, 'll') call term_sendkeys(buf, 'll')
call term_sendkeys(buf, ":call Trigger()\<CR>") call term_sendkeys(buf, ":call Trigger()\<CR>")
sleep 150m " Wait for balloon to show up (100ms balloondelay time)
call VerifyScreenDump(buf, 'Test_balloon_eval_term_01', {}) call VerifyScreenDump(buf, 'Test_balloon_eval_term_01', {})
" Make sure the balloon still shows after 'updatetime' passed and CursorHold " Make sure the balloon still shows after 'updatetime' passed and CursorHold

View File

@ -4,11 +4,15 @@ source screendump.vim
CheckScreendump CheckScreendump
" Run the command in terminal and wait for it to complete via notification
func s:RunCommandAndWait(buf, cmd)
call term_sendkeys(a:buf, a:cmd .. "; printf '" .. TermNotifyParentCmd(v:false) .. "'\<cr>")
call WaitForChildNotification()
endfunc
func Test_crash1() func Test_crash1()
CheckNotBSD CheckNotBSD
CheckExecutable dash CheckExecutable dash
" Test 7 fails on Mac ...
CheckNotMac
" The following used to crash Vim " The following used to crash Vim
let opts = #{cmd: 'sh'} let opts = #{cmd: 'sh'}
@ -19,70 +23,59 @@ func Test_crash1()
let file = 'crash/poc_huaf1' let file = 'crash/poc_huaf1'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 1: [OK]" > X_crash1_result.txt' .. "\<cr>") \ ' && echo "crash 1: [OK]" > X_crash1_result.txt')
call TermWait(buf, 50)
let file = 'crash/poc_huaf2' let file = 'crash/poc_huaf2'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 2: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' && echo "crash 2: [OK]" >> X_crash1_result.txt')
call TermWait(buf, 50)
let file = 'crash/poc_huaf3' let file = 'crash/poc_huaf3'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 3: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' && echo "crash 3: [OK]" >> X_crash1_result.txt')
call TermWait(buf, 100)
let file = 'crash/bt_quickfix_poc' let file = 'crash/bt_quickfix_poc'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 4: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' && echo "crash 4: [OK]" >> X_crash1_result.txt')
" clean up " clean up
call delete('Xerr') call delete('Xerr')
" This test takes a bit longer
call TermWait(buf, 1000)
let file = 'crash/poc_tagfunc.vim' let file = 'crash/poc_tagfunc.vim'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
" using || because this poc causes vim to exit with exitstatus != 0 " using || because this poc causes vim to exit with exitstatus != 0
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' || echo "crash 5: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' || echo "crash 5: [OK]" >> X_crash1_result.txt')
call TermWait(buf, 100)
let file = 'crash/bt_quickfix1_poc' let file = 'crash/bt_quickfix1_poc'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 6: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' && echo "crash 6: [OK]" >> X_crash1_result.txt')
" clean up " clean up
call delete('X') call delete('X')
call TermWait(buf, 3000)
let file = 'crash/vim_regsub_both_poc' let file = 'crash/vim_regsub_both_poc'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 7: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' && echo "crash 7: [OK]" >> X_crash1_result.txt')
call TermWait(buf, 3000)
let file = 'crash/vim_msg_trunc_poc' let file = 'crash/vim_msg_trunc_poc'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' || echo "crash 8: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' || echo "crash 8: [OK]" >> X_crash1_result.txt')
call TermWait(buf, 3000)
let file = 'crash/crash_scrollbar' let file = 'crash/crash_scrollbar'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 9: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' && echo "crash 9: [OK]" >> X_crash1_result.txt')
call TermWait(buf, 1000)
let file = 'crash/editing_arg_idx_POC_1' let file = 'crash/editing_arg_idx_POC_1'
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' || echo "crash 10: [OK]" >> X_crash1_result.txt' .. "\<cr>") \ ' || echo "crash 10: [OK]" >> X_crash1_result.txt')
call TermWait(buf, 1000)
call delete('Xerr') call delete('Xerr')
call delete('@') call delete('@')
@ -113,7 +106,6 @@ endfunc
func Test_crash1_2() func Test_crash1_2()
CheckNotBSD CheckNotBSD
CheckExecutable dash CheckExecutable dash
let g:test_is_flaky = 1
" The following used to crash Vim " The following used to crash Vim
let opts = #{cmd: 'sh'} let opts = #{cmd: 'sh'}
@ -125,37 +117,32 @@ func Test_crash1_2()
let file = 'crash/poc1' let file = 'crash/poc1'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 1: [OK]" > '.. result .. "\<cr>") \ ' && echo "crash 1: [OK]" > '.. result)
call TermWait(buf, 150)
let file = 'crash/poc_win_enter_ext' let file = 'crash/poc_win_enter_ext'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 2: [OK]" >> '.. result .. "\<cr>") \ ' && echo "crash 2: [OK]" >> '.. result)
call TermWait(buf, 350)
let file = 'crash/poc_suggest_trie_walk' let file = 'crash/poc_suggest_trie_walk'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' && echo "crash 3: [OK]" >> '.. result .. "\<cr>") \ ' && echo "crash 3: [OK]" >> '.. result)
call TermWait(buf, 150)
let file = 'crash/poc_did_set_langmap' let file = 'crash/poc_did_set_langmap'
let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' ; echo "crash 4: [OK]" >> '.. result .. "\<cr>") \ ' ; echo "crash 4: [OK]" >> '.. result)
call TermWait(buf, 150)
let file = 'crash/reverse_text_overflow' let file = 'crash/reverse_text_overflow'
let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args .. call s:RunCommandAndWait(buf, args ..
\ ' ; echo "crash 5: [OK]" >> '.. result .. "\<cr>") \ ' ; echo "crash 5: [OK]" >> '.. result)
call TermWait(buf, 150)
" clean up " clean up
exe buf .. "bw!" exe buf .. "bw!"
@ -181,64 +168,49 @@ func Test_crash1_3()
let buf = RunVimInTerminal('sh', #{cmd: 'sh'}) let buf = RunVimInTerminal('sh', #{cmd: 'sh'})
let file = 'crash/poc_ex_substitute' let file = 'crash/poc_ex_substitute'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 150)
let file = 'crash/poc_uaf_exec_instructions' let file = 'crash/poc_uaf_exec_instructions'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 150)
let file = 'crash/poc_uaf_check_argument_types' let file = 'crash/poc_uaf_check_argument_types'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 150)
let file = 'crash/double_free' let file = 'crash/double_free'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 50)
let file = 'crash/dialog_changed_uaf' let file = 'crash/dialog_changed_uaf'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 150)
let file = 'crash/nullpointer' let file = 'crash/nullpointer'
let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'\<cr>" let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 50)
let file = 'crash/heap_overflow3' let file = 'crash/heap_overflow3'
let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 150)
let file = 'crash/heap_overflow_glob2regpat' let file = 'crash/heap_overflow_glob2regpat'
let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 50)
let file = 'crash/nullptr_regexp_nfa' let file = 'crash/nullptr_regexp_nfa'
let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'" let cmn_args = "%s -u NONE -i NONE -n -X -m -n -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file) let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args) call s:RunCommandAndWait(buf, args)
call TermWait(buf, 50)
let file = 'crash/ex_redraw_crash'
let cmn_args = "%s -u NONE -i NONE -n -m -X -Z -e -s -S %s -c ':qa!'"
let args = printf(cmn_args, vim, file)
call term_sendkeys(buf, args)
call TermWait(buf, 150)
" clean up " clean up
exe buf .. "bw!" exe buf .. "bw!"

View File

@ -370,6 +370,7 @@ endfunc
func Test_visual_ends_before_showbreak() func Test_visual_ends_before_showbreak()
CheckScreendump CheckScreendump
" Redraw at the end is necessary due to https://github.com/vim/vim/issues/16620
let lines =<< trim END let lines =<< trim END
vim9script vim9script
&wrap = true &wrap = true
@ -377,6 +378,7 @@ func Test_visual_ends_before_showbreak()
&showbreak = '↪ ' &showbreak = '↪ '
['xxxxx ' .. 'y'->repeat(&columns - 6) .. ' zzzz']->setline(1) ['xxxxx ' .. 'y'->repeat(&columns - 6) .. ' zzzz']->setline(1)
normal! wvel normal! wvel
redraw
END END
call writefile(lines, 'XvisualEndsBeforeShowbreak', 'D') call writefile(lines, 'XvisualEndsBeforeShowbreak', 'D')
let buf = RunVimInTerminal('-S XvisualEndsBeforeShowbreak', #{rows: 6}) let buf = RunVimInTerminal('-S XvisualEndsBeforeShowbreak', #{rows: 6})

View File

@ -751,15 +751,15 @@ func Test_tag_guess()
call writefile(code, 'Xfoo', 'D') call writefile(code, 'Xfoo', 'D')
let v:statusmsg = '' let v:statusmsg = ''
ta func1 silent ta func1
call assert_match('E435:', v:statusmsg) call assert_match('E435:', v:statusmsg)
call assert_equal(2, line('.')) call assert_equal(2, line('.'))
let v:statusmsg = '' let v:statusmsg = ''
ta func2 silent ta func2
call assert_match('E435:', v:statusmsg) call assert_match('E435:', v:statusmsg)
call assert_equal(4, line('.')) call assert_equal(4, line('.'))
let v:statusmsg = '' let v:statusmsg = ''
ta func3 silent ta func3
call assert_match('E435:', v:statusmsg) call assert_match('E435:', v:statusmsg)
call assert_equal(5, line('.')) call assert_equal(5, line('.'))
call assert_fails('ta func4', 'E434:') call assert_fails('ta func4', 'E434:')
@ -1625,7 +1625,7 @@ func Test_tag_guess_short()
let v:statusmsg = '' let v:statusmsg = ''
let @/ = '' let @/ = ''
ta y silent ta y
call assert_match('E435:', v:statusmsg) call assert_match('E435:', v:statusmsg)
call assert_equal(2, line('.')) call assert_equal(2, line('.'))
call assert_match('<y', @/) call assert_match('<y', @/)

View File

@ -60,8 +60,8 @@ func Test_terminal_TerminalWinOpen()
close close
if has("unix") if has("unix")
terminal ++hidden ++open sleep 1 terminal ++hidden ++open echo
sleep 1 call WaitForAssert({-> assert_equal('terminal', &buftype)})
call assert_fails("echo b:done", 'E121:') call assert_fails("echo b:done", 'E121:')
endif endif

View File

@ -292,11 +292,6 @@ endfunc
func Test_termwinscroll_topline2() func Test_termwinscroll_topline2()
" calling the terminal API doesn't work on Windows " calling the terminal API doesn't work on Windows
CheckNotMSWindows CheckNotMSWindows
let g:test_is_flaky = 1
let g:print_complete = 0
func! Tapi_print_complete(bufnum, arglist)
let g:print_complete = 1
endfunc
set termwinscroll=50000 mouse=a set termwinscroll=50000 mouse=a
set shell=sh set shell=sh
@ -310,17 +305,15 @@ func Test_termwinscroll_topline2()
let num1 = &termwinscroll / 1000 * 999 let num1 = &termwinscroll / 1000 * 999
call writefile(range(num1), 'Xtext', 'D') call writefile(range(num1), 'Xtext', 'D')
call term_sendkeys(buf, "cat Xtext\<CR>") call term_sendkeys(buf, "cat Xtext\<CR>")
call term_sendkeys(buf, 'printf ''\033]51;["call", "Tapi_print_complete", []]\007''' .. "\<cr>") call term_sendkeys(buf, "printf '" .. TermNotifyParentCmd(v:false) .. "'\<cr>")
let rows = term_getsize(buf)[0] let rows = term_getsize(buf)[0]
let cnt = 0 let cnt = 0
while !g:print_complete && cnt <= 1000 while !g:child_notification && cnt <= 50000
" max number of runs " Spin wait to process the terminal print as quickly as possible. This is
" more efficient than calling WaitForChildNotification() as we don't want
" to sleep here as the print is I/O-bound.
let cnt += 1 let cnt += 1
" sleep a bit, to give the the terminal some time to finish call term_wait(buf, 0)
" It may take a while to finish on a slow system
" so wait a bit and handle the callback
call term_wait(buf)
endwhile endwhile
call WaitForAssert({-> assert_match(string(num1 - 1), term_getline(buf, rows - 1) .. '\|' .. term_getline(buf, rows - 2))}) call WaitForAssert({-> assert_match(string(num1 - 1), term_getline(buf, rows - 1) .. '\|' .. term_getline(buf, rows - 2))})
call feedkeys("\<C-W>N", 'xt') call feedkeys("\<C-W>N", 'xt')
@ -347,8 +340,6 @@ func Test_termwinscroll_topline2()
exe buf . 'bwipe!' exe buf . 'bwipe!'
set termwinscroll& mouse& sh& set termwinscroll& mouse& sh&
delfunc Tapi_print_complete
unlet! g:print_complete
endfunc endfunc
" Resizing the terminal window caused an ml_get error. " Resizing the terminal window caused an ml_get error.

View File

@ -75,22 +75,30 @@ func Terminal_color(group_name, highlight_cmds, highlight_opt, open_cmds)
let lines = [ let lines = [
\ 'call setline(1, range(20))', \ 'call setline(1, range(20))',
\ 'func NotifyParent()',
\ ' call echoraw("' .. TermNotifyParentCmd(v:true) .. '")',
\ 'endfunc',
\ 'func OpenTerm()', \ 'func OpenTerm()',
\ ' set noruler', \ ' set noruler',
\ " call term_start('cat', #{vertical: 1, " .. a:highlight_opt .. "})", \ " call term_start('cat', #{vertical: 1, "
\ .. 'exit_cb: {->NotifyParent()}, '
\ .. a:highlight_opt .. "})",
\ ' call NotifyParent()',
\ ] + a:open_cmds + [ \ ] + a:open_cmds + [
\ 'endfunc', \ 'endfunc',
\ ] + a:highlight_cmds \ ] + a:highlight_cmds + [
\ 'call NotifyParent()',
\ ]
call writefile(lines, 'XtermStart', 'D') call writefile(lines, 'XtermStart', 'D')
let buf = RunVimInTerminal('-S XtermStart', #{rows: 15}) let buf = RunVimInTerminal('-S XtermStart', #{rows: 15})
call TermWait(buf, 100) call WaitForChildNotification()
call term_sendkeys(buf, ":call OpenTerm()\<CR>") call term_sendkeys(buf, ":call OpenTerm()\<CR>")
call TermWait(buf, 50) call WaitForChildNotification()
call term_sendkeys(buf, "hello\<CR>") call term_sendkeys(buf, "hello\<CR>")
call VerifyScreenDump(buf, 'Test_terminal_color_' .. a:group_name, {}) call VerifyScreenDump(buf, 'Test_terminal_color_' .. a:group_name, {})
call term_sendkeys(buf, "\<C-D>") call term_sendkeys(buf, "\<C-D>")
call TermWait(buf, 50) call WaitForChildNotification()
call StopVimInTerminal(buf) call StopVimInTerminal(buf)
endfunc endfunc
@ -270,11 +278,11 @@ func Test_terminal_in_popup()
\ ] \ ]
call writefile(lines, 'XtermPopup', 'D') call writefile(lines, 'XtermPopup', 'D')
let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15}) let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15})
call TermWait(buf, 200) call TermWait(buf,0)
call term_sendkeys(buf, ":call OpenTerm(0)\<CR>") call term_sendkeys(buf, ":call OpenTerm(0)\<CR>")
call TermWait(buf, 800) call TermWait(buf,0)
call term_sendkeys(buf, ":\<CR>") call term_sendkeys(buf, ":\<CR>")
call TermWait(buf, 500) call TermWait(buf,0)
call term_sendkeys(buf, "\<C-W>:echo getwinvar(g:winid, \"&buftype\") win_gettype(g:winid)\<CR>") call term_sendkeys(buf, "\<C-W>:echo getwinvar(g:winid, \"&buftype\") win_gettype(g:winid)\<CR>")
call VerifyScreenDump(buf, 'Test_terminal_popup_1', {}) call VerifyScreenDump(buf, 'Test_terminal_popup_1', {})
@ -282,16 +290,16 @@ func Test_terminal_in_popup()
call VerifyScreenDump(buf, 'Test_terminal_popup_2', {}) call VerifyScreenDump(buf, 'Test_terminal_popup_2', {})
call term_sendkeys(buf, ":call OpenTerm(1)\<CR>") call term_sendkeys(buf, ":call OpenTerm(1)\<CR>")
call TermWait(buf, 800) call TermWait(buf,0)
call term_sendkeys(buf, ":set hlsearch\<CR>") call term_sendkeys(buf, ":set hlsearch\<CR>")
call TermWait(buf, 500) call TermWait(buf,0)
call term_sendkeys(buf, "/edit\<CR>") call term_sendkeys(buf, "/edit\<CR>")
call VerifyScreenDump(buf, 'Test_terminal_popup_3', {}) call VerifyScreenDump(buf, 'Test_terminal_popup_3', {})
call term_sendkeys(buf, "\<C-W>:call HidePopup()\<CR>") call term_sendkeys(buf, "\<C-W>:call HidePopup()\<CR>")
call VerifyScreenDump(buf, 'Test_terminal_popup_4', {}) call VerifyScreenDump(buf, 'Test_terminal_popup_4', {})
call term_sendkeys(buf, "\<CR>") call term_sendkeys(buf, "\<CR>")
call TermWait(buf, 50) call TermWait(buf,0)
call term_sendkeys(buf, "\<C-W>:call ClosePopup()\<CR>") call term_sendkeys(buf, "\<C-W>:call ClosePopup()\<CR>")
call VerifyScreenDump(buf, 'Test_terminal_popup_5', {}) call VerifyScreenDump(buf, 'Test_terminal_popup_5', {})
@ -307,9 +315,9 @@ func Test_terminal_in_popup()
call term_sendkeys(buf, "A") call term_sendkeys(buf, "A")
call VerifyScreenDump(buf, 'Test_terminal_popup_8', {}) call VerifyScreenDump(buf, 'Test_terminal_popup_8', {})
call TermWait(buf, 50) call TermWait(buf,0)
call term_sendkeys(buf, ":q\<CR>") call term_sendkeys(buf, ":q\<CR>")
call TermWait(buf, 250) " wait for terminal to vanish call WaitForAssert({-> assert_equal(0, match(term_getline(buf, 6), '^5\s*$'))}, 250) " wait for terminal to vanish
call StopVimInTerminal(buf) call StopVimInTerminal(buf)
endfunc endfunc
@ -353,27 +361,32 @@ func Terminal_in_popup_color(group_name, highlight_cmds, highlight_opt, popup_cm
let lines = [ let lines = [
\ 'call setline(1, range(20))', \ 'call setline(1, range(20))',
\ 'func NotifyParent(...)',
\ ' call echoraw("' .. TermNotifyParentCmd(v:true) .. '")',
\ 'endfunc',
\ 'func OpenTerm()', \ 'func OpenTerm()',
\ " let s:buf = term_start('cat', #{hidden: 1, " \ " let s:buf = term_start('cat', #{hidden: 1, term_finish: 'close', "
\ .. a:highlight_opt .. "})", \ .. a:highlight_opt .. "})",
\ ' let g:winid = popup_create(s:buf, #{border: [], ' \ ' let g:winid = popup_create(s:buf, #{border: [], '
\ .. a:popup_opt .. '})', \ .. 'callback: {->NotifyParent()}, '
\ ] + a:popup_cmds + [ \ .. a:popup_opt .. '})',
\ ] + a:popup_cmds + [
\ ' call NotifyParent()',
\ 'endfunc', \ 'endfunc',
\ ] + a:highlight_cmds \ ] + a:highlight_cmds + [
\ 'call NotifyParent()',
\ ]
call writefile(lines, 'XtermPopup', 'D') call writefile(lines, 'XtermPopup', 'D')
let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15}) let buf = RunVimInTerminal('-S XtermPopup', #{rows: 15})
call TermWait(buf, 100) call WaitForChildNotification()
call term_sendkeys(buf, ":set noruler\<CR>") call term_sendkeys(buf, ":set noruler\<CR>")
call term_sendkeys(buf, ":call OpenTerm()\<CR>") call term_sendkeys(buf, ":call OpenTerm()\<CR>")
call TermWait(buf, 50) call WaitForChildNotification()
call term_sendkeys(buf, "hello\<CR>") call term_sendkeys(buf, "hello\<CR>")
call VerifyScreenDump(buf, 'Test_terminal_popup_' .. a:group_name, {}) call VerifyScreenDump(buf, 'Test_terminal_popup_' .. a:group_name, {})
call term_sendkeys(buf, "\<C-D>") call term_sendkeys(buf, "\<C-D>")
call TermWait(buf, 50) call WaitForChildNotification()
call term_sendkeys(buf, ":q\<CR>")
call TermWait(buf, 50) " wait for terminal to vanish
call StopVimInTerminal(buf) call StopVimInTerminal(buf)
endfunc endfunc

View File

@ -44,11 +44,15 @@ func Test_timer_repeat_three()
let slept = WaitFor('g:val == 3') let slept = WaitFor('g:val == 3')
call assert_equal(3, g:val) call assert_equal(3, g:val)
if has('reltime') if has('reltime')
" Timer has an internal 1ms allowance in calculating due time, so it's
" possible for each timer to undershoot by 1ms resulting in only 49*3=147
" ms elapsed. Additionally we started the timer before we called
" WaitFor(), so the reported time could be a couple more ms below 147.
if has('mac') if has('mac')
" Mac on Travis can be slow. " Mac in CI can be slow.
call assert_inrange(149, 400, slept) call assert_inrange(145, 400, slept)
else else
call assert_inrange(149, 250, slept) call assert_inrange(145, 250, slept)
endif endif
else else
call assert_inrange(80, 200, slept) call assert_inrange(80, 200, slept)
@ -143,13 +147,13 @@ def Test_timer_stopall_with_popup()
# Another timer will fire in half a second and close it early after stopping # Another timer will fire in half a second and close it early after stopping
# all timers. # all timers.
var pop = popup_create('Popup', {time: 10000}) var pop = popup_create('Popup', {time: 10000})
var tmr = timer_start(500, (_) => { var tmr = timer_start(100, (_) => {
timer_stopall() timer_stopall()
popup_clear() popup_clear()
}) })
sleep 1 sleep 100m
g:WaitForAssert(() => assert_equal([], popup_list()), 1000)
assert_equal([], timer_info(tmr)) assert_equal([], timer_info(tmr))
assert_equal([], popup_list())
enddef enddef
func Test_timer_paused() func Test_timer_paused()

View File

@ -1559,10 +1559,12 @@ endfunc
func Test_visual_hl_with_showbreak() func Test_visual_hl_with_showbreak()
CheckScreendump CheckScreendump
" Redraw at the end is necessary due to https://github.com/vim/vim/issues/16620
let lines =<< trim END let lines =<< trim END
setlocal showbreak=+ setlocal showbreak=+
call setline(1, repeat('a', &columns + 10)) call setline(1, repeat('a', &columns + 10))
normal g$v4lo normal g$v4lo
redraw
END END
call writefile(lines, 'XTest_visual_sbr', 'D') call writefile(lines, 'XTest_visual_sbr', 'D')

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1110,
/**/ /**/
1109, 1109,
/**/ /**/