This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Sync Test-Simple with CPAN version 1.302173
[perl5.git] / cpan / Test-Simple / lib / Test2 / Hub.pm
1 package Test2::Hub;
2 use strict;
3 use warnings;
4
5 our $VERSION = '1.302173';
6
7
8 use Carp qw/carp croak confess/;
9 use Test2::Util qw/get_tid gen_uid/;
10
11 use Scalar::Util qw/weaken/;
12 use List::Util qw/first/;
13
14 use Test2::Util::ExternalMeta qw/meta get_meta set_meta delete_meta/;
15 use 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
39 my $UUID_VIA;
40
41 sub 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
67 sub is_subtest { 0 }
68
69 sub _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
84 sub 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
97 sub 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
125 sub format {
126     my $self = shift;
127
128     my $old = $self->{+_FORMATTER};
129     ($self->{+_FORMATTER}) = @_ if @_;
130
131     return $old;
132 }
133
134 sub is_local {
135     my $self = shift;
136     return $$ == $self->{+PID}
137         && get_tid() == $self->{+TID};
138 }
139
140 sub 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
155 sub 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
166 sub 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
181 sub 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
189 sub 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
201 sub pre_unfilter {
202     my $self = shift;
203     my %subs = map {$_ => $_} @_;
204     @{$self->{+_PRE_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_PRE_FILTERS}};
205 }
206
207 sub 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;
221 sub 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;
234 sub remove_context_acquire {
235     my $self = shift;
236     my %subs = map {$_ => $_} @_;
237     @{$self->{+_CONTEXT_ACQUIRE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_ACQUIRE}};
238 }
239
240 sub 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
252 sub remove_context_init {
253     my $self = shift;
254     my %subs = map {$_ => $_} @_;
255     @{$self->{+_CONTEXT_INIT}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_INIT}};
256 }
257
258 sub 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
270 sub remove_context_release {
271     my $self = shift;
272     my %subs = map {$_ => $_} @_;
273     @{$self->{+_CONTEXT_RELEASE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_RELEASE}};
274 }
275
276 sub 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
319 sub 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}->{terminate};
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}->{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}->{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
392 sub terminate {
393     my $self = shift;
394     my ($code) = @_;
395     exit($code);
396 }
397
398 sub 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
408 sub 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 (($plan && $plan eq 'NO PLAN') || ($do_plan && !$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"
452 Test already ended!
453 First End:  $ffile line $fline
454 Second 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
466 sub 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
496 sub 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
515 sub 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
527 sub 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
535 1;
536
537 __END__
538
539 =pod
540
541 =encoding UTF-8
542
543 =head1 NAME
544
545 Test2::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
556 The hub is the place where all events get processed and handed off to the
557 formatter. The hub also tracks test state, and provides several hooks into the
558 event pipeline.
559
560 =head1 COMMON TASKS
561
562 =head2 SENDING EVENTS
563
564     $hub->send($event)
565
566 The C<send()> method is used to issue an event to the hub. This method will
567 handle thread/fork sync, filters, listeners, TAP output, etc.
568
569 =head2 ALTERING OR REMOVING EVENTS
570
571 You can use either C<filter()> or C<pre_filter()>, depending on your
572 needs. 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
594 By default, filters are not inherited by child hubs. That means if you start a
595 subtest, the subtest will not inherit the filter. You can change this behavior
596 with 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
610 By default listeners are not inherited by child hubs. That means if you start a
611 subtest, the subtest will not inherit the listener. You can change this behavior
612 with 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
627 follow_up subs are called only once, either when done_testing is called, or in
628 an END block.
629
630 =head2 SETTING THE FORMATTER
631
632 By default an instance of L<Test2::Formatter::TAP> is created and used.
633
634     my $old = $hub->format(My::Formatter->new);
635
636 Setting the formatter will REPLACE any existing formatter. You may set the
637 formatter to undef to prevent output. The old formatter will be returned if one
638 was 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
646 This is where all events enter the hub for processing.
647
648 =item $hub->process($event)
649
650 This is called by send after it does any IPC handling. You can use this to
651 bypass the IPC process, but in general you should avoid using this.
652
653 =item $old = $hub->format($formatter)
654
655 Replace the existing formatter instance with a new one. Formatters must be
656 objects that implement a C<< $formatter->write($event) >> method.
657
658 =item $sub = $hub->listen(sub { ... }, %optional_params)
659
660 You can use this to record all events AFTER they have been sent to the
661 formatter. No changes made here will be meaningful, except possibly to other
662 listeners.
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
672 Normally listeners are not inherited by child hubs such as subtests. You can
673 add the C<< inherit => 1 >> parameter to allow a listener to be inherited.
674
675 =item $hub->unlisten($sub)
676
677 You can use this to remove a listen callback. You must pass in the coderef
678 returned 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
684 These can be used to add filters. Filters can modify, replace, or remove events
685 before 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
700 If you are not using threads, forking, or IPC then the only difference between
701 a C<filter> and a C<pre_filter> is that C<pre_filter> subs run first. When you
702 are using threads, forking, or IPC, pre_filters happen to events before they
703 are sent to their destination proc/thread, ordinary filters happen only in the
704 destination hub/thread.
705
706 You cannot add a regular filter to a hub if the hub was created in another
707 process or thread. You can always add a pre_filter.
708
709 =item $hub->unfilter($sub)
710
711 =item $hub->pre_unfilter($sub)
712
713 These can be used to remove filters and pre_filters. The C<$sub> argument is
714 the reference returned by C<filter()> or C<pre_filter()>.
715
716 =item $hub->follow_op(sub { ... })
717
718 Use this to add behaviors that are called just before the hub is finalized. The
719 only 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
729 follow_up subs are called only once, ether when done_testing is called, or in
730 an END block.
731
732 =item $sub = $hub->add_context_acquire(sub { ... });
733
734 Add a callback that will be called every time someone tries to acquire a
735 context. It gets a single argument, a reference of the hash of parameters
736 being used the construct the context. This is your chance to change the
737 parameters by directly altering the hash.
738
739     test2_add_callback_context_acquire(sub {
740         my $params = shift;
741         $params->{level}++;
742     });
743
744 This is a very scary API function. Please do not use this unless you need to.
745 This is here for L<Test::Builder> and backwards compatibility. This has you
746 directly manipulate the hash instead of returning a new one for performance
747 reasons.
748
749 B<Note> Using this hook could have a huge performance impact.
750
751 The coderef you provide is returned and can be used to remove the hook later.
752
753 =item $hub->remove_context_acquire($sub);
754
755 This can be used to remove a context acquire hook.
756
757 =item $sub = $hub->add_context_init(sub { ... });
758
759 This allows you to add callbacks that will trigger every time a new context is
760 created for the hub. The only argument to the sub will be the
761 L<Test2::API::Context> instance that was created.
762
763 B<Note> Using this hook could have a huge performance impact.
764
765 The coderef you provide is returned and can be used to remove the hook later.
766
767 =item $hub->remove_context_init($sub);
768
769 This can be used to remove a context init hook.
770
771 =item $sub = $hub->add_context_release(sub { ... });
772
773 This allows you to add callbacks that will trigger every time a context for
774 this hub is released. The only argument to the sub will be the
775 L<Test2::API::Context> instance that was released. These will run in reverse
776 order.
777
778 B<Note> Using this hook could have a huge performance impact.
779
780 The coderef you provide is returned and can be used to remove the hook later.
781
782 =item $hub->remove_context_release($sub);
783
784 This can be used to remove a context release hook.
785
786 =item $hub->cull()
787
788 Cull any IPC events (and process them).
789
790 =item $pid = $hub->pid()
791
792 Get the process id under which the hub was created.
793
794 =item $tid = $hub->tid()
795
796 Get the thread id under which the hub was created.
797
798 =item $hud = $hub->hid()
799
800 Get the identifier string of the hub.
801
802 =item $uuid = $hub->uuid()
803
804 If UUID tagging is enabled (see L<Test2::API>) then the hub will have a UUID.
805
806 =item $ipc = $hub->ipc()
807
808 Get the IPC object used by the hub.
809
810 =item $hub->set_no_ending($bool)
811
812 =item $bool = $hub->no_ending
813
814 This can be used to disable auto-ending behavior for a hub. The auto-ending
815 behavior is triggered by an end block and is used to cull IPC events, and
816 output the final plan if the plan was 'no_plan'.
817
818 =item $bool = $hub->active
819
820 =item $hub->set_active($bool)
821
822 These are used to get/set the 'active' attribute. When true this attribute will
823 force C<< hub->finalize() >> to take action even if there is no plan, and no
824 tests have been run. This flag is useful for plugins that add follow-up
825 behaviors 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
835 Reset all state to the start. This sets the test count to 0, clears the plan,
836 removes the failures, etc.
837
838 =item $num = $hub->count
839
840 Get the number of tests that have been run.
841
842 =item $num = $hub->failed
843
844 Get the number of failures (Not all failures come from a test fail, so this
845 number can be larger than the count).
846
847 =item $bool = $hub->ended
848
849 True if the testing has ended. This MAY return the stack frame of the tool that
850 ended the test, but that is not guaranteed.
851
852 =item $bool = $hub->is_passing
853
854 =item $hub->is_passing($bool)
855
856 Check if the overall test run is a failure. Can also be used to set the
857 pass/fail status.
858
859 =item $hub->plan($plan)
860
861 =item $plan = $hub->plan
862
863 Get or set the plan. The plan must be an integer larger than 0, the string
864 'no_plan', or the string 'skip_all'.
865
866 =item $bool = $hub->check_plan
867
868 Check if the plan and counts match, but only if the tests have ended. If tests
869 have not ended this will return undef, otherwise it will be a true/false.
870
871 =back
872
873 =head1 THIRD PARTY META-DATA
874
875 This object consumes L<Test2::Util::ExternalMeta> which provides a consistent
876 way for you to attach meta-data to instances of this class. This is useful for
877 tools, plugins, and other extensions.
878
879 =head1 SOURCE
880
881 The source code repository for Test2 can be found at
882 F<http://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
902 Copyright 2019 Chad Granum E<lt>exodist@cpan.orgE<gt>.
903
904 This program is free software; you can redistribute it and/or
905 modify it under the same terms as Perl itself.
906
907 See F<http://dev.perl.org/licenses/>
908
909 =cut