From dfd3ac6f48328288eaff37cd4a7ef22c5124231a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 2 Oct 2025 12:11:03 +0800 Subject: [PATCH] vim-patch:partial: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: vim/vim#16615 https://github.com/vim/vim/commit/e70587dbdbb1aba2c3f92490b8f870361d4a4177 Part of shared.vim and test_crash.vim changes only. Co-authored-by: Yee Cheng Chin --- test/old/testdir/shared.vim | 4 +- test/old/testdir/test_crash.vim | 85 ++++++++++++++------------------- 2 files changed, 38 insertions(+), 51 deletions(-) diff --git a/test/old/testdir/shared.vim b/test/old/testdir/shared.vim index bb1a6c8f5b..cefca1c2e6 100644 --- a/test/old/testdir/shared.vim +++ b/test/old/testdir/shared.vim @@ -211,11 +211,11 @@ func s:WaitForCommon(expr, assert, timeout) call remove(v:errors, -1) endif - sleep 10m + sleep 1m if exists('*reltimefloat') let slept = float2nr(reltimefloat(reltime(start)) * 1000) else - let slept += 10 + let slept += 1 endif endwhile diff --git a/test/old/testdir/test_crash.vim b/test/old/testdir/test_crash.vim index 5ddbd07b55..b8fc55bb2d 100644 --- a/test/old/testdir/test_crash.vim +++ b/test/old/testdir/test_crash.vim @@ -4,11 +4,15 @@ source screendump.vim 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) .. "'\") + call WaitForChildNotification() +endfunc + func Test_crash1() CheckNotBSD CheckExecutable dash - " Test 7 fails on Mac ... - CheckNotMac " The following used to crash Vim let opts = #{cmd: 'sh'} @@ -19,70 +23,59 @@ func Test_crash1() let file = 'crash/poc_huaf1' let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'" let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 1: [OK]" > X_crash1_result.txt' .. "\") - call TermWait(buf, 50) + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 1: [OK]" > X_crash1_result.txt') let file = 'crash/poc_huaf2' let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 2: [OK]" >> X_crash1_result.txt' .. "\") - call TermWait(buf, 50) + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 2: [OK]" >> X_crash1_result.txt') let file = 'crash/poc_huaf3' let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 3: [OK]" >> X_crash1_result.txt' .. "\") - call TermWait(buf, 100) + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 3: [OK]" >> X_crash1_result.txt') let file = 'crash/bt_quickfix_poc' let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 4: [OK]" >> X_crash1_result.txt' .. "\") + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 4: [OK]" >> X_crash1_result.txt') " clean up call delete('Xerr') - " This test takes a bit longer - call TermWait(buf, 1000) let file = 'crash/poc_tagfunc.vim' let args = printf(cmn_args, vim, file) " using || because this poc causes vim to exit with exitstatus != 0 - call term_sendkeys(buf, args .. - \ ' || echo "crash 5: [OK]" >> X_crash1_result.txt' .. "\") + call s:RunCommandAndWait(buf, args .. + \ ' || echo "crash 5: [OK]" >> X_crash1_result.txt') - call TermWait(buf, 100) let file = 'crash/bt_quickfix1_poc' let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 6: [OK]" >> X_crash1_result.txt' .. "\") + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 6: [OK]" >> X_crash1_result.txt') " clean up call delete('X') - call TermWait(buf, 3000) let file = 'crash/vim_regsub_both_poc' let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 7: [OK]" >> X_crash1_result.txt' .. "\") - call TermWait(buf, 3000) + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 7: [OK]" >> X_crash1_result.txt') let file = 'crash/vim_msg_trunc_poc' let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' || echo "crash 8: [OK]" >> X_crash1_result.txt' .. "\") - call TermWait(buf, 3000) + call s:RunCommandAndWait(buf, args .. + \ ' || echo "crash 8: [OK]" >> X_crash1_result.txt') let file = 'crash/crash_scrollbar' let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 9: [OK]" >> X_crash1_result.txt' .. "\") - call TermWait(buf, 1000) + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 9: [OK]" >> X_crash1_result.txt') let file = 'crash/editing_arg_idx_POC_1' let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' || echo "crash 10: [OK]" >> X_crash1_result.txt' .. "\") - call TermWait(buf, 1000) + call s:RunCommandAndWait(buf, args .. + \ ' || echo "crash 10: [OK]" >> X_crash1_result.txt') call delete('Xerr') call delete('@') @@ -113,7 +106,6 @@ endfunc func Test_crash1_2() CheckNotBSD CheckExecutable dash - let g:test_is_flaky = 1 " The following used to crash Vim let opts = #{cmd: 'sh'} @@ -125,37 +117,32 @@ func Test_crash1_2() let file = 'crash/poc1' let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'" let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 1: [OK]" > '.. result .. "\") - call TermWait(buf, 150) + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 1: [OK]" > '.. result) let file = 'crash/poc_win_enter_ext' let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'" let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 2: [OK]" >> '.. result .. "\") - call TermWait(buf, 350) + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 2: [OK]" >> '.. result) let file = 'crash/poc_suggest_trie_walk' let cmn_args = "%s -u NONE -i NONE -n -e -s -S %s -c ':qa!'" let args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' && echo "crash 3: [OK]" >> '.. result .. "\") - call TermWait(buf, 150) + call s:RunCommandAndWait(buf, args .. + \ ' && echo "crash 3: [OK]" >> '.. result) 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 args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' ; echo "crash 4: [OK]" >> '.. result .. "\") - call TermWait(buf, 150) + call s:RunCommandAndWait(buf, args .. + \ ' ; echo "crash 4: [OK]" >> '.. result) 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 args = printf(cmn_args, vim, file) - call term_sendkeys(buf, args .. - \ ' ; echo "crash 5: [OK]" >> '.. result .. "\") - call TermWait(buf, 150) + call s:RunCommandAndWait(buf, args .. + \ ' ; echo "crash 5: [OK]" >> '.. result) " clean up exe buf .. "bw!"