File Coverage

blib/lib/App/TestOnTap/WorkDirManager.pm
Criterion Covered Total %
statement 151 154 98.0
branch 21 36 58.3
condition n/a
subroutine 34 34 100.0
pod 0 17 0.0
total 206 241 85.4


line stmt bran cond sub pod time code
1             package App::TestOnTap::WorkDirManager;
2              
3 19     19   143 use strict;
  19         38  
  19         591  
4 19     19   100 use warnings;
  19         45  
  19         570  
5              
6 19     19   110 use App::TestOnTap::Util qw(slashify stringifyTime $IS_WINDOWS);
  19         42  
  19         1873  
7              
8 19     19   12168 use Archive::Zip qw(:ERROR_CODES);
  19         1514559  
  19         2064  
9 19     19   177 use File::Path;
  19         44  
  19         883  
10 19     19   141 use File::Basename;
  19         47  
  19         957  
11 19     19   121 use File::Spec;
  19         47  
  19         565  
12 19     19   9816 use File::Copy::Recursive qw(dircopy);
  19         77110  
  19         1373  
13 19     19   156 use File::Temp qw(tempdir);
  19         48  
  19         1246  
14 19     19   10017 use File::Slurp qw(write_file);
  19         73120  
  19         1113  
15 19     19   11724 use JSON;
  19         162521  
  19         114  
16 19     19   10886 use Net::Domain qw(hostfqdn);
  19         158415  
  19         1322  
17 19     19   163 use POSIX qw(uname);
  19         50  
  19         112  
18              
19             # CTOR
20             #
21             sub new
22             {
23 27     27 0 97 my $class = shift;
24 27         67 my $args = shift;
25 27         64 my $workdir = shift;
26 27         73 my $suiteRoot = shift;
27            
28 27 50       99 if ($workdir)
29             {
30             # if user specifies a workdir this implies that it should be kept
31             # just make sure there is no such directory beforehand, and create it here
32             # (similar to below; tempdir() will also create one)
33             #
34 0         0 $workdir = slashify(File::Spec->rel2abs($workdir));
35 0 0       0 die("The workdir '$workdir' already exists\n") if -e $workdir;
36 0 0       0 mkpath($workdir) or die("Failed to create workdir '$workdir': $!\n");
37             }
38             else
39             {
40             # create a temp dir; use automatic cleanup
41             #
42 27         271 $workdir = slashify(tempdir("testontap-workdir-XXXX", TMPDIR => 1, CLEANUP => 1));
43             }
44              
45 27         228 my $self = bless
46             (
47             {
48             args => $args,
49             suiteroot => $suiteRoot,
50             root => $workdir,
51             tmp => slashify("$workdir/tmp"),
52             save => slashify("$workdir/save"),
53             save_suite => slashify("$workdir/save/suite"),
54             save_testontap => slashify("$workdir/save/testontap"),
55             tap => slashify("$workdir/save/testontap/tap"),
56             result => slashify("$workdir/save/testontap/result"),
57             json => JSON->new()->utf8()->pretty()->canonical(),
58             orderstrategy => undef,
59             dispensedorder => [],
60             foundtests => [],
61             commandlines => {},
62             fullgraph => undef,
63             prunedgraph => undef,
64             preprocess => undef,
65             },
66             $class
67             );
68              
69 27         244 foreach my $p (qw(tmp save save_suite save_testontap tap result))
70             {
71 162 50       19745 mkpath($self->{$p}) || die("Failed to mkdir '$self->{$p}': $!\n");
72             }
73              
74 27         245 return $self;
75             }
76              
77             sub beginTestRun
78             {
79 25     25 0 63 my $self = shift;
80            
81 25         99 $self->{begin} = time();
82            
83 25         980 $self->__save("$self->{save_testontap}/env", { %ENV });
84             }
85              
86             sub endTestRun
87             {
88 25     25 0 135 my $self = shift;
89 25         175 my $args = shift;
90 25         105 my $aggregator = shift;
91            
92 25         278 $self->{end} = time();
93 25         363 $self->{runid} = $args->getId();
94              
95 25 100       199 my $summary =
96             {
97             all_passed => $aggregator->all_passed() ? 1 : 0,
98             status => $aggregator->get_status(),
99             failed => [ $aggregator->failed() ],
100             parse_errors => [ $aggregator->parse_errors() ],
101             passed => [ $aggregator->passed() ],
102             planned => [ $aggregator->planned() ],
103             skipped => [ $aggregator->skipped() ],
104             todo => [ $aggregator->todo() ],
105             todo_passed => [ $aggregator->todo_passed() ],
106             };
107 25         3224 $self->__save("$self->{save_testontap}/summary", $summary);
108              
109             my $testinfo =
110             {
111             config => $self->{args}->getConfig()->getRawCfg(),
112             dispensedorder => $self->{dispensedorder},
113             found => $self->{foundtests},
114             commandlines => $self->{commandlines},
115             fullgraph => $self->{fullgraph},
116             prunedgraph => $self->{prunedgraph},
117 25         5761 };
118 25         274 $self->__save("$self->{save_testontap}/testinfo", $testinfo);
119              
120 25         5525 my $elapsed = $aggregator->elapsed();
121             my $meta =
122             {
123             format => { major => 1, minor => 0 }, # Change when format of result tree is changed in any way.
124             runid => $args->getId(),
125             suiteid => $args->getConfig()->getId(),
126             suitename => basename($args->getSuiteRoot()),
127             begin => stringifyTime($self->{begin}),
128             end => stringifyTime($self->{end}),
129             elapsed =>
130             {
131             str => $aggregator->elapsed_timestr(),
132             real => $elapsed->real(),
133             cpu => $elapsed->cpu_a(),
134             },
135             user => $IS_WINDOWS ? getlogin() : scalar(getpwuid($<)),
136             host => hostfqdn(),
137             jobs => $args->getJobs(),
138             dollar0 => slashify(File::Spec->rel2abs($0)),
139             argv => $args->getFullArgv(),
140             defines => $args->getDefines(),
141             platform => $^O,
142             uname => [ uname() ],
143 25 50       1244 order => $self->{orderstrategy} ? $self->{orderstrategy}->getStrategyName() : undef,
    100          
144             };
145 25         277 $self->__save("$self->{save_testontap}/meta", $meta);
146              
147 25 100       5904 $self->__saveText("$self->{save_testontap}/preprocess", $self->{preprocess}) if $self->{preprocess};
148             }
149              
150             # retain the tap handles we issue so we can 'manually' close them
151             # this can be necessary during a bailout on windows, where the
152             # spool handle closing is not called, and the automatic cleanup
153             # of temp stuff spouts errors to delete a file due to it having an
154             # open handle to it.
155             #
156             # note that putting the handle as a key stringifies it, so we
157             # must use the actual value when closing, not the string...
158             #
159             my %tapHandles;
160             END
161             {
162 19     19   310978 close($tapHandles{$_}) foreach (keys(%tapHandles));
163             }
164              
165             sub openTAPHandle
166             {
167 47     47 0 116 my $self = shift;
168 47         231 my $testPath = slashify(shift, '/');
169            
170 47         243 my $sr = slashify($self->{suiteroot}, '/');
171 47         935 $testPath =~ s#^\Q$sr\E/(.*)#$1#;
172 47         360 my $tapPath = slashify("$self->{tap}/$testPath.tap");
173 47         3630 mkpath(dirname($tapPath));
174 47 50       3532 open(my $h, '>', $tapPath) or die("Failed to open '$tapPath': $!");
175              
176             # save the handle in the list, forcibly stringify it as key and
177             # save the actual value
178             #
179 47         500 $tapHandles{"$h"} = $h;
180            
181 47         458 return $h;
182             }
183              
184             sub closeTAPHandle
185             {
186 47     47 0 403 my $self = shift;
187 47         232 my $parser = shift;
188            
189 47         400 my $spool_handle = $parser->delete_spool;
190 47 50       742 if ($spool_handle)
191             {
192 47         2144 close($spool_handle);
193            
194             # don't forget to remove the key/value in the list
195             # using the stringified version of the handle!
196             #
197 47         1129 delete($tapHandles{"$spool_handle"});
198             }
199            
200 47         1063 return;
201             }
202              
203             sub getResultCollector
204             {
205 27     27 0 77 my $self = shift;
206            
207             return
208             sub
209             {
210 47     47   4449 my $pathAndNamePair = shift;
211 47         378 my $parser = shift;
212              
213 47 100       380 my %results =
    50          
    50          
214             (
215             # individual test results
216             #
217             passed => [ $parser->passed() ],
218             actual_passed => [ $parser->actual_passed() ],
219             failed => [ $parser->failed() ],
220             actual_failed => [ $parser->actual_failed() ],
221             todo => [ $parser->todo() ],
222             todo_passed => [ $parser->failed() ],
223             skipped => [ $parser->skipped() ],
224            
225             # total test results
226             #
227             has_problems => $parser->has_problems() ? 1 : 0,
228             plan => $parser->plan(),
229             is_good_plan => $parser->is_good_plan() ? 1 : 0,
230             tests_planned => $parser->tests_planned(),
231             tests_run => $parser->tests_run(),
232             skip_all => ($parser->skip_all() ? $parser->skip_all() : 0),
233             start_time => stringifyTime($parser->start_time()),
234             end_time => stringifyTime($parser->end_time()),
235             version => $parser->version(),
236             'exit' => $parser->exit(),
237             parse_errors => [ $parser->parse_errors() ],
238             );
239            
240 47         2758 $self->__save("$self->{result}/$pathAndNamePair->[1]", \%results);
241 27         398 };
242             }
243              
244             sub saveResult
245             {
246 2     2 0 6 my $self = shift;
247 2         4 my $resultDir = shift;
248 2         4 my $asArchive = shift;
249              
250 2         82 my $pfx = basename($self->{suiteroot});
251 2         13 my $runid = $self->{runid};
252 2         8 my $ts = stringifyTime($self->{begin});
253 2         14 my $name = "$pfx.$ts.$runid";
254 2         10 my $from = slashify($self->{save});
255              
256 2         6 my $to;
257 2 100       8 if ($asArchive)
258             {
259 1         5 $to = slashify("$resultDir/$name.zip");
260 1         26 my $zip = Archive::Zip->new();
261 1         121 $zip->addTree($from, $name);
262 1         11558 my $err = $zip->writeToFileNamed($to);
263 1 50       15826 die("Failed to write archive '$to': $!\n") if $err != AZ_OK;
264             }
265             else
266             {
267 1         7 $to = slashify("$resultDir/$name");
268             {
269 1         3 local $File::Copy::Recursive::KeepMode = 0;
  1         12  
270 1 50       19 die("Failed to copy result '$from' => '$to': $!\n") unless dircopy($from, $to);
271             }
272             }
273            
274 2         5658 return $to;
275             }
276              
277             sub getTmp
278             {
279 26     26 0 79 my $self = shift;
280            
281 26         194 return $self->{tmp};
282             }
283              
284             sub getSaveSuite
285             {
286 26     26 0 67 my $self = shift;
287            
288 26         183 return $self->{save_suite};
289             }
290              
291             sub recordOrderStrategy
292             {
293 55     55 0 134 my $self = shift;
294 55         111 my $orderstrategy = shift;
295            
296 55         223 $self->{orderstrategy} = $orderstrategy;
297             }
298              
299             sub recordDispensedOrder
300             {
301 55     55 0 131 my $self = shift;
302 55         172 my @dispensed = @_;
303            
304 55         150 push(@{$self->{dispensedorder}}, @dispensed);
  55         271  
305             }
306              
307             sub recordFoundTests
308             {
309 27     27 0 92 my $self = shift;
310 27         127 my @foundTests = @_;
311            
312 27         75 push(@{$self->{foundtests}}, @foundTests);
  27         141  
313             }
314              
315             sub recordFullGraph
316             {
317 27     27 0 81 my $self = shift;
318 27         126 my %fullgraph = @_;
319            
320 27         141 $self->{fullgraph} = \%fullgraph;
321             }
322              
323             sub recordPrunedGraph
324             {
325 2     2 0 15 my $self = shift;
326 2         8 my %prunedgraph = @_;
327            
328 2         25 $self->{prunedgraph} = \%prunedgraph;
329             }
330              
331             sub recordPreprocess
332             {
333 1     1 0 10 my $self = shift;
334 1         10 my $preproc = shift;
335            
336 1         27 $self->{preprocess} = $preproc;
337             }
338              
339             sub recordPostprocess
340             {
341 1     1 0 12 my $self = shift;
342 1         4 my $postproc = shift;
343            
344 1         15 $self->__saveText("$self->{save_testontap}/postprocess", $postproc);
345             }
346              
347             sub recordCommandLine
348             {
349 48     48 0 115 my $self = shift;
350 48         190 my $test = shift;
351 48         139 my $cmdline = shift;
352            
353 48         287 $self->{commandlines}->{$test} = $cmdline;
354             }
355              
356             sub __save
357             {
358 147     147   557 my $self = shift;
359 147         547 my $name = shift;
360 147         323 my $data = shift;
361            
362 147         1200 my $file = slashify("$name.json");
363 147         18502 mkpath(dirname($file));
364 147 50       6068 write_file($file, $self->{json}->encode($data)) || die("Failed to write '$file': $!\n");
365             }
366              
367             sub __saveText
368             {
369 2     2   11 my $self = shift;
370 2         10 my $name = shift;
371 2         15 my $data = shift;
372            
373 2         35 my $file = slashify("$name.txt");
374 2         338 mkpath(dirname($file));
375 2 50       43 write_file($file, @$data) || die("Failed to write '$file': $!\n");
376             }
377              
378             1;