Mercurial > octave
changeset 22376:3473246a824e
allow %!test blocks to be tagged with messages or bug ids
* test.m: Handle <MESSAGE> option for assert, fail, test, testif, and
xtest blocks.
* doc/interpreter/testfun.txi: Update docs.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Wed, 24 Aug 2016 20:04:52 -0400 |
parents | 179d088a6375 |
children | c0f446d657bf |
files | doc/interpreter/testfun.txi scripts/testfun/test.m |
diffstat | 2 files changed, 115 insertions(+), 40 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/interpreter/testfun.txi Wed Aug 24 15:06:30 2016 +0100 +++ b/doc/interpreter/testfun.txi Wed Aug 24 20:04:52 2016 -0400 @@ -332,43 +332,74 @@ @table @code @item %!test -check that entire block is correct +@itemx %!test <MESSAGE> +Check that entire block is correct. If @code{<MESSAGE>} is present, the +test block is interpreted as for @code{xtest}. @item %!testif HAVE_XXX -check block only if Octave was compiled with feature HAVE_XXX. +@itemx %!testif HAVE_XXX, HAVE_YYY, @dots{} +@itemx %!testif @dots{} <MESSAGE> +Check block only if Octave was compiled with feature HAVE_XXX. If +@code{<MESSAGE>} is present, the test block is interpreted as for +@code{xtest}. @item %!xtest -check block, report a test failure but do not abort testing. +@itemx %!xtest <MESSAGE> +Check block, report a test failure but do not abort testing. If +@code{<MESSAGE>} is present, then the text of the message is displayed +if the test fails, like this: + +@example +!!!!! Known bug: MESSAGE +@end example + +@noindent +If the message is an integer, it is interpreted as a bug ID for the +Octave bug tracker and reported as + +@example +!!!!! Known bug: http://octave.org/testfailure/?BUG-ID +@end example + +@noindent +in which BUG-ID is the integer bug number. The intent is to allow +clearer documentation of known problems. @item %!error -check for correct error message - -@item %!warning -check for correct warning message +@itemx %!error <MESSAGE> +@itemx %!warning +@itemx %!warning <MESSAGE> +Check for correct error or warning message. If @code{<MESSAGE>} is +supplied it is interpreted as a regular expression pattern that is +expected to match the error or warning message. @item %!demo -demo only executes in interactive mode +Demo only executes in interactive mode. @item %!# -comment: ignore everything within the block +Comment. Ignore everything within the block @item %!shared x,y,z -declare variables for use in multiple tests +Declare variables for use in multiple tests. @item %!function -define a function for use in multiple tests +Define a function for use in multiple tests. @item %!endfunction -close a function definition +Close a function definition. @item %!assert (x, y, tol) -shorthand for @code{%!test assert (x, y, tol)} - +@item %!assert <MESSAGE> (x, y, tol) @item %!fail (CODE, PATTERN) -shorthand for @code{%!test fail (CODE, PATTERN)} +@item %!fail <MESSAGE> (CODE, PATTERN) +Shorthand for @code{%!test assert (x, y, tol)} or +@code{%!test fail (CODE, PATTERN)}. If @code{<MESSAGE>} is present, the +test block is interpreted as for @code{xtest}. @end table +@anchor{test-message-anchor} + When coding tests the Octave convention is that lines that begin with a block type do not have a semicolon at the end. Any code that is within a block, however, is normal Octave code and usually will have a trailing semicolon.
--- a/scripts/testfun/test.m Wed Aug 24 15:06:30 2016 +0100 +++ b/scripts/testfun/test.m Wed Aug 24 20:04:52 2016 -0400 @@ -327,7 +327,9 @@ ## Assume the block will succeed. __success = true; __msg = []; + __istest = false; __isxtest = false; + __bug_id = ""; ### DEMO @@ -338,8 +340,6 @@ __isdemo = strcmp (__type, "demo"); if (__grabdemo || __isdemo) - __istest = false; - if (__grabdemo && __isdemo) if (isempty (__demo_code)) __demo_code = __code; @@ -368,8 +368,6 @@ ### SHARED elseif (strcmp (__type, "shared")) - __istest = false; - ## Separate initialization code from variables. __idx = find (__code == "\n"); if (isempty (__idx)) @@ -409,7 +407,6 @@ ### FUNCTION elseif (strcmp (__type, "function")) - __istest = false; persistent __fn = 0; __name_position = function_name (__block); if (isempty (__name_position)) @@ -433,15 +430,19 @@ elseif (strcmp (__type, "endfunction")) ## endfunction simply declares the end of a previous function block. ## There is no processing to be done here, just skip to next block. - __istest = false; __code = ""; ### ASSERT/FAIL elseif (strcmp (__type, "assert") || strcmp (__type, "fail")) - __istest = true; + [__bug_id, __code] = getbugid (__code); + if (isempty (__bug_id)) + __istest = true; + else + __isxtest = true; + endif ## Put the keyword back on the code. - __code = __block; + __code = [__type __code]; ## The code will be evaluated below as a test block. ### ERROR/WARNING @@ -528,17 +529,30 @@ elseif (strcmp (__type, "testif")) __e = regexp (__code, '.$', 'lineanchors', 'once'); - ## Strip any comment from testif line before looking for features + ## Strip any comment and bug-id from testif line before + ## looking for features __feat_line = strtok (__code(1:__e), '#%'); + __idx1 = index (__feat_line, "<"); + if (__idx1) + __tmp = __feat_line(__idx1+1:end); + __idx2 = index (__tmp, ">"); + if (__idx2) + __bug_id = __tmp(1:__idx2-1); + __feat_line = __feat_line(1:__idx1-1); + endif + endif __feat = regexp (__feat_line, '\w+', 'match'); __feat = strrep (__feat, "HAVE_", ""); __have_feat = __have_feature__ (__feat); if (__have_feat) - __istest = true; + if (isempty (__bug_id)) + __istest = true; + else + __isxtest = true; + endif __code = __code(__e + 1 : end); else __xskip += 1; - __istest = false; __code = ""; # Skip the code. __msg = [__signal_skip "skipped test\n"]; endif @@ -546,20 +560,24 @@ ### TEST elseif (strcmp (__type, "test")) - __istest = true; + [__bug_id, __code] = getbugid (__code); + if (! isempty (__bug_id)) + __isxtest = true; + else + __istest = true; + endif ## Code will be evaluated below. ### XTEST elseif (strcmp (__type, "xtest")) - __istest = false; __isxtest = true; + [__bug_id, __code] = getbugid (__code); ## Code will be evaluated below. ### Comment block. elseif (strcmp (__block(1:1), "#")) - __istest = false; __code = ""; # skip the code ### Unknown block. @@ -586,16 +604,24 @@ "Use the %!function/%!endfunction syntax instead to define shared functions for testing.\n"]); endif catch - if (strcmp (__type, "xtest")) - __msg = [__signal_fail "known failure\n"]; - __xfail += 1; - __success = false; - else - __msg = [__signal_fail "test failed\n" lasterr()]; - __success = false; - endif if (isempty (lasterr ())) error ("test: empty error text, probably Ctrl-C --- aborting"); + else + __success = false; + if (__isxtest) + __xfail += 1; + if (isempty (__bug_id)) + __msg = [__signal_fail "known failure\n"]; + else + if (all (isdigit (__bug_id))) + __bug_id = ["http://octave.org/testfailure/?" __bug_id]; + endif + __msg = ["known bug: " __bug_id "\n"]; + endif + else + __msg = "test failed\n"; + endif + __msg = [__signal_fail __msg lasterr()]; endif end_try_catch clear __test__; @@ -651,7 +677,7 @@ if (nargout == 0) if (__tests || __xfail || __xskip) if (__xfail) - printf ("PASSES %d out of %d test%s (%d expected failure%s)\n", + printf ("PASSES %d out of %d test%s (%d known failure%s)\n", __successes, __tests, ifelse (__tests > 1, "s", ""), __xfail, ifelse (__xfail > 1, "s", "")); else @@ -713,7 +739,7 @@ endfunction ## Strip <pattern> from '<pattern> code'. -## Also handles 'id=ID code' +## Optionally also handles 'id=ID code' function [pattern, id, rest] = getpattern (str) pattern = "."; @@ -732,6 +758,24 @@ endfunction +## Strip <bug-id> from '<pattern> code'. +function [bug_id, rest] = getbugid (str) + + bug_id = ""; + id = []; + rest = str; + str = trimleft (str); + if (! isempty (str) && str(1) == "<") + close = index (str, ">"); + if (close) + bug_id = str(2:close-1); + rest = str(close+1:end); + endif + endif + +endfunction + + ## Strip '.*prefix:' from '.*prefix: msg\n' and strip trailing blanks. function msg = trimerr (msg, prefix) idx = index (msg, [prefix ":"]); @@ -901,7 +945,7 @@ ## All of the following tests should fail. These tests should ## be disabled unless you are developing test() since users don't -## like to be presented with expected failures. +## like to be presented with known failures. ## %!test error("---------Failure tests. Use test('test','verbose',1)"); ## %!test assert([a,b,c],[1,3,6]); # variables have wrong values ## %!invalid # unknown block type