diff --git a/src/test/Asserter.winxed b/src/test/Asserter.winxed index 92d46ecf..3db73c62 100644 --- a/src/test/Asserter.winxed +++ b/src/test/Asserter.winxed @@ -3,20 +3,332 @@ namespace Rosella { namespace Test /* Test Asserter to provide an OO-like interface to the functions defined in the Assert namespace */ - // TODO: Delete Assert namespace and turn all functions there into methods - // on this object. + // TODO: Add the ability for the user to provide a custom subclass for + // asserter instead. class Asserter { - function find_method[vtable](string name) + // Routine to get a default message, if none is provided + function default_message(string msg, int has_msg, string def) { - var assert_name = name; - return function(var obj, var p [slurpy], var n [slurpy,named]) { - var assert_ns = null; - var assert_name_pmc = "Assert"; - ${ get_hll_namespace assert_ns, assert_name_pmc }; - var assertion = assert_ns.find_sub(assert_name); - assertion(p:[flat], n:[flat]); - }; + if (has_msg) + return msg; + return def; } + + // Unconditional fail. Throws a Rosella.Test.Failure + function fail(string why, + int is_internal [optional,named], int has_is_i [opt_flag], + var exception [optional,named], int has_ex [opt_flag] + ) + { + using Rosella.build; + + if (!has_is_i) + is_internal = 0; + if (!has_ex) + exception = null; + + var ex = build(class Rosella.Test.Failure, why, exception, is_internal); + ex.throw(); + } + + /* Assertion Functions + Each of these functions asserts some condition. If the condition + holds, nothing happens. If the condition fails, we call fail(). + */ + + function block(string message, var block) + { + if (!block()) + self.fail(message); + } + + function block_false(string message, var block) + { + if (block()) + self.fail(message); + } + + function can(var obj, string method, + string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "item failed 'can'"); + int i_can = 0; + ${ can i_can, obj, method }; + if (!i_can) + self.fail(message); + } + + function can_not(var obj, string method, + string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "item failed 'can_not'"); + int i_can = 0; + ${ can i_can, obj, method }; + if (i_can) + self.fail(message); + } + + function defined(var obj, + string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "item not defined"); + int is_defined = 0; + ${ defined is_defined, obj }; + if (!is_defined) + self.fail(message); + } + + function not_defined(var obj, + string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "item is defined"); + int is_defined = 0; + ${ defined is_defined, obj }; + if (is_defined) + self.fail(message); + } + + function does(var obj, var role, + string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "item failed 'does'"); + int obj_does = 0; + ${ does obj_does, obj, role }; + if (!obj_does) + self.fail(message); + } + + function does_not(obj, role, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "item failed 'does'"); + int obj_does = 0; + ${ does obj_does, obj, role }; + if (obj_does) + self.fail(message); + } + + function equal(var o1, var o2, + string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "objects not equal"); + int equal = 0; + ${ iseq equal, o1, o2 }; + if (!equal) + self.fail(message); + } + + function not_equal(var o1, var o2, + string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "objects equal"); + int equal = 0; + ${ iseq equal, o1, o2 }; + if (equal) + self.fail(message); + } + + function instance_of(var obj, var type, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "object is not instance_of"); + using Rosella.get_type_class; + var class_obj = get_type_class(type); + int it_isa = 0; + ${ isa it_isa, obj, class_obj }; + if (!it_isa) + self.fail(message); + } + + function not_instance_of(var obj, var type, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "object is instance_of"); + using Rosella.get_type_class; + var class_obj = get_type_class(type); + int it_isa = 0; + ${ isa it_isa, obj, class_obj }; + if (it_isa) + self.fail(message); + } + + //function isa($obj, $class, string message [optional], int has_msg [opt_flag] = "object not isa") { + // self.fail($message) unless pir::isa__iPP($obj, P6metaclass.get_parrotclass($class)); + //} + + //function not_isa($obj, $class, string message [optional], int has_msg [opt_flag] = "object isa") { + // self.fail($message) if pir::isa__iPP($obj, P6metaclass.get_parrotclass($class)); + //} + + // TODO: Port matcher library + //function match($obj, $matcher, string message [optional], int has_msg [opt_flag] = "match failed") { + // unless $matcher.matches($obj) { + // my $explain = $matcher.describe_self("\nExpected ") + // ~ $matcher.describe_failure("\nbut ", $obj); + // self.fail($message ~ $explain); + // } + //} + + //function not_match($obj, $matcher, string message [optional], int has_msg [opt_flag]) { + // if $matcher.matches($obj) { + // self.fail($message); + // } + //} + + function is_null(var obj, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "object is not null"); + if (obj != null) + self.fail(message); + } + + function not_null(var obj, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "object is null"); + if (obj == null) + self.fail(message); + } + + function same(var o1, var o2, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "objects not same"); + if (o1 !== o2) + self.fail(message); + } + + function not_same(var o1, var o2, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "objects are same"); + if (o1 === o2) + self.fail(message); + } + + function throws(var block, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "does not throw"); + try { + block(); + self.fail(message); + } catch() {} + } + + function throws_nothing(var block, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "block throws"); + try { + block(); + } catch(e) { + self.fail(message, e:[named("exception")]); + } + } + + function is_true(int bool, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "is not true"); + if (!bool) + self.fail(message); + } + + function is_false(int bool, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "is not false"); + if (bool) + self.fail(message); + } + + //function within_delta($o1, $o2, $delta, string message [optional], int has_msg [opt_flag] "difference not within delta") { + // my $difference = $o1 - $o2; + // $difference = - $difference if $difference < 0; + // self.fail($message) unless $difference < $delta; + //} + + //#~ like(obj, regex, message) + //#~ not_like + + //#function want_fail($message, &block) { + //# throws(Exception::RosellaFailure, $message, &block); + //#} + + //#function want_pass($message, &block) { + //# throws_nothing($message, &block); + //#} + + // meta-test. Runs the block as a test, and expects a failure + function expect_fail(var block, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "test did not fail"); + var exception = null; + try { + block(); + } catch (e) { + exception = e; + } + if (exception == null) + self.fail(message); + + var payload = exception.payload; + if (payload == null) + self.fail(message, exception:[named("exception")]); + + using Rosella.isa_type; + int isa_failure = isa_type(class Rosella.Test.Failure, payload); + if (!isa_failure) + self.fail(message, exception:[named("exception")]); + } + + // meta-test. Runs the block as a test, and expects a pass + function expect_pass(var block, string message [optional], int has_msg [opt_flag]) + { + message = self.default_message(message, has_msg, "test did not pass"); + var exception = null; + try { + block(); + } catch (e) { + exception = e; + } + if (exception != null) + self.fail(message, exception:[named("exception")]); + } + + // Run the test, show that the output (and maybe the error output) matches + // the values expected + function output_is(var block, string output, + string message [optional], int has_msg [opt_flag], + string erroutput [named,optional], int has_erroutput [opt_flag]) + { + using Rosella.IO.swap_handles; + message = self.default_message(message, has_msg, "output did not match"); + var stdout = new "StringHandle"; + stdout.open("Rosella.Test.Assert.stdout", "rw"); + var stderr = new "StringHandle"; + stderr.open("Rosella.Test.Assert.stderr", "rw"); + var save_handles = swap_handles({"stdout": stdout, "stderr": stderr}); + var exception = null; + try { + block(); + } catch (e) { + exception = e; + } + swap_handles(save_handles); + + if (exception != null) { + say(exception); + self.fail("Block threw exception before output received", + exception:[named("exception")] + ); + } + + // TODO: We should trim leading/trailing whitespace + + string out_str = stdout.readall(); + if (out_str != output) + self.fail(sprintf("%s\nExpected: '%s'\nReceived:'%s'", [message, output, out_str])); + + if (!has_erroutput) + return; + + string errout_str = stderr.readall(); + if (errout_str != erroutput) + self.fail(message); + } + } }} diff --git a/src/test/Assertions.winxed b/src/test/Assertions.winxed index 13f67802..8b137891 100644 --- a/src/test/Assertions.winxed +++ b/src/test/Assertions.winxed @@ -1,332 +1 @@ -/* Functions for test assertions. These are in the ["Assert"] namespace, not - ["Rosella";"Test";"Assert"], for brevity. -*/ -// TODO: We really should fully-qualify the namespace here, but we are going -// to need a tool to relocate namespace entries to preserve existing -// tests. -namespace Assert -{ - // Routine to get a default message, if none is provided - function default_message(string msg, int has_msg, string def) - { - if (has_msg) - return msg; - return def; - } - - // Unconditional fail. Throws a Rosella.Test.Failure - function fail(string why, - int is_internal [optional,named], int has_is_i [opt_flag], - var exception [optional,named], int has_ex [opt_flag] - ) - { - using Rosella.build; - - if (!has_is_i) - is_internal = 0; - if (!has_ex) - exception = null; - - var ex = build(class Rosella.Test.Failure, why, exception, is_internal); - ex.throw(); - } - - /* Assertion Functions - Each of these functions asserts some condition. If the condition - holds, nothing happens. If the condition fails, we call fail(). - */ - - function block(string message, var block) - { - if (!block()) - fail(message); - } - - function block_false(string message, var block) - { - if (block()) - fail(message); - } - - function can(var obj, string method, - string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "item failed 'can'"); - int i_can = 0; - ${ can i_can, obj, method }; - if (!i_can) - fail(message); - } - - function can_not(var obj, string method, - string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "item failed 'can_not'"); - int i_can = 0; - ${ can i_can, obj, method }; - if (i_can) - fail(message); - } - - function defined(var obj, - string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "item not defined"); - int is_defined = 0; - ${ defined is_defined, obj }; - if (!is_defined) - fail(message); - } - - function not_defined(var obj, - string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "item is defined"); - int is_defined = 0; - ${ defined is_defined, obj }; - if (is_defined) - fail(message); - } - - function does(var obj, var role, - string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "item failed 'does'"); - int obj_does = 0; - ${ does obj_does, obj, role }; - if (!obj_does) - fail(message); - } - - function does_not(obj, role, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "item failed 'does'"); - int obj_does = 0; - ${ does obj_does, obj, role }; - if (obj_does) - fail(message); - } - - function equal(var o1, var o2, - string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "objects not equal"); - int equal = 0; - ${ iseq equal, o1, o2 }; - if (!equal) - fail(message); - } - - function not_equal(var o1, var o2, - string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "objects equal"); - int equal = 0; - ${ iseq equal, o1, o2 }; - if (equal) - fail(message); - } - - function instance_of(var obj, var type, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "object is not instance_of"); - using Rosella.get_type_class; - var class_obj = get_type_class(type); - int it_isa = 0; - ${ isa it_isa, obj, class_obj }; - if (!it_isa) - fail(message); - } - - function not_instance_of(var obj, var type, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "object is instance_of"); - using Rosella.get_type_class; - var class_obj = get_type_class(type); - int it_isa = 0; - ${ isa it_isa, obj, class_obj }; - if (it_isa) - fail(message); - } - - //function isa($obj, $class, string message [optional], int has_msg [opt_flag] = "object not isa") { - // fail($message) unless pir::isa__iPP($obj, P6metaclass.get_parrotclass($class)); - //} - - //function not_isa($obj, $class, string message [optional], int has_msg [opt_flag] = "object isa") { - // fail($message) if pir::isa__iPP($obj, P6metaclass.get_parrotclass($class)); - //} - - // TODO: Port matcher library - //function match($obj, $matcher, string message [optional], int has_msg [opt_flag] = "match failed") { - // unless $matcher.matches($obj) { - // my $explain = $matcher.describe_self("\nExpected ") - // ~ $matcher.describe_failure("\nbut ", $obj); - // fail($message ~ $explain); - // } - //} - - //function not_match($obj, $matcher, string message [optional], int has_msg [opt_flag]) { - // if $matcher.matches($obj) { - // fail($message); - // } - //} - - function is_null(var obj, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "object is not null"); - if (obj != null) - fail(message); - } - - function not_null(var obj, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "object is null"); - if (obj == null) - fail(message); - } - - function same(var o1, var o2, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "objects not same"); - if (o1 !== o2) - fail(message); - } - - function not_same(var o1, var o2, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "objects are same"); - if (o1 === o2) - fail(message); - } - - function throws(var block, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "does not throw"); - try { - block(); - fail(message); - } catch() {} - } - - function throws_nothing(var block, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "block throws"); - try { - block(); - } catch(e) { - fail(message, e:[named("exception")]); - } - } - - function is_true(int bool, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "is not true"); - if (!bool) - fail(message); - } - - function is_false(int bool, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "is not false"); - if (bool) - fail(message); - } - - //function within_delta($o1, $o2, $delta, string message [optional], int has_msg [opt_flag] "difference not within delta") { - // my $difference = $o1 - $o2; - // $difference = - $difference if $difference < 0; - // fail($message) unless $difference < $delta; - //} - - //#~ like(obj, regex, message) - //#~ not_like - - //#function want_fail($message, &block) { - //# throws(Exception::RosellaFailure, $message, &block); - //#} - - //#function want_pass($message, &block) { - //# throws_nothing($message, &block); - //#} - - // meta-test. Runs the block as a test, and expects a failure - function expect_fail(var block, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "test did not fail"); - var exception = null; - try { - block(); - } catch (e) { - exception = e; - } - if (exception == null) - fail(message); - - var payload = exception.payload; - if (payload == null) - fail(message, exception:[named("exception")]); - - using Rosella.isa_type; - int isa_failure = isa_type(class Rosella.Test.Failure, payload); - if (!isa_failure) - fail(message, exception:[named("exception")]); - } - - // meta-test. Runs the block as a test, and expects a pass - function expect_pass(var block, string message [optional], int has_msg [opt_flag]) - { - message = default_message(message, has_msg, "test did not pass"); - var exception = null; - try { - block(); - } catch (e) { - exception = e; - } - if (exception != null) - fail(message, exception:[named("exception")]); - } - - // Run the test, show that the output (and maybe the error output) matches - // the values expected - function output_is(var block, string output, - string message [optional], int has_msg [opt_flag], - string erroutput [named,optional], int has_erroutput [opt_flag]) - { - using Rosella.IO.swap_handles; - message = default_message(message, has_msg, "output did not match"); - var stdout = new "StringHandle"; - stdout.open("Rosella.Test.Assert.stdout", "rw"); - var stderr = new "StringHandle"; - stderr.open("Rosella.Test.Assert.stderr", "rw"); - var save_handles = swap_handles({"stdout": stdout, "stderr": stderr}); - var exception = null; - try { - block(); - } catch (e) { - exception = e; - } - swap_handles(save_handles); - - if (exception != null) { - say(exception); - fail("Block threw exception before output received", - exception:[named("exception")] - ); - } - - // TODO: We should trim leading/trailing whitespace - - string out_str = stdout.readall(); - if (out_str != output) - fail(sprintf("%s\nExpected: '%s'\nReceived:'%s'", [message, output, out_str])); - - if (!has_erroutput) - return; - - string errout_str = stderr.readall(); - if (errout_str != erroutput) - fail(message); - } -}