File Coverage

blib/lib/Test/Pretty.pm
Criterion Covered Total %
statement 66 180 36.6
branch 9 72 12.5
condition 4 29 13.7
subroutine 18 29 62.0
pod n/a
total 97 310 31.2


line stmt bran cond sub pod time code
1             package Test::Pretty;
2 3     3   37511 use strict;
  3         8  
  3         78  
3 3     3   14 use warnings;
  3         4  
  3         65  
4 3     3   81 use 5.008001;
  3         12  
5             our $VERSION = '0.32';
6              
7 3     3   1922 use Test::Builder 0.82;
  3         20525  
  3         94  
8              
9             # Conditionally load Windows Term encoding
10 3     3   2567 use if $^O eq 'MSWin32', 'Win32::Console::ANSI';
  3         28  
  3         15  
11 3     3   2040 use Term::Encoding ();
  3         1605  
  3         60  
12              
13 3     3   16 use File::Spec ();
  3         5  
  3         40  
14 3     3   1573417 use Term::ANSIColor ();
  3         58006  
  3         101  
15 3     3   1959 use Test::More ();
  3         10874  
  3         54  
16 3     3   2302 use Scope::Guard;
  3         1071  
  3         106  
17 3     3   15 use Carp ();
  3         4  
  3         36  
18              
19 3     3   14 use Cwd ();
  3         4  
  3         852  
20              
21 0     0   0 *colored = -t STDOUT || $ENV{PERL_TEST_PRETTY_ENABLED} ? \&Term::ANSIColor::colored : sub { $_[1] };
22              
23             my $ORIGINAL_PID = $$;
24              
25             my $SHOW_DUMMY_TAP;
26             my $TERM_ENCODING = Term::Encoding::term_encoding();
27             my $ENCODING_IS_UTF8 = $TERM_ENCODING =~ /^utf-?8$/i;
28              
29             our $NO_ENDING; # Force disable the Test::Pretty finalization process.
30              
31             my $ORIGINAL_subtest = \&Test::Builder::subtest;
32              
33             our $BASE_DIR = Cwd::getcwd();
34             my %filecache;
35             my $get_src_line = sub {
36             my ($filename, $lineno) = @_;
37             $filename = File::Spec->rel2abs($filename, $BASE_DIR);
38             # read a source as utf-8... Yes. it's bad. but works for most of users.
39             # I may need to remove binmode for STDOUT?
40             my $lines = $filecache{$filename} ||= sub {
41             # :encoding is likely to override $@
42             local $@;
43             open my $fh, "<:encoding(utf-8)", $filename
44             or return '';
45             [<$fh>]
46             }->();
47             return unless ref $lines eq 'ARRAY';
48             my $line = $lines->[$lineno-1];
49             $line =~ s/^\s+|\s+$//g;
50             return $line;
51             };
52              
53             if ((!$ENV{HARNESS_ACTIVE} || $ENV{PERL_TEST_PRETTY_ENABLED})) {
54             # make pretty
55 3     3   16 no warnings 'redefine';
  3         4  
  3         923  
56             *Test::Builder::subtest = \&_subtest;
57             *Test::Builder::ok = \&_ok;
58             *Test::Builder::done_testing = \&_done_testing;
59             *Test::Builder::skip = \&_skip;
60             *Test::Builder::skip_all = \&_skip_all;
61             *Test::Builder::expected_tests = \&_expected_tests;
62              
63             my %plan_cmds = (
64             no_plan => \&Test::Builder::no_plan,
65             skip_all => \&_skip_all,
66             tests => \&__plan_tests,
67             );
68             *Test::Builder::plan = sub {
69             my( $self, $cmd, $arg ) = @_;
70              
71             return unless $cmd;
72              
73             local $Test::Builder::Level = $Test::Builder::Level + 1;
74              
75             $self->croak("You tried to plan twice") if $self->{Have_Plan};
76              
77             if( my $method = $plan_cmds{$cmd} ) {
78             local $Test::Builder::Level = $Test::Builder::Level + 1;
79             $self->$method($arg);
80             }
81             else {
82             my @args = grep { defined } ( $cmd, $arg );
83             $self->croak("plan() doesn't understand @args");
84             }
85              
86             return 1;
87             };
88              
89             my $builder = Test::Builder->new;
90             $builder->no_ending(1);
91             $builder->no_header(1); # plan
92              
93             binmode $builder->output(), "encoding($TERM_ENCODING)";
94             binmode $builder->failure_output(), "encoding($TERM_ENCODING)";
95             binmode $builder->todo_output(), "encoding($TERM_ENCODING)";
96              
97             if ($ENV{HARNESS_ACTIVE}) {
98             $SHOW_DUMMY_TAP++;
99             }
100             } else {
101 3     3   20 no warnings 'redefine';
  3         5  
  3         3954  
102             my $ORIGINAL_ok = \&Test::Builder::ok;
103             my @NAMES;
104              
105             $|++;
106              
107             my $builder = Test::Builder->new;
108 3     3   99 binmode $builder->output(), "encoding($TERM_ENCODING)";
  3         3  
  3         21  
109             binmode $builder->failure_output(), "encoding($TERM_ENCODING)";
110             binmode $builder->todo_output(), "encoding($TERM_ENCODING)";
111              
112             my ($arrow_mark, $failed_mark);
113             if ($ENCODING_IS_UTF8) {
114             $arrow_mark = "\x{bb}";
115             $failed_mark = " \x{2192} ";
116             } else {
117             $arrow_mark = ">>";
118             $failed_mark = " x ";
119             }
120              
121             *Test::Builder::subtest = sub {
122 0     0   0 push @NAMES, $_[1];
123             my $guard = Scope::Guard->new(sub {
124 0     0   0 pop @NAMES;
125 0         0 });
126 0         0 $_[0]->note(colored(['cyan'], $arrow_mark x (@NAMES*2)) . " " . join(colored(['yellow'], $failed_mark), $NAMES[-1]));
127 0         0 $_[2]->();
128             };
129             *Test::Builder::ok = sub {
130 4     4   263494 my @args = @_;
131 4   66     26 $args[2] ||= do {
132 2         22 my ( $package, $filename, $line ) = caller($Test::Builder::Level);
133 2         16 "L $line: " . $get_src_line->($filename, $line);
134             };
135 4 50       18 if (@NAMES) {
136 0         0 $args[2] = "(" . join( '/', @NAMES) . ") " . $args[2];
137             }
138 4         11 local $Test::Builder::Level = $Test::Builder::Level + 1;
139 4         35 &$ORIGINAL_ok(@_);
140             };
141             }
142              
143             END {
144 3     3   3858 my $builder = Test::Builder->new;
145 3         151 my $real_exit_code = $?;
146              
147             # Don't bother with an ending if this is a forked copy. Only the parent
148             # should do the ending.
149 3 100       108 if( $ORIGINAL_PID!= $$ ) {
150 1         13 goto NO_ENDING;
151             }
152 2 50       16 if ($Test::Pretty::NO_ENDING) {
153 0         0 goto NO_ENDING;
154             }
155              
156             # see Test::Builder::_ending
157 2 0 33     23 if( !$builder->{Have_Plan} and $builder->{Curr_Test} ) {
158 0         0 $builder->is_passing(0);
159 0         0 $builder->diag("Tests were run but no plan was declared and done_testing() was not seen.");
160             }
161              
162 2 50 33     52 if ($builder->{Have_Plan} && !$builder->{No_Plan}) {
163 2 50       16 if ($builder->{Curr_Test} != $builder->{Expected_Tests}) {
164 0         0 $builder->diag("Bad plan: $builder->{Curr_Test} != $builder->{Expected_Tests}");
165 0         0 $builder->is_passing(0);
166             }
167             }
168 2 50       24 if ($SHOW_DUMMY_TAP) {
169 0 0 0     0 printf("\n%s\n", ($?==0 && $builder->is_passing) ? 'ok' : 'not ok');
170             }
171 2 50       17 if (!$real_exit_code) {
172 2 50       19 if ($builder->is_passing) {
173             ## no critic (Variables::RequireLocalizedPunctuationVars)
174 2         32 $? = 0;
175             } else {
176             # TODO: exit status may be 'how many failed'
177             ## no critic (Variables::RequireLocalizedPunctuationVars)
178 0         0 $? = 1;
179             }
180             }
181             NO_ENDING:
182             }
183              
184             sub _skip_all {
185 0     0     my ($self, $reason) = @_;
186              
187 0 0         $self->{Skip_All} = $self->parent ? $reason : 1;
188              
189 0           $self->_print("1..0 # SKIP" . " $reason");
190 0           $SHOW_DUMMY_TAP = 0;
191 0 0         if ( $self->parent ) {
192 0           die bless {} => 'Test::Builder::Exception';
193             }
194 0           exit(0);
195             }
196              
197             sub _ok {
198 0     0     my( $self, $test, $name ) = @_;
199              
200 0           my ($pkg, $filename, $line, $sub) = caller($Test::Builder::Level);
201 0           my $src_line;
202 0 0         if (defined($line)) {
203 0           $src_line = $get_src_line->($filename, $line);
204             } else {
205 0           $self->diag(Carp::longmess("\$Test::Builder::Level is invalid. Testing library you are using is broken. : $Test::Builder::Level"));
206 0           $src_line = '';
207             }
208              
209 0 0 0       if ( $self->{Child_Name} and not $self->{In_Destroy} ) {
210 0 0         $name = 'unnamed test' unless defined $name;
211 0           $self->is_passing(0);
212 0           $self->croak("Cannot run test ($name) with active children");
213             }
214             # $test might contain an object which we don't want to accidentally
215             # store, so we turn it into a boolean.
216 0 0         $test = $test ? 1 : 0;
217              
218 0           lock $self->{Curr_Test};
219 0           $self->{Curr_Test}++;
220              
221             # In case $name is a string overloaded object, force it to stringify.
222 0           $self->_unoverload_str( \$name );
223              
224 0 0 0       $self->diag(<<"ERR") if defined $name and $name =~ /^[\d\s]+$/;
225             You named your test '$name'. You shouldn't use numbers for your test names.
226             Very confusing.
227             ERR
228              
229             # Capture the value of $TODO for the rest of this ok() call
230             # so it can more easily be found by other routines.
231 0           my $todo = $self->todo();
232 0           my $in_todo = $self->in_todo;
233 0 0         local $self->{Todo} = $todo if $in_todo;
234              
235 0           $self->_unoverload_str( \$todo );
236              
237 0           my $out;
238 0           my $result = &Test::Builder::share( {} );
239              
240              
241 0 0         unless($test) {
242 0 0         my $fail_char = $ENCODING_IS_UTF8 ? "\x{2716}" : "x";
243 0           $out .= colored(['red'], $fail_char);
244 0 0         @$result{ 'ok', 'actual_ok' } = ( ( $self->in_todo ? 1 : 0 ), 0 );
245             }
246             else {
247 0 0         my $success_char = $ENCODING_IS_UTF8 ? "\x{2713}" : "o";
248 0           $out .= colored(['green'], $success_char);
249 0           @$result{ 'ok', 'actual_ok' } = ( 1, $test );
250             }
251              
252 0   0       $name ||= " L$line: $src_line";
253              
254             # $out .= " $self->{Curr_Test}" if $self->use_numbers;
255              
256 0 0         if( defined $name ) {
257 0           $name =~ s|#|\\#|g; # # in a name can confuse Test::Harness.
258 0   0       $out .= colored([$ENV{TEST_PRETTY_COLOR_NAME} || 'BRIGHT_BLACK'], " $name");
259 0           $result->{name} = $name;
260             }
261             else {
262 0           $result->{name} = '';
263             }
264              
265 0 0         if( $self->in_todo ) {
266 0           $out .= " # TODO $todo";
267 0           $result->{reason} = $todo;
268 0           $result->{type} = 'todo';
269             }
270             else {
271 0           $result->{reason} = '';
272 0           $result->{type} = '';
273             }
274              
275 0           $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = $result;
276 0           $out .= "\n";
277              
278             # Dont print 'ok's for subtests. It's not pretty.
279 0 0 0       $self->_print($out) unless $sub =~/subtest/ and $test;
280              
281 0 0         unless($test) {
282 0 0         my $msg = $self->in_todo ? "Failed (TODO)" : "Failed";
283 0 0         $self->_print_to_fh( $self->_diag_fh, "\n" ) if $ENV{HARNESS_ACTIVE};
284              
285 0           my( undef, $file, $line ) = $self->caller;
286 0 0         if( defined $name ) {
287 0           $self->diag(qq[ $msg test '$name'\n]);
288 0           $self->diag(qq[ at $file line $line.\n]);
289             }
290             else {
291 0           $self->diag(qq[ $msg test at $file line $line.\n]);
292             }
293             }
294              
295 0 0 0       $self->is_passing(0) unless $test || $self->in_todo;
296              
297             # Check that we haven't violated the plan
298 0           $self->_check_is_passing_plan();
299              
300 0 0         return $test ? 1 : 0;
301             }
302              
303             sub _done_testing {
304             # do nothing
305 0     0     my $builder = Test::More->builder;
306 0           $builder->{Have_Plan} = 1;
307 0           $builder->{Done_Testing} = [caller];
308 0           $builder->{Expected_Tests} = $builder->current_test;
309             }
310              
311             sub _subtest {
312 0     0     my ($self, $name) = @_;
313 0           my $orig_indent = $self->_indent();
314 0           my $ORIGINAL_note = \&Test::Builder::note;
315 3     3   16 no warnings 'redefine';
  3         3  
  3         1420  
316             *Test::Builder::note = sub {
317             # Not sure why the output looses its encoding but lets set it back again.
318             # Otherwise we get "Wide character in print" errors.
319 0     0     binmode $_[0]->output(), "encoding($TERM_ENCODING)";
320             # If printing the beginning of a subtest, make it pretty
321 0 0         if ( $_[1] eq "Subtest: $name") {
322 0           print {$self->output} do {
  0            
323 0           $orig_indent . " $name\n";
324             };
325 0           return 0;
326             } else {
327 0           $ORIGINAL_note->(@_);
328             }
329 0           };
330             # Now that we've redefined note(), let Test::Builder run as normal.
331 0           my $retval = $ORIGINAL_subtest->(@_);
332 0           *Test::Builder::note = $ORIGINAL_note;
333 0           $retval;
334             }
335              
336             sub __plan_tests {
337 0     0     my ( $self, $arg ) = @_;
338              
339 0 0         if ($arg) {
    0          
340 0           local $Test::Builder::Level = $Test::Builder::Level + 1;
341 0           return $self->expected_tests($arg);
342             }
343             elsif ( !defined $arg ) {
344 0           $self->croak("Got an undefined number of tests");
345             }
346             else {
347 0           $self->croak("You said to run 0 tests");
348             }
349              
350 0           return;
351             }
352              
353             sub _expected_tests {
354 0     0     my $self = shift;
355 0           my($max) = @_;
356              
357 0 0         if(@_) {
358 0 0         $self->croak("Number of tests must be a positive integer. You gave it '$max'")
359             unless $max =~ /^\+?\d+$/;
360              
361 0           $self->{Expected_Tests} = $max;
362 0           $self->{Have_Plan} = 1;
363              
364             # $self->_output_plan($max) unless $self->no_header;
365             }
366 0           return $self->{Expected_Tests};
367             }
368              
369             sub _skip {
370 0     0     my ($self, $why) = @_;
371              
372 0           lock( $self->{Curr_Test} );
373 0           $self->{Curr_Test}++;
374              
375 0           $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &Test::Builder::share(
376             {
377             'ok' => 1,
378             actual_ok => 1,
379             name => '',
380             type => 'skip',
381             reason => $why,
382             }
383             );
384              
385 0           $self->_print(colored(['yellow'], 'skip') . " $why");
386              
387 0           return 1;
388             }
389              
390             1;
391             __END__