File Coverage

blib/lib/Test/Chunks.pm
Criterion Covered Total %
statement 426 457 93.2
branch 126 154 81.8
condition 44 60 73.3
subroutine 75 79 94.9
pod 15 22 68.1
total 686 772 88.8


line stmt bran cond sub pod time code
1             package Test::Chunks;
2 67     67   206084 use Spiffy 0.24 -Base;
  67         539226  
  67         889  
3 67     67   568115 use Spiffy ':XXX';
  67     67   188  
  67     67   2664  
  67         364  
  67         142  
  67         12524  
  67         382  
  67         287  
  67         337  
4             my @test_more_exports;
5             BEGIN {
6 67     67   26356 @test_more_exports = qw(
7             ok isnt like unlike is_deeply cmp_ok
8             skip todo_skip pass fail
9             eq_array eq_hash eq_set
10             plan can_ok isa_ok diag
11             $TODO
12             );
13             }
14 67     67   106193 use Test::More import => \@test_more_exports;
  67         1946692  
  67         987  
15 67     67   37093 use Carp;
  67         147  
  67         292688  
16              
17             our @EXPORT = (@test_more_exports, qw(
18             is
19              
20             chunks next_chunk
21             delimiters spec_file spec_string
22             filters filters_delay filter_arguments
23             run run_is run_is_deeply run_like run_unlike
24             WWW XXX YYY ZZZ
25             tie_output
26              
27             find_my_self default_object
28              
29             croak carp cluck confess
30             ));
31              
32             our $VERSION = '0.39';
33              
34             field '_spec_file';
35             field '_spec_string';
36             field _filters => [qw(norm trim)];
37             field _filters_map => {};
38             field spec =>
39             -init => '$self->_spec_init';
40             field chunk_list =>
41             -init => '$self->_chunk_list_init';
42             field _next_list => [];
43             field chunk_delim =>
44             -init => '$self->chunk_delim_default';
45             field data_delim =>
46             -init => '$self->data_delim_default';
47             field _filters_delay => 0;
48              
49             field chunk_delim_default => '===';
50             field data_delim_default => '---';
51              
52             my $default_class;
53             my $default_object;
54             my $reserved_section_names = {};
55              
56 313     313 1 652 sub default_object {
57 313   66     7146 $default_object ||= $default_class->new;
58 313         1552 return $default_object;
59             }
60              
61             sub import() {
62 73 100   73   5166 my $class = (grep /^-base$/i, @_)
63             ? scalar(caller)
64             : $_[0];
65 73 100       353 if (not defined $default_class) {
66 67         161 $default_class = $class;
67             }
68             else {
69 6 100       490 croak "Can't use $class after using $default_class"
70             unless $default_class->isa($class);
71             }
72              
73 72 100 100     775 if (@_ > 1 and not grep /^-base$/i, @_) {
74 13         45 my @args = @_;
75 13         26 shift @args;
76 13         310 Test::More->import(import => \@test_more_exports, @args);
77             }
78            
79 72         10906 _strict_warnings();
80 72         1862 goto &Spiffy::import;
81             }
82              
83 135     135 0 3779 sub chunk_class { $self->find_class('Chunk') }
  135         462  
84 481     481 0 2970 sub filter_class { $self->find_class('Filter') }
  481         1053  
85              
86 616     616 0 794 sub find_class {
87 616         791 my $suffix = shift;
88 616         1426 my $class = ref($self) . "::$suffix";
89 616 100       3375 return $class if $class->can('new');
90 1         3 $class = __PACKAGE__ . "::$suffix";
91 1 50       13 return $class if $class->can('new');
92 0         0 die "Can't find a class for $suffix";
93             }
94              
95 29     29 0 45 sub check_late {
96 29 100       120 if ($self->{chunk_list}) {
97 3         30 my $caller = (caller(1))[3];
98 3         29 $caller =~ s/.*:://;
99 3         924 croak "Too late to call $caller()"
100             }
101             }
102              
103             sub find_my_self() {
104 364 100   364 0 1601 my $self = ref($_[0]) eq $default_class
105             ? splice(@_, 0, 1)
106             : default_object();
107 364         1740 return $self, @_;
108             }
109              
110             sub chunks() {
111 90     90 1 14874 (my ($self), @_) = find_my_self(@_);
112              
113 90 100       638 croak "Invalid arguments passed to 'chunks'"
114             if @_ > 1;
115 89 50 66     375 croak sprintf("'%s' is invalid argument to chunks()", shift(@_))
116             if @_ && $_[0] !~ /^[a-zA-Z]\w*$/;
117              
118 89         2850 my $chunks = $self->chunk_list;
119            
120 76   100     1464 my $section_name = shift || '';
121 6         13 my @chunks = $section_name
122 76 100       381 ? (grep { exists $_->{$section_name} } @$chunks)
123             : (@$chunks);
124              
125 76 100       4457 return scalar(@chunks) unless wantarray;
126            
127 36 100       1075 return (@chunks) if $self->_filters_delay;
128              
129 35         1971 for my $chunk (@chunks) {
130 66 100       3242 $chunk->run_filters
131             unless $chunk->is_filtered;
132             }
133              
134 35         484 return (@chunks);
135             }
136              
137             sub next_chunk() {
138 26     26 1 1503 (my ($self), @_) = find_my_self(@_);
139 26         871 my $list = $self->_next_list;
140 26 100       224 if (@$list == 0) {
141 9         19 $list = [@{$self->chunk_list}, undef];
  9         214  
142 9         311 $self->_next_list($list);
143             }
144 26         278 my $chunk = shift @$list;
145 26 100 100     588 if (defined $chunk and not $chunk->is_filtered) {
146 19         192 $chunk->run_filters;
147             }
148 24         266 return $chunk;
149             }
150              
151             sub filters_delay() {
152 1     1 1 6 (my ($self), @_) = find_my_self(@_);
153 1 50       28 $self->_filters_delay(defined $_[0] ? shift : 1);
154             }
155              
156             sub delimiters() {
157 5     5 1 556 (my ($self), @_) = find_my_self(@_);
158 5         28 $self->check_late;
159 4         9 my ($chunk_delimiter, $data_delimiter) = @_;
160 4   33     18 $chunk_delimiter ||= $self->chunk_delim_default;
161 4   33     17 $data_delimiter ||= $self->data_delim_default;
162 4         150 $self->chunk_delim($chunk_delimiter);
163 4         269 $self->data_delim($data_delimiter);
164 4         65 return $self;
165             }
166              
167             sub spec_file() {
168 4     4 1 1586 (my ($self), @_) = find_my_self(@_);
169 4         33 $self->check_late;
170 3         107 $self->_spec_file(shift);
171 3         35 return $self;
172             }
173              
174             sub spec_string() {
175 20     20 1 11789 (my ($self), @_) = find_my_self(@_);
176 20         49 $self->check_late;
177 19         567 $self->_spec_string(shift);
178 19         152 return $self;
179             }
180              
181             sub filters() {
182 20     20 1 15648 (my ($self), @_) = find_my_self(@_);
183 20 100       132 if (ref($_[0]) eq 'HASH') {
184 5         169 $self->_filters_map(shift);
185             }
186             else {
187 15         677 my $filters = $self->_filters;
188 15         329 push @$filters, @_;
189             }
190 20         129 return $self;
191             }
192              
193             sub filter_arguments() {
194 2     2 1 14 $Test::Chunks::Filter::arguments;
195             }
196              
197 4     4 0 11 sub have_text_diff {
198 4         23 eval { require Text::Diff; 1 };
  4         1048  
  4         12020  
199             }
200              
201             sub is($$;$) {
202 155     155 0 2930 (my ($self), @_) = find_my_self(@_);
203 155         524 my ($actual, $expected, $name) = @_;
204 155         321 local $Test::Builder::Level = $Test::Builder::Level + 1;
205 155 100 66     1365 if ($ENV{TEST_SHOW_NO_DIFFS} or
      66        
      100        
206             $actual eq $expected or
207             not($self->have_text_diff) or
208             $expected !~ /\n./s
209             ) {
210 152         557 Test::More::is($actual, $expected, $name);
211             }
212             else {
213 3 50       11 $name = '' unless defined $name;
214 3         47 ok $actual eq $expected,
215             $name . "\n" . Text::Diff::diff(\$actual, \$expected);
216             }
217             }
218              
219             sub run(&) {
220 14     14 1 3030 (my ($self), @_) = find_my_self(@_);
221 14         38 my $callback = shift;
222 14         35 for my $chunk (@{$self->chunk_list}) {
  14         596  
223 25 100       15223 $chunk->run_filters unless $chunk->is_filtered;
224 24         183 &{$callback}($chunk);
  24         94  
225             }
226             }
227              
228             sub run_is() {
229 19     19 1 1723 (my ($self), @_) = find_my_self(@_);
230 19         323 my ($x, $y) = @_;
231 19         46 local $Test::Builder::Level = $Test::Builder::Level + 1;
232 19         43 for my $chunk (@{$self->chunk_list}) {
  19         631  
233 39 100 100     17466 next unless exists($chunk->{$x}) and exists($chunk->{$y});
234 31 100       959 $chunk->run_filters unless $chunk->is_filtered;
235 31 100       290 is($chunk->$x, $chunk->$y,
236             $chunk->name ? $chunk->name : ()
237             );
238             }
239             }
240              
241             sub run_is_deeply() {
242 2     2 1 8 (my ($self), @_) = find_my_self(@_);
243 2         6 my ($x, $y) = @_;
244 2         4 for my $chunk (@{$self->chunk_list}) {
  2         49  
245 2 50 33     27 next unless exists($chunk->{$x}) and exists($chunk->{$y});
246 2 50       48 $chunk->run_filters unless $chunk->is_filtered;
247 1 50       9 is_deeply($chunk->$x, $chunk->$y,
248             $chunk->name ? $chunk->name : ()
249             );
250             }
251             }
252              
253             sub run_like() {
254 6     6 1 23 (my ($self), @_) = find_my_self(@_);
255 6         110 my ($x, $y) = @_;
256 6         10 for my $chunk (@{$self->chunk_list}) {
  6         269  
257 10 100 66     1559 next unless exists($chunk->{$x}) and defined($y);
258 9 100       292 $chunk->run_filters unless $chunk->is_filtered;
259 9 100       86 my $regexp = ref $y ? $y : $chunk->$y;
260 9 100       167 like($chunk->$x, $regexp,
261             $chunk->name ? $chunk->name : ()
262             );
263             }
264             }
265              
266             sub run_unlike() {
267 1     1 1 5 (my ($self), @_) = find_my_self(@_);
268 1         43 my ($x, $y) = @_;
269 1         3 for my $chunk (@{$self->chunk_list}) {
  1         28  
270 1 50 33     16 next unless exists($chunk->{$x}) and defined($y);
271 1 50       27 $chunk->run_filters unless $chunk->is_filtered;
272 1 50       11 my $regexp = ref $y ? $y : $chunk->$y;
273 1 50       4 unlike($chunk->$x, $regexp,
274             $chunk->name ? $chunk->name : ()
275             );
276             }
277             }
278              
279 74     74   193 sub _pre_eval {
280 74         270 my $spec = shift;
281 74 100       763 return $spec unless $spec =~
282             s/\A\s*<<<(.*?)>>>\s*$//sm;
283 1         2 my $eval_code = $1;
284 1         115 eval "package main; $eval_code";
285 1 50       6 croak $@ if $@;
286 1         3 return $spec;
287             }
288              
289 74     74   738 sub _chunk_list_init {
290 74         1797 my $spec = $self->spec;
291 74         906 $spec = $self->_pre_eval($spec);
292 74         2058 my $cd = $self->chunk_delim;
293 74         7132 my @hunks = ($spec =~ /^(\Q${cd}\E.*?(?=^\Q${cd}\E|\z))/msg);
294 74         461 my $chunks = $self->_choose_chunks(@hunks);
295 61         1898 $self->chunk_list($chunks); # Need to set early for possible filter use
296 61         718 my $seq = 1;
297 61         274 for my $chunk (@$chunks) {
298 119         4002 $chunk->chunks_object($self);
299 119         4395 $chunk->seq_num($seq++);
300             }
301 61         1232 return $chunks;
302             }
303              
304 74     74   283 sub _choose_chunks {
305 74         233 my $chunks = [];
306 74         243 for my $hunk (@_) {
307 136         562 my $chunk = $self->_make_chunk($hunk);
308 123 100       453 if (exists $chunk->{ONLY}) {
309 2         33 return [$chunk];
310             }
311 121 100       506 next if exists $chunk->{SKIP};
312 119         242 push @$chunks, $chunk;
313 119 100       582 if (exists $chunk->{LAST}) {
314 1         5 return $chunks;
315             }
316             }
317 58         234 return $chunks;
318             }
319              
320 208     208   301 sub _check_reserved {
321 208         327 my $id = shift;
322 208 100 66     3586 croak "'$id' is a reserved name. Use something else.\n"
323             if $reserved_section_names->{$id} or
324             $id =~ /^_/;
325             }
326              
327 136     136   274 sub _make_chunk {
328 136         260 my $hunk = shift;
329 136         3900 my $cd = $self->chunk_delim;
330 136         4044 my $dd = $self->data_delim;
331 136         4010 my $chunk = $self->chunk_class->new;
332 136 50       5354 $hunk =~ s/\A\Q${cd}\E[ \t]*(.*)\s+// or die;
333 136         396 my $name = $1;
334 136         2534 my @parts = split /^\Q${dd}\E +(\w+) *(.*)?\n/m, $hunk;
335 136         391 my $description = shift @parts;
336 136   100     752 $description ||= '';
337 136 100       444 unless ($description =~ /\S/) {
338 133         232 $description = $name;
339             }
340 136         634 $description =~ s/\s*\z//;
341 136         477 $chunk->set_value(description => $description);
342            
343 136         244 my $section_map = {};
344 136         349 my $section_order = [];
345 136         590 while (@parts) {
346 208         657 my ($type, $filters, $value) = splice(@parts, 0, 3);
347 208         3470 $self->_check_reserved($type);
348 195 100       659 $value = '' unless defined $value;
349 195         1050 $section_map->{$type} = {
350             filters => $filters,
351             };
352 195         488 push @$section_order, $type;
353 195         515 $chunk->set_value($type, $value);
354             }
355 123         388 $chunk->set_value(name => $name);
356 123         337 $chunk->set_value(_section_map => $section_map);
357 123         363 $chunk->set_value(_section_order => $section_order);
358 123         418 return $chunk;
359             }
360              
361 74     74   726 sub _spec_init {
362 74 100       3466 return $self->_spec_string
363             if $self->_spec_string;
364 55         571 local $/;
365 55         115 my $spec;
366 55 100       1462 if (my $spec_file = $self->_spec_file) {
367 3 50       233 open FILE, $spec_file or die $!;
368 3         128 $spec = ;
369 3         35 close FILE;
370             }
371             else {
372 52         415 $spec = do {
373             package main;
374 67     67   755 no warnings 'once';
  67         221  
  67         57381  
375 52         1869 ;
376             };
377             }
378 55         422 return $spec;
379             }
380              
381             # XXX Copied from Spiffy. Refactor at some point.
382             sub _strict_warnings() {
383 72     72   621 require Filter::Util::Call;
384 72         154 my $done = 0;
385             Filter::Util::Call::filter_add(
386             sub {
387 86 100   86   96222 return 0 if $done;
388 71         324 my ($data, $end) = ('', '');
389 71         2173 while (my $status = Filter::Util::Call::filter_read()) {
390 1107 50       2554 return $status if $status < 0;
391 1107 100       2660 if (/^__(?:END|DATA)__\r?$/) {
392 56         126 $end = $_;
393 56         135 last;
394             }
395 1051         13932 $data .= $_;
396 1051         3656 $_ = '';
397             }
398 71         283 $_ = "use strict;use warnings;$data$end";
399 71         2498 $done = 1;
400             }
401 72         890 );
402             }
403              
404             sub tie_output() {
405 4     4 1 22 my $handle = shift;
406 4 50       17 die "No buffer to tie" unless @_;
407 4         1130 tie $handle, 'Test::Chunks::Handle', $_[0];
408             }
409              
410             package Test::Chunks::Handle;
411              
412             sub TIEHANDLE() {
413 0     0   0 my $class = shift;
414 0         0 bless \ $_[0], $class;
415             }
416              
417 0     0   0 sub PRINT {
418 0         0 $$self .= $_ for @_;
419             }
420              
421             #===============================================================================
422             # Test::Chunks::Chunk
423             #
424             # This is the default class for accessing a Test::Chunks chunk object.
425             #===============================================================================
426             package Test::Chunks::Chunk;
427             our @ISA = qw(Spiffy);
428              
429             our @EXPORT = qw(chunk_accessor);
430              
431             sub chunk_accessor() {
432 364     364   2004 my $accessor = shift;
433 67     67   457 no strict 'refs';
  67         175  
  67         18103  
434 364 50       1796 return if defined &$accessor;
435             *$accessor = sub {
436 700     700   17667 my $self = shift;
437 700 50       1561 if (@_) {
438 0         0 Carp::croak "Not allowed to set values for '$accessor'";
439             }
440 700 50       976 my @list = @{$self->{$accessor} || []};
  700         2512  
441             return wantarray
442 700 100       15032 ? (@list)
443             : $list[0];
444 364         2974 };
445             }
446              
447             chunk_accessor 'name';
448             chunk_accessor 'description';
449             Spiffy::field 'seq_num';
450             Spiffy::field 'is_filtered';
451             Spiffy::field 'chunks_object';
452             Spiffy::field 'original_values' => {};
453              
454 1222     1222   1808 sub set_value {
455 67     67   462 no strict 'refs';
  67         154  
  67         19662  
456 1222         1600 my $accessor = shift;
457 1222 100       5146 chunk_accessor $accessor
458             unless defined &$accessor;
459 1222         6728 $self->{$accessor} = [@_];
460             }
461              
462 114     114   1775 sub run_filters {
463 114         539 my $map = $self->_section_map;
464 114         299 my $order = $self->_section_order;
465 114 50       3014 Carp::croak "Attempt to filter a chunk twice"
466             if $self->is_filtered;
467 114         913 for my $type (@$order) {
468 180         463 my $filters = $map->{$type}{filters};
469 180         738 my @value = $self->$type;
470 180         4986 $self->original_values->{$type} = $value[0];
471 180         1969 for my $filter ($self->_get_filters($type, $filters)) {
472 526 100       1944 $Test::Chunks::Filter::arguments =
473             $filter =~ s/=(.*)$// ? $1 : undef;
474 526         934 my $function = "main::$filter";
475 67     67   1390 no strict 'refs';
  67         124  
  67         50579  
476 526 100       3034 if (defined &$function) {
477 36         198 $_ = join '', @value;
478 36         259 @value = &$function(@value);
479 36 100 66     10846 if (not(@value) or
      66        
480             @value == 1 and $value[0] =~ /\A(\d+|)\z/
481             ) {
482 16         47 @value = ($_);
483             }
484             }
485             else {
486 490         12613 my $filter_object = $self->chunks_object->filter_class->new;
487 490 100       11047 die "Can't find a function or method for '$filter' filter\n"
488             unless $filter_object->can($filter);
489 489         14563 $filter_object->chunk($self);
490 489         4264 @value = $filter_object->$filter(@value);
491             }
492             # Set the value after each filter since other filters may be
493             # introspecting.
494 522         7845 $self->set_value($type, @value);
495             }
496             }
497 110         3131 $self->is_filtered(1);
498             }
499              
500 180     180   1074 sub _get_filters {
501 180         281 my $type = shift;
502 180   100     901 my $string = shift || '';
503 180         874 $string =~ s/\s*(.*?)\s*/$1/;
504 180         396 my @filters = ();
505 180   100     7569 my $map_filters = $self->chunks_object->_filters_map->{$type} || [];
506 180 100       8194 $map_filters = [ $map_filters ] unless ref $map_filters;
507 180         1020 my @append = ();
508 180         325 for (
509 180         4992 @{$self->chunks_object->_filters},
510             @$map_filters,
511             split(/\s+/, $string),
512             ) {
513 551         7268 my $filter = $_;
514 551 50       1377 last unless length $filter;
515 551 100       2396 if ($filter =~ s/^-//) {
    100          
516 2         4 @filters = grep { $_ ne $filter } @filters;
  5         13  
517             }
518             elsif ($filter =~ s/^\+//) {
519 2         5 push @append, $filter;
520             }
521             else {
522 547         851 @filters = grep { $_ ne $filter } @filters;
  637         1823  
523 547         1651 push @filters, $filter;
524             }
525             }
526 180         704 return @filters, @append;
527             }
528              
529             {
530             %$reserved_section_names = map {
531             ($_, 1);
532             } keys(%Test::Chunks::Chunk::), qw( new DESTROY );
533             }
534              
535             #===============================================================================
536             # Test::Chunks::Filter
537             #
538             # This is the default class for handling Test::Chunks data filtering.
539             #===============================================================================
540             package Test::Chunks::Filter;
541 67     67   448 use Spiffy -base;
  67         118  
  67         1097  
542              
543             field 'chunk';
544              
545             our $arguments;
546 17     17   29 sub arguments {
547 17 100       68 return undef unless defined $arguments;
548 10         18 my $args = $arguments;
549 10         41 $args =~ s/(\\[a-z])/'"' . $1 . '"'/gee;
  5         303  
550 10         43 return $args;
551             }
552              
553 223     223   385 sub assert_scalar {
554 223 50       894 return if @_ == 1;
555 0         0 require Carp;
556 0         0 my $filter = (caller(1))[3];
557 0         0 $filter =~ s/.*:://;
558 0         0 Carp::croak "Input to the '$filter' filter must be a scalar, not a list";
559             }
560              
561 180     180   660 sub norm {
562 180         647 $self->assert_scalar(@_);
563 180   100     533 my $text = shift || '';
564 180         376 $text =~ s/\015\012/\n/g;
565 180         287 $text =~ s/\r/\n/g;
566 180         1121 return $text;
567             }
568              
569 54     54   96 sub chomp {
570 54         104 map { CORE::chomp; $_ } @_;
  74         178  
  74         528  
571             }
572              
573 1     1   2 sub unchomp {
574 1         3 map { $_ . "\n" } @_;
  3         13  
575             }
576              
577 3     3   6 sub chop {
578 3         8 map { CORE::chop; $_ } @_;
  7         12  
  7         26  
579             }
580              
581 3     3   3 sub append {
582 3         9 my $suffix = $self->arguments;
583 3         6 map { $_ . $suffix } @_;
  7         23  
584             }
585              
586 179     179   323 sub trim {
587 179         1113 map {
588 179         353 s/\A([ \t]*\n)+//;
589 179         1534 s/(?<=\n)\s*\z//g;
590 179         1052 $_;
591             } @_;
592             }
593              
594 1     1   2 sub base64_decode {
595 1         3 $self->assert_scalar(@_);
596 1         1414 require MIME::Base64;
597 1         1558 MIME::Base64::decode_base64(shift);
598             }
599              
600 1     1   3 sub base64_encode {
601 1         3 $self->assert_scalar(@_);
602 1         7 require MIME::Base64;
603 1         7 MIME::Base64::encode_base64(shift);
604             }
605              
606 2     2   3 sub escape {
607 2         4 $self->assert_scalar(@_);
608 2         3 my $text = shift;
609 2         9 $text =~ s/(\\.)/eval "qq{$1}"/ge;
  4         196  
610 2         10 return $text;
611             }
612              
613 12     12   19 sub eval {
614 12         32 $self->assert_scalar(@_);
615 12         1029 my @return = CORE::eval(shift);
616 12 50       50 return $@ if $@;
617 12         45 return @return;
618             }
619              
620 1     1   2 sub eval_stdout {
621 1         4 $self->assert_scalar(@_);
622 1         2 my $output = '';
623 1         5 Test::Chunks::tie_output(*STDOUT, $output);
624 0         0 CORE::eval(shift);
625 67     67   106897 no warnings;
  67         162  
  67         9277  
626 0         0 untie *STDOUT;
627 0         0 return $output;
628             }
629              
630 1     1   2 sub eval_stderr {
631 1         4 $self->assert_scalar(@_);
632 1         2 my $output = '';
633 1         5 Test::Chunks::tie_output(*STDERR, $output);
634 0         0 CORE::eval(shift);
635 67     67   380 no warnings;
  67         170  
  67         8940  
636 0         0 untie *STDERR;
637 0         0 return $output;
638             }
639              
640 1     1   2 sub eval_all {
641 1         4 $self->assert_scalar(@_);
642 1         1 my $out = '';
643 1         3 my $err = '';
644 1         14 Test::Chunks::tie_output(*STDOUT, $out);
645 0         0 Test::Chunks::tie_output(*STDERR, $err);
646 0         0 my $return = CORE::eval(shift);
647 67     67   392 no warnings;
  67         140  
  67         41932  
648 0         0 untie *STDOUT;
649 0         0 untie *STDERR;
650 0         0 return $return, $@, $out, $err;
651             }
652              
653 3     3   4 sub exec_perl_stdout {
654 3         25 my $tmpfile = "/tmp/test-chunks-$$";
655 3         12 $self->_write_to($tmpfile, @_);
656 3 50       15060 open my $execution, "$^X $tmpfile 2>&1 |"
657             or die "Couldn't open subprocess: $!\n";
658 3         101 local $/;
659 3         398219 my $output = <$execution>;
660 3         314 close $execution;
661 3 50       685 unlink($tmpfile)
662             or die "Couldn't unlink $tmpfile: $!\n";
663 3         241 return $output;
664             }
665              
666 3     3   7 sub _write_to {
667 3         11 my $filename = shift;
668 3 50       777 open my $script, ">$filename"
669             or die "Couldn't open $filename: $!\n";
670 3         106 print $script @_;
671 3 50       250 close $script
672             or die "Couldn't close $filename: $!\n";
673             }
674              
675 0     0   0 sub yaml {
676 0         0 $self->assert_scalar(@_);
677 0         0 require YAML;
678 0         0 return YAML::Load(shift);
679             }
680              
681 17     17   29 sub lines {
682 17         48 $self->assert_scalar(@_);
683 17         33 my $text = shift;
684 17 100       79 return () unless length $text;
685 16         126 my @lines = ($text =~ /^(.*\n?)/gm);
686 16         86 return @lines;
687             }
688              
689 6     6   14 sub array {
690 6         28 [@_];
691             }
692              
693 7     7   9 sub join {
694 7         24 my $string = $self->arguments;
695 7 100       20 $string = '' unless defined $string;
696 7         40 CORE::join $string, @_;
697             }
698              
699 2     2   3 sub dumper {
700 67     67   438 no warnings 'once';
  67         124  
  67         37419  
701 2         14 require Data::Dumper;
702 2         7 local $Data::Dumper::Sortkeys = 1;
703 2         4 local $Data::Dumper::Indent = 1;
704 2         6 local $Data::Dumper::Terse = 1;
705 2         8 Data::Dumper::Dumper(@_);
706             }
707              
708 1     1   2 sub strict {
709 1         3 $self->assert_scalar(@_);
710 1         5 <<'...' . shift;
711             use strict;
712             use warnings;
713             ...
714             }
715              
716 6     6   12 sub regexp {
717 6         12 $self->assert_scalar(@_);
718 6         8 my $text = shift;
719 6         19 my $flags = $self->arguments;
720 6 100       44 if ($text =~ /\n.*?\n/s) {
721 4 100       10 $flags = 'xism'
722             unless defined $flags;
723             }
724             else {
725 2         8 CORE::chomp($text);
726             }
727 6   100     24 $flags ||= '';
728 6         523 my $regexp = eval "qr{$text}$flags";
729 6 50       25 die $@ if $@;
730 6         24 return $regexp;
731             }
732              
733 0     0     sub get_url {
734 0           $self->assert_scalar(@_);
735 0           my $url = shift;
736 0           CORE::chomp($url);
737 0           require LWP::Simple;
738 0           LWP::Simple::get($url);
739             }
740            
741             __DATA__