X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=t%2Flib%2FDBICTest%2FUtil.pm;h=cbbce3536d76124218930cb020b3e11bcf4a747a;hb=c0329273268971824784f239f32c7246e68da9c5;hp=d4bac7c7a9815bc562931b6a564635b3c7c13a5b;hpb=c9abd679a19b02556ed765f725c3fd7a68207257;p=dbsrgits%2FDBIx-Class.git diff --git a/t/lib/DBICTest/Util.pm b/t/lib/DBICTest/Util.pm index d4bac7c..cbbce35 100644 --- a/t/lib/DBICTest/Util.pm +++ b/t/lib/DBICTest/Util.pm @@ -3,54 +3,146 @@ package DBICTest::Util; use warnings; use strict; -# this noop trick initializes the STDOUT, so that the TAP::Harness -# issued IO::Select->can_read calls (which are blocking wtf wtf wtf) -# keep spinning and scheduling jobs -# This results in an overall much smoother job-queue drainage, since -# the Harness blocks less -# (ideally this needs to be addressed in T::H, but a quick patchjob -# broke everything so tabling it for now) -BEGIN { - if ($INC{'Test/Builder.pm'}) { - local $| = 1; - print "#\n"; - } -} +use ANFANG; + +use constant DEBUG_TEST_CONCURRENCY_LOCKS => + ( ($ENV{DBICTEST_DEBUG_CONCURRENCY_LOCKS}||'') =~ /^(\d+)$/ )[0] + || + 0 +; use Config; -use Carp 'confess'; +use Carp qw(cluck confess croak); +use Fcntl ':flock'; use Scalar::Util qw(blessed refaddr); +use DBIx::Class::_Util qw( scope_guard parent_dir ); use base 'Exporter'; -our @EXPORT_OK = qw(local_umask stacktrace check_customcond_args visit_namespaces); +our @EXPORT_OK = qw( + dbg stacktrace + local_umask find_co_root + visit_namespaces + check_customcond_args + await_flock DEBUG_TEST_CONCURRENCY_LOCKS +); + +if (DEBUG_TEST_CONCURRENCY_LOCKS) { + require DBI; + my $oc = DBI->can('connect'); + no warnings 'redefine'; + *DBI::connect = sub { + DBICTest::Util::dbg("Connecting to $_[1]"); + goto $oc; + } +} + +sub dbg ($) { + require Time::HiRes; + printf STDERR "\n%.06f %5s %-78s %s\n", + scalar Time::HiRes::time(), + $$, + $_[0], + $0, + ; +} -sub local_umask { +# File locking is hard. Really hard. By far the best lock implementation +# I've seen is part of the guts of File::Temp. However it is sadly not +# reusable. Since I am not aware of folks doing NFS parallel testing, +# nor are we known to work on VMS, I am just going to punt this and +# use the portable-ish flock() provided by perl itself. If this does +# not work for you - patches more than welcome. +# +# This figure esentially means "how long can a single test hold a +# resource before everyone else gives up waiting and aborts" or +# in other words "how long does the longest test-group legitimally run?" +my $lock_timeout_minutes = 15; # yes, that's long, I know +my $wait_step_seconds = 0.25; + +sub await_flock ($$) { + my ($fh, $locktype) = @_; + + my ($res, $tries); + while( + ! ( $res = flock( $fh, $locktype | LOCK_NB ) ) + and + ++$tries <= $lock_timeout_minutes * 60 / $wait_step_seconds + ) { + select( undef, undef, undef, $wait_step_seconds ); + + # "say something" every 10 cycles to work around RT#108390 + # jesus christ our tooling is such a crock of shit :( + print "#\n" if not $tries % 10; + } + + return $res; +} + + +sub local_umask ($) { return unless defined $Config{d_umask}; - die 'Calling local_umask() in void context makes no sense' + croak 'Calling local_umask() in void context makes no sense' if ! defined wantarray; - my $old_umask = umask(shift()); + my $old_umask = umask($_[0]); die "Setting umask failed: $!" unless defined $old_umask; - return bless \$old_umask, 'DBICTest::Util::UmaskGuard'; + scope_guard(sub { + local ($@, $!, $?); + + eval { + defined(umask $old_umask) or die "nope"; + 1; + } or cluck ( + "Unable to reset old umask '$old_umask': " . ($! || 'Unknown error') + ); + }); } -{ - package DBICTest::Util::UmaskGuard; - sub DESTROY { - local ($@, $!); - eval { defined (umask ${$_[0]}) or die }; - warn ( "Unable to reset old umask ${$_[0]}: " . ($!||'Unknown error') ) - if ($@ || $!); + +# Try to determine the root of a checkout/untar if possible +# OR throws an exception +my $co_root; +sub find_co_root () { + + $co_root ||= do { + + my @mod_parts = split /::/, (__PACKAGE__ . '.pm'); + my $inc_key = join ('/', @mod_parts); # %INC stores paths with / regardless of OS + + # a bit convoluted, but what we do here essentially is: + # - get the file name of this particular module + # - do 'cd ..' as many times as necessary to get to t/lib/../.. + + my $root = $INC{$inc_key} + or croak "\$INC{'$inc_key'} seems to be missing, this can't happen..."; + + $root = parent_dir $root + for 1 .. @mod_parts + 2; + + # do the check twice so that the exception is more informative in the + # very unlikely case of realpath returning garbage + # (Paththools are in really bad shape - handholding all the way down) + for my $call_realpath (0,1) { + + require Cwd and $root = ( Cwd::realpath($root) . '/' ) + if $call_realpath; + + croak "Unable to find root of DBIC checkout/untar: '${root}Makefile.PL' does not exist" + unless -f "${root}Makefile.PL"; + } + + $root; } } + sub stacktrace { my $frame = shift; $frame++; my (@stack, @frame); - while (@frame = caller($frame++)) { + while (@frame = CORE::caller($frame++)) { push @stack, [@frame[3,1,2]]; }