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   132 use strict;
  19         40  
  19         574  
4 19     19   93 use warnings;
  19         34  
  19         585  
5              
6 19     19   99 use App::TestOnTap::Util qw(slashify ensureArray);
  19         34  
  19         1109  
7 19     19   7825 use App::TestOnTap::OrderStrategy;
  19         53  
  19         643  
8 19     19   8088 use App::TestOnTap::ExecMap;
  19         72  
  19         749  
9 19     19   8759 use App::TestOnTap::ParallelGroupManager;
  19         66  
  19         646  
10              
11 19     19   10538 use Config::Std;
  19         323759  
  19         122  
12 19     19   1222 use File::Spec;
  19         42  
  19         560  
13 19     19   115 use Grep::Query;
  19         44  
  19         754  
14 19     19   10016 use UUID::Tiny qw(:std);
  19         244570  
  19         30726  
15              
16             # CTOR
17             #
18             sub new
19             {
20 28     28 0 130 my $class = shift;
21 28         82 my $suiteRoot = shift;
22 28         72 my $userCfgFile = shift;
23 28         62 my $ignoreDeps = shift;
24              
25 28   66     319 my $configFile = slashify(File::Spec->rel2abs($userCfgFile || "$suiteRoot/" . getName()));
26 28 100       674 die("Missing configuration file '$configFile'\n") unless -f $configFile;
27              
28 27         186 my $self = bless({}, $class);
29 27         206 $self->__readCfgFile($configFile, $ignoreDeps);
30              
31 27         2402 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         82 my $configFile = shift;
41 27         85 my $ignoreDeps = shift;
42            
43 27         289 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         27926 my $dummy = *Config::Std::Hash::DEMOLISH;
53 27         115 $dummy = *Config::Std::Hash::DEMOLISH;
54            
55             # pick the necessities from the blank section
56             #
57 27   100     311 my $blankSection = $cfg->{''} || {};
58              
59             # a valid uuid is required
60             #
61 27   100     177 my $id = $blankSection->{id} || '';
62 27 100       103 if (!$id)
63             {
64 12         55 $id = create_uuid_as_string();
65 12         2259 warn("WARNING: No id found, using generated '$id'!\n");
66 12         96 $blankSection->{id} = $id;
67             }
68 27 50       331 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         151 $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         80 my $skip = $blankSection->{skip};
76 27 100       104 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         108487 $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         82 my $parallelizable = $blankSection->{parallelizable};
88 27 100       95 if (defined($parallelizable))
89             {
90 2 50       6 $parallelizable = join("\n", @$parallelizable) if ref($parallelizable) eq 'ARRAY';
91 2         47 $parallelizable = Grep::Query->new($parallelizable);
92             }
93 27         59082 $self->{parallelizable} = $parallelizable;
94              
95             # read the optional order strategy
96             #
97 27 100       151 $self->{orderstrategy} = App::TestOnTap::OrderStrategy->new($blankSection->{order}) if $blankSection->{order};
98            
99             # read the preprocess (optional) command
100             #
101 27         669 $self->{preprocesscmd} = ensureArray($blankSection->{preprocess});
102            
103             # read the postprocess (optional) command
104             #
105 27         385 $self->{postprocesscmd} = ensureArray($blankSection->{postprocess});
106              
107             # set up optional ParallelGroup's
108             #
109 27         487 $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         353 $self->{execmap} = App::TestOnTap::ExecMap->new($cfg);
114              
115 27         85 my %depRules;
116 27 100       172 if (!$ignoreDeps)
117             {
118             # find all dependency sections
119             #
120 26         132 my $depRx = qr(^\s*DEPENDENCY\s+(.+?)\s*$);
121 26         340 foreach my $depRuleSectionName (grep(/$depRx/, keys(%$cfg)))
122             {
123 5         32 $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         15 foreach my $key (qw( match dependson ))
130             {
131 10         16400 my $value = $cfg->{$depRuleSectionName}->{$key};
132 10 50       35 die("Missing key '$key' in dependency rule section '$depRuleName'\n") unless defined($value);
133 10 50       30 $value = join("\n", @$value) if ref($value) eq 'ARRAY';
134 10         33 $depRules{$depRuleName}->{$key} = Grep::Query->new($value);
135             }
136            
137             # check for unknown keys...
138             #
139 5         16308 my %validSectionKeys = map { $_ => 1 } qw(match dependson);
  10         68  
140 5         14 foreach my $key (keys(%{$cfg->{$depRuleSectionName}}))
  5         29  
141             {
142 11 100       107 warn("WARNING: Unknown key '$key' in section '[$depRuleSectionName]'\n") unless exists($validSectionKeys{$key});
143             }
144             }
145             }
146 27         198 $self->{deprules} = \%depRules;
147            
148             # finally check the config for unknown sections/keys...
149             #
150 27         275 my @validSections = (qr/^$/, qr/^DEPENDENCY\s/, qr/^EXECMAP\s+[^\s]+\s*$/, qr/^PARALLELGROUP\s+[^\s]+\s*$/);
151 27         155 foreach my $section (sort(keys(%$cfg)))
152             {
153 37         98 my $knownSection = 0;
154 37         106 foreach my $secToMatch (@validSections)
155             {
156 51 100       337 if ($section =~ /$secToMatch/)
157             {
158 36         87 $knownSection = 1;
159 36         87 last;
160             }
161             }
162 37 100       178 warn("WARNING: Unknown section: '[$section]'\n") unless $knownSection;
163             }
164              
165 27         112 my %validBlankSectionKeys = map { $_ => 1 } qw(id skip preprocess postprocess parallelizable order execmap);
  189         627  
166 27         176 foreach my $key (sort(keys(%$blankSection)))
167             {
168 38 100       171 warn("WARNING: Unknown key '$key' in default section\n") unless exists($validBlankSectionKeys{$key});
169             }
170            
171 27         538 $self->{rawcfg} = { %$cfg };
172             }
173              
174             sub getId
175             {
176 25     25 0 110 my $self = shift;
177            
178 25         338 return $self->{id};
179             }
180              
181             sub skip
182             {
183 58     58 0 135 my $self = shift;
184 58         117 my $test = shift;
185            
186             return
187             $self->{skip}
188 58 100       370 ? $self->{skip}->qgrep($test)
189             : 0;
190             }
191              
192             sub getName
193             {
194             # works as both class/instance/sub...
195             #
196 110     110 0 833 return 'config.testontap';
197             }
198              
199             sub getOrderStrategy
200             {
201 43     43 0 100 my $self = shift;
202            
203 43         427 return $self->{orderstrategy};
204             }
205              
206             sub getPreprocessCmd
207             {
208 27     27 0 85 my $self = shift;
209            
210 27         1655 return $self->{preprocesscmd};
211             }
212              
213             sub getPostprocessCmd
214             {
215 26     26 0 99 my $self = shift;
216            
217 26         459 return $self->{postprocesscmd};
218             }
219              
220             sub hasParallelizableRule
221             {
222 2     2 0 8 my $self = shift;
223            
224 2 100       18 return $self->{parallelizable} ? 1 : 0
225             }
226              
227             sub parallelizable
228             {
229 83     83 0 183 my $self = shift;
230 83         175 my $test = shift;
231            
232             return
233             $self->{parallelizable}
234 83 100       389 ? $self->{parallelizable}->qgrep($test)
235             : 0;
236             }
237              
238             sub hasExecMapping
239             {
240 56     56 0 123 my $self = shift;
241 56         148 my $testName = shift;
242              
243 56         299 return $self->{execmap}->hasMapping($testName);
244             }
245              
246             sub getExecMapping
247             {
248 48     48 0 129 my $self = shift;
249 48         166 my $testName = shift;
250              
251 48         640 return $self->{execmap}->getMapping($testName);
252             }
253              
254             sub getRawCfg
255             {
256 25     25 0 198 my $self = shift;
257              
258 25         558 return $self->{rawcfg};
259             }
260              
261             sub getDependencyRuleNames
262             {
263 27     27 0 86 my $self = shift;
264            
265 27         62 return keys(%{$self->{deprules}});
  27         155  
266             }
267              
268             sub getMatchesAndDependenciesForRule
269             {
270 5     5 0 11 my $self = shift;
271 5         12 my $depRuleName = shift;
272 5         10 my $tests = shift;
273            
274 5         25 my @matches = $self->{deprules}->{$depRuleName}->{match}->qgrep(@$tests);
275 5         747 my @dependencies = $self->{deprules}->{$depRuleName}->{dependson}->qgrep(@$tests);
276            
277 5         724 return (\@matches, \@dependencies);
278             }
279              
280             sub getParallelGroupManager
281             {
282 55     55 0 168 my $self = shift;
283 55         132 my $testName = shift;
284              
285 55         205 return $self->{parallelgroupmanager};
286             }
287              
288             1;