The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Copyright (c) 2008-2017 Martin Becker.  All rights reserved.
# This package is free software; you can redistribute it and/or modify it
# under the same terms as Perl itself.

# Utility functions for tests:
# * conditionally skip tests if required modules are not available
# * conditionally skip tests intended for the maintainer only
# * conditionally skip tests if some file cannot be read into a string
# * fetch an executable file name of the perl binary currently running

package Test::MyUtils;

use 5.006;
use strict;
use warnings;
use Config;
use base 'Exporter';

our $VERSION   = '0.009';
our @EXPORT    = qw(use_or_bail maintainer_only);
our @EXPORT_OK = qw(
    slurp_or_bail this_perl report_version
    init_comp_check comp_ok
);
our %EXPORT_TAGS = (
    comp    => [qw(init_comp_check comp_ok)],
);

our $DIST_NAME    = _guess_distname();
our $MAX_FILESIZE = 1024 * 1024;

my $comp_failure  = 'no good reason';

sub _guess_distname {
    my $distname = 'This-Distribution';
    if (open my $rh, '<', 'README') {
        my $headline = <$rh>;
        if (defined $headline && $headline =~ /^\s*(\w+(?:[^\w\s]+\w+)*)\s/) {
            $distname = $1;
        }
        close $rh;
    }
    return $distname;
}

sub _skip_all {
    my ($reason) = @_;
    print "1..0 # SKIP $reason\n";
    exit 0;
}

# To enforce a minimum version of a module, supply a $version value.
# To use a module with default imports, omit $imports_ref.
# To use a module with explicit or no imports, supply an array reference.
sub use_or_bail {
    my ($module, $version, $imports_ref) = @_;

    if (!eval "require $module") {
        _skip_all("$module not available");
    }

    my $loaded_version = eval { $module->VERSION };
    if (defined $loaded_version) {
        print "# module $module version is $loaded_version\n";
    }

    if (defined($version) && !defined eval { $module->VERSION($version) }) {
        _skip_all("$module version $version or higher not available");
    }

    if (!$imports_ref || @{$imports_ref}) {
        my $package = caller;
        my @imports = $imports_ref? @{$imports_ref}: ();
        if (!eval "package $package; \$module->import(\@imports); 1") {
            my $error = $@;
            $error =~ s/\n.*//s;
            _skip_all("import of $module failed: $error");
        }
    }
    return 1;
}

# Call this before plan() in test scripts reserved for the maintainer.
# Add names of mandatory configuration features for further restrictions.
sub maintainer_only {
    my @required_features = @_;
    my $env_maint = 'MAINTAINER_OF_' . uc $DIST_NAME;
    $env_maint =~ s/[_\W]+/_/g;
    if (!$ENV{$env_maint}) {
        _skip_all("setenv $env_maint=1 to run these tests");
    }
    foreach my $feature (@required_features) {
        if (!$Config{$feature}) {
            _skip_all("feature not available: $feature");
        }
    }
    return 1;
}

# Call this before plan() in test scripts analysing some file.
# Return value is the file content.  Returns only on success.
sub slurp_or_bail {
    my ($filename) = @_;
    local $/;
    my $fh;
    my $result;
    my $err;
    if (!-e $filename) {
        $err = 'file does not exist';
    }
    elsif (!-f _) {
        $err = 'not a plain file';
    }
    elsif ($MAX_FILESIZE < -s _) {
        $err = 'file too large';
    }
    elsif (open $fh, '<', $filename) {
        defined($result = <$fh>) or $err = "cannot read: $!";
        close $fh;
    }
    else {
        $err = "cannot open: $!";
    }
    if (!defined $result) {
        _skip_all("$filename: $err");
    }
    return $result;
}

sub this_perl {
    my $this_perl = $Config{'perlpath'};
    my $suffix    = exists($Config{'_exe'})? $Config{'_exe'}: '';
    if ($^O ne 'VMS' && '' ne $suffix && $this_perl !~ /$suffix\z/) {
        $this_perl .= $suffix;
    }
    return $this_perl;
}

sub report_version {
    foreach my $module (@_) {
        my $text = 'can not be loaded';
        if (eval "require $module") {
            my $version = eval { $module->VERSION };
            if (defined $version) {
                $text = "version is $version";
            }
            else {
                $text = 'has no version';
            }
        }
        print "# module $module $text\n";
    }
}

sub init_comp_check {
    my ($ours, @alien) =
        map {
            my $v = $_->VERSION;
            "$_ v$v"
        } @_;
    my $theirs    = join q[ + ], @alien;
    my $is        = 1 == @alien? 'is': 'are';
    $comp_failure = "$theirs $is not compatible with $ours";
    die qq{use "Test" before initializing compatibility tests\n}
        if !exists $INC{'Test.pm'};
}

sub comp_ok {
    my ($ok, $topic) = @_;
    print "# failure with $topic\n" if !$ok && defined $topic;
    Test::skip(!$ok && $comp_failure, $ok);
}

1;
__END__