Friday, February 12, 2010

Progress!

My taxes aren't paid yet.

That said, I've at least seen some forward progress under the new NQP. The change to not using :init on code at the top level means that I'm getting the chance to write more test cases for every ... single ... file in the kakapo library. And yeah, there's some "why the hell doesn't this work?" going on, as well.

I'm pleased to report that I have a working UnitTest system. It's modeled on xUnit, but the default also includes a TAP Listener so that test cases report as TAP tests.


Here's what it looks like:
class Test::Pmc::Undef
    is Test::Pmc::COMMON {
   
    INIT {
        use(    'P6metaclass' );
        use(    'UnitTest::Testcase' );
       
        Program::register_main();
    }
   
    sub main() {
        my $proto := Opcode::get_root_global(Opcode::get_namespace().get_name);
        $proto.suite.run;
    }
   
    method test_defined() {
        verify_that( "Defined returns false" );
        my $object := self.class.new;

        if $object.defined { fail( "Undef.defined reports yes" ); }
    }

Note that I've created a Test::Pmc::COMMON testcase class to handle some common test cases for all the Pmc types. So each Test::Pmc::<type> gets a bunch of test_... methods inherited.

The INIT block runs after the class is declared. I import P6metaclass, which Kakapo extends with some class definition subs. You don't see them here, but things like "   has( '$!attribute' ); " are there.

The main sub is magic. Black magic. Secrets man was not meant to know. Kakapo's "register_main" only works with subs - not methods. (For now...PHP anyone?) So there needs to be a sub, and it needs to have some kind of link to the class or namespace of the test case. So each test file includes this boilerplate 'main' that does eldritch mummery to figure out what the current class's proto-object is, and uses that to create and run a test suite from the current test case.

The test_defined method is a pretty representative test method. It's short and simple, checks a single condition and either returns quietly or throws an exception. The verify_that function sets the $.verify attribute on the current object (it uses dynamic scope to find 'self') so that tests can provide a little more description. Here's the output:
/usr/local/bin/parrot -Llibrary t/Pmc/Undef.t.pbc
1..5
ok 1 - 'new' returns an object of the right type
ok 2 - Defined returns false
ok 3 - 'isa' returns correct results
ok 4 - Clone returns a different, valid object
ok 5 - A 'can' method exists, and returns known results

No comments:

Post a Comment