Excise live test left over after ac0c082542
[dbsrgits/DBIx-Class.git] / t / lib / DBICTest / BaseSchema.pm
1 package #hide from pause
2   DBICTest::BaseSchema;
3
4 use strict;
5 use warnings;
6
7 use base qw(DBICTest::Base DBIx::Class::Schema);
8
9 use Fcntl qw(:DEFAULT :seek :flock);
10 use Time::HiRes 'sleep';
11 use DBICTest::Util::LeakTracer qw(populate_weakregistry assert_empty_weakregistry);
12 use DBICTest::Util 'local_umask';
13 use namespace::clean;
14
15 our $locker;
16 END {
17   # we need the $locker to be referenced here for delayed destruction
18   if ($locker->{lock_name} and ($ENV{DBICTEST_LOCK_HOLDER}||0) == $$) {
19     #warn "$$ $0 $locker->{type} LOCK RELEASED";
20   }
21 }
22
23 my $weak_registry = {};
24
25 sub connection {
26   my $self = shift->next::method(@_);
27
28 # MASSIVE FIXME
29 # we can't really lock based on DSN, as we do not yet have a way to tell that e.g.
30 # DBICTEST_MSSQL_DSN=dbi:Sybase:server=192.168.0.11:1433;database=dbtst
31 #  and
32 # DBICTEST_MSSQL_ODBC_DSN=dbi:ODBC:server=192.168.0.11;port=1433;database=dbtst;driver=FreeTDS;tds_version=8.0
33 # are the same server
34 # hence we lock everything based on sqlt_type or just globally if not available
35 # just pretend we are python you know? :)
36
37
38   # when we get a proper DSN resolution sanitize to produce a portable lockfile name
39   # this may look weird and unnecessary, but consider running tests from
40   # windows over a samba share >.>
41   #utf8::encode($dsn);
42   #$dsn =~ s/([^A-Za-z0-9_\-\.\=])/ sprintf '~%02X', ord($1) /ge;
43   #$dsn =~ s/^dbi/dbi/i;
44
45   # provide locking for physical (non-memory) DSNs, so that tests can
46   # safely run in parallel. While the harness (make -jN test) does set
47   # an envvar, we can not detect when a user invokes prove -jN. Hence
48   # perform the locking at all times, it shouldn't hurt.
49   # the lock fh *should* inherit across forks/subprocesses
50   #
51   # File locking is hard. Really hard. By far the best lock implementation
52   # I've seen is part of the guts of File::Temp. However it is sadly not
53   # reusable. Since I am not aware of folks doing NFS parallel testing,
54   # nor are we known to work on VMS, I am just going to punt this and
55   # use the portable-ish flock() provided by perl itself. If this does
56   # not work for you - patches more than welcome.
57   if (
58     ! $DBICTest::global_exclusive_lock
59       and
60     ( ! $ENV{DBICTEST_LOCK_HOLDER} or $ENV{DBICTEST_LOCK_HOLDER} == $$ )
61       and
62     ref($_[0]) ne 'CODE'
63       and
64     ($_[0]||'') !~ /^ (?i:dbi) \: SQLite \: (?: dbname\= )? (?: \:memory\: | t [\/\\] var [\/\\] DBIxClass\-) /x
65   ) {
66
67     my $locktype = do {
68       # guard against infinite recursion
69       local $ENV{DBICTEST_LOCK_HOLDER} = -1;
70
71       # we need to connect a forced fresh clone so that we do not upset any state
72       # of the main $schema (some tests examine it quite closely)
73       local $SIG{__WARN__} = sub {};
74       local $@;
75       my $storage = eval {
76         my $st = ref($self)->connect(@{$self->storage->connect_info})->storage;
77         $st->ensure_connected;  # do connect here, to catch a possible throw
78         $st;
79       };
80       $storage
81         ? do {
82           my $t = $storage->sqlt_type || 'generic';
83           eval { $storage->disconnect };
84           $t;
85         }
86         : undef
87       ;
88     };
89
90     # Never hold more than one lock. This solves the "lock in order" issues
91     # unrelated tests may have
92     # Also if there is no connection - there is no lock to be had
93     if ($locktype and (!$locker or $locker->{type} ne $locktype)) {
94
95       # this will release whatever lock we may currently be holding
96       # which is fine since the type does not match as checked above
97       undef $locker;
98
99       my $lockpath = DBICTest::RunMode->tmpdir->file("_dbictest_$locktype.lock");
100
101       #warn "$$ $0 $locktype GRABBING LOCK";
102       my $lock_fh;
103       {
104         my $u = local_umask(0); # so that the file opens as 666, and any user can lock
105         sysopen ($lock_fh, $lockpath, O_RDWR|O_CREAT) or die "Unable to open $lockpath: $!";
106       }
107       flock ($lock_fh, LOCK_EX) or die "Unable to lock $lockpath: $!";
108       #warn "$$ $0 $locktype LOCK GRABBED";
109
110       # see if anyone was holding a lock before us, and wait up to 5 seconds for them to terminate
111       # if we do not do this we may end up trampling over some long-running END or somesuch
112       seek ($lock_fh, 0, SEEK_SET) or die "seek failed $!";
113       my $old_pid;
114       if (
115         read ($lock_fh, $old_pid, 100)
116           and
117         ($old_pid) = $old_pid =~ /^(\d+)$/
118       ) {
119         for (1..50) {
120           kill (0, $old_pid) or last;
121           sleep 0.1;
122         }
123       }
124       #warn "$$ $0 $locktype POST GRAB WAIT";
125
126       truncate $lock_fh, 0;
127       seek ($lock_fh, 0, SEEK_SET) or die "seek failed $!";
128       $lock_fh->autoflush(1);
129       print $lock_fh $$;
130
131       $ENV{DBICTEST_LOCK_HOLDER} ||= $$;
132
133       $locker = {
134         type => $locktype,
135         fh => $lock_fh,
136         lock_name => "$lockpath",
137       };
138     }
139   }
140
141   if ($INC{'Test/Builder.pm'}) {
142     populate_weakregistry ( $weak_registry, $self->storage );
143
144     my $cur_connect_call = $self->storage->on_connect_call;
145
146     $self->storage->on_connect_call([
147       (ref $cur_connect_call eq 'ARRAY'
148         ? @$cur_connect_call
149         : ($cur_connect_call || ())
150       ),
151       [sub {
152         populate_weakregistry( $weak_registry, shift->_dbh )
153       }],
154     ]);
155   }
156
157   return $self;
158 }
159
160 sub clone {
161   my $self = shift->next::method(@_);
162   populate_weakregistry ( $weak_registry, $self )
163     if $INC{'Test/Builder.pm'};
164   $self;
165 }
166
167 END {
168   assert_empty_weakregistry($weak_registry, 'quiet');
169 }
170
171 1;