9 Test::Leaner - A slimmer Test::More for when you favor performance over completeness.
17 our $VERSION = '0.02';
21 use Test::Leaner tests => 10_000;
24 is $one, 1, "checking situation $_";
30 When profiling some L<Test::More>-based test script that contained about 10 000 unit tests, I realized that 60% of the time was spent in L<Test::Builder> itself, even though every single test actually involved a costly C<eval STRING>.
32 This module aims to be a partial replacement to L<Test::More> in those situations where you want to run a large number of simple tests.
33 Its functions behave the same as their L<Test::More> counterparts, except for the following differences :
39 Stringification isn't forced on the test operands.
40 However, L</ok> honors C<'bool'> overloading, L</is> and L</is_deeply> honor C<'eq'> overloading (and just that one), L</isnt> honors C<'ne'> overloading, and L</cmp_ok> honors whichever overloading category corresponds to the specified operator.
44 L</pass>, L</fail>, L</ok>, L</is>, L</isnt>, L</like>, L</unlike>, L</cmp_ok> and L</is_deeply> are all guaranteed to return the truth value of the test.
48 L</like> and L</unlike> don't special case regular expressions that are passed as C<'/.../'> strings.
49 A string regexp argument is always treated as the source of the regexp, making C<like $text, $rx> and C<like $text, qr[$rx]> equivalent to each other and to C<cmp_ok $text, '=~', $rx> (and likewise for C<unlike>).
53 L</cmp_ok> throws an exception if the given operator isn't a valid Perl binary operator (except C<'='> and variants).
54 It also tests in scalar context, so C<'..'> will be treated as the flip-flop operator and not the range operator.
58 L</is_deeply> doesn't guard for memory cycles.
59 If the two first arguments present parallel memory cycles, the test may result in an infinite loop.
63 The tests don't output any kind of default diagnostic in case of failure ; the rationale being that if you have a large number of tests and a lot of them are failing, then you don't want to be flooded by diagnostics.
64 Moreover, this allows a much faster variant of L</is_deeply>.
68 C<use_ok>, C<require_ok>, C<can_ok>, C<isa_ok>, C<new_ok>, C<subtest>, C<explain>, C<TODO> blocks and C<todo_skip> are not implemented.
81 if ($] >= 5.008 and $INC{'threads.pm'}) {
82 my $use_ithreads = do {
85 $Config::Config{useithreads};
88 require threads::shared;
89 *THREADSAFE = sub () { 1 };
92 unless (defined &Test::Leaner::THREADSAFE) {
93 *THREADSAFE = sub () { 0 }
97 my ($TAP_STREAM, $DIAG_STREAM);
99 my ($plan, $test, $failed, $no_diag, $done_testing);
121 =head2 C<PERL_TEST_LEANER_USES_TEST_MORE>
123 If this environment variable is set, L<Test::Leaner> will replace its functions by those from L<Test::More>.
124 Moreover, the symbols that are imported when you C<use Test::Leaner> will be those from L<Test::More>, but you can still only import the symbols originally defined in L<Test::Leaner> (hence the functions from L<Test::More> that are not implemented in L<Test::Leaner> will not be imported).
125 If your version of L<Test::More> is too old and doesn't have some symbols (like L</note> or L</done_testing>), they will be replaced in L<Test::Leaner> by croaking stubs.
127 This may be useful if your L<Test::Leaner>-based test script fails and you want extra diagnostics.
131 sub _handle_import_args {
139 if ($item eq 'import') {
140 push @imports, @{ $_[$i+1] };
142 } elsif ($item eq 'no_diag') {
143 lock $plan if THREADSAFE;
149 splice @_, $i, $splice;
158 if ($ENV{PERL_TEST_LEANER_USES_TEST_MORE}) {
161 my $leaner_stash = \%Test::Leaner::;
162 my $more_stash = \%Test::More::;
167 my $replacement = exists $more_stash->{$_} ? *{$more_stash->{$_}}{CODE}
169 if (defined $replacement) {
170 $valid_imports{$_} = 1;
173 @_ = ("$_ is not implemented in this version of Test::More");
177 no warnings 'redefine';
178 $leaner_stash->{$_} = $replacement;
184 my @imports = &_handle_import_args;
185 @imports = @EXPORT unless @imports;
186 my @test_more_imports;
188 if ($valid_imports{$_}) {
189 push @test_more_imports, $_;
193 *{$pkg."::$_"} = $leaner_stash->{$_};
197 my $test_more_import = 'Test::More'->can('import');
198 return unless $test_more_import;
203 import => \@test_more_imports,
206 lock $plan if THREADSAFE;
207 push @_, 'no_diag' if $no_diag;
210 goto $test_more_import;
213 no warnings 'redefine';
219 sub NO_PLAN () { -1 }
220 sub SKIP_ALL () { -2 }
224 threads::shared::share($_) for $plan, $test, $failed, $no_diag, $done_testing;
227 lock $plan if THREADSAFE;
235 my $level = 1 + ($Test::Builder::Level || 0);
238 @caller = caller $level--;
239 } while (!@caller and $level >= 0);
240 my ($file, $line) = @caller[1, 2];
241 warn @_, " at $file line $line.\n";
245 my $level = 1 + ($Test::Builder::Level || 0);
248 @caller = caller $level--;
249 } while (!@caller and $level >= 0);
250 my ($file, $line) = @caller[1, 2];
251 die @_, " at $file line $line.\n";
254 sub _sanitize_comment {
257 $_[0] =~ s/\n/\n# /g;
262 The following functions from L<Test::More> are implemented and exported by default.
264 =head2 C<< plan [ tests => $count | 'no_plan' | skip_all => $reason ] >>
266 See L<Test::More/plan>.
271 my ($key, $value) = @_;
275 lock $plan if THREADSAFE;
277 croak("You tried to plan twice") if defined $plan;
281 if ($key eq 'no_plan') {
282 croak("no_plan takes no arguments") if $value;
284 } elsif ($key eq 'tests') {
285 croak("Got an undefined number of tests") unless defined $value;
286 croak("You said to run 0 tests") unless $value;
287 croak("Number of tests must be a positive integer. You gave it '$value'")
288 unless $value =~ /^\+?[0-9]+$/;
290 $plan_str = "1..$value";
291 } elsif ($key eq 'skip_all') {
293 $plan_str = '1..0 # SKIP';
294 if (defined $value) {
295 _sanitize_comment($value);
296 $plan_str .= " $value" if length $value;
299 my @args = grep defined, $key, $value;
300 croak("plan() doesn't understand @args");
303 if (defined $plan_str) {
305 print $TAP_STREAM "$plan_str\n";
308 exit 0 if $plan == SKIP_ALL;
316 my @imports = &_handle_import_args;
319 local $Test::Builder::Level = ($Test::Builder::Level || 0) + 1;
323 @_ = ($class, @imports);
324 goto &Exporter::import;
327 =head2 C<< skip $reason => $count >>
329 See L<Test::More/skip>.
334 my ($reason, $count) = @_;
336 lock $plan if THREADSAFE;
338 if (not defined $count) {
339 carp("skip() needs to know \$how_many tests are in the block")
340 unless defined $plan and $plan == NO_PLAN;
342 } elsif ($count =~ /[^0-9]/) {
343 carp('skip() was passed a non-numeric number of tests. Did you get the arguments backwards?');
350 my $skip_str = "ok $test # skip";
351 if (defined $reason) {
352 _sanitize_comment($reason);
353 $skip_str .= " $reason" if length $reason;
357 print $TAP_STREAM "$skip_str\n";
360 no warnings 'exiting';
364 =head2 C<done_testing [ $count ]>
366 See L<Test::More/done_testing>.
373 lock $plan if THREADSAFE;
375 $count = $test unless defined $count;
376 croak("Number of tests must be a positive integer. You gave it '$count'")
377 unless $count =~ /^\+?[0-9]+$/;
379 if (not defined $plan or $plan == NO_PLAN) {
380 $plan = $count; # $plan can't be NO_PLAN anymore
383 print $TAP_STREAM "1..$plan\n";
386 @_ = ('done_testing() was already called');
388 } elsif ($plan != $count) {
389 @_ = ("planned to run $plan tests but done_testing() expects $count");
397 =head2 C<ok $ok [, $desc ]>
399 See L<Test::More/ok>.
404 my ($ok, $desc) = @_;
406 lock $plan if THREADSAFE;
410 my $test_str = "ok $test";
412 $test_str = "not $test_str";
416 _sanitize_comment($desc);
417 $test_str .= " - $desc" if length $desc;
421 print $TAP_STREAM "$test_str\n";
426 =head2 C<pass [ $desc ]>
428 See L<Test::More/pass>.
437 =head2 C<fail [ $desc ]>
439 See L<Test::More/fail>.
448 =head2 C<is $got, $expected [, $desc ]>
450 See L<Test::More/is>.
455 my ($got, $expected, $desc) = @_;
456 no warnings 'uninitialized';
458 (not(defined $got xor defined $expected) and $got eq $expected),
464 =head2 C<isnt $got, $expected [, $desc ]>
466 See L<Test::More/isnt>.
471 my ($got, $expected, $desc) = @_;
472 no warnings 'uninitialized';
474 ((defined $got xor defined $expected) or $got ne $expected),
486 ('//' => 'dor') x ($] >= 5.010),
511 ('~~' => 'smartmatch') x ($] >= 5.010),
523 '...' => 'altflipflop',
530 sub _create_binop_handler {
532 my $name = $binops{$op};
533 croak("Operator $op not supported") unless defined $name;
537 sub is_$name (\$\$;\$) {
538 my (\$got, \$expected, \$desc) = \@_;
539 \@_ = (scalar(\$got $op \$expected), \$desc);
545 $binop_handlers{$op} = do {
547 \&{__PACKAGE__."::is_$name"};
551 =head2 C<like $got, $regexp_expected [, $desc ]>
553 See L<Test::More/like>.
555 =head2 C<unlike $got, $regexp_expected, [, $desc ]>
557 See L<Test::More/unlike>.
563 *like = _create_binop_handler('=~');
564 *unlike = _create_binop_handler('!~');
567 =head2 C<cmp_ok $got, $op, $expected [, $desc ]>
569 See L<Test::More/cmp_ok>.
574 my ($got, $op, $expected, $desc) = @_;
575 my $handler = $binop_handlers{$op};
577 local $Test::More::Level = ($Test::More::Level || 0) + 1;
578 $handler = _create_binop_handler($op);
580 @_ = ($got, $expected, $desc);
584 =head2 C<is_deeply $got, $expected [, $desc ]>
586 See L<Test::More/is_deeply>.
592 if (eval { require Scalar::Util; 1 }) {
593 *_reftype = \&Scalar::Util::reftype;
595 # Stolen from Scalar::Util::PP
607 *_reftype = sub ($) {
610 return undef unless length ref $r;
612 my $t = ref B::svref_2object($r);
614 return exists $tmap{$t} ? $tmap{$t}
615 : length ref $$r ? 'REF'
621 sub _deep_ref_check {
622 my ($x, $y, $ry) = @_;
624 no warnings qw<numeric uninitialized>;
626 if ($ry eq 'ARRAY') {
627 return 0 unless $#$x == $#$y;
634 # Inline the beginning of _deep_check
635 return 0 if defined $ex xor defined $ey;
637 next if not(ref $ex xor ref $ey) and $ex eq $ey;
640 return 0 if _reftype($ex) ne $ry;
642 return 0 unless $ry and _deep_ref_check($ex, $ey, $ry);
646 } elsif ($ry eq 'HASH') {
647 return 0 unless keys(%$x) == keys(%$y);
651 return 0 unless exists $x->{$_};
655 # Inline the beginning of _deep_check
656 return 0 if defined $ex xor defined $ey;
658 next if not(ref $ex xor ref $ey) and $ex eq $ey;
661 return 0 if _reftype($ex) ne $ry;
663 return 0 unless $ry and _deep_ref_check($ex, $ey, $ry);
667 } elsif ($ry eq 'SCALAR' or $ry eq 'REF') {
668 return _deep_check($$x, $$y);
677 no warnings qw<numeric uninitialized>;
679 return 0 if defined $x xor defined $y;
681 # Try object identity/eq overloading first. It also covers the case where
682 # $x and $y are both undefined.
683 # If either $x or $y is overloaded but none has eq overloading, the test will
684 # break at that point.
685 return 1 if not(ref $x xor ref $y) and $x eq $y;
687 # Test::More::is_deeply happily breaks encapsulation if the objects aren't
689 my $ry = _reftype($y);
690 return 0 if _reftype($x) ne $ry;
692 # Shortcut if $x and $y are both not references and failed the previous
696 # We know that $x and $y are both references of type $ry, without overloading.
697 _deep_ref_check($x, $y, $ry);
713 lock $plan if THREADSAFE;
716 my $msg = join '', map { defined($_) ? $_ : 'undef' } @_;
717 _sanitize_comment($msg);
718 return unless length $msg;
721 print $fh "# $msg\n";
728 See L<Test::More/diag>.
733 unshift @_, $DIAG_STREAM;
739 See L<Test::More/note>.
744 unshift @_, $TAP_STREAM;
748 =head2 C<BAIL_OUT [ $desc ]>
750 See L<Test::More/BAIL_OUT>.
757 lock $plan if THREADSAFE;
759 my $bail_out_str = 'Bail out!';
761 _sanitize_comment($desc);
762 $bail_out_str .= " $desc" if length $desc; # Two spaces
766 print $TAP_STREAM "$bail_out_str\n";
772 if ($main_process == $$ and not $?) {
773 lock $plan if THREADSAFE;
777 $? = $failed <= 254 ? $failed : 254;
778 } elsif ($plan >= 0) {
779 $? = $test == $plan ? 0 : 255;
781 if ($plan == NO_PLAN) {
783 print $TAP_STREAM "1..$test\n";
791 L<Test::Leaner> also provides some functions of its own, which are never exported.
793 =head2 C<tap_stream [ $fh ]>
795 Read/write accessor for the filehandle to which the tests are outputted.
796 On write, it also turns autoflush on onto C<$fh>.
798 Note that it can only be used as a write accessor before you start any thread, as L<threads::shared> cannot reliably share filehandles.
800 Defaults to C<STDOUT>.
804 sub tap_stream (;*) {
808 my $fh = select $TAP_STREAM;
818 =head2 C<diag_stream [ $fh ]>
820 Read/write accessor for the filehandle to which the diagnostics are printed.
821 On write, it also turns autoflush on onto C<$fh>.
823 Just like L</tap_stream>, it can only be used as a write accessor before you start any thread, as L<threads::shared> cannot reliably share filehandles.
825 Defaults to C<STDERR>.
829 sub diag_stream (;*) {
831 $DIAG_STREAM = $_[0];
833 my $fh = select $DIAG_STREAM;
845 This constant evaluates to true if and only if L<Test::Leaner> is thread-safe, i.e. when this version of C<perl> is at least 5.8, has been compiled with C<useithreads> defined, and L<threads> has been loaded B<before> L<Test::Leaner>.
846 In that case, it also needs a working L<threads::shared>.
852 L<Exporter>, L<Test::More>.
856 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
858 You can contact me by mail or on C<irc.perl.org> (vincent).
862 Please report any bugs or feature requests to C<bug-test-leaner at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-Leaner>.
863 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
867 You can find documentation for this module with the perldoc command.
871 =head1 COPYRIGHT & LICENSE
873 Copyright 2010,2011 Vincent Pit, all rights reserved.
875 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
879 1; # End of Test::Leaner