File Coverage

blib/lib/CAD/Firemen/Analyze.pm
Criterion Covered Total %
statement 27 351 7.6
branch 0 168 0.0
condition 0 45 0.0
subroutine 9 14 64.2
pod 5 5 100.0
total 41 583 7.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             ######################
3             #
4             # Copyright (C) 2011 TU Clausthal, Institut fuer Maschinenwesen, Joachim Langenbach
5             #
6             # This program is free software: you can redistribute it and/or modify
7             # it under the terms of the GNU General Public License as published by
8             # the Free Software Foundation, either version 3 of the License, or
9             # (at your option) any later version.
10             #
11             # This program is distributed in the hope that it will be useful,
12             # but WITHOUT ANY WARRANTY; without even the implied warranty of
13             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14             # GNU General Public License for more details.
15             #
16             # You should have received a copy of the GNU General Public License
17             # along with this program. If not, see .
18             #
19             ######################
20              
21             # Pod::Weaver infos
22             # ABSTRACT: Module provides functions to compare to lists with options (and values)
23              
24 1     1   5 use strict;
  1         1  
  1         21  
25 1     1   5 use warnings;
  1         2  
  1         40  
26              
27             package CAD::Firemen::Analyze;
28             {
29             $CAD::Firemen::Analyze::VERSION = '0.7.1';
30             }
31 1     1   4 use Exporter 'import';
  1         7  
  1         47  
32             our @EXPORT_OK = qw(compare checkConfig checkTreeConfig optionsToIngoreAtPathCheckings);
33 1     1   757 use POSIX;
  1         6717  
  1         5  
34 1     1   17128 use File::Spec;
  1         1  
  1         27  
35              
36 1     1   606 use CAD::Firemen::Change;
  1         2  
  1         48  
37 1     1   5 use CAD::Firemen::Common qw(print2ColsRightAligned printBlock maxLength testPassed testFailed printColored strip);
  1         1  
  1         69  
38 1     1   459 use CAD::Firemen::Load qw(loadCDB loadConfig loadDatabase);
  1         2  
  1         57  
39 1     1   475 use CAD::Firemen::Option::Check;
  1         2  
  1         2927  
40              
41             sub compare {
42 0     0 1   my %options1 = ();
43 0           my %options2 = ();
44 0           my $refOptions1 = shift;
45 0           my $refOptions2 = shift;
46              
47             # result variables
48 0           my %added = ();
49 0           my %changed = ();
50 0           my %removed = ();
51 0           my %duplicates = ();
52              
53 0 0         if(!defined($refOptions1)){
54 0           return (\%added, \%changed, \%removed, \%duplicates);
55             }
56 0 0         if(!defined($refOptions2)){
57 0           return (\%added, \%changed, \%removed, \%duplicates);
58             }
59 0           %options1 = %{$refOptions1};
  0            
60 0           %options2= %{$refOptions2};
  0            
61              
62             # Compare the options
63             # get the options, which are set in config 2 and not in config 1 (added)
64             # also determine changed options
65 0           foreach my $opt (sort(keys(%options2))){
66 0 0         if(scalar(keys(%{$options2{$opt}})) > 1){
  0            
67 0           $duplicates{$opt} = "Duplicated in second config at lines ". join(", ", keys(%{$options2{$opt}})) .".";
  0            
68             }
69 0 0         if(!exists($options1{$opt})){
70 0           my @keys = keys(%{$options2{$opt}});
  0            
71 0           $added{$opt} = $keys[0];
72             }
73             else{
74 0 0         if(scalar(keys(%{$options1{$opt}})) > 1){
  0            
75 0 0         if(exists($duplicates{$opt})){
76 0           $duplicates{$opt} .= " ";
77             }
78             else{
79 0           $duplicates{$opt} = "";
80             }
81 0           $duplicates{$opt} .= "Duplicated in first config at lines ". join(", ", keys(%{$options1{$opt}})) .".";
  0            
82             }
83             # evalute changes and assume, that the order is not changed
84             # @TODO Get those combination of changes, with less changes
85             # e.g. a change with Case only is smaller change than NoSpecial and so on
86 0           my $max = $options1{$opt};
87 0           my $min = $options2{$opt};
88 0           my @keys1 = sort(keys(%{$options1{$opt}}));
  0            
89 0           my @keys2 = sort(keys(%{$options2{$opt}}));
  0            
90 0 0         if(ref($options1{$opt}->{$keys1[0]}) eq "HASH"){
91             # it's the format of the cdb files, do not try to detect changes
92 0           next;
93             }
94 0 0         if(scalar(@keys1) < scalar(@keys2)){
95 0           $max = $options2{$opt};
96 0           $min = $options1{$opt};
97             }
98 0           for(my $i = 0; $i < scalar(keys(%{$max})); $i++){
  0            
99 0           my $change = new CAD::Firemen::Change("name" => $opt);
100 0 0         if($i < scalar(keys(%{$min}))){
  0            
101 0           $change->setValueOld($options1{$opt}->{$keys1[$i]});
102 0           $change->setValueNew($options2{$opt}->{$keys2[$i]});
103             }
104             else{
105 0 0         if(scalar(@keys1) < scalar(@keys2)){
106 0           $change->setValueOld("NOT AVAILABLE");
107 0           $change->setValueNew($options2{$opt}->{$keys2[$i]});
108             }
109             else{
110 0           $change->setValueOld($options1{$opt}->{$keys1[$i]});
111 0           $change->setValueNew("NOT AVAILABLE");
112             }
113             }
114 0           $change->evalChange();
115 0 0         if(!$change->changeType(CAD::Firemen::Change::Type->NoChange)){
116 0 0         if(!exists($changed{$opt})){
117 0           $changed{$opt} = [];
118             }
119 0           push(@{$changed{$opt}}, $change);
  0            
120             }
121             }
122             }
123             }
124              
125 0           foreach my $opt (sort(keys(%options1))){
126 0 0         if(!exists($options2{$opt})){
127 0           my @keys = keys(%{$options1{$opt}});
  0            
128 0           $removed{$opt} = $keys[0];
129             }
130             }
131              
132 0           return (\%added, \%changed, \%removed, \%duplicates);
133             }
134              
135             sub optionsToIngoreAtPathCheckings {
136             # allowed options with relative paths
137             # (it is only checked, whether the key exists, the value is ignored)
138 0     0 1   my %allowedRelativePaths = ();
139 0           $allowedRelativePaths{"TRAIL_DIR"} = 1;
140 0           $allowedRelativePaths{"PROTKDAT"} = 1;
141 0           return %allowedRelativePaths;
142             }
143              
144             sub optionsToIngoreAtDuplicatesCheckings {
145             # allowed options with relative paths
146             # (it is only checked, whether the key exists, the value is ignored)
147 0     0 1   my %allowedDuplicates = ();
148 0           $allowedDuplicates{"PROTKDAT"} = 1;
149 0           return %allowedDuplicates;
150             }
151              
152             sub checkConfig {
153 0     0 1   my (%params) = @_;
154 0           my $dbh = $params{"databaseHandle"};
155 0           my $cdbUrl = $params{"cdbUrl"};
156 0           my $cfgUrl = $params{"cfgUrl"};
157 0           my $caseInsensitive = $params{"caseInsensitive"};
158 0           my $verbose = $params{"verbose"};
159 0           my $description = $params{"description"};
160              
161             # allowed options with relative paths
162             # (it is only checked, whether the key exists, the value is ignored)
163 0           my %allowedRelativePaths = optionsToIngoreAtPathCheckings();
164              
165             # allowed options with occurence greater one
166 0           my %allowedDuplicates = optionsToIngoreAtDuplicatesCheckings();
167              
168 0           my %options = ();
169 0           my %descriptions = ();
170 0           my %resultsCompare = ();
171 0           my %resultsDuplicates = ();
172 0           my %resultsWrongValues = ();
173 0           my %resultsAbsolutePaths = ();
174 0           my %resultsDefaultValues= ();
175 0           my $checkResult = 1;
176              
177 0 0         if(!defined($verbose)){
178 0           $verbose = 0;
179             }
180 0 0         if(!defined($caseInsensitive)){
181 0           $caseInsensitive = 0;
182             }
183 0 0         if(!defined($description)){
184 0           $description = 0;
185             }
186              
187 0 0 0       if(!defined($dbh) && (!defined($cdbUrl) || $cdbUrl eq "")){
      0        
188 0           return 0;
189             }
190              
191 0 0 0       if(!defined($cfgUrl) || $cfgUrl eq ""){
192 0           return 0;
193             }
194              
195 0 0         if($verbose > 2){
196 0 0         if($dbh){
197 0           print "Database: ". $dbh->{Name} ."\n";
198             }
199 0           print "CDB URL: ". $cdbUrl ."\n";
200 0           print "Config URL: ". $cfgUrl ."\n";
201             }
202              
203 0 0         if(!defined($dbh)){
204 0           my ($ref1, $ref2) = loadCDB($cdbUrl, $verbose);
205 0           %options = %{$ref1};
  0            
206 0           my %errorsCDB = %{$ref2};
  0            
207 0 0         if(scalar(keys(%errorsCDB))){
208 0 0         if($verbose > 0){
209 0           testFailed("Load CDB");
210             }
211 0 0         if($verbose > 1){
212 0           print "Errors while parsing ". $cdbUrl .":\n";
213 0           my @lines = sort { $a <=> $b } keys(%errorsCDB);
  0            
214 0           my $max = length($lines[scalar(@lines) - 1]);
215 0           foreach my $line (@lines){
216 0           printColored(sprintf("%". $max ."s", $line) .": ". $errorsCDB{$line} ."\n", "red");
217             }
218             }
219 0           return 0;
220             }
221             }
222             else{
223 0           my ($ref1, $ref2, $ref3) = loadDatabase($dbh, "SELECT * FROM options", $verbose);
224 0           %options = %{$ref1};
  0            
225 0           my %errors = %{$ref2};
  0            
226 0           %descriptions = %{$ref3};
  0            
227 0 0         if(scalar(keys(%errors))){
228 0 0         if($verbose > 0){
229 0           testFailed("Query Database");
230             }
231 0 0         if($verbose > 1){
232 0           print "Errors whilequerying the database ". $dbh->{Name} .":\n";
233 0           my @lines = sort { $a <=> $b } keys(%errors);
  0            
234 0           my $max = length($lines[scalar(@lines) - 1]);
235 0           foreach my $line (@lines){
236 0           printColored(sprintf("%". $max ."s", $line) .": ". $errors{$line} ."\n", "red");
237             }
238             }
239 0           return 0;
240             }
241             }
242              
243 0 0         if($verbose > 0){
244 0           my $name = $cdbUrl;
245 0 0         if($dbh){
246 0           $name = $dbh->{Name};
247             }
248 0           print2ColsRightAligned("Load Options from ". $name, scalar(keys(%options)), "green");
249             }
250 0 0         if($verbose > 2){
251 0           foreach my $key (sort(keys(%options))){
252 0           print $key ."\n";
253 0           foreach my $param (sort(keys(%{$options{$key}}))){
  0            
254 0           print " ". $param;
255 0 0         if($options{$param}){
256 0           print " (Default)";
257             }
258 0           print "\n";
259             }
260             }
261             }
262              
263             # Load the config.pro file and check, if there are not supported options
264 0           my ($resultRef, $errorRef, $parsedLines) = loadConfig($cfgUrl);
265 0           my %cfgOptions = %{$resultRef};
  0            
266 0           my %errors = %{$errorRef};
  0            
267 0 0         if(scalar(keys(%errors)) < 1){
268 0 0         if($verbose > 0){
269 0           testPassed("Load Config (Lines: ". $parsedLines .", Options: ". scalar(keys(%cfgOptions)) .")");
270             }
271             }
272             else{
273 0 0         if($verbose > 0){
274 0           testFailed("Load Config");
275             }
276 0 0         if($verbose > 1){
277 0           my @lines = sort { $a <=> $b } keys(%errors);
  0            
278 0           my $length = length($lines[scalar(@lines) - 1]);
279 0           foreach my $line (@lines){
280 0           print sprintf("%". $length ."s", $line) .": ". $errors{$line} ."\n";
281             }
282             }
283 0           return 0;
284             }
285              
286 0           foreach my $opt (keys(%cfgOptions)){
287             # check of existence
288 0 0         if(!exists($options{$opt})){
289 0           my @lines = keys(%{$cfgOptions{$opt}});
  0            
290 0           $resultsCompare{$lines[0]} = new CAD::Firemen::Option::Check(
291             "name" => $opt,
292             "errorString" => "The option ". $opt ." is not listed in given cdb"
293 0           ); "The option ". $opt ." is not listed in given cdb";
294             }
295             else{
296 0           foreach my $line (keys(%{$cfgOptions{$opt}})){
  0            
297             # checks whether the given value is supported
298 0 0         if(scalar(keys(%{$options{$opt}})) > 0){
  0            
299 0           my $found = 0;
300 0           my $case = 0;
301 0           my @cdbKeys = keys(%{$options{$opt}});
  0            
302 0           my $cdbKey = $cdbKeys[0];
303 0           foreach my $value (keys(%{$options{$opt}->{$cdbKey}})){
  0            
304             # handle special case ( -FS )
305             # I think ( -Fs ) means something like Free String
306             # Therefore all values which have the possible Value ( -Fs )
307             # are set to found, if they are not empty
308 0 0         if($value eq "( -Fs )"){
309 0 0         if($cfgOptions{$opt}->{$line} ne ""){
310 0           $found = 1;
311 0           last;
312             }
313             }
314             # it's equal to default value
315 0 0 0       if((uc($value) eq uc($cfgOptions{$opt}->{$line})) && $options{$opt}->{$cdbKey}->{$value}){
316 0           $resultsDefaultValues{$line} = new CAD::Firemen::Option::Check(
317             "name" => $opt,
318             "errorString" => "The option ". $opt ." is equal to default value (". $value .")"
319             );
320             }
321 0 0         if($value eq $cfgOptions{$opt}->{$line}){
322 0           $found = 1;
323 0           last;
324             }
325 0 0         if(uc($value) eq uc($cfgOptions{$opt}->{$line})){
326 0           $case = 1;
327 0           last;
328             }
329             }
330 0 0         if(!$found){
331             $resultsWrongValues{$line} = new CAD::Firemen::Option::Check(
332             "name" => $opt,
333 0           "errorString" => "The option ". $opt ." has not supported value ". $cfgOptions{$opt}->{$line} ." (Possible: ". join("|", keys(%{$options{$opt}->{$cdbKey}})) .")",
  0            
334             "case" => $case
335             );
336             }
337             }
338             # check that only relative paths are used,
339             # if this option is not listed in %allowedRelativePaths
340 0 0 0       if(!exists($allowedRelativePaths{$opt}) && File::Spec->file_name_is_absolute($cfgOptions{$opt}->{$line})){
341             $resultsAbsolutePaths{$line} = new CAD::Firemen::Option::Check(
342             "name" => $opt,
343 0           "errorString" => "The Option ". $opt ." contains an absolute path: ". $cfgOptions{$opt}->{$line}
344             );
345             }
346             }
347             }
348              
349             # check for duplicates
350 0 0 0       if(!exists($allowedDuplicates{$opt}) && (scalar(keys(%{$cfgOptions{$opt}})) > 1)){
  0            
351             $resultsDuplicates{$opt} = new CAD::Firemen::Option::Check(
352             "name" => $opt,
353 0           "errorString" => "The Option ". $opt ." is set at lines ". join(", ", keys(%{$cfgOptions{$opt}}))
  0            
354             );
355             }
356             }
357              
358             # print the result of the compare check (if option exists)
359 0           my @keys = sort { $a <=> $b } keys(%resultsCompare);
  0            
360 0 0         if(scalar(@keys) < 1){
361 0 0         if($verbose > 0){
362 0           testPassed("COMPARE");
363             }
364             }
365             else{
366 0           $checkResult = 0;
367 0 0         if($verbose > 0){
368 0           testFailed("COMPARE");
369             }
370 0 0         if($verbose > 1){
371 0           my $length = length($keys[scalar(@keys) - 1]);
372 0           foreach my $key (@keys){
373 0           print sprintf("%". $length ."s", $key) .": ". $resultsCompare{$key}->errorString() ."\n";
374 0 0 0       if($description && exists($descriptions{$resultsCompare{$key}->option()})){
375 0           printBlock($descriptions{$resultsCompare{$key}->option()}, $length + 4);
376             }
377             }
378             }
379             }
380              
381             # print the result of the value check
382             # handle ignored cases
383 0           @keys = sort { $a <=> $b } keys(%resultsWrongValues);
  0            
384 0           my $ignored = 0;
385 0           foreach my $key (@keys){
386 0 0         if($resultsWrongValues{$key}->case()){
387 0           $ignored++;
388             }
389             }
390 0 0         if(!$caseInsensitive){
391 0           $ignored = 0;
392             }
393 0 0 0       if((scalar(@keys) < 1) || ($caseInsensitive && (scalar(@keys) == $ignored))){
      0        
394 0 0         if($verbose > 0){
395 0           testPassed("VALUES (Ignored: ". $ignored .")");
396             }
397             }
398             else{
399 0           $checkResult = 0;
400 0 0         if($verbose > 0){
401 0           testFailed("VALUES (Ignored: ". $ignored .")");
402             }
403 0 0         if($verbose > 1){
404 0           my $length = length($keys[scalar(@keys) - 1]);
405 0           foreach my $key (@keys){
406 0           my $color = "reset";
407 0 0         if($resultsWrongValues{$key}->case()){
408 0           $color = "cyan";
409             }
410 0 0 0       if(!$resultsWrongValues{$key}->case() || !$caseInsensitive){
411 0           printColored(sprintf("%". $length ."s", $key) .": ". $resultsWrongValues{$key}->errorString() ."\n", $color);
412 0 0 0       if($description && exists($descriptions{$resultsWrongValues{$key}->option()})){
413 0           printBlock($descriptions{$resultsWrongValues{$key}->option()}, $length + 4);
414             }
415             }
416             }
417             }
418             }
419              
420             # print the result of the default values here (they are not treated as errors)
421 0           @keys = sort { $a <=> $b } keys(%resultsDefaultValues);
  0            
422 0 0         if(scalar(@keys) < 1){
423 0 0         if($verbose > 0){
424 0           testPassed("Default values");
425             }
426             }
427             else{
428 0 0         if($verbose > 0){
429 0           print2ColsRightAligned("Default values", scalar(@keys), "yellow");
430             }
431 0 0         if($verbose > 1){
432 0           my $length = length($keys[scalar(@keys) - 1]);
433 0           foreach my $key (@keys){
434 0           print sprintf("%". $length ."s", $key) .": ". $resultsDefaultValues{$key}->errorString() ."\n";
435 0 0 0       if($description && exists($descriptions{$resultsDefaultValues{$key}->option()})){
436 0           printBlock($descriptions{$resultsDefaultValues{$key}->option()}, $length + 4);
437             }
438             }
439             }
440             }
441              
442             # print the result of the duplicate check
443 0           @keys = sort(keys(%resultsDuplicates));
444 0 0         if(scalar(@keys) < 1){
445 0 0         if($verbose > 0){
446 0           testPassed("DUPLICATES");
447             }
448             }
449             else{
450 0           $checkResult = 0;
451 0 0         if($verbose > 0){
452 0           testFailed("DUPLICATES");
453             }
454 0 0         if($verbose > 1){
455 0           my $length = maxLength(@keys);
456 0           foreach my $key (@keys){
457 0           printColored(sprintf("%". $length ."s", $key) .": ". $resultsDuplicates{$key}->errorString() ."\n", "red");
458 0 0 0       if($description && exists($descriptions{$resultsDuplicates{$key}->option()})){
459 0           printBlock($descriptions{$resultsDuplicates{$key}->option()}, $length + 4);
460             }
461             }
462             }
463             }
464              
465             # print the result of the no absolute path check
466 0           @keys = sort { $a <=> $b } keys(%resultsAbsolutePaths);
  0            
467 0 0         if(scalar(@keys) < 1){
468 0 0         if($verbose > 0){
469 0           testPassed("NO ABSOLUTE PATHS");
470             }
471             }
472             else{
473 0           $checkResult = 0;
474 0 0         if($verbose > 0){
475 0           testFailed("NO ABSOLUTE PATHS");
476             }
477 0 0         if($verbose > 1){
478 0           my $length = length($keys[scalar(@keys) - 1]);
479 0           foreach my $key (@keys){
480 0           printColored(sprintf("%". $length ."s", $key) .": ". $resultsAbsolutePaths{$key}->errorString() ."\n", "red");
481 0 0 0       if($description && exists($descriptions{$resultsAbsolutePaths{$key}->option()})){
482 0           printBlock($descriptions{$resultsAbsolutePaths{$key}->option()}, $length + 4);
483             }
484             }
485             }
486             }
487              
488 0           return $checkResult;
489             }
490              
491             sub checkTreeConfig {
492 0     0 1   my $cfgUrl = shift;
493 0           my $verbose = shift;
494              
495 0           my $CFG;
496 0           my %resultsEmptyLines = ();
497              
498 0 0         if(!defined($verbose)){
499 0           $verbose = 0;
500             }
501              
502 0 0 0       if(!defined($cfgUrl) || $cfgUrl eq ""){
503 0 0         if($verbose > 0){
504 0           print "No URL given\n";
505             }
506 0           return 0;
507             }
508              
509             # Get all possible options listed within the cdb file
510 0 0         if(!open($CFG, "<", $cfgUrl)){
511 0 0         if($verbose > 0){
512 0           print "Could not open file! (". $cfgUrl .")\n";
513             }
514 0           return 0;
515             }
516              
517 0           my $i = 0;
518 0           while(<$CFG>){
519 0           my $line = strip($_);
520 0           $i++;
521 0 0         if($line eq ""){
522 0           $resultsEmptyLines{$i} = "Empty line";
523             }
524             }
525              
526 0           close($CFG);
527              
528 0           my $result = 1;
529             # print the result of the no blank lines
530 0           my @keys = sort { $a <=> $b } keys(%resultsEmptyLines);
  0            
531 0 0         if(scalar(@keys) < 1){
532 0 0         if($verbose > 0){
533 0           testPassed("NO EMPTY LINES");
534             }
535             }
536             else{
537 0           $result = 0;
538 0 0         if($verbose > 0){
539 0           testFailed("NO EMPTY LINES");
540             }
541 0 0         if($verbose > 1){
542 0           my $length = length($keys[scalar(@keys) - 1]);
543 0           foreach my $key (@keys){
544 0           printColored(sprintf("%". $length ."s", $key) .": ". $resultsEmptyLines{$key} ."\n", "red");
545             }
546             }
547             }
548              
549 0           return $result;
550             }
551              
552             1;
553              
554             __END__