File Coverage

blib/lib/Devel/Git/MultiBisect/Auxiliary.pm
Criterion Covered Total %
statement 74 81 91.3
branch 24 32 75.0
condition n/a
subroutine 11 13 84.6
pod 3 4 75.0
total 112 130 86.1


line stmt bran cond sub pod time code
1             package Devel::Git::MultiBisect::Auxiliary;
2 5     5   679 use strict;
  5         17  
  5         197  
3 5     5   29 use warnings;
  5         12  
  5         169  
4 5     5   86 use v5.14.0;
  5         16  
5             our $VERSION = '0.16';
6 5     5   36 use base qw( Exporter );
  5         17  
  5         1088  
7             our @EXPORT_OK = qw(
8             clean_outputfile
9             hexdigest_one_file
10             validate_list_sequence
11             get_current_branch
12             );
13 5     5   41 use Carp;
  5         10  
  5         385  
14 5     5   42 use Digest::MD5;
  5         21  
  5         325  
15 5     5   2961 use File::Copy;
  5         15592  
  5         435  
16 5     5   44 use List::Util qw(first);
  5         10  
  5         4362  
17              
18             =head1 NAME
19              
20             Devel::Git::MultiBisect::Auxiliary - Helper functions for Devel::Git::MultiBisect
21              
22             =head1 SYNOPSIS
23              
24             use Devel::Git::MultiBisect::Auxiliary qw(
25             clean_outputfile
26             hexdigest_one_file
27             validate_list_sequence
28             );
29              
30             =head1 DESCRIPTION
31              
32             This package exports, on demand only, subroutines used within publicly available
33             methods in Devel::Git::MultiBisect.
34              
35             =head1 SUBROUTINES
36              
37             =head2 C
38              
39             =over 4
40              
41             =item * Purpose
42              
43             When we redirect the output of a test harness program such as F to a
44             file, we typically get at the end a line matching this pattern:
45              
46             m/^Files=\d+,\sTests=\d+/
47              
48             This line also contains measurements of the time it took for a particular file
49             to be run. These timings vary from one run to the next, which makes the
50             content of otherwise identical files different, which in turn makes their
51             md5_hex digests different. So we simply rewrite the test output file to
52             remove this line.
53              
54             =item * Arguments
55              
56             $outputfile = clean_outputfile($outputfile);
57              
58             A string holding the path to a file holding TAP output.
59              
60             =item * Return Value
61              
62             A string holding the path to a file holding TAP output.
63              
64             =item * Comment
65              
66             The return value is provided for the purpose of chaining function calls; the
67             file itself is changed in place.
68              
69             =back
70              
71             =cut
72              
73             sub clean_outputfile {
74 2     2 1 4829 my $outputfile = shift;
75 2         42 my $replacement = "$outputfile.tmp";
76 2 50       78 open my $IN, '<', $outputfile
77             or croak "Could not open $outputfile for reading";
78 2 50       130 open my $OUT, '>', $replacement
79             or croak "Could not open $replacement for writing";
80 2         46 while (my $l = <$IN>) {
81 98         148 chomp $l;
82 98 100       341 say $OUT $l unless $l =~ m/^Files=\d+,\sTests=\d+/;
83             }
84 2 50       62 close $OUT or croak "Could not close after writing";
85 2 50       76 close $IN or croak "Could not close after reading";
86 2 50       11 move $replacement => $outputfile or croak "Could not replace";
87 2         365 return $outputfile;
88             }
89              
90             =head2 C
91              
92             =over 4
93              
94             =item * Purpose
95              
96             To compare multiple files for same or different content, we need a convenient,
97             short datum. We will use the C value provided by the F
98             module which is part of the Perl 5 core distribution.
99              
100             =item * Arguments
101              
102             $md5_hex = hexdigest_one_file($outputfile);
103              
104             A string holding the path to a file holding TAP output.
105              
106             =item * Return Value
107              
108             A string holding the C digest for that file.
109              
110             =item * Comment
111              
112             The file provided as argument should be run through C
113             before being passed to this function.
114              
115             =back
116              
117             =cut
118              
119             sub hexdigest_one_file {
120 10     10 1 12106 my $filename = shift;
121 10         163 my $state = Digest::MD5->new();
122 10 50       412 open my $FH, '<', $filename or croak "Unable to open $filename for reading";
123 10         408 $state->addfile($FH);
124 10 50       104 close $FH or croak "Unable to close $filename after reading";
125 10         75 my $hexdigest = $state->hexdigest;
126 10         99 return $hexdigest;
127             }
128              
129             =head2 C
130              
131             =over 4
132              
133             =item * Purpose
134              
135             Determine whether a given list consists of one or more sub-lists, each of
136             which conforms to the following properties:
137              
138             =over 4
139              
140             =item 1
141              
142             The sub-list consists of one or more elements, the first and last of which are
143             defined and identical. Elements between the first and last (if any) are
144             either identical to the first and last or are undefined.
145              
146             =item 2
147              
148             The sole defined value in any sub-list is not found in any other sub-list.
149              
150             =back
151              
152             Examples:
153              
154             =over 4
155              
156             =item * C<['alpha', 'alpha', undef, 'alpha', undef, 'beta']>
157              
158             Does not qualify, as the sub-list terminating with C starts with an C.
159              
160             =item * C<['alpha', 'alpha', undef, 'alpha', 'beta', undef]>
161              
162             Does not qualify, as the sub-list starting with C ends with an C.
163              
164             =item * C<['alpha', 'alpha', undef, 'alpha', 'beta', undef, 'beta', 'alpha', 'alpha']>
165              
166             Does not qualify, as C occurs in both the first and third sub-lists.
167              
168             =item * C<['alpha', 'alpha', undef, 'alpha', 'beta', undef, 'beta']>
169              
170             Qualifies.
171              
172             =back
173              
174             =item * Arguments
175              
176             my $vls = validate_list_sequence( [
177             'alpha', 'alpha', undef, 'alpha', 'beta', undef, 'beta'
178             ] );
179              
180             Reference to an array holding scalars.
181              
182             =item * Return Value
183              
184             Array reference consisting of either 1 or 3 elements. If the list qualifies,
185             the array holds just one element which is a Perl-true value. If the list does
186             B qualify, the array hold 3 elements as follows:
187              
188             =over 4
189              
190             =item * Element 0
191              
192             Perl-false value, indicating that the list does not qualify.
193              
194             =item * Element 1
195              
196             Index of the array element at which the first non-conforming value was observed.
197              
198             =item * Element 2
199              
200             String holding explanation for failure to qualify.
201              
202             =back
203              
204             Examples:
205              
206             =over 4
207              
208             =item 1
209              
210             Qualifying list:
211              
212             use Data::Dumper; $Data::Dumper::Indent = 0;
213             my $vls;
214              
215             my $good =
216             ['alpha', 'alpha', undef, 'alpha', 'beta', undef, 'beta', 'gamma'];
217             $vls = validate_list_sequence($good);
218             print Dumper($vls);
219              
220             #####
221              
222             $VAR1 = [1];
223              
224             =item 2
225              
226             Non-qualifying list:
227              
228             my $bad =
229             ['alpha', 'alpha', undef, 'alpha', 'beta', undef, 'beta', 'alpha', 'alpha'];
230             $vls = validate_list_sequence($bad);
231             print Dumper($vls);
232              
233             #####
234              
235             $VAR1 = [0,7,'alpha previously observed']
236              
237             =back
238              
239             =back
240              
241             =cut
242              
243             sub validate_list_sequence {
244 10     10 1 27293 my $list = shift;
245 10 100       453 croak "Must provide array ref to validate_list_sequence()"
246             unless ref($list) eq 'ARRAY';;
247 9         24 my $rv = [];
248 9         20 my $status = 1;
249 9 100       37 if (! defined $list->[0]) {
250 1         5 $rv = [0, 0, 'first element undefined'];
251 1         4 return $rv;
252             }
253 8 100       20 if (! defined $list->[$#{$list}]) {
  8         32  
254 1         2 $rv = [0, $#{$list}, 'last element undefined'];
  1         4  
255 1         4 return $rv;
256             }
257             # lpd => 'last previously defined'
258 7         21 my $lpd = $list->[0];
259 7         18 my %previous = ();
260 7         26 for (my $j = 1; $j <= $#{$list}; $j++) {
  425         781  
261 422 100       676 if (! defined $list->[$j]) {
262 262         375 next;
263             }
264             else {
265 160 100       265 if ($list->[$j] eq $lpd) {
266 141         210 next;
267             }
268             else {
269             # Value differs from last previously observed.
270             # Was it ever previously observed? If so, bad.
271 19 100       39 if (exists $previous{$list->[$j]}) {
272 3         7 $status = 0;
273 3         14 $rv = [$status, $j, "$list->[$j] previously observed"];
274 3         16 return $rv;
275             }
276             else {
277             # Value not previously observed, but since previous
278             # sequence ends with an undef, that sequence was not
279             # properly terminated. Bad.
280 16 100       36 if (! defined $list->[$j-1]) {
281 1         2 $status = 0;
282 1         6 $rv = [
283             $status,
284             $j,
285             "Immediately preceding element (index " . ($j-1) . ") not defined",
286             ];
287 1         5 return $rv;
288             }
289             else {
290 15         40 $previous{$lpd}++;
291 15 50       32 if (defined $list->[$j]) { $lpd = $list->[$j]; }
  15         28  
292 15         26 next;
293             }
294             }
295             }
296             }
297             }
298 3         16 return [$status];
299             }
300              
301             sub get_current_branch {
302 0     0 0   my @branches = qx{git branch};
303 0           chomp(@branches);
304 0           my ($cb, $current_branch);
305 0     0     $cb = first { m/^\*\s+?/ } @branches;
  0            
306 0           ($current_branch) = $cb =~ m{^\*\s+?(.*)};
307 0           return $current_branch;
308             }
309              
310             1;
311              
312