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

Reply via email to