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