xref: /openbsd-src/gnu/usr.bin/perl/cpan/Test-Simple/lib/Test2/Hub.pm (revision 5486feefcc8cb79b19e014ab332cc5dfd05b3b33)
15759b3d2Safresh1package Test2::Hub;
25759b3d2Safresh1use strict;
35759b3d2Safresh1use warnings;
45759b3d2Safresh1
5*5486feefSafresh1our $VERSION = '1.302199';
65759b3d2Safresh1
75759b3d2Safresh1
85759b3d2Safresh1use Carp qw/carp croak confess/;
9f3efcd01Safresh1use Test2::Util qw/get_tid gen_uid/;
105759b3d2Safresh1
115759b3d2Safresh1use Scalar::Util qw/weaken/;
125759b3d2Safresh1use List::Util qw/first/;
135759b3d2Safresh1
145759b3d2Safresh1use Test2::Util::ExternalMeta qw/meta get_meta set_meta delete_meta/;
155759b3d2Safresh1use Test2::Util::HashBase qw{
165759b3d2Safresh1    pid tid hid ipc
175759b3d2Safresh1    nested buffered
185759b3d2Safresh1    no_ending
195759b3d2Safresh1    _filters
205759b3d2Safresh1    _pre_filters
215759b3d2Safresh1    _listeners
225759b3d2Safresh1    _follow_ups
235759b3d2Safresh1    _formatter
245759b3d2Safresh1    _context_acquire
255759b3d2Safresh1    _context_init
265759b3d2Safresh1    _context_release
275759b3d2Safresh1
285759b3d2Safresh1    uuid
295759b3d2Safresh1    active
305759b3d2Safresh1    count
315759b3d2Safresh1    failed
325759b3d2Safresh1    ended
335759b3d2Safresh1    bailed_out
345759b3d2Safresh1    _passing
355759b3d2Safresh1    _plan
365759b3d2Safresh1    skip_reason
375759b3d2Safresh1};
385759b3d2Safresh1
395759b3d2Safresh1my $UUID_VIA;
405759b3d2Safresh1
415759b3d2Safresh1sub init {
425759b3d2Safresh1    my $self = shift;
435759b3d2Safresh1
445759b3d2Safresh1    $self->{+PID} = $$;
455759b3d2Safresh1    $self->{+TID} = get_tid();
46f3efcd01Safresh1    $self->{+HID} = gen_uid();
475759b3d2Safresh1
485759b3d2Safresh1    $UUID_VIA ||= Test2::API::_add_uuid_via_ref();
495759b3d2Safresh1    $self->{+UUID} = ${$UUID_VIA}->('hub') if $$UUID_VIA;
505759b3d2Safresh1
515759b3d2Safresh1    $self->{+NESTED}   = 0 unless defined $self->{+NESTED};
525759b3d2Safresh1    $self->{+BUFFERED} = 0 unless defined $self->{+BUFFERED};
535759b3d2Safresh1
545759b3d2Safresh1    $self->{+COUNT}    = 0;
555759b3d2Safresh1    $self->{+FAILED}   = 0;
565759b3d2Safresh1    $self->{+_PASSING} = 1;
575759b3d2Safresh1
585759b3d2Safresh1    if (my $formatter = delete $self->{formatter}) {
595759b3d2Safresh1        $self->format($formatter);
605759b3d2Safresh1    }
615759b3d2Safresh1
625759b3d2Safresh1    if (my $ipc = $self->{+IPC}) {
635759b3d2Safresh1        $ipc->add_hub($self->{+HID});
645759b3d2Safresh1    }
655759b3d2Safresh1}
665759b3d2Safresh1
675759b3d2Safresh1sub is_subtest { 0 }
685759b3d2Safresh1
695759b3d2Safresh1sub _tb_reset {
705759b3d2Safresh1    my $self = shift;
715759b3d2Safresh1
725759b3d2Safresh1    # Nothing to do
735759b3d2Safresh1    return if $self->{+PID} == $$ && $self->{+TID} == get_tid();
745759b3d2Safresh1
755759b3d2Safresh1    $self->{+PID} = $$;
765759b3d2Safresh1    $self->{+TID} = get_tid();
77f3efcd01Safresh1    $self->{+HID} = gen_uid();
785759b3d2Safresh1
795759b3d2Safresh1    if (my $ipc = $self->{+IPC}) {
805759b3d2Safresh1        $ipc->add_hub($self->{+HID});
815759b3d2Safresh1    }
825759b3d2Safresh1}
835759b3d2Safresh1
845759b3d2Safresh1sub reset_state {
855759b3d2Safresh1    my $self = shift;
865759b3d2Safresh1
875759b3d2Safresh1    $self->{+COUNT} = 0;
885759b3d2Safresh1    $self->{+FAILED} = 0;
895759b3d2Safresh1    $self->{+_PASSING} = 1;
905759b3d2Safresh1
915759b3d2Safresh1    delete $self->{+_PLAN};
925759b3d2Safresh1    delete $self->{+ENDED};
935759b3d2Safresh1    delete $self->{+BAILED_OUT};
945759b3d2Safresh1    delete $self->{+SKIP_REASON};
955759b3d2Safresh1}
965759b3d2Safresh1
975759b3d2Safresh1sub inherit {
985759b3d2Safresh1    my $self = shift;
995759b3d2Safresh1    my ($from, %params) = @_;
1005759b3d2Safresh1
1015759b3d2Safresh1    $self->{+NESTED} ||= 0;
1025759b3d2Safresh1
1035759b3d2Safresh1    $self->{+_FORMATTER} = $from->{+_FORMATTER}
1045759b3d2Safresh1        unless $self->{+_FORMATTER} || exists($params{formatter});
1055759b3d2Safresh1
1065759b3d2Safresh1    if ($from->{+IPC} && !$self->{+IPC} && !exists($params{ipc})) {
1075759b3d2Safresh1        my $ipc = $from->{+IPC};
1085759b3d2Safresh1        $self->{+IPC} = $ipc;
1095759b3d2Safresh1        $ipc->add_hub($self->{+HID});
1105759b3d2Safresh1    }
1115759b3d2Safresh1
1125759b3d2Safresh1    if (my $ls = $from->{+_LISTENERS}) {
1135759b3d2Safresh1        push @{$self->{+_LISTENERS}} => grep { $_->{inherit} } @$ls;
1145759b3d2Safresh1    }
1155759b3d2Safresh1
1165759b3d2Safresh1    if (my $pfs = $from->{+_PRE_FILTERS}) {
1175759b3d2Safresh1        push @{$self->{+_PRE_FILTERS}} => grep { $_->{inherit} } @$pfs;
1185759b3d2Safresh1    }
1195759b3d2Safresh1
1205759b3d2Safresh1    if (my $fs = $from->{+_FILTERS}) {
1215759b3d2Safresh1        push @{$self->{+_FILTERS}} => grep { $_->{inherit} } @$fs;
1225759b3d2Safresh1    }
1235759b3d2Safresh1}
1245759b3d2Safresh1
1255759b3d2Safresh1sub format {
1265759b3d2Safresh1    my $self = shift;
1275759b3d2Safresh1
1285759b3d2Safresh1    my $old = $self->{+_FORMATTER};
1295759b3d2Safresh1    ($self->{+_FORMATTER}) = @_ if @_;
1305759b3d2Safresh1
1315759b3d2Safresh1    return $old;
1325759b3d2Safresh1}
1335759b3d2Safresh1
1345759b3d2Safresh1sub is_local {
1355759b3d2Safresh1    my $self = shift;
1365759b3d2Safresh1    return $$ == $self->{+PID}
1375759b3d2Safresh1        && get_tid() == $self->{+TID};
1385759b3d2Safresh1}
1395759b3d2Safresh1
1405759b3d2Safresh1sub listen {
1415759b3d2Safresh1    my $self = shift;
1425759b3d2Safresh1    my ($sub, %params) = @_;
1435759b3d2Safresh1
1445759b3d2Safresh1    carp "Useless addition of a listener in a child process or thread!"
1455759b3d2Safresh1        if $$ != $self->{+PID} || get_tid() != $self->{+TID};
1465759b3d2Safresh1
1475759b3d2Safresh1    croak "listen only takes coderefs for arguments, got '$sub'"
1485759b3d2Safresh1        unless ref $sub && ref $sub eq 'CODE';
1495759b3d2Safresh1
1505759b3d2Safresh1    push @{$self->{+_LISTENERS}} => { %params, code => $sub };
1515759b3d2Safresh1
1525759b3d2Safresh1    $sub; # Intentional return.
1535759b3d2Safresh1}
1545759b3d2Safresh1
1555759b3d2Safresh1sub unlisten {
1565759b3d2Safresh1    my $self = shift;
1575759b3d2Safresh1
1585759b3d2Safresh1    carp "Useless removal of a listener in a child process or thread!"
1595759b3d2Safresh1        if $$ != $self->{+PID} || get_tid() != $self->{+TID};
1605759b3d2Safresh1
1615759b3d2Safresh1    my %subs = map {$_ => $_} @_;
1625759b3d2Safresh1
1635759b3d2Safresh1    @{$self->{+_LISTENERS}} = grep { !$subs{$_->{code}} } @{$self->{+_LISTENERS}};
1645759b3d2Safresh1}
1655759b3d2Safresh1
1665759b3d2Safresh1sub filter {
1675759b3d2Safresh1    my $self = shift;
1685759b3d2Safresh1    my ($sub, %params) = @_;
1695759b3d2Safresh1
1705759b3d2Safresh1    carp "Useless addition of a filter in a child process or thread!"
1715759b3d2Safresh1        if $$ != $self->{+PID} || get_tid() != $self->{+TID};
1725759b3d2Safresh1
1735759b3d2Safresh1    croak "filter only takes coderefs for arguments, got '$sub'"
1745759b3d2Safresh1        unless ref $sub && ref $sub eq 'CODE';
1755759b3d2Safresh1
1765759b3d2Safresh1    push @{$self->{+_FILTERS}} => { %params, code => $sub };
1775759b3d2Safresh1
1785759b3d2Safresh1    $sub; # Intentional Return
1795759b3d2Safresh1}
1805759b3d2Safresh1
1815759b3d2Safresh1sub unfilter {
1825759b3d2Safresh1    my $self = shift;
1835759b3d2Safresh1    carp "Useless removal of a filter in a child process or thread!"
1845759b3d2Safresh1        if $$ != $self->{+PID} || get_tid() != $self->{+TID};
1855759b3d2Safresh1    my %subs = map {$_ => $_} @_;
1865759b3d2Safresh1    @{$self->{+_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_FILTERS}};
1875759b3d2Safresh1}
1885759b3d2Safresh1
1895759b3d2Safresh1sub pre_filter {
1905759b3d2Safresh1    my $self = shift;
1915759b3d2Safresh1    my ($sub, %params) = @_;
1925759b3d2Safresh1
1935759b3d2Safresh1    croak "pre_filter only takes coderefs for arguments, got '$sub'"
1945759b3d2Safresh1        unless ref $sub && ref $sub eq 'CODE';
1955759b3d2Safresh1
1965759b3d2Safresh1    push @{$self->{+_PRE_FILTERS}} => { %params, code => $sub };
1975759b3d2Safresh1
1985759b3d2Safresh1    $sub; # Intentional Return
1995759b3d2Safresh1}
2005759b3d2Safresh1
2015759b3d2Safresh1sub pre_unfilter {
2025759b3d2Safresh1    my $self = shift;
2035759b3d2Safresh1    my %subs = map {$_ => $_} @_;
2045759b3d2Safresh1    @{$self->{+_PRE_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_PRE_FILTERS}};
2055759b3d2Safresh1}
2065759b3d2Safresh1
2075759b3d2Safresh1sub follow_up {
2085759b3d2Safresh1    my $self = shift;
2095759b3d2Safresh1    my ($sub) = @_;
2105759b3d2Safresh1
2115759b3d2Safresh1    carp "Useless addition of a follow-up in a child process or thread!"
2125759b3d2Safresh1        if $$ != $self->{+PID} || get_tid() != $self->{+TID};
2135759b3d2Safresh1
2145759b3d2Safresh1    croak "follow_up only takes coderefs for arguments, got '$sub'"
2155759b3d2Safresh1        unless ref $sub && ref $sub eq 'CODE';
2165759b3d2Safresh1
2175759b3d2Safresh1    push @{$self->{+_FOLLOW_UPS}} => $sub;
2185759b3d2Safresh1}
2195759b3d2Safresh1
2205759b3d2Safresh1*add_context_aquire = \&add_context_acquire;
2215759b3d2Safresh1sub add_context_acquire {
2225759b3d2Safresh1    my $self = shift;
2235759b3d2Safresh1    my ($sub) = @_;
2245759b3d2Safresh1
2255759b3d2Safresh1    croak "add_context_acquire only takes coderefs for arguments, got '$sub'"
2265759b3d2Safresh1        unless ref $sub && ref $sub eq 'CODE';
2275759b3d2Safresh1
2285759b3d2Safresh1    push @{$self->{+_CONTEXT_ACQUIRE}} => $sub;
2295759b3d2Safresh1
2305759b3d2Safresh1    $sub; # Intentional return.
2315759b3d2Safresh1}
2325759b3d2Safresh1
2335759b3d2Safresh1*remove_context_aquire = \&remove_context_acquire;
2345759b3d2Safresh1sub remove_context_acquire {
2355759b3d2Safresh1    my $self = shift;
2365759b3d2Safresh1    my %subs = map {$_ => $_} @_;
2375759b3d2Safresh1    @{$self->{+_CONTEXT_ACQUIRE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_ACQUIRE}};
2385759b3d2Safresh1}
2395759b3d2Safresh1
2405759b3d2Safresh1sub add_context_init {
2415759b3d2Safresh1    my $self = shift;
2425759b3d2Safresh1    my ($sub) = @_;
2435759b3d2Safresh1
2445759b3d2Safresh1    croak "add_context_init only takes coderefs for arguments, got '$sub'"
2455759b3d2Safresh1        unless ref $sub && ref $sub eq 'CODE';
2465759b3d2Safresh1
2475759b3d2Safresh1    push @{$self->{+_CONTEXT_INIT}} => $sub;
2485759b3d2Safresh1
2495759b3d2Safresh1    $sub; # Intentional return.
2505759b3d2Safresh1}
2515759b3d2Safresh1
2525759b3d2Safresh1sub remove_context_init {
2535759b3d2Safresh1    my $self = shift;
2545759b3d2Safresh1    my %subs = map {$_ => $_} @_;
2555759b3d2Safresh1    @{$self->{+_CONTEXT_INIT}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_INIT}};
2565759b3d2Safresh1}
2575759b3d2Safresh1
2585759b3d2Safresh1sub add_context_release {
2595759b3d2Safresh1    my $self = shift;
2605759b3d2Safresh1    my ($sub) = @_;
2615759b3d2Safresh1
2625759b3d2Safresh1    croak "add_context_release only takes coderefs for arguments, got '$sub'"
2635759b3d2Safresh1        unless ref $sub && ref $sub eq 'CODE';
2645759b3d2Safresh1
2655759b3d2Safresh1    push @{$self->{+_CONTEXT_RELEASE}} => $sub;
2665759b3d2Safresh1
2675759b3d2Safresh1    $sub; # Intentional return.
2685759b3d2Safresh1}
2695759b3d2Safresh1
2705759b3d2Safresh1sub remove_context_release {
2715759b3d2Safresh1    my $self = shift;
2725759b3d2Safresh1    my %subs = map {$_ => $_} @_;
2735759b3d2Safresh1    @{$self->{+_CONTEXT_RELEASE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_RELEASE}};
2745759b3d2Safresh1}
2755759b3d2Safresh1
2765759b3d2Safresh1sub send {
2775759b3d2Safresh1    my $self = shift;
2785759b3d2Safresh1    my ($e) = @_;
2795759b3d2Safresh1
280f3efcd01Safresh1    $e->eid;
281f3efcd01Safresh1
2825759b3d2Safresh1    $e->add_hub(
2835759b3d2Safresh1        {
2845759b3d2Safresh1            details => ref($self),
2855759b3d2Safresh1
2865759b3d2Safresh1            buffered => $self->{+BUFFERED},
2875759b3d2Safresh1            hid      => $self->{+HID},
2885759b3d2Safresh1            nested   => $self->{+NESTED},
2895759b3d2Safresh1            pid      => $self->{+PID},
2905759b3d2Safresh1            tid      => $self->{+TID},
2915759b3d2Safresh1            uuid     => $self->{+UUID},
2925759b3d2Safresh1
2935759b3d2Safresh1            ipc => $self->{+IPC} ? 1 : 0,
2945759b3d2Safresh1        }
2955759b3d2Safresh1    );
2965759b3d2Safresh1
2975759b3d2Safresh1    $e->set_uuid(${$UUID_VIA}->('event')) if $$UUID_VIA;
2985759b3d2Safresh1
2995759b3d2Safresh1    if ($self->{+_PRE_FILTERS}) {
3005759b3d2Safresh1        for (@{$self->{+_PRE_FILTERS}}) {
3015759b3d2Safresh1            $e = $_->{code}->($self, $e);
3025759b3d2Safresh1            return unless $e;
3035759b3d2Safresh1        }
3045759b3d2Safresh1    }
3055759b3d2Safresh1
3065759b3d2Safresh1    my $ipc = $self->{+IPC} || return $self->process($e);
3075759b3d2Safresh1
3085759b3d2Safresh1    if($e->global) {
3095759b3d2Safresh1        $ipc->send($self->{+HID}, $e, 'GLOBAL');
3105759b3d2Safresh1        return $self->process($e);
3115759b3d2Safresh1    }
3125759b3d2Safresh1
3135759b3d2Safresh1    return $ipc->send($self->{+HID}, $e)
3145759b3d2Safresh1        if $$ != $self->{+PID} || get_tid() != $self->{+TID};
3155759b3d2Safresh1
3165759b3d2Safresh1    $self->process($e);
3175759b3d2Safresh1}
3185759b3d2Safresh1
3195759b3d2Safresh1sub process {
3205759b3d2Safresh1    my $self = shift;
3215759b3d2Safresh1    my ($e) = @_;
3225759b3d2Safresh1
3235759b3d2Safresh1    if ($self->{+_FILTERS}) {
3245759b3d2Safresh1        for (@{$self->{+_FILTERS}}) {
3255759b3d2Safresh1            $e = $_->{code}->($self, $e);
3265759b3d2Safresh1            return unless $e;
3275759b3d2Safresh1        }
3285759b3d2Safresh1    }
3295759b3d2Safresh1
3305759b3d2Safresh1    # Optimize the most common case
3315759b3d2Safresh1    my $type = ref($e);
3325759b3d2Safresh1    if ($type eq 'Test2::Event::Pass' || ($type eq 'Test2::Event::Ok' && $e->{pass})) {
3335759b3d2Safresh1        my $count = ++($self->{+COUNT});
3345759b3d2Safresh1        $self->{+_FORMATTER}->write($e, $count) if $self->{+_FORMATTER};
3355759b3d2Safresh1
3365759b3d2Safresh1        if ($self->{+_LISTENERS}) {
3375759b3d2Safresh1            $_->{code}->($self, $e, $count) for @{$self->{+_LISTENERS}};
3385759b3d2Safresh1        }
3395759b3d2Safresh1
3405759b3d2Safresh1        return $e;
3415759b3d2Safresh1    }
3425759b3d2Safresh1
3435759b3d2Safresh1    my $f = $e->facet_data;
3445759b3d2Safresh1
3455759b3d2Safresh1    my $fail = 0;
3465759b3d2Safresh1    $fail = 1 if $f->{assert} && !$f->{assert}->{pass};
3475759b3d2Safresh1    $fail = 1 if $f->{errors} && grep { $_->{fail} } @{$f->{errors}};
3485759b3d2Safresh1    $fail = 0 if $f->{amnesty};
3495759b3d2Safresh1
3505759b3d2Safresh1    $self->{+COUNT}++ if $f->{assert};
3515759b3d2Safresh1    $self->{+FAILED}++ if $fail && $f->{assert};
3525759b3d2Safresh1    $self->{+_PASSING} = 0 if $fail;
3535759b3d2Safresh1
354256a93a4Safresh1    my $code = $f->{control} ? $f->{control}->{terminate} : undef;
3555759b3d2Safresh1    my $count = $self->{+COUNT};
3565759b3d2Safresh1
3575759b3d2Safresh1    if (my $plan = $f->{plan}) {
3585759b3d2Safresh1        if ($plan->{skip}) {
3595759b3d2Safresh1            $self->plan('SKIP');
3605759b3d2Safresh1            $self->set_skip_reason($plan->{details} || 1);
3615759b3d2Safresh1            $code ||= 0;
3625759b3d2Safresh1        }
3635759b3d2Safresh1        elsif ($plan->{none}) {
3645759b3d2Safresh1            $self->plan('NO PLAN');
3655759b3d2Safresh1        }
3665759b3d2Safresh1        else {
3675759b3d2Safresh1            $self->plan($plan->{count});
3685759b3d2Safresh1        }
3695759b3d2Safresh1    }
3705759b3d2Safresh1
371256a93a4Safresh1    $e->callback($self) if $f->{control} && $f->{control}->{has_callback};
3725759b3d2Safresh1
3735759b3d2Safresh1    $self->{+_FORMATTER}->write($e, $count, $f) if $self->{+_FORMATTER};
3745759b3d2Safresh1
3755759b3d2Safresh1    if ($self->{+_LISTENERS}) {
3765759b3d2Safresh1        $_->{code}->($self, $e, $count, $f) for @{$self->{+_LISTENERS}};
3775759b3d2Safresh1    }
3785759b3d2Safresh1
379256a93a4Safresh1    if ($f->{control} && $f->{control}->{halt}) {
3805759b3d2Safresh1        $code ||= 255;
3815759b3d2Safresh1        $self->set_bailed_out($e);
3825759b3d2Safresh1    }
3835759b3d2Safresh1
3845759b3d2Safresh1    if (defined $code) {
3855759b3d2Safresh1        $self->{+_FORMATTER}->terminate($e, $f) if $self->{+_FORMATTER};
3865759b3d2Safresh1        $self->terminate($code, $e, $f);
3875759b3d2Safresh1    }
3885759b3d2Safresh1
3895759b3d2Safresh1    return $e;
3905759b3d2Safresh1}
3915759b3d2Safresh1
3925759b3d2Safresh1sub terminate {
3935759b3d2Safresh1    my $self = shift;
3945759b3d2Safresh1    my ($code) = @_;
3955759b3d2Safresh1    exit($code);
3965759b3d2Safresh1}
3975759b3d2Safresh1
3985759b3d2Safresh1sub cull {
3995759b3d2Safresh1    my $self = shift;
4005759b3d2Safresh1
4015759b3d2Safresh1    my $ipc = $self->{+IPC} || return;
4025759b3d2Safresh1    return if $self->{+PID} != $$ || $self->{+TID} != get_tid();
4035759b3d2Safresh1
4045759b3d2Safresh1    # No need to do IPC checks on culled events
4055759b3d2Safresh1    $self->process($_) for $ipc->cull($self->{+HID});
4065759b3d2Safresh1}
4075759b3d2Safresh1
4085759b3d2Safresh1sub finalize {
4095759b3d2Safresh1    my $self = shift;
4105759b3d2Safresh1    my ($trace, $do_plan) = @_;
4115759b3d2Safresh1
4125759b3d2Safresh1    $self->cull();
4135759b3d2Safresh1
4145759b3d2Safresh1    my $plan   = $self->{+_PLAN};
4155759b3d2Safresh1    my $count  = $self->{+COUNT};
4165759b3d2Safresh1    my $failed = $self->{+FAILED};
4175759b3d2Safresh1    my $active = $self->{+ACTIVE};
4185759b3d2Safresh1
4195759b3d2Safresh1    # return if NOTHING was done.
4205759b3d2Safresh1    unless ($active || $do_plan || defined($plan) || $count || $failed) {
4215759b3d2Safresh1        $self->{+_FORMATTER}->finalize($plan, $count, $failed, 0, $self->is_subtest) if $self->{+_FORMATTER};
4225759b3d2Safresh1        return;
4235759b3d2Safresh1    }
4245759b3d2Safresh1
4255759b3d2Safresh1    unless ($self->{+ENDED}) {
4265759b3d2Safresh1        if ($self->{+_FOLLOW_UPS}) {
4275759b3d2Safresh1            $_->($trace, $self) for reverse @{$self->{+_FOLLOW_UPS}};
4285759b3d2Safresh1        }
4295759b3d2Safresh1
4305759b3d2Safresh1        # These need to be refreshed now
4315759b3d2Safresh1        $plan   = $self->{+_PLAN};
4325759b3d2Safresh1        $count  = $self->{+COUNT};
4335759b3d2Safresh1        $failed = $self->{+FAILED};
4345759b3d2Safresh1
435*5486feefSafresh1        if ((defined($plan) && $plan eq 'NO PLAN') || ($do_plan && !defined($plan))) {
4365759b3d2Safresh1            $self->send(
4375759b3d2Safresh1                Test2::Event::Plan->new(
4385759b3d2Safresh1                    trace => $trace,
4395759b3d2Safresh1                    max => $count,
4405759b3d2Safresh1                )
4415759b3d2Safresh1            );
4425759b3d2Safresh1        }
4435759b3d2Safresh1        $plan = $self->{+_PLAN};
4445759b3d2Safresh1    }
4455759b3d2Safresh1
4465759b3d2Safresh1    my $frame = $trace->frame;
4475759b3d2Safresh1    if($self->{+ENDED}) {
4485759b3d2Safresh1        my (undef, $ffile, $fline) = @{$self->{+ENDED}};
4495759b3d2Safresh1        my (undef, $sfile, $sline) = @$frame;
4505759b3d2Safresh1
4515759b3d2Safresh1        die <<"        EOT"
4525759b3d2Safresh1Test already ended!
4535759b3d2Safresh1First End:  $ffile line $fline
4545759b3d2Safresh1Second End: $sfile line $sline
4555759b3d2Safresh1        EOT
4565759b3d2Safresh1    }
4575759b3d2Safresh1
4585759b3d2Safresh1    $self->{+ENDED} = $frame;
4595759b3d2Safresh1    my $pass = $self->is_passing(); # Generate the final boolean.
4605759b3d2Safresh1
4615759b3d2Safresh1    $self->{+_FORMATTER}->finalize($plan, $count, $failed, $pass, $self->is_subtest) if $self->{+_FORMATTER};
4625759b3d2Safresh1
4635759b3d2Safresh1    return $pass;
4645759b3d2Safresh1}
4655759b3d2Safresh1
4665759b3d2Safresh1sub is_passing {
4675759b3d2Safresh1    my $self = shift;
4685759b3d2Safresh1
4695759b3d2Safresh1    ($self->{+_PASSING}) = @_ if @_;
4705759b3d2Safresh1
4715759b3d2Safresh1    # If we already failed just return 0.
4725759b3d2Safresh1    my $pass = $self->{+_PASSING} or return 0;
4735759b3d2Safresh1    return $self->{+_PASSING} = 0 if $self->{+FAILED};
4745759b3d2Safresh1
4755759b3d2Safresh1    my $count = $self->{+COUNT};
4765759b3d2Safresh1    my $ended = $self->{+ENDED};
4775759b3d2Safresh1    my $plan = $self->{+_PLAN};
4785759b3d2Safresh1
4795759b3d2Safresh1    return $pass if !$count && $plan && $plan =~ m/^SKIP$/;
4805759b3d2Safresh1
4815759b3d2Safresh1    return $self->{+_PASSING} = 0
4825759b3d2Safresh1        if $ended && (!$count || !$plan);
4835759b3d2Safresh1
4845759b3d2Safresh1    return $pass unless $plan && $plan =~ m/^\d+$/;
4855759b3d2Safresh1
4865759b3d2Safresh1    if ($ended) {
4875759b3d2Safresh1        return $self->{+_PASSING} = 0 if $count != $plan;
4885759b3d2Safresh1    }
4895759b3d2Safresh1    else {
4905759b3d2Safresh1        return $self->{+_PASSING} = 0 if $count > $plan;
4915759b3d2Safresh1    }
4925759b3d2Safresh1
4935759b3d2Safresh1    return $pass;
4945759b3d2Safresh1}
4955759b3d2Safresh1
4965759b3d2Safresh1sub plan {
4975759b3d2Safresh1    my $self = shift;
4985759b3d2Safresh1
4995759b3d2Safresh1    return $self->{+_PLAN} unless @_;
5005759b3d2Safresh1
5015759b3d2Safresh1    my ($plan) = @_;
5025759b3d2Safresh1
5035759b3d2Safresh1    confess "You cannot unset the plan"
5045759b3d2Safresh1        unless defined $plan;
5055759b3d2Safresh1
5065759b3d2Safresh1    confess "You cannot change the plan"
5075759b3d2Safresh1        if $self->{+_PLAN} && $self->{+_PLAN} !~ m/^NO PLAN$/;
5085759b3d2Safresh1
5095759b3d2Safresh1    confess "'$plan' is not a valid plan! Plan must be an integer greater than 0, 'NO PLAN', or 'SKIP'"
5105759b3d2Safresh1        unless $plan =~ m/^(\d+|NO PLAN|SKIP)$/;
5115759b3d2Safresh1
5125759b3d2Safresh1    $self->{+_PLAN} = $plan;
5135759b3d2Safresh1}
5145759b3d2Safresh1
5155759b3d2Safresh1sub check_plan {
5165759b3d2Safresh1    my $self = shift;
5175759b3d2Safresh1
5185759b3d2Safresh1    return undef unless $self->{+ENDED};
5195759b3d2Safresh1    my $plan = $self->{+_PLAN} || return undef;
5205759b3d2Safresh1
5215759b3d2Safresh1    return 1 if $plan !~ m/^\d+$/;
5225759b3d2Safresh1
5235759b3d2Safresh1    return 1 if $plan == $self->{+COUNT};
5245759b3d2Safresh1    return 0;
5255759b3d2Safresh1}
5265759b3d2Safresh1
5275759b3d2Safresh1sub DESTROY {
5285759b3d2Safresh1    my $self = shift;
5295759b3d2Safresh1    my $ipc = $self->{+IPC} || return;
5305759b3d2Safresh1    return unless $$ == $self->{+PID};
5315759b3d2Safresh1    return unless get_tid() == $self->{+TID};
5325759b3d2Safresh1    $ipc->drop_hub($self->{+HID});
5335759b3d2Safresh1}
5345759b3d2Safresh1
5355759b3d2Safresh11;
5365759b3d2Safresh1
5375759b3d2Safresh1__END__
5385759b3d2Safresh1
5395759b3d2Safresh1=pod
5405759b3d2Safresh1
5415759b3d2Safresh1=encoding UTF-8
5425759b3d2Safresh1
5435759b3d2Safresh1=head1 NAME
5445759b3d2Safresh1
5455759b3d2Safresh1Test2::Hub - The conduit through which all events flow.
5465759b3d2Safresh1
5475759b3d2Safresh1=head1 SYNOPSIS
5485759b3d2Safresh1
5495759b3d2Safresh1    use Test2::Hub;
5505759b3d2Safresh1
5515759b3d2Safresh1    my $hub = Test2::Hub->new();
5525759b3d2Safresh1    $hub->send(...);
5535759b3d2Safresh1
5545759b3d2Safresh1=head1 DESCRIPTION
5555759b3d2Safresh1
5565759b3d2Safresh1The hub is the place where all events get processed and handed off to the
5575759b3d2Safresh1formatter. The hub also tracks test state, and provides several hooks into the
5585759b3d2Safresh1event pipeline.
5595759b3d2Safresh1
5605759b3d2Safresh1=head1 COMMON TASKS
5615759b3d2Safresh1
5625759b3d2Safresh1=head2 SENDING EVENTS
5635759b3d2Safresh1
5645759b3d2Safresh1    $hub->send($event)
5655759b3d2Safresh1
5665759b3d2Safresh1The C<send()> method is used to issue an event to the hub. This method will
5675759b3d2Safresh1handle thread/fork sync, filters, listeners, TAP output, etc.
5685759b3d2Safresh1
5695759b3d2Safresh1=head2 ALTERING OR REMOVING EVENTS
5705759b3d2Safresh1
5715759b3d2Safresh1You can use either C<filter()> or C<pre_filter()>, depending on your
5725759b3d2Safresh1needs. Both have identical syntax, so only C<filter()> is shown here.
5735759b3d2Safresh1
5745759b3d2Safresh1    $hub->filter(sub {
5755759b3d2Safresh1        my ($hub, $event) = @_;
5765759b3d2Safresh1
5775759b3d2Safresh1        my $action = get_action($event);
5785759b3d2Safresh1
5795759b3d2Safresh1        # No action should be taken
5805759b3d2Safresh1        return $event if $action eq 'none';
5815759b3d2Safresh1
5825759b3d2Safresh1        # You want your filter to remove the event
5835759b3d2Safresh1        return undef if $action eq 'delete';
5845759b3d2Safresh1
5855759b3d2Safresh1        if ($action eq 'do_it') {
5865759b3d2Safresh1            my $new_event = copy_event($event);
5875759b3d2Safresh1            ... Change your copy of the event ...
5885759b3d2Safresh1            return $new_event;
5895759b3d2Safresh1        }
5905759b3d2Safresh1
5915759b3d2Safresh1        die "Should not happen";
5925759b3d2Safresh1    });
5935759b3d2Safresh1
5945759b3d2Safresh1By default, filters are not inherited by child hubs. That means if you start a
5955759b3d2Safresh1subtest, the subtest will not inherit the filter. You can change this behavior
5965759b3d2Safresh1with the C<inherit> parameter:
5975759b3d2Safresh1
5985759b3d2Safresh1    $hub->filter(sub { ... }, inherit => 1);
5995759b3d2Safresh1
6005759b3d2Safresh1=head2 LISTENING FOR EVENTS
6015759b3d2Safresh1
6025759b3d2Safresh1    $hub->listen(sub {
6035759b3d2Safresh1        my ($hub, $event, $number) = @_;
6045759b3d2Safresh1
6055759b3d2Safresh1        ... do whatever you want with the event ...
6065759b3d2Safresh1
6075759b3d2Safresh1        # return is ignored
6085759b3d2Safresh1    });
6095759b3d2Safresh1
6105759b3d2Safresh1By default listeners are not inherited by child hubs. That means if you start a
6115759b3d2Safresh1subtest, the subtest will not inherit the listener. You can change this behavior
6125759b3d2Safresh1with the C<inherit> parameter:
6135759b3d2Safresh1
6145759b3d2Safresh1    $hub->listen(sub { ... }, inherit => 1);
6155759b3d2Safresh1
6165759b3d2Safresh1
6175759b3d2Safresh1=head2 POST-TEST BEHAVIORS
6185759b3d2Safresh1
6195759b3d2Safresh1    $hub->follow_up(sub {
6205759b3d2Safresh1        my ($trace, $hub) = @_;
6215759b3d2Safresh1
6225759b3d2Safresh1        ... do whatever you need to ...
6235759b3d2Safresh1
6245759b3d2Safresh1        # Return is ignored
6255759b3d2Safresh1    });
6265759b3d2Safresh1
6275759b3d2Safresh1follow_up subs are called only once, either when done_testing is called, or in
6285759b3d2Safresh1an END block.
6295759b3d2Safresh1
6305759b3d2Safresh1=head2 SETTING THE FORMATTER
6315759b3d2Safresh1
6325759b3d2Safresh1By default an instance of L<Test2::Formatter::TAP> is created and used.
6335759b3d2Safresh1
6345759b3d2Safresh1    my $old = $hub->format(My::Formatter->new);
6355759b3d2Safresh1
6365759b3d2Safresh1Setting the formatter will REPLACE any existing formatter. You may set the
6375759b3d2Safresh1formatter to undef to prevent output. The old formatter will be returned if one
6385759b3d2Safresh1was already set. Only one formatter is allowed at a time.
6395759b3d2Safresh1
6405759b3d2Safresh1=head1 METHODS
6415759b3d2Safresh1
6425759b3d2Safresh1=over 4
6435759b3d2Safresh1
6445759b3d2Safresh1=item $hub->send($event)
6455759b3d2Safresh1
6465759b3d2Safresh1This is where all events enter the hub for processing.
6475759b3d2Safresh1
6485759b3d2Safresh1=item $hub->process($event)
6495759b3d2Safresh1
6505759b3d2Safresh1This is called by send after it does any IPC handling. You can use this to
6515759b3d2Safresh1bypass the IPC process, but in general you should avoid using this.
6525759b3d2Safresh1
6535759b3d2Safresh1=item $old = $hub->format($formatter)
6545759b3d2Safresh1
6555759b3d2Safresh1Replace the existing formatter instance with a new one. Formatters must be
6565759b3d2Safresh1objects that implement a C<< $formatter->write($event) >> method.
6575759b3d2Safresh1
6585759b3d2Safresh1=item $sub = $hub->listen(sub { ... }, %optional_params)
6595759b3d2Safresh1
6605759b3d2Safresh1You can use this to record all events AFTER they have been sent to the
6615759b3d2Safresh1formatter. No changes made here will be meaningful, except possibly to other
6625759b3d2Safresh1listeners.
6635759b3d2Safresh1
6645759b3d2Safresh1    $hub->listen(sub {
6655759b3d2Safresh1        my ($hub, $event, $number) = @_;
6665759b3d2Safresh1
6675759b3d2Safresh1        ... do whatever you want with the event ...
6685759b3d2Safresh1
6695759b3d2Safresh1        # return is ignored
6705759b3d2Safresh1    });
6715759b3d2Safresh1
6725759b3d2Safresh1Normally listeners are not inherited by child hubs such as subtests. You can
6735759b3d2Safresh1add the C<< inherit => 1 >> parameter to allow a listener to be inherited.
6745759b3d2Safresh1
6755759b3d2Safresh1=item $hub->unlisten($sub)
6765759b3d2Safresh1
6775759b3d2Safresh1You can use this to remove a listen callback. You must pass in the coderef
6785759b3d2Safresh1returned by the C<listen()> method.
6795759b3d2Safresh1
6805759b3d2Safresh1=item $sub = $hub->filter(sub { ... }, %optional_params)
6815759b3d2Safresh1
6825759b3d2Safresh1=item $sub = $hub->pre_filter(sub { ... }, %optional_params)
6835759b3d2Safresh1
6845759b3d2Safresh1These can be used to add filters. Filters can modify, replace, or remove events
6855759b3d2Safresh1before anything else can see them.
6865759b3d2Safresh1
6875759b3d2Safresh1    $hub->filter(
6885759b3d2Safresh1        sub {
6895759b3d2Safresh1            my ($hub, $event) = @_;
6905759b3d2Safresh1
6915759b3d2Safresh1            return $event;    # No Changes
6925759b3d2Safresh1            return;           # Remove the event
6935759b3d2Safresh1
6945759b3d2Safresh1            # Or you can modify an event before returning it.
6955759b3d2Safresh1            $event->modify;
6965759b3d2Safresh1            return $event;
6975759b3d2Safresh1        }
6985759b3d2Safresh1    );
6995759b3d2Safresh1
7005759b3d2Safresh1If you are not using threads, forking, or IPC then the only difference between
7015759b3d2Safresh1a C<filter> and a C<pre_filter> is that C<pre_filter> subs run first. When you
7025759b3d2Safresh1are using threads, forking, or IPC, pre_filters happen to events before they
7035759b3d2Safresh1are sent to their destination proc/thread, ordinary filters happen only in the
7045759b3d2Safresh1destination hub/thread.
7055759b3d2Safresh1
7065759b3d2Safresh1You cannot add a regular filter to a hub if the hub was created in another
7075759b3d2Safresh1process or thread. You can always add a pre_filter.
7085759b3d2Safresh1
7095759b3d2Safresh1=item $hub->unfilter($sub)
7105759b3d2Safresh1
7115759b3d2Safresh1=item $hub->pre_unfilter($sub)
7125759b3d2Safresh1
7135759b3d2Safresh1These can be used to remove filters and pre_filters. The C<$sub> argument is
7145759b3d2Safresh1the reference returned by C<filter()> or C<pre_filter()>.
7155759b3d2Safresh1
7165759b3d2Safresh1=item $hub->follow_op(sub { ... })
7175759b3d2Safresh1
7185759b3d2Safresh1Use this to add behaviors that are called just before the hub is finalized. The
7195759b3d2Safresh1only argument to your codeblock will be a L<Test2::EventFacet::Trace> instance.
7205759b3d2Safresh1
7215759b3d2Safresh1    $hub->follow_up(sub {
7225759b3d2Safresh1        my ($trace, $hub) = @_;
7235759b3d2Safresh1
7245759b3d2Safresh1        ... do whatever you need to ...
7255759b3d2Safresh1
7265759b3d2Safresh1        # Return is ignored
7275759b3d2Safresh1    });
7285759b3d2Safresh1
7295759b3d2Safresh1follow_up subs are called only once, ether when done_testing is called, or in
7305759b3d2Safresh1an END block.
7315759b3d2Safresh1
7325759b3d2Safresh1=item $sub = $hub->add_context_acquire(sub { ... });
7335759b3d2Safresh1
7345759b3d2Safresh1Add a callback that will be called every time someone tries to acquire a
7355759b3d2Safresh1context. It gets a single argument, a reference of the hash of parameters
7365759b3d2Safresh1being used the construct the context. This is your chance to change the
7375759b3d2Safresh1parameters by directly altering the hash.
7385759b3d2Safresh1
7395759b3d2Safresh1    test2_add_callback_context_acquire(sub {
7405759b3d2Safresh1        my $params = shift;
7415759b3d2Safresh1        $params->{level}++;
7425759b3d2Safresh1    });
7435759b3d2Safresh1
7445759b3d2Safresh1This is a very scary API function. Please do not use this unless you need to.
7455759b3d2Safresh1This is here for L<Test::Builder> and backwards compatibility. This has you
7465759b3d2Safresh1directly manipulate the hash instead of returning a new one for performance
7475759b3d2Safresh1reasons.
7485759b3d2Safresh1
7495759b3d2Safresh1B<Note> Using this hook could have a huge performance impact.
7505759b3d2Safresh1
7515759b3d2Safresh1The coderef you provide is returned and can be used to remove the hook later.
7525759b3d2Safresh1
7535759b3d2Safresh1=item $hub->remove_context_acquire($sub);
7545759b3d2Safresh1
7555759b3d2Safresh1This can be used to remove a context acquire hook.
7565759b3d2Safresh1
7575759b3d2Safresh1=item $sub = $hub->add_context_init(sub { ... });
7585759b3d2Safresh1
7595759b3d2Safresh1This allows you to add callbacks that will trigger every time a new context is
7605759b3d2Safresh1created for the hub. The only argument to the sub will be the
7615759b3d2Safresh1L<Test2::API::Context> instance that was created.
7625759b3d2Safresh1
7635759b3d2Safresh1B<Note> Using this hook could have a huge performance impact.
7645759b3d2Safresh1
7655759b3d2Safresh1The coderef you provide is returned and can be used to remove the hook later.
7665759b3d2Safresh1
7675759b3d2Safresh1=item $hub->remove_context_init($sub);
7685759b3d2Safresh1
7695759b3d2Safresh1This can be used to remove a context init hook.
7705759b3d2Safresh1
7715759b3d2Safresh1=item $sub = $hub->add_context_release(sub { ... });
7725759b3d2Safresh1
7735759b3d2Safresh1This allows you to add callbacks that will trigger every time a context for
7745759b3d2Safresh1this hub is released. The only argument to the sub will be the
7755759b3d2Safresh1L<Test2::API::Context> instance that was released. These will run in reverse
7765759b3d2Safresh1order.
7775759b3d2Safresh1
7785759b3d2Safresh1B<Note> Using this hook could have a huge performance impact.
7795759b3d2Safresh1
7805759b3d2Safresh1The coderef you provide is returned and can be used to remove the hook later.
7815759b3d2Safresh1
7825759b3d2Safresh1=item $hub->remove_context_release($sub);
7835759b3d2Safresh1
7845759b3d2Safresh1This can be used to remove a context release hook.
7855759b3d2Safresh1
7865759b3d2Safresh1=item $hub->cull()
7875759b3d2Safresh1
7885759b3d2Safresh1Cull any IPC events (and process them).
7895759b3d2Safresh1
7905759b3d2Safresh1=item $pid = $hub->pid()
7915759b3d2Safresh1
7925759b3d2Safresh1Get the process id under which the hub was created.
7935759b3d2Safresh1
7945759b3d2Safresh1=item $tid = $hub->tid()
7955759b3d2Safresh1
7965759b3d2Safresh1Get the thread id under which the hub was created.
7975759b3d2Safresh1
7985759b3d2Safresh1=item $hud = $hub->hid()
7995759b3d2Safresh1
8005759b3d2Safresh1Get the identifier string of the hub.
8015759b3d2Safresh1
8025759b3d2Safresh1=item $uuid = $hub->uuid()
8035759b3d2Safresh1
8045759b3d2Safresh1If UUID tagging is enabled (see L<Test2::API>) then the hub will have a UUID.
8055759b3d2Safresh1
8065759b3d2Safresh1=item $ipc = $hub->ipc()
8075759b3d2Safresh1
8085759b3d2Safresh1Get the IPC object used by the hub.
8095759b3d2Safresh1
8105759b3d2Safresh1=item $hub->set_no_ending($bool)
8115759b3d2Safresh1
8125759b3d2Safresh1=item $bool = $hub->no_ending
8135759b3d2Safresh1
8145759b3d2Safresh1This can be used to disable auto-ending behavior for a hub. The auto-ending
8155759b3d2Safresh1behavior is triggered by an end block and is used to cull IPC events, and
816de8cc8edSafresh1output the final plan if the plan was 'NO PLAN'.
8175759b3d2Safresh1
8185759b3d2Safresh1=item $bool = $hub->active
8195759b3d2Safresh1
8205759b3d2Safresh1=item $hub->set_active($bool)
8215759b3d2Safresh1
8225759b3d2Safresh1These are used to get/set the 'active' attribute. When true this attribute will
8235759b3d2Safresh1force C<< hub->finalize() >> to take action even if there is no plan, and no
8245759b3d2Safresh1tests have been run. This flag is useful for plugins that add follow-up
8255759b3d2Safresh1behaviors that need to run even if no events are seen.
8265759b3d2Safresh1
8275759b3d2Safresh1=back
8285759b3d2Safresh1
8295759b3d2Safresh1=head2 STATE METHODS
8305759b3d2Safresh1
8315759b3d2Safresh1=over 4
8325759b3d2Safresh1
8335759b3d2Safresh1=item $hub->reset_state()
8345759b3d2Safresh1
8355759b3d2Safresh1Reset all state to the start. This sets the test count to 0, clears the plan,
8365759b3d2Safresh1removes the failures, etc.
8375759b3d2Safresh1
8385759b3d2Safresh1=item $num = $hub->count
8395759b3d2Safresh1
8405759b3d2Safresh1Get the number of tests that have been run.
8415759b3d2Safresh1
8425759b3d2Safresh1=item $num = $hub->failed
8435759b3d2Safresh1
8445759b3d2Safresh1Get the number of failures (Not all failures come from a test fail, so this
8455759b3d2Safresh1number can be larger than the count).
8465759b3d2Safresh1
8475759b3d2Safresh1=item $bool = $hub->ended
8485759b3d2Safresh1
8495759b3d2Safresh1True if the testing has ended. This MAY return the stack frame of the tool that
8505759b3d2Safresh1ended the test, but that is not guaranteed.
8515759b3d2Safresh1
8525759b3d2Safresh1=item $bool = $hub->is_passing
8535759b3d2Safresh1
8545759b3d2Safresh1=item $hub->is_passing($bool)
8555759b3d2Safresh1
8565759b3d2Safresh1Check if the overall test run is a failure. Can also be used to set the
8575759b3d2Safresh1pass/fail status.
8585759b3d2Safresh1
8595759b3d2Safresh1=item $hub->plan($plan)
8605759b3d2Safresh1
8615759b3d2Safresh1=item $plan = $hub->plan
8625759b3d2Safresh1
8635759b3d2Safresh1Get or set the plan. The plan must be an integer larger than 0, the string
864de8cc8edSafresh1'NO PLAN', or the string 'SKIP'.
8655759b3d2Safresh1
8665759b3d2Safresh1=item $bool = $hub->check_plan
8675759b3d2Safresh1
8685759b3d2Safresh1Check if the plan and counts match, but only if the tests have ended. If tests
8695759b3d2Safresh1have not ended this will return undef, otherwise it will be a true/false.
8705759b3d2Safresh1
8715759b3d2Safresh1=back
8725759b3d2Safresh1
8735759b3d2Safresh1=head1 THIRD PARTY META-DATA
8745759b3d2Safresh1
8755759b3d2Safresh1This object consumes L<Test2::Util::ExternalMeta> which provides a consistent
8765759b3d2Safresh1way for you to attach meta-data to instances of this class. This is useful for
8775759b3d2Safresh1tools, plugins, and other extensions.
8785759b3d2Safresh1
8795759b3d2Safresh1=head1 SOURCE
8805759b3d2Safresh1
8815759b3d2Safresh1The source code repository for Test2 can be found at
882*5486feefSafresh1L<https://github.com/Test-More/test-more/>.
8835759b3d2Safresh1
8845759b3d2Safresh1=head1 MAINTAINERS
8855759b3d2Safresh1
8865759b3d2Safresh1=over 4
8875759b3d2Safresh1
8885759b3d2Safresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt>
8895759b3d2Safresh1
8905759b3d2Safresh1=back
8915759b3d2Safresh1
8925759b3d2Safresh1=head1 AUTHORS
8935759b3d2Safresh1
8945759b3d2Safresh1=over 4
8955759b3d2Safresh1
8965759b3d2Safresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt>
8975759b3d2Safresh1
8985759b3d2Safresh1=back
8995759b3d2Safresh1
9005759b3d2Safresh1=head1 COPYRIGHT
9015759b3d2Safresh1
902256a93a4Safresh1Copyright 2020 Chad Granum E<lt>exodist@cpan.orgE<gt>.
9035759b3d2Safresh1
9045759b3d2Safresh1This program is free software; you can redistribute it and/or
9055759b3d2Safresh1modify it under the same terms as Perl itself.
9065759b3d2Safresh1
907*5486feefSafresh1See L<https://dev.perl.org/licenses/>
9085759b3d2Safresh1
9095759b3d2Safresh1=cut
910