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