File Coverage

blib/lib/App/TestOnTap/Util.pm
Criterion Covered Total %
statement 54 81 66.6
branch 10 34 29.4
condition 4 5 80.0
subroutine 12 14 85.7
pod 0 6 0.0
total 80 140 57.1


line stmt bran cond sub pod time code
1             package App::TestOnTap::Util;
2              
3 19     19   114 use strict;
  19         36  
  19         508  
4 19     19   83 use warnings;
  19         34  
  19         884  
5              
6             our $VERSION = '1.001';
7             my $version = $VERSION;
8             $VERSION = eval $VERSION;
9              
10 19     19   97 use Cwd;
  19         39  
  19         1220  
11 19     19   8792 use POSIX qw(strftime);
  19         141898  
  19         103  
12 19     19   27114 use File::Basename;
  19         39  
  19         1107  
13 19     19   110 use File::Spec;
  19         33  
  19         1759  
14              
15             our $IS_WINDOWS = $^O eq 'MSWin32';
16             our $IS_PACKED = $ENV{PAR_0} ? 1 : 0;
17             our $FILE_SEP = $IS_WINDOWS ? '\\' : '/';
18             our $SHELL_ARG_DELIM = $IS_WINDOWS ? '"' : "'";
19              
20 19     19   120 use Exporter qw(import);
  19         42  
  19         20116  
21             our @EXPORT_OK =
22             qw
23             (
24             slashify
25             trim
26             stringifyTime
27             ensureArray
28             expandAts
29             runprocess
30             $IS_WINDOWS
31             $IS_PACKED
32             $FILE_SEP
33             $SHELL_ARG_DELIM
34             );
35              
36             # pass in a path and ensure it contains the native form of slash vs backslash
37             # (or force either one)
38             #
39             sub slashify
40             {
41 878     878 0 18065 my $s = shift;
42 878   66     4088 my $fsep = shift || $FILE_SEP;
43              
44 878         14565 my $dblStart = $s =~ s#^[\\/]{2}##;
45 878         5850 $s =~ s#[/\\]+#$fsep#g;
46              
47 878 50       6040 return $dblStart ? "$fsep$fsep$s" : $s;
48             }
49              
50             # trim off any ws at front/end of a string
51             #
52             sub trim
53             {
54 0     0 0 0 my $s = shift;
55              
56 0 0       0 $s =~ s/^\s+|\s+$//g if defined($s);
57              
58 0         0 return $s;
59             }
60              
61             # turn an epoch time into a compact ISO8601 UTC string
62             #
63             sub stringifyTime
64             {
65 146     146 0 5585 my $tm = shift;
66            
67             # deal with possible hires timings and
68             # convert the raw timestamps to strings
69             #
70 146         394 my $subsecs = '';
71 146 100       1851 if ($tm =~ m#\.#)
72             {
73 94         1530 $tm =~ s#^([^\.]+)\.(.*)#$1#;
74 94         358 $subsecs = ".$2";
75             }
76            
77 146         16281 return strftime("%Y%m%dT%H%M%S${subsecs}Z", gmtime($tm));
78             }
79              
80             # ensure we end up with an array
81             # input can be:
82             # undef
83             # an array
84             # a space-separared string
85             # a newline-separated string
86             #
87             sub ensureArray
88             {
89 283   100 283 0 1221 my $data = shift || [];
90            
91             return
92 283 50       1629 (ref($data) eq 'ARRAY')
    100          
93             ? $data
94             : ($data =~ m#\n#)
95             ? [ split("\n", $data) ]
96             : [ split(' ', $data) ];
97             }
98              
99             # expand any array elements using '@xyz' as new line elements read from 'xyz'
100             # also, handle recursion where included files itself refers to further files
101             # possibly using relative paths
102             #
103             sub expandAts
104             {
105 29     29 0 83 my $dirctx = shift;
106            
107 29         92 my @a;
108 29         142 foreach my $e (@_)
109             {
110 90 50       284 if ($e =~ /^@(.+)/)
111             {
112             # if we find a filename use as-if its absolute, otherwise tack on
113             # the current dir context
114             #
115 0         0 my $fn = $1;
116 0 0       0 $fn = File::Spec->file_name_is_absolute($fn) ? $fn : "$dirctx/$fn";
117            
118             # recursively read file contents into the array
119             # using the current files directory as the new dir context
120             #
121 0         0 push(@a, expandAts(dirname($fn), __readLines($fn)))
122             }
123             else
124             {
125             # just keep the value as-is
126             #
127 90         219 push(@a, $e);
128             }
129             }
130 29         136 return @a;
131             }
132              
133             # read all lines from a file and return as an array
134             # supports line continuation, e.g. a line with '\' at the end causes
135             # appending the line after etc, in order to create a single line.
136             # - a line starting with '#' will be ignored as a comment
137             # - all lines will be trimmed from space at each end
138             # - an empty line will be ignored
139             #
140             sub __readLines
141             {
142 0     0   0 my $fn = slashify(File::Spec->rel2abs(shift()));
143            
144 0 0       0 die("No such file: '$fn'\n") unless -f $fn;
145              
146 0         0 my @lines;
147 0 0       0 open (my $fh, '<', $fn) or die("Failed to open '$fn': $!\n");
148 0         0 my $line;
149 0         0 while (defined($line = <$fh>))
150             {
151 0         0 chomp($line);
152            
153             # handle lines with line continuation
154             # until no more continuation is found
155             #
156 0 0       0 if ($line =~ s#\\\s*$##)
157             {
158             # append lines...
159             #
160 0         0 $line .= <$fh>;
161            
162             # ...and repeat, unless we hit eof
163             #
164 0 0       0 redo unless eof($fh);
165             }
166            
167             # if the resulting line is a comment line, ignore it
168             #
169 0 0       0 if ($line !~ /^\s*#/)
170             {
171             # ensure removing any trailing line continuation is removed
172             # (can happen if there is no extra line after a line continuation, just eof)
173             #
174 0         0 $line =~ s#\\\s*$##;
175            
176             # trim the ends, and add it - but only if it's not empty
177             #
178 0         0 $line = trim($line);
179            
180 0 0       0 if ($line)
181             {
182             # expand any ${envvar} in the line
183             #
184 0         0 while ($line =~ m#\$\{([^}]+)\}#)
185             {
186 0         0 my $ev = $1;
187 0 0       0 die("No environment variable '$ev' in '$line'\n") unless exists($ENV{$ev});
188 0         0 $line =~ s#\$\{\Q$ev\E\}#$ENV{$ev}#;
189             }
190            
191             # line is ready
192             #
193 0         0 push(@lines, $line);
194             }
195             }
196             }
197 0         0 close($fh);
198            
199 0         0 return @lines;
200             }
201              
202             sub runprocess
203             {
204 2     2 0 327 my $cb = shift;
205 2         13 my $wd = shift;
206 2         16 my @argv = @_;
207            
208 2         25 my $cwd = getcwd();
209 2 50       51 chdir($wd) || die("Failed to change directory to '$wd': $!\n");
210              
211 2         17 $_ = "$SHELL_ARG_DELIM$_$SHELL_ARG_DELIM" foreach (@argv);
212 2         10 my $cmdline = join(' ', @argv);
213            
214 2 50       9523 open(my $fh, '-|', "$cmdline 2>&1") || die("Failed to run '$cmdline': $!\n");
215 2         14121 while (my $line = <$fh>)
216             {
217 44         200 $cb->($line);
218             }
219 2         100 close($fh);
220 2         38 my $xit = $? >> 8;
221              
222 2 50       129 chdir($cwd) || die("Failed to change directory back to '$cwd': $!\n");
223            
224 2         122 return $xit;
225             }
226              
227             1;