| 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: |