xref: /openbsd-src/gnu/usr.bin/perl/cpan/Test-Simple/lib/Test2/Util.pm (revision 5486feefcc8cb79b19e014ab332cc5dfd05b3b33)
15759b3d2Safresh1package Test2::Util;
25759b3d2Safresh1use strict;
35759b3d2Safresh1use warnings;
45759b3d2Safresh1
5*5486feefSafresh1our $VERSION = '1.302199';
65759b3d2Safresh1
75759b3d2Safresh1use POSIX();
85759b3d2Safresh1use Config qw/%Config/;
95759b3d2Safresh1use Carp qw/croak/;
105759b3d2Safresh1
115759b3d2Safresh1BEGIN {
125759b3d2Safresh1    local ($@, $!, $SIG{__DIE__});
135759b3d2Safresh1    *HAVE_PERLIO = eval { require PerlIO; PerlIO->VERSION(1.02); } ? sub() { 1 } : sub() { 0 };
145759b3d2Safresh1}
155759b3d2Safresh1
165759b3d2Safresh1our @EXPORT_OK = qw{
175759b3d2Safresh1    try
185759b3d2Safresh1
195759b3d2Safresh1    pkg_to_file
205759b3d2Safresh1
215759b3d2Safresh1    get_tid USE_THREADS
225759b3d2Safresh1    CAN_THREAD
235759b3d2Safresh1    CAN_REALLY_FORK
245759b3d2Safresh1    CAN_FORK
255759b3d2Safresh1
265759b3d2Safresh1    CAN_SIGSYS
275759b3d2Safresh1
285759b3d2Safresh1    IS_WIN32
295759b3d2Safresh1
305759b3d2Safresh1    ipc_separator
315759b3d2Safresh1
32f3efcd01Safresh1    gen_uid
33f3efcd01Safresh1
345759b3d2Safresh1    do_rename do_unlink
355759b3d2Safresh1
365759b3d2Safresh1    try_sig_mask
375759b3d2Safresh1
385759b3d2Safresh1    clone_io
395759b3d2Safresh1};
405759b3d2Safresh1BEGIN { require Exporter; our @ISA = qw(Exporter) }
415759b3d2Safresh1
425759b3d2Safresh1BEGIN {
435759b3d2Safresh1    *IS_WIN32 = ($^O eq 'MSWin32') ? sub() { 1 } : sub() { 0 };
445759b3d2Safresh1}
455759b3d2Safresh1
465759b3d2Safresh1sub _can_thread {
475759b3d2Safresh1    return 0 unless $] >= 5.008001;
485759b3d2Safresh1    return 0 unless $Config{'useithreads'};
495759b3d2Safresh1
505759b3d2Safresh1    # Threads are broken on perl 5.10.0 built with gcc 4.8+
515759b3d2Safresh1    if ($] == 5.010000 && $Config{'ccname'} eq 'gcc' && $Config{'gccversion'}) {
52256a93a4Safresh1        return 0 unless $Config{'gccversion'} =~ m/^(\d+)\.(\d+)/;
53256a93a4Safresh1        my @parts = split /[\.\s]+/, $Config{'gccversion'};
545759b3d2Safresh1        return 0 if $parts[0] > 4 || ($parts[0] == 4 && $parts[1] >= 8);
555759b3d2Safresh1    }
565759b3d2Safresh1
575759b3d2Safresh1    # Change to a version check if this ever changes
585759b3d2Safresh1    return 0 if $INC{'Devel/Cover.pm'};
595759b3d2Safresh1    return 1;
605759b3d2Safresh1}
615759b3d2Safresh1
625759b3d2Safresh1sub _can_fork {
635759b3d2Safresh1    return 1 if $Config{d_fork};
645759b3d2Safresh1    return 0 unless IS_WIN32 || $^O eq 'NetWare';
655759b3d2Safresh1    return 0 unless $Config{useithreads};
665759b3d2Safresh1    return 0 unless $Config{ccflags} =~ /-DPERL_IMPLICIT_SYS/;
675759b3d2Safresh1
685759b3d2Safresh1    return _can_thread();
695759b3d2Safresh1}
705759b3d2Safresh1
715759b3d2Safresh1BEGIN {
725759b3d2Safresh1    no warnings 'once';
735759b3d2Safresh1    *CAN_THREAD      = _can_thread()   ? sub() { 1 } : sub() { 0 };
745759b3d2Safresh1}
755759b3d2Safresh1my $can_fork;
765759b3d2Safresh1sub CAN_FORK () {
775759b3d2Safresh1    return $can_fork
785759b3d2Safresh1        if defined $can_fork;
795759b3d2Safresh1    $can_fork = !!_can_fork();
805759b3d2Safresh1    no warnings 'redefine';
815759b3d2Safresh1    *CAN_FORK = $can_fork ? sub() { 1 } : sub() { 0 };
825759b3d2Safresh1    $can_fork;
835759b3d2Safresh1}
845759b3d2Safresh1my $can_really_fork;
855759b3d2Safresh1sub CAN_REALLY_FORK () {
865759b3d2Safresh1    return $can_really_fork
875759b3d2Safresh1        if defined $can_really_fork;
885759b3d2Safresh1    $can_really_fork = !!$Config{d_fork};
895759b3d2Safresh1    no warnings 'redefine';
905759b3d2Safresh1    *CAN_REALLY_FORK = $can_really_fork ? sub() { 1 } : sub() { 0 };
915759b3d2Safresh1    $can_really_fork;
925759b3d2Safresh1}
935759b3d2Safresh1
945759b3d2Safresh1sub _manual_try(&;@) {
955759b3d2Safresh1    my $code = shift;
965759b3d2Safresh1    my $args = \@_;
975759b3d2Safresh1    my $err;
985759b3d2Safresh1
995759b3d2Safresh1    my $die = delete $SIG{__DIE__};
1005759b3d2Safresh1
1015759b3d2Safresh1    eval { $code->(@$args); 1 } or $err = $@ || "Error was squashed!\n";
1025759b3d2Safresh1
1035759b3d2Safresh1    $die ? $SIG{__DIE__} = $die : delete $SIG{__DIE__};
1045759b3d2Safresh1
1055759b3d2Safresh1    return (!defined($err), $err);
1065759b3d2Safresh1}
1075759b3d2Safresh1
1085759b3d2Safresh1sub _local_try(&;@) {
1095759b3d2Safresh1    my $code = shift;
1105759b3d2Safresh1    my $args = \@_;
1115759b3d2Safresh1    my $err;
1125759b3d2Safresh1
1135759b3d2Safresh1    no warnings;
1145759b3d2Safresh1    local $SIG{__DIE__};
1155759b3d2Safresh1    eval { $code->(@$args); 1 } or $err = $@ || "Error was squashed!\n";
1165759b3d2Safresh1
1175759b3d2Safresh1    return (!defined($err), $err);
1185759b3d2Safresh1}
1195759b3d2Safresh1
1205759b3d2Safresh1# Older versions of perl have a nasty bug on win32 when localizing a variable
1215759b3d2Safresh1# before forking or starting a new thread. So for those systems we use the
1225759b3d2Safresh1# non-local form. When possible though we use the faster 'local' form.
1235759b3d2Safresh1BEGIN {
1245759b3d2Safresh1    if (IS_WIN32 && $] < 5.020002) {
1255759b3d2Safresh1        *try = \&_manual_try;
1265759b3d2Safresh1    }
1275759b3d2Safresh1    else {
1285759b3d2Safresh1        *try = \&_local_try;
1295759b3d2Safresh1    }
1305759b3d2Safresh1}
1315759b3d2Safresh1
1325759b3d2Safresh1BEGIN {
1335759b3d2Safresh1    if (CAN_THREAD) {
1345759b3d2Safresh1        if ($INC{'threads.pm'}) {
1355759b3d2Safresh1            # Threads are already loaded, so we do not need to check if they
1365759b3d2Safresh1            # are loaded each time
1375759b3d2Safresh1            *USE_THREADS = sub() { 1 };
1385759b3d2Safresh1            *get_tid     = sub() { threads->tid() };
1395759b3d2Safresh1        }
1405759b3d2Safresh1        else {
1415759b3d2Safresh1            # :-( Need to check each time to see if they have been loaded.
1425759b3d2Safresh1            *USE_THREADS = sub() { $INC{'threads.pm'} ? 1 : 0 };
1435759b3d2Safresh1            *get_tid     = sub() { $INC{'threads.pm'} ? threads->tid() : 0 };
1445759b3d2Safresh1        }
1455759b3d2Safresh1    }
1465759b3d2Safresh1    else {
1475759b3d2Safresh1        # No threads, not now, not ever!
1485759b3d2Safresh1        *USE_THREADS = sub() { 0 };
1495759b3d2Safresh1        *get_tid     = sub() { 0 };
1505759b3d2Safresh1    }
1515759b3d2Safresh1}
1525759b3d2Safresh1
1535759b3d2Safresh1sub pkg_to_file {
1545759b3d2Safresh1    my $pkg = shift;
1555759b3d2Safresh1    my $file = $pkg;
1565759b3d2Safresh1    $file =~ s{(::|')}{/}g;
1575759b3d2Safresh1    $file .= '.pm';
1585759b3d2Safresh1    return $file;
1595759b3d2Safresh1}
1605759b3d2Safresh1
1615759b3d2Safresh1sub ipc_separator() { "~" }
1625759b3d2Safresh1
163f3efcd01Safresh1my $UID = 1;
164f3efcd01Safresh1sub gen_uid() { join ipc_separator() => ($$, get_tid(), time, $UID++) }
165f3efcd01Safresh1
1665759b3d2Safresh1sub _check_for_sig_sys {
1675759b3d2Safresh1    my $sig_list = shift;
1685759b3d2Safresh1    return $sig_list =~ m/\bSYS\b/;
1695759b3d2Safresh1}
1705759b3d2Safresh1
1715759b3d2Safresh1BEGIN {
1725759b3d2Safresh1    if (_check_for_sig_sys($Config{sig_name})) {
1735759b3d2Safresh1        *CAN_SIGSYS = sub() { 1 };
1745759b3d2Safresh1    }
1755759b3d2Safresh1    else {
1765759b3d2Safresh1        *CAN_SIGSYS = sub() { 0 };
1775759b3d2Safresh1    }
1785759b3d2Safresh1}
1795759b3d2Safresh1
1805759b3d2Safresh1my %PERLIO_SKIP = (
1815759b3d2Safresh1    unix => 1,
1825759b3d2Safresh1    via  => 1,
1835759b3d2Safresh1);
1845759b3d2Safresh1
1855759b3d2Safresh1sub clone_io {
1865759b3d2Safresh1    my ($fh) = @_;
187f3efcd01Safresh1    my $fileno = eval { fileno($fh) };
1885759b3d2Safresh1
1895759b3d2Safresh1    return $fh if !defined($fileno) || !length($fileno) || $fileno < 0;
1905759b3d2Safresh1
1915759b3d2Safresh1    open(my $out, '>&' . $fileno) or die "Can't dup fileno $fileno: $!";
1925759b3d2Safresh1
1935759b3d2Safresh1    my %seen;
1945759b3d2Safresh1    my @layers = HAVE_PERLIO ? grep { !$PERLIO_SKIP{$_} and !$seen{$_}++ } PerlIO::get_layers($fh) : ();
1955759b3d2Safresh1    binmode($out, join(":", "", "raw", @layers));
1965759b3d2Safresh1
1975759b3d2Safresh1    my $old = select $fh;
1985759b3d2Safresh1    my $af  = $|;
1995759b3d2Safresh1    select $out;
2005759b3d2Safresh1    $| = $af;
2015759b3d2Safresh1    select $old;
2025759b3d2Safresh1
2035759b3d2Safresh1    return $out;
2045759b3d2Safresh1}
2055759b3d2Safresh1
2065759b3d2Safresh1BEGIN {
2075759b3d2Safresh1    if (IS_WIN32) {
2085759b3d2Safresh1        my $max_tries = 5;
2095759b3d2Safresh1
2105759b3d2Safresh1        *do_rename = sub {
2115759b3d2Safresh1            my ($from, $to) = @_;
2125759b3d2Safresh1
2135759b3d2Safresh1            my $err;
2145759b3d2Safresh1            for (1 .. $max_tries) {
2155759b3d2Safresh1                return (1) if rename($from, $to);
2165759b3d2Safresh1                $err = "$!";
2175759b3d2Safresh1                last if $_ == $max_tries;
2185759b3d2Safresh1                sleep 1;
2195759b3d2Safresh1            }
2205759b3d2Safresh1
2215759b3d2Safresh1            return (0, $err);
2225759b3d2Safresh1        };
2235759b3d2Safresh1        *do_unlink = sub {
2245759b3d2Safresh1            my ($file) = @_;
2255759b3d2Safresh1
2265759b3d2Safresh1            my $err;
2275759b3d2Safresh1            for (1 .. $max_tries) {
2285759b3d2Safresh1                return (1) if unlink($file);
2295759b3d2Safresh1                $err = "$!";
2305759b3d2Safresh1                last if $_ == $max_tries;
2315759b3d2Safresh1                sleep 1;
2325759b3d2Safresh1            }
2335759b3d2Safresh1
2345759b3d2Safresh1            return (0, "$!");
2355759b3d2Safresh1        };
2365759b3d2Safresh1    }
2375759b3d2Safresh1    else {
2385759b3d2Safresh1        *do_rename = sub {
2395759b3d2Safresh1            my ($from, $to) = @_;
2405759b3d2Safresh1            return (1) if rename($from, $to);
2415759b3d2Safresh1            return (0, "$!");
2425759b3d2Safresh1        };
2435759b3d2Safresh1        *do_unlink = sub {
2445759b3d2Safresh1            my ($file) = @_;
2455759b3d2Safresh1            return (1) if unlink($file);
2465759b3d2Safresh1            return (0, "$!");
2475759b3d2Safresh1        };
2485759b3d2Safresh1    }
2495759b3d2Safresh1}
2505759b3d2Safresh1
2515759b3d2Safresh1sub try_sig_mask(&) {
2525759b3d2Safresh1    my $code = shift;
2535759b3d2Safresh1
2545759b3d2Safresh1    my ($old, $blocked);
2555759b3d2Safresh1    unless(IS_WIN32) {
2565759b3d2Safresh1        my $to_block = POSIX::SigSet->new(
2575759b3d2Safresh1            POSIX::SIGINT(),
2585759b3d2Safresh1            POSIX::SIGALRM(),
2595759b3d2Safresh1            POSIX::SIGHUP(),
2605759b3d2Safresh1            POSIX::SIGTERM(),
2615759b3d2Safresh1            POSIX::SIGUSR1(),
2625759b3d2Safresh1            POSIX::SIGUSR2(),
2635759b3d2Safresh1        );
2645759b3d2Safresh1        $old = POSIX::SigSet->new;
2655759b3d2Safresh1        $blocked = POSIX::sigprocmask(POSIX::SIG_BLOCK(), $to_block, $old);
2665759b3d2Safresh1        # Silently go on if we failed to log signals, not much we can do.
2675759b3d2Safresh1    }
2685759b3d2Safresh1
2695759b3d2Safresh1    my ($ok, $err) = &try($code);
2705759b3d2Safresh1
2715759b3d2Safresh1    # If our block was successful we want to restore the old mask.
2725759b3d2Safresh1    POSIX::sigprocmask(POSIX::SIG_SETMASK(), $old, POSIX::SigSet->new()) if defined $blocked;
2735759b3d2Safresh1
2745759b3d2Safresh1    return ($ok, $err);
2755759b3d2Safresh1}
2765759b3d2Safresh1
2775759b3d2Safresh11;
2785759b3d2Safresh1
2795759b3d2Safresh1__END__
2805759b3d2Safresh1
2815759b3d2Safresh1=pod
2825759b3d2Safresh1
2835759b3d2Safresh1=encoding UTF-8
2845759b3d2Safresh1
2855759b3d2Safresh1=head1 NAME
2865759b3d2Safresh1
2875759b3d2Safresh1Test2::Util - Tools used by Test2 and friends.
2885759b3d2Safresh1
2895759b3d2Safresh1=head1 DESCRIPTION
2905759b3d2Safresh1
2915759b3d2Safresh1Collection of tools used by L<Test2> and friends.
2925759b3d2Safresh1
2935759b3d2Safresh1=head1 EXPORTS
2945759b3d2Safresh1
2955759b3d2Safresh1All exports are optional. You must specify subs to import.
2965759b3d2Safresh1
2975759b3d2Safresh1=over 4
2985759b3d2Safresh1
2995759b3d2Safresh1=item ($success, $error) = try { ... }
3005759b3d2Safresh1
3015759b3d2Safresh1Eval the codeblock, return success or failure, and the error message. This code
3025759b3d2Safresh1protects $@ and $!, they will be restored by the end of the run. This code also
3035759b3d2Safresh1temporarily blocks $SIG{DIE} handlers.
3045759b3d2Safresh1
3055759b3d2Safresh1=item protect { ... }
3065759b3d2Safresh1
3075759b3d2Safresh1Similar to try, except that it does not catch exceptions. The idea here is to
3085759b3d2Safresh1protect $@ and $! from changes. $@ and $! will be restored to whatever they
3095759b3d2Safresh1were before the run so long as it is successful. If the run fails $! will still
3105759b3d2Safresh1be restored, but $@ will contain the exception being thrown.
3115759b3d2Safresh1
3125759b3d2Safresh1=item CAN_FORK
3135759b3d2Safresh1
3145759b3d2Safresh1True if this system is capable of true or pseudo-fork.
3155759b3d2Safresh1
3165759b3d2Safresh1=item CAN_REALLY_FORK
3175759b3d2Safresh1
3185759b3d2Safresh1True if the system can really fork. This will be false for systems where fork
3195759b3d2Safresh1is emulated.
3205759b3d2Safresh1
3215759b3d2Safresh1=item CAN_THREAD
3225759b3d2Safresh1
3235759b3d2Safresh1True if this system is capable of using threads.
3245759b3d2Safresh1
3255759b3d2Safresh1=item USE_THREADS
3265759b3d2Safresh1
3275759b3d2Safresh1Returns true if threads are enabled, false if they are not.
3285759b3d2Safresh1
3295759b3d2Safresh1=item get_tid
3305759b3d2Safresh1
3315759b3d2Safresh1This will return the id of the current thread when threads are enabled,
3325759b3d2Safresh1otherwise it returns 0.
3335759b3d2Safresh1
3345759b3d2Safresh1=item my $file = pkg_to_file($package)
3355759b3d2Safresh1
3365759b3d2Safresh1Convert a package name to a filename.
3375759b3d2Safresh1
338f3efcd01Safresh1=item $string = ipc_separator()
339f3efcd01Safresh1
340f3efcd01Safresh1Get the IPC separator. Currently this is always the string C<'~'>.
341f3efcd01Safresh1
342f3efcd01Safresh1=item $string = gen_uid()
343f3efcd01Safresh1
344f3efcd01Safresh1Generate a unique id (NOT A UUID). This will typically be the process id, the
345f3efcd01Safresh1thread id, the time, and an incrementing integer all joined with the
346f3efcd01Safresh1C<ipc_separator()>.
347f3efcd01Safresh1
348f3efcd01Safresh1These ID's are unique enough for most purposes. For identical ids to be
349f3efcd01Safresh1generated you must have 2 processes with the same PID generate IDs at the same
350f3efcd01Safresh1time with the same current state of the incrementing integer. This is a
351f3efcd01Safresh1perfectly reasonable thing to expect to happen across multiple machines, but is
352f3efcd01Safresh1quite unlikely to happen on one machine.
353f3efcd01Safresh1
354f3efcd01Safresh1This can fail to be unique if a process generates an id, calls exec, and does
355f3efcd01Safresh1it again after the exec and it all happens in less than a second. It can also
356f3efcd01Safresh1happen if the systems process id's cycle in less than a second allowing 2
357f3efcd01Safresh1different programs that use this generator to run with the same PID in less
358f3efcd01Safresh1than a second. Both these cases are sufficiently unlikely. If you need
359f3efcd01Safresh1universally unique ids, or ids that are unique in these conditions, look at
360f3efcd01Safresh1L<Data::UUID>.
361f3efcd01Safresh1
3625759b3d2Safresh1=item ($ok, $err) = do_rename($old_name, $new_name)
3635759b3d2Safresh1
3645759b3d2Safresh1Rename a file, this wraps C<rename()> in a way that makes it more reliable
3655759b3d2Safresh1cross-platform when trying to rename files you recently altered.
3665759b3d2Safresh1
3675759b3d2Safresh1=item ($ok, $err) = do_unlink($filename)
3685759b3d2Safresh1
3695759b3d2Safresh1Unlink a file, this wraps C<unlink()> in a way that makes it more reliable
3705759b3d2Safresh1cross-platform when trying to unlink files you recently altered.
3715759b3d2Safresh1
3725759b3d2Safresh1=item ($ok, $err) = try_sig_mask { ... }
3735759b3d2Safresh1
3745759b3d2Safresh1Complete an action with several signals masked, they will be unmasked at the
3755759b3d2Safresh1end allowing any signals that were intercepted to get handled.
3765759b3d2Safresh1
3775759b3d2Safresh1This is primarily used when you need to make several actions atomic (against
3785759b3d2Safresh1some signals anyway).
3795759b3d2Safresh1
3805759b3d2Safresh1Signals that are intercepted:
3815759b3d2Safresh1
3825759b3d2Safresh1=over 4
3835759b3d2Safresh1
3845759b3d2Safresh1=item SIGINT
3855759b3d2Safresh1
3865759b3d2Safresh1=item SIGALRM
3875759b3d2Safresh1
3885759b3d2Safresh1=item SIGHUP
3895759b3d2Safresh1
3905759b3d2Safresh1=item SIGTERM
3915759b3d2Safresh1
3925759b3d2Safresh1=item SIGUSR1
3935759b3d2Safresh1
3945759b3d2Safresh1=item SIGUSR2
3955759b3d2Safresh1
3965759b3d2Safresh1=back
3975759b3d2Safresh1
3985759b3d2Safresh1=back
3995759b3d2Safresh1
4005759b3d2Safresh1=head1 NOTES && CAVEATS
4015759b3d2Safresh1
4025759b3d2Safresh1=over 4
4035759b3d2Safresh1
4045759b3d2Safresh1=item 5.10.0
4055759b3d2Safresh1
4065759b3d2Safresh1Perl 5.10.0 has a bug when compiled with newer gcc versions. This bug causes a
4075759b3d2Safresh1segfault whenever a new thread is launched. Test2 will attempt to detect
4085759b3d2Safresh1this, and note that the system is not capable of forking when it is detected.
4095759b3d2Safresh1
4105759b3d2Safresh1=item Devel::Cover
4115759b3d2Safresh1
4125759b3d2Safresh1Devel::Cover does not support threads. CAN_THREAD will return false if
4135759b3d2Safresh1Devel::Cover is loaded before the check is first run.
4145759b3d2Safresh1
4155759b3d2Safresh1=back
4165759b3d2Safresh1
4175759b3d2Safresh1=head1 SOURCE
4185759b3d2Safresh1
4195759b3d2Safresh1The source code repository for Test2 can be found at
420*5486feefSafresh1L<https://github.com/Test-More/test-more/>.
4215759b3d2Safresh1
4225759b3d2Safresh1=head1 MAINTAINERS
4235759b3d2Safresh1
4245759b3d2Safresh1=over 4
4255759b3d2Safresh1
4265759b3d2Safresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt>
4275759b3d2Safresh1
4285759b3d2Safresh1=back
4295759b3d2Safresh1
4305759b3d2Safresh1=head1 AUTHORS
4315759b3d2Safresh1
4325759b3d2Safresh1=over 4
4335759b3d2Safresh1
4345759b3d2Safresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt>
4355759b3d2Safresh1
4365759b3d2Safresh1=item Kent Fredric E<lt>kentnl@cpan.orgE<gt>
4375759b3d2Safresh1
4385759b3d2Safresh1=back
4395759b3d2Safresh1
4405759b3d2Safresh1=head1 COPYRIGHT
4415759b3d2Safresh1
442256a93a4Safresh1Copyright 2020 Chad Granum E<lt>exodist@cpan.orgE<gt>.
4435759b3d2Safresh1
4445759b3d2Safresh1This program is free software; you can redistribute it and/or
4455759b3d2Safresh1modify it under the same terms as Perl itself.
4465759b3d2Safresh1
447*5486feefSafresh1See L<https://dev.perl.org/licenses/>
4485759b3d2Safresh1
4495759b3d2Safresh1=cut
450