File Coverage

inc/Test/Base.pm
Criterion Covered Total %
statement 220 384 57.2
branch 56 202 27.7
condition 20 83 24.1
subroutine 38 63 60.3
pod 0 29 0.0
total 334 761 43.8


line stmt bran cond sub pod time code
1             #line 1
2             our $VERSION = '0.89';
3              
4 5     5   4148 use Spiffy -Base;
  5         16  
  5         25  
5 5     5   89 use Spiffy ':XXX';
  5     5   8  
  5     5   140  
  5         21  
  5         7  
  5         225  
  5         21  
  5         10  
  5         25  
6              
7             my $HAS_PROVIDER;
8             BEGIN {
9 5     5   365 $HAS_PROVIDER = eval "require Test::Builder::Provider; 1";
10              
11 5 50       56 if ($HAS_PROVIDER) {
12 0         0 Test::Builder::Provider->import('provides');
13             }
14             else {
15 5     5   211 *provides = sub { 1 };
  5         11  
16             }
17             }
18              
19              
20             my @test_more_exports;
21             BEGIN {
22 5     5   111 @test_more_exports = qw(
23             ok isnt like unlike is_deeply cmp_ok
24             skip todo_skip pass fail
25             eq_array eq_hash eq_set
26             plan can_ok isa_ok diag
27             use_ok
28             $TODO
29             );
30             }
31              
32 5     5   2827 use Test::More import => \@test_more_exports;
  5         16  
  5         61  
33 5     5   33 use Carp;
  5         7  
  5         2134  
34              
35             our @EXPORT = (@test_more_exports, qw(
36             is no_diff
37              
38             blocks next_block first_block
39             delimiters spec_file spec_string
40             filters filters_delay filter_arguments
41             run run_compare run_is run_is_deeply run_like run_unlike
42             skip_all_unless_require is_deep run_is_deep
43             WWW XXX YYY ZZZ
44             tie_output no_diag_on_only
45              
46             find_my_self default_object
47              
48             croak carp cluck confess
49             ));
50              
51             field '_spec_file';
52             field '_spec_string';
53             field _filters => [qw(norm trim)];
54             field _filters_map => {};
55             field spec =>
56             -init => '$self->_spec_init';
57             field block_list =>
58             -init => '$self->_block_list_init';
59             field _next_list => [];
60             field block_delim =>
61             -init => '$self->block_delim_default';
62             field data_delim =>
63             -init => '$self->data_delim_default';
64             field _filters_delay => 0;
65             field _no_diag_on_only => 0;
66              
67             field block_delim_default => '===';
68             field data_delim_default => '---';
69              
70             my $default_class;
71             my $default_object;
72             my $reserved_section_names = {};
73              
74 12     12 0 20 $default_object ||= $default_class->new;
75 12   66     55 return $default_object;
76 12         19 }
77              
78             my $import_called = 0;
79             $import_called = 1;
80             my $class = (grep /^-base$/i, @_)
81 5     5   44 ? scalar(caller)
82 5 50       38 : $_[0];
83             if (not defined $default_class) {
84             $default_class = $class;
85 5 50       27 }
86 5         11 # else {
87             # croak "Can't use $class after using $default_class"
88             # unless $default_class->isa($class);
89             # }
90              
91             unless (grep /^-base$/i, @_) {
92             my @args;
93 5 50       40 for (my $ii = 1; $ii <= $#_; ++$ii) {
94 5         12 if ($_[$ii] eq '-package') {
95 5         22 ++$ii;
96 0 0       0 } else {
97 0         0 push @args, $_[$ii];
98             }
99 0         0 }
100             Test::More->import(import => \@test_more_exports, @args)
101             if @args;
102 5 50       19 }
103              
104             _strict_warnings();
105             goto &Spiffy::import;
106 5         23 }
107 5         112  
108             # Wrap Test::Builder::plan
109             my $plan_code = \&Test::Builder::plan;
110             my $Have_Plan = 0;
111             {
112             no warnings 'redefine';
113             *Test::Builder::plan = sub {
114 5     5   33 $Have_Plan = 1;
  5         8  
  5         19017  
115             goto &$plan_code;
116 5     5   14 };
117 5         29 }
118              
119             my $DIED = 0;
120             $SIG{__DIE__} = sub { $DIED = 1; die @_ };
121              
122              
123             my $suffix = shift;
124 3     3 0 5 my $class = ref($self) . "::$suffix";
  3         9  
125 28     28 0 34 return $class if $class->can('new');
  28         45  
126             $class = __PACKAGE__ . "::$suffix";
127 31     31 0 38 return $class if $class->can('new');
128 31         36 eval "require $class";
129 31         61 return $class if $class->can('new');
130 31 100       105 die "Can't find a class for $suffix";
131 2         7 }
132 2 50       19  
133 2         142 if ($self->{block_list}) {
134 2 50       21 my $caller = (caller(1))[3];
135 0         0 $caller =~ s/.*:://;
136             croak "Too late to call $caller()"
137             }
138 0     0 0 0 }
139 0 0       0  
140 0         0 my $self = ref($_[0]) eq $default_class
141 0         0 ? splice(@_, 0, 1)
142 0         0 : default_object();
143             return $self, @_;
144             }
145              
146             (my ($self), @_) = find_my_self(@_);
147 12 50   12 0 47  
148             croak "Invalid arguments passed to 'blocks'"
149             if @_ > 1;
150 12         40 croak sprintf("'%s' is invalid argument to blocks()", shift(@_))
151             if @_ && $_[0] !~ /^[a-zA-Z]\w*$/;
152              
153             my $blocks = $self->block_list;
154 2     2 0 71  
155             my $section_name = shift || '';
156 2 50       7 my @blocks = $section_name
157             ? (grep { exists $_->{$section_name} } @$blocks)
158 2 50 33     10 : (@$blocks);
159              
160             return scalar(@blocks) unless wantarray;
161 2         44  
162             return (@blocks) if $self->_filters_delay;
163 2   50     18  
164             for my $block (@blocks) {
165 2 50       11 $block->run_filters
  0         0  
166             unless $block->is_filtered;
167             }
168 2 50       19  
169             return (@blocks);
170 0 0       0 }
171              
172 0         0 (my ($self), @_) = find_my_self(@_);
173 0 0       0 my $list = $self->_next_list;
174             if (@$list == 0) {
175             $list = [@{$self->block_list}, undef];
176             $self->_next_list($list);
177 0         0 }
178             my $block = shift @$list;
179             if (defined $block and not $block->is_filtered) {
180             $block->run_filters;
181 0     0 0 0 }
182 0         0 return $block;
183 0 0       0 }
184 0         0  
  0         0  
185 0         0 (my ($self), @_) = find_my_self(@_);
186             $self->_next_list([]);
187 0         0 $self->next_block;
188 0 0 0     0 }
189 0         0  
190             (my ($self), @_) = find_my_self(@_);
191 0         0 $self->_filters_delay(defined $_[0] ? shift : 1);
192             }
193              
194             (my ($self), @_) = find_my_self(@_);
195 0     0 0 0 $self->_no_diag_on_only(defined $_[0] ? shift : 1);
196 0         0 }
197 0         0  
198             (my ($self), @_) = find_my_self(@_);
199             $self->check_late;
200             my ($block_delimiter, $data_delimiter) = @_;
201 0     0 0 0 $block_delimiter ||= $self->block_delim_default;
202 0 0       0 $data_delimiter ||= $self->data_delim_default;
203             $self->block_delim($block_delimiter);
204             $self->data_delim($data_delimiter);
205             return $self;
206 0     0 0 0 }
207 0 0       0  
208             (my ($self), @_) = find_my_self(@_);
209             $self->check_late;
210             $self->_spec_file(shift);
211 0     0 0 0 return $self;
212 0         0 }
213 0         0  
214 0   0     0 (my ($self), @_) = find_my_self(@_);
215 0   0     0 $self->check_late;
216 0         0 $self->_spec_string(shift);
217 0         0 return $self;
218 0         0 }
219              
220             (my ($self), @_) = find_my_self(@_);
221             if (ref($_[0]) eq 'HASH') {
222 0     0 0 0 $self->_filters_map(shift);
223 0         0 }
224 0         0 else {
225 0         0 my $filters = $self->_filters;
226             push @$filters, @_;
227             }
228             return $self;
229 0     0 0 0 }
230 0         0  
231 0         0 $Test::Base::Filter::arguments;
232 0         0 }
233              
234             eval { require Text::Diff; 1 } &&
235             $Text::Diff::VERSION >= 0.35 &&
236 1     1 0 4 $Algorithm::Diff::VERSION >= 1.15;
237 1 50       5 }
238 1         32  
239             provides 'is';
240             (my ($self), @_) = find_my_self(@_);
241 0         0 my ($actual, $expected, $name) = @_;
242 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1 unless $HAS_PROVIDER;
243             if ($ENV{TEST_SHOW_NO_DIFFS} or
244 1         2 not defined $actual or
245             not defined $expected or
246             $actual eq $expected or
247             not($self->have_text_diff) or
248 0     0 0 0 $expected !~ /\n./s
249             ) {
250             Test::More::is($actual, $expected, $name);
251 0     0 0 0 }
252 0 0 0     0 else {
  0         0  
  0         0  
253             $name = '' unless defined $name;
254             ok $actual eq $expected, $name;
255             diag Text::Diff::diff(\$expected, \$actual);
256             }
257             }
258              
259 7     7 0 23 (my ($self), @_) = find_my_self(@_);
260 7         16 my $callback = shift;
261 7 50       22 for my $block (@{$self->block_list}) {
262 7 50 66     75 $block->run_filters unless $block->is_filtered;
      66        
      66        
      33        
      33        
263             &{$callback}($block);
264             }
265             }
266              
267             my $name_error = "Can't determine section names";
268             return unless defined $self->spec;
269 7         27 return @_ if @_ == 2;
270             my $block = $self->first_block
271             or croak $name_error;
272 0 0       0 my @names = grep {
273 0         0 $_ !~ /^(ONLY|LAST|SKIP)$/;
274 0         0 } @{$block->{_section_order}[0] || []};
275             croak "$name_error. Need two sections in first block"
276             unless @names == 2;
277             return @names;
278             }
279 2     2 0 7  
280 2         5 plan('no_plan') unless $Have_Plan;
281 2         5 }
  2         40  
282 3 50       101  
283 3         7 run_compare() unless $Have_Plan or $DIED or not $import_called;
  3         11  
284             }
285              
286             (my ($self), @_) = find_my_self(@_);
287             return unless defined $self->spec;
288 0     0   0 $self->_assert_plan;
289 0 0       0 my ($x, $y) = $self->_section_names(@_);
290 0 0       0 local $Test::Builder::Level = $Test::Builder::Level + 1;
291 0 0       0 for my $block (@{$self->block_list}) {
292             next unless exists($block->{$x}) and exists($block->{$y});
293             $block->run_filters unless $block->is_filtered;
294 0         0 if (ref $block->$x) {
295 0 0       0 is_deeply($block->$x, $block->$y,
  0         0  
296 0 0       0 $block->name ? $block->name : ());
297             }
298 0         0 elsif (ref $block->$y eq 'Regexp') {
299             my $regexp = ref $y ? $y : $block->$y;
300             like($block->$x, $regexp, $block->name ? $block->name : ());
301 0     0   0 }
302 0 0       0 else {
303             is($block->$x, $block->$y, $block->name ? $block->name : ());
304             }
305 5     5   3129 }
306 5 0 33     139 }
      33        
307              
308             (my ($self), @_) = find_my_self(@_);
309             $self->_assert_plan;
310 0     0 0 0 my ($x, $y) = $self->_section_names(@_);
311 0 0       0 local $Test::Builder::Level = $Test::Builder::Level + 1;
312 0         0 for my $block (@{$self->block_list}) {
313 0         0 next unless exists($block->{$x}) and exists($block->{$y});
314 0         0 $block->run_filters unless $block->is_filtered;
315 0         0 is($block->$x, $block->$y,
  0         0  
316 0 0 0     0 $block->name ? $block->name : ()
317 0 0       0 );
318 0 0       0 }
    0          
319 0 0       0 }
320              
321             (my ($self), @_) = find_my_self(@_);
322             $self->_assert_plan;
323 0 0       0 my ($x, $y) = $self->_section_names(@_);
324 0 0       0 for my $block (@{$self->block_list}) {
325             next unless exists($block->{$x}) and exists($block->{$y});
326             $block->run_filters unless $block->is_filtered;
327 0 0       0 is_deeply($block->$x, $block->$y,
328             $block->name ? $block->name : ()
329             );
330             }
331             }
332              
333 0     0 0 0 (my ($self), @_) = find_my_self(@_);
334 0         0 $self->_assert_plan;
335 0         0 my ($x, $y) = $self->_section_names(@_);
336 0         0 for my $block (@{$self->block_list}) {
337 0         0 next unless exists($block->{$x}) and defined($y);
  0         0  
338 0 0 0     0 $block->run_filters unless $block->is_filtered;
339 0 0       0 my $regexp = ref $y ? $y : $block->$y;
340 0 0       0 like($block->$x, $regexp,
341             $block->name ? $block->name : ()
342             );
343             }
344             }
345              
346             (my ($self), @_) = find_my_self(@_);
347 0     0 0 0 $self->_assert_plan;
348 0         0 my ($x, $y) = $self->_section_names(@_);
349 0         0 for my $block (@{$self->block_list}) {
350 0         0 next unless exists($block->{$x}) and defined($y);
  0         0  
351 0 0 0     0 $block->run_filters unless $block->is_filtered;
352 0 0       0 my $regexp = ref $y ? $y : $block->$y;
353 0 0       0 unlike($block->$x, $regexp,
354             $block->name ? $block->name : ()
355             );
356             }
357             }
358              
359             (my ($self), @_) = find_my_self(@_);
360 0     0 0 0 my $module = shift;
361 0         0 eval "require $module; 1"
362 0         0 or Test::More::plan(
363 0         0 skip_all => "$module failed to load"
  0         0  
364 0 0 0     0 );
365 0 0       0 }
366 0 0       0  
367 0 0       0 (my ($self), @_) = find_my_self(@_);
368             require Test::Deep;
369             Test::Deep::cmp_deeply(@_);
370             }
371              
372             (my ($self), @_) = find_my_self(@_);
373             $self->_assert_plan;
374 0     0 0 0 my ($x, $y) = $self->_section_names(@_);
375 0         0 for my $block (@{$self->block_list}) {
376 0         0 next unless exists($block->{$x}) and exists($block->{$y});
377 0         0 $block->run_filters unless $block->is_filtered;
  0         0  
378 0 0 0     0 is_deep($block->$x, $block->$y,
379 0 0       0 $block->name ? $block->name : ()
380 0 0       0 );
381 0 0       0 }
382             }
383              
384             my $spec = shift;
385             return unless defined $spec;
386             return $spec unless $spec =~
387             s/\A\s*<<<(.*?)>>>\s*$//sm;
388 0     0 0 0 my $eval_code = $1;
389 0         0 eval "package main; $eval_code";
390 0 0       0 croak $@ if $@;
391             return $spec;
392             }
393              
394             my $spec = $self->spec;
395             return [] unless defined $spec;
396             $spec = $self->_pre_eval($spec);
397 0     0 0 0 my $cd = $self->block_delim;
398 0         0 my @hunks = ($spec =~ /^(\Q${cd}\E.*?(?=^\Q${cd}\E|\z))/msg);
399 0         0 my $blocks = $self->_choose_blocks(@hunks);
400             $self->block_list($blocks); # Need to set early for possible filter use
401             my $seq = 1;
402             for my $block (@$blocks) {
403 0     0 0 0 $block->blocks_object($self);
404 0         0 $block->seq_num($seq++);
405 0         0 }
406 0         0 return $blocks;
  0         0  
407 0 0 0     0 }
408 0 0       0  
409 0 0       0 my $blocks = [];
410             for my $hunk (@_) {
411             my $block = $self->_make_block($hunk);
412             if (exists $block->{ONLY}) {
413             diag "I found ONLY: maybe you're debugging?"
414             unless $self->_no_diag_on_only;
415 2     2   3 return [$block];
416 2         6 }
417 2 50       6 next if exists $block->{SKIP};
418 2 50       12 push @$blocks, $block;
419             if (exists $block->{LAST}) {
420 0         0 return $blocks;
421 0         0 }
422 0 0       0 }
423 0         0 return $blocks;
424             }
425              
426 2     2   3 my $id = shift;
427 2         37 croak "'$id' is a reserved name. Use something else.\n"
428 2 50       8 if $reserved_section_names->{$id} or
429 2         9 $id =~ /^_/;
430 2         43 }
431 2         90  
432 2         8 my $hunk = shift;
433 2         47 my $cd = $self->block_delim;
434 2         3 my $dd = $self->data_delim;
435 2         5 my $block = $self->block_class->new;
436 3         51 $hunk =~ s/\A\Q${cd}\E[ \t]*(.*)\s+// or die;
437 3         51 my $name = $1;
438             my @parts = split /^\Q${dd}\E +\(?(\w+)\)? *(.*)?\n/m, $hunk;
439 2         8 my $description = shift @parts;
440             $description ||= '';
441             unless ($description =~ /\S/) {
442 2     2   6 $description = $name;
443 2         4 }
444 2         6 $description =~ s/\s*\z//;
445 3         11 $block->set_value(description => $description);
446 3 50       10  
447 0 0       0 my $section_map = {};
448             my $section_order = [];
449 0         0 while (@parts) {
450             my ($type, $filters, $value) = splice(@parts, 0, 3);
451 3 50       9 $self->_check_reserved($type);
452 3         4 $value = '' unless defined $value;
453 3 50       9 $filters = '' unless defined $filters;
454 0         0 if ($filters =~ /:(\s|\z)/) {
455             croak "Extra lines not allowed in '$type' section"
456             if $value =~ /\S/;
457 2         5 ($filters, $value) = split /\s*:(?:\s+|\z)/, $filters, 2;
458             $value = '' unless defined $value;
459             $value =~ s/^\s*(.*?)\s*$/$1/;
460 11     11   15 }
461 11         15 $section_map->{$type} = {
462             filters => $filters,
463 11 50 33     41 };
464             push @$section_order, $type;
465             $block->set_value($type, $value);
466             }
467 3     3   5 $block->set_value(name => $name);
468 3         4 $block->set_value(_section_map => $section_map);
469 3         54 $block->set_value(_section_order => $section_order);
470 3         47 return $block;
471 3         10 }
472 3 50       62  
473 3         11 return $self->_spec_string
474 3         67 if $self->_spec_string;
475 3         7 local $/;
476 3   50     23 my $spec;
477 3 50       9 if (my $spec_file = $self->_spec_file) {
478 3         5 open FILE, $spec_file or die $!;
479             $spec = <FILE>;
480 3         17 close FILE;
481 3         13 }
482             else {
483 3         5 require Scalar::Util;
484 3         7 my $handle = Scalar::Util::openhandle( \*main::DATA );
485 3         9 if ($handle) {
486 11         26 $spec = <$handle>;
487 11         30 }
488 11 100       21 }
489 11 50       20 return $spec;
490 11 100       27 }
491 6 50       15  
492             require Filter::Util::Call;
493 6         26 my $done = 0;
494 6 50       12 Filter::Util::Call::filter_add(
495 6         28 sub {
496             return 0 if $done;
497 11         31 my ($data, $end) = ('', '');
498             while (my $status = Filter::Util::Call::filter_read()) {
499             return $status if $status < 0;
500 11         19 if (/^__(?:END|DATA)__\r?$/) {
501 11         25 $end = $_;
502             last;
503 3         11 }
504 3         9 $data .= $_;
505 3         8 $_ = '';
506 3         8 }
507             $_ = "use strict;use warnings;$data$end";
508             $done = 1;
509 2     2   4 }
510 2 50       35 );
511             }
512 2         9  
513 2         3 my $handle = shift;
514 2 50       34 die "No buffer to tie" unless @_;
515 0 0       0 tie *$handle, 'Test::Base::Handle', $_[0];
516 0         0 }
517 0         0  
518             $ENV{TEST_SHOW_NO_DIFFS} = 1;
519             }
520 2         12  
521 2         12  
522 2 50       6 my $class = shift;
523 2         40 bless \ $_[0], $class;
524             }
525              
526 2         18 $$self .= $_ for @_;
527             }
528              
529             #===============================================================================
530 5     5   26 # Test::Base::Block
531 5         21 #
532             # This is the default class for accessing a Test::Base block object.
533             #===============================================================================
534 5 50   5   23 our @ISA = qw(Spiffy);
535 5         14  
536 5         95 our @EXPORT = qw(block_accessor);
537 268 50       451  
538 268 100       415 return;
539 5         11 }
540 5         15  
541             my $accessor = shift;
542 263         286 no strict 'refs';
543 263         478 return if defined &$accessor;
544             *$accessor = sub {
545 5         51 my $self = shift;
546 5         216 if (@_) {
547             Carp::croak "Not allowed to set values for '$accessor'";
548 5         42 }
549             my @list = @{$self->{$accessor} || []};
550             return wantarray
551             ? (@list)
552 0     0 0 0 : $list[0];
553 0 0       0 };
554 0         0 }
555              
556             block_accessor 'name';
557 0     0 0 0 block_accessor 'description';
558 0         0 Spiffy::field 'seq_num';
559             Spiffy::field 'is_filtered';
560             Spiffy::field 'blocks_object';
561             Spiffy::field 'original_values' => {};
562              
563             no strict 'refs';
564 0     0   0 my $accessor = shift;
565 0         0 block_accessor $accessor
566             unless defined &$accessor;
567             $self->{$accessor} = [@_];
568 0     0   0 }
569 0         0  
570             my $map = $self->_section_map;
571             my $order = $self->_section_order;
572             Carp::croak "Attempt to filter a block twice"
573             if $self->is_filtered;
574             for my $type (@$order) {
575             my $filters = $map->{$type}{filters};
576             my @value = $self->$type;
577             $self->original_values->{$type} = $value[0];
578             for my $filter ($self->_get_filters($type, $filters)) {
579             $Test::Base::Filter::arguments =
580             $filter =~ s/=(.*)$// ? $1 : undef;
581             my $function = "main::$filter";
582 0     0   0 no strict 'refs';
583 0         0 if (defined &$function) {
584             local $_ =
585             (@value == 1 and not defined($value[0])) ? undef :
586             join '', @value;
587 23     23   41 my $old = $_;
588 5     5   102 @value = &$function(@value);
  5         10  
  5         1088  
589 23 50       79 if (not(@value) or
590             @value == 1 and defined($value[0]) and $value[0] =~ /\A(\d+|)\z/
591 36     36   397 ) {
592 36 50       72 if ($value[0] && $_ eq $old) {
593 0         0 Test::Base::diag("Filters returning numbers are supposed to do munging \$_: your filter '$function' apparently doesn't.");
594             }
595 36 50       43 @value = ($_);
  36         115  
596             }
597 36 100       136 }
598             else {
599 23         113 my $filter_object = $self->blocks_object->filter_class->new;
600             die "Can't find a function or method for '$filter' filter\n"
601             unless $filter_object->can($filter);
602             $filter_object->current_block($self);
603             @value = $filter_object->$filter(@value);
604             }
605             # Set the value after each filter since other filters may be
606             # introspecting.
607             $self->set_value($type, @value);
608             }
609 51     51   56 }
610 5     5   34 $self->is_filtered(1);
  5         7  
  5         1163  
611 51         64 }
612 51 100       141  
613             my $type = shift;
614 51         174 my $string = shift || '';
615             $string =~ s/\s*(.*?)\s*/$1/;
616             my @filters = ();
617 3     3   6 my $map_filters = $self->blocks_object->_filters_map->{$type} || [];
618 3         9 $map_filters = [ $map_filters ] unless ref $map_filters;
619 3         8 my @append = ();
620 3 50       77 for (
621             @{$self->blocks_object->_filters},
622 3         8 @$map_filters,
623 11         29 split(/\s+/, $string),
624 11         67 ) {
625 11         202 my $filter = $_;
626 11         29 last unless length $filter;
627 28 50       67 if ($filter =~ s/^-//) {
628             @filters = grep { $_ ne $filter } @filters;
629 28         46 }
630 5     5   34 elsif ($filter =~ s/^\+//) {
  5         12  
  5         2907  
631 28 50       97 push @append, $filter;
632 0 0 0     0 }
633             else {
634             push @filters, $filter;
635 0         0 }
636 0         0 }
637 0 0 0     0 return @filters, @append;
      0        
      0        
638             }
639              
640 0 0 0     0 {
641 0         0 %$reserved_section_names = map {
642             ($_, 1);
643 0         0 } keys(%Test::Base::Block::), qw( new DESTROY );
644             }
645              
646             1;