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   975222 use strict;
  4         28  
  4         125  
3 4     4   25 use warnings;
  4         8  
  4         166  
4              
5             our $VERSION = '0.000013';
6              
7 4     4   22 use Carp qw/croak/;
  4         7  
  4         231  
8 4     4   31 use Scalar::Util qw/blessed reftype/;
  4         8  
  4         243  
9              
10             our @EXPORT = qw/dump_event dump_events/;
11 4     4   22 use base 'Exporter';
  4         10  
  4         11536  
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, start_stamp => 1, stop_stamp => 1},
46              
47             indent_sequence => ' ',
48              
49             adjust_filename => \&adjust_filename,
50             );
51              
52             sub adjust_filename {
53 30729     30729 1 152579 my $file = shift;
54 30729         164764 $file =~ s{^.*[/\\]}{}g;
55 30729         90390 return "match qr{\\Q$file\\E\$}";
56             }
57              
58             sub dump_event {
59 12305     12305 1 26922884 my ($event, %settings) = @_;
60              
61 12305 100       46082 croak "No event to dump"
62             unless $event;
63              
64 12304 100 66     106214 croak "dump_event() requires a Test2::Event (or subclass) instance, Got: $event"
65             unless blessed($event) && $event->isa('Test2::Event');
66              
67 12303 50       60972 my $settings = keys %settings ? parse_settings(\%settings) : \%DEFAULTS;
68              
69 12303         35094 my $out = do_event_dump($event, $settings);
70              
71 12303         36021 return finalize($out, $settings);
72             }
73              
74             sub dump_events {
75 12301     12301 1 209393502 my ($events, %settings) = @_;
76              
77 12301 100       55584 croak "No events to dump"
78             unless $events;
79              
80 12300 100       67881 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     35089 if grep { !$_ || !blessed($_) || !$_->isa('Test2::Event') } @$events;
  86044         559361  
85              
86 12298 100       68017 my $settings = keys %settings ? parse_settings(\%settings) : \%DEFAULTS;
87              
88 12298         44379 my $out = do_array_dump($events, $settings);
89              
90 12298         42187 return finalize($out, $settings);
91             }
92              
93             sub finalize {
94 24602     24602 0 68595 my ($out, $settings) = @_;
95              
96 24602         249676 $out =~ s[(\s+)$][join '' => grep { $_ eq "\n" } split //, $1]msge;
  172113         602975  
  715548         2298734  
97              
98 24602 100       118395 if ($settings->{add_line_numbers}) {
99 12290         24132 my $line = 1;
100 12290         194156 my $count = length( 0 + map { 1 } split /\n/, $out );
  529199         738791  
101 12290         75556 $out =~ s/^/sprintf("L%0${count}i: ", $line++)/gmse;
  529199         1591795  
102 12290         275830 $out =~ s/^L\d+: $//gms;
103             }
104              
105 24602         238743 return $out;
106             }
107              
108             sub parse_settings {
109 24604     24604 0 62919 my $settings = shift;
110              
111 24604         46206 my %out;
112 24604         126463 my %clone = %$settings;
113              
114 24604         80608 for my $field (qw/field_order include_fields exclude_fields/) {
115 73812 100       188163 next unless exists $clone{$field}; # Nothing to do.
116 24652 100       68843 next unless defined $clone{$field}; # Do not modify an undef
117              
118             # Remove it from the clone
119 24629         56274 my $order = delete $clone{$field};
120              
121 24629 50       170155 croak "settings field '$field' must be either an arrayref or hashref, got: $order"
122             unless ref($order) =~ m/^(ARRAY|HASH)$/;
123              
124 24629         44824 my $count = 1;
125 24629 100       93073 $out{$field} = ref($order) eq 'HASH' ? $order : {map { $_ => $count++ } @$order};
  14         46  
126             }
127              
128             return {
129 24604         387622 %DEFAULTS,
130             %clone,
131             %out,
132             };
133             }
134              
135             sub do_event_dump {
136 135229     135229 0 269362 my ($event, $settings) = @_;
137              
138 135229 100 100     540827 my ($ps, $pe) = ($settings->{qualify_functions} || $settings->{paren_functions}) ? ('(', ')') : (' ', '');
139 135229 100       297502 my $qf = $settings->{qualify_functions} ? "Test2::Tools::Compare::" : "";
140              
141 135229         379262 my $start = "${qf}event${ps}" . render_event($event, $settings);
142              
143 135229         353148 my @fields = get_fields($event, $settings);
144              
145 135229         283785 my @rows = map { get_rows($event, $_, $settings) } @fields;
  497884         1173538  
146 135229   33     531889 shift @rows while @rows && !@{$rows[0]}; # Strip leading empty rows
  135229         433999  
147              
148 135229         273159 my $nest = "";
149 135229 50 100     520401 if (@rows == 0) {
    100 66        
150 0         0 $start .= " => {";
151             }
152             elsif (@rows == 1 && $settings->{shorten_single_field} && !$rows[0]->[3]) {
153 2308         5133 $start .= " => {";
154 2308         5336 my ($row) = @rows;
155 2308         6072 $nest = quote_key($row->[1]) . " => $row->[2]";
156             }
157             else {
158 132921         243369 $start .= " => sub {\n";
159              
160 132921         268635 for my $row (@rows) {
161 630852 100       1165796 unless (@$row) {
162 73787         110936 $nest .= "\n";
163 73787         135522 next;
164             }
165              
166 557065         1125262 my ($func, $field, $qval, $comment) = @$row;
167 557065         905025 my $key = quote_key($field);
168 557065         1669383 $nest .= "${qf}${func}${ps}${key} => ${qval}${pe};";
169 557065 100       1071535 $nest .= " # $comment" if $comment;
170 557065         1012123 $nest .= "\n";
171             }
172              
173 132921         1682185 $nest =~ s/^/$settings->{indent_sequence}/mg;
174             }
175              
176 135229         948692 return "${start}${nest}}${pe}";
177             }
178              
179             sub do_array_dump {
180 24596     24596 0 60675 my ($array, $settings) = @_;
181              
182 24596 100 100     133461 my ($ps, $pe) = ($settings->{qualify_functions} || $settings->{paren_functions}) ? ('(sub ', ')') : (' ', '');
183 24596 100       70978 my $qf = $settings->{qualify_functions} ? "Test2::Tools::Compare::" : "";
184              
185 24596         71500 my $out = "${qf}array${ps}\{\n";
186              
187 24596         58255 my $nest = "";
188 24596         51322 my $not_first = 0;
189 24596         63427 for my $event (@$array) {
190 122926 100       306052 $nest .= "\n" if $not_first++;
191 122926         257973 $nest .= do_event_dump($event, $settings) . ";\n"
192             }
193 24596         84778 $nest .= "${qf}end();\n";
194 24596         1598098 $nest =~ s/^/$settings->{indent_sequence}/mg;
195              
196 24596         77609 $out .= $nest;
197 24596         55703 $out .= "}${pe}";
198              
199 24596         145154 return $out;
200             }
201              
202             sub quote_val {
203 282780     282780 0 549156 my ($val, $settings) = @_;
204              
205 282780 100       563205 return 'undef' unless defined $val;
206              
207 270490 100       1125030 return $val if $val =~ m/^\d+$/;
208              
209             return 'match qr{^\\n?Failed test}'
210 122935 100 100     417942 if $settings->{clean_fail_messages} && $val =~ m/^\n?Failed test/;
211              
212 116787         241540 return quote_str(@_);
213             }
214              
215             sub quote_key {
216 694608     694608 0 1176072 my ($val, $settings) = @_;
217              
218 694608 100       1698869 return $val if $val =~ m/^\d+$/;
219 694607 100       2102236 return $val if $val =~ m/^\w+$/;
220              
221 67592         152838 return quote_str(@_);
222             }
223              
224             sub quote_str {
225 184388     184388 0 366935 my ($val, $settings) = @_;
226              
227 184388         302400 my $use_qq = 0;
228 184388 100       519306 $use_qq = 1 if $val =~ s/\n/\\n/g;
229 184388 100       387030 $use_qq = 1 if $val =~ s/\r/\\r/g;
230 184388 100       386965 $use_qq = 1 if $val =~ s/[\b]/\\b/g;
231              
232 184388         350320 my @delims = ('"', grep {$QUOTE_MATCH{$_}} qw<{ ( [ />);
  737552         1506532  
233 184388 100       549454 unshift @delims => "'" unless $use_qq;
234 184388         322372 my ($s1) = grep { $val !~ m/\Q$_\E/ } @delims;
  1100181         8131341  
235              
236 184388 100       467776 unless($s1) {
237 12290         24239 $s1 = $delims[0];
238 12290         65175 $val =~ s/$s1/\\$s1/g;
239             }
240              
241 184388   66     636363 my $s2 = $QUOTE_MATCH{$s1} || $s1;
242              
243 184388 100       419870 $use_qq = 0 if $s1 eq '"';
244              
245 184388 100 66     653490 my $qq = ($QUOTE_MATCH{$s1} || $use_qq) ? 'qq' : '';
246              
247 184388         913089 return "${qq}${s1}${val}${s2}";
248             }
249              
250             sub render_event {
251 135232     135232 0 235167 my ($event, $settings) = @_;
252 135232         412526 my $type = blessed($event);
253              
254             return quote_key("+$type", $settings)
255             if $settings->{use_full_event_type}
256 135232 100 100     704885 || $type !~ m/^Test2::Event::(.+)$/;
257              
258 67641         174726 return quote_key($1, $settings);
259             }
260              
261             sub get_fields {
262 135229     135229 0 264028 my ($event, $settings) = @_;
263              
264 135229         469061 my @fields = grep { $_ !~ m/^_/ } keys %$event;
  762225         1731689  
265              
266 67589         188173 push @fields => keys %{$settings->{include_fields}}
267 135229 100       412845 if $settings->{include_fields};
268              
269 135229         239405 my %seen;
270 135229   50     320247 my $exclude = $settings->{exclude_fields} || {};
271 135229   100     252220 @fields = grep { !$seen{$_}++ && !$exclude->{$_} } @fields;
  694589         2462572  
272              
273 264404 100 100     1138942 @fields = grep { exists $event->{$_} && defined $event->{$_} && length $event->{$_} } @fields
274 135229 100       340229 unless $settings->{show_empty};
275              
276             return sort {
277 135229         472549 my $a_has_array = ref($event->{$a}) eq 'ARRAY';
  551682         1019041  
278 551682         918025 my $b_has_array = ref($event->{$b}) eq 'ARRAY';
279              
280 551682 100 66     1284801 my $av = $a_has_array ? $settings->{array_sort_order} : ($settings->{field_order}->{$a} || $settings->{other_sort_order});
281 551682 100 66     1230939 my $bv = $b_has_array ? $settings->{array_sort_order} : ($settings->{field_order}->{$b} || $settings->{other_sort_order});
282              
283 551682   66     1586606 return $av <=> $bv || $a cmp $b;
284             } @fields;
285             }
286              
287             sub get_rows {
288 497884     497884 0 904703 my ($event, $field, $settings) = @_;
289              
290             return ['field', $field, 'DNE()']
291 497884 100       1068606 unless exists $event->{$field};
292              
293 485591         734324 my ($func, $val);
294 485591 100 66     1691892 if ($settings->{call_when_can} && $event->can($field)) {
295 242884         409071 $func = 'call';
296 242884         672622 $val = $event->$field;
297             }
298             else {
299 242707         352163 $func = 'field';
300 242707         412465 $val = $event->{$field};
301             }
302              
303 485591 50 100     2506550 if ($settings->{convert_trace} && $field eq 'trace' && blessed($val) && ($val->isa('Test2::Util::Trace') || $val->isa('Test2::EventFacet::Trace'))) {
      66        
      33        
      66        
304 61489         207065 my $file = $settings->{adjust_filename}->($val->file);
305             return (
306 61489         373771 [],
307             [ 'prop', 'file', $file ],
308             [ 'prop', 'line', $val->line ],
309             );
310             }
311              
312 424102         718193 my $ref = ref $val;
313              
314 424102 100       931141 return [ $func, $field, quote_val($val, $settings) ]
315             unless $ref;
316              
317             return ( [], [ $func, $field, do_array_dump($val, $settings) ] )
318 141327 100 66     377585 if $ref eq 'ARRAY' && !grep { !blessed($_) || !$_->isa('Test2::Event') } @$val;
  104469   100     610899  
319              
320 129029   66     767211 return [ $func, $field, 'T()', "Unknown value: " . (blessed($val) || $ref) ];
321             }
322              
323             __END__