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