File Coverage

blib/lib/Test2/Tools/QuickDB.pm
Criterion Covered Total %
statement 59 75 78.6
branch 24 38 63.1
condition 14 17 82.3
subroutine 11 11 100.0
pod 4 5 80.0
total 112 146 76.7


line stmt bran cond sub pod time code
1             package Test2::Tools::QuickDB;
2 26     26   1942158 use strict;
  26         37  
  26         803  
3 26     26   93 use warnings;
  26         41  
  26         1470  
4              
5             our $VERSION = '0.000049';
6              
7 26     26   105 use Carp qw/croak/;
  26         34  
  26         1147  
8 26     26   103 use Test2::API qw/context/;
  26         38  
  26         839  
9 26     26   7292 use DBIx::QuickDB();
  26         74  
  26         656  
10              
11 26     26   9292 use Importer Importer => 'import';
  26         88878  
  26         166  
12              
13             our @EXPORT = qw/get_db_or_skipall get_db skipall_unless_can_db skipall_on_resource_error/;
14              
15             # Match the errors a host throws when it cannot give a database server the
16             # System V IPC resources it needs to start -- semaphore or shared-memory table
17             # exhaustion. These come through as the failed initdb/start command's captured
18             # output (e.g. PostgreSQL: "could not create semaphores: No space left on
19             # device", "semget(...)", SEMMNI/SEMMNS hints). Returns a human reason if the
20             # error is one of these, else undef.
21             sub resource_exhaustion_reason {
22 5     5 0 8 my ($err) = @_;
23 5 50       10 return undef unless defined $err;
24              
25 5 50 66     58 return "host is out of System V semaphores (cannot start a database server)"
      66        
26             if $err =~ /could not create semaphores/i
27             || $err =~ /\bsemget\(/
28             || $err =~ /\bSEMM(?:NI|NS)\b/;
29              
30 3 100 66     13 return "host is out of System V shared memory (cannot start a database server)"
31             if $err =~ /could not create shared memory/i
32             || $err =~ /\bshmget\(/;
33              
34 2         5 return undef;
35             }
36              
37             # If $err is a host IPC-exhaustion error, skip the whole test (it is an
38             # environment limit, not a fault in this distribution) -- this does not return,
39             # it terminates like skip_all. Otherwise returns false so the caller can rethrow.
40             sub skipall_on_resource_error {
41 5     5 1 168737 my ($err) = @_;
42              
43 5 100       11 my $reason = resource_exhaustion_reason($err) or return 0;
44              
45 3         21 my $ctx = context();
46 3         187 $ctx->plan(0, SKIP => ucfirst($reason));
47 0         0 $ctx->release;
48              
49 0         0 return 1;
50             }
51              
52             sub skipall_unless_can_db {
53 28     28 1 2395980 my %spec;
54 28 100       118 if (@_ == 1) {
55 10   100     85 my $type = ref($_[0]) || '';
56 10 100       35 if (!$type) {
    50          
    0          
57 9         35 $spec{driver} = $_[0];
58             }
59             elsif ($type eq 'ARRAY') {
60 1         3 $spec{drivers} = $_[0];
61             }
62             elsif ($type eq 'HASH') {
63 0         0 %spec = %{$_[0]};
  0         0  
64             }
65             else {
66 0         0 croak "Invalid Argument: $_[0]";
67             }
68             }
69             else {
70 18         80 %spec = @_;
71             }
72              
73 28         131 my $ctx = context();
74              
75 28 50       108615 $spec{bootstrap} = 1 unless defined $spec{bootstrap};
76 28 50       97 $spec{autostart} = 1 unless defined $spec{autostart};
77 28 50       78 $spec{load_sql} = 1 unless defined $spec{load_sql};
78              
79 28 100 100     137 my $drivers = $spec{driver} ? [$spec{driver}] : $spec{drivers} || [DBIx::QuickDB->plugins];
80              
81 28         11207 my $reason;
82 28         41 my $ok = 0;
83 28         67 for my $driver (@$drivers) {
84 53 50       111 next unless defined $driver;
85 53         310 my ($v, $fqn, $why) = DBIx::QuickDB->check_driver($driver, \%spec);
86 53 100       208 $reason = $why if @$drivers == 1;
87 53 50       204 next unless $v;
88 0         0 $ok = $fqn;
89 0         0 last;
90             }
91              
92 28 50       1388 if ($ok) {
93 0         0 $ctx->release;
94 0         0 return $ok;
95             }
96              
97 28   100     234 $ctx->plan(0, 'SKIP' => $reason || "no db driver is viable");
98 0         0 $ctx->release;
99              
100 0         0 return;
101             }
102              
103             sub get_db {
104             # Get a context in case anything below here has testing code.
105 2     2 1 3444 my $ctx = context();
106              
107 2         112 my $db = eval { DBIx::QuickDB->build_db(@_) };
  2         9  
108 2 50       15 if (my $err = $@) {
109             # A host out of System V semaphores/shared memory cannot start a server;
110             # that is an environment limit, not a fault here, so skip rather than
111             # fail. skipall_on_resource_error() terminates the test in that case;
112             # any other error is real and is rethrown.
113 2         5 skipall_on_resource_error($err);
114 1         4 $ctx->release;
115 1         26 die $err;
116             }
117              
118 0         0 $ctx->release;
119              
120 0         0 return $db;
121             }
122              
123             sub get_db_or_skipall {
124 4 100   4 1 413197 my $name = ref($_[0]) ? undef : shift(@_);
125 4   100     19 my $spec = shift(@_) || {};
126              
127 4         15 my $ctx = context();
128              
129 4         13024 skipall_unless_can_db(%$spec);
130 0 0         my $db = get_db($name ? $name : (), $spec);
131              
132 0           $ctx->release;
133              
134 0           return $db;
135             }
136              
137             1;
138              
139             __END__