File Coverage

blib/lib/Test2/Tools/EventDumper.pm
Criterion Covered Total %
statement 159 160 99.3
branch 84 88 95.4
condition 56 74 75.6
subroutine 18 18 100.0
pod 3 13 23.0
total 320 353 90.6


line stmt bran cond sub pod time code
1             package Test2::Tools::EventDumper;
2 4     4   948503 use strict;
  4         27  
  4         117  
3 4     4   21 use warnings;
  4         9  
  4         164  
4              
5             our $VERSION = '0.000012';
6              
7 4     4   22 use Carp qw/croak/;
  4         9  
  4         180  
8 4     4   23 use Scalar::Util qw/blessed reftype/;
  4         8  
  4         231  
9              
10             our @EXPORT = qw/dump_event dump_events/;
11 4     4   26 use base 'Exporter';
  4         28  
  4         11000  
12              
13             my %QUOTE_MATCH = (
14             '{' => '}',
15             '(' => ')',
16             '[' => ']',
17             '/' => '/',
18             );
19              
20             my %DEFAULTS = (
21             qualify_functions => 0,
22             paren_functions => 0,
23             use_full_event_type => 0,
24             show_empty => 0,
25             add_line_numbers => 0,
26             call_when_can => 1,
27             convert_trace => 1,
28             shorten_single_field => 1,
29             clean_fail_messages => 1,
30              
31             field_order => {
32             name => 1,
33             pass => 2,
34             effective_pass => 3,
35             todo => 4,
36             max => 5,
37             directive => 6,
38             reason => 7,
39             trace => 9999,
40             },
41             array_sort_order => 10000,
42             other_sort_order => 9000,
43              
44             include_fields => undef,
45             exclude_fields => {buffered => 1, nested => 1, in_subtest => 1, is_subtest => 1, subtest_id => 1, hubs => 1},
46              
47             indent_sequence => ' ',
48              
49             adjust_filename => \&adjust_filename,
50             );
51              
52             sub adjust_filename {
53 30729     30729 1 150091 my $file = shift;
54 30729         149407 $file =~ s{^.*[/\\]}{}g;
55 30729         91362 return "match qr{\\Q$file\\E\$}";
56             }
57              
58             sub dump_event {
59 12305     12305 1 24170958 my ($event, %settings) = @_;
60              
61 12305 100       38362 croak "No event to dump"
62             unless $event;
63              
64 12304 100 66     89840 croak "dump_event() requires a Test2::Event (or subclass) instance, Got: $event"
65             unless blessed($event) && $event->isa('Test2::Event');
66              
67 12303 50       49388 my $settings = keys %settings ? parse_settings(\%settings) : \%DEFAULTS;
68              
69 12303         26937 my $out = do_event_dump($event, $settings);
70              
71 12303         27409 return finalize($out, $settings);
72             }
73              
74             sub dump_events {
75 12301     12301 1 204115997 my ($events, %settings) = @_;
76              
77 12301 100       51805 croak "No events to dump"
78             unless $events;
79              
80 12300 100       67822 croak "dump_events() requires an array reference, Got: $events"
81             unless reftype($events) eq 'ARRAY';
82              
83             croak "dump_events() requires an array reference of Test2::Event (or subclass) instances, some array elements are not Test2::Event instances"
84 12299 100 66     34057 if grep { !$_ || !blessed($_) || !$_->isa('Test2::Event') } @$events;
  86044         524385  
85              
86 12298 100       60479 my $settings = keys %settings ? parse_settings(\%settings) : \%DEFAULTS;
87              
88 12298         44180 my $out = do_array_dump($events, $settings);
89              
90 12298         38309 return finalize($out, $settings);
91             }
92              
93             sub finalize {
94 24602     24602 0 61995 my ($out, $settings) = @_;
95              
96 24602         231623 $out =~ s[(\s+)$][join '' => grep { $_ eq "\n" } split //, $1]msge;
  172113         616992  
  715548         2292892  
97              
98 24602 100       119484 if ($settings->{add_line_numbers}) {
99 12290         23444 my $line = 1;
100 12290         196247 my $count = length( 0 + map { 1 } split /\n/, $out );
  529199         726419  
101 12290         69066 $out =~ s/^/sprintf("L%0${count}i: ", $line++)/gmse;
  529199         1716515  
102 12290         274211 $out =~ s/^L\d+: $//gms;
103             }
104              
105 24602         221532 return $out;
106             }
107              
108             sub parse_settings {
109 24604     24604 0 57664 my $settings = shift;
110              
111 24604         43201 my %out;
112 24604         128185 my %clone = %$settings;
113              
114 24604         70175 for my $field (qw/field_order include_fields exclude_fields/) {
115 73812 100       173276 next unless exists $clone{$field}; # Nothing to do.
116 24652 100       61424 next unless defined $clone{$field}; # Do not modify an undef
117              
118             # Remove it from the clone
119 24629         53064 my $order = delete $clone{$field};
120              
121 24629 50       151140 croak "settings field '$field' must be either an arrayref or hashref, got: $order"
122             unless ref($order) =~ m/^(ARRAY|HASH)$/;
123              
124 24629         46565 my $count = 1;
125 24629 100       81340 $out{$field} = ref($order) eq 'HASH' ? $order : {map { $_ => $count++ } @$order};
  14         43  
126             }
127              
128             return {
129 24604         353238 %DEFAULTS,
130             %clone,
131             %out,
132             };
133             }
134              
135             sub do_event_dump {
136 135229     135229 0 260777 my ($event, $settings) = @_;
137              
138 135229 100 100     493801 my ($ps, $pe) = ($settings->{qualify_functions} || $settings->{paren_functions}) ? ('(', ')') : (' ', '');
139 135229 100       297610 my $qf = $settings->{qualify_functions} ? "Test2::Tools::Compare::" : "";
140              
141 135229         345655 my $start = "${qf}event${ps}" . render_event($event, $settings);
142              
143 135229         350384 my @fields = get_fields($event, $settings);
144              
145 135229         257765 my @rows = map { get_rows($event, $_, $settings) } @fields;
  497884         1094837  
146 135229   33     480016 shift @rows while @rows && !@{$rows[0]}; # Strip leading empty rows
  135229         414911  
147              
148 135229         250904 my $nest = "";
149 135229 50 100     472622 if (@rows == 0) {
    100 66        
150 0         0 $start .= " => {";
151             }
152             elsif (@rows == 1 && $settings->{shorten_single_field} && !$rows[0]->[3]) {
153 2308         4780 $start .= " => {";
154 2308         4024 my ($row) = @rows;
155 2308         5419 $nest = quote_key($row->[1]) . " => $row->[2]";
156             }
157             else {
158 132921         216127 $start .= " => sub {\n";
159              
160 132921         244982 for my $row (@rows) {
161 630852 100       1172527 unless (@$row) {
162 73787         109744 $nest .= "\n";
163 73787         136998 next;
164             }
165              
166 557065         1098987 my ($func, $field, $qval, $comment) = @$row;
167 557065         898089 my $key = quote_key($field);
168 557065         1579581 $nest .= "${qf}${func}${ps}${key} => ${qval}${pe};";
169 557065 100       1089186 $nest .= " # $comment" if $comment;
170 557065         1059223 $nest .= "\n";
171             }
172              
173 132921         1643894 $nest =~ s/^/$settings->{indent_sequence}/mg;
174             }
175              
176 135229         962373 return "${start}${nest}}${pe}";
177             }
178              
179             sub do_array_dump {
180 24596     24596 0 55916 my ($array, $settings) = @_;
181              
182 24596 100 100     114781 my ($ps, $pe) = ($settings->{qualify_functions} || $settings->{paren_functions}) ? ('(sub ', ')') : (' ', '');
183 24596 100       64644 my $qf = $settings->{qualify_functions} ? "Test2::Tools::Compare::" : "";
184              
185 24596         67283 my $out = "${qf}array${ps}\{\n";
186              
187 24596         50295 my $nest = "";
188 24596         38560 my $not_first = 0;
189 24596         56062 for my $event (@$array) {
190 122926 100       312236 $nest .= "\n" if $not_first++;
191 122926         255458 $nest .= do_event_dump($event, $settings) . ";\n"
192             }
193 24596         74688 $nest .= "${qf}end();\n";
194 24596         1464753 $nest =~ s/^/$settings->{indent_sequence}/mg;
195              
196 24596         72018 $out .= $nest;
197 24596         47402 $out .= "}${pe}";
198              
199 24596         143691 return $out;
200             }
201              
202             sub quote_val {
203 282780     282780 0 515161 my ($val, $settings) = @_;
204              
205 282780 100       583207 return 'undef' unless defined $val;
206              
207 270490 100       1102880 return $val if $val =~ m/^\d+$/;
208              
209             return 'match qr{^\\n?Failed test}'
210 122935 100 100     428981 if $settings->{clean_fail_messages} && $val =~ m/^\n?Failed test/;
211              
212 116787         255406 return quote_str(@_);
213             }
214              
215             sub quote_key {
216 694608     694608 0 1249920 my ($val, $settings) = @_;
217              
218 694608 100       1671388 return $val if $val =~ m/^\d+$/;
219 694607 100       2009113 return $val if $val =~ m/^\w+$/;
220              
221 67592         143615 return quote_str(@_);
222             }
223              
224             sub quote_str {
225 184388     184388 0 350887 my ($val, $settings) = @_;
226              
227 184388         268151 my $use_qq = 0;
228 184388 100       508958 $use_qq = 1 if $val =~ s/\n/\\n/g;
229 184388 100       414641 $use_qq = 1 if $val =~ s/\r/\\r/g;
230 184388 100       379122 $use_qq = 1 if $val =~ s/[\b]/\\b/g;
231              
232 184388         328185 my @delims = ('"', grep {$QUOTE_MATCH{$_}} qw<{ ( [ />);
  737552         1427415  
233 184388 100       548550 unshift @delims => "'" unless $use_qq;
234 184388         322850 my ($s1) = grep { $val !~ m/\Q$_\E/ } @delims;
  1100181         8221189  
235              
236 184388 100       471404 unless($s1) {
237 12290         20621 $s1 = $delims[0];
238 12290         63426 $val =~ s/$s1/\\$s1/g;
239             }
240              
241 184388   66     632781 my $s2 = $QUOTE_MATCH{$s1} || $s1;
242              
243 184388 100       443897 $use_qq = 0 if $s1 eq '"';
244              
245 184388 100 66     639721 my $qq = ($QUOTE_MATCH{$s1} || $use_qq) ? 'qq' : '';
246              
247 184388         882975 return "${qq}${s1}${val}${s2}";
248             }
249              
250             sub render_event {
251 135232     135232 0 244727 my ($event, $settings) = @_;
252 135232         397026 my $type = blessed($event);
253              
254             return quote_key("+$type", $settings)
255             if $settings->{use_full_event_type}
256 135232 100 100     679297 || $type !~ m/^Test2::Event::(.+)$/;
257              
258 67641         161383 return quote_key($1, $settings);
259             }
260              
261             sub get_fields {
262 135229     135229 0 241944 my ($event, $settings) = @_;
263              
264 135229         427737 my @fields = grep { $_ !~ m/^_/ } keys %$event;
  762225         1689346  
265              
266 67589         173012 push @fields => keys %{$settings->{include_fields}}
267 135229 100       397455 if $settings->{include_fields};
268              
269 135229         216465 my %seen;
270 135229   50     326178 my $exclude = $settings->{exclude_fields} || {};
271 135229   100     226665 @fields = grep { !$seen{$_}++ && !$exclude->{$_} } @fields;
  694589         2343015  
272              
273 264404 100 100     1086303 @fields = grep { exists $event->{$_} && defined $event->{$_} && length $event->{$_} } @fields
274 135229 100       350291 unless $settings->{show_empty};
275              
276             return sort {
277 135229         417648 my $a_has_array = ref($event->{$a}) eq 'ARRAY';
  560901         969984  
278 560901         888012 my $b_has_array = ref($event->{$b}) eq 'ARRAY';
279              
280 560901 100 66     1309749 my $av = $a_has_array ? $settings->{array_sort_order} : ($settings->{field_order}->{$a} || $settings->{other_sort_order});
281 560901 100 66     1270582 my $bv = $b_has_array ? $settings->{array_sort_order} : ($settings->{field_order}->{$b} || $settings->{other_sort_order});
282              
283 560901   66     1618591 return $av <=> $bv || $a cmp $b;
284             } @fields;
285             }
286              
287             sub get_rows {
288 497884     497884 0 895841 my ($event, $field, $settings) = @_;
289              
290             return ['field', $field, 'DNE()']
291 497884 100       1082301 unless exists $event->{$field};
292              
293 485591         693565 my ($func, $val);
294 485591 100 66     1614203 if ($settings->{call_when_can} && $event->can($field)) {
295 242884         405870 $func = 'call';
296 242884         677815 $val = $event->$field;
297             }
298             else {
299 242707         378052 $func = 'field';
300 242707         389477 $val = $event->{$field};
301             }
302              
303 485591 50 100     2423006 if ($settings->{convert_trace} && $field eq 'trace' && blessed($val) && ($val->isa('Test2::Util::Trace') || $val->isa('Test2::EventFacet::Trace'))) {
      66        
      33        
      66        
304 61489         199945 my $file = $settings->{adjust_filename}->($val->file);
305             return (
306 61489         352928 [],
307             [ 'prop', 'file', $file ],
308             [ 'prop', 'line', $val->line ],
309             );
310             }
311              
312 424102         759134 my $ref = ref $val;
313              
314 424102 100       953341 return [ $func, $field, quote_val($val, $settings) ]
315             unless $ref;
316              
317             return ( [], [ $func, $field, do_array_dump($val, $settings) ] )
318 141327 100 66     382731 if $ref eq 'ARRAY' && !grep { !blessed($_) || !$_->isa('Test2::Event') } @$val;
  104469   100     549889  
319              
320 129029   66     733607 return [ $func, $field, 'T()', "Unknown value: " . (blessed($val) || $ref) ];
321             }
322              
323             __END__