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 exe 'call ' . a:test 128 catch /^\cskipped/ 129 call add(s:messages, ' Skipped') 130 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', '')) 131 catch 132 call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) 133 endtry 134 endif 135 136 if exists("*TearDown") 137 try 138 call TearDown() 139 catch 140 call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) 141 endtry 142 endif 143 144 " Clear any autocommands 145 au! 146 147 " Close any extra tab pages and windows and make the current one not modified. 148 while tabpagenr('$') > 1 149 quit! 150 endwhile 151 152 while 1 153 let wincount = winnr('$') 154 if wincount == 1 155 break 156 endif 157 bwipe! 158 if wincount == winnr('$') 159 " Did not manage to close a window. 160 only! 161 break 162 endif 163 endwhile 164 165 exe 'cd ' . save_cwd 166endfunc 167 168func AfterTheTest() 169 if len(v:errors) > 0 170 let s:fail += 1 171 call add(s:errors, 'Found errors in ' . s:test . ':') 172 call extend(s:errors, v:errors) 173 let v:errors = [] 174 endif 175endfunc 176 177" This function can be called by a test if it wants to abort testing. 178func FinishTesting() 179 call AfterTheTest() 180 181 " Don't write viminfo on exit. 182 set viminfo= 183 184 " Clean up files created by setup.vim 185 call delete('XfakeHOME', 'rf') 186 187 if s:fail == 0 188 " Success, create the .res file so that make knows it's done. 189 exe 'split ' . fnamemodify(g:testname, ':r') . '.res' 190 write 191 endif 192 193 if len(s:errors) > 0 194 " Append errors to test.log 195 split test.log 196 call append(line('$'), '') 197 call append(line('$'), 'From ' . g:testname . ':') 198 call append(line('$'), s:errors) 199 write 200 endif 201 202 let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test') 203 echo message 204 call add(s:messages, message) 205 if s:fail > 0 206 let message = s:fail . ' FAILED:' 207 echo message 208 call add(s:messages, message) 209 call extend(s:messages, s:errors) 210 endif 211 212 " Add SKIPPED messages 213 call extend(s:messages, s:skipped) 214 215 " Append messages to the file "messages" 216 split messages 217 call append(line('$'), '') 218 call append(line('$'), 'From ' . g:testname . ':') 219 call append(line('$'), s:messages) 220 write 221 222 qall! 223endfunc 224 225" Source the test script. First grab the file name, in case the script 226" navigates away. g:testname can be used by the tests. 227let g:testname = expand('%') 228let s:done = 0 229let s:fail = 0 230let s:errors = [] 231let s:messages = [] 232let s:skipped = [] 233if expand('%') =~ 'test_vimscript.vim' 234 " this test has intentional s:errors, don't use try/catch. 235 source % 236else 237 try 238 source % 239 catch 240 let s:fail += 1 241 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) 242 endtry 243endif 244 245" Names of flaky tests. 246let s:flaky = [ 247 \ 'Test_client_server()', 248 \ 'Test_close_and_exit_cb()', 249 \ 'Test_collapse_buffers()', 250 \ 'Test_communicate()', 251 \ 'Test_exit_callback_interval()', 252 \ 'Test_nb_basic()', 253 \ 'Test_oneshot()', 254 \ 'Test_out_cb()', 255 \ 'Test_paused()', 256 \ 'Test_pipe_through_sort_all()', 257 \ 'Test_pipe_through_sort_some()', 258 \ 'Test_quoteplus()', 259 \ 'Test_quotestar()', 260 \ 'Test_reltime()', 261 \ 'Test_terminal_composing_unicode()', 262 \ 'Test_terminal_noblock()', 263 \ 'Test_terminal_redir_file()', 264 \ 'Test_terminal_tmap()', 265 \ 'Test_with_partial_callback()', 266 \ ] 267 268" Locate Test_ functions and execute them. 269redir @q 270silent function /^Test_ 271redir END 272let s:tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g')) 273 274" If there is an extra argument filter the function names against it. 275if argc() > 1 276 let s:tests = filter(s:tests, 'v:val =~ argv(1)') 277endif 278 279" Execute the tests in alphabetical order. 280for s:test in sort(s:tests) 281 " Silence, please! 282 set belloff=all 283 284 call RunTheTest(s:test) 285 286 if len(v:errors) > 0 && index(s:flaky, s:test) >= 0 287 call add(s:messages, 'Found errors in ' . s:test . ':') 288 call extend(s:messages, v:errors) 289 call add(s:messages, 'Flaky test failed, running it again') 290 let first_run = v:errors 291 292 " Flakiness is often caused by the system being very busy. Sleep a couple 293 " of seconds to have a higher chance of succeeding the second time. 294 sleep 2 295 296 let v:errors = [] 297 call RunTheTest(s:test) 298 if len(v:errors) > 0 299 let second_run = v:errors 300 let v:errors = ['First run:'] 301 call extend(v:errors, first_run) 302 call add(v:errors, 'Second run:') 303 call extend(v:errors, second_run) 304 endif 305 endif 306 307 call AfterTheTest() 308endfor 309 310call FinishTesting() 311 312" vim: shiftwidth=2 sts=2 expandtab 313