5 Test::More - Parrot extension for testing modules
10 load_bytecode 'Test/More.pbc'
12 # get the testing functions
13 .local pmc exports, curr_namespace, test_namespace
14 curr_namespace = get_namespace
15 test_namespace = get_namespace [ 'Test'; 'More' ]
16 exports = split ' ', 'plan diag ok nok is is_deeply like isa_ok skip isnt todo throws_like'
18 test_namespace.'export_to'(curr_namespace, exports)
25 ok( 0, 'failing test with diagnostic' )
28 is( 200, 100, 'failing integer compare with diagnostic' )
30 is( 1.001, 1.001, 'passing float compare with diagnostic' )
33 is( 'foo', 'foo', 'passing string compare with diagnostic' )
34 is( 'foo', 'bar', 'failing string compare with diagnostic' )
36 is( some_pmc, another_pmc, 'pmc comparison uses "eq" op' )
38 diag( 'this may take a while' )
39 is_deeply( some_deep_pmc, another_deep_pmc, 'deep structure comparison' )
41 like( 'foo', 'f o**{2}', 'passing regex compare with diagnostic' )
42 skip(1, 'reason for skipping')
43 todo(0, 'this is a failed test', 'reason for todo')
45 $P0 = get_class "Squirrel"
48 isa_ok($P0, "Squirrel", "new Squirrel")
52 C<Test::More> is a pure-Parrot library for testing modules. It provides the
53 C<ok()>, C<is()>, C<isnt()>, C<is_deeply()>, and C<like()> comparison functions
54 for you. It also provides the C<plan()> and C<diag()> helper functions. It
55 uses C<Test::Builder>, a simple, single backend for multiple test modules
56 to use within your tests.
60 This class defines the following functions:
66 .namespace [ 'Test'; 'More' ]
68 .sub _initialize :load
69 load_bytecode 'Test/Builder.pbc'
72 test = new [ 'Test'; 'Builder' ]
74 set_hll_global [ 'Test'; 'More' ], '_test', test
77 =item C<plan( number_or_no_plan )>
79 Declares the number of tests you plan to run, either an integer greater than
80 zero or the string C<no_plan>. This will throw an exception if you have
81 already declared a plan or if you pass an invalid argument.
89 get_hll_global test, [ 'Test'; 'More' ], '_test'
93 =item C<ok( passed, description )>
95 Records a test as pass or fail depending on the truth of the PMC C<passed>,
96 recording it with the optional test description in C<description>.
102 .param string description :optional
105 get_hll_global test, [ 'Test'; 'More' ], '_test'
108 test.'ok'( $I0, description )
111 =item C<nok( passed, description )>
113 Records a test as pass or fail depending on the falsehood of the integer
114 C<passed>, recording it with the optional test description in C<description>.
120 .param string description :optional
123 get_hll_global test, [ 'Test'; 'More' ], '_test'
125 .local int reverse_passed
126 reverse_passed = isfalse passed
128 test.'ok'( reverse_passed, description )
131 =item C<is( left, right, description )>
133 Compares the parameters passed as C<left> and C<right>, passing if they are
134 equal and failing otherwise. This will report the results with the optional
135 test description in C<description>.
137 This is a multi-method, with separate implementations for int-int, float-float,
138 string-string, and PMC-PMC comparisons. The latter uses the C<eq> opcode for
141 If there is a mismatch, the current implementation takes the type of C<left> as
142 the proper type for the comparison, converting any numeric arguments to floats.
143 Note that there is a hard-coded precision check to avoid certain rounding
144 errors. It's not entirely robust, but it's not completely awful either.
146 Patches very welcome. Multi-dispatch is a bit tricky here.
148 This probably doesn't handle all of the comparisons you want, but it's easy to
153 .sub is :multi(PMC, Integer)
156 .param pmc description :optional
157 .param int have_desc :opt_flag
160 get_hll_global test, [ 'Test'; 'More' ], '_test'
162 .local int l, r, pass
167 test.'ok'( pass, description )
170 .local string diagnostic
171 .local string l_string
172 .local string r_string
177 diagnostic = _make_diagnostic( l_string, r_string )
178 test.'diag'( diagnostic )
182 .sub is :multi(PMC, Float)
185 .param pmc description :optional
186 .param int have_desc :opt_flag
187 .param pmc precision :optional
188 .param int have_prec :opt_flag
191 get_hll_global test, [ 'Test'; 'More' ], '_test'
200 unless have_prec goto report
202 .local num diff, prec_num
206 pass = isle diff, prec_num
209 test.'ok'( pass, description )
212 .local string diagnostic
213 .local string l_string
214 .local string r_string
219 diagnostic = _make_diagnostic( l_string, r_string )
220 test.'diag'( diagnostic )
224 .sub is :multi(PMC, String)
227 .param pmc description :optional
228 .param int have_desc :opt_flag
231 get_hll_global test, [ 'Test'; 'More' ], '_test'
239 test.'ok'( pass, description )
242 .local string diagnostic
243 .local string l_string
244 .local string r_string
249 diagnostic = _make_diagnostic( l_string, r_string )
250 test.'diag'( diagnostic )
254 .sub is :multi(PMC, PMC)
257 .param pmc description :optional
258 .param int have_desc :opt_flag
261 get_hll_global test, [ 'Test'; 'More' ], '_test'
266 does_type = does right, 'String'
267 if does_type goto check_string
269 does_type = does right, 'Float'
270 if does_type goto check_float
272 does_type = does right, 'Integer'
273 if does_type goto check_integer
276 pass = iseq left, right
294 test.'ok'( pass, description )
297 .local string diagnostic
298 .local string l_string
299 .local string r_string
304 diagnostic = _make_diagnostic( l_string, r_string )
305 test.'diag'( diagnostic )
309 =item C<isnt( left, right, description )>
311 Like C<is>, but succeeds if the arguments I<don't> match.
315 .sub isnt :multi(Integer, Integer)
318 .param pmc description :optional
319 .param int have_desc :opt_flag
322 get_hll_global test, [ 'Test'; 'More' ], '_test'
327 if left != right goto pass_it
334 test.'ok'( pass, description )
337 .local string diagnostic
338 .local string l_string
339 .local string r_string
343 r_string = 'not ' . r_string
345 diagnostic = _make_diagnostic( l_string, r_string )
346 test.'diag'( diagnostic )
350 .sub isnt :multi(Float, Float)
353 .param pmc description :optional
354 .param int have_desc :opt_flag
357 get_hll_global test, [ 'Test'; 'More' ], '_test'
362 ne left, right, pass_it
369 test.'ok'( pass, description )
372 .local string diagnostic
373 .local string l_string
374 .local string r_string
378 r_string = 'not ' . r_string
380 diagnostic = _make_diagnostic( l_string, r_string )
381 test.'diag'( diagnostic )
385 .sub isnt :multi(String, String)
388 .param pmc description :optional
389 .param int have_desc :opt_flag
392 get_hll_global test, [ 'Test'; 'More' ], '_test'
397 ne left, right, pass_it
404 test.'ok'( pass, description )
407 .local string diagnostic
408 .local string l_string
409 .local string r_string
413 r_string = 'not ' . r_string
415 diagnostic = _make_diagnostic( l_string, r_string )
416 test.'diag'( diagnostic )
420 .sub isnt :multi(PMC, PMC)
423 .param pmc description :optional
424 .param int have_desc :opt_flag
427 get_hll_global test, [ 'Test'; 'More' ], '_test'
429 # this comparison may not work in general, but it's worth trying
431 pass = isne left, right
434 test.'ok'( pass, description )
437 .local string diagnostic
438 .local string l_string
439 .local string r_string
443 r_string = 'not ' . r_string
445 diagnostic = _make_diagnostic( l_string, r_string )
446 test.'diag'( diagnostic )
450 =item C<diag( diagnostic )>
452 Prints C<diagnostic> to the screen, without affecting test comparisons.
457 .param string diagnostic
460 get_hll_global test, [ 'Test'; 'More' ], '_test'
461 test.'diag'( diagnostic )
465 =item C<is_deeply( left, right, description )>
467 Compares the data structures passed as C<left> and C<right>. If data
468 structures are passed, C<is_deeply> does a deep comparison by walking each
469 structure. It passes if they are equal and fails otherwise. This will
470 report the results with the optional test description in C<description>.
472 This handles comparisons of array-like and hash-like structures.
476 .sub is_deeply :multi(PMC, PMC)
479 .param pmc description :optional
480 .param int have_desc :opt_flag
483 .local string diagnosis
486 position = new 'ResizablePMCArray'
489 get_hll_global test, [ 'Test'; 'More' ], '_test'
492 does_flag = does left, 'array'
493 if does_flag goto compare_array
495 does_flag = does left, 'hash'
496 if does_flag goto compare_hash
498 diagnosis = typeof left
499 diagnosis .= ' is not a nested data structure'
504 ( result, diagnosis ) = compare_array( left, right, position )
508 (result, diagnosis ) = compare_hash( left, right, position )
512 test.'ok'( result, description )
514 unless result goto report_diagnostic
518 ne diagnosis, '', return_it
520 .local string left_value
521 .local string right_value
522 right_value = pop position
523 left_value = pop position
525 .local string nested_path
526 nested_path = join '][', position
528 diagnosis = 'Mismatch'
529 unless nested_path goto show_expected
532 diagnosis .= nested_path
536 diagnosis .= ': expected '
537 diagnosis .= left_value
538 diagnosis .= ', received '
539 diagnosis .= right_value
542 test.'diag'( diagnosis )
552 get_hll_global test, [ 'Test'; 'More' ], '_test'
558 if l_count == r_count goto compare_contents
560 .local string l_count_string
561 .local string r_count_string
562 l_count_string = l_count
563 l_count_string .= ' element'
565 if l_count == 1 goto pluralization_done
566 l_count_string .= 's'
569 r_count_string = r_count
571 push position, l_count_string
572 push position, r_count_string
581 l_iter = iter l_array
582 r_iter = iter r_array
589 .local int elems_equal
592 unless l_iter goto iter_end
593 l_elem = shift l_iter
594 r_elem = shift r_iter
597 elems_equal = compare_elements( l_elem, r_elem, position )
598 unless elems_equal goto elems_not_equal
604 unshift position, count
617 get_hll_global test, [ 'Test'; 'More' ], '_test'
623 if l_count == r_count goto compare_contents
625 .local string l_count_string
626 .local string r_count_string
627 l_count_string = l_count
628 l_count_string .= ' element'
630 if l_count == 1 goto pluralization_done
631 l_count_string .= 's'
634 r_count_string = r_count
636 push position, l_count_string
637 push position, r_count_string
652 .local int elems_equal
655 unless l_iter goto iter_end
657 l_elem = l_hash[ key ]
658 r_elem = r_hash[ key ]
660 elems_equal = compare_elements( l_elem, r_elem, position )
661 unless elems_equal goto elems_not_equal
667 unshift position, key
674 .sub compare_elements :multi(String, String, PMC)
681 eq left, right, are_equal
692 .sub compare_elements :multi(Integer, Integer, PMC)
698 eq left, right, are_equal
709 .sub compare_elements :multi(String, String, PMC)
714 eq left, right, are_equal
725 .sub compare_elements :multi(Integer, Integer, PMC)
731 eq left, right, are_equal
742 .sub compare_elements :multi(Array, Array, PMC)
748 equal = compare_array( left, right, position )
752 .sub compare_elements :multi(Hash, Hash, PMC)
758 equal = compare_hash( left, right, position )
762 .sub compare_elements :multi(Undef, Undef, PMC)
770 .sub compare_elements :multi(Undef, PMC, PMC)
775 .local string l_undef
777 push position, l_undef
782 .sub compare_elements :multi(PMC, Undef, PMC)
787 .local string r_undef
790 push position, r_undef
794 .sub compare_elements :multi(PMC, PMC, PMC)
803 does_flag = does left, 'array'
804 unless does_flag goto check_hash
805 equal = compare_array( left, right, position )
809 does_flag = does left, 'hash'
810 if does_flag goto compare_hash
814 equal = compare_hash( left, right, position )
818 =item C<throws_like( codestring, pattern, description )>
820 Takes PIR code in C<codestring> and a PGE pattern to match in C<pattern>, as
821 well as an optional message in C<description>. Passes a test if the PIR throws
822 an exception that matches the pattern, fails the test otherwise.
828 .param string pattern
829 .param string description :optional
832 get_hll_global test, [ 'Test'; 'More' ], '_test'
837 compiler = compreg 'PIR'
840 eh = new 'ExceptionHandler'
841 set_addr eh, handler # set handler label for exceptions
844 compfun = compiler(target)
845 compfun() # eval the target code
849 # if it doesn't throw an exception, fail
850 test.'ok'( 0, description )
851 test.'diag'( 'no error thrown' )
857 .local string error_msg
861 like(error_msg, pattern, description)
866 =item C<like( target, pattern, description )>
868 Similar to is, but using the Parrot Grammar Engine to compare the string
869 passed as C<target> to the pattern passed as C<pattern>. It passes if the
870 pattern matches and fails otherwise. This will report the results with the
871 optional test description in C<description>.
877 .param string pattern
878 .param string description :optional
881 get_hll_global test, [ 'Test'; 'More' ], '_test'
883 .local pmc p6rule_compile
884 load_bytecode "PGE.pbc"
885 load_bytecode "PGE/Dumper.pbc"
886 load_bytecode "PGE/Text.pbc"
887 load_bytecode "PGE/Util.pbc"
888 p6rule_compile = compreg "PGE::Perl6Regex"
890 .local string diagnostic
899 (rulesub, code, exp) = p6rule_compile(pattern)
900 if_null rulesub, rule_fail
901 match = rulesub(target)
902 unless match goto match_fail
906 diagnostic = "match failed: target '"
908 diagnostic .= "' does not match pattern '"
909 diagnostic .= pattern
913 diagnostic = "rule error"
920 test.'ok'( pass, description )
923 test.'diag'( diagnostic )
927 =item C<skip( how_many, why )>
929 Pass a number of tests, but with a comment that marks the test was
930 actually skipped. Arguments are optional.
934 .sub skip :multi(Integer, String)
936 .param string description
939 get_hll_global test, [ 'Test'; 'More' ], '_test'
940 test.'skip'(how_many, description)
943 .sub skip :multi(Integer)
947 get_hll_global test, [ 'Test'; 'More' ], '_test'
948 test.'skip'(how_many)
951 .sub skip :multi(String)
952 .param string description
955 get_hll_global test, [ 'Test'; 'More' ], '_test'
956 test.'skip'(1, description)
961 get_hll_global test, [ 'Test'; 'More' ], '_test'
965 =item C<todo( passed, description, reason )>
967 Records a test as pass or fail (like C<ok>, but marks it as TODO so it always
968 appears as a success. This also records the optional C<description> of the test
969 and the C<reason> you have marked it as TODO.
974 .param pmc args :slurpy
977 get_hll_global test, [ 'Test'; 'More' ], '_test'
979 test.'todo'( args :flat )
982 =item C<isa_ok( object, class_name, object_name )>
984 Pass if the object C<isa> class of the given class name. The object
985 name passed in is not a full description, but a name to be included in
986 the description. The description is presented as "<object_name> isa
989 Good input: "C<new MyObject>", "C<return from bar()>"
991 Bad input: "C<test that the return from Foo is correct type>"
997 .param pmc class_name
998 .param pmc object_name :optional
999 .param int got_name :opt_flag
1002 get_hll_global test, [ 'Test'; 'More' ], '_test'
1004 .local string description, diagnostic
1005 description = "The object"
1006 unless got_name goto keep_default
1007 description = object_name
1009 diagnostic = description
1010 description .= " isa "
1014 $I0 = isa thingy, class_name
1015 test.'ok'($I0, description)
1017 diagnostic .= " isn't a "
1020 diagnostic .= " it's a "
1023 test.'diag'(diagnostic)
1027 .sub _make_diagnostic
1028 .param string received
1029 .param string expected
1030 .local string diagnostic
1032 diagnostic = 'Have: '
1033 diagnostic .= received
1034 diagnostic .= "\nWant: "
1035 diagnostic .= expected
1037 .return( diagnostic )
1044 Written and maintained by chromatic, C<< chromatic at wgz dot org >>, based on
1045 the Perl 6 port he wrote, based on the original Perl 5 version he wrote with
1046 ideas from Michael G. Schwern. Please send patches, feedback, and suggestions
1047 to the Perl 6 internals mailing list.
1051 Copyright (C) 2005-2009, Parrot Foundation.
1059 # vim: expandtab shiftwidth=4 ft=pir: