" Vim script language tests" Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com>" Last Change: 2006 Apr 28 "-------------------------------------------------------------------------------" Test environment {{{1"------------------------------------------------------------------------------- " Adding new tests easily. {{{2"" Writing new tests is eased considerably with the following functions and" abbreviations (see "Commands for recording the execution path", "Automatic" argument generation")."" To get the abbreviations, execute the command"" :let test49_set_env = 1 | source test49.vim"" To get them always (from src/testdir), put a line"" au! BufRead test49.vim let test49_set_env = 1 | source test49.vim"" into the local .vimrc file in the src/testdir directory."if exists("test49_set_env") && test49_set_env " Automatic argument generation for the test environment commands. function! Xsum() let addend = substitute(getline("."), '^.*"\s*X:\s*\|^.*', '', "") " Evaluate arithmetic expression. if addend != "" exec "let g:Xsum = g:Xsum + " . addend endif endfunction function! Xcheck() let g:Xsum=0 ?XpathINIT?,.call Xsum() exec "norm A " return g:Xsum endfunction iab Xcheck Xcheck<Space><C-R>=Xcheck()<CR><C-O>x function! Xcomment(num) let str = "" let tabwidth = &sts ? &sts : &ts let tabs = (48+tabwidth - a:num - virtcol(".")) / tabwidth while tabs > 0 let str = str . "\t" let tabs = tabs - 1 endwhile let str = str . '" X:' return str endfunction function! Xloop() let back = line(".") . "|norm" . virtcol(".") . "|" norm 0 let last = search('X\(loop\|path\)INIT\|Xloop\>', "bW") exec back let theline = getline(last) if theline =~ 'X\(loop\|path\)INIT' let num = 1 else let num = 2 * substitute(theline, '.*Xloop\s*\(\d\+\).*', '\1', "") endif ?X\(loop\|path\)INIT? \s/\(XloopINIT!\=\s*\d\+\s\+\)\@<=\(\d\+\)/\=2*submatch(2)/ exec back exec "norm a " return num . Xcomment(strlen(num)) endfunction iab Xloop Xloop<Space><C-R>=Xloop()<CR><C-O>x function! Xpath(loopinit) let back = line(".") . "|norm" . virtcol(".") . "|" norm 0 let last = search('XpathINIT\|Xpath\>\|XloopINIT', "bW") exec back let theline = getline(last) if theline =~ 'XpathINIT' let num = 1 elseif theline =~ 'Xpath\>' let num = 2 * substitute(theline, '.*Xpath\s*\(\d\+\).*', '\1', "") else let pattern = '.*XloopINIT!\=\s*\(\d\+\)\s*\(\d\+\).*' let num = substitute(theline, pattern, '\1', "") let factor = substitute(theline, pattern, '\2', "") " The "<C-O>x" from the "Xpath" iab and the character triggering its " expansion are in the input buffer. Save and clear typeahead so " that it is not read away by the call to "input()" below. Restore " afterwards. call inputsave() let loops = input("Number of iterations in previous loop? ") call inputrestore() while (loops > 0) let num = num * factor let loops = loops - 1 endwhile endif exec "norm a " if a:loopinit return num . " 1" endif return num . Xcomment(strlen(num)) endfunction iab Xpath Xpath<Space><C-R>=Xpath(0)<CR><C-O>x iab XloopINIT XloopINIT<Space><C-R>=Xpath(1)<CR><C-O>x " Also useful (see ExtraVim below): aug ExtraVim au! au BufEnter <sfile> syn region ExtraVim \ start=+^if\s\+ExtraVim(.*)+ end=+^endif+ \ transparent keepend au BufEnter <sfile> syn match ExtraComment /^"/ \ contained containedin=ExtraVim au BufEnter <sfile> hi link ExtraComment vimComment aug END aug Xpath au BufEnter <sfile> syn keyword Xpath \ XpathINIT Xpath XloopINIT Xloop XloopNEXT Xcheck Xout au BufEnter <sfile> hi link Xpath Special aug END do BufEnter <sfile> " Do not execute the tests when sourcing this file for getting the functions " and abbreviations above, which are intended for easily adding new test " cases; they are not needed for test execution. Unlet the variable " controlling this so that an explicit ":source" command for this file will " execute the tests. unlet test49_set_env finish endif " Commands for recording the execution path. {{{2"" The Xpath/Xloop commands can be used for computing the eXecution path by" adding (different) powers of 2 from those script lines, for which the" execution should be checked. Xloop provides different addends for each" execution of a loop. Permitted values are 2^0 to 2^30, so that 31 execution" points (multiply counted inside loops) can be tested."" Note that the arguments of the following commands can be generated" automatically, see below."" Usage: {{{3"" - Use XpathINIT at the beginning of the test."" - Use Xpath to check if a line is executed." Argument: power of 2 (decimal)."" - To check multiple execution of loops use Xloop for automatically" computing Xpath values:"" - Use XloopINIT before the loop." Two arguments:" - the first Xpath value (power of 2) to be used (Xnext)," - factor for computing a new Xnext value when reexecuting a loop" (by a ":continue" or ":endwhile"); this should be 2^n where" n is the number of Xloop commands inside the loop." If XloopINIT! is used, the first execution of XloopNEXT is" a no-operation."" - Use Xloop inside the loop:" One argument:" The argument and the Xnext value are multiplied to build the" next Xpath value. No new Xnext value is prepared. The argument" should be 2^(n-1) for the nth Xloop command inside the loop." If the loop has only one Xloop command, the argument can be" ommitted (default: 1)."" - Use XloopNEXT before ":continue" and ":endwhile". This computes a new" Xnext value for the next execution of the loop by multiplying the old" one with the factor specified in the XloopINIT command. No Argument." Alternatively, when XloopINIT! is used, a single XloopNEXT at the" beginning of the loop can be used."" Nested loops are not supported."" - Use Xcheck at end of each test. It prints the test number, the expected" execution path value, the test result ("OK" or "FAIL"), and, if the tests" fails, the actual execution path." One argument:" Expected Xpath/Xloop sum for the correct execution path." In order that this value can be computed automatically, do the" following: For each line in the test with an Xpath and Xloop" command, add a comment starting with "X:" and specifying an" expression that evaluates to the value contributed by this line to" the correct execution path. (For copying an Xpath argument of at" least two digits into the comment, press <C-P>.) At the end of the" test, just type "Xcheck" and press <Esc>."" - In order to add additional information to the test output file, use the" Xout command. Argument(s) like ":echo"."" Automatic argument generation: {{{3"" The arguments of the Xpath, XloopINIT, Xloop, and Xcheck commands can be" generated automatically, so that new tests can easily be written without" mental arithmetic. The Xcheck argument is computed from the "X:" comments" of the preceding Xpath and Xloop commands. See the commands and" abbreviations at the beginning of this file."" Implementation: {{{3" XpathINIT, Xpath, XloopINIT, Xloop, XloopNEXT, Xcheck, Xout."" The variants for existing g:ExtraVimResult are needed when executing a script" in an extra Vim process, see ExtraVim below. " EXTRA_VIM_START - do not change or remove this line. com! XpathINIT let g:Xpath = 0 if exists("g:ExtraVimResult") com! -count -bar Xpath exec "!echo <count> >>" . g:ExtraVimResultelse com! -count -bar Xpath let g:Xpath = g:Xpath + <count>endif com! -count -nargs=1 -bang \ XloopINIT let g:Xnext = <count> | \ let g:Xfactor = <args> | \ let g:Xskip = strlen("<bang>") if exists("g:ExtraVimResult") com! -count=1 -bar Xloop exec "!echo " . (g:Xnext * <count>) . " >>" . \ g:ExtraVimResultelse com! -count=1 -bar Xloop let g:Xpath = g:Xpath + g:Xnext * <count>endif com! XloopNEXT let g:Xnext = g:Xnext * \ (g:Xskip ? 1 : g:Xfactor) | \ let g:Xskip = 0 let @r = ""let Xtest = 1com! -count Xcheck let Xresult = "*** Test " . \ (Xtest<10?" ":Xtest<100?" ":"") . \ Xtest . ": " . ( \ (Xpath==<count>) ? "OK (".Xpath.")" : \ "FAIL (".Xpath." instead of <count>)" \ ) | \ let @R = Xresult . "\n" | \ echo Xresult | \ let Xtest = Xtest + 1 if exists("g:ExtraVimResult") com! -nargs=+ Xoutq exec "!echo @R:'" . \ substitute(substitute(<q-args>, \ "'", '&\\&&', "g"), "\n", "@NL@", "g") \ . "' >>" . g:ExtraVimResultelse com! -nargs=+ Xoutq let @R = "--- Test " . \ (g:Xtest<10?" ":g:Xtest<100?" ":"") . \ g:Xtest . ": " . substitute(<q-args>, \ "\n", "&\t ", "g") . "\n"endifcom! -nargs=+ Xout exec 'Xoutq' <args> " Switch off storing of lines for undoing changes. Speeds things up a little.set undolevels=-1 " EXTRA_VIM_STOP - do not change or remove this line. " ExtraVim() - Run a script file in an extra Vim process. {{{2"" This is useful for testing immediate abortion of the script processing due to" an error in a command dynamically enclosed by a :try/:tryend region or when an" exception is thrown but not caught or when an interrupt occurs. It can also" be used for testing :finish."" An interrupt location can be specified by an "INTERRUPT" comment. A number" telling how often this location is reached (in a loop or in several function" calls) should be specified as argument. When missing, once per script" invocation or function call is assumed. INTERRUPT locations are tested by" setting a breakpoint in that line and using the ">quit" debug command when" the breakpoint is reached. A function for which an INTERRUPT location is" specified must be defined before calling it (or executing it as a script by" using ExecAsScript below)."" This function is only called in normal modus ("g:ExtraVimResult" undefined)."" Tests to be executed as an extra script should be written as follows:"" column 1 column 1" | |" v v"" XpathINIT XpathINIT" if ExtraVim() if ExtraVim()" ... " ..." ... " ..." endif endif" Xcheck <number> Xcheck <number>"" Double quotes in column 1 are removed before the script is executed." They should be used if the test has unbalanced conditionals (:if/:endif," :while:/endwhile, :try/:endtry) or for a line with a syntax error. The" extra script may use Xpath, XloopINIT, Xloop, XloopNEXT, and Xout as usual."" A file name may be specified as argument. All messages of the extra Vim" process are then redirected to the file. An existing file is overwritten."let ExtraVimCount = 0let ExtraVimBase = expand("<sfile>")let ExtraVimTestEnv = """function! ExtraVim(...) " Count how often this function is called. let g:ExtraVimCount = g:ExtraVimCount + 1 " Disable folds to prevent that the ranges in the ":write" commands below " are extended up to the end of a closed fold. This also speeds things up " considerably. set nofoldenable " Open a buffer for this test script and copy the test environment to " a temporary file. Take account of parts relevant for the extra script " execution only. let current_buffnr = bufnr("%") execute "view +1" g:ExtraVimBase if g:ExtraVimCount == 1 let g:ExtraVimTestEnv = tempname() execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w" \ g:ExtraVimTestEnv "|']+" execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" \ g:ExtraVimTestEnv "|']+" execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" \ g:ExtraVimTestEnv "|']+" execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" \ g:ExtraVimTestEnv "|']+" endif " Start the extra Vim script with a ":source" command for the test " environment. The source line number where the extra script will be " appended, needs to be passed as variable "ExtraVimBegin" to the script. let extra_script = tempname() exec "!echo 'source " . g:ExtraVimTestEnv . "' >" . extra_script let extra_begin = 1 " Starting behind the test environment, skip over the first g:ExtraVimCount " occurrences of "if ExtraVim()" and copy the following lines up to the " matching "endif" to the extra Vim script. execute "/E" . "ND_OF_TEST_ENVIRONMENT/" exec 'norm ' . g:ExtraVimCount . '/^\s*if\s\+ExtraVim(.*)/+' . "\n" execute ".,/^endif/-write >>" . extra_script " Open a buffer for the extra Vim script, delete all ^", and write the " script if was actually modified. execute "edit +" . (extra_begin + 1) extra_script ,$s/^"//e update " Count the INTERRUPTs and build the breakpoint and quit commands. let breakpoints = "" let debug_quits = "" let in_func = 0 exec extra_begin while search( \ '"\s*INTERRUPT\h\@!\|^\s*fu\%[nction]\>!\=\s*\%(\u\|s:\)\w*\s*(\|' \ . '^\s*\\\|^\s*endf\%[unction]\>\|' \ . '\%(^\s*fu\%[nction]!\=\s*\)\@<!\%(\u\|s:\)\w*\s*(\|' \ . 'ExecAsScript\s\+\%(\u\|s:\)\w*', \ "W") > 0 let theline = getline(".") if theline =~ '^\s*fu' " Function definition. let in_func = 1 let func_start = line(".") let func_name = substitute(theline, \ '^\s*fu\%[nction]!\=\s*\(\%(\u\|s:\)\w*\).*', '\1', "") elseif theline =~ '^\s*endf' " End of function definition. let in_func = 0 else let finding = substitute(theline, '.*\(\%' . col(".") . 'c.*\)', \ '\1', "") if finding =~ '^"\s*INTERRUPT\h\@!' " Interrupt comment. Compose as many quit commands as " specified. let cnt = substitute(finding, \ '^"\s*INTERRUPT\s*\(\d*\).*$', '\1', "") let quits = "" while cnt > 0 " Use "\r" rather than "\n" to separate the quit commands. " "\r" is not interpreted as command separator by the ":!" " command below but works to separate commands in the " external vim. let quits = quits . "q\r" let cnt = cnt - 1 endwhile if in_func " Add the function breakpoint and note the number of quits " to be used, if specified, or one for every call else. let breakpoints = breakpoints . " -c 'breakadd func " . \ (line(".") - func_start) . " " . \ func_name . "'" if quits != "" let debug_quits = debug_quits . quits elseif !exists("quits{func_name}") let quits{func_name} = "q\r" else let quits{func_name} = quits{func_name} . "q\r" endif else " Add the file breakpoint and the quits to be used for it. let breakpoints = breakpoints . " -c 'breakadd file " . \ line(".") . " " . extra_script . "'" if quits == "" let quits = "q\r" endif let debug_quits = debug_quits . quits endif else " Add the quits to be used for calling the function or executing " it as script file. if finding =~ '^ExecAsScript' " Sourcing function as script. let finding = substitute(finding, \ '^ExecAsScript\s\+\(\%(\u\|s:\)\w*\).*', '\1', "") else " Function call. let finding = substitute(finding, \ '^\(\%(\u\|s:\)\w*\).*', '\1', "") endif if exists("quits{finding}") let debug_quits = debug_quits . quits{finding} endif endif endif endwhile " Close the buffer for the script and create an (empty) resultfile. bwipeout let resultfile = tempname() exec "!>" . resultfile " Run the script in an extra vim. Switch to extra modus by passing the " resultfile in ExtraVimResult. Redirect messages to the file specified as " argument if any. Use ":debuggreedy" so that the commands provided on the " pipe are consumed at the debug prompt. Use "-N" to enable command-line " continuation ("C" in 'cpo'). Add "nviminfo" to 'viminfo' to avoid " messing up the user's viminfo file. let redirect = a:0 ? \ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : "" exec "!echo '" . debug_quits . "q' | ../vim -u NONE -N -Xes" . redirect . \ " -c 'debuggreedy|set viminfo+=nviminfo'" . \ " -c 'let ExtraVimBegin = " . extra_begin . "'" . \ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints . \ " -S " . extra_script " Build the resulting sum for resultfile and add it to g:Xpath. Add Xout " information provided by the extra Vim process to the test output. let sum = 0 exec "edit" resultfile let line = 1 while line <= line("$") let theline = getline(line) if theline =~ '^@R:' exec 'Xout "' . substitute(substitute( \ escape(escape(theline, '"'), '\"'), \ '^@R:', '', ""), '@NL@', "\n", "g") . '"' else let sum = sum + getline(line) endif let line = line + 1 endwhile bwipeout let g:Xpath = g:Xpath + sum " Delete the extra script and the resultfile. call delete(extra_script) call delete(resultfile) " Switch back to the buffer that was active when this function was entered. exec "buffer" current_buffnr " Return 0. This protects extra scripts from being run in the main Vim " process. return 0endfunction " ExtraVimThrowpoint() - Relative throwpoint in ExtraVim script {{{2"" Evaluates v:throwpoint and returns the throwpoint relative to the beginning of" an ExtraVim script as passed by ExtraVim() in ExtraVimBegin.Lines 1–500 of 9802. Load more
" Vim script language tests " Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com> " Last Change: 2006 Apr 28 "------------------------------------------------------------------------------- " Test environment {{{1 "------------------------------------------------------------------------------- " Adding new tests easily. {{{2 " " Writing new tests is eased considerably with the following functions and " abbreviations (see "Commands for recording the execution path", "Automatic " argument generation"). " " To get the abbreviations, execute the command " " :let test49_set_env = 1 | source test49.vim " " To get them always (from src/testdir), put a line " " au! BufRead test49.vim let test49_set_env = 1 | source test49.vim " " into the local .vimrc file in the src/testdir directory. " if exists("test49_set_env") && test49_set_env " Automatic argument generation for the test environment commands. function! Xsum() let addend = substitute(getline("."), '^.*"\s*X:\s*\|^.*', '', "") " Evaluate arithmetic expression. if addend != "" exec "let g:Xsum = g:Xsum + " . addend endif endfunction function! Xcheck() let g:Xsum=0 ?XpathINIT?,.call Xsum() exec "norm A " return g:Xsum endfunction iab Xcheck Xcheck<Space><C-R>=Xcheck()<CR><C-O>x function! Xcomment(num) let str = "" let tabwidth = &sts ? &sts : &ts let tabs = (48+tabwidth - a:num - virtcol(".")) / tabwidth while tabs > 0 let str = str . "\t" let tabs = tabs - 1 endwhile let str = str . '" X:' return str endfunction function! Xloop() let back = line(".") . "|norm" . virtcol(".") . "|" norm 0 let last = search('X\(loop\|path\)INIT\|Xloop\>', "bW") exec back let theline = getline(last) if theline =~ 'X\(loop\|path\)INIT' let num = 1 else let num = 2 * substitute(theline, '.*Xloop\s*\(\d\+\).*', '\1', "") endif ?X\(loop\|path\)INIT? \s/\(XloopINIT!\=\s*\d\+\s\+\)\@<=\(\d\+\)/\=2*submatch(2)/ exec back exec "norm a " return num . Xcomment(strlen(num)) endfunction iab Xloop Xloop<Space><C-R>=Xloop()<CR><C-O>x function! Xpath(loopinit) let back = line(".") . "|norm" . virtcol(".") . "|" norm 0 let last = search('XpathINIT\|Xpath\>\|XloopINIT', "bW") exec back let theline = getline(last) if theline =~ 'XpathINIT' let num = 1 elseif theline =~ 'Xpath\>' let num = 2 * substitute(theline, '.*Xpath\s*\(\d\+\).*', '\1', "") else let pattern = '.*XloopINIT!\=\s*\(\d\+\)\s*\(\d\+\).*' let num = substitute(theline, pattern, '\1', "") let factor = substitute(theline, pattern, '\2', "") " The "<C-O>x" from the "Xpath" iab and the character triggering its " expansion are in the input buffer. Save and clear typeahead so " that it is not read away by the call to "input()" below. Restore " afterwards. call inputsave() let loops = input("Number of iterations in previous loop? ") call inputrestore() while (loops > 0) let num = num * factor let loops = loops - 1 endwhile endif exec "norm a " if a:loopinit return num . " 1" endif return num . Xcomment(strlen(num)) endfunction iab Xpath Xpath<Space><C-R>=Xpath(0)<CR><C-O>x iab XloopINIT XloopINIT<Space><C-R>=Xpath(1)<CR><C-O>x " Also useful (see ExtraVim below): aug ExtraVim au! au BufEnter <sfile> syn region ExtraVim \ start=+^if\s\+ExtraVim(.*)+ end=+^endif+ \ transparent keepend au BufEnter <sfile> syn match ExtraComment /^"/ \ contained containedin=ExtraVim au BufEnter <sfile> hi link ExtraComment vimComment aug END aug Xpath au BufEnter <sfile> syn keyword Xpath \ XpathINIT Xpath XloopINIT Xloop XloopNEXT Xcheck Xout au BufEnter <sfile> hi link Xpath Special aug END do BufEnter <sfile> " Do not execute the tests when sourcing this file for getting the functions " and abbreviations above, which are intended for easily adding new test " cases; they are not needed for test execution. Unlet the variable " controlling this so that an explicit ":source" command for this file will " execute the tests. unlet test49_set_env finish endif " Commands for recording the execution path. {{{2 " " The Xpath/Xloop commands can be used for computing the eXecution path by " adding (different) powers of 2 from those script lines, for which the " execution should be checked. Xloop provides different addends for each " execution of a loop. Permitted values are 2^0 to 2^30, so that 31 execution " points (multiply counted inside loops) can be tested. " " Note that the arguments of the following commands can be generated " automatically, see below. " " Usage: {{{3 " " - Use XpathINIT at the beginning of the test. " " - Use Xpath to check if a line is executed. " Argument: power of 2 (decimal). " " - To check multiple execution of loops use Xloop for automatically " computing Xpath values: " " - Use XloopINIT before the loop. " Two arguments: " - the first Xpath value (power of 2) to be used (Xnext), " - factor for computing a new Xnext value when reexecuting a loop " (by a ":continue" or ":endwhile"); this should be 2^n where " n is the number of Xloop commands inside the loop. " If XloopINIT! is used, the first execution of XloopNEXT is " a no-operation. " " - Use Xloop inside the loop: " One argument: " The argument and the Xnext value are multiplied to build the " next Xpath value. No new Xnext value is prepared. The argument " should be 2^(n-1) for the nth Xloop command inside the loop. " If the loop has only one Xloop command, the argument can be " ommitted (default: 1). " " - Use XloopNEXT before ":continue" and ":endwhile". This computes a new " Xnext value for the next execution of the loop by multiplying the old " one with the factor specified in the XloopINIT command. No Argument. " Alternatively, when XloopINIT! is used, a single XloopNEXT at the " beginning of the loop can be used. " " Nested loops are not supported. " " - Use Xcheck at end of each test. It prints the test number, the expected " execution path value, the test result ("OK" or "FAIL"), and, if the tests " fails, the actual execution path. " One argument: " Expected Xpath/Xloop sum for the correct execution path. " In order that this value can be computed automatically, do the " following: For each line in the test with an Xpath and Xloop " command, add a comment starting with "X:" and specifying an " expression that evaluates to the value contributed by this line to " the correct execution path. (For copying an Xpath argument of at " least two digits into the comment, press <C-P>.) At the end of the " test, just type "Xcheck" and press <Esc>. " " - In order to add additional information to the test output file, use the " Xout command. Argument(s) like ":echo". " " Automatic argument generation: {{{3 " " The arguments of the Xpath, XloopINIT, Xloop, and Xcheck commands can be " generated automatically, so that new tests can easily be written without " mental arithmetic. The Xcheck argument is computed from the "X:" comments " of the preceding Xpath and Xloop commands. See the commands and " abbreviations at the beginning of this file. " " Implementation: {{{3 " XpathINIT, Xpath, XloopINIT, Xloop, XloopNEXT, Xcheck, Xout. " " The variants for existing g:ExtraVimResult are needed when executing a script " in an extra Vim process, see ExtraVim below. " EXTRA_VIM_START - do not change or remove this line. com! XpathINIT let g:Xpath = 0 if exists("g:ExtraVimResult") com! -count -bar Xpath exec "!echo <count> >>" . g:ExtraVimResult else com! -count -bar Xpath let g:Xpath = g:Xpath + <count> endif com! -count -nargs=1 -bang \ XloopINIT let g:Xnext = <count> | \ let g:Xfactor = <args> | \ let g:Xskip = strlen("<bang>") if exists("g:ExtraVimResult") com! -count=1 -bar Xloop exec "!echo " . (g:Xnext * <count>) . " >>" . \ g:ExtraVimResult else com! -count=1 -bar Xloop let g:Xpath = g:Xpath + g:Xnext * <count> endif com! XloopNEXT let g:Xnext = g:Xnext * \ (g:Xskip ? 1 : g:Xfactor) | \ let g:Xskip = 0 let @r = "" let Xtest = 1 com! -count Xcheck let Xresult = "*** Test " . \ (Xtest<10?" ":Xtest<100?" ":"") . \ Xtest . ": " . ( \ (Xpath==<count>) ? "OK (".Xpath.")" : \ "FAIL (".Xpath." instead of <count>)" \ ) | \ let @R = Xresult . "\n" | \ echo Xresult | \ let Xtest = Xtest + 1 if exists("g:ExtraVimResult") com! -nargs=+ Xoutq exec "!echo @R:'" . \ substitute(substitute(<q-args>, \ "'", '&\\&&', "g"), "\n", "@NL@", "g") \ . "' >>" . g:ExtraVimResult else com! -nargs=+ Xoutq let @R = "--- Test " . \ (g:Xtest<10?" ":g:Xtest<100?" ":"") . \ g:Xtest . ": " . substitute(<q-args>, \ "\n", "&\t ", "g") . "\n" endif com! -nargs=+ Xout exec 'Xoutq' <args> " Switch off storing of lines for undoing changes. Speeds things up a little. set undolevels=-1 " EXTRA_VIM_STOP - do not change or remove this line. " ExtraVim() - Run a script file in an extra Vim process. {{{2 " " This is useful for testing immediate abortion of the script processing due to " an error in a command dynamically enclosed by a :try/:tryend region or when an " exception is thrown but not caught or when an interrupt occurs. It can also " be used for testing :finish. " " An interrupt location can be specified by an "INTERRUPT" comment. A number " telling how often this location is reached (in a loop or in several function " calls) should be specified as argument. When missing, once per script " invocation or function call is assumed. INTERRUPT locations are tested by " setting a breakpoint in that line and using the ">quit" debug command when " the breakpoint is reached. A function for which an INTERRUPT location is " specified must be defined before calling it (or executing it as a script by " using ExecAsScript below). " " This function is only called in normal modus ("g:ExtraVimResult" undefined). " " Tests to be executed as an extra script should be written as follows: " " column 1 column 1 " | | " v v " " XpathINIT XpathINIT " if ExtraVim() if ExtraVim() " ... " ... " ... " ... " endif endif " Xcheck <number> Xcheck <number> " " Double quotes in column 1 are removed before the script is executed. " They should be used if the test has unbalanced conditionals (:if/:endif, " :while:/endwhile, :try/:endtry) or for a line with a syntax error. The " extra script may use Xpath, XloopINIT, Xloop, XloopNEXT, and Xout as usual. " " A file name may be specified as argument. All messages of the extra Vim " process are then redirected to the file. An existing file is overwritten. " let ExtraVimCount = 0 let ExtraVimBase = expand("<sfile>") let ExtraVimTestEnv = "" " function! ExtraVim(...) " Count how often this function is called. let g:ExtraVimCount = g:ExtraVimCount + 1 " Disable folds to prevent that the ranges in the ":write" commands below " are extended up to the end of a closed fold. This also speeds things up " considerably. set nofoldenable " Open a buffer for this test script and copy the test environment to " a temporary file. Take account of parts relevant for the extra script " execution only. let current_buffnr = bufnr("%") execute "view +1" g:ExtraVimBase if g:ExtraVimCount == 1 let g:ExtraVimTestEnv = tempname() execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w" \ g:ExtraVimTestEnv "|']+" execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" \ g:ExtraVimTestEnv "|']+" execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" \ g:ExtraVimTestEnv "|']+" execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>" \ g:ExtraVimTestEnv "|']+" endif " Start the extra Vim script with a ":source" command for the test " environment. The source line number where the extra script will be " appended, needs to be passed as variable "ExtraVimBegin" to the script. let extra_script = tempname() exec "!echo 'source " . g:ExtraVimTestEnv . "' >" . extra_script let extra_begin = 1 " Starting behind the test environment, skip over the first g:ExtraVimCount " occurrences of "if ExtraVim()" and copy the following lines up to the " matching "endif" to the extra Vim script. execute "/E" . "ND_OF_TEST_ENVIRONMENT/" exec 'norm ' . g:ExtraVimCount . '/^\s*if\s\+ExtraVim(.*)/+' . "\n" execute ".,/^endif/-write >>" . extra_script " Open a buffer for the extra Vim script, delete all ^", and write the " script if was actually modified. execute "edit +" . (extra_begin + 1) extra_script ,$s/^"//e update " Count the INTERRUPTs and build the breakpoint and quit commands. let breakpoints = "" let debug_quits = "" let in_func = 0 exec extra_begin while search( \ '"\s*INTERRUPT\h\@!\|^\s*fu\%[nction]\>!\=\s*\%(\u\|s:\)\w*\s*(\|' \ . '^\s*\\\|^\s*endf\%[unction]\>\|' \ . '\%(^\s*fu\%[nction]!\=\s*\)\@<!\%(\u\|s:\)\w*\s*(\|' \ . 'ExecAsScript\s\+\%(\u\|s:\)\w*', \ "W") > 0 let theline = getline(".") if theline =~ '^\s*fu' " Function definition. let in_func = 1 let func_start = line(".") let func_name = substitute(theline, \ '^\s*fu\%[nction]!\=\s*\(\%(\u\|s:\)\w*\).*', '\1', "") elseif theline =~ '^\s*endf' " End of function definition. let in_func = 0 else let finding = substitute(theline, '.*\(\%' . col(".") . 'c.*\)', \ '\1', "") if finding =~ '^"\s*INTERRUPT\h\@!' " Interrupt comment. Compose as many quit commands as " specified. let cnt = substitute(finding, \ '^"\s*INTERRUPT\s*\(\d*\).*$', '\1', "") let quits = "" while cnt > 0 " Use "\r" rather than "\n" to separate the quit commands. " "\r" is not interpreted as command separator by the ":!" " command below but works to separate commands in the " external vim. let quits = quits . "q\r" let cnt = cnt - 1 endwhile if in_func " Add the function breakpoint and note the number of quits " to be used, if specified, or one for every call else. let breakpoints = breakpoints . " -c 'breakadd func " . \ (line(".") - func_start) . " " . \ func_name . "'" if quits != "" let debug_quits = debug_quits . quits elseif !exists("quits{func_name}") let quits{func_name} = "q\r" else let quits{func_name} = quits{func_name} . "q\r" endif else " Add the file breakpoint and the quits to be used for it. let breakpoints = breakpoints . " -c 'breakadd file " . \ line(".") . " " . extra_script . "'" if quits == "" let quits = "q\r" endif let debug_quits = debug_quits . quits endif else " Add the quits to be used for calling the function or executing " it as script file. if finding =~ '^ExecAsScript' " Sourcing function as script. let finding = substitute(finding, \ '^ExecAsScript\s\+\(\%(\u\|s:\)\w*\).*', '\1', "") else " Function call. let finding = substitute(finding, \ '^\(\%(\u\|s:\)\w*\).*', '\1', "") endif if exists("quits{finding}") let debug_quits = debug_quits . quits{finding} endif endif endif endwhile " Close the buffer for the script and create an (empty) resultfile. bwipeout let resultfile = tempname() exec "!>" . resultfile " Run the script in an extra vim. Switch to extra modus by passing the " resultfile in ExtraVimResult. Redirect messages to the file specified as " argument if any. Use ":debuggreedy" so that the commands provided on the " pipe are consumed at the debug prompt. Use "-N" to enable command-line " continuation ("C" in 'cpo'). Add "nviminfo" to 'viminfo' to avoid " messing up the user's viminfo file. let redirect = a:0 ? \ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : "" exec "!echo '" . debug_quits . "q' | ../vim -u NONE -N -Xes" . redirect . \ " -c 'debuggreedy|set viminfo+=nviminfo'" . \ " -c 'let ExtraVimBegin = " . extra_begin . "'" . \ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints . \ " -S " . extra_script " Build the resulting sum for resultfile and add it to g:Xpath. Add Xout " information provided by the extra Vim process to the test output. let sum = 0 exec "edit" resultfile let line = 1 while line <= line("$") let theline = getline(line) if theline =~ '^@R:' exec 'Xout "' . substitute(substitute( \ escape(escape(theline, '"'), '\"'), \ '^@R:', '', ""), '@NL@', "\n", "g") . '"' else let sum = sum + getline(line) endif let line = line + 1 endwhile bwipeout let g:Xpath = g:Xpath + sum " Delete the extra script and the resultfile. call delete(extra_script) call delete(resultfile) " Switch back to the buffer that was active when this function was entered. exec "buffer" current_buffnr " Return 0. This protects extra scripts from being run in the main Vim " process. return 0 endfunction " ExtraVimThrowpoint() - Relative throwpoint in ExtraVim script {{{2 " " Evaluates v:throwpoint and returns the throwpoint relative to the beginning of " an ExtraVim script as passed by ExtraVim() in ExtraVimBegin.