File Coverage

blib/lib/Test/Smoke/Syncer/Base.pm
Criterion Covered Total %
statement 98 140 70.0
branch 29 56 51.7
condition 11 28 39.2
subroutine 17 21 80.9
pod 10 10 100.0
total 165 255 64.7


line stmt bran cond sub pod time code
1             package Test::Smoke::Syncer::Base;
2 11     11   105 use warnings;
  11         26  
  11         393  
3 11     11   72 use strict;
  11         28  
  11         235  
4 11     11   67 use Carp;
  11         35  
  11         788  
5              
6             our $VERSION = '0.001';
7              
8 11     11   91 use Cwd qw/cwd abs_path/;
  11         33  
  11         727  
9 11     11   1212 use Test::Smoke::Util qw/whereis/;
  11         33  
  11         826  
10 11     11   83 use Test::Smoke::LogMixin;
  11         32  
  11         23618  
11              
12             =head1 NAME
13              
14             Test;:Smoke::Syncer::Base - Base class for all the syncers.
15              
16             =head1 DESCRIPTION
17              
18             =head2 Test::Smoke::Syncer::Baase->new(%arguments)
19              
20             Return a new instance.
21              
22             =cut
23              
24             sub new {
25 23     23 1 91 my $class = shift;
26              
27 23         417 return bless {@_}, $class;
28             }
29              
30             =head2 $syncer->verbose
31              
32             Get/Set verbosity.
33              
34             =cut
35              
36             sub verbose {
37 23     23 1 399 my $self = shift;
38 23 50       106 $self->{v} = shift if @_;
39 23         670 return $self->{v};
40             }
41              
42             =head2 $syncer->sync()
43              
44             Abstract method.
45              
46             =cut
47              
48             sub sync {
49 0     0 1 0 my $self = shift;
50 0   0     0 my $class = ref $self || $self;
51 0         0 Carp::croak("Should have been implemented by '$class'");
52             }
53              
54             =head2 $syncer->_clear_souce_tree( [$tree_dir] )
55              
56             [ Method | private-ish ]
57              
58             C<_clear_source_tree()> removes B files in the source-tree
59             using B. (See L for caveats.)
60              
61             If C<$tree_dir> is not specified, C<< $self->{ddir} >> is used.
62              
63             =cut
64              
65             sub _clear_source_tree {
66 2     2   8 my( $self, $tree_dir ) = @_;
67              
68 2   33     26 $tree_dir ||= $self->{ddir};
69              
70 2         48 $self->log_info("Clear source-tree from '$tree_dir' ");
71 2         1966 my $cnt = File::Path::rmtree( $tree_dir, $self->{v} > 1 );
72              
73 2 50       583 File::Path::mkpath( $tree_dir, $self->{v} > 1 ) unless -d $tree_dir;
74 2         26 $self->log_info("clear-source-tree: $cnt items OK");
75              
76             }
77              
78             =head2 $syncer->_relocate_tree( $source_dir )
79              
80             [ Method | Private-ish ]
81              
82             C<_relocate_tree()> uses B to move the source-tree
83             from C<< $source_dir >> to its destination (C<< $self->{ddir} >>).
84              
85             =cut
86              
87             sub _relocate_tree {
88 2     2   25 my( $self, $source_dir ) = @_;
89              
90 2         1366 require File::Copy;
91              
92 2 50       2914 $self->{v} and print "relocate source-tree ";
93              
94             # try to move it at once (sort of a rename)
95 2 50       57 my $ddir = $^O eq 'VMS' ? $self->{vms_ddir} : $self->{ddir};
96             my $ok = $source_dir eq $ddir
97 2 50       56 ? 1 : File::Copy::move( $source_dir, $self->{ddir} );
98              
99             # Failing that: Copy-By-File :-(
100 2 50 33     492 if ( ! $ok && -d $source_dir ) {
101 0         0 my $cwd = cwd();
102 0 0       0 chdir $source_dir or do {
103 0         0 print "Cannot chdir($source_dir): $!\n";
104 0         0 return 0;
105             };
106 0         0 require File::Find;
107             File::Find::finddepth( sub {
108              
109 0     0   0 my $dest = File::Spec->canonpath( $File::Find::name );
110 0         0 $dest =~ s/^\Q$source_dir//;
111 0         0 $dest = File::Spec->catfile( $self->{ddir}, $dest );
112              
113 0 0       0 $self->{v} > 1 and print "move $_ $dest\n";
114 0         0 File::Copy::move( $_, $dest );
115 0         0 }, "./" );
116 0 0       0 chdir $cwd or print "Cannot chdir($cwd) back: $!\n";
117 0         0 File::Path::rmtree( $source_dir, $self->{v} > 1 );
118 0         0 $ok = ! -d $source_dir;
119             }
120 2 50       16 die "Can't move '$source_dir' to $self->{ddir}' ($!)" unless $ok;
121 2 50       18 $self->{v} and print "OK\n";
122             }
123              
124             =head2 $syncer->check_dot_patch( )
125              
126             [ Method | Public ]
127              
128             C checks if there is a '.patch' file in the source-tree.
129             It will try to create one if it is not there (this is the case for snapshots).
130              
131             It returns the patchlevel found or C.
132              
133             =cut
134              
135             sub check_dot_patch {
136 16     16 1 126 my $self = shift;
137              
138 16         267 my $dot_patch = File::Spec->catfile( $self->{ddir}, '.patch' );
139              
140 16         134 local *DOTPATCH;
141 16         123 my $patch_level = '?????';
142 16 100       1248 if ( open DOTPATCH, "< $dot_patch" ) {
143 7         257 chomp( $patch_level = );
144 7         95 close DOTPATCH;
145             # From rsync:
146             # blead 2019-11-06.00:32:06 +0100 cc8ba724ccabff255f384ab68d6f6806ac2eae7c v5.31.5-174-gcc8ba72
147             # from make_dot_patch.pl:
148             # blead 2019-11-05.23:32:06 cc8ba724ccabff255f384ab68d6f6806ac2eae7c v5.31.5-174-gcc8ba724cc
149 7 50       54 if ( $patch_level ) {
150 7         68 my @dot_patch = split ' ', $patch_level;
151              
152             # As we do not use time information, we can just pick the first and
153             # the last two elements
154 7         72 my ($branch, $sha, $describe) = @dot_patch[0, -2, -1];
155             # $sha -> sysinfo.git_id
156             # $describe -> sysinfo.git_describe
157              
158 7   33     99 $self->{patchlevel} = $sha || $branch;
159 7   33     53 $self->{patchdescr} = $describe || $branch;
160 7         146 return $self->{patchlevel};
161             }
162             }
163              
164             # There does not seem to be a '.patch', try 'patchlevel.h'
165 9         122 local *PATCHLEVEL_H;
166 9         199 my $patchlevel_h = File::Spec->catfile( $self->{ddir}, 'patchlevel.h' );
167 9 50       484 if ( open PATCHLEVEL_H, "< $patchlevel_h" ) {
168 9         81 my $declaration_seen = 0;
169 9         529 while ( ) {
170 1345   100     5257 $declaration_seen ||= /local_patches\[\]/;
171 1345 100 100     5070 $declaration_seen && /^\s+,"(?:DEVEL|MAINT)(\d+)|(RC\d+)"/ or next;
172 9   0     151 $patch_level = $1 || $2 || '?????';
173 9 50       73 if ( $patch_level =~ /^RC/ ) {
174 0         0 $patch_level = $self->version_from_patchlevel_h .
175             "-$patch_level";
176             } else {
177 9         57 $patch_level++;
178             }
179             }
180             # save 'patchlevel.h' mtime, so you can set it on '.patch'
181 9         185 my $mtime = ( stat PATCHLEVEL_H )[9];
182 9         234 close PATCHLEVEL_H;
183             # Now create '.patch' and return if $patch_level
184             # The patchlevel is off by one in snapshots
185 9 50 33     211 if ( $patch_level && $patch_level !~ /-RC\d+$/ ) {
186 9 50       861 if ( open DOTPATCH, "> $dot_patch" ) {
187 9         184 print DOTPATCH "$patch_level\n";
188 9         487 close DOTPATCH; # no use generating the error
189 9         397 utime $mtime, $mtime, $dot_patch;
190             }
191 9         128 $self->{patchlevel} = $patch_level;
192 9         126 return $self->{patchlevel};
193             } else {
194 0         0 $self->{patchlevel} = $patch_level;
195             return $self->{patchlevel}
196 0         0 }
197             }
198 0         0 return undef;
199             }
200              
201             =head2 version_from_patchlevel_h( $ddir )
202              
203             C returns a "dotted" version as derived
204             from the F file in the distribution.
205              
206             =cut
207              
208             sub version_from_patchlevel_h {
209 0     0 1 0 my $self = shift;
210              
211 0         0 require Test::Smoke::Util;
212 0         0 return Test::Smoke::Util::version_from_patchelevel( $self->{ddir} );
213             }
214              
215             =head2 is_git_dir()
216              
217             Checks, in a git way, if we are in a real git repository directory.
218              
219             =cut
220              
221             sub is_git_dir {
222 1     1 1 19 my $self = shift;
223              
224 1         53 my $gitbin = whereis('git');
225 1 50       32 if (!$gitbin) {
226 0         0 $self->log_debug("Could not find a git-binary to run for 'is_git_dir'");
227 0         0 return 0;
228             }
229 1         42 $self->log_debug("Found '$gitbin' for 'is_git_dir'");
230              
231 1         25 my $git = Test::Smoke::Util::Execute->new(
232             command => $gitbin,
233             verbose => $self->verbose,
234             );
235 1         32 my $out = $git->run(
236             'rev-parse' => '--is-inside-work-tree',
237             '2>&1'
238             );
239 1         466 $self->log_debug("git rev-parse --is-inside-work-tree: " . $out);
240 1 50       237 return $out eq 'true' ? 1 : 0;
241             }
242              
243             =head2 make_dot_patch
244              
245             If this is a git repo, run the C<< Porting/make_dot_patch.pl >> to generate the
246             .patch file
247              
248             =cut
249              
250             sub make_dot_patch {
251 0     0 1 0 my $self = shift;
252              
253 0         0 my $mk_dot_patch = Test::Smoke::Util::Execute->new(
254             command => "$^X",
255             verbose => $self->verbose,
256             );
257 0         0 my $perlout = $mk_dot_patch->run("Porting/make_dot_patch.pl", ">", ".patch");
258 0         0 $self->log_debug($perlout);
259             }
260              
261             =head2 $syncer->clean_from_directory( $source_dir[, @leave_these] )
262              
263             C uses File::Find to get the contents of
264             C<$source_dir> and compare these to {ddir} and remove all other files.
265              
266             The contents of @leave_these should be in "MANIFEST-format"
267             (See L).
268              
269             =cut
270              
271             sub clean_from_directory {
272 5     5 1 18 my $self = shift;
273 5         23 my ($clean_dir, @leave_these) = @_;
274 5         78 my $this_dir = abs_path(File::Spec->curdir);
275              
276 5 50       55 my $source_dir = File::Spec->file_name_is_absolute($clean_dir)
277             ? $clean_dir
278             : File::Spec->rel2abs($clean_dir, $this_dir);
279 5         110 $self->log_debug("[clean_from_directory($this_dir)] $clean_dir => $source_dir\n");
280              
281 5         1307 require Test::Smoke::SourceTree;
282 5         72 my $tree = Test::Smoke::SourceTree->new($source_dir, $self->{v});
283              
284 5         19 my %orig_dir = map { ( $_ => 1) } @leave_these;
  0         0  
285             File::Find::find( sub {
286 40 100   40   1305 return unless -f;
287 30         177 my $file = $tree->abs2mani( $File::Find::name );
288 30         493 $orig_dir{ $file } = 1;
289 5         365 }, $source_dir );
290              
291 5         72 $tree = Test::Smoke::SourceTree->new( $self->{ddir}, $self->{v} );
292             File::Find::find( sub {
293 41 100   41   1542 return unless -f;
294 31         182 my $file = $tree->abs2mani( $File::Find::name );
295 31 100       472 return if exists $orig_dir{ $file };
296 1         136 1 while unlink $_;
297 1 50       32 $self->log_debug("Unlink '$file': " . (-e $_ ? "$!" : "ok"));
298 5         341 }, $self->{ddir} );
299             }
300              
301             =head2 $syncer->pre_sync
302              
303             C should be called by the C methods to setup the
304             sync environment. Currently only useful on I.
305              
306             =cut
307              
308             sub pre_sync {
309 15 50   15 1 156 return 1 unless $^O eq 'VMS';
310 0         0 my $self = shift;
311 0         0 require Test::Smoke::Util;
312              
313 0         0 Test::Smoke::Util::set_vms_rooted_logical( TSP5SRC => $self->{ddir} );
314 0         0 $self->{vms_ddir} = $self->{ddir};
315 0         0 $self->{ddir} = 'TSP5SRC:[000000]';
316             }
317              
318             =head2 $syncer->post_sync
319              
320             C should be called by the C methods to unset the
321             sync environment. Currently only useful on I.
322              
323             =cut
324              
325             sub post_sync {
326 14 50   14 1 133 return 1 unless $^O eq 'VMS';
327 0           my $self = shift;
328              
329 0   0       ( my $logical = $self->{ddir} || '' ) =~ s/:\[000000\]$//;
330 0 0         return unless $logical;
331 0           my $result = system "DEASSIGN/JOB $logical";
332              
333 0           $self->{ddir} = delete $self->{vms_ddir};
334 0           return $result == 0;
335             }
336              
337             1;
338              
339             =head1 COPYRIGHT
340              
341             (c) 2002-2013, All rights reserved.
342              
343             * Abe Timmerman
344              
345             This library is free software; you can redistribute it and/or modify
346             it under the same terms as Perl itself.
347              
348             See:
349              
350             * ,
351             *
352              
353             This program is distributed in the hope that it will be useful,
354             but WITHOUT ANY WARRANTY; without even the implied warranty of
355             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
356              
357             =cut