]> git.vpit.fr Git - perl/modules/VPIT-TestHelpers.git/blobdiff - lib/VPIT/TestHelpers.pm
Fallback to $Config{perlpath} if $^X is not good enough
[perl/modules/VPIT-TestHelpers.git] / lib / VPIT / TestHelpers.pm
index 8e056293f5ab422177c0b0fe65a4a4c9bf423a44..475a86a47c4e6636199f2db4394026ef0fd2277c 100644 (file)
@@ -3,18 +3,58 @@ package VPIT::TestHelpers;
 use strict;
 use warnings;
 
-my %exports = (
- load_or_skip => \&load_or_skip,
- skip_all     => \&skip_all,
-);
+use Config ();
 
-sub import {
- my $pkg = caller;
+sub export_to_pkg {
+ my ($subs, $pkg) = @_;
 
- while (my ($name, $code) = each %exports) {
+ while (my ($name, $code) = each %$subs) {
   no strict 'refs';
   *{$pkg.'::'.$name} = $code;
  }
+
+ return 1;
+}
+
+my %default_exports = (
+ load_or_skip     => \&load_or_skip,
+ load_or_skip_all => \&load_or_skip_all,
+ run_perl         => \&run_perl,
+ skip_all         => \&skip_all,
+);
+
+my %features = (
+ threads => \&init_threads,
+ usleep  => \&init_usleep,
+);
+
+sub import {
+ shift;
+ my @opts = @_;
+
+ my %exports = %default_exports;
+
+ for (my $i = 0; $i <= $#opts; ++$i) {
+  my $feature = $opts[$i];
+  next unless defined $feature;
+
+  my $args;
+  if ($i < $#opts and defined $opts[$i+1] and ref $opts[$i+1] eq 'ARRAY') {
+   ++$i;
+   $args = $opts[$i];
+  } else {
+   $args = [ ];
+  }
+
+  my $handler = $features{$feature};
+  die "Unknown feature '$feature'" unless defined $handler;
+
+  my %syms = $handler->(@$args);
+
+  $exports{$_} = $syms{$_} for sort keys %syms;
+ }
+
+ export_to_pkg \%exports => scalar caller;
 }
 
 my $test_sub = sub {
@@ -32,6 +72,8 @@ my $test_sub = sub {
  return $glob ? *$glob{CODE} : undef;
 };
 
+sub skip { $test_sub->('skip')->(@_) }
+
 sub skip_all { $test_sub->('plan')->(skip_all => $_[0]) }
 
 sub diag {
@@ -42,9 +84,12 @@ sub diag {
 our $TODO;
 local $TODO;
 
-sub load_or_skip {
- my ($pkg, $ver, $imports, $desc) = @_;
+sub load {
+ my ($pkg, $ver, $imports) = @_;
+
  my $spec = $ver && $ver !~ /^[0._]*$/ ? "$pkg $ver" : $pkg;
+ my $err;
+
  local $@;
  if (eval "use $spec (); 1") {
   $ver = do { no strict 'refs'; ${"${pkg}::VERSION"} };
@@ -52,7 +97,7 @@ sub load_or_skip {
 
   if ($imports) {
    my @imports = @$imports;
-   my $caller  = (caller 0)[0];
+   my $caller  = (caller 1)[0];
    local $@;
    my $res = eval <<"IMPORTER";
 package
@@ -60,16 +105,120 @@ package
 BEGIN { \$pkg->import(\@imports) }
 1;
 IMPORTER
-   skip_all "Could not import '@imports' from $pkg $ver: $@" unless $res;
+   $err = "Could not import '@imports' from $pkg $ver: $@" unless $res;
   }
-  diag "Using $pkg $ver";
  } else {
   (my $file = "$pkg.pm") =~ s{::}{/}g;
   delete $INC{$file};
-  skip_all "$spec $desc";
+  $err = "Could not load $spec";
+ }
+
+ if ($err) {
+  return wantarray ? (0, $err) : 0;
+ } else {
+  diag "Using $pkg $ver";
+  return 1;
  }
 }
 
+sub load_or_skip {
+ my ($pkg, $ver, $imports, $tests) = @_;
+
+ die 'You must specify how many tests to skip' unless defined $tests;
+
+ my ($loaded, $err) = load($pkg, $ver, $imports);
+ skip $err => $tests unless $loaded;
+
+ return $loaded;
+}
+
+sub load_or_skip_all {
+ my ($pkg, $ver, $imports) = @_;
+
+ my ($loaded, $err) = load($pkg, $ver, $imports);
+ skip_all $err unless $loaded;
+
+ return $loaded;
+}
+
+sub run_perl {
+ my $code = shift;
+
+ my ($SystemRoot, $PATH) = @ENV{qw<SystemRoot PATH>};
+ my $ld_name  = $Config::Config{ldlibpthname};
+ my $ldlibpth = $ENV{$ld_name};
+
+ local %ENV;
+ $ENV{$ld_name}   = $ldlibpth   if                      defined $ldlibpth;
+ $ENV{SystemRoot} = $SystemRoot if $^O eq 'MSWin32' and defined $SystemRoot;
+ $ENV{PATH}       = $PATH       if $^O eq 'cygwin'  and defined $PATH;
+
+ my $perl = $^X;
+ unless (-e $perl and -x $perl) {
+  $perl = $Config::Config{perlpath};
+ }
+
+ system { $perl } $perl, '-T', map("-I$_", @INC), '-e', $code;
+}
+
+sub init_threads {
+ my ($pkg, $threadsafe, $force_var) = @_;
+
+ skip_all 'This perl wasn\'t built to support threads'
+                                            unless $Config::Config{useithreads};
+
+ $pkg = 'package' unless defined $pkg;
+ skip_all "This $pkg isn't thread safe" if defined $threadsafe and !$threadsafe;
+
+ $force_var = 'PERL_FORCE_TEST_THREADS' unless defined $force_var;
+ my $force  = $ENV{$force_var} ? 1 : !1;
+ skip_all 'perl 5.13.4 required to test thread safety'
+                                             unless $force or "$]" >= 5.013_004;
+
+ if (($INC{'Test/More.pm'} || $INC{'Test/Leaner.pm'}) && !$INC{'threads.pm'}) {
+  die 'Test::More/Test::Leaner was loaded too soon';
+ }
+
+ load_or_skip_all 'threads',         $force ? '0' : '1.67', [ ];
+ load_or_skip_all 'threads::shared', $force ? '0' : '1.14', [ ];
+
+ require Test::Leaner;
+
+ diag "Threads testing forced by \$ENV{$force_var}" if $force;
+
+ return spawn => \&spawn;
+}
+
+sub init_usleep {
+ my $usleep;
+
+ if (do { local $@; eval { require Time::HiRes; 1 } }) {
+  defined and diag "Using usleep() from Time::HiRes $_"
+                                                      for $Time::HiRes::VERSION;
+  $usleep = \&Time::HiRes::usleep;
+ } else {
+  diag 'Using fallback usleep()';
+  $usleep = sub {
+   my $s = int($_[0] / 2.5e5);
+   sleep $s if $s;
+  };
+ }
+
+ return usleep => $usleep;
+}
+
+sub spawn {
+ local $@;
+ my @diag;
+ my $thread = eval {
+  local $SIG{__WARN__} = sub { push @diag, "Thread creation warning: @_" };
+  threads->create(@_);
+ };
+ push @diag, "Thread creation error: $@" if $@;
+ diag @diag;
+ return $thread ? $thread : ();
+}
+
 package VPIT::TestHelpers::Guard;
 
 sub new {