File Coverage

blib/lib/Data/Checker.pm
Criterion Covered Total %
statement 96 130 73.8
branch 46 66 69.7
condition 24 42 57.1
subroutine 14 17 82.3
pod 9 9 100.0
total 189 264 71.5


line stmt bran cond sub pod time code
1             package Data::Checker;
2             # Copyright (c) 2013-2016 Sullivan Beck. All rights reserved.
3             # This program is free software; you can redistribute it and/or modify it
4             # under the same terms as Perl itself.
5              
6             ###############################################################################
7              
8             require 5.008;
9 7     7   191489 use warnings 'all';
  7         22  
  7         619  
10 7     7   53 use strict;
  7         15  
  7         288  
11 7     7   6230 use Module::Loaded;
  7         7807  
  7         884  
12 7     7   5817 use Parallel::ForkManager 0.7.6;
  7         346354  
  7         5148  
13              
14             our($VERSION);
15             $VERSION='1.07';
16              
17             ###############################################################################
18             # BASE METHODS
19             ###############################################################################
20              
21             sub version {
22 0     0 1 0 my($self) = @_;
23              
24 0         0 return $VERSION;
25             }
26              
27             sub new {
28 7     7 1 1918 my($class,@args) = @_;
29              
30 7         35 my $self = {
31             'parallel' => 1,
32             };
33              
34 7         21 bless $self, $class;
35              
36 7         31 return $self;
37             }
38              
39             # Some checks can be run in parallel. For these, passing in $n
40             # has the following effect:
41             # $n = 0 : all of them will run simultaneously
42             # $n = 1 : only one check at a time
43             # $n > 1 : $n checks at a time
44             #
45             sub parallel {
46 2     2 1 10 my($self,$n) = @_;
47              
48 2 50 33     24 if (defined($n) && $n =~ /^\d+$/) {
49 2         6 $n += 0;
50             } else {
51 0         0 warn "WARNING: Invalid argument to Data::Checker::parallel\n";
52 0         0 return;
53             }
54              
55 2         10 $$self{'parallel'} = $n + 0;
56             }
57              
58             ###############################################################################
59              
60             sub check {
61 26     26 1 136504 my($self,$data,$type,$opts) = @_;
62              
63             # Check for data
64              
65 26         55 my (%data,$wantlist);
66 26 100       125 if (ref($data) eq 'ARRAY') {
    50          
67 24         117 %data = (map { $_,undef } @$data);
  60         259  
68 24         68 $wantlist = 1;
69             } elsif (ref($data) eq 'HASH') {
70 2         16 %data = %$data;
71 2         5 $wantlist = 0;
72             } else {
73 0         0 die "ERROR: invalid data passed to Data::Checker::check\n";
74             }
75              
76             # Find the check function
77              
78 26         49 my $func;
79 26 50       138 if (! defined($type)) {
    100          
80 0         0 die "ERROR: invalid check function passed to Data::Checker::check\n";
81              
82             } elsif (ref($type) eq 'CODE') {
83 1         3 $func = $type;
84              
85             } else {
86 25         124 my $caller = ( caller() )[0];
87              
88             TRY:
89 25         202 foreach my $name ("${type}",
90             "${type}::check",
91             "${caller}::${type}",
92             "${caller}::${type}::check",
93             "Data::Checker::${type}",
94             "Data::Checker::${type}::check",
95             ) {
96              
97             # Ignore the case where $name does not have '::' because that means
98             # we called it with the name of a function in the CALLER namespace
99             # (so it'll get handled by one of the "${caller}::" cases, or $type
100             # is a sub-namespace of Data::Checker.
101              
102 150 100       1115 next if ($name !~ /^(.*)::(.+)$/);
103 125         403 my($mod) = ($1);
104 125 50       356 $mod = "main" if (! defined $mod);
105              
106             # Try loading the module (but not main:: or CALLER::
107              
108 125 100 66     1004 if ($mod ne 'main' &&
      100        
109             $mod ne $caller &&
110             ! is_loaded($mod)) {
111 55 100       6420 next TRY if (! eval "require $mod");
112             }
113              
114             # Look for the function
115              
116 7     7   90 no strict 'refs';
  7         18  
  7         13071  
117 72 100       1030 if (defined &{$name}) {
  72         685  
118 22         39 $func = \&{$name};
  22         129  
119 22         67 last TRY;
120             }
121             }
122              
123 25 100       571 die "ERROR: no valid check function passed to Data::Checker::check\n"
124             if (! defined $func);
125             }
126              
127             # Call parallel or serial check
128              
129 23 50       134 if ($$self{'parallel'} != 1) {
130 0         0 return $self->_check_parallel(\%data,$wantlist,$func,$opts);
131             } else {
132 23         110 return $self->_check_serial(\%data,$wantlist,$func,$opts);
133             }
134             }
135              
136             sub _check_parallel {
137 0     0   0 my($self,$data,$wantlist,$func,$opts) = @_;
138 0         0 my(%pass,%fail,%info,%warn);
139 0         0 my @ele = keys %$data;
140 0 0       0 my $max_proc = ($$self{'parallel'} > 1 ? $$self{'parallel'} : @ele);
141              
142 0         0 my $manager = Parallel::ForkManager->new($max_proc);
143             $manager->run_on_finish
144             (
145             sub {
146 0     0   0 my($pid,$exit_code,$id,$signal,$core_dump,$funcdata) = @_;
147 0         0 my($ele,$err,$warn,$info) = @$funcdata;
148              
149 0 0 0     0 if (defined($err) && @$err) {
150 0         0 $fail{$ele} = $err;
151             } else {
152 0         0 $pass{$ele} = $$data{$ele};
153             }
154              
155 0 0 0     0 if (defined($warn) && @$warn) {
156 0         0 $warn{$ele} = $warn;
157             }
158 0 0 0     0 if (defined($info) && @$info) {
159 0         0 $info{$ele} = $info;
160             }
161 0         0 });
162              
163             ELE:
164 0         0 foreach my $ele (sort keys %$data) {
165 0 0       0 $manager->start and next;
166              
167 0         0 my($element,$err,$warn,$info) = &$func($self,$ele,$$data{$ele},$opts);
168              
169 0         0 $manager->finish(0,[$element,$err,$warn,$info]);
170             }
171              
172 0         0 $manager->wait_all_children();
173              
174 0 0       0 if ($wantlist) {
175 0         0 my @pass = sort keys %pass;
176 0         0 return (\@pass,\%fail,\%warn,\%info);
177             } else {
178 0         0 return (\%pass,\%fail,\%warn,\%info);
179             }
180             }
181              
182             sub _check_serial {
183 23     23   56 my($self,$data,$wantlist,$func,$opts) = @_;
184 23         34 my(%pass,%fail,%info,%warn);
185              
186             ELE:
187 23         35 foreach my $ele (sort keys %{ $data }) {
  23         159  
188 56         249 my($element,$err,$warn,$info) = &$func($self,$ele,$$data{$ele},$opts);
189              
190 56 100 66     567 if (defined($err) && @$err) {
191 32         105 $fail{$ele} = $err;
192             } else {
193 24         93 $pass{$ele} = $$data{$ele};
194             }
195              
196 56 100 66     348 if (defined($warn) && @$warn) {
197 4         7 $warn{$ele} = $warn;
198             }
199 56 100 66     414 if (defined($info) && @$info) {
200 4         10 $info{$ele} = $info;
201             }
202             }
203              
204 23 100       87 if ($wantlist) {
205 21         120 my @pass = sort keys %pass;
206 21         197 return (\@pass,\%fail,\%warn,\%info);
207             } else {
208 2         19 return (\%pass,\%fail,\%warn,\%info);
209             }
210             }
211              
212             ###############################################################################
213             # CHECK OPTIONS METHODS
214             ###############################################################################
215              
216             sub check_performed {
217 214     214 1 323 my($self,$check_opts,$label) = @_;
218              
219 214 100       1242 return 1 if (exists $$check_opts{$label});
220 130         495 return 0;
221             }
222              
223             sub check_option {
224 235     235 1 418 my($self,$check_opts,$opt,$default,$label) = @_;
225              
226 235 100 66     1300 if (defined $label &&
    100 33        
227             exists $$check_opts{$label} &&
228             exists $$check_opts{$label}{$opt}) {
229 25         91 return $$check_opts{$label}{$opt};
230              
231             } elsif (exists $$check_opts{$opt}) {
232 1         3 return $$check_opts{$opt};
233              
234             } else {
235 209         549 return $default;
236             }
237             }
238              
239             sub check_level {
240 90     90 1 148 my($self,$check_opts,$label) = @_;
241 90         235 return $self->check_option($check_opts,'level','err',$label);
242             }
243              
244             sub check_message {
245 28     28 1 78 my($self,$check_opts,$label,$element,$message,$level,$err,$warn,$info) = @_;
246              
247 28         68 my $mess = $self->check_option($check_opts,'message',$message,$label);
248 28         56 my @mess;
249 28 50       82 if (ref($mess) eq 'ARRAY') {
250 0         0 @mess = @$mess;
251             } else {
252 28         85 @mess = ($mess);
253             }
254 28         68 foreach my $m (@mess) {
255 28         100 $m =~ s/__ELEMENT__/$element/g;
256             }
257              
258 28 50       104 if ($level eq 'info') {
    50          
259 0         0 push(@$info,@mess);
260             } elsif ($level eq 'warn') {
261 0         0 push(@$warn,@mess);
262             } else {
263 28         118 push(@$err,@mess);
264             }
265             }
266              
267             sub check_value {
268 122     122 1 356 my($self,$check_opts,$label,$element,$value,$std_fail,$negate_fail,
269             $err,$warn,$info) = @_;
270              
271 122         166 while (1) {
272              
273             # We perform the check if the $label check is performed, or if
274             # there is no label.
275              
276 122 100 100     529 my $do_check = 1 if (! $label ||
277             $self->check_performed($check_opts,$label));
278 122 100       351 last if (! $do_check);
279              
280             # Find the severity level and negate options (negate will never
281             # occur if we didn't pass in a negate_fail message).
282              
283 90         237 my $level = $self->check_level($check_opts,$label);
284 90         217 my $negate = $self->check_option($check_opts,'negate',0,$label);
285 90 100       290 $negate = 0 if (! defined($negate_fail));
286              
287             # Check the value.
288              
289 90 100 100     809 if (! $negate && ! $value) {
    100 100        
290 22         70 $self->check_message($check_opts,$label,$element,$std_fail,
291             $level,$err,$warn,$info);
292             } elsif ($negate && $value) {
293 6         35 $self->check_message($check_opts,$label,$element,$negate_fail,
294             $level,$err,$warn,$info);
295             }
296              
297 90         460 last;
298             }
299              
300 122         376 return ($element,$err,$warn,$info);
301             }
302              
303             1;
304             # Local Variables:
305             # mode: cperl
306             # indent-tabs-mode: nil
307             # cperl-indent-level: 3
308             # cperl-continued-statement-offset: 2
309             # cperl-continued-brace-offset: 0
310             # cperl-brace-offset: 0
311             # cperl-brace-imaginary-offset: 0
312             # cperl-label-offset: 0
313             # End: