Hi all, Here's a first shot at Test::More in pure PIR. It only supports plan(), ok(), and is() (for integers, floats, strings, and PMCs) right now. If everyone's reasonably happy, I'll check it in. Everything's pretty straightforward here and any decent PIR hacker should be able to follow along.
I've also attached a pure PIR test script that uses Test::Builder::Tester. One recurring problem I ran into is the one where you can't use load_bytecode on a library file multiple times, else it warns about reinitialization. I don't remember if there is a workaround, so I welcome any suggestions. The next step is diving into Parrot::Test to see what it needs and what I already have working. At some point we ought to figure out where tests for libraries should go. I have pure PIR tests for Test::Builder, Test::Builder::Tester, and Test::More now. I've attached the latter. -- c
Index: MANIFEST =================================================================== --- MANIFEST (revision 10187) +++ MANIFEST (working copy) @@ -1656,6 +1656,7 @@ runtime/parrot/library/Test/Builder/Test.pir [library] runtime/parrot/library/Test/Builder/TestPlan.pir [library] runtime/parrot/library/Test/Builder/Tester.pir [library] +runtime/parrot/library/Test/More.pir [library] runtime/parrot/library/YAML/Parser/Syck.imc [library] runtime/parrot/library/config.imc [library] runtime/parrot/library/dumper.imc [library] --- /dev/null 1969-12-31 16:00:00.000000000 -0800 +++ runtime/parrot/library/Test/More.pir 2005-11-26 13:05:47.000000000 -0800 @@ -0,0 +1,272 @@ +=head1 NAME + +Test::More - Parrot extension for testing modules + +=head1 SYNOPSIS + + # load this library + load_bytecode 'library/Test/More.pir' + + # get the testing functions + .local pmc plan + .local pmc ok + .local pmc is + + plan = find_global 'Test::More', 'plan' + ok = find_global 'Test::More', 'ok' + is = find_global 'Test::More', 'is' + + # set a test plan + plan( 10 ) + + # run your tests + ok( 1 ) + ok( 0, 'failing test with diagnostic' ) + + is( 100, 100 ) + is( 200, 100, 'failing integer compare with diagnostic' ) + + is( 1.001, 1.001, 'passing float compare with diagnostic' ) + is( 8.008, 4.004 ) + + is( 'foo', 'foo', 'passing string compare with diagnostic' ) + is( 'foo', 'bar', 'failing string compare with diagnostic' ) + + is( some_pmc, another_pmc, 'pmc comparison uses "eq" op' ) + +=head1 DESCRIPTION + +C<Test::More> is a pure-Parrot library for testing modules. It provides the +C<ok()> and C<is()> functions for you. It uses C<Test::Builder>, a simple, +single backend for multiple test modules to use within your tests. + +=head1 FUNCTIONS + +This class defines the following functions: + +=over 4 + +=cut + +.namespace [ 'Test::More' ] + +.sub _initialize :load +# XXX: can't prevent multiple initialization yet +# load_bytecode 'library/Test/Builder.pir' + + .local pmc test + .local int test_type + + find_type test_type, 'Test::Builder' + test = new test_type + + store_global 'Test::More', '_test', test +.end + +=item C<plan( number_or_no_plan )> + +Declares the number of tests you plan to run, either an integer greater than +zero or the string C<no_plan>. This will throw an exception if you have +already declared a plan or if you pass an invalid argument. + +=cut + +.sub plan + .param string tests + + .local pmc test + find_global test, 'Test::More', '_test' + test.plan( tests ) +.end + +=item C<ok( passed, description )> + +Records a test as pass or fail depending on the truth of the integer C<passed>, +recording it with the optional test description in C<description>. + +=cut + +.sub ok + .param int passed + .param string description :optional + + .local pmc test + find_global test, 'Test::More', '_test' + + test.ok( passed, description ) +.end + +=item C<is( left, right, description )> + +Compares the parameters passed as C<left> and C<right>, passing if they are +equal and failing otherwise. This will report the results with the optional +test description in C<description>. + +This is a multi-method, with separate implementations for int-int, float-float, +string-string, and PMC-PMC comparisons. The latter uses the C<eq> opcode for +comparison. + +This probably doesn't handle all of the comparisons you want, but it's easy to +add more. + +=cut + +.sub is :multi( int, int ) + .param int left + .param int right + .param string description :optional + + .local pmc test + find_global test, 'Test::More', '_test' + + .local int pass + pass = 0 + + eq left, right, PASS + goto REPORT + + PASS: + pass = 1 + + REPORT: + test.ok( pass, description ) + if pass goto DONE + + .local string diagnostic + .local string l_string + .local string r_string + + l_string = left + r_string = right + + diagnostic = make_diagnostic( l_string, r_string ) + test.diag( diagnostic ) + DONE: +.end + +.sub is :multi( float, float ) + .param float left + .param float right + .param string description :optional + + .local pmc test + find_global test, 'Test::More', '_test' + + .local int pass + pass = 0 + + eq left, right, PASS + goto REPORT + + PASS: + pass = 1 + + REPORT: + test.ok( pass, description ) + if pass goto DONE + + .local string diagnostic + .local string l_string + .local string r_string + + l_string = left + r_string = right + + diagnostic = make_diagnostic( l_string, r_string ) + test.diag( diagnostic ) + DONE: +.end + +.sub is :multi( string, string ) + .param string left + .param string right + .param string description :optional + + .local pmc test + find_global test, 'Test::More', '_test' + + .local int pass + pass = 0 + + eq left, right, PASS + goto REPORT + + PASS: + pass = 1 + + REPORT: + test.ok( pass, description ) + if pass goto DONE + + .local string diagnostic + .local string l_string + .local string r_string + + l_string = left + r_string = right + + diagnostic = make_diagnostic( l_string, r_string ) + test.diag( diagnostic ) + DONE: +.end + +.sub is :multi( pmc, pmc ) + .param pmc left + .param pmc right + .param string description :optional + + .local pmc test + find_global test, 'Test::More', '_test' + + .local int pass + pass = 0 + + eq left, right, PASS + goto REPORT + + PASS: + pass = 1 + + REPORT: + test.ok( pass, description ) + if pass goto DONE + + .local string diagnostic + .local string l_string + .local string r_string + + l_string = left + r_string = right + + diagnostic = make_diagnostic( l_string, r_string ) + test.diag( diagnostic ) + DONE: +.end + +.sub make_diagnostic + .param string received + .param string expected + .local string diagnostic + + diagnostic = 'Received: ' + diagnostic .= received + diagnostic .= "\nExpected: " + diagnostic .= expected + + .return( diagnostic ) +.end + +=back + +=head1 AUTHOR + +Written and maintained by chromatic, C<< chromatic at wgz dot org >>, based on +the Perl 6 port he wrote, based on the original Perl 5 version he wrote with +ideas from Michael G. Schwern. Please send patches, feedback, and suggestions +to the Perl 6 internals mailing list. + +=head1 COPYRIGHT + +Copyright (c) 2005, the Perl Foundation. + +=cut
.sub _main @MAIN load_bytecode 'library/Test/Builder/Tester.pir' load_bytecode 'library/Test/More.pir' .local int tb_type find_type tb_type, 'Test::Builder' .local pmc tb_args tb_args = new .Hash .local pmc test test = new tb_type, tb_args .local pmc plan .local pmc test_pass .local pmc test_fail .local pmc test_diag .local pmc test_test .local pmc ok .local pmc is plan = find_global 'Test::Builder::Tester', 'plan' test_pass = find_global 'Test::Builder::Tester', 'test_pass' test_fail = find_global 'Test::Builder::Tester', 'test_fail' test_diag = find_global 'Test::Builder::Tester', 'test_diag' test_test = find_global 'Test::Builder::Tester', 'test_test' ok = find_global 'Test::More', 'ok' is = find_global 'Test::More', 'is' plan( 20 ) test_pass() ok( 1 ) test_test( 'passing test ok()') test_fail() ok( 0 ) test_test( 'failing test ok()') test_pass( 'with description' ) ok( 1, 'with description' ) test_test( 'passing test ok() with description') test_fail( 'with description' ) ok( 0, 'with description' ) test_test( 'failing test ok() with description') test_pass() is( 100, 100 ) test_test( 'passing test is() for ints') test_fail() test_diag( 'Received: -100' ) test_diag( 'Expected: 200' ) is( -100, 200 ) test_test( 'failing test is() for ints') test_pass( 'comparing two integers' ) is( 512, 512, 'comparing two integers' ) test_test( 'passing test is() for ints with description') test_fail( 'comparing two integers' ) is( -512, 5120, 'comparing two integers' ) test_diag( 'Received: -512' ) test_diag( 'Expected: 5120' ) test_test( 'failing test is() for ints with description') test_pass() is( 6.5, 6.5 ) test_test( 'passing test is() for floats') test_fail() is( 1.235, 5.321 ) test_diag( 'Received: 1.235' ) test_diag( 'Expected: 5.321' ) test_test( 'failing test is() for floats') test_pass( 'comparing two floats' ) is( 80.80, 80.80, 'comparing two floats' ) test_test( 'passing test is() for floats with description') test_fail( 'comparing two floats' ) is( 777.1, 888.8, 'comparing two floats' ) test_diag( 'Received: 777.1' ) test_diag( 'Expected: 888.8' ) test_test( 'failing test is() for floats with description') test_pass() is( 'bob', 'bob' ) test_test( 'passing test is() for strings') test_fail() is( 'larry', 'bob' ) test_diag( 'Received: larry' ) test_diag( 'Expected: bob' ) test_test( 'failing test is() for strings') test_pass( 'comparing two strings' ) is( 'larry', 'larry', 'comparing two strings' ) test_test( 'passing test is() for strings with description') test_fail( 'comparing two strings' ) is( 'zeke', 'zelda', 'comparing two strings' ) test_diag( 'Received: zeke' ) test_diag( 'Expected: zelda' ) test_test( 'failing test is() for strings with description') .local pmc left .local pmc right left = new .String right = new .String left = 'zero' right = 'zero' test_pass() is( left, right ) test_test( 'passing test is() for pmcs') right = new .Integer right = 0 test_fail() test_diag( 'Received: zero' ) test_diag( 'Expected: 0' ) is( left, right ) test_test( 'failing test is() for pmcs') left = '0' test_pass( 'comparing two pmcs' ) is( left, right, 'comparing two pmcs' ) test_test( 'passing test is() for pmcs with description') right = new .Hash .local string expected .local string hash_string expected = 'Expected: ' hash_string = right expected .= hash_string test_fail( 'comparing two pmcs' ) test_diag( 'Received: 0' ) test_diag( expected ) is( left, right, 'comparing two pmcs' ) test_test( 'failing test is() for pmcs with description') test.'finish'() .end