1" 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" 5" To execute only specific test functions, add a second argument. It will be 6" matched against the names of the Test_ funtion. E.g.: 7" ../vim -u NONE -S runtest.vim test_channel.vim open_delay 8" The output can be found in the "messages" file. 9" 10" The test script may contain anything, only functions that start with 11" "Test_" are special. These will be invoked and should contain assert 12" functions. See test_assert.vim for an example. 13" 14" It is possible to source other files that contain "Test_" functions. This 15" can speed up testing, since Vim does not need to restart. But be careful 16" that the tests do not interfere with each other. 17" 18" If an error cannot be detected properly with an assert function add the 19" error to the v:errors list: 20" call add(v:errors, 'test foo failed: Cannot find xyz') 21" 22" If preparation for each Test_ function is needed, define a SetUp function. 23" It will be called before each Test_ function. 24" 25" If cleanup after each Test_ function is needed, define a TearDown function. 26" It will be called after each Test_ function. 27" 28" When debugging a test it can be useful to add messages to v:errors: 29" call add(v:errors, "this happened") 30 31 32" Without the +eval feature we can't run these tests, bail out. 33so small.vim 34 35" Check that the screen size is at least 24 x 80 characters. 36if &lines < 24 || &columns < 80 37 let error = 'Screen size too small! Tests require at least 24 lines with 80 characters' 38 echoerr error 39 split test.log 40 $put =error 41 w 42 cquit 43endif 44 45" Common with all tests on all systems. 46source setup.vim 47 48" For consistency run all tests with 'nocompatible' set. 49" This also enables use of line continuation. 50set nocp viminfo+=nviminfo 51 52" Use utf-8 or latin1 by default, instead of whatever the system default 53" happens to be. Individual tests can overrule this at the top of the file. 54if has('multi_byte') 55 set encoding=utf-8 56else 57 set encoding=latin1 58endif 59 60" Avoid stopping at the "hit enter" prompt 61set nomore 62 63" Output all messages in English. 64lang mess C 65 66" Always use forward slashes. 67set shellslash 68 69let s:srcdir = expand('%:p:h:h') 70 71" Prepare for calling test_garbagecollect_now(). 72let v:testing = 1 73 74" Support function: get the alloc ID by name. 75function GetAllocId(name) 76 exe 'split ' . s:srcdir . '/alloc.h' 77 let top = search('typedef enum') 78 if top == 0 79 call add(v:errors, 'typedef not found in alloc.h') 80 endif 81 let lnum = search('aid_' . a:name . ',') 82 if lnum == 0 83 call add(v:errors, 'Alloc ID ' . a:name . ' not defined') 84 endif 85 close 86 return lnum - top - 1 87endfunc 88 89func RunTheTest(test) 90 echo 'Executing ' . a:test 91 92 " Avoid stopping at the "hit enter" prompt 93 set nomore 94 95 " Avoid a three second wait when a message is about to be overwritten by the 96 " mode message. 97 set noshowmode 98 99 " Clear any overrides. 100 call test_override('ALL', 0) 101 102 " Some tests wipe out buffers. To be consistent, always wipe out all 103 " buffers. 104 %bwipe! 105 106 " The test may change the current directory. Save and restore the 107 " directory after executing the test. 108 let save_cwd = getcwd() 109 110 if exists("*SetUp") 111 try 112 call SetUp() 113 catch 114 call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) 115 endtry 116 endif 117 118 call add(s:messages, 'Executing ' . a:test) 119 let s:done += 1 120 121 if a:test =~ 'Test_nocatch_' 122 " Function handles errors itself. This avoids skipping commands after the 123 " error. 124 exe 'call ' . a:test 125 else 126 try 127 let s:test = a:test 128 au VimLeavePre * call EarlyExit(s:test) 129 exe 'call ' . a:test 130 au! VimLeavePre 131 catch /^\cskipped/ 132 call add(s:messages, ' Skipped') 133 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', '')) 134 catch 135 call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) 136 endtry 137 endif 138 139 if exists("*TearDown") 140 try 141 call TearDown() 142 catch 143 call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) 144 endtry 145 endif 146 147 " Clear any autocommands 148 au! 149 150 " Close any extra tab pages and windows and make the current one not modified. 151 while tabpagenr('$') > 1 152 quit! 153 endwhile 154 155 while 1 156 let wincount = winnr('$') 157 if wincount == 1 158 break 159 endif 160 bwipe! 161 if wincount == winnr('$') 162 " Did not manage to close a window. 163 only! 164 break 165 endif 166 endwhile 167 168 exe 'cd ' . save_cwd 169endfunc 170 171func AfterTheTest() 172 if len(v:errors) > 0 173 let s:fail += 1 174 call add(s:errors, 'Found errors in ' . s:test . ':') 175 call extend(s:errors, v:errors) 176 let v:errors = [] 177 endif 178endfunc 179 180func EarlyExit(test) 181 " It's OK for the test we use to test the quit detection. 182 if a:test != 'Test_zz_quit_detected()' 183 call add(v:errors, 'Test caused Vim to exit: ' . a:test) 184 endif 185 186 call FinishTesting() 187endfunc 188 189" This function can be called by a test if it wants to abort testing. 190func FinishTesting() 191 call AfterTheTest() 192 193 " Don't write viminfo on exit. 194 set viminfo= 195 196 " Clean up files created by setup.vim 197 call delete('XfakeHOME', 'rf') 198 199 if s:fail == 0 200 " Success, create the .res file so that make knows it's done. 201 exe 'split ' . fnamemodify(g:testname, ':r') . '.res' 202 write 203 endif 204 205 if len(s:errors) > 0 206 " Append errors to test.log 207 split test.log 208 call append(line('$'), '') 209 call append(line('$'), 'From ' . g:testname . ':') 210 call append(line('$'), s:errors) 211 write 212 endif 213 214 if s:done == 0 215 let message = 'NO tests executed' 216 else 217 let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test') 218 endif 219 echo message 220 call add(s:messages, message) 221 if s:fail > 0 222 let message = s:fail . ' FAILED:' 223 echo message 224 call add(s:messages, message) 225 call extend(s:messages, s:errors) 226 endif 227 228 " Add SKIPPED messages 229 call extend(s:messages, s:skipped) 230 231 " Append messages to the file "messages" 232 split messages 233 call append(line('$'), '') 234 call append(line('$'), 'From ' . g:testname . ':') 235 call append(line('$'), s:messages) 236 write 237 238 qall! 239endfunc 240 241" Source the test script. First grab the file name, in case the script 242" navigates away. g:testname can be used by the tests. 243let g:testname = expand('%') 244let s:done = 0 245let s:fail = 0 246let s:errors = [] 247let s:messages = [] 248let s:skipped = [] 249if expand('%') =~ 'test_vimscript.vim' 250 " this test has intentional s:errors, don't use try/catch. 251 source % 252else 253 try 254 source % 255 catch 256 let s:fail += 1 257 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) 258 endtry 259endif 260 261" Names of flaky tests. 262let s:flaky = [ 263 \ 'Test_client_server()', 264 \ 'Test_close_and_exit_cb()', 265 \ 'Test_collapse_buffers()', 266 \ 'Test_communicate()', 267 \ 'Test_cwd()', 268 \ 'Test_exit_callback_interval()', 269 \ 'Test_nb_basic()', 270 \ 'Test_oneshot()', 271 \ 'Test_out_cb()', 272 \ 'Test_paused()', 273 \ 'Test_pipe_through_sort_all()', 274 \ 'Test_pipe_through_sort_some()', 275 \ 'Test_popup_and_window_resize()', 276 \ 'Test_quoteplus()', 277 \ 'Test_quotestar()', 278 \ 'Test_reltime()', 279 \ 'Test_repeat_three()', 280 \ 'Test_terminal_composing_unicode()', 281 \ 'Test_terminal_noblock()', 282 \ 'Test_terminal_redir_file()', 283 \ 'Test_terminal_tmap()', 284 \ 'Test_with_partial_callback()', 285 \ ] 286 287" Locate Test_ functions and execute them. 288redir @q 289silent function /^Test_ 290redir END 291let s:tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g')) 292 293" If there is an extra argument filter the function names against it. 294if argc() > 1 295 let s:tests = filter(s:tests, 'v:val =~ argv(1)') 296endif 297 298" Execute the tests in alphabetical order. 299for s:test in sort(s:tests) 300 " Silence, please! 301 set belloff=all 302 303 call RunTheTest(s:test) 304 305 if len(v:errors) > 0 && index(s:flaky, s:test) >= 0 306 call add(s:messages, 'Found errors in ' . s:test . ':') 307 call extend(s:messages, v:errors) 308 call add(s:messages, 'Flaky test failed, running it again') 309 let first_run = v:errors 310 311 " Flakiness is often caused by the system being very busy. Sleep a couple 312 " of seconds to have a higher chance of succeeding the second time. 313 sleep 2 314 315 let v:errors = [] 316 call RunTheTest(s:test) 317 if len(v:errors) > 0 318 let second_run = v:errors 319 let v:errors = ['First run:'] 320 call extend(v:errors, first_run) 321 call add(v:errors, 'Second run:') 322 call extend(v:errors, second_run) 323 endif 324 endif 325 326 call AfterTheTest() 327endfor 328 329call FinishTesting() 330 331" vim: shiftwidth=2 sts=2 expandtab 332