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 if exists("*SetUp") 103 try 104 call SetUp() 105 catch 106 call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) 107 endtry 108 endif 109 110 call add(s:messages, 'Executing ' . a:test) 111 let s:done += 1 112 try 113 exe 'call ' . a:test 114 catch /^\cskipped/ 115 call add(s:messages, ' Skipped') 116 call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', '')) 117 catch 118 call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) 119 endtry 120 121 if exists("*TearDown") 122 try 123 call TearDown() 124 catch 125 call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) 126 endtry 127 endif 128 129 " Close any extra windows and make the current one not modified. 130 while 1 131 let wincount = winnr('$') 132 if wincount == 1 133 break 134 endif 135 bwipe! 136 if wincount == winnr('$') 137 " Did not manage to close a window. 138 only! 139 break 140 endif 141 endwhile 142 set nomodified 143endfunc 144 145func AfterTheTest() 146 if len(v:errors) > 0 147 let s:fail += 1 148 call add(s:errors, 'Found errors in ' . s:test . ':') 149 call extend(s:errors, v:errors) 150 let v:errors = [] 151 endif 152endfunc 153 154" This function can be called by a test if it wants to abort testing. 155func FinishTesting() 156 call AfterTheTest() 157 158 " Don't write viminfo on exit. 159 set viminfo= 160 161 " Clean up files created by setup.vim 162 call delete('XfakeHOME', 'rf') 163 164 if s:fail == 0 165 " Success, create the .res file so that make knows it's done. 166 exe 'split ' . fnamemodify(g:testname, ':r') . '.res' 167 write 168 endif 169 170 if len(s:errors) > 0 171 " Append errors to test.log 172 split test.log 173 call append(line('$'), '') 174 call append(line('$'), 'From ' . g:testname . ':') 175 call append(line('$'), s:errors) 176 write 177 endif 178 179 let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test') 180 echo message 181 call add(s:messages, message) 182 if s:fail > 0 183 let message = s:fail . ' FAILED:' 184 echo message 185 call add(s:messages, message) 186 call extend(s:messages, s:errors) 187 endif 188 189 " Add SKIPPED messages 190 call extend(s:messages, s:skipped) 191 192 " Append messages to the file "messages" 193 split messages 194 call append(line('$'), '') 195 call append(line('$'), 'From ' . g:testname . ':') 196 call append(line('$'), s:messages) 197 write 198 199 qall! 200endfunc 201 202" Source the test script. First grab the file name, in case the script 203" navigates away. g:testname can be used by the tests. 204let g:testname = expand('%') 205let s:done = 0 206let s:fail = 0 207let s:errors = [] 208let s:messages = [] 209let s:skipped = [] 210if expand('%') =~ 'test_vimscript.vim' 211 " this test has intentional s:errors, don't use try/catch. 212 source % 213else 214 try 215 source % 216 catch 217 let s:fail += 1 218 call add(s:errors, 'Caught exception: ' . v:exception . ' @ ' . v:throwpoint) 219 endtry 220endif 221 222" Names of flaky tests. 223let s:flaky = [ 224 \ 'Test_client_server()', 225 \ 'Test_close_and_exit_cb()', 226 \ 'Test_collapse_buffers()', 227 \ 'Test_communicate()', 228 \ 'Test_exit_callback_interval()', 229 \ 'Test_nb_basic()', 230 \ 'Test_oneshot()', 231 \ 'Test_pipe_through_sort_all()', 232 \ 'Test_pipe_through_sort_some()', 233 \ 'Test_quoteplus()', 234 \ 'Test_quotestar()', 235 \ 'Test_reltime()', 236 \ 'Test_with_partial_callback()', 237 \ ] 238 239" Locate Test_ functions and execute them. 240redir @q 241silent function /^Test_ 242redir END 243let s:tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g')) 244 245" If there is an extra argument filter the function names against it. 246if argc() > 1 247 let s:tests = filter(s:tests, 'v:val =~ argv(1)') 248endif 249 250" Execute the tests in alphabetical order. 251for s:test in sort(s:tests) 252 call RunTheTest(s:test) 253 254 if len(v:errors) > 0 && index(s:flaky, s:test) >= 0 255 call add(s:messages, 'Found errors in ' . s:test . ':') 256 call extend(s:messages, v:errors) 257 call add(s:messages, 'Flaky test failed, running it again') 258 let first_run = v:errors 259 260 let v:errors = [] 261 call RunTheTest(s:test) 262 if len(v:errors) > 0 263 let second_run = v:errors 264 let v:errors = ['First run:'] 265 call extend(v:errors, first_run) 266 call add(v:errors, 'Second run:') 267 call extend(v:errors, second_run) 268 endif 269 endif 270 271 call AfterTheTest() 272endfor 273 274call FinishTesting() 275 276" vim: shiftwidth=2 sts=2 expandtab 277