File Coverage

inc/Test/Base.pm
Criterion Covered Total %
statement 225 375 60.0
branch 60 188 31.9
condition 23 83 27.7
subroutine 42 62 67.7
pod 20 29 68.9
total 370 737 50.2


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