File Coverage

blib/lib/Test2/API/InterceptResult/Event.pm
Criterion Covered Total %
statement 304 321 94.7
branch 174 212 82.0
condition 32 51 62.7
subroutine 82 88 93.1
pod 66 69 95.6
total 658 741 88.8


line stmt bran cond sub pod time code
1             package Test2::API::InterceptResult::Event;
2 35     35   1481 use strict;
  35         80  
  35         1105  
3 35     35   229 use warnings;
  35         76  
  35         1622  
4              
5             our $VERSION = '1.302181';
6              
7 35     35   369 use List::Util qw/first/;
  35         79  
  35         2005  
8 35     35   252 use Test2::Util qw/pkg_to_file/;
  35         85  
  35         1816  
9 35     35   255 use Scalar::Util qw/reftype blessed/;
  35         95  
  35         1997  
10              
11 35     35   1611 use Storable qw/dclone/;
  35         6815  
  35         1775  
12 35     35   283 use Carp qw/confess croak/;
  35         105  
  35         1991  
13              
14 35     35   16024 use Test2::API::InterceptResult::Facet;
  35         116  
  35         1089  
15 35     35   15363 use Test2::API::InterceptResult::Hub;
  35         102  
  35         1124  
16              
17 35         148 use Test2::Util::HashBase qw{
18             +causes_failure
19             <facet_data
20             <result_class
21 35     35   241 };
  35         73  
22              
23             my %FACETS;
24             BEGIN {
25 35     35   163 local $@;
26 35         118 local *plugins;
27 35 50       77 if (eval { require Module::Pluggable; 1 }) {
  35         18097  
  35         388183  
28             Module::Pluggable->import(
29             # We will replace the sub later
30             require => 1,
31 0         0 on_require_error => sub { 1 },
32 35         388 search_path => ['Test2::EventFacet'],
33             max_depth => 3,
34             min_depth => 3,
35             );
36              
37 35         2633 for my $facet_type (__PACKAGE__->plugins) {
38 420         320101 my ($key, $list);
39 420         598 eval {
40 420         3173 $key = $facet_type->facet_key;
41 420         1605 $list = $facet_type->is_list;
42             };
43 420 50 33     1467 next unless $key && defined($list);
44              
45 420         1810 $FACETS{$key} = {list => $list, class => $facet_type, loaded => 1};
46             }
47             }
48              
49 35         161367 $FACETS{__GENERIC__} = {class => 'Test2::API::InterceptResult::Facet', loaded => 1};
50             }
51              
52 1     1 0 18 sub facet_map { \%FACETS }
53              
54             sub facet_info {
55 813     813 0 1236 my $facet = pop;
56              
57 813 100       2048 return $FACETS{$facet} if exists $FACETS{$facet};
58              
59 3         14 my $mname = ucfirst(lc($facet));
60 3         12 $mname =~ s/s$//;
61              
62 3         8 for my $name ($mname, "${mname}s") {
63 6         17 my $file = "Test2/EventFacet/$name.pm";
64 6         17 my $class = "Test2::EventFacet::$name";
65              
66 6         11 local $@;
67 6         10 my $ok = eval {
68 6         1374 require $file;
69              
70 0         0 my $key = $class->facet_key;
71 0         0 my $list = $class->is_list;
72              
73 0         0 $FACETS{$key} = {list => $list, class => $class, loaded => 1};
74 0 0       0 $FACETS{$facet} = $FACETS{$key} if $facet ne $key;
75              
76 0         0 1;
77             };
78              
79 6 0 33     57 return $FACETS{$facet} if $ok && $FACETS{$facet};
80             }
81              
82 3         16 return $FACETS{$facet} = $FACETS{__GENERIC__};
83             }
84              
85             sub init {
86 118     118 0 211 my $self = shift;
87              
88 118   100     378 my $rc = $self->{+RESULT_CLASS} ||= 'Test2::API::InterceptResult';
89 118         298 my $rc_file = pkg_to_file($rc);
90 118 100       1674 require($rc_file) unless $INC{$rc_file};
91              
92 118   100     327 my $fd = $self->{+FACET_DATA} ||= {};
93              
94 118         406 for my $facet (keys %$fd) {
95 355         672 my $finfo = $self->facet_info($facet);
96 355         608 my $is_list = $finfo->{list};
97 355 100       651 next unless defined $is_list;
98              
99 349         756 my $type = reftype($fd->{$facet});
100              
101 349 100       660 if ($is_list) {
102 128 100       408 confess "Facet '$facet' is a list facet, but got '$type' instead of an arrayref"
103             unless $type eq 'ARRAY';
104              
105 127         179 for my $item (@{$fd->{$facet}}) {
  127         301  
106 176         322 my $itype = reftype($item);
107 176 100       510 next if $itype eq 'HASH';
108              
109 1         114 confess "Got item type '$itype' in list-facet '$facet', all items must be hashrefs";
110             }
111             }
112             else {
113 221 100       772 confess "Facet '$facet' is an only-one facet, but got '$type' instead of a hashref"
114             unless $type eq 'HASH';
115             }
116             }
117             }
118              
119             sub clone {
120 18     18 1 31 my $self = shift;
121 18         48 my $class = blessed($self);
122              
123 18         70 my %data = %$self;
124              
125 18         624 $data{+FACET_DATA} = dclone($data{+FACET_DATA});
126              
127 18         101 return bless(\%data, $class);
128             }
129              
130             sub _facet_class {
131 217     217   329 my $self = shift;
132 217         372 my ($name) = @_;
133              
134 217         455 my $spec = $self->facet_info($name);
135 217         420 my $class = $spec->{class};
136 217 50       469 unless ($spec->{loaded}) {
137 0         0 my $file = pkg_to_file($class);
138 0 0       0 require $file unless $INC{$file};
139 0         0 $spec->{loaded} = 1;
140             }
141              
142 217         444 return $class;
143             }
144              
145             sub the_facet {
146 155     155 1 248 my $self = shift;
147 155         291 my ($name) = @_;
148              
149 155 100       518 return undef unless defined $self->{+FACET_DATA}->{$name};
150              
151 131         224 my $data = $self->{+FACET_DATA}->{$name};
152              
153 131 50       409 my $type = reftype($data) or confess "Facet '$name' has a value that is not a reference, this should not happen";
154              
155 131 100       397 return $self->_facet_class($name)->new(%{dclone($data)})
  127         3479  
156             if $type eq 'HASH';
157              
158 4 50       10 if ($type eq 'ARRAY') {
159 4 50       11 return undef unless @$data;
160 4 100       227 croak "'the_facet' called for facet '$name', but '$name' has '" . @$data . "' items" if @$data != 1;
161 3         8 return $self->_facet_class($name)->new(%{dclone($data->[0])});
  3         44  
162             }
163              
164 0         0 die "Invalid facet data type: $type";
165             }
166              
167             sub facet {
168 131     131 1 239 my $self = shift;
169 131         647 my ($name) = @_;
170              
171 131 100       595 return () unless exists $self->{+FACET_DATA}->{$name};
172              
173 87         157 my $data = $self->{+FACET_DATA}->{$name};
174              
175 87 50       284 my $type = reftype($data) or confess "Facet '$name' has a value that is not a reference, this should not happen";
176              
177 87         138 my @out;
178 87 100       200 @out = ($data) if $type eq 'HASH';
179 87 100       223 @out = (@$data) if $type eq 'ARRAY';
180              
181 87         249 my $class = $self->_facet_class($name);
182              
183 87         188 return map { $class->new(%{dclone($_)}) } @out;
  216         319  
  216         3632  
184             }
185              
186             sub causes_failure {
187 55     55 1 87 my $self = shift;
188              
189             return $self->{+CAUSES_FAILURE}
190 55 100       155 if exists $self->{+CAUSES_FAILURE};
191              
192 37         150 my $hub = Test2::API::InterceptResult::Hub->new();
193 37         136 $hub->process($self);
194              
195 37 100       116 return $self->{+CAUSES_FAILURE} = ($hub->is_passing ? 0 : 1);
196             }
197              
198 31     31 1 82 sub causes_fail { shift->causes_failure }
199              
200 2     2 1 16 sub trace { $_[0]->facet('trace') }
201 144     144 1 311 sub the_trace { $_[0]->the_facet('trace') }
202 82 100   82 1 155 sub frame { my $t = $_[0]->the_trace or return undef; $t->{frame} || undef }
  64 50       295  
203 24 100   24 1 56 sub trace_details { my $t = $_[0]->the_trace or return undef; $t->{details} || undef }
  19 100       108  
204 3 100   3 1 10 sub trace_package { my $f = $_[0]->frame or return undef; $f->[0] || undef }
  2 100       16  
205 24 100   24 1 59 sub trace_file { my $f = $_[0]->frame or return undef; $f->[1] || undef }
  19 100       86  
206 25 100   25 1 73 sub trace_line { my $f = $_[0]->frame or return undef; $f->[2] || undef }
  20 100       92  
207 24 100   24 1 59 sub trace_subname { my $f = $_[0]->frame or return undef; $f->[3] || undef }
  19 100       85  
208 3 100   3 1 12 sub trace_tool { my $f = $_[0]->frame or return undef; $f->[3] || undef }
  2 100       16  
209              
210 36 50   36 1 78 sub trace_signature { my $t = $_[0]->the_trace or return undef; Test2::EventFacet::Trace::signature($t) || undef }
  36 50       90  
211              
212             sub brief {
213 30     30 1 53 my $self = shift;
214              
215 30         79 my @try = qw{
216             bailout_brief
217             error_brief
218             assert_brief
219             plan_brief
220             };
221              
222 30         55 for my $meth (@try) {
223 79 100       220 my $got = $self->$meth or next;
224 27         119 return $got;
225             }
226              
227 3         20 return;
228             }
229              
230             sub flatten {
231 13     13 1 44 my $self = shift;
232 13         29 my %params = @_;
233              
234 13         23 my $todo = {%{$self->{+FACET_DATA}}};
  13         84  
235 13         31 delete $todo->{hubs};
236 13         24 delete $todo->{meta};
237 13         22 delete $todo->{trace};
238              
239 13         35 my $out = $self->summary;
240 13         28 delete $out->{brief};
241 13         27 delete $out->{facets};
242 13         23 delete $out->{trace_tool};
243 13 100       35 delete $out->{trace_details} unless defined($out->{trace_details});
244              
245 13 100       68 for my $tagged (grep { my $finfo = $self->facet_info($_); $finfo->{list} && $finfo->{class}->can('tag') } keys %FACETS, keys %$todo) {
  229         412  
  229         804  
246 65 100       148 my $set = delete $todo->{$tagged} or next;
247              
248 13         25 my $fd = $self->{+FACET_DATA};
249 13         33 my $has_assert = $self->has_assert;
250 13         27 my $has_parent = $self->has_subtest;
251 13   66     29 my $has_fatal_error = $self->has_errors && grep { $_->{fail} } $self->errors;
252              
253 13 100 100     93 next if $tagged eq 'amnesty' && !($has_assert || $has_parent || $has_fatal_error);
      100        
254              
255 12         26 for my $item (@$set) {
256 34 100       47 push @{$out->{lc($item->{tag})}} => $item->{fail} ? "FATAL: $item->{details}" : $item->{details};
  34         120  
257             }
258             }
259              
260 13 100       48 if (my $assert = delete $todo->{assert}) {
261 7         16 $out->{pass} = $assert->{pass};
262 7         15 $out->{name} = $assert->{details};
263             }
264              
265 13 100       35 if (my $parent = delete $todo->{parent}) {
266 2 50       11 delete $out->{subtest}->{bailed_out} unless defined $out->{subtest}->{bailed_out};
267 2 50       9 delete $out->{subtest}->{skip_reason} unless defined $out->{subtest}->{skip_reason};
268              
269 2 50       7 if (my $res = $self->subtest_result) {
270 2         7 my $state = $res->state;
271 2         10 delete $state->{$_} for grep { !defined($state->{$_}) } keys %$state;
  14         34  
272 2         6 $out->{subtest} = $state;
273             $out->{subevents} = $res->flatten(%params)
274 2 100       10 if $params{include_subevents};
275             }
276             }
277              
278 13 100       49 if (my $control = delete $todo->{control}) {
279 3 100       11 if ($control->{halt}) {
    50          
280 1   50     7 $out->{bailed_out} = $control->{details} || 1;
281             }
282             elsif(defined $control->{details}) {
283 2         8 $out->{control} = $control->{details};
284             }
285             }
286              
287 13 100       32 if (my $plan = delete $todo->{plan}) {
288 1         8 $out->{plan} = $self->plan_brief;
289 1         7 $out->{plan} =~ s/^PLAN\s*//;
290             }
291              
292 13         32 for my $other (keys %$todo) {
293 8 50       23 my $data = $todo->{$other} or next;
294              
295 8 100       29 if (reftype($data) eq 'ARRAY') {
296 2 50 33     12 if (!$out->{$other} || reftype($out->{$other}) eq 'ARRAY') {
297 2         5 for my $item (@$data) {
298 2 50       6 push @{$out->{$other}} => $item->{details} if defined $item->{details};
  2         9  
299             }
300             }
301             }
302             else {
303 6 50 33     42 $out->{$other} = $data->{details} if defined($data->{details}) && !defined($out->{$other});
304             }
305             }
306              
307 13 100       67 if (my $fields = $params{fields}) {
308 1 50       5 $out = { map {exists($out->{$_}) ? ($_ => $out->{$_}) : ()} @$fields };
  2         10  
309             }
310              
311 13 100       34 if (my $remove = $params{remove}) {
312 1         10 delete $out->{$_} for @$remove;
313             }
314              
315 13         145 return $out;
316             }
317              
318             sub summary {
319 21     21 1 45 my $self = shift;
320 21         42 my %params = @_;
321              
322             my $out = {
323             brief => $self->brief || '',
324              
325             causes_failure => $self->causes_failure,
326              
327             trace_line => $self->trace_line,
328             trace_file => $self->trace_file,
329             trace_tool => $self->trace_subname,
330             trace_details => $self->trace_details,
331              
332 21   100     51 facets => [ sort keys(%{$self->{+FACET_DATA}}) ],
  21         181  
333             };
334              
335 21 100       78 if (my $fields = $params{fields}) {
336 1 50       5 $out = { map {exists($out->{$_}) ? ($_ => $out->{$_}) : ()} @$fields };
  2         13  
337             }
338              
339 21 100       50 if (my $remove = $params{remove}) {
340 1         9 delete $out->{$_} for @$remove;
341             }
342              
343 21         78 return $out;
344             }
345              
346 87 100   87 1 317 sub has_assert { $_[0]->{+FACET_DATA}->{assert} ? 1 : 0 }
347 0     0 1 0 sub the_assert { $_[0]->the_facet('assert') }
348 4     4 1 14 sub assert { $_[0]->facet('assert') }
349              
350             sub assert_brief {
351 24     24 1 33 my $self = shift;
352              
353 24         48 my $fd = $self->{+FACET_DATA};
354 24 100       62 my $as = $fd->{assert} or return;
355 18         36 my $am = $fd->{amnesty};
356              
357 18 100       42 my $out = $as->{pass} ? "PASS" : "FAIL";
358 18 100       40 $out .= " with amnesty" if $am;
359 18         51 return $out;
360             }
361              
362 48 100   48 1 169 sub has_subtest { $_[0]->{+FACET_DATA}->{parent} ? 1 : 0 }
363 2     2 1 11 sub the_subtest { $_[0]->the_facet('parent') }
364 2     2 1 6 sub subtest { $_[0]->facet('parent') }
365              
366             sub subtest_result {
367 9     9 1 20 my $self = shift;
368              
369 9 100       30 my $parent = $self->{+FACET_DATA}->{parent} or return;
370 7   50     20 my $children = $parent->{children} || [];
371              
372             $children = $self->{+RESULT_CLASS}->new(@$children)->upgrade
373 7 100 66     59 unless blessed($children) && $children->isa($self->{+RESULT_CLASS});
374              
375 7         39 return $children;
376             }
377              
378 29 100   29 1 80 sub has_bailout { $_[0]->bailout ? 1 : 0 }
379 0     0 1 0 sub the_bailout { my ($b) = $_[0]->bailout; $b }
  0         0  
380              
381             sub bailout {
382 66     66 1 105 my $self = shift;
383 66 100       259 my $control = $self->{+FACET_DATA}->{control} or return;
384 12 100       54 return $control if $control->{halt};
385 2         11 return;
386             }
387              
388             sub bailout_brief {
389 33     33 1 57 my $self = shift;
390 33 100       107 my $bo = $self->bailout or return;
391              
392 7 50       25 my $reason = $bo->{details} or return "BAILED OUT";
393 7         32 return "BAILED OUT: $reason";
394             }
395              
396             sub bailout_reason {
397 2     2 1 5 my $self = shift;
398 2 100       6 my $bo = $self->bailout or return;
399 1   50     10 return $bo->{details} || '';
400             }
401              
402 35 100   35 1 97 sub has_plan { $_[0]->{+FACET_DATA}->{plan} ? 1 : 0 }
403 0     0 1 0 sub the_plan { $_[0]->the_facet('plan') }
404 2     2 1 9 sub plan { $_[0]->facet('plan') }
405              
406             sub plan_brief {
407 13     13 1 19 my $self = shift;
408              
409 13 100       45 my $plan = $self->{+FACET_DATA}->{plan} or return;
410              
411 9         45 my $base = $self->_plan_brief($plan);
412              
413 9 100       37 my $reason = $plan->{details} or return $base;
414 4         26 return "$base: $reason";
415             }
416              
417             sub _plan_brief {
418 9     9   19 my $self = shift;
419 9         19 my ($plan) = @_;
420              
421 9 100       27 return 'NO PLAN' if $plan->{none};
422 8 100 100     36 return "SKIP ALL" if $plan->{skip} || !$plan->{count};
423 6         21 return "PLAN $plan->{count}";
424             }
425              
426 2 100   2 1 21 sub has_amnesty { $_[0]->{+FACET_DATA}->{amnesty} ? 1 : 0 }
427 0     0 1 0 sub the_amnesty { $_[0]->the_facet('amnesty') }
428 22     22 1 60 sub amnesty { $_[0]->facet('amnesty') }
429 2     2 1 8 sub amnesty_reasons { map { $_->{details} } $_[0]->amnesty }
  6         18  
430              
431 1 100   1 1 22 sub has_todos { &first(sub { uc($_->{tag}) eq 'TODO' }, $_[0]->amnesty) ? 1 : 0 }
  2     2   14  
432 4     4 1 17 sub todos { grep { uc($_->{tag}) eq 'TODO' } $_[0]->amnesty }
  12         36  
433 2 50   2 1 6 sub todo_reasons { map { $_->{details} || 'TODO' } $_[0]->todos }
  2         14  
434              
435 3 100   3 1 13 sub has_skips { &first(sub { uc($_->{tag}) eq 'SKIP' }, $_[0]->amnesty) ? 1 : 0 }
  2     2   12  
436 4     4 1 14 sub skips { grep { uc($_->{tag}) eq 'SKIP' } $_[0]->amnesty }
  12         35  
437 2 50   2 1 6 sub skip_reasons { map { $_->{details} || 'SKIP' } $_[0]->skips }
  2         15  
438              
439             my %TODO_OR_SKIP = (SKIP => 1, TODO => 1);
440 5 100   5 1 17 sub has_other_amnesty { &first( sub { !$TODO_OR_SKIP{uc($_->{tag})} }, $_[0]->amnesty) ? 1 : 0 }
  2     2   13  
441 4     4 1 14 sub other_amnesty { grep { !$TODO_OR_SKIP{uc($_->{tag})} } $_[0]->amnesty }
  12         41  
442 2 0 33 2 1 6 sub other_amnesty_reasons { map { $_->{details} || $_->{tag} || 'AMNESTY' } $_[0]->other_amnesty }
  2         14  
443              
444 50 100   50 1 176 sub has_errors { $_[0]->{+FACET_DATA}->{errors} ? 1 : 0 }
445 0     0 1 0 sub the_errors { $_[0]->the_facet('errors') }
446 26     26 1 72 sub errors { $_[0]->facet('errors') }
447 10 0 33 10 1 27 sub error_messages { map { $_->{details} || $_->{tag} || 'ERROR' } $_[0]->errors }
  6         53  
448              
449             sub error_brief {
450 30     30 1 53 my $self = shift;
451              
452 30 100       95 my $errors = $self->{+FACET_DATA}->{errors} or return;
453              
454 10 100       34 my $base = @$errors > 1 ? "ERRORS" : "ERROR";
455              
456 10 50       26 return $base unless @$errors;
457              
458 10         57 my ($msg, @extra) = split /[\n\r]+/, $errors->[0]->{details};
459              
460 10         31 my $out = "$base: $msg";
461              
462 10 100 100     52 $out .= " [...]" if @extra || @$errors > 1;
463              
464 10         52 return $out;
465             }
466              
467 34 100   34 1 134 sub has_info { $_[0]->{+FACET_DATA}->{info} ? 1 : 0 }
468 0     0 1 0 sub the_info { $_[0]->the_facet('info') }
469 64     64 1 167 sub info { $_[0]->facet('info') }
470 7     7 1 32 sub info_messages { map { $_->{details} } $_[0]->info }
  14         63  
471              
472 3 100   3 1 35 sub has_diags { &first(sub { uc($_->{tag}) eq 'DIAG' }, $_[0]->info) ? 1 : 0 }
  8     8   41  
473 10     10 1 24 sub diags { grep { uc($_->{tag}) eq 'DIAG' } $_[0]->info }
  20         76  
474 8 50   8 1 25 sub diag_messages { map { $_->{details} || 'DIAG' } $_[0]->diags }
  6         63  
475              
476 5 100   5 1 30 sub has_notes { &first(sub { uc($_->{tag}) eq 'NOTE' }, $_[0]->info) ? 1 : 0 }
  8     8   47  
477 10     10 1 31 sub notes { grep { uc($_->{tag}) eq 'NOTE' } $_[0]->info }
  20         78  
478 8 50   8 1 25 sub note_messages { map { $_->{details} || 'NOTE' } $_[0]->notes }
  6         60  
479              
480             my %NOTE_OR_DIAG = (NOTE => 1, DIAG => 1);
481 5 100   5 1 17 sub has_other_info { &first(sub { !$NOTE_OR_DIAG{uc($_->{tag})} }, $_[0]->info) ? 1 : 0 }
  2     2   11  
482 4     4 1 10 sub other_info { grep { !$NOTE_OR_DIAG{uc($_->{tag})} } $_[0]->info }
  12         37  
483 2 0 33 2 1 6 sub other_info_messages { map { $_->{details} || $_->{tag} || 'INFO' } $_[0]->other_info }
  2         13  
484              
485             1;
486              
487             __END__
488              
489             =pod
490              
491             =encoding UTF-8
492              
493             =head1 NAME
494              
495             Test2::API::InterceptResult::Event - Representation of an event for use in
496             testing other test tools.
497              
498             =head1 DESCRIPTION
499              
500             C<intercept { ... }> from L<Test2::API> returns an instance of
501             L<Test2::API::InterceptResult> which is a blessed arrayref of
502             L<Test2::API::InterceptResult::Event> objects.
503              
504             This POD documents the methods of these events, which are mainly provided for
505             you to use when testing your test tools.
506              
507             =head1 SYNOPSIS
508              
509             use Test2::V0;
510             use Test2::API qw/intercept/;
511              
512             my $events = intercept {
513             ok(1, "A passing assertion");
514             plan(1);
515             };
516              
517             # This will convert all events into instances of
518             # Test2::API::InterceptResult::Event. Until we do this they are the
519             # original Test::Event::* instances
520             $events->upgrade(in_place => 1);
521              
522             # Now we can get individual events in this form
523             my $assert = $events->[0];
524             my $plan = $events->[1];
525              
526             # Or we can operate on all events at once:
527             my $flattened = $events->flatten;
528             is(
529             $flattened,
530             [
531             {
532             causes_failure => 0,
533              
534             name => 'A passing assertion',
535             pass => 1,
536              
537             trace_file => 'xxx.t',
538             trace_line => 5,
539             },
540             {
541             causes_failure => 0,
542              
543             plan => 1,
544              
545             trace_file => 'xxx.t',
546             trace_line => 6,
547             },
548             ],
549             "Flattened both events and returned an arrayref of the results
550             );
551              
552             =head1 METHODS
553              
554             =head2 !!! IMPORTANT NOTES ON DESIGN !!!
555              
556             Please pay attention to what these return, many return a scalar when
557             applicable or an empty list when not (as opposed to undef). Many also always
558             return a list of 0 or more items. Some always return a scalar. Note that none
559             of the methods care about context, their behavior is consistent regardless of
560             scalar, list, or void context.
561              
562             This was done because this class was specifically designed to be used in a list
563             and generate more lists in bulk operations. Sometimes in a map you want nothing
564             to show up for the event, and you do not want an undef in its place. In general
565             single event instances are not going to be used alone, though that is allowed.
566              
567             As a general rule any method prefixed with C<the_> implies the event should
568             have exactly 1 of the specified item, and and exception will be thrown if there
569             are 0, or more than 1 of the item.
570              
571             =head2 ATTRIBUTES
572              
573             =over 4
574              
575             =item $hashref = $event->facet_data
576              
577             This will return the facet data hashref, which is all Test2 cares about for any
578             given event.
579              
580             =item $class = $event->result_class
581              
582             This is normally L<Test2::API::InterceptResult>. This is set at construction so
583             that subtest results can be turned into instances of it on demand.
584              
585             =back
586              
587             =head2 DUPLICATION
588              
589             =over 4
590              
591             =item $copy = $event->clone
592              
593             Create a deep copy of the event. Modifying either event will not effect the
594             other.
595              
596             =back
597              
598             =head2 CONDENSED MULTI-FACET DATA
599              
600             =over 4
601              
602             =item $bool = $event->causes_failure
603              
604             =item $bool = $event->causes_fail
605              
606             These are both aliases of the same functionality.
607              
608             This will always return either a true value, or a false value. This never
609             returns a list.
610              
611             This method may be relatively slow (still super fast) because it determines
612             pass or fail by creating an instance of L<Test2::Hub> and asking it to process
613             the event, and then asks the hub for its pass/fail state. This is slower than
614             bulding in logic to do the check, but it is more reliable as it will always
615             tell you what the hub thinks, so the logic will never be out of date relative
616             to the Test2 logic that actually cares.
617              
618             =item STRING_OR_EMPTY_LIST = $event->brief
619              
620             Not all events have a brief, some events are not rendered by the formatter,
621             others have no "brief" data worth seeing. When this is the case an empty list
622             is returned. This is done intentionally so it can be used in a map operation
623             without having C<undef> being included in the result.
624              
625             When a brief can be generated it is always a single 1-line string, and is
626             returned as-is, not in a list.
627              
628             Possible briefs:
629              
630             # From control facets
631             "BAILED OUT"
632             "BAILED OUT: $why"
633              
634             # From error facets
635             "ERROR"
636             "ERROR: $message"
637             "ERROR: $partial_message [...]"
638             "ERRORS: $first_error_message [...]"
639              
640             # From assert facets
641             "PASS"
642             "FAIL"
643             "PASS with amnesty"
644             "FAIL with amnesty"
645              
646             # From plan facets
647             "PLAN $count"
648             "NO PLAN"
649             "SKIP ALL"
650             "SKIP ALL: $why"
651              
652             Note that only the first applicable brief is returned. This is essnetially a
653             poor-mans TAP that only includes facets that could (but not necessarily do)
654             cause a failure.
655              
656             =item $hashref = $event->flatten
657              
658             =item $hashref = $event->flatten(include_subevents => 1)
659              
660             This ALWAYS returns a hashref. This puts all the most useful data for the most
661             interesting facets into a single hashref for easy validation.
662              
663             If there are no meaningful facets this will return an empty hashref.
664              
665             If given the 'include_subevents' parameter it will also include subtest data:
666              
667             Here is a list of EVERY possible field. If a field is not applicable it will
668             not be present.
669              
670             =over 4
671              
672             =item always present
673              
674             causes_failure => 1, # Always present
675              
676             =item Present if the event has a trace facet
677              
678             trace_line => 42,
679             trace_file => 'Foo/Bar.pm',
680             trace_details => 'Extra trace details', # usually not present
681              
682             =item If an assertion is present
683              
684             pass => 0,
685             name => "1 + 1 = 2, so math works",
686              
687             =item If a plan is present:
688              
689             plan => $count_or_SKIP_ALL_or_NO_PLAN,
690              
691             =item If amnesty facets are present
692              
693             You get an array for each type that is present.
694              
695             todo => [ # Yes you could be under multiple todos, this will list them all.
696             "I will fix this later",
697             "I promise to fix these",
698             ],
699              
700             skip => ["This will format the main drive, do not run"],
701              
702             ... => ["Other amnesty"]
703              
704             =item If Info (note/diag) facets are present
705              
706             You get an arrayref for any that are present, the key is not defined if they are not present.
707              
708             diag => [
709             "Test failed at Foo/Bar.pm line 42",
710             "You forgot to tie your boots",
711             ],
712              
713             note => ["Your boots are red"],
714              
715             ... => ["Other info"],
716              
717             =item If error facets are present
718              
719             Always an arrayref
720              
721             error => [
722             "non fatal error (does not cause test failure, just an FYI",
723             "FATAL: This is a fatal error (causes failure)",
724             ],
725              
726             # Errors can have alternative tags, but in practice are always 'error',
727             # listing this for completeness.
728             ... => [ ... ]
729              
730             =item Present if the event is a subtest
731              
732             subtest => {
733             count => 2, # Number of assertions made
734             failed => 1, # Number of test failures seen
735             is_passing => 0, # Boolean, true if the test would be passing
736             # after the events are processed.
737              
738             plan => 2, # Plan, either a number, undef, 'SKIP', or 'NO PLAN'
739             follows_plan => 1, # True if there is a plan and it was followed.
740             # False if the plan and assertions did not
741             # match, undef if no plan was present in the
742             # event list.
743              
744             bailed_out => "foo", # if there was a bail-out in the
745             # events in this will be a string explaining
746             # why there was a bailout, if no reason was
747             # given this will simply be set to true (1).
748              
749             skip_reason => "foo", # If there was a skip_all this will give the
750             # reason.
751             },
752              
753             if C<< (include_subtest => 1) >> was provided as a parameter then the following
754             will be included. This is the result of turning all subtest child events into
755             an L<Test2::API::InterceptResult> instance and calling the C<flatten> method on
756             it.
757              
758             subevents => Test2::API::InterceptResult->new(@child_events)->flatten(...),
759              
760             =item If a bail-out is being requested
761              
762             If no reason was given this will be set to 1.
763              
764             bailed_out => "reason",
765              
766             =back
767              
768             =item $hashref = $event->summary()
769              
770             This returns a limited summary. See C<flatten()>, which is usually a better
771             option.
772              
773             {
774             brief => $event->brief || '',
775              
776             causes_failure => $event->causes_failure,
777              
778             trace_line => $event->trace_line,
779             trace_file => $event->trace_file,
780             trace_tool => $event->trace_subname,
781             trace_details => $event->trace_details,
782              
783             facets => [ sort keys(%{$event->{+FACET_DATA}}) ],
784             }
785              
786             =back
787              
788             =head2 DIRECT ARBITRARY FACET ACCESS
789              
790             =over 4
791              
792             =item @list_of_facets = $event->facet($name)
793              
794             This always returns a list of 0 or more items. This fetches the facet instances
795             from the event. For facets like 'assert' this will always return 0 or 1
796             item. For events like 'info' (diags, notes) this will return 0 or more
797             instances, once for each instance of the facet.
798              
799             These will be blessed into the proper L<Test2::EventFacet> subclass. If no
800             subclass can be found it will be blessed as an
801             L<Test2::API::InterceptResult::Facet> generic facet class.
802              
803             =item $undef_or_facet = $event->the_facet($name)
804              
805             If you know you will have exactly 1 instance of a facet you can call this.
806              
807             If you are correct and there is exactly one instance of the facet it will
808             always return the hashref.
809              
810             If there are 0 instances of the facet this will reutrn undef, not an empty
811             list.
812              
813             If there are more than 1 instance this will throw an exception because your
814             assumption was incorrect.
815              
816             =back
817              
818             =head2 TRACE FACET
819              
820             =over 4
821              
822             =item @list_of_facets = $event->trace
823              
824             TODO
825              
826             =item $undef_or_hashref = $event->the_trace
827              
828             This returns the trace hashref, or undef if it is not present.
829              
830             =item $undef_or_arrayref = $event->frame
831              
832             If a trace is present, and has a caller frame, this will be an arrayref:
833              
834             [$package, $file, $line, $subname]
835              
836             If the trace is not present, or has no caller frame this will return undef.
837              
838             =item $undef_or_string = $event->trace_details
839              
840             This is usually undef, but occasionally has a string that overrides the
841             file/line number debugging a trace usually provides on test failure.
842              
843             =item $undef_or_string = $event->trace_package
844              
845             Same as C<(caller())[0]>, the first element of the trace frame.
846              
847             Will be undef if not present.
848              
849             =item $undef_or_string = $event->trace_file
850              
851             Same as C<(caller())[1]>, the second element of the trace frame.
852              
853             Will be undef if not present.
854              
855             =item $undef_or_integer = $event->trace_line
856              
857             Same as C<(caller())[2]>, the third element of the trace frame.
858              
859             Will be undef if not present.
860              
861             =item $undef_or_string = $event->trace_subname
862              
863             =item $undef_or_string = $event->trace_tool
864              
865             Aliases for the same thing
866              
867             Same as C<(caller($level))[4]>, the fourth element of the trace frame.
868              
869             Will be undef if not present.
870              
871             =item $undef_or_string = $event->trace_signature
872              
873             A string that is a unique signature for the trace. If a single context
874             generates multiple events they will all have the same signature. This can be
875             used to tie assertions and diagnostics sent as seperate events together after
876             the fact.
877              
878             =back
879              
880             =head2 ASSERT FACET
881              
882             =over 4
883              
884             =item $bool = $event->has_assert
885              
886             Returns true if the event has an assert facet, false if it does not.
887              
888             =item $undef_or_hashref = $event->the_assert
889              
890             Returns the assert facet if present, undef if it is not.
891              
892             =item @list_of_facets = $event->assert
893              
894             TODO
895              
896             =item EMPTY_LIST_OR_STRING = $event->assert_brief
897              
898             Returns a string giving a brief of the assertion if an assertion is present.
899             Returns an empty list if no assertion is present.
900              
901             =back
902              
903             =head2 SUBTESTS (PARENT FACET)
904              
905             =over 4
906              
907             =item $bool = $event->has_subtest
908              
909             True if a subetest is present in this event.
910              
911             =item $undef_or_hashref = $event->the_subtest
912              
913             Get the one subtest if present, otherwise undef.
914              
915             =item @list_of_facets = $event->subtest
916              
917             TODO
918              
919             =item EMPTY_LIST_OR_OBJECT = $event->subtest_result
920              
921             Returns an empty list if there is no subtest.
922              
923             Get an instance of L<Test2::API::InterceptResult> representing the subtest.
924              
925             =back
926              
927             =head2 CONTROL FACET (BAILOUT, ENCODING)
928              
929             =over 4
930              
931             =item $bool = $event->has_bailout
932              
933             True if there was a bailout
934              
935             =item $undef_hashref = $event->the_bailout
936              
937             Return the control facet if it requested a bailout.
938              
939             =item EMPTY_LIST_OR_HASHREF = $event->bailout
940              
941             Get a list of 0 or 1 hashrefs. The hashref will be the control facet if a
942             bail-out was requested.
943              
944             =item EMPTY_LIST_OR_STRING = $event->bailout_brief
945              
946             Get the brief of the balout if present.
947              
948             =item EMPTY_LIST_OR_STRING = $event->bailout_reason
949              
950             Get the reason for the bailout, an empty string if no reason was provided, or
951             an empty list if there was no bailout.
952              
953             =back
954              
955             =head2 PLAN FACET
956              
957             TODO
958              
959             =over 4
960              
961             =item $bool = $event->has_plan
962              
963             =item $undef_or_hashref = $event->the_plan
964              
965             =item @list_if_hashrefs = $event->plan
966              
967             =item EMPTY_LIST_OR_STRING $event->plan_brief
968              
969             =back
970              
971             =head2 AMNESTY FACET (TODO AND SKIP)
972              
973             TODO
974              
975             =over 4
976              
977             =item $event->has_amnesty
978              
979             =item $event->the_amnesty
980              
981             =item $event->amnesty
982              
983             =item $event->amnesty_reasons
984              
985             =item $event->has_todos
986              
987             =item $event->todos
988              
989             =item $event->todo_reasons
990              
991             =item $event->has_skips
992              
993             =item $event->skips
994              
995             =item $event->skip_reasons
996              
997             =item $event->has_other_amnesty
998              
999             =item $event->other_amnesty
1000              
1001             =item $event->other_amnesty_reasons
1002              
1003             =back
1004              
1005             =head2 ERROR FACET (CAPTURED EXCEPTIONS)
1006              
1007             TODO
1008              
1009             =over 4
1010              
1011             =item $event->has_errors
1012              
1013             =item $event->the_errors
1014              
1015             =item $event->errors
1016              
1017             =item $event->error_messages
1018              
1019             =item $event->error_brief
1020              
1021             =back
1022              
1023             =head2 INFO FACET (DIAG, NOTE)
1024              
1025             TODO
1026              
1027             =over 4
1028              
1029             =item $event->has_info
1030              
1031             =item $event->the_info
1032              
1033             =item $event->info
1034              
1035             =item $event->info_messages
1036              
1037             =item $event->has_diags
1038              
1039             =item $event->diags
1040              
1041             =item $event->diag_messages
1042              
1043             =item $event->has_notes
1044              
1045             =item $event->notes
1046              
1047             =item $event->note_messages
1048              
1049             =item $event->has_other_info
1050              
1051             =item $event->other_info
1052              
1053             =item $event->other_info_messages
1054              
1055             =back
1056              
1057             =head1 SOURCE
1058              
1059             The source code repository for Test2 can be found at
1060             F<http://github.com/Test-More/test-more/>.
1061              
1062             =head1 MAINTAINERS
1063              
1064             =over 4
1065              
1066             =item Chad Granum E<lt>exodist@cpan.orgE<gt>
1067              
1068             =back
1069              
1070             =head1 AUTHORS
1071              
1072             =over 4
1073              
1074             =item Chad Granum E<lt>exodist@cpan.orgE<gt>
1075              
1076             =back
1077              
1078             =head1 COPYRIGHT
1079              
1080             Copyright 2020 Chad Granum E<lt>exodist@cpan.orgE<gt>.
1081              
1082             This program is free software; you can redistribute it and/or
1083             modify it under the same terms as Perl itself.
1084              
1085             See F<http://dev.perl.org/licenses/>
1086              
1087             =cut