File Coverage

blib/lib/Data/Checker.pm
Criterion Covered Total %
statement 92 130 70.7
branch 44 66 66.6
condition 23 42 54.7
subroutine 13 17 76.4
pod 9 9 100.0
total 181 264 68.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   181887 use warnings 'all';
  7         16  
  7         526  
10 7     7   46 use strict;
  7         15  
  7         282  
11 7     7   4857 use Module::Loaded;
  7         5690  
  7         644  
12 7     7   5469 use Parallel::ForkManager 0.7.6;
  7         232073  
  7         3489  
13              
14             our($VERSION);
15             $VERSION='1.08';
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 4     4 1 129330 my($class,@args) = @_;
29              
30 4         19 my $self = {
31             'parallel' => 1,
32             };
33              
34 4         11 bless $self, $class;
35              
36 4         16 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 0     0 1 0 my($self,$n) = @_;
47              
48 0 0 0     0 if (defined($n) && $n =~ /^\d+$/) {
49 0         0 $n += 0;
50             } else {
51 0         0 warn "WARNING: Invalid argument to Data::Checker::parallel\n";
52 0         0 return;
53             }
54              
55 0         0 $$self{'parallel'} = $n + 0;
56             }
57              
58             ###############################################################################
59              
60             sub check {
61 23     23 1 83639 my($self,$data,$type,$opts) = @_;
62              
63             # Check for data
64              
65 23         67 my (%data,$wantlist);
66 23 100       113 if (ref($data) eq 'ARRAY') {
    50          
67 21         58 %data = (map { $_,undef } @$data);
  51         195  
68 21         53 $wantlist = 1;
69             } elsif (ref($data) eq 'HASH') {
70 2         21 %data = %$data;
71 2         7 $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 23         104 my $func;
79 23 50       141 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         2 $func = $type;
84              
85             } else {
86 22         109 my $caller = ( caller() )[0];
87              
88             TRY:
89 22         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 132 100       1036 next if ($name !~ /^(.*)::(.+)$/);
103 110         359 my($mod) = ($1);
104 110 50       279 $mod = "main" if (! defined $mod);
105              
106             # Try loading the module (but not main:: or CALLER::
107              
108 110 100 66     994 if ($mod ne 'main' &&
      100        
109             $mod ne $caller &&
110             ! is_loaded($mod)) {
111 46 100       5530 next TRY if (! eval "require $mod");
112             }
113              
114             # Look for the function
115              
116 7     7   58 no strict 'refs';
  7         13  
  7         8813  
117 66 100       1035 if (defined &{$name}) {
  66         875  
118 22         73 $func = \&{$name};
  22         100  
119 22         76 last TRY;
120             }
121             }
122              
123 22 50       133 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       132 if ($$self{'parallel'} != 1) {
130 0         0 return $self->_check_parallel(\%data,$wantlist,$func,$opts);
131             } else {
132 23         118 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   73 my($self,$data,$wantlist,$func,$opts) = @_;
184 23         34 my(%pass,%fail,%info,%warn);
185              
186             ELE:
187 23         62 foreach my $ele (sort keys %{ $data }) {
  23         235  
188 56         244 my($element,$err,$warn,$info) = &$func($self,$ele,$$data{$ele},$opts);
189              
190 56 100 66     488 if (defined($err) && @$err) {
191 32         93 $fail{$ele} = $err;
192             } else {
193 24         92 $pass{$ele} = $$data{$ele};
194             }
195              
196 56 100 66     340 if (defined($warn) && @$warn) {
197 4         5 $warn{$ele} = $warn;
198             }
199 56 100 66     416 if (defined($info) && @$info) {
200 4         6 $info{$ele} = $info;
201             }
202             }
203              
204 23 100       135 if ($wantlist) {
205 21         147 my @pass = sort keys %pass;
206 21         231 return (\@pass,\%fail,\%warn,\%info);
207             } else {
208 2         34 return (\%pass,\%fail,\%warn,\%info);
209             }
210             }
211              
212             ###############################################################################
213             # CHECK OPTIONS METHODS
214             ###############################################################################
215              
216             sub check_performed {
217 214     214 1 439 my($self,$check_opts,$label) = @_;
218              
219 214 100       1207 return 1 if (exists $$check_opts{$label});
220 130         588 return 0;
221             }
222              
223             sub check_option {
224 235     235 1 518 my($self,$check_opts,$opt,$default,$label) = @_;
225              
226 235 100 66     1608 if (defined $label &&
    100 33        
227             exists $$check_opts{$label} &&
228             exists $$check_opts{$label}{$opt}) {
229 25         117 return $$check_opts{$label}{$opt};
230              
231             } elsif (exists $$check_opts{$opt}) {
232 1         4 return $$check_opts{$opt};
233              
234             } else {
235 209         650 return $default;
236             }
237             }
238              
239             sub check_level {
240 90     90 1 173 my($self,$check_opts,$label) = @_;
241 90         259 return $self->check_option($check_opts,'level','err',$label);
242             }
243              
244             sub check_message {
245 28     28 1 83 my($self,$check_opts,$label,$element,$message,$level,$err,$warn,$info) = @_;
246              
247 28         75 my $mess = $self->check_option($check_opts,'message',$message,$label);
248 28         62 my @mess;
249 28 50       107 if (ref($mess) eq 'ARRAY') {
250 0         0 @mess = @$mess;
251             } else {
252 28         78 @mess = ($mess);
253             }
254 28         74 foreach my $m (@mess) {
255 28         123 $m =~ s/__ELEMENT__/$element/g;
256             }
257              
258 28 50       109 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         130 push(@$err,@mess);
264             }
265             }
266              
267             sub check_value {
268 122     122 1 548 my($self,$check_opts,$label,$element,$value,$std_fail,$negate_fail,
269             $err,$warn,$info) = @_;
270              
271 122         182 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     604 my $do_check = 1 if (! $label ||
277             $self->check_performed($check_opts,$label));
278 122 100       381 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         305 my $level = $self->check_level($check_opts,$label);
284 90         386 my $negate = $self->check_option($check_opts,'negate',0,$label);
285 90 100       309 $negate = 0 if (! defined($negate_fail));
286              
287             # Check the value.
288              
289 90 100 100     876 if (! $negate && ! $value) {
    100 100        
290 22         78 $self->check_message($check_opts,$label,$element,$std_fail,
291             $level,$err,$warn,$info);
292             } elsif ($negate && $value) {
293 6         36 $self->check_message($check_opts,$label,$element,$negate_fail,
294             $level,$err,$warn,$info);
295             }
296              
297 90         257 last;
298             }
299              
300 122         400 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: