]> git.vpit.fr Git - perl/modules/Test-Leaner.git/blob - lib/Test/Leaner.pm
8e45f3c8bfe9d4a24266ffeea5b68c78d5263eac
[perl/modules/Test-Leaner.git] / lib / Test / Leaner.pm
1 package Test::Leaner;
2
3 use 5.006;
4 use strict;
5 use warnings;
6
7 =head1 NAME
8
9 Test::Leaner - A slimmer Test::More for when you favor performance over completeness.
10
11 =head1 VERSION
12
13 Version 0.02
14
15 =cut
16
17 our $VERSION = '0.02';
18
19 =head1 SYNOPSIS
20
21     use Test::Leaner tests => 10_000;
22     for (1 .. 10_000) {
23      ...
24      is $one, 1, "checking situation $_";
25     }
26
27
28 =head1 DESCRIPTION
29
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>.
31
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 :
34
35 =over 4
36
37 =item *
38
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.
41
42 =item *
43
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.
45
46 =item *
47
48 C<isn't> (the sub C<t> in package C<isn>) is not aliased to L</isnt>.
49
50 =item *
51
52 L</like> and L</unlike> don't special case regular expressions that are passed as C<'/.../'> strings.
53 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>).
54
55 =item *
56
57 L</cmp_ok> throws an exception if the given operator isn't a valid Perl binary operator (except C<'='> and variants).
58 It also tests in scalar context, so C<'..'> will be treated as the flip-flop operator and not the range operator.
59
60 =item *
61
62 L</is_deeply> doesn't guard for memory cycles.
63 If the two first arguments present parallel memory cycles, the test may result in an infinite loop.
64
65 =item *
66
67 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.
68 Moreover, this allows a much faster variant of L</is_deeply>.
69
70 =item *
71
72 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.
73
74 =back
75
76 =cut
77
78 use Exporter ();
79
80 my $main_process;
81
82 BEGIN {
83  $main_process = $$;
84
85  if ($] >= 5.008 and $INC{'threads.pm'}) {
86   my $use_ithreads = do {
87    require Config;
88    no warnings 'once';
89    $Config::Config{useithreads};
90   };
91   if ($use_ithreads) {
92    require threads::shared;
93    *THREADSAFE = sub () { 1 };
94   }
95  }
96  unless (defined &Test::Leaner::THREADSAFE) {
97   *THREADSAFE = sub () { 0 }
98  }
99 }
100
101 my ($TAP_STREAM, $DIAG_STREAM);
102
103 my ($plan, $test, $failed, $no_diag, $done_testing);
104
105 our @EXPORT = qw<
106  plan
107  skip
108  done_testing
109  pass
110  fail
111  ok
112  is
113  isnt
114  like
115  unlike
116  cmp_ok
117  is_deeply
118  diag
119  note
120  BAIL_OUT
121 >;
122
123 =head1 ENVIRONMENT
124
125 =head2 C<PERL_TEST_LEANER_USES_TEST_MORE>
126
127 If this environment variable is set, L<Test::Leaner> will replace its functions by those from L<Test::More>.
128 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).
129 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.
130
131 This may be useful if your L<Test::Leaner>-based test script fails and you want extra diagnostics.
132
133 =cut
134
135 sub _handle_import_args {
136  my @imports;
137
138  my $i = 0;
139  while ($i <= $#_) {
140   my $item = $_[$i];
141   my $splice;
142   if (defined $item) {
143    if ($item eq 'import') {
144     push @imports, @{ $_[$i+1] };
145     $splice  = 2;
146    } elsif ($item eq 'no_diag') {
147     lock $plan if THREADSAFE;
148     $no_diag = 1;
149     $splice  = 1;
150    }
151   }
152   if ($splice) {
153    splice @_, $i, $splice;
154   } else {
155    ++$i;
156   }
157  }
158
159  return @imports;
160 }
161
162 if ($ENV{PERL_TEST_LEANER_USES_TEST_MORE}) {
163  require Test::More;
164
165  my $leaner_stash = \%Test::Leaner::;
166  my $more_stash   = \%Test::More::;
167
168  my %stubbed;
169
170  for (@EXPORT) {
171   my $replacement = exists $more_stash->{$_} ? *{$more_stash->{$_}}{CODE}
172                                              : undef;
173   unless (defined $replacement) {
174    $stubbed{$_}++;
175    $replacement = sub {
176     @_ = ("$_ is not implemented in this version of Test::More");
177     goto &croak;
178    };
179   }
180   no warnings 'redefine';
181   $leaner_stash->{$_} = $replacement;
182  }
183
184  my $import = sub {
185   shift;
186
187   my @imports = &_handle_import_args;
188   @imports    = @EXPORT unless @imports;
189   my @test_more_imports;
190   for (@imports) {
191    if ($stubbed{$_}) {
192     my $pkg = caller;
193     no strict 'refs';
194     *{$pkg."::$_"} = $leaner_stash->{$_};
195    } else {
196     push @test_more_imports, $_;
197    }
198   }
199
200   my $test_more_import = 'Test::More'->can('import');
201   return unless $test_more_import;
202
203   @_ = (
204    'Test::More',
205    @_,
206    import => \@test_more_imports,
207   );
208   {
209    lock $plan if THREADSAFE;
210    push @_, 'no_diag' if $no_diag;
211   }
212
213   goto $test_more_import;
214  };
215
216  no warnings 'redefine';
217  *import = $import;
218
219  return 1;
220 }
221
222 sub NO_PLAN  () { -1 }
223 sub SKIP_ALL () { -2 }
224
225 BEGIN {
226  if (THREADSAFE) {
227   threads::shared::share($_) for $plan, $test, $failed, $no_diag, $done_testing;
228  }
229
230  lock $plan if THREADSAFE;
231
232  $plan   = undef;
233  $test   = 0;
234  $failed = 0;
235 }
236
237 sub carp {
238  my $level = 1 + ($Test::Builder::Level || 0);
239  my @caller;
240  do {
241   @caller = caller $level--;
242  } while (!@caller and $level >= 0);
243  my ($file, $line) = @caller[1, 2];
244  warn @_, " at $file line $line.\n";
245 }
246
247 sub croak {
248  my $level = 1 + ($Test::Builder::Level || 0);
249  my @caller;
250  do {
251   @caller = caller $level--;
252  } while (!@caller and $level >= 0);
253  my ($file, $line) = @caller[1, 2];
254  die @_, " at $file line $line.\n";
255 }
256
257 sub _sanitize_comment {
258  $_[0] =~ s/\n+\z//;
259  $_[0] =~ s/#/\\#/g;
260  $_[0] =~ s/\n/\n# /g;
261 }
262
263 =head1 FUNCTIONS
264
265 The following functions from L<Test::More> are implemented and exported by default.
266
267 =head2 C<< plan [ tests => $count | 'no_plan' | skip_all => $reason ] >>
268
269 See L<Test::More/plan>.
270
271 =cut
272
273 sub plan {
274  my ($key, $value) = @_;
275
276  return unless $key;
277
278  lock $plan if THREADSAFE;
279
280  croak("You tried to plan twice") if defined $plan;
281
282  my $plan_str;
283
284  if ($key eq 'no_plan') {
285   croak("no_plan takes no arguments") if $value;
286   $plan       = NO_PLAN;
287  } elsif ($key eq 'tests') {
288   croak("Got an undefined number of tests") unless defined $value;
289   croak("You said to run 0 tests")          unless $value;
290   croak("Number of tests must be a positive integer.  You gave it '$value'")
291                                             unless $value =~ /^\+?[0-9]+$/;
292   $plan       = $value;
293   $plan_str   = "1..$value";
294  } elsif ($key eq 'skip_all') {
295   $plan       = SKIP_ALL;
296   $plan_str   = '1..0 # SKIP';
297   if (defined $value) {
298    _sanitize_comment($value);
299    $plan_str .= " $value" if length $value;
300   }
301  } else {
302   my @args = grep defined, $key, $value;
303   croak("plan() doesn't understand @args");
304  }
305
306  if (defined $plan_str) {
307   local $\;
308   print $TAP_STREAM "$plan_str\n";
309  }
310
311  exit 0 if $plan == SKIP_ALL;
312
313  return 1;
314 }
315
316 sub import {
317  my $class = shift;
318
319  my @imports = &_handle_import_args;
320
321  if (@_) {
322   local $Test::Builder::Level = ($Test::Builder::Level || 0) + 1;
323   &plan;
324  }
325
326  @_ = ($class, @imports);
327  goto &Exporter::import;
328 }
329
330 =head2 C<< skip $reason => $count >>
331
332 See L<Test::More/skip>.
333
334 =cut
335
336 sub skip {
337  my ($reason, $count) = @_;
338
339  lock $plan if THREADSAFE;
340
341  if (not defined $count) {
342   carp("skip() needs to know \$how_many tests are in the block")
343                                       unless defined $plan and $plan == NO_PLAN;
344   $count = 1;
345  } elsif ($count =~ /[^0-9]/) {
346   carp('skip() was passed a non-numeric number of tests.  Did you get the arguments backwards?');
347   $count = 1;
348  }
349
350  for (1 .. $count) {
351   ++$test;
352
353   my $skip_str = "ok $test # skip";
354   if (defined $reason) {
355    _sanitize_comment($reason);
356    $skip_str  .= " $reason" if length $reason;
357   }
358
359   local $\;
360   print $TAP_STREAM "$skip_str\n";
361  }
362
363  no warnings 'exiting';
364  last SKIP;
365 }
366
367 =head2 C<done_testing [ $count ]>
368
369 See L<Test::More/done_testing>.
370
371 =cut
372
373 sub done_testing {
374  my ($count) = @_;
375
376  lock $plan if THREADSAFE;
377
378  $count = $test unless defined $count;
379  croak("Number of tests must be a positive integer.  You gave it '$count'")
380                                                  unless $count =~ /^\+?[0-9]+$/;
381
382  if (not defined $plan or $plan == NO_PLAN) {
383   $plan         = $count; # $plan can't be NO_PLAN anymore
384   $done_testing = 1;
385   local $\;
386   print $TAP_STREAM "1..$plan\n";
387  } else {
388   if ($done_testing) {
389    @_ = ('done_testing() was already called');
390    goto &fail;
391   } elsif ($plan != $count) {
392    @_ = ("planned to run $plan tests but done_testing() expects $count");
393    goto &fail;
394   }
395  }
396
397  return 1;
398 }
399
400 =head2 C<ok $ok [, $desc ]>
401
402 See L<Test::More/ok>.
403
404 =cut
405
406 sub ok ($;$) {
407  my ($ok, $desc) = @_;
408
409  lock $plan if THREADSAFE;
410
411  ++$test;
412
413  my $test_str = "ok $test";
414  $ok or do {
415   $test_str   = "not $test_str";
416   ++$failed;
417  };
418  if (defined $desc) {
419   _sanitize_comment($desc);
420   $test_str .= " - $desc" if length $desc;
421  }
422
423  local $\;
424  print $TAP_STREAM "$test_str\n";
425
426  return $ok;
427 }
428
429 =head2 C<pass [ $desc ]>
430
431 See L<Test::More/pass>.
432
433 =cut
434
435 sub pass (;$) {
436  unshift @_, 1;
437  goto &ok;
438 }
439
440 =head2 C<fail [ $desc ]>
441
442 See L<Test::More/fail>.
443
444 =cut
445
446 sub fail (;$) {
447  unshift @_, 0;
448  goto &ok;
449 }
450
451 =head2 C<is $got, $expected [, $desc ]>
452
453 See L<Test::More/is>.
454
455 =cut
456
457 sub is ($$;$) {
458  my ($got, $expected, $desc) = @_;
459  no warnings 'uninitialized';
460  @_ = (
461   (not(defined $got xor defined $expected) and $got eq $expected),
462   $desc,
463  );
464  goto &ok;
465 }
466
467 =head2 C<isnt $got, $expected [, $desc ]>
468
469 See L<Test::More/isnt>.
470
471 =cut
472
473 sub isnt ($$;$) {
474  my ($got, $expected, $desc) = @_;
475  no warnings 'uninitialized';
476  @_ = (
477   ((defined $got xor defined $expected) or $got ne $expected),
478   $desc,
479  );
480  goto &ok;
481 }
482
483 my %binops = (
484  'or'  => 'or',
485  'xor' => 'xor',
486  'and' => 'and',
487
488  '||'  => 'hor',
489  ('//' => 'dor') x ($] >= 5.010),
490  '&&'  => 'hand',
491
492  '|'   => 'bor',
493  '^'   => 'bxor',
494  '&'   => 'band',
495
496  'lt'  => 'lt',
497  'le'  => 'le',
498  'gt'  => 'gt',
499  'ge'  => 'ge',
500  'eq'  => 'eq',
501  'ne'  => 'ne',
502  'cmp' => 'cmp',
503
504  '<'   => 'nlt',
505  '<='  => 'nle',
506  '>'   => 'ngt',
507  '>='  => 'nge',
508  '=='  => 'neq',
509  '!='  => 'nne',
510  '<=>' => 'ncmp',
511
512  '=~'  => 'like',
513  '!~'  => 'unlike',
514  ('~~' => 'smartmatch') x ($] >= 5.010),
515
516  '+'   => 'add',
517  '-'   => 'substract',
518  '*'   => 'multiply',
519  '/'   => 'divide',
520  '%'   => 'modulo',
521  '<<'  => 'lshift',
522  '>>'  => 'rshift',
523
524  '.'   => 'concat',
525  '..'  => 'flipflop',
526  '...' => 'altflipflop',
527  ','   => 'comma',
528  '=>'  => 'fatcomma',
529 );
530
531 my %binop_handlers;
532
533 sub _create_binop_handler {
534  my ($op) = @_;
535  my $name = $binops{$op};
536  croak("Operator $op not supported") unless defined $name;
537  {
538   local $@;
539   eval <<"IS_BINOP";
540 sub is_$name (\$\$;\$) {
541  my (\$got, \$expected, \$desc) = \@_;
542  \@_ = (scalar(\$got $op \$expected), \$desc);
543  goto &ok;
544 }
545 IS_BINOP
546   die $@ if $@;
547  }
548  $binop_handlers{$op} = do {
549   no strict 'refs';
550   \&{__PACKAGE__."::is_$name"};
551  }
552 }
553
554 =head2 C<like $got, $regexp_expected [, $desc ]>
555
556 See L<Test::More/like>.
557
558 =head2 C<unlike $got, $regexp_expected, [, $desc ]>
559
560 See L<Test::More/unlike>.
561
562 =cut
563
564 {
565  no warnings 'once';
566  *like   = _create_binop_handler('=~');
567  *unlike = _create_binop_handler('!~');
568 }
569
570 =head2 C<cmp_ok $got, $op, $expected [, $desc ]>
571
572 See L<Test::More/cmp_ok>.
573
574 =cut
575
576 sub cmp_ok ($$$;$) {
577  my ($got, $op, $expected, $desc) = @_;
578  my $handler = $binop_handlers{$op};
579  unless ($handler) {
580   local $Test::More::Level = ($Test::More::Level || 0) + 1;
581   $handler = _create_binop_handler($op);
582  }
583  @_ = ($got, $expected, $desc);
584  goto $handler;
585 }
586
587 =head2 C<is_deeply $got, $expected [, $desc ]>
588
589 See L<Test::More/is_deeply>.
590
591 =cut
592
593 BEGIN {
594  local $@;
595  if (eval { require Scalar::Util; 1 }) {
596   *_reftype = \&Scalar::Util::reftype;
597  } else {
598   # Stolen from Scalar::Util::PP
599   require B;
600   my %tmap = qw<
601    B::NULL   SCALAR
602
603    B::HV     HASH
604    B::AV     ARRAY
605    B::CV     CODE
606    B::IO     IO
607    B::GV     GLOB
608    B::REGEXP REGEXP
609   >;
610   *_reftype = sub ($) {
611    my $r = shift;
612
613    return undef unless length ref $r;
614
615    my $t = ref B::svref_2object($r);
616
617    return exists $tmap{$t} ? $tmap{$t}
618                            : length ref $$r ? 'REF'
619                                             : 'SCALAR'
620   }
621  }
622 }
623
624 sub _deep_ref_check {
625  my ($x, $y, $ry) = @_;
626
627  no warnings qw<numeric uninitialized>;
628
629  if ($ry eq 'ARRAY') {
630   return 0 unless $#$x == $#$y;
631
632   my ($ex, $ey);
633   for (0 .. $#$y) {
634    $ex = $x->[$_];
635    $ey = $y->[$_];
636
637    # Inline the beginning of _deep_check
638    return 0 if defined $ex xor defined $ey;
639
640    next if not(ref $ex xor ref $ey) and $ex eq $ey;
641
642    $ry = _reftype($ey);
643    return 0 if _reftype($ex) ne $ry;
644
645    return 0 unless $ry and _deep_ref_check($ex, $ey, $ry);
646   }
647
648   return 1;
649  } elsif ($ry eq 'HASH') {
650   return 0 unless keys(%$x) == keys(%$y);
651
652   my ($ex, $ey);
653   for (keys %$y) {
654    return 0 unless exists $x->{$_};
655    $ex = $x->{$_};
656    $ey = $y->{$_};
657
658    # Inline the beginning of _deep_check
659    return 0 if defined $ex xor defined $ey;
660
661    next if not(ref $ex xor ref $ey) and $ex eq $ey;
662
663    $ry = _reftype($ey);
664    return 0 if _reftype($ex) ne $ry;
665
666    return 0 unless $ry and _deep_ref_check($ex, $ey, $ry);
667   }
668
669   return 1;
670  } elsif ($ry eq 'SCALAR' or $ry eq 'REF') {
671   return _deep_check($$x, $$y);
672  }
673
674  return 0;
675 }
676
677 sub _deep_check {
678  my ($x, $y) = @_;
679
680  no warnings qw<numeric uninitialized>;
681
682  return 0 if defined $x xor defined $y;
683
684  # Try object identity/eq overloading first. It also covers the case where
685  # $x and $y are both undefined.
686  # If either $x or $y is overloaded but none has eq overloading, the test will
687  # break at that point.
688  return 1 if not(ref $x xor ref $y) and $x eq $y;
689
690  # Test::More::is_deeply happily breaks encapsulation if the objects aren't
691  # overloaded.
692  my $ry = _reftype($y);
693  return 0 if _reftype($x) ne $ry;
694
695  # Shortcut if $x and $y are both not references and failed the previous
696  # $x eq $y test.
697  return 0 unless $ry;
698
699  # We know that $x and $y are both references of type $ry, without overloading.
700  _deep_ref_check($x, $y, $ry);
701 }
702
703 sub is_deeply {
704  @_ = (
705   &_deep_check,
706   $_[2],
707  );
708  goto &ok;
709 }
710
711 sub _diag_fh {
712  my $fh = shift;
713
714  return unless @_;
715
716  lock $plan if THREADSAFE;
717  return if $no_diag;
718
719  my $msg = join '', map { defined($_) ? $_ : 'undef' } @_;
720  _sanitize_comment($msg);
721  return unless length $msg;
722
723  local $\;
724  print $fh "# $msg\n";
725
726  return 0;
727 };
728
729 =head2 C<diag @text>
730
731 See L<Test::More/diag>.
732
733 =cut
734
735 sub diag {
736  unshift @_, $DIAG_STREAM;
737  goto &_diag_fh;
738 }
739
740 =head2 C<note @text>
741
742 See L<Test::More/note>.
743
744 =cut
745
746 sub note {
747  unshift @_, $TAP_STREAM;
748  goto &_diag_fh;
749 }
750
751 =head2 C<BAIL_OUT [ $desc ]>
752
753 See L<Test::More/BAIL_OUT>.
754
755 =cut
756
757 sub BAIL_OUT {
758  my ($desc) = @_;
759
760  lock $plan if THREADSAFE;
761
762  my $bail_out_str = 'Bail out!';
763  if (defined $desc) {
764   _sanitize_comment($desc);
765   $bail_out_str  .= "  $desc" if length $desc; # Two spaces
766  }
767
768  local $\;
769  print $TAP_STREAM "$bail_out_str\n";
770
771  exit 255;
772 }
773
774 END {
775  if ($main_process == $$ and not $?) {
776   lock $plan if THREADSAFE;
777
778   if (defined $plan) {
779    if ($failed) {
780     $? = $failed <= 254 ? $failed : 254;
781    } elsif ($plan >= 0) {
782     $? = $test == $plan ? 0 : 255;
783    }
784    if ($plan == NO_PLAN) {
785     local $\;
786     print $TAP_STREAM "1..$test\n";
787    }
788   }
789  }
790 }
791
792 =pod
793
794 L<Test::Leaner> also provides some functions of its own, which are never exported.
795
796 =head2 C<tap_stream [ $fh ]>
797
798 Read/write accessor for the filehandle to which the tests are outputted.
799 On write, it also turns autoflush on onto C<$fh>.
800
801 Note that it can only be used as a write accessor before you start any thread, as L<threads::shared> cannot reliably share filehandles.
802
803 Defaults to C<STDOUT>.
804
805 =cut
806
807 sub tap_stream (;*) {
808  if (@_) {
809   $TAP_STREAM = $_[0];
810
811   my $fh = select $TAP_STREAM;
812   $|++;
813   select $fh;
814  }
815
816  return $TAP_STREAM;
817 }
818
819 tap_stream *STDOUT;
820
821 =head2 C<diag_stream [ $fh ]>
822
823 Read/write accessor for the filehandle to which the diagnostics are printed.
824 On write, it also turns autoflush on onto C<$fh>.
825
826 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.
827
828 Defaults to C<STDERR>.
829
830 =cut
831
832 sub diag_stream (;*) {
833  if (@_) {
834   $DIAG_STREAM = $_[0];
835
836   my $fh = select $DIAG_STREAM;
837   $|++;
838   select $fh;
839  }
840
841  return $DIAG_STREAM;
842 }
843
844 diag_stream *STDERR;
845
846 =head2 C<THREADSAFE>
847
848 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>.
849 In that case, it also needs a working L<threads::shared>.
850
851 =head1 DEPENDENCIES
852
853 L<perl> 5.6.
854
855 L<Exporter>, L<Test::More>.
856
857 =head1 AUTHOR
858
859 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
860
861 You can contact me by mail or on C<irc.perl.org> (vincent).
862
863 =head1 BUGS
864
865 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>.
866 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
867
868 =head1 SUPPORT
869
870 You can find documentation for this module with the perldoc command.
871
872     perldoc Test::Leaner
873
874 =head1 COPYRIGHT & LICENSE
875
876 Copyright 2010,2011 Vincent Pit, all rights reserved.
877
878 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
879
880 =cut
881
882 1; # End of Test::Leaner