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