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   1756584 use strict;
  26         38  
  26         755  
3 26     26   92 use warnings;
  26         33  
  26         1514  
4              
5             our $VERSION = '0.000050';
6              
7 26     26   113 use Carp qw/croak/;
  26         34  
  26         1139  
8 26     26   103 use Test2::API qw/context/;
  26         59  
  26         957  
9 26     26   7357 use DBIx::QuickDB();
  26         60  
  26         684  
10              
11 26     26   9203 use Importer Importer => 'import';
  26         89141  
  26         197  
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 6 my ($err) = @_;
23 5 50       12 return undef unless defined $err;
24              
25 5 50 66     57 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     16 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         6 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 170647 my ($err) = @_;
42              
43 5 100       12 my $reason = resource_exhaustion_reason($err) or return 0;
44              
45 3         6 my $ctx = context();
46 3         201 $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 2355748 my %spec;
54 28 100       106 if (@_ == 1) {
55 10   100     60 my $type = ref($_[0]) || '';
56 10 100       30 if (!$type) {
    50          
    0          
57 9         34 $spec{driver} = $_[0];
58             }
59             elsif ($type eq 'ARRAY') {
60 1         4 $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         53 %spec = @_;
71             }
72              
73 28         124 my $ctx = context();
74              
75 28 50       109553 $spec{bootstrap} = 1 unless defined $spec{bootstrap};
76 28 50       118 $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     144 my $drivers = $spec{driver} ? [$spec{driver}] : $spec{drivers} || [DBIx::QuickDB->plugins];
80              
81 28         10605 my $reason;
82 28         46 my $ok = 0;
83 28         69 for my $driver (@$drivers) {
84 53 50       122 next unless defined $driver;
85 53         295 my ($v, $fqn, $why) = DBIx::QuickDB->check_driver($driver, \%spec);
86 53 100       1502 $reason = $why if @$drivers == 1;
87 53 50       151 next unless $v;
88 0         0 $ok = $fqn;
89 0         0 last;
90             }
91              
92 28 50       141 if ($ok) {
93 0         0 $ctx->release;
94 0         0 return $ok;
95             }
96              
97 28   100     1574 $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 5575 my $ctx = context();
106              
107 2         109 my $db = eval { DBIx::QuickDB->build_db(@_) };
  2         9  
108 2 50       16 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         3 $ctx->release;
115 1         25 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 293767 my $name = ref($_[0]) ? undef : shift(@_);
125 4   100     18 my $spec = shift(@_) || {};
126              
127 4         14 my $ctx = context();
128              
129 4         9853 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__