1package Test2::Hub; 2use strict; 3use warnings; 4 5our $VERSION = '1.302199'; 6 7 8use Carp qw/carp croak confess/; 9use Test2::Util qw/get_tid gen_uid/; 10 11use Scalar::Util qw/weaken/; 12use List::Util qw/first/; 13 14use Test2::Util::ExternalMeta qw/meta get_meta set_meta delete_meta/; 15use Test2::Util::HashBase qw{ 16 pid tid hid ipc 17 nested buffered 18 no_ending 19 _filters 20 _pre_filters 21 _listeners 22 _follow_ups 23 _formatter 24 _context_acquire 25 _context_init 26 _context_release 27 28 uuid 29 active 30 count 31 failed 32 ended 33 bailed_out 34 _passing 35 _plan 36 skip_reason 37}; 38 39my $UUID_VIA; 40 41sub init { 42 my $self = shift; 43 44 $self->{+PID} = $$; 45 $self->{+TID} = get_tid(); 46 $self->{+HID} = gen_uid(); 47 48 $UUID_VIA ||= Test2::API::_add_uuid_via_ref(); 49 $self->{+UUID} = ${$UUID_VIA}->('hub') if $$UUID_VIA; 50 51 $self->{+NESTED} = 0 unless defined $self->{+NESTED}; 52 $self->{+BUFFERED} = 0 unless defined $self->{+BUFFERED}; 53 54 $self->{+COUNT} = 0; 55 $self->{+FAILED} = 0; 56 $self->{+_PASSING} = 1; 57 58 if (my $formatter = delete $self->{formatter}) { 59 $self->format($formatter); 60 } 61 62 if (my $ipc = $self->{+IPC}) { 63 $ipc->add_hub($self->{+HID}); 64 } 65} 66 67sub is_subtest { 0 } 68 69sub _tb_reset { 70 my $self = shift; 71 72 # Nothing to do 73 return if $self->{+PID} == $$ && $self->{+TID} == get_tid(); 74 75 $self->{+PID} = $$; 76 $self->{+TID} = get_tid(); 77 $self->{+HID} = gen_uid(); 78 79 if (my $ipc = $self->{+IPC}) { 80 $ipc->add_hub($self->{+HID}); 81 } 82} 83 84sub reset_state { 85 my $self = shift; 86 87 $self->{+COUNT} = 0; 88 $self->{+FAILED} = 0; 89 $self->{+_PASSING} = 1; 90 91 delete $self->{+_PLAN}; 92 delete $self->{+ENDED}; 93 delete $self->{+BAILED_OUT}; 94 delete $self->{+SKIP_REASON}; 95} 96 97sub inherit { 98 my $self = shift; 99 my ($from, %params) = @_; 100 101 $self->{+NESTED} ||= 0; 102 103 $self->{+_FORMATTER} = $from->{+_FORMATTER} 104 unless $self->{+_FORMATTER} || exists($params{formatter}); 105 106 if ($from->{+IPC} && !$self->{+IPC} && !exists($params{ipc})) { 107 my $ipc = $from->{+IPC}; 108 $self->{+IPC} = $ipc; 109 $ipc->add_hub($self->{+HID}); 110 } 111 112 if (my $ls = $from->{+_LISTENERS}) { 113 push @{$self->{+_LISTENERS}} => grep { $_->{inherit} } @$ls; 114 } 115 116 if (my $pfs = $from->{+_PRE_FILTERS}) { 117 push @{$self->{+_PRE_FILTERS}} => grep { $_->{inherit} } @$pfs; 118 } 119 120 if (my $fs = $from->{+_FILTERS}) { 121 push @{$self->{+_FILTERS}} => grep { $_->{inherit} } @$fs; 122 } 123} 124 125sub format { 126 my $self = shift; 127 128 my $old = $self->{+_FORMATTER}; 129 ($self->{+_FORMATTER}) = @_ if @_; 130 131 return $old; 132} 133 134sub is_local { 135 my $self = shift; 136 return $$ == $self->{+PID} 137 && get_tid() == $self->{+TID}; 138} 139 140sub listen { 141 my $self = shift; 142 my ($sub, %params) = @_; 143 144 carp "Useless addition of a listener in a child process or thread!" 145 if $$ != $self->{+PID} || get_tid() != $self->{+TID}; 146 147 croak "listen only takes coderefs for arguments, got '$sub'" 148 unless ref $sub && ref $sub eq 'CODE'; 149 150 push @{$self->{+_LISTENERS}} => { %params, code => $sub }; 151 152 $sub; # Intentional return. 153} 154 155sub unlisten { 156 my $self = shift; 157 158 carp "Useless removal of a listener in a child process or thread!" 159 if $$ != $self->{+PID} || get_tid() != $self->{+TID}; 160 161 my %subs = map {$_ => $_} @_; 162 163 @{$self->{+_LISTENERS}} = grep { !$subs{$_->{code}} } @{$self->{+_LISTENERS}}; 164} 165 166sub filter { 167 my $self = shift; 168 my ($sub, %params) = @_; 169 170 carp "Useless addition of a filter in a child process or thread!" 171 if $$ != $self->{+PID} || get_tid() != $self->{+TID}; 172 173 croak "filter only takes coderefs for arguments, got '$sub'" 174 unless ref $sub && ref $sub eq 'CODE'; 175 176 push @{$self->{+_FILTERS}} => { %params, code => $sub }; 177 178 $sub; # Intentional Return 179} 180 181sub unfilter { 182 my $self = shift; 183 carp "Useless removal of a filter in a child process or thread!" 184 if $$ != $self->{+PID} || get_tid() != $self->{+TID}; 185 my %subs = map {$_ => $_} @_; 186 @{$self->{+_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_FILTERS}}; 187} 188 189sub pre_filter { 190 my $self = shift; 191 my ($sub, %params) = @_; 192 193 croak "pre_filter only takes coderefs for arguments, got '$sub'" 194 unless ref $sub && ref $sub eq 'CODE'; 195 196 push @{$self->{+_PRE_FILTERS}} => { %params, code => $sub }; 197 198 $sub; # Intentional Return 199} 200 201sub pre_unfilter { 202 my $self = shift; 203 my %subs = map {$_ => $_} @_; 204 @{$self->{+_PRE_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_PRE_FILTERS}}; 205} 206 207sub follow_up { 208 my $self = shift; 209 my ($sub) = @_; 210 211 carp "Useless addition of a follow-up in a child process or thread!" 212 if $$ != $self->{+PID} || get_tid() != $self->{+TID}; 213 214 croak "follow_up only takes coderefs for arguments, got '$sub'" 215 unless ref $sub && ref $sub eq 'CODE'; 216 217 push @{$self->{+_FOLLOW_UPS}} => $sub; 218} 219 220*add_context_aquire = \&add_context_acquire; 221sub add_context_acquire { 222 my $self = shift; 223 my ($sub) = @_; 224 225 croak "add_context_acquire only takes coderefs for arguments, got '$sub'" 226 unless ref $sub && ref $sub eq 'CODE'; 227 228 push @{$self->{+_CONTEXT_ACQUIRE}} => $sub; 229 230 $sub; # Intentional return. 231} 232 233*remove_context_aquire = \&remove_context_acquire; 234sub remove_context_acquire { 235 my $self = shift; 236 my %subs = map {$_ => $_} @_; 237 @{$self->{+_CONTEXT_ACQUIRE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_ACQUIRE}}; 238} 239 240sub add_context_init { 241 my $self = shift; 242 my ($sub) = @_; 243 244 croak "add_context_init only takes coderefs for arguments, got '$sub'" 245 unless ref $sub && ref $sub eq 'CODE'; 246 247 push @{$self->{+_CONTEXT_INIT}} => $sub; 248 249 $sub; # Intentional return. 250} 251 252sub remove_context_init { 253 my $self = shift; 254 my %subs = map {$_ => $_} @_; 255 @{$self->{+_CONTEXT_INIT}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_INIT}}; 256} 257 258sub add_context_release { 259 my $self = shift; 260 my ($sub) = @_; 261 262 croak "add_context_release only takes coderefs for arguments, got '$sub'" 263 unless ref $sub && ref $sub eq 'CODE'; 264 265 push @{$self->{+_CONTEXT_RELEASE}} => $sub; 266 267 $sub; # Intentional return. 268} 269 270sub remove_context_release { 271 my $self = shift; 272 my %subs = map {$_ => $_} @_; 273 @{$self->{+_CONTEXT_RELEASE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_RELEASE}}; 274} 275 276sub send { 277 my $self = shift; 278 my ($e) = @_; 279 280 $e->eid; 281 282 $e->add_hub( 283 { 284 details => ref($self), 285 286 buffered => $self->{+BUFFERED}, 287 hid => $self->{+HID}, 288 nested => $self->{+NESTED}, 289 pid => $self->{+PID}, 290 tid => $self->{+TID}, 291 uuid => $self->{+UUID}, 292 293 ipc => $self->{+IPC} ? 1 : 0, 294 } 295 ); 296 297 $e->set_uuid(${$UUID_VIA}->('event')) if $$UUID_VIA; 298 299 if ($self->{+_PRE_FILTERS}) { 300 for (@{$self->{+_PRE_FILTERS}}) { 301 $e = $_->{code}->($self, $e); 302 return unless $e; 303 } 304 } 305 306 my $ipc = $self->{+IPC} || return $self->process($e); 307 308 if($e->global) { 309 $ipc->send($self->{+HID}, $e, 'GLOBAL'); 310 return $self->process($e); 311 } 312 313 return $ipc->send($self->{+HID}, $e) 314 if $$ != $self->{+PID} || get_tid() != $self->{+TID}; 315 316 $self->process($e); 317} 318 319sub process { 320 my $self = shift; 321 my ($e) = @_; 322 323 if ($self->{+_FILTERS}) { 324 for (@{$self->{+_FILTERS}}) { 325 $e = $_->{code}->($self, $e); 326 return unless $e; 327 } 328 } 329 330 # Optimize the most common case 331 my $type = ref($e); 332 if ($type eq 'Test2::Event::Pass' || ($type eq 'Test2::Event::Ok' && $e->{pass})) { 333 my $count = ++($self->{+COUNT}); 334 $self->{+_FORMATTER}->write($e, $count) if $self->{+_FORMATTER}; 335 336 if ($self->{+_LISTENERS}) { 337 $_->{code}->($self, $e, $count) for @{$self->{+_LISTENERS}}; 338 } 339 340 return $e; 341 } 342 343 my $f = $e->facet_data; 344 345 my $fail = 0; 346 $fail = 1 if $f->{assert} && !$f->{assert}->{pass}; 347 $fail = 1 if $f->{errors} && grep { $_->{fail} } @{$f->{errors}}; 348 $fail = 0 if $f->{amnesty}; 349 350 $self->{+COUNT}++ if $f->{assert}; 351 $self->{+FAILED}++ if $fail && $f->{assert}; 352 $self->{+_PASSING} = 0 if $fail; 353 354 my $code = $f->{control} ? $f->{control}->{terminate} : undef; 355 my $count = $self->{+COUNT}; 356 357 if (my $plan = $f->{plan}) { 358 if ($plan->{skip}) { 359 $self->plan('SKIP'); 360 $self->set_skip_reason($plan->{details} || 1); 361 $code ||= 0; 362 } 363 elsif ($plan->{none}) { 364 $self->plan('NO PLAN'); 365 } 366 else { 367 $self->plan($plan->{count}); 368 } 369 } 370 371 $e->callback($self) if $f->{control} && $f->{control}->{has_callback}; 372 373 $self->{+_FORMATTER}->write($e, $count, $f) if $self->{+_FORMATTER}; 374 375 if ($self->{+_LISTENERS}) { 376 $_->{code}->($self, $e, $count, $f) for @{$self->{+_LISTENERS}}; 377 } 378 379 if ($f->{control} && $f->{control}->{halt}) { 380 $code ||= 255; 381 $self->set_bailed_out($e); 382 } 383 384 if (defined $code) { 385 $self->{+_FORMATTER}->terminate($e, $f) if $self->{+_FORMATTER}; 386 $self->terminate($code, $e, $f); 387 } 388 389 return $e; 390} 391 392sub terminate { 393 my $self = shift; 394 my ($code) = @_; 395 exit($code); 396} 397 398sub cull { 399 my $self = shift; 400 401 my $ipc = $self->{+IPC} || return; 402 return if $self->{+PID} != $$ || $self->{+TID} != get_tid(); 403 404 # No need to do IPC checks on culled events 405 $self->process($_) for $ipc->cull($self->{+HID}); 406} 407 408sub finalize { 409 my $self = shift; 410 my ($trace, $do_plan) = @_; 411 412 $self->cull(); 413 414 my $plan = $self->{+_PLAN}; 415 my $count = $self->{+COUNT}; 416 my $failed = $self->{+FAILED}; 417 my $active = $self->{+ACTIVE}; 418 419 # return if NOTHING was done. 420 unless ($active || $do_plan || defined($plan) || $count || $failed) { 421 $self->{+_FORMATTER}->finalize($plan, $count, $failed, 0, $self->is_subtest) if $self->{+_FORMATTER}; 422 return; 423 } 424 425 unless ($self->{+ENDED}) { 426 if ($self->{+_FOLLOW_UPS}) { 427 $_->($trace, $self) for reverse @{$self->{+_FOLLOW_UPS}}; 428 } 429 430 # These need to be refreshed now 431 $plan = $self->{+_PLAN}; 432 $count = $self->{+COUNT}; 433 $failed = $self->{+FAILED}; 434 435 if ((defined($plan) && $plan eq 'NO PLAN') || ($do_plan && !defined($plan))) { 436 $self->send( 437 Test2::Event::Plan->new( 438 trace => $trace, 439 max => $count, 440 ) 441 ); 442 } 443 $plan = $self->{+_PLAN}; 444 } 445 446 my $frame = $trace->frame; 447 if($self->{+ENDED}) { 448 my (undef, $ffile, $fline) = @{$self->{+ENDED}}; 449 my (undef, $sfile, $sline) = @$frame; 450 451 die <<" EOT" 452Test already ended! 453First End: $ffile line $fline 454Second End: $sfile line $sline 455 EOT 456 } 457 458 $self->{+ENDED} = $frame; 459 my $pass = $self->is_passing(); # Generate the final boolean. 460 461 $self->{+_FORMATTER}->finalize($plan, $count, $failed, $pass, $self->is_subtest) if $self->{+_FORMATTER}; 462 463 return $pass; 464} 465 466sub is_passing { 467 my $self = shift; 468 469 ($self->{+_PASSING}) = @_ if @_; 470 471 # If we already failed just return 0. 472 my $pass = $self->{+_PASSING} or return 0; 473 return $self->{+_PASSING} = 0 if $self->{+FAILED}; 474 475 my $count = $self->{+COUNT}; 476 my $ended = $self->{+ENDED}; 477 my $plan = $self->{+_PLAN}; 478 479 return $pass if !$count && $plan && $plan =~ m/^SKIP$/; 480 481 return $self->{+_PASSING} = 0 482 if $ended && (!$count || !$plan); 483 484 return $pass unless $plan && $plan =~ m/^\d+$/; 485 486 if ($ended) { 487 return $self->{+_PASSING} = 0 if $count != $plan; 488 } 489 else { 490 return $self->{+_PASSING} = 0 if $count > $plan; 491 } 492 493 return $pass; 494} 495 496sub plan { 497 my $self = shift; 498 499 return $self->{+_PLAN} unless @_; 500 501 my ($plan) = @_; 502 503 confess "You cannot unset the plan" 504 unless defined $plan; 505 506 confess "You cannot change the plan" 507 if $self->{+_PLAN} && $self->{+_PLAN} !~ m/^NO PLAN$/; 508 509 confess "'$plan' is not a valid plan! Plan must be an integer greater than 0, 'NO PLAN', or 'SKIP'" 510 unless $plan =~ m/^(\d+|NO PLAN|SKIP)$/; 511 512 $self->{+_PLAN} = $plan; 513} 514 515sub check_plan { 516 my $self = shift; 517 518 return undef unless $self->{+ENDED}; 519 my $plan = $self->{+_PLAN} || return undef; 520 521 return 1 if $plan !~ m/^\d+$/; 522 523 return 1 if $plan == $self->{+COUNT}; 524 return 0; 525} 526 527sub DESTROY { 528 my $self = shift; 529 my $ipc = $self->{+IPC} || return; 530 return unless $$ == $self->{+PID}; 531 return unless get_tid() == $self->{+TID}; 532 $ipc->drop_hub($self->{+HID}); 533} 534 5351; 536 537__END__ 538 539=pod 540 541=encoding UTF-8 542 543=head1 NAME 544 545Test2::Hub - The conduit through which all events flow. 546 547=head1 SYNOPSIS 548 549 use Test2::Hub; 550 551 my $hub = Test2::Hub->new(); 552 $hub->send(...); 553 554=head1 DESCRIPTION 555 556The hub is the place where all events get processed and handed off to the 557formatter. The hub also tracks test state, and provides several hooks into the 558event pipeline. 559 560=head1 COMMON TASKS 561 562=head2 SENDING EVENTS 563 564 $hub->send($event) 565 566The C<send()> method is used to issue an event to the hub. This method will 567handle thread/fork sync, filters, listeners, TAP output, etc. 568 569=head2 ALTERING OR REMOVING EVENTS 570 571You can use either C<filter()> or C<pre_filter()>, depending on your 572needs. Both have identical syntax, so only C<filter()> is shown here. 573 574 $hub->filter(sub { 575 my ($hub, $event) = @_; 576 577 my $action = get_action($event); 578 579 # No action should be taken 580 return $event if $action eq 'none'; 581 582 # You want your filter to remove the event 583 return undef if $action eq 'delete'; 584 585 if ($action eq 'do_it') { 586 my $new_event = copy_event($event); 587 ... Change your copy of the event ... 588 return $new_event; 589 } 590 591 die "Should not happen"; 592 }); 593 594By default, filters are not inherited by child hubs. That means if you start a 595subtest, the subtest will not inherit the filter. You can change this behavior 596with the C<inherit> parameter: 597 598 $hub->filter(sub { ... }, inherit => 1); 599 600=head2 LISTENING FOR EVENTS 601 602 $hub->listen(sub { 603 my ($hub, $event, $number) = @_; 604 605 ... do whatever you want with the event ... 606 607 # return is ignored 608 }); 609 610By default listeners are not inherited by child hubs. That means if you start a 611subtest, the subtest will not inherit the listener. You can change this behavior 612with the C<inherit> parameter: 613 614 $hub->listen(sub { ... }, inherit => 1); 615 616 617=head2 POST-TEST BEHAVIORS 618 619 $hub->follow_up(sub { 620 my ($trace, $hub) = @_; 621 622 ... do whatever you need to ... 623 624 # Return is ignored 625 }); 626 627follow_up subs are called only once, either when done_testing is called, or in 628an END block. 629 630=head2 SETTING THE FORMATTER 631 632By default an instance of L<Test2::Formatter::TAP> is created and used. 633 634 my $old = $hub->format(My::Formatter->new); 635 636Setting the formatter will REPLACE any existing formatter. You may set the 637formatter to undef to prevent output. The old formatter will be returned if one 638was already set. Only one formatter is allowed at a time. 639 640=head1 METHODS 641 642=over 4 643 644=item $hub->send($event) 645 646This is where all events enter the hub for processing. 647 648=item $hub->process($event) 649 650This is called by send after it does any IPC handling. You can use this to 651bypass the IPC process, but in general you should avoid using this. 652 653=item $old = $hub->format($formatter) 654 655Replace the existing formatter instance with a new one. Formatters must be 656objects that implement a C<< $formatter->write($event) >> method. 657 658=item $sub = $hub->listen(sub { ... }, %optional_params) 659 660You can use this to record all events AFTER they have been sent to the 661formatter. No changes made here will be meaningful, except possibly to other 662listeners. 663 664 $hub->listen(sub { 665 my ($hub, $event, $number) = @_; 666 667 ... do whatever you want with the event ... 668 669 # return is ignored 670 }); 671 672Normally listeners are not inherited by child hubs such as subtests. You can 673add the C<< inherit => 1 >> parameter to allow a listener to be inherited. 674 675=item $hub->unlisten($sub) 676 677You can use this to remove a listen callback. You must pass in the coderef 678returned by the C<listen()> method. 679 680=item $sub = $hub->filter(sub { ... }, %optional_params) 681 682=item $sub = $hub->pre_filter(sub { ... }, %optional_params) 683 684These can be used to add filters. Filters can modify, replace, or remove events 685before anything else can see them. 686 687 $hub->filter( 688 sub { 689 my ($hub, $event) = @_; 690 691 return $event; # No Changes 692 return; # Remove the event 693 694 # Or you can modify an event before returning it. 695 $event->modify; 696 return $event; 697 } 698 ); 699 700If you are not using threads, forking, or IPC then the only difference between 701a C<filter> and a C<pre_filter> is that C<pre_filter> subs run first. When you 702are using threads, forking, or IPC, pre_filters happen to events before they 703are sent to their destination proc/thread, ordinary filters happen only in the 704destination hub/thread. 705 706You cannot add a regular filter to a hub if the hub was created in another 707process or thread. You can always add a pre_filter. 708 709=item $hub->unfilter($sub) 710 711=item $hub->pre_unfilter($sub) 712 713These can be used to remove filters and pre_filters. The C<$sub> argument is 714the reference returned by C<filter()> or C<pre_filter()>. 715 716=item $hub->follow_op(sub { ... }) 717 718Use this to add behaviors that are called just before the hub is finalized. The 719only argument to your codeblock will be a L<Test2::EventFacet::Trace> instance. 720 721 $hub->follow_up(sub { 722 my ($trace, $hub) = @_; 723 724 ... do whatever you need to ... 725 726 # Return is ignored 727 }); 728 729follow_up subs are called only once, ether when done_testing is called, or in 730an END block. 731 732=item $sub = $hub->add_context_acquire(sub { ... }); 733 734Add a callback that will be called every time someone tries to acquire a 735context. It gets a single argument, a reference of the hash of parameters 736being used the construct the context. This is your chance to change the 737parameters by directly altering the hash. 738 739 test2_add_callback_context_acquire(sub { 740 my $params = shift; 741 $params->{level}++; 742 }); 743 744This is a very scary API function. Please do not use this unless you need to. 745This is here for L<Test::Builder> and backwards compatibility. This has you 746directly manipulate the hash instead of returning a new one for performance 747reasons. 748 749B<Note> Using this hook could have a huge performance impact. 750 751The coderef you provide is returned and can be used to remove the hook later. 752 753=item $hub->remove_context_acquire($sub); 754 755This can be used to remove a context acquire hook. 756 757=item $sub = $hub->add_context_init(sub { ... }); 758 759This allows you to add callbacks that will trigger every time a new context is 760created for the hub. The only argument to the sub will be the 761L<Test2::API::Context> instance that was created. 762 763B<Note> Using this hook could have a huge performance impact. 764 765The coderef you provide is returned and can be used to remove the hook later. 766 767=item $hub->remove_context_init($sub); 768 769This can be used to remove a context init hook. 770 771=item $sub = $hub->add_context_release(sub { ... }); 772 773This allows you to add callbacks that will trigger every time a context for 774this hub is released. The only argument to the sub will be the 775L<Test2::API::Context> instance that was released. These will run in reverse 776order. 777 778B<Note> Using this hook could have a huge performance impact. 779 780The coderef you provide is returned and can be used to remove the hook later. 781 782=item $hub->remove_context_release($sub); 783 784This can be used to remove a context release hook. 785 786=item $hub->cull() 787 788Cull any IPC events (and process them). 789 790=item $pid = $hub->pid() 791 792Get the process id under which the hub was created. 793 794=item $tid = $hub->tid() 795 796Get the thread id under which the hub was created. 797 798=item $hud = $hub->hid() 799 800Get the identifier string of the hub. 801 802=item $uuid = $hub->uuid() 803 804If UUID tagging is enabled (see L<Test2::API>) then the hub will have a UUID. 805 806=item $ipc = $hub->ipc() 807 808Get the IPC object used by the hub. 809 810=item $hub->set_no_ending($bool) 811 812=item $bool = $hub->no_ending 813 814This can be used to disable auto-ending behavior for a hub. The auto-ending 815behavior is triggered by an end block and is used to cull IPC events, and 816output the final plan if the plan was 'NO PLAN'. 817 818=item $bool = $hub->active 819 820=item $hub->set_active($bool) 821 822These are used to get/set the 'active' attribute. When true this attribute will 823force C<< hub->finalize() >> to take action even if there is no plan, and no 824tests have been run. This flag is useful for plugins that add follow-up 825behaviors that need to run even if no events are seen. 826 827=back 828 829=head2 STATE METHODS 830 831=over 4 832 833=item $hub->reset_state() 834 835Reset all state to the start. This sets the test count to 0, clears the plan, 836removes the failures, etc. 837 838=item $num = $hub->count 839 840Get the number of tests that have been run. 841 842=item $num = $hub->failed 843 844Get the number of failures (Not all failures come from a test fail, so this 845number can be larger than the count). 846 847=item $bool = $hub->ended 848 849True if the testing has ended. This MAY return the stack frame of the tool that 850ended the test, but that is not guaranteed. 851 852=item $bool = $hub->is_passing 853 854=item $hub->is_passing($bool) 855 856Check if the overall test run is a failure. Can also be used to set the 857pass/fail status. 858 859=item $hub->plan($plan) 860 861=item $plan = $hub->plan 862 863Get or set the plan. The plan must be an integer larger than 0, the string 864'NO PLAN', or the string 'SKIP'. 865 866=item $bool = $hub->check_plan 867 868Check if the plan and counts match, but only if the tests have ended. If tests 869have not ended this will return undef, otherwise it will be a true/false. 870 871=back 872 873=head1 THIRD PARTY META-DATA 874 875This object consumes L<Test2::Util::ExternalMeta> which provides a consistent 876way for you to attach meta-data to instances of this class. This is useful for 877tools, plugins, and other extensions. 878 879=head1 SOURCE 880 881The source code repository for Test2 can be found at 882L<https://github.com/Test-More/test-more/>. 883 884=head1 MAINTAINERS 885 886=over 4 887 888=item Chad Granum E<lt>exodist@cpan.orgE<gt> 889 890=back 891 892=head1 AUTHORS 893 894=over 4 895 896=item Chad Granum E<lt>exodist@cpan.orgE<gt> 897 898=back 899 900=head1 COPYRIGHT 901 902Copyright 2020 Chad Granum E<lt>exodist@cpan.orgE<gt>. 903 904This program is free software; you can redistribute it and/or 905modify it under the same terms as Perl itself. 906 907See L<https://dev.perl.org/licenses/> 908 909=cut 910