1package Test2::API::InterceptResult::Event; 2use strict; 3use warnings; 4 5our $VERSION = '1.302194'; 6 7use List::Util qw/first/; 8use Test2::Util qw/pkg_to_file/; 9use Scalar::Util qw/reftype blessed/; 10 11use Storable qw/dclone/; 12use Carp qw/confess croak/; 13 14use Test2::API::InterceptResult::Facet; 15use Test2::API::InterceptResult::Hub; 16 17use Test2::Util::HashBase qw{ 18 +causes_failure 19 <facet_data 20 <result_class 21}; 22 23my %FACETS; 24BEGIN { 25 local $@; 26 local *plugins; 27 if (eval { require Module::Pluggable; 1 }) { 28 Module::Pluggable->import( 29 # We will replace the sub later 30 require => 1, 31 on_require_error => sub { 1 }, 32 search_path => ['Test2::EventFacet'], 33 max_depth => 3, 34 min_depth => 3, 35 ); 36 37 for my $facet_type (__PACKAGE__->plugins) { 38 my ($key, $list); 39 eval { 40 $key = $facet_type->facet_key; 41 $list = $facet_type->is_list; 42 }; 43 next unless $key && defined($list); 44 45 $FACETS{$key} = {list => $list, class => $facet_type, loaded => 1}; 46 } 47 } 48 49 $FACETS{__GENERIC__} = {class => 'Test2::API::InterceptResult::Facet', loaded => 1}; 50} 51 52sub facet_map { \%FACETS } 53 54sub facet_info { 55 my $facet = pop; 56 57 return $FACETS{$facet} if exists $FACETS{$facet}; 58 59 my $mname = ucfirst(lc($facet)); 60 $mname =~ s/s$//; 61 62 for my $name ($mname, "${mname}s") { 63 my $file = "Test2/EventFacet/$name.pm"; 64 my $class = "Test2::EventFacet::$name"; 65 66 local $@; 67 my $ok = eval { 68 require $file; 69 70 my $key = $class->facet_key; 71 my $list = $class->is_list; 72 73 $FACETS{$key} = {list => $list, class => $class, loaded => 1}; 74 $FACETS{$facet} = $FACETS{$key} if $facet ne $key; 75 76 1; 77 }; 78 79 return $FACETS{$facet} if $ok && $FACETS{$facet}; 80 } 81 82 return $FACETS{$facet} = $FACETS{__GENERIC__}; 83} 84 85sub init { 86 my $self = shift; 87 88 my $rc = $self->{+RESULT_CLASS} ||= 'Test2::API::InterceptResult'; 89 my $rc_file = pkg_to_file($rc); 90 require($rc_file) unless $INC{$rc_file}; 91 92 my $fd = $self->{+FACET_DATA} ||= {}; 93 94 for my $facet (keys %$fd) { 95 my $finfo = $self->facet_info($facet); 96 my $is_list = $finfo->{list}; 97 next unless defined $is_list; 98 99 my $type = reftype($fd->{$facet}); 100 101 if ($is_list) { 102 confess "Facet '$facet' is a list facet, but got '$type' instead of an arrayref" 103 unless $type eq 'ARRAY'; 104 105 for my $item (@{$fd->{$facet}}) { 106 my $itype = reftype($item); 107 next if $itype eq 'HASH'; 108 109 confess "Got item type '$itype' in list-facet '$facet', all items must be hashrefs"; 110 } 111 } 112 else { 113 confess "Facet '$facet' is an only-one facet, but got '$type' instead of a hashref" 114 unless $type eq 'HASH'; 115 } 116 } 117} 118 119sub clone { 120 my $self = shift; 121 my $class = blessed($self); 122 123 my %data = %$self; 124 125 $data{+FACET_DATA} = dclone($data{+FACET_DATA}); 126 127 return bless(\%data, $class); 128} 129 130sub _facet_class { 131 my $self = shift; 132 my ($name) = @_; 133 134 my $spec = $self->facet_info($name); 135 my $class = $spec->{class}; 136 unless ($spec->{loaded}) { 137 my $file = pkg_to_file($class); 138 require $file unless $INC{$file}; 139 $spec->{loaded} = 1; 140 } 141 142 return $class; 143} 144 145sub the_facet { 146 my $self = shift; 147 my ($name) = @_; 148 149 return undef unless defined $self->{+FACET_DATA}->{$name}; 150 151 my $data = $self->{+FACET_DATA}->{$name}; 152 153 my $type = reftype($data) or confess "Facet '$name' has a value that is not a reference, this should not happen"; 154 155 return $self->_facet_class($name)->new(%{dclone($data)}) 156 if $type eq 'HASH'; 157 158 if ($type eq 'ARRAY') { 159 return undef unless @$data; 160 croak "'the_facet' called for facet '$name', but '$name' has '" . @$data . "' items" if @$data != 1; 161 return $self->_facet_class($name)->new(%{dclone($data->[0])}); 162 } 163 164 die "Invalid facet data type: $type"; 165} 166 167sub facet { 168 my $self = shift; 169 my ($name) = @_; 170 171 return () unless exists $self->{+FACET_DATA}->{$name}; 172 173 my $data = $self->{+FACET_DATA}->{$name}; 174 175 my $type = reftype($data) or confess "Facet '$name' has a value that is not a reference, this should not happen"; 176 177 my @out; 178 @out = ($data) if $type eq 'HASH'; 179 @out = (@$data) if $type eq 'ARRAY'; 180 181 my $class = $self->_facet_class($name); 182 183 return map { $class->new(%{dclone($_)}) } @out; 184} 185 186sub causes_failure { 187 my $self = shift; 188 189 return $self->{+CAUSES_FAILURE} 190 if exists $self->{+CAUSES_FAILURE}; 191 192 my $hub = Test2::API::InterceptResult::Hub->new(); 193 $hub->process($self); 194 195 return $self->{+CAUSES_FAILURE} = ($hub->is_passing ? 0 : 1); 196} 197 198sub causes_fail { shift->causes_failure } 199 200sub trace { $_[0]->facet('trace') } 201sub the_trace { $_[0]->the_facet('trace') } 202sub frame { my $t = $_[0]->the_trace or return undef; $t->{frame} || undef } 203sub trace_details { my $t = $_[0]->the_trace or return undef; $t->{details} || undef } 204sub trace_package { my $f = $_[0]->frame or return undef; $f->[0] || undef } 205sub trace_file { my $f = $_[0]->frame or return undef; $f->[1] || undef } 206sub trace_line { my $f = $_[0]->frame or return undef; $f->[2] || undef } 207sub trace_subname { my $f = $_[0]->frame or return undef; $f->[3] || undef } 208sub trace_tool { my $f = $_[0]->frame or return undef; $f->[3] || undef } 209 210sub trace_signature { my $t = $_[0]->the_trace or return undef; Test2::EventFacet::Trace::signature($t) || undef } 211 212sub brief { 213 my $self = shift; 214 215 my @try = qw{ 216 bailout_brief 217 error_brief 218 assert_brief 219 plan_brief 220 }; 221 222 for my $meth (@try) { 223 my $got = $self->$meth or next; 224 return $got; 225 } 226 227 return; 228} 229 230sub flatten { 231 my $self = shift; 232 my %params = @_; 233 234 my $todo = {%{$self->{+FACET_DATA}}}; 235 delete $todo->{hubs}; 236 delete $todo->{meta}; 237 delete $todo->{trace}; 238 239 my $out = $self->summary; 240 delete $out->{brief}; 241 delete $out->{facets}; 242 delete $out->{trace_tool}; 243 delete $out->{trace_details} unless defined($out->{trace_details}); 244 245 for my $tagged (grep { my $finfo = $self->facet_info($_); $finfo->{list} && $finfo->{class}->can('tag') } keys %FACETS, keys %$todo) { 246 my $set = delete $todo->{$tagged} or next; 247 248 my $fd = $self->{+FACET_DATA}; 249 my $has_assert = $self->has_assert; 250 my $has_parent = $self->has_subtest; 251 my $has_fatal_error = $self->has_errors && grep { $_->{fail} } $self->errors; 252 253 next if $tagged eq 'amnesty' && !($has_assert || $has_parent || $has_fatal_error); 254 255 for my $item (@$set) { 256 push @{$out->{lc($item->{tag})}} => $item->{fail} ? "FATAL: $item->{details}" : $item->{details}; 257 } 258 } 259 260 if (my $assert = delete $todo->{assert}) { 261 $out->{pass} = $assert->{pass}; 262 $out->{name} = $assert->{details}; 263 } 264 265 if (my $parent = delete $todo->{parent}) { 266 delete $out->{subtest}->{bailed_out} unless defined $out->{subtest}->{bailed_out}; 267 delete $out->{subtest}->{skip_reason} unless defined $out->{subtest}->{skip_reason}; 268 269 if (my $res = $self->subtest_result) { 270 my $state = $res->state; 271 delete $state->{$_} for grep { !defined($state->{$_}) } keys %$state; 272 $out->{subtest} = $state; 273 $out->{subevents} = $res->flatten(%params) 274 if $params{include_subevents}; 275 } 276 } 277 278 if (my $control = delete $todo->{control}) { 279 if ($control->{halt}) { 280 $out->{bailed_out} = $control->{details} || 1; 281 } 282 elsif(defined $control->{details}) { 283 $out->{control} = $control->{details}; 284 } 285 } 286 287 if (my $plan = delete $todo->{plan}) { 288 $out->{plan} = $self->plan_brief; 289 $out->{plan} =~ s/^PLAN\s*//; 290 } 291 292 for my $other (keys %$todo) { 293 my $data = $todo->{$other} or next; 294 295 if (reftype($data) eq 'ARRAY') { 296 if (!$out->{$other} || reftype($out->{$other}) eq 'ARRAY') { 297 for my $item (@$data) { 298 push @{$out->{$other}} => $item->{details} if defined $item->{details}; 299 } 300 } 301 } 302 else { 303 $out->{$other} = $data->{details} if defined($data->{details}) && !defined($out->{$other}); 304 } 305 } 306 307 if (my $fields = $params{fields}) { 308 $out = { map {exists($out->{$_}) ? ($_ => $out->{$_}) : ()} @$fields }; 309 } 310 311 if (my $remove = $params{remove}) { 312 delete $out->{$_} for @$remove; 313 } 314 315 return $out; 316} 317 318sub summary { 319 my $self = shift; 320 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 facets => [ sort keys(%{$self->{+FACET_DATA}}) ], 333 }; 334 335 if (my $fields = $params{fields}) { 336 $out = { map {exists($out->{$_}) ? ($_ => $out->{$_}) : ()} @$fields }; 337 } 338 339 if (my $remove = $params{remove}) { 340 delete $out->{$_} for @$remove; 341 } 342 343 return $out; 344} 345 346sub has_assert { $_[0]->{+FACET_DATA}->{assert} ? 1 : 0 } 347sub the_assert { $_[0]->the_facet('assert') } 348sub assert { $_[0]->facet('assert') } 349 350sub assert_brief { 351 my $self = shift; 352 353 my $fd = $self->{+FACET_DATA}; 354 my $as = $fd->{assert} or return; 355 my $am = $fd->{amnesty}; 356 357 my $out = $as->{pass} ? "PASS" : "FAIL"; 358 $out .= " with amnesty" if $am; 359 return $out; 360} 361 362sub has_subtest { $_[0]->{+FACET_DATA}->{parent} ? 1 : 0 } 363sub the_subtest { $_[0]->the_facet('parent') } 364sub subtest { $_[0]->facet('parent') } 365 366sub subtest_result { 367 my $self = shift; 368 369 my $parent = $self->{+FACET_DATA}->{parent} or return; 370 my $children = $parent->{children} || []; 371 372 $children = $self->{+RESULT_CLASS}->new(@$children)->upgrade 373 unless blessed($children) && $children->isa($self->{+RESULT_CLASS}); 374 375 return $children; 376} 377 378sub has_bailout { $_[0]->bailout ? 1 : 0 } 379sub the_bailout { my ($b) = $_[0]->bailout; $b } 380 381sub bailout { 382 my $self = shift; 383 my $control = $self->{+FACET_DATA}->{control} or return; 384 return $control if $control->{halt}; 385 return; 386} 387 388sub bailout_brief { 389 my $self = shift; 390 my $bo = $self->bailout or return; 391 392 my $reason = $bo->{details} or return "BAILED OUT"; 393 return "BAILED OUT: $reason"; 394} 395 396sub bailout_reason { 397 my $self = shift; 398 my $bo = $self->bailout or return; 399 return $bo->{details} || ''; 400} 401 402sub has_plan { $_[0]->{+FACET_DATA}->{plan} ? 1 : 0 } 403sub the_plan { $_[0]->the_facet('plan') } 404sub plan { $_[0]->facet('plan') } 405 406sub plan_brief { 407 my $self = shift; 408 409 my $plan = $self->{+FACET_DATA}->{plan} or return; 410 411 my $base = $self->_plan_brief($plan); 412 413 my $reason = $plan->{details} or return $base; 414 return "$base: $reason"; 415} 416 417sub _plan_brief { 418 my $self = shift; 419 my ($plan) = @_; 420 421 return 'NO PLAN' if $plan->{none}; 422 return "SKIP ALL" if $plan->{skip} || !$plan->{count}; 423 return "PLAN $plan->{count}"; 424} 425 426sub has_amnesty { $_[0]->{+FACET_DATA}->{amnesty} ? 1 : 0 } 427sub the_amnesty { $_[0]->the_facet('amnesty') } 428sub amnesty { $_[0]->facet('amnesty') } 429sub amnesty_reasons { map { $_->{details} } $_[0]->amnesty } 430 431sub has_todos { &first(sub { uc($_->{tag}) eq 'TODO' }, $_[0]->amnesty) ? 1 : 0 } 432sub todos { grep { uc($_->{tag}) eq 'TODO' } $_[0]->amnesty } 433sub todo_reasons { map { $_->{details} || 'TODO' } $_[0]->todos } 434 435sub has_skips { &first(sub { uc($_->{tag}) eq 'SKIP' }, $_[0]->amnesty) ? 1 : 0 } 436sub skips { grep { uc($_->{tag}) eq 'SKIP' } $_[0]->amnesty } 437sub skip_reasons { map { $_->{details} || 'SKIP' } $_[0]->skips } 438 439my %TODO_OR_SKIP = (SKIP => 1, TODO => 1); 440sub has_other_amnesty { &first( sub { !$TODO_OR_SKIP{uc($_->{tag})} }, $_[0]->amnesty) ? 1 : 0 } 441sub other_amnesty { grep { !$TODO_OR_SKIP{uc($_->{tag})} } $_[0]->amnesty } 442sub other_amnesty_reasons { map { $_->{details} || $_->{tag} || 'AMNESTY' } $_[0]->other_amnesty } 443 444sub has_errors { $_[0]->{+FACET_DATA}->{errors} ? 1 : 0 } 445sub the_errors { $_[0]->the_facet('errors') } 446sub errors { $_[0]->facet('errors') } 447sub error_messages { map { $_->{details} || $_->{tag} || 'ERROR' } $_[0]->errors } 448 449sub error_brief { 450 my $self = shift; 451 452 my $errors = $self->{+FACET_DATA}->{errors} or return; 453 454 my $base = @$errors > 1 ? "ERRORS" : "ERROR"; 455 456 return $base unless @$errors; 457 458 my ($msg, @extra) = split /[\n\r]+/, $errors->[0]->{details}; 459 460 my $out = "$base: $msg"; 461 462 $out .= " [...]" if @extra || @$errors > 1; 463 464 return $out; 465} 466 467sub has_info { $_[0]->{+FACET_DATA}->{info} ? 1 : 0 } 468sub the_info { $_[0]->the_facet('info') } 469sub info { $_[0]->facet('info') } 470sub info_messages { map { $_->{details} } $_[0]->info } 471 472sub has_diags { &first(sub { uc($_->{tag}) eq 'DIAG' }, $_[0]->info) ? 1 : 0 } 473sub diags { grep { uc($_->{tag}) eq 'DIAG' } $_[0]->info } 474sub diag_messages { map { $_->{details} || 'DIAG' } $_[0]->diags } 475 476sub has_notes { &first(sub { uc($_->{tag}) eq 'NOTE' }, $_[0]->info) ? 1 : 0 } 477sub notes { grep { uc($_->{tag}) eq 'NOTE' } $_[0]->info } 478sub note_messages { map { $_->{details} || 'NOTE' } $_[0]->notes } 479 480my %NOTE_OR_DIAG = (NOTE => 1, DIAG => 1); 481sub has_other_info { &first(sub { !$NOTE_OR_DIAG{uc($_->{tag})} }, $_[0]->info) ? 1 : 0 } 482sub other_info { grep { !$NOTE_OR_DIAG{uc($_->{tag})} } $_[0]->info } 483sub other_info_messages { map { $_->{details} || $_->{tag} || 'INFO' } $_[0]->other_info } 484 4851; 486 487__END__ 488 489=pod 490 491=encoding UTF-8 492 493=head1 NAME 494 495Test2::API::InterceptResult::Event - Representation of an event for use in 496testing other test tools. 497 498=head1 DESCRIPTION 499 500C<intercept { ... }> from L<Test2::API> returns an instance of 501L<Test2::API::InterceptResult> which is a blessed arrayref of 502L<Test2::API::InterceptResult::Event> objects. 503 504This POD documents the methods of these events, which are mainly provided for 505you 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 556Please pay attention to what these return, many return a scalar when 557applicable or an empty list when not (as opposed to undef). Many also always 558return a list of 0 or more items. Some always return a scalar. Note that none 559of the methods care about context, their behavior is consistent regardless of 560scalar, list, or void context. 561 562This was done because this class was specifically designed to be used in a list 563and generate more lists in bulk operations. Sometimes in a map you want nothing 564to show up for the event, and you do not want an undef in its place. In general 565single event instances are not going to be used alone, though that is allowed. 566 567As a general rule any method prefixed with C<the_> implies the event should 568have exactly 1 of the specified item, and and exception will be thrown if there 569are 0, or more than 1 of the item. 570 571=head2 ATTRIBUTES 572 573=over 4 574 575=item $hashref = $event->facet_data 576 577This will return the facet data hashref, which is all Test2 cares about for any 578given event. 579 580=item $class = $event->result_class 581 582This is normally L<Test2::API::InterceptResult>. This is set at construction so 583that 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 593Create a deep copy of the event. Modifying either event will not effect the 594other. 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 606These are both aliases of the same functionality. 607 608This will always return either a true value, or a false value. This never 609returns a list. 610 611This method may be relatively slow (still super fast) because it determines 612pass or fail by creating an instance of L<Test2::Hub> and asking it to process 613the event, and then asks the hub for its pass/fail state. This is slower than 614bulding in logic to do the check, but it is more reliable as it will always 615tell you what the hub thinks, so the logic will never be out of date relative 616to the Test2 logic that actually cares. 617 618=item STRING_OR_EMPTY_LIST = $event->brief 619 620Not all events have a brief, some events are not rendered by the formatter, 621others have no "brief" data worth seeing. When this is the case an empty list 622is returned. This is done intentionally so it can be used in a map operation 623without having C<undef> being included in the result. 624 625When a brief can be generated it is always a single 1-line string, and is 626returned as-is, not in a list. 627 628Possible 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 652Note that only the first applicable brief is returned. This is essnetially a 653poor-mans TAP that only includes facets that could (but not necessarily do) 654cause a failure. 655 656=item $hashref = $event->flatten 657 658=item $hashref = $event->flatten(include_subevents => 1) 659 660This ALWAYS returns a hashref. This puts all the most useful data for the most 661interesting facets into a single hashref for easy validation. 662 663If there are no meaningful facets this will return an empty hashref. 664 665If given the 'include_subevents' parameter it will also include subtest data: 666 667Here is a list of EVERY possible field. If a field is not applicable it will 668not 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 693You 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 706You 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 719Always 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 753if C<< (include_subtest => 1) >> was provided as a parameter then the following 754will be included. This is the result of turning all subtest child events into 755an L<Test2::API::InterceptResult> instance and calling the C<flatten> method on 756it. 757 758 subevents => Test2::API::InterceptResult->new(@child_events)->flatten(...), 759 760=item If a bail-out is being requested 761 762If 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 770This returns a limited summary. See C<flatten()>, which is usually a better 771option. 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 794This always returns a list of 0 or more items. This fetches the facet instances 795from the event. For facets like 'assert' this will always return 0 or 1 796item. For events like 'info' (diags, notes) this will return 0 or more 797instances, once for each instance of the facet. 798 799These will be blessed into the proper L<Test2::EventFacet> subclass. If no 800subclass can be found it will be blessed as an 801L<Test2::API::InterceptResult::Facet> generic facet class. 802 803=item $undef_or_facet = $event->the_facet($name) 804 805If you know you will have exactly 1 instance of a facet you can call this. 806 807If you are correct and there is exactly one instance of the facet it will 808always return the hashref. 809 810If there are 0 instances of the facet this will reutrn undef, not an empty 811list. 812 813If there are more than 1 instance this will throw an exception because your 814assumption was incorrect. 815 816=back 817 818=head2 TRACE FACET 819 820=over 4 821 822=item @list_of_facets = $event->trace 823 824TODO 825 826=item $undef_or_hashref = $event->the_trace 827 828This returns the trace hashref, or undef if it is not present. 829 830=item $undef_or_arrayref = $event->frame 831 832If a trace is present, and has a caller frame, this will be an arrayref: 833 834 [$package, $file, $line, $subname] 835 836If the trace is not present, or has no caller frame this will return undef. 837 838=item $undef_or_string = $event->trace_details 839 840This is usually undef, but occasionally has a string that overrides the 841file/line number debugging a trace usually provides on test failure. 842 843=item $undef_or_string = $event->trace_package 844 845Same as C<(caller())[0]>, the first element of the trace frame. 846 847Will be undef if not present. 848 849=item $undef_or_string = $event->trace_file 850 851Same as C<(caller())[1]>, the second element of the trace frame. 852 853Will be undef if not present. 854 855=item $undef_or_integer = $event->trace_line 856 857Same as C<(caller())[2]>, the third element of the trace frame. 858 859Will be undef if not present. 860 861=item $undef_or_string = $event->trace_subname 862 863=item $undef_or_string = $event->trace_tool 864 865Aliases for the same thing 866 867Same as C<(caller($level))[4]>, the fourth element of the trace frame. 868 869Will be undef if not present. 870 871=item $undef_or_string = $event->trace_signature 872 873A string that is a unique signature for the trace. If a single context 874generates multiple events they will all have the same signature. This can be 875used to tie assertions and diagnostics sent as seperate events together after 876the fact. 877 878=back 879 880=head2 ASSERT FACET 881 882=over 4 883 884=item $bool = $event->has_assert 885 886Returns true if the event has an assert facet, false if it does not. 887 888=item $undef_or_hashref = $event->the_assert 889 890Returns the assert facet if present, undef if it is not. 891 892=item @list_of_facets = $event->assert 893 894TODO 895 896=item EMPTY_LIST_OR_STRING = $event->assert_brief 897 898Returns a string giving a brief of the assertion if an assertion is present. 899Returns 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 909True if a subetest is present in this event. 910 911=item $undef_or_hashref = $event->the_subtest 912 913Get the one subtest if present, otherwise undef. 914 915=item @list_of_facets = $event->subtest 916 917TODO 918 919=item EMPTY_LIST_OR_OBJECT = $event->subtest_result 920 921Returns an empty list if there is no subtest. 922 923Get 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 933True if there was a bailout 934 935=item $undef_hashref = $event->the_bailout 936 937Return the control facet if it requested a bailout. 938 939=item EMPTY_LIST_OR_HASHREF = $event->bailout 940 941Get a list of 0 or 1 hashrefs. The hashref will be the control facet if a 942bail-out was requested. 943 944=item EMPTY_LIST_OR_STRING = $event->bailout_brief 945 946Get the brief of the balout if present. 947 948=item EMPTY_LIST_OR_STRING = $event->bailout_reason 949 950Get the reason for the bailout, an empty string if no reason was provided, or 951an empty list if there was no bailout. 952 953=back 954 955=head2 PLAN FACET 956 957TODO 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 973TODO 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 1007TODO 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 1025TODO 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 1059The source code repository for Test2 can be found at 1060F<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 1080Copyright 2020 Chad Granum E<lt>exodist@cpan.orgE<gt>. 1081 1082This program is free software; you can redistribute it and/or 1083modify it under the same terms as Perl itself. 1084 1085See F<http://dev.perl.org/licenses/> 1086 1087=cut 1088