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   103 use warnings;
  11         36  
  11         400  
3 11     11   116 use strict;
  11         46  
  11         257  
4 11     11   51 use Carp;
  11         35  
  11         809  
5              
6             our $VERSION = '0.001';
7              
8 11     11   103 use Cwd qw/cwd abs_path/;
  11         26  
  11         738  
9 11     11   1200 use Test::Smoke::Util qw/whereis/;
  11         34  
  11         924  
10 11     11   89 use Test::Smoke::LogMixin;
  11         29  
  11         23144  
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 104 my $class = shift;
26              
27 23         465 return bless {@_}, $class;
28             }
29              
30             =head2 $syncer->verbose
31              
32             Get/Set verbosity.
33              
34             =cut
35              
36             sub verbose {
37 21     21 1 410 my $self = shift;
38 21 50       74 $self->{v} = shift if @_;
39 21         350 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   12 my( $self, $tree_dir ) = @_;
67              
68 2   33     25 $tree_dir ||= $self->{ddir};
69              
70 2         57 $self->log_info("Clear source-tree from '$tree_dir' ");
71 2         1599 my $cnt = File::Path::rmtree( $tree_dir, $self->{v} > 1 );
72              
73 2 50       432 File::Path::mkpath( $tree_dir, $self->{v} > 1 ) unless -d $tree_dir;
74 2         22 $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   29 my( $self, $source_dir ) = @_;
89              
90 2         847 require File::Copy;
91              
92 2 50       3024 $self->{v} and print "relocate source-tree ";
93              
94             # try to move it at once (sort of a rename)
95 2 50       37 my $ddir = $^O eq 'VMS' ? $self->{vms_ddir} : $self->{ddir};
96             my $ok = $source_dir eq $ddir
97 2 50       93 ? 1 : File::Copy::move( $source_dir, $self->{ddir} );
98              
99             # Failing that: Copy-By-File :-(
100 2 50 33     402 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       12 die "Can't move '$source_dir' to $self->{ddir}' ($!)" unless $ok;
121 2 50       19 $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 129 my $self = shift;
137              
138 16         536 my $dot_patch = File::Spec->catfile( $self->{ddir}, '.patch' );
139              
140 16         141 local *DOTPATCH;
141 16         161 my $patch_level = '?????';
142 16 100       1079 if ( open DOTPATCH, "< $dot_patch" ) {
143 7         306 chomp( $patch_level = );
144 7         98 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       61 if ( $patch_level ) {
150 7         75 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         74 my ($branch, $sha, $describe) = @dot_patch[0, -2, -1];
155             # $sha -> sysinfo.git_id
156             # $describe -> sysinfo.git_describe
157              
158 7   33     115 $self->{patchlevel} = $sha || $branch;
159 7   33     67 $self->{patchdescr} = $describe || $branch;
160 7         102 return $self->{patchlevel};
161             }
162             }
163              
164             # There does not seem to be a '.patch', try 'patchlevel.h'
165 9         64 local *PATCHLEVEL_H;
166 9         214 my $patchlevel_h = File::Spec->catfile( $self->{ddir}, 'patchlevel.h' );
167 9 50       428 if ( open PATCHLEVEL_H, "< $patchlevel_h" ) {
168 9         103 my $declaration_seen = 0;
169 9         459 while ( ) {
170 1345   100     4891 $declaration_seen ||= /local_patches\[\]/;
171 1345 100 100     4600 $declaration_seen && /^\s+,"(?:DEVEL|MAINT)(\d+)|(RC\d+)"/ or next;
172 9   0     115 $patch_level = $1 || $2 || '?????';
173 9 50       69 if ( $patch_level =~ /^RC/ ) {
174 0         0 $patch_level = $self->version_from_patchlevel_h .
175             "-$patch_level";
176             } else {
177 9         113 $patch_level++;
178             }
179             }
180             # save 'patchlevel.h' mtime, so you can set it on '.patch'
181 9         276 my $mtime = ( stat PATCHLEVEL_H )[9];
182 9         146 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     166 if ( $patch_level && $patch_level !~ /-RC\d+$/ ) {
186 9 50       706 if ( open DOTPATCH, "> $dot_patch" ) {
187 9         154 print DOTPATCH "$patch_level\n";
188 9         425 close DOTPATCH; # no use generating the error
189 9         256 utime $mtime, $mtime, $dot_patch;
190             }
191 9         113 $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 15 my $self = shift;
223              
224 1         54 my $gitbin = whereis('git');
225 1 50       9 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         24 $self->log_debug("Found '$gitbin' for 'is_git_dir'");
230              
231 1         23 my $git = Test::Smoke::Util::Execute->new(
232             command => $gitbin,
233             verbose => $self->verbose,
234             );
235 1         56 my $out = $git->run(
236             'rev-parse' => '--is-inside-work-tree',
237             '2>&1'
238             );
239 1         68 $self->log_debug("git rev-parse --is-inside-work-tree: " . $out);
240 1 50       96 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 17 my $self = shift;
273 5         37 my ($clean_dir, @leave_these) = @_;
274 5         91 my $this_dir = abs_path(File::Spec->curdir);
275              
276 5 50       71 my $source_dir = File::Spec->file_name_is_absolute($clean_dir)
277             ? $clean_dir
278             : File::Spec->rel2abs($clean_dir, $this_dir);
279 5         105 $self->log_debug("[clean_from_directory($this_dir)] $clean_dir => $source_dir\n");
280              
281 5         1548 require Test::Smoke::SourceTree;
282 5         83 my $tree = Test::Smoke::SourceTree->new($source_dir, $self->{v});
283              
284 5         34 my %orig_dir = map { ( $_ => 1) } @leave_these;
  0         0  
285             File::Find::find( sub {
286 40 100   40   1340 return unless -f;
287 30         175 my $file = $tree->abs2mani( $File::Find::name );
288 30         524 $orig_dir{ $file } = 1;
289 5         371 }, $source_dir );
290              
291 5         115 $tree = Test::Smoke::SourceTree->new( $self->{ddir}, $self->{v} );
292             File::Find::find( sub {
293 41 100   41   1449 return unless -f;
294 31         164 my $file = $tree->abs2mani( $File::Find::name );
295 31 100       481 return if exists $orig_dir{ $file };
296 1         134 1 while unlink $_;
297 1 50       22 $self->log_debug("Unlink '$file': " . (-e $_ ? "$!" : "ok"));
298 5         333 }, $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 159 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 120 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