1package Test2::Event; 2use strict; 3use warnings; 4 5our $VERSION = '1.302175'; 6 7use Scalar::Util qw/blessed reftype/; 8use Carp qw/croak/; 9 10use Test2::Util::HashBase qw/trace -amnesty uuid -_eid -hubs/; 11use Test2::Util::ExternalMeta qw/meta get_meta set_meta delete_meta/; 12use Test2::Util qw/pkg_to_file gen_uid/; 13 14use Test2::EventFacet::About(); 15use Test2::EventFacet::Amnesty(); 16use Test2::EventFacet::Assert(); 17use Test2::EventFacet::Control(); 18use Test2::EventFacet::Error(); 19use Test2::EventFacet::Info(); 20use Test2::EventFacet::Meta(); 21use Test2::EventFacet::Parent(); 22use Test2::EventFacet::Plan(); 23use Test2::EventFacet::Trace(); 24use Test2::EventFacet::Hub(); 25 26# Legacy tools will expect this to be loaded now 27require Test2::Util::Trace; 28 29my %LOADED_FACETS = ( 30 'about' => 'Test2::EventFacet::About', 31 'amnesty' => 'Test2::EventFacet::Amnesty', 32 'assert' => 'Test2::EventFacet::Assert', 33 'control' => 'Test2::EventFacet::Control', 34 'errors' => 'Test2::EventFacet::Error', 35 'info' => 'Test2::EventFacet::Info', 36 'meta' => 'Test2::EventFacet::Meta', 37 'parent' => 'Test2::EventFacet::Parent', 38 'plan' => 'Test2::EventFacet::Plan', 39 'trace' => 'Test2::EventFacet::Trace', 40 'hubs' => 'Test2::EventFacet::Hub', 41); 42 43sub FACET_TYPES { sort values %LOADED_FACETS } 44 45sub load_facet { 46 my $class = shift; 47 my ($facet) = @_; 48 49 return $LOADED_FACETS{$facet} if exists $LOADED_FACETS{$facet}; 50 51 my @check = ($facet); 52 if ('s' eq substr($facet, -1, 1)) { 53 push @check => substr($facet, 0, -1); 54 } 55 else { 56 push @check => $facet . 's'; 57 } 58 59 my $found; 60 for my $check (@check) { 61 my $mod = "Test2::EventFacet::" . ucfirst($facet); 62 my $file = pkg_to_file($mod); 63 next unless eval { require $file; 1 }; 64 $found = $mod; 65 last; 66 } 67 68 return undef unless $found; 69 $LOADED_FACETS{$facet} = $found; 70} 71 72sub causes_fail { 0 } 73sub increments_count { 0 } 74sub diagnostics { 0 } 75sub no_display { 0 } 76sub subtest_id { undef } 77 78sub callback { } 79 80sub terminate { () } 81sub global { () } 82sub sets_plan { () } 83 84sub summary { ref($_[0]) } 85 86sub related { 87 my $self = shift; 88 my ($event) = @_; 89 90 my $tracea = $self->trace or return undef; 91 my $traceb = $event->trace or return undef; 92 93 my $uuida = $tracea->uuid; 94 my $uuidb = $traceb->uuid; 95 if ($uuida && $uuidb) { 96 return 1 if $uuida eq $uuidb; 97 return 0; 98 } 99 100 my $siga = $tracea->signature or return undef; 101 my $sigb = $traceb->signature or return undef; 102 103 return 1 if $siga eq $sigb; 104 return 0; 105} 106 107sub add_hub { 108 my $self = shift; 109 unshift @{$self->{+HUBS}} => @_; 110} 111 112sub add_amnesty { 113 my $self = shift; 114 115 for my $am (@_) { 116 $am = {%$am} if ref($am) ne 'ARRAY'; 117 $am = Test2::EventFacet::Amnesty->new($am); 118 119 push @{$self->{+AMNESTY}} => $am; 120 } 121} 122 123sub eid { $_[0]->{+_EID} ||= gen_uid() } 124 125sub common_facet_data { 126 my $self = shift; 127 128 my %out; 129 130 $out{about} = {package => ref($self) || undef}; 131 if (my $uuid = $self->uuid) { 132 $out{about}->{uuid} = $uuid; 133 } 134 135 $out{about}->{eid} = $self->{+_EID} || $self->eid; 136 137 if (my $trace = $self->trace) { 138 $out{trace} = { %$trace }; 139 } 140 141 if (my $hubs = $self->hubs) { 142 $out{hubs} = $hubs; 143 } 144 145 $out{amnesty} = [map {{ %{$_} }} @{$self->{+AMNESTY}}] 146 if $self->{+AMNESTY}; 147 148 if (my $meta = $self->meta_facet_data) { 149 $out{meta} = $meta; 150 } 151 152 return \%out; 153} 154 155sub meta_facet_data { 156 my $self = shift; 157 158 my $key = Test2::Util::ExternalMeta::META_KEY(); 159 160 my $hash = $self->{$key} or return undef; 161 return {%$hash}; 162} 163 164sub facet_data { 165 my $self = shift; 166 167 my $out = $self->common_facet_data; 168 169 $out->{about}->{details} = $self->summary || undef; 170 $out->{about}->{no_display} = $self->no_display || undef; 171 172 # Might be undef, we want to preserve that 173 my $terminate = $self->terminate; 174 $out->{control} = { 175 global => $self->global || 0, 176 terminate => $terminate, 177 has_callback => $self->can('callback') == \&callback ? 0 : 1, 178 }; 179 180 $out->{assert} = { 181 no_debug => 1, # Legacy behavior 182 pass => $self->causes_fail ? 0 : 1, 183 details => $self->summary, 184 } if $self->increments_count; 185 186 $out->{parent} = {hid => $self->subtest_id} if $self->subtest_id; 187 188 if (my @plan = $self->sets_plan) { 189 $out->{plan} = {}; 190 191 $out->{plan}->{count} = $plan[0] if defined $plan[0]; 192 $out->{plan}->{details} = $plan[2] if defined $plan[2]; 193 194 if ($plan[1]) { 195 $out->{plan}->{skip} = 1 if $plan[1] eq 'SKIP'; 196 $out->{plan}->{none} = 1 if $plan[1] eq 'NO PLAN'; 197 } 198 199 $out->{control}->{terminate} ||= 0 if $out->{plan}->{skip}; 200 } 201 202 if ($self->causes_fail && !$out->{assert}) { 203 $out->{errors} = [ 204 { 205 tag => 'FAIL', 206 fail => 1, 207 details => $self->summary, 208 } 209 ]; 210 } 211 212 my %IGNORE = (trace => 1, about => 1, control => 1); 213 my $do_info = !grep { !$IGNORE{$_} } keys %$out; 214 215 if ($do_info && !$self->no_display && $self->diagnostics) { 216 $out->{info} = [ 217 { 218 tag => 'DIAG', 219 debug => 1, 220 details => $self->summary, 221 } 222 ]; 223 } 224 225 return $out; 226} 227 228sub facets { 229 my $self = shift; 230 my %out; 231 232 my $data = $self->facet_data; 233 my @errors = $self->validate_facet_data($data); 234 die join "\n" => @errors if @errors; 235 236 for my $facet (keys %$data) { 237 my $class = $self->load_facet($facet); 238 my $val = $data->{$facet}; 239 240 unless($class) { 241 $out{$facet} = $val; 242 next; 243 } 244 245 my $is_list = reftype($val) eq 'ARRAY' ? 1 : 0; 246 if ($is_list) { 247 $out{$facet} = [map { $class->new($_) } @$val]; 248 } 249 else { 250 $out{$facet} = $class->new($val); 251 } 252 } 253 254 return \%out; 255} 256 257sub validate_facet_data { 258 my $class_or_self = shift; 259 my ($f, %params); 260 261 $f = shift if @_ && (reftype($_[0]) || '') eq 'HASH'; 262 %params = @_; 263 264 $f ||= $class_or_self->facet_data if blessed($class_or_self); 265 croak "No facet data" unless $f; 266 267 my @errors; 268 269 for my $k (sort keys %$f) { 270 my $fclass = $class_or_self->load_facet($k); 271 272 push @errors => "Could not find a facet class for facet '$k'" 273 if $params{require_facet_class} && !$fclass; 274 275 next unless $fclass; 276 277 my $v = $f->{$k}; 278 next unless defined($v); # undef is always fine 279 280 my $is_list = $fclass->is_list(); 281 my $got_list = reftype($v) eq 'ARRAY' ? 1 : 0; 282 283 push @errors => "Facet '$k' should be a list, but got a single item ($v)" 284 if $is_list && !$got_list; 285 286 push @errors => "Facet '$k' should not be a list, but got a a list ($v)" 287 if $got_list && !$is_list; 288 } 289 290 return @errors; 291} 292 293sub nested { 294 my $self = shift; 295 296 Carp::cluck("Use of Test2::Event->nested() is deprecated, use Test2::Event->trace->nested instead") 297 if $ENV{AUTHOR_TESTING}; 298 299 if (my $hubs = $self->{+HUBS}) { 300 return $hubs->[0]->{nested} if @$hubs; 301 } 302 303 my $trace = $self->{+TRACE} or return undef; 304 return $trace->{nested}; 305} 306 307sub in_subtest { 308 my $self = shift; 309 310 Carp::cluck("Use of Test2::Event->in_subtest() is deprecated, use Test2::Event->trace->hid instead") 311 if $ENV{AUTHOR_TESTING}; 312 313 my $hubs = $self->{+HUBS}; 314 if ($hubs && @$hubs) { 315 return undef unless $hubs->[0]->{nested}; 316 return $hubs->[0]->{hid} 317 } 318 319 my $trace = $self->{+TRACE} or return undef; 320 return undef unless $trace->{nested}; 321 return $trace->{hid}; 322} 323 3241; 325 326__END__ 327 328=pod 329 330=encoding UTF-8 331 332=head1 NAME 333 334Test2::Event - Base class for events 335 336=head1 DESCRIPTION 337 338Base class for all event objects that get passed through 339L<Test2>. 340 341=head1 SYNOPSIS 342 343 package Test2::Event::MyEvent; 344 use strict; 345 use warnings; 346 347 # This will make our class an event subclass (required) 348 use base 'Test2::Event'; 349 350 # Add some accessors (optional) 351 # You are not obligated to use HashBase, you can use any object tool you 352 # want, or roll your own accessors. 353 use Test2::Util::HashBase qw/foo bar baz/; 354 355 # Use this if you want the legacy API to be written for you, for this to 356 # work you will need to implement a facet_data() method. 357 use Test2::Util::Facets2Legacy; 358 359 # Chance to initialize some defaults 360 sub init { 361 my $self = shift; 362 # no other args in @_ 363 364 $self->set_foo('xxx') unless defined $self->foo; 365 366 ... 367 } 368 369 # This is the new way for events to convey data to the Test2 system 370 sub facet_data { 371 my $self = shift; 372 373 # Get common facets such as 'about', 'trace' 'amnesty', and 'meta' 374 my $facet_data = $self->common_facet_data(); 375 376 # Are you making an assertion? 377 $facet_data->{assert} = {pass => 1, details => 'my assertion'}; 378 ... 379 380 return $facet_data; 381 } 382 383 1; 384 385=head1 METHODS 386 387=head2 GENERAL 388 389=over 4 390 391=item $trace = $e->trace 392 393Get a snapshot of the L<Test2::EventFacet::Trace> as it was when this event was 394generated 395 396=item $bool_or_undef = $e->related($e2) 397 398Check if 2 events are related. In this case related means their traces share a 399signature meaning they were created with the same context (or at the very least 400by contexts which share an id, which is the same thing unless someone is doing 401something very bad). 402 403This can be used to reliably link multiple events created by the same tool. For 404instance a failing test like C<ok(0, "fail"> will generate 2 events, one being 405a L<Test2::Event::Ok>, the other being a L<Test2::Event::Diag>, both of these 406events are related having been created under the same context and by the same 407initial tool (though multiple tools may have been nested under the initial 408one). 409 410This will return C<undef> if the relationship cannot be checked, which happens 411if either event has an incomplete or missing trace. This will return C<0> if 412the traces are complete, but do not match. C<1> will be returned if there is a 413match. 414 415=item $e->add_amnesty({tag => $TAG, details => $DETAILS}); 416 417This can be used to add amnesty to this event. Amnesty only effects failing 418assertions in most cases, but some formatters may display them for passing 419assertions, or even non-assertions as well. 420 421Amnesty will prevent a failed assertion from causing the overall test to fail. 422In other words it marks a failure as expected and allowed. 423 424B<Note:> This is how 'TODO' is implemented under the hood. TODO is essentially 425amnesty with the 'TODO' tag. The details are the reason for the TODO. 426 427=item $uuid = $e->uuid 428 429If UUID tagging is enabled (See L<Test::API>) then any event that has made its 430way through a hub will be tagged with a UUID. A newly created event will not 431yet be tagged in most cases. 432 433=item $class = $e->load_facet($name) 434 435This method is used to load a facet by name (or key). It will attempt to load 436the facet class, if it succeeds it will return the class it loaded. If it fails 437it will return C<undef>. This caches the result at the class level so that 438future calls will be faster. 439 440The C<$name> variable should be the key used to access the facet in a facets 441hashref. For instance the assertion facet has the key 'assert', the information 442facet has the 'info' key, and the error facet has the key 'errors'. You may 443include or omit the 's' at the end of the name, the method is smart enough to 444try both the 's' and no-'s' forms, it will check what you provided first, and 445if that is not found it will add or strip the 's and try again. 446 447=item @classes = $e->FACET_TYPES() 448 449=item @classes = Test2::Event->FACET_TYPES() 450 451This returns a list of all facets that have been loaded using the 452C<load_facet()> method. This will not return any classes that have not been 453loaded, or have been loaded directly without a call to C<load_facet()>. 454 455B<Note:> The core facet types are automatically loaded and populated in this 456list. 457 458=back 459 460=head2 NEW API 461 462=over 4 463 464=item $hashref = $e->common_facet_data(); 465 466This can be used by subclasses to generate a starting facet data hashref. This 467will populate the hashref with the trace, meta, amnesty, and about facets. 468These facets are nearly always produced the same way for all events. 469 470=item $hashref = $e->facet_data() 471 472If you do not override this then the default implementation will attempt to 473generate facets from the legacy API. This generation is limited only to what 474the legacy API can provide. It is recommended that you override this method and 475write out explicit facet data. 476 477=item $hashref = $e->facets() 478 479This takes the hashref from C<facet_data()> and blesses each facet into the 480proper C<Test2::EventFacet::*> subclass. If no class can be found for any given 481facet it will be passed along unchanged. 482 483=item @errors = $e->validate_facet_data(); 484 485=item @errors = $e->validate_facet_data(%params); 486 487=item @errors = $e->validate_facet_data(\%facets, %params); 488 489=item @errors = Test2::Event->validate_facet_data(%params); 490 491=item @errors = Test2::Event->validate_facet_data(\%facets, %params); 492 493This method will validate facet data and return a list of errors. If no errors 494are found this will return an empty list. 495 496This can be called as an object method with no arguments, in which case the 497C<facet_data()> method will be called to get the facet data to be validated. 498 499When used as an object method the C<\%facet_data> argument may be omitted. 500 501When used as a class method the C<\%facet_data> argument is required. 502 503Remaining arguments will be slurped into a C<%params> hash. 504 505Currently only 1 parameter is defined: 506 507=over 4 508 509=item require_facet_class => $BOOL 510 511When set to true (default is false) this will reject any facets where a facet 512class cannot be found. Normally facets without classes are assumed to be custom 513and are ignored. 514 515=back 516 517=back 518 519=head3 WHAT ARE FACETS? 520 521Facets are how events convey their purpose to the Test2 internals and 522formatters. An event without facets will have no intentional effect on the 523overall test state, and will not be displayed at all by most formatters, except 524perhaps to say that an event of an unknown type was seen. 525 526Facets are produced by the C<facet_data()> subroutine, which you should 527nearly-always override. C<facet_data()> is expected to return a hashref where 528each key is the facet type, and the value is either a hashref with the data for 529that facet, or an array of hashrefs. Some facets must be defined as single 530hashrefs, some must be defined as an array of hashrefs, No facets allow both. 531 532C<facet_data()> B<MUST NOT> bless the data it returns, the main hashref, and 533nested facet hashrefs B<MUST> be bare, though items contained within each 534facet may be blessed. The data returned by this method B<should> also be copies 535of the internal data in order to prevent accidental state modification. 536 537C<facets()> takes the data from C<facet_data()> and blesses it into the 538C<Test2::EventFacet::*> packages. This is rarely used however, the EventFacet 539packages are primarily for convenience and documentation. The EventFacet 540classes are not used at all internally, instead the raw data is used. 541 542Here is a list of facet types by package. The packages are not used internally, 543but are where the documentation for each type is kept. 544 545B<Note:> Every single facet type has the C<'details'> field. This field is 546always intended for human consumption, and when provided, should explain the 547'why' for the facet. All other fields are facet specific. 548 549=over 4 550 551=item about => {...} 552 553L<Test2::EventFacet::About> 554 555This contains information about the event itself such as the event package 556name. The C<details> field for this facet is an overall summary of the event. 557 558=item assert => {...} 559 560L<Test2::EventFacet::Assert> 561 562This facet is used if an assertion was made. The C<details> field of this facet 563is the description of the assertion. 564 565=item control => {...} 566 567L<Test2::EventFacet::Control> 568 569This facet is used to tell the L<Test2::Event::Hub> about special actions the 570event causes. Things like halting all testing, terminating the current test, 571etc. In this facet the C<details> field explains why any special action was 572taken. 573 574B<Note:> This is how bail-out is implemented. 575 576=item meta => {...} 577 578L<Test2::EventFacet::Meta> 579 580The meta facet contains all the meta-data attached to the event. In this case 581the C<details> field has no special meaning, but may be present if something 582sets the 'details' meta-key on the event. 583 584=item parent => {...} 585 586L<Test2::EventFacet::Parent> 587 588This facet contains nested events and similar details for subtests. In this 589facet the C<details> field will typically be the name of the subtest. 590 591=item plan => {...} 592 593L<Test2::EventFacet::Plan> 594 595This facet tells the system that a plan has been set. The C<details> field of 596this is usually left empty, but when present explains why the plan is what it 597is, this is most useful if the plan is to skip-all. 598 599=item trace => {...} 600 601L<Test2::EventFacet::Trace> 602 603This facet contains information related to when and where the event was 604generated. This is how the test file and line number of a failure is known. 605This facet can also help you to tell if tests are related. 606 607In this facet the C<details> field overrides the "failed at test_file.t line 60842." message provided on assertion failure. 609 610=item amnesty => [{...}, ...] 611 612L<Test2::EventFacet::Amnesty> 613 614The amnesty facet is a list instead of a single item, this is important as 615amnesty can come from multiple places at once. 616 617For each instance of amnesty the C<details> field explains why amnesty was 618granted. 619 620B<Note:> Outside of formatters amnesty only acts to forgive a failing 621assertion. 622 623=item errors => [{...}, ...] 624 625L<Test2::EventFacet::Error> 626 627The errors facet is a list instead of a single item, any number of errors can 628be listed. In this facet C<details> describes the error, or may contain the raw 629error message itself (such as an exception). In perl exception may be blessed 630objects, as such the raw data for this facet may contain nested items which are 631blessed. 632 633Not all errors are considered fatal, there is a C<fail> field that must be set 634for an error to cause the test to fail. 635 636B<Note:> This facet is unique in that the field name is 'errors' while the 637package is 'Error'. This is because this is the only facet type that is both a 638list, and has a name where the plural is not the same as the singular. This may 639cause some confusion, but I feel it will be less confusing than the 640alternative. 641 642=item info => [{...}, ...] 643 644L<Test2::EventFacet::Info> 645 646The 'info' facet is a list instead of a single item, any quantity of extra 647information can be attached to an event. Some information may be critical 648diagnostics, others may be simply commentary in nature, this is determined by 649the C<debug> flag. 650 651For this facet the C<details> flag is the info itself. This info may be a 652string, or it may be a data structure to display. This is one of the few facet 653types that may contain blessed items. 654 655=back 656 657=head2 LEGACY API 658 659=over 4 660 661=item $bool = $e->causes_fail 662 663Returns true if this event should result in a test failure. In general this 664should be false. 665 666=item $bool = $e->increments_count 667 668Should be true if this event should result in a test count increment. 669 670=item $e->callback($hub) 671 672If your event needs to have extra effects on the L<Test2::Hub> you can override 673this method. 674 675This is called B<BEFORE> your event is passed to the formatter. 676 677=item $num = $e->nested 678 679If this event is nested inside of other events, this should be the depth of 680nesting. (This is mainly for subtests) 681 682=item $bool = $e->global 683 684Set this to true if your event is global, that is ALL threads and processes 685should see it no matter when or where it is generated. This is not a common 686thing to want, it is used by bail-out and skip_all to end testing. 687 688=item $code = $e->terminate 689 690This is called B<AFTER> your event has been passed to the formatter. This 691should normally return undef, only change this if your event should cause the 692test to exit immediately. 693 694If you want this event to cause the test to exit you should return the exit 695code here. Exit code of 0 means exit success, any other integer means exit with 696failure. 697 698This is used by L<Test2::Event::Plan> to exit 0 when the plan is 699'skip_all'. This is also used by L<Test2::Event:Bail> to force the test 700to exit with a failure. 701 702This is called after the event has been sent to the formatter in order to 703ensure the event is seen and understood. 704 705=item $msg = $e->summary 706 707This is intended to be a human readable summary of the event. This should 708ideally only be one line long, but you can use multiple lines if necessary. This 709is intended for human consumption. You do not need to make it easy for machines 710to understand. 711 712The default is to simply return the event package name. 713 714=item ($count, $directive, $reason) = $e->sets_plan() 715 716Check if this event sets the testing plan. It will return an empty list if it 717does not. If it does set the plan it will return a list of 1 to 3 items in 718order: Expected Test Count, Test Directive, Reason for directive. 719 720=item $bool = $e->diagnostics 721 722True if the event contains diagnostics info. This is useful because a 723non-verbose harness may choose to hide events that are not in this category. 724Some formatters may choose to send these to STDERR instead of STDOUT to ensure 725they are seen. 726 727=item $bool = $e->no_display 728 729False by default. This will return true on events that should not be displayed 730by formatters. 731 732=item $id = $e->in_subtest 733 734If the event is inside a subtest this should have the subtest ID. 735 736=item $id = $e->subtest_id 737 738If the event is a final subtest event, this should contain the subtest ID. 739 740=back 741 742=head1 THIRD PARTY META-DATA 743 744This object consumes L<Test2::Util::ExternalMeta> which provides a consistent 745way for you to attach meta-data to instances of this class. This is useful for 746tools, plugins, and other extensions. 747 748=head1 SOURCE 749 750The source code repository for Test2 can be found at 751F<http://github.com/Test-More/test-more/>. 752 753=head1 MAINTAINERS 754 755=over 4 756 757=item Chad Granum E<lt>exodist@cpan.orgE<gt> 758 759=back 760 761=head1 AUTHORS 762 763=over 4 764 765=item Chad Granum E<lt>exodist@cpan.orgE<gt> 766 767=back 768 769=head1 COPYRIGHT 770 771Copyright 2019 Chad Granum E<lt>exodist@cpan.orgE<gt>. 772 773This program is free software; you can redistribute it and/or 774modify it under the same terms as Perl itself. 775 776See F<http://dev.perl.org/licenses/> 777 778=cut 779