File Coverage

blib/lib/App/TestOnTap/Config.pm
Criterion Covered Total %
statement 135 135 100.0
branch 31 36 86.1
condition 6 7 85.7
subroutine 26 26 100.0
pod 0 15 0.0
total 198 219 90.4


line stmt bran cond sub pod time code
1             package App::TestOnTap::Config;
2              
3 19     19   172 use strict;
  19         35  
  19         564  
4 19     19   89 use warnings;
  19         35  
  19         1063  
5              
6             our $VERSION = '1.001';
7             my $version = $VERSION;
8             $VERSION = eval $VERSION;
9              
10 19     19   133 use App::TestOnTap::Util qw(slashify ensureArray);
  19         64  
  19         1358  
11 19     19   7660 use App::TestOnTap::OrderStrategy;
  19         42  
  19         609  
12 19     19   8181 use App::TestOnTap::ExecMap;
  19         75  
  19         742  
13 19     19   8576 use App::TestOnTap::ParallelGroupManager;
  19         66  
  19         712  
14              
15 19     19   10544 use Config::Std;
  19         311830  
  19         127  
16 19     19   1254 use File::Spec;
  19         44  
  19         527  
17 19     19   98 use Grep::Query;
  19         39  
  19         788  
18 19     19   10104 use UUID::Tiny qw(:std);
  19         236188  
  19         29953  
19              
20             # CTOR
21             #
22             sub new
23             {
24 28     28 0 82 my $class = shift;
25 28         82 my $suiteRoot = shift;
26 28         106 my $userCfgFile = shift;
27 28         56 my $ignoreDeps = shift;
28              
29 28   66     260 my $configFile = slashify(File::Spec->rel2abs($userCfgFile || "$suiteRoot/" . getName()));
30 28 100       587 die("Missing configuration file '$configFile'\n") unless -f $configFile;
31              
32 27         156 my $self = bless({}, $class);
33 27         170 $self->__readCfgFile($configFile, $ignoreDeps);
34              
35 27         2618 return $self;
36             }
37              
38             # read the raw Config::Std file and fill in
39             # data fields
40             #
41             sub __readCfgFile
42             {
43 27     27   66 my $self = shift;
44 27         88 my $configFile = shift;
45 27         63 my $ignoreDeps = shift;
46            
47 27         278 read_config($configFile, my $cfg);
48            
49             # this looks weird, I know - see https://rt.cpan.org/Public/Bug/Display.html?id=56862
50             #
51             # I seem to hit the problem with "Warning: Name "Config::Std::Hash::DEMOLISH" used only once..."
52             # when running a Par::Packer binary but not when as a 'normal' script.
53             #
54             # The below incantation seem to get rid of that, at least for now. Let's see if it reappears...
55             #
56 27         25327 my $dummy = *Config::Std::Hash::DEMOLISH;
57 27         109 $dummy = *Config::Std::Hash::DEMOLISH;
58            
59             # pick the necessities from the blank section
60             #
61 27   100     251 my $blankSection = $cfg->{''} || {};
62              
63             # a valid uuid is required
64             #
65 27   100     167 my $id = $blankSection->{id} || '';
66 27 100       104 if (!$id)
67             {
68 12         48 $id = create_uuid_as_string();
69 12         2003 warn("WARNING: No id found, using generated '$id'!\n");
70 12         86 $blankSection->{id} = $id;
71             }
72 27 50       292 die("Invalid/missing suite id: '$id'") unless $id =~ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
73 27         136 $self->{id} = $id;
74            
75             # an optional filter to skip parts while scanning suite root
76             #
77             # ensure it's in text form - an array is simply joined using newlines
78             #
79 27         84 my $skip = $blankSection->{skip};
80 27 100       97 if (defined($skip))
81             {
82 2 50       8 $skip = join("\n", @$skip) if ref($skip) eq 'ARRAY';
83 2         19 $skip = Grep::Query->new($skip);
84             }
85 27         110065 $self->{skip} = $skip;
86              
87             # an optional filter to check if a test can run in parallel (with any other test)
88             #
89             # ensure it's in text form - an array is simply joined using newlines
90             #
91 27         93 my $parallelizable = $blankSection->{parallelizable};
92 27 100       107 if (defined($parallelizable))
93             {
94 2 50       7 $parallelizable = join("\n", @$parallelizable) if ref($parallelizable) eq 'ARRAY';
95 2         36 $parallelizable = Grep::Query->new($parallelizable);
96             }
97 27         59566 $self->{parallelizable} = $parallelizable;
98              
99             # read the optional order strategy
100             #
101 27 100       131 $self->{orderstrategy} = App::TestOnTap::OrderStrategy->new($blankSection->{order}) if $blankSection->{order};
102            
103             # read the preprocess (optional) command
104             #
105 27         207 $self->{preprocesscmd} = ensureArray($blankSection->{preprocess});
106            
107             # read the postprocess (optional) command
108             #
109 27         129 $self->{postprocesscmd} = ensureArray($blankSection->{postprocess});
110              
111             # set up optional ParallelGroup's
112             #
113 27         459 $self->{parallelgroupmanager} = App::TestOnTap::ParallelGroupManager->new($cfg);
114              
115             # set up the execmap, possibly as a delegate from a user defined one
116             #
117 27         365 $self->{execmap} = App::TestOnTap::ExecMap->new($cfg);
118              
119 27         73 my %depRules;
120 27 100       150 if (!$ignoreDeps)
121             {
122             # find all dependency sections
123             #
124 26         182 my $depRx = qr(^\s*DEPENDENCY\s+(.+?)\s*$);
125 26         382 foreach my $depRuleSectionName (grep(/$depRx/, keys(%$cfg)))
126             {
127 5         31 $depRuleSectionName =~ /$depRx/;
128 5         18 my $depRuleName = $1;
129            
130             # all dep sections requires match/dependson Grep::Query queries
131             # in case they're written as arrays, just join using newlines
132             #
133 5         14 foreach my $key (qw( match dependson ))
134             {
135 10         14877 my $value = $cfg->{$depRuleSectionName}->{$key};
136 10 50       33 die("Missing key '$key' in dependency rule section '$depRuleName'\n") unless defined($value);
137 10 50       26 $value = join("\n", @$value) if ref($value) eq 'ARRAY';
138 10         35 $depRules{$depRuleName}->{$key} = Grep::Query->new($value);
139             }
140            
141             # check for unknown keys...
142             #
143 5         14765 my %validSectionKeys = map { $_ => 1 } qw(match dependson);
  10         35  
144 5         12 foreach my $key (keys(%{$cfg->{$depRuleSectionName}}))
  5         23  
145             {
146 11 100       98 warn("WARNING: Unknown key '$key' in section '[$depRuleSectionName]'\n") unless exists($validSectionKeys{$key});
147             }
148             }
149             }
150 27         167 $self->{deprules} = \%depRules;
151            
152             # finally check the config for unknown sections/keys...
153             #
154 27         267 my @validSections = (qr/^$/, qr/^DEPENDENCY\s/, qr/^EXECMAP\s+[^\s]+\s*$/, qr/^PARALLELGROUP\s+[^\s]+\s*$/);
155 27         160 foreach my $section (sort(keys(%$cfg)))
156             {
157 37         77 my $knownSection = 0;
158 37         93 foreach my $secToMatch (@validSections)
159             {
160 51 100       321 if ($section =~ /$secToMatch/)
161             {
162 36         88 $knownSection = 1;
163 36         83 last;
164             }
165             }
166 37 100       148 warn("WARNING: Unknown section: '[$section]'\n") unless $knownSection;
167             }
168              
169 27         109 my %validBlankSectionKeys = map { $_ => 1 } qw(id skip preprocess postprocess parallelizable order execmap);
  189         642  
170 27         164 foreach my $key (sort(keys(%$blankSection)))
171             {
172 38 100       158 warn("WARNING: Unknown key '$key' in default section\n") unless exists($validBlankSectionKeys{$key});
173             }
174            
175 27         572 $self->{rawcfg} = { %$cfg };
176             }
177              
178             sub getId
179             {
180 25     25 0 110 my $self = shift;
181            
182 25         294 return $self->{id};
183             }
184              
185             sub skip
186             {
187 58     58 0 123 my $self = shift;
188 58         123 my $test = shift;
189            
190             return
191             $self->{skip}
192 58 100       349 ? $self->{skip}->qgrep($test)
193             : 0;
194             }
195              
196             sub getName
197             {
198             # works as both class/instance/sub...
199             #
200 110     110 0 719 return 'config.testontap';
201             }
202              
203             sub getOrderStrategy
204             {
205 43     43 0 115 my $self = shift;
206            
207 43         360 return $self->{orderstrategy};
208             }
209              
210             sub getPreprocessCmd
211             {
212 27     27 0 84 my $self = shift;
213            
214 27         1562 return $self->{preprocesscmd};
215             }
216              
217             sub getPostprocessCmd
218             {
219 26     26 0 563 my $self = shift;
220            
221 26         480 return $self->{postprocesscmd};
222             }
223              
224             sub hasParallelizableRule
225             {
226 2     2 0 8 my $self = shift;
227            
228 2 100       30 return $self->{parallelizable} ? 1 : 0
229             }
230              
231             sub parallelizable
232             {
233 83     83 0 161 my $self = shift;
234 83         165 my $test = shift;
235            
236             return
237             $self->{parallelizable}
238 83 100       359 ? $self->{parallelizable}->qgrep($test)
239             : 0;
240             }
241              
242             sub hasExecMapping
243             {
244 56     56 0 124 my $self = shift;
245 56         113 my $testName = shift;
246              
247 56         303 return $self->{execmap}->hasMapping($testName);
248             }
249              
250             sub getExecMapping
251             {
252 48     48 0 123 my $self = shift;
253 48         129 my $testName = shift;
254              
255 48         627 return $self->{execmap}->getMapping($testName);
256             }
257              
258             sub getRawCfg
259             {
260 25     25 0 103 my $self = shift;
261              
262 25         509 return $self->{rawcfg};
263             }
264              
265             sub getDependencyRuleNames
266             {
267 27     27 0 117 my $self = shift;
268            
269 27         63 return keys(%{$self->{deprules}});
  27         137  
270             }
271              
272             sub getMatchesAndDependenciesForRule
273             {
274 5     5 0 9 my $self = shift;
275 5         11 my $depRuleName = shift;
276 5         8 my $tests = shift;
277            
278 5         22 my @matches = $self->{deprules}->{$depRuleName}->{match}->qgrep(@$tests);
279 5         659 my @dependencies = $self->{deprules}->{$depRuleName}->{dependson}->qgrep(@$tests);
280            
281 5         586 return (\@matches, \@dependencies);
282             }
283              
284             sub getParallelGroupManager
285             {
286 55     55 0 127 my $self = shift;
287 55         127 my $testName = shift;
288              
289 55         249 return $self->{parallelgroupmanager};
290             }
291              
292             1;