3 Test::More - Parrot extension for testing modules
8 load_bytecode 'library/Test/More.pbc'
10 # get the testing functions
11 .local pmc exports, curr_namespace, test_namespace
12 curr_namespace = get_namespace
13 test_namespace = get_namespace [ 'Test'; 'More' ]
14 exports = split ' ', 'plan diag ok is is_deeply like isa_ok isnt'
16 test_namespace.'export_to'(curr_namespace, exports)
23 ok( 0, 'failing test with diagnostic' )
26 is( 200, 100, 'failing integer compare with diagnostic' )
28 is( 1.001, 1.001, 'passing float compare with diagnostic' )
31 is( 'foo', 'foo', 'passing string compare with diagnostic' )
32 is( 'foo', 'bar', 'failing string compare with diagnostic' )
34 is( some_pmc, another_pmc, 'pmc comparison uses "eq" op' )
36 diag( 'this may take a while' )
37 is_deeply( some_deep_pmc, another_deep_pmc, 'deep structure comparison' )
39 like( 'foo', 'f o**{2}', 'passing regex compare with diagnostic' )
40 skip(1, 'reason for skipping')
41 todo(0, 'this is a failed test', 'reason for todo')
43 $P0 = get_class "Squirrel"
46 isa_ok($P0, "Squirrel", "new Squirrel")
50 C<Test::More> is a pure-Parrot library for testing modules. It provides the
51 C<ok()>, C<is()>, C<isnt()>, C<is_deeply()>, and C<like()> comparison functions
52 for you. It also provides the C<plan()> and C<diag()> helper functions. It
53 uses C<Test::Builder>, a simple, single backend for multiple test modules
54 to use within your tests.
58 This class defines the following functions:
64 .namespace [ 'Test'; 'More' ]
66 .sub _initialize :load
67 load_bytecode 'library/Test/Builder.pbc'
70 test = new [ 'Test'; 'Builder' ]
72 store_global [ 'Test'; 'More' ], '_test', test
75 =item C<plan( number_or_no_plan )>
77 Declares the number of tests you plan to run, either an integer greater than
78 zero or the string C<no_plan>. This will throw an exception if you have
79 already declared a plan or if you pass an invalid argument.
87 find_global test, [ 'Test'; 'More' ], '_test'
91 =item C<ok( passed, description )>
93 Records a test as pass or fail depending on the truth of the integer C<passed>,
94 recording it with the optional test description in C<description>.
100 .param string description :optional
103 find_global test, [ 'Test'; 'More' ], '_test'
105 test.ok( passed, description )
108 =item C<nok( passed, description )>
110 Records a test as pass or fail depending on the falsehood of the integer
111 C<passed>, recording it with the optional test description in C<description>.
117 .param string description :optional
120 find_global test, [ 'Test'; 'More' ], '_test'
122 .local int reverse_passed
123 reverse_passed = not passed
125 test.ok( reverse_passed, description )
128 =item C<is( left, right, description )>
130 Compares the parameters passed as C<left> and C<right>, passing if they are
131 equal and failing otherwise. This will report the results with the optional
132 test description in C<description>.
134 This is a multi-method, with separate implementations for int-int, float-float,
135 string-string, and PMC-PMC comparisons. The latter uses the C<eq> opcode for
138 If there is a mismatch, the current implementation takes the type of C<left> as
139 the proper type for the comparison, converting any numeric arguments to floats.
140 Note that there is a hard-coded precision check to avoid certain rounding
141 errors. It's not entirely robust, but it's not completely awful either.
143 Patches very welcome. Multi-dispatch is a bit tricky here.
145 This probably doesn't handle all of the comparisons you want, but it's easy to
150 .sub is :multi( int, int )
153 .param string description :optional
156 find_global test, [ 'Test'; 'More' ], '_test'
161 if left == right goto pass_it
168 test.ok( pass, description )
171 .local string diagnostic
172 .local string l_string
173 .local string r_string
178 diagnostic = _make_diagnostic( l_string, r_string )
179 test.diag( diagnostic )
183 .sub is :multi( num, num )
186 .param string description :optional
189 find_global test, [ 'Test'; 'More' ], '_test'
194 eq left, right, pass_it
201 test.ok( pass, description )
204 .local string diagnostic
205 .local string l_string
206 .local string r_string
211 diagnostic = _make_diagnostic( l_string, r_string )
212 test.diag( diagnostic )
216 .sub is :multi( string, string )
219 .param string description :optional
222 find_global test, [ 'Test'; 'More' ], '_test'
227 eq left, right, pass_it
234 test.ok( pass, description )
237 .local string diagnostic
238 .local string l_string
239 .local string r_string
244 diagnostic = _make_diagnostic( l_string, r_string )
245 test.diag( diagnostic )
252 .param string description :optional
255 find_global test, [ 'Test'; 'More' ], '_test'
261 r_type = typeof right
263 if r_type == 'Float' goto num_compare
264 if r_type == 'Int' goto num_compare
273 if l_val == r_val goto pass_it
275 # XXX - significant places? I don't care :)
279 if diff < 0.000000000001 goto pass_it
286 eq l_val, r_val, pass_it
293 test.ok( pass, description )
296 .local string diagnostic
297 .local string l_string
298 .local string r_string
303 diagnostic = _make_diagnostic( l_string, r_string )
304 test.diag( diagnostic )
308 =item C<isnt( left, right, description )>
310 Like C<is>, but succeeds if the arguments I<don't> match.
314 .sub isnt :multi( int, int )
317 .param string description :optional
320 find_global test, [ 'Test'; 'More' ], '_test'
325 if left != right goto pass_it
332 test.ok( pass, description )
335 .local string diagnostic
336 .local string l_string
337 .local string r_string
341 r_string = 'not ' . r_string
343 diagnostic = _make_diagnostic( l_string, r_string )
344 test.diag( diagnostic )
348 .sub isnt :multi( num, num )
351 .param string description :optional
354 find_global test, [ 'Test'; 'More' ], '_test'
359 ne left, right, pass_it
366 test.ok( pass, description )
369 .local string diagnostic
370 .local string l_string
371 .local string r_string
375 r_string = 'not ' . r_string
377 diagnostic = _make_diagnostic( l_string, r_string )
378 test.diag( diagnostic )
382 .sub isnt :multi( string, string )
385 .param string description :optional
388 find_global test, [ 'Test'; 'More' ], '_test'
393 ne left, right, pass_it
400 test.ok( pass, description )
403 .local string diagnostic
404 .local string l_string
405 .local string r_string
409 r_string = 'not ' . r_string
411 diagnostic = _make_diagnostic( l_string, r_string )
412 test.diag( diagnostic )
419 .param string description :optional
422 find_global test, [ 'Test'; 'More' ], '_test'
428 r_type = typeof right
430 if r_type == 'Float' goto num_compare
431 if r_type == 'Int' goto num_compare
440 if l_val != r_val goto pass_it
442 # XXX - significant places? I don't care :)
446 if diff < 0.000000000001 goto pass_it
454 ne l_val, r_val, pass_it
461 test.ok( pass, description )
464 .local string diagnostic
465 .local string l_string
466 .local string r_string
470 r_string = 'not ' . r_string
472 diagnostic = _make_diagnostic( l_string, r_string )
473 test.diag( diagnostic )
477 =item C<diag( diagnostic )>
479 Prints C<diagnostic> to the screen, without affecting test comparisons.
484 .param string diagnostic
487 find_global test, [ 'Test'; 'More' ], '_test'
488 test.diag( diagnostic )
492 =item C<is_deeply( left, right, description )>
494 Compares the data structures passed as C<left> and C<right>. If data
495 structures are passed, C<is_deeply> does a deep comparison by walking each
496 structure. It passes if they are equal and fails otherwise. This will
497 report the results with the optional test description in C<description>.
499 This only handles comparisons of array-like structures. It shouldn't be too
500 hard to extend it for hash-like structures, too.
504 .sub is_deeply :multi( pmc, pmc )
507 .param string description :optional
510 .local string diagnosis
513 position = new 'ResizablePMCArray'
516 find_global test, [ 'Test'; 'More' ], '_test'
519 does_flag = does left, 'array'
520 if does_flag goto compare_array
522 does_flag = does left, 'hash'
523 if does_flag goto compare_hash
525 diagnosis = typeof left
526 diagnosis .= ' is not a nested data structure'
531 ( result, diagnosis ) = compare_array( left, right, position )
535 (result, diagnosis ) = compare_hash( left, right, position )
539 test.'ok'( result, description )
541 unless result goto report_diagnostic
545 ne diagnosis, '', return_it
552 .local string nested_path
553 nested_path = join '][', position
555 diagnosis = 'Mismatch'
556 unless nested_path goto show_expected
559 diagnosis .= nested_path
563 diagnosis .= ': expected '
565 diagnosis .= ', received '
569 test.'diag'( diagnosis )
579 find_global test, [ 'Test'; 'More' ], '_test'
585 if l_count == r_count goto compare_contents
587 .local string l_count_string
588 .local string r_count_string
589 l_count_string = l_count
590 l_count_string .= ' element'
592 if l_count == 1 goto pluralization_done
593 l_count_string .= 's'
596 r_count_string = r_count
598 push position, l_count_string
599 push position, r_count_string
608 l_iter = new 'Iterator', l_array
609 r_iter = new 'Iterator', r_array
616 .local int elems_equal
619 unless l_iter goto iter_end
620 l_elem = shift l_iter
621 r_elem = shift r_iter
624 elems_equal = compare_elements( l_elem, r_elem, position )
625 unless elems_equal goto elems_not_equal
631 unshift position, count
644 find_global test, [ 'Test'; 'More' ], '_test'
650 if l_count == r_count goto compare_contents
652 .local string l_count_string
653 .local string r_count_string
654 l_count_string = l_count
655 l_count_string .= ' element'
657 if l_count == 1 goto pluralization_done
658 l_count_string .= 's'
661 r_count_string = r_count
663 push position, l_count_string
664 push position, r_count_string
673 l_iter = new 'Iterator', l_hash
674 r_iter = new 'Iterator', r_hash
683 .local int elems_equal
686 unless l_iter goto iter_end
689 l_elem = l_hash[ l_key ]
690 r_elem = r_hash[ r_key ]
692 elems_equal = compare_elements( l_elem, r_elem, position )
693 unless elems_equal goto elems_not_equal
699 unshift position, l_key
706 .sub compare_elements :multi( string, string, PMC )
713 eq left, right, are_equal
722 .sub compare_elements :multi( int, int, PMC )
728 eq left, right, are_equal
739 .sub compare_elements :multi( String, String, PMC )
745 eq left, right, are_equal
756 .sub compare_elements :multi( Integer, Integer, PMC )
762 eq left, right, are_equal
773 .sub compare_elements :multi( Array, Array, PMC )
779 equal = compare_array( left, right, position )
783 .sub compare_elements :multi( Hash, Hash, PMC )
789 equal = compare_hash( left, right, position )
793 .sub compare_elements :multi( Undef, Undef, PMC )
801 .sub compare_elements :multi( Undef, PMC, PMC )
806 .local string l_undef
808 push position, l_undef
813 .sub compare_elements :multi( PMC, Undef, PMC )
818 .local string r_undef
821 push position, r_undef
825 .sub compare_elements :multi( PMC, PMC, PMC )
834 does_flag = does left, 'array'
835 unless does_flag goto check_hash
836 equal = compare_array( left, right, position )
840 does_flag = does left, 'hash'
841 if does_flag goto compare_hash
845 equal = compare_hash( left, right, position )
849 =item C<like( target, pattern, description )>
851 Similar to is, but using the Parrot Grammar Engine to compare the string
852 passed as C<target> to the pattern passed as C<pattern>. It passes if the
853 pattern matches and fails otherwise. This will report the results with the
854 optional test description in C<description>.
860 .param string pattern
861 .param string description :optional
864 find_global test, [ 'Test'; 'More' ], '_test'
866 .local pmc p6rule_compile
867 load_bytecode "PGE.pbc"
868 load_bytecode "PGE/Dumper.pbc"
869 load_bytecode "PGE/Text.pbc"
870 load_bytecode "PGE/Util.pbc"
871 p6rule_compile = compreg "PGE::Perl6Regex"
873 .local string diagnostic
882 (rulesub, code, exp) = p6rule_compile(pattern)
883 if_null rulesub, rule_fail
884 match = rulesub(target)
885 unless match goto match_fail
889 diagnostic = "match failed"
892 diagnostic = "rule error"
899 test.ok( pass, description )
902 test.diag( diagnostic )
906 =item C<skip( how_many, why )>
908 Pass a number of tests, but with a comment that marks the test was
909 actually skipped. Arguments are optional.
913 .sub skip :multi(int, string)
915 .param string description
918 find_global test, [ 'Test'; 'More' ], '_test'
919 test.'skip'(how_many, description)
922 .sub skip :multi(int)
926 find_global test, [ 'Test'; 'More' ], '_test'
927 test.'skip'(how_many)
930 .sub skip :multi(string)
931 .param string description
934 find_global test, [ 'Test'; 'More' ], '_test'
935 test.'skip'(1, description)
940 find_global test, [ 'Test'; 'More' ], '_test'
944 =item C<todo( passed, description, reason )>
946 Records a test as pass or fail (like C<ok>, but marks it as TODO so it always
947 appears as a success. This also records the optional C<description> of the test
948 and the C<reason> you have marked it as TODO.
953 .param pmc args :slurpy
956 find_global test, [ 'Test'; 'More' ], '_test'
958 test.todo( args :flat )
961 =item C<isa_ok( object, class_name, object_name )>
963 Pass if the object C<isa> class of the given class name. The object
964 name passed in is not a full description, but a name to be included in
965 the description. The description is presented as "<object_name> isa
968 Good input: "C<new MyObject>", "C<return from bar()>"
970 Bad input: "C<test that the return from Foo is correct type>"
976 .param pmc class_name
977 .param pmc object_name :optional
978 .param int got_name :opt_flag
981 find_global test, [ 'Test'; 'More' ], '_test'
983 .local string description, diagnostic
984 description = "The object"
985 unless got_name goto keep_default
986 description = object_name
988 diagnostic = description
989 description .= " isa "
993 $I0 = isa thingy, class_name
994 test.'ok'($I0, description)
996 diagnostic .= " isn't a "
999 diagnostic .= " it's a "
1002 test.'diag'(diagnostic)
1006 .sub _make_diagnostic
1007 .param string received
1008 .param string expected
1009 .local string diagnostic
1011 diagnostic = 'Have: '
1012 diagnostic .= received
1013 diagnostic .= "\nWant: "
1014 diagnostic .= expected
1016 .return( diagnostic )
1023 Written and maintained by chromatic, C<< chromatic at wgz dot org >>, based on
1024 the Perl 6 port he wrote, based on the original Perl 5 version he wrote with
1025 ideas from Michael G. Schwern. Please send patches, feedback, and suggestions
1026 to the Perl 6 internals mailing list.
1030 Copyright (C) 2005-2008, The Perl Foundation.
1038 # vim: expandtab shiftwidth=4 ft=pir: