File Coverage

blib/lib/App/SmokeBox/Mini.pm
Criterion Covered Total %
statement 221 281 78.6
branch 50 118 42.3
condition 13 62 20.9
subroutine 35 39 89.7
pod 1 1 100.0
total 320 501 63.8


line stmt bran cond sub pod time code
1             package App::SmokeBox::Mini;
2             $App::SmokeBox::Mini::VERSION = '0.68';
3             #ABSTRACT: the guts of the minismokebox command
4              
5 10     10   172857 use strict;
  10         24  
  10         247  
6 10     10   45 use warnings;
  10         19  
  10         225  
7 10     10   4505 use Pod::Usage;
  10         405811  
  10         1073  
8 10     10   4224 use Config::Tiny;
  10         7202  
  10         245  
9 10     10   56 use File::Spec;
  10         19  
  10         192  
10 10     10   46 use File::Path qw[mkpath];
  10         18  
  10         465  
11 10     10   50 use Cwd;
  10         18  
  10         491  
12 10     10   5607 use Getopt::Long;
  10         75601  
  10         54  
13 10     10   5477 use Time::Duration qw(duration_exact);
  10         14001  
  10         605  
14 10     10   4126 use Module::Pluggable search_path => ['App::SmokeBox::Mini::Plugin'];
  10         87542  
  10         70  
15 10     10   5194 use Module::Load;
  10         8417  
  10         57  
16 10     10   665 use if ( $^O eq 'linux' ), 'POE::Kernel', { loop => 'POE::XS::Loop::EPoll' };
  10         23  
  10         66  
17 10     10   694068 use unless ( $^O =~ /^(?:linux|MSWin32|darwin)$/ ), 'POE::Kernel', { loop => 'POE::XS::Loop::Poll' };
  10         121  
  10         93  
18 10     10   562 use if ( scalar grep { $^O eq $_ } qw(MSWin32 darwin) ), 'POE::Kernel', { loop => 'POE::Loop::Event' };
  10         20  
  10         313  
  20         121  
19 10     10   4445 use POE;
  10         4666  
  10         53  
20 10     10   41259 use POE::Component::SmokeBox;
  10         482289  
  10         1110  
21 10     10   77 use POE::Component::SmokeBox::Smoker;
  10         21  
  10         145  
22 10     10   45 use POE::Component::SmokeBox::Job;
  10         20  
  10         157  
23 10     10   4521 use POE::Component::SmokeBox::Dists;
  10         851521  
  10         300  
24 10     10   4725 use POE::Component::SmokeBox::Recent;
  10         784265  
  10         539  
25 10     10   4090 use App::SmokeBox::PerlVersion;
  10         9250  
  10         269  
26              
27 10     10   71 use constant CPANURL => 'ftp://cpan.cpantesters.org/CPAN/';
  10         67  
  10         23489  
28              
29             $ENV{PERL5_MINISMOKEBOX} = $App::SmokeBox::Mini::VERSION;
30              
31             sub _smokebox_dir {
32             return $ENV{PERL5_SMOKEBOX_DIR}
33             if exists $ENV{PERL5_SMOKEBOX_DIR}
34 16 50 33 16   29304 && defined $ENV{PERL5_SMOKEBOX_DIR};
35              
36 0         0 my @os_home_envs = qw( APPDATA HOME USERPROFILE WINDIR SYS$LOGIN );
37              
38 0         0 for my $env ( @os_home_envs ) {
39 0 0       0 next unless exists $ENV{ $env };
40 0 0 0     0 next unless defined $ENV{ $env } && length $ENV{ $env };
41 0 0       0 return $ENV{ $env } if -d $ENV{ $env };
42             }
43              
44 0         0 return cwd();
45             }
46              
47             sub _read_config {
48 3     3   389 my $smokebox_dir = File::Spec->catdir( _smokebox_dir(), '.smokebox' );
49 3 50       62 return unless -d $smokebox_dir;
50 3         31 my $conf_file = File::Spec->catfile( $smokebox_dir, 'minismokebox' );
51 3 50       42 return unless -e $conf_file;
52 3         46 my $Config = Config::Tiny->read( $conf_file );
53 3         559 my @config;
54 3 50       18 if ( defined $Config->{_} ) {
55 3         8 my $root = delete $Config->{_};
56 3         14 @config = map { $_, $root->{$_} } grep { exists $root->{$_} }
  12         92  
  36         66  
57             qw(debug perl indices recent backend url home nolog rss random noepoch perlenv);
58             }
59 3 100       12 push @config, 'sections', $Config if scalar keys %{ $Config };
  3         22  
60 3         31 return @config;
61             }
62              
63             sub _read_ts_data {
64 2     2   7 my $timestamp = File::Spec->catfile( _smokebox_dir(), '.smokebox', 'timestamp' );
65 2         6 my %data;
66 2 50       50 if ( -e $timestamp ) {
67 0 0       0 open my $fh, '<', $timestamp or die "Could not open 'timestamp': $!\n";
68 0         0 while (<$fh>) {
69 0         0 chomp;
70 0         0 my ($prefix,$ts) = $_ =~ /^(\[.+?\])([\d\.]+)$/;
71 0 0 0     0 if ( $prefix and $ts ) {
72 0         0 $data{ $prefix } = $ts;
73             }
74             }
75 0         0 close $fh;
76             }
77 2 50       8 return %data if wantarray;
78 2         17 return \%data;
79             }
80              
81             sub _get_jobs_from_file {
82 3   50 3   197 my $jobs = shift || return;
83 3 50       57 unless ( open JOBS, "< $jobs" ) {
84 0         0 warn "Could not open '$jobs' '$!'\n";
85 0         0 return;
86             }
87 3         10 my @jobs;
88 3         42 while () {
89 15         41 chomp;
90 15         47 push @jobs, $_;
91             }
92 3         20 close JOBS;
93 3         18 return @jobs;
94             }
95              
96             sub _display_version {
97 0     0   0 print "minismokebox version ", $App::SmokeBox::Mini::VERSION,
98             ", powered by POE::Component::SmokeBox ", POE::Component::SmokeBox->VERSION, "\n\n";
99 0         0 print <
100             Copyright (C) 2011 Chris 'BinGOs' Williams
101             This module may be used, modified, and distributed under the same terms as Perl itself.
102             Please see the license that came with your Perl distribution for details.
103             EOF
104 0         0 exit;
105             }
106              
107             sub run {
108 2     2 1 1628 my $package = shift;
109 2         15 my %config = _read_config();
110 2         7 my $version;
111             GetOptions(
112 0     0   0 "help" => sub { pod2usage(1); },
113 0     0   0 "version" => sub { $version = 1 },
114             "debug" => \$config{debug},
115             "perl=s" => \$config{perl},
116             "indices" => \$config{indices},
117             "recent" => \$config{recent},
118             "jobs=s" => \$config{jobs},
119             "backend=s" => \$config{backend},
120             "author=s" => \$config{author},
121             "package=s" => \$config{package},
122             "phalanx" => \$config{phalanx},
123             "url=s" => \$config{url},
124             "reverse" => \$config{reverse},
125             "home=s" => \$config{home},
126             "nolog" => \$config{nolog},
127             "noepoch" => \$config{noepoch},
128             "rss" => \$config{rss},
129             "random" => \$config{random},
130             "perlenv" => \$config{perlenv},
131 2 50       77 ) or pod2usage(2);
132              
133 2 50       2750 _display_version() if $version;
134              
135 2 50 33     13 $config{perl} = $^X unless $config{perl} and -e $config{perl};
136 2 50       11 $ENV{PERL5_SMOKEBOX_DEBUG} = 1 if $config{debug};
137 2         12 $ENV{AUTOMATED_TESTING} = 1; # We need this because some backends do not set it.
138 2         10 $ENV{PERL_MM_USE_DEFAULT} = 1; # And this.
139 2         9 $ENV{PERL_EXTUTILS_AUTOINSTALL} = '--defaultdeps'; # Got this from CPAN::Reporter::Smoker. Cheers, xdg!
140              
141 2 50 33     37 if ( $config{jobs} and -e $config{jobs} ) {
142 2         11 my @jobs = _get_jobs_from_file( $config{jobs} );
143 2 50       9 $config{jobs} = \@jobs if scalar @jobs;
144             }
145              
146 2   50     15 my $env = delete $config{sections}->{ENVIRONMENT} || { };
147              
148 2         301 print "Running minismokebox with options:\n";
149             printf("%-20s %s\n", $_, $config{$_})
150 2         11 for grep { defined $config{$_} } qw(debug indices perl jobs backend author package
  30         470  
151             phalanx reverse url home nolog random noepoch perlenv);
152 2 50       9 if ( keys %{ $env } ) {
  2         14  
153 0         0 print "ENVIRONMENT:\n";
154 0         0 printf("%-20s %s\n", $_, $env->{$_}) for keys %{ $env };
  0         0  
155             }
156              
157 2 50 33     11 if ( $config{home} and ! -e $config{home} ) {
158 0 0       0 mkpath( $config{home} ) or die "Could not create '$config{home}': $!\n";
159             }
160              
161 2 50 33     11 if ( $config{home} and ! -d $config{home} ) {
162 0         0 warn "Home option was specified but '$config{home}' is not a directory, ignoring\n";
163 0         0 delete $config{home};
164             }
165              
166 2         7 my $self = bless \%config, $package;
167              
168 2         10 $self->{_tsdata} = _read_ts_data();
169              
170 2         8 $self->{env} = $env;
171 2 50       7 $self->{env}->{HOME} = $self->{home} if $self->{home};
172             $self->{env}->{PERL5LIB} = $ENV{PERL5LIB}
173 2 0 33     8 if $self->{perlenv} and $ENV{PERL5LIB};
174              
175             $self->{sbox} = POE::Component::SmokeBox->spawn(
176             smokers => [
177             POE::Component::SmokeBox::Smoker->new(
178             perl => $self->{perl},
179 2 50       6 ( scalar keys %{ $self->{env} } ? ( env => $self->{env} ) : () ),
  2         41  
180             ),
181             ],
182             );
183              
184 2         5940 $self->{session_id} = POE::Session->create(
185             object_states => [
186             $self => { recent => '_submission', dists => '_submission', },
187             $self => [qw(_start _stop _check _child _indices _smoke _search _perl_version)],
188             ],
189             heap => $self,
190             )->ID();
191              
192 2         295 $poe_kernel->run();
193 2         4837 return 1;
194             }
195              
196             sub _start {
197 2     2   559 my ($kernel,$self) = @_[KERNEL,OBJECT];
198 2         52 $self->{session_id} = $_[SESSION]->ID();
199             # Run a check to make sure the backend exists in the designated perl
200             $kernel->post( $self->{sbox}->session_id(), 'submit', event => '_check', job =>
201             POE::Component::SmokeBox::Job->new(
202 2 50       15 ( $self->{backend} ? ( type => $self->{backend} ) : () ),
203             command => 'check',
204             ),
205             );
206             $self->{stats} = {
207 2         2357 started => time(),
208             totaljobs => 0,
209             avg_run => 0,
210             min_run => 0,
211             max_run => 0,
212             _sum => 0,
213             idle => 0,
214             excess => 0,
215             };
216             # Initialise plugins
217 2         18 foreach my $plugin ( $self->plugins() ) {
218 0         0 load $plugin;
219 0         0 $plugin->init( $self->{sections} );
220             }
221 2         698 return;
222             }
223              
224             sub _child {
225 4     4   150330 my ($kernel,$self,$reason,$child) = @_[KERNEL,OBJECT,ARG0,ARG1];
226 4 100       35 return unless $reason eq 'create';
227 2         9 push @{ $self->{_sessions} }, $child->ID();
  2         19  
228 2         36 $kernel->detach_child( $child );
229 2         289 return;
230             }
231              
232             sub _stop {
233 2     2   489 my ($kernel,$self) = @_[KERNEL,OBJECT];
234 2         24 $kernel->call( $self->{sbox}->session_id(), 'shutdown' );
235 2         669 my $finish = time();
236 2         26 my $cumulative = duration_exact( $finish - $self->{stats}->{started} );
237 2         243 my @stats = map { $self->{stats}->{$_} } qw(totaljobs idle excess avg_run min_run max_run);
  12         35  
238 2         7 $kernel->call( $_, 'sbox_stop', $self->{stats}->{started}, $finish, @stats ) for @{ $self->{_sessions} };
  2         24  
239 2         142 $stats[$_] = duration_exact( $stats[$_] ) for 3 .. 5;
240 2         910 print "minismokebox started at: \t", scalar localtime($self->{stats}->{started}), "\n";
241 2         299 print "minismokebox finished at: \t", scalar localtime($finish), "\n";
242 2         236 print "minismokebox ran for: \t", $cumulative, "\n";
243 2         232 print "minismokebox tot jobs:\t", $stats[0], "\n";
244 2 50       21 print "minismokebox idle kills:\t", $stats[1], "\n" if $stats[1];
245 2 50       12 print "minismokebox excess kills:\t", $stats[2], "\n" if $stats[2];
246 2         160 print "minismokebox avg run: \t", $stats[3], "\n";
247 2         157 print "minismokebox min run: \t", $stats[4], "\n";
248 2         152 print "minismokebox max run: \t", $stats[5], "\n";
249 2 50       19 return if $self->{noepoch};
250 2         16 my $smokebox_dir = File::Spec->catdir( _smokebox_dir(), '.smokebox' );
251 2 50       53 mkpath( $smokebox_dir ) unless -d $smokebox_dir;
252             {
253 2         6 $self->{_tsdata}->{ $self->{_tsprefix} } = $self->{stats}->{started};
  2         27  
254 2 50       470 open my $ts, '>', File::Spec->catfile( $smokebox_dir, 'timestamp' ) or die "Could not open 'timestamp': $!\n";
255 2         12 print {$ts} join('', $_, $self->{_tsdata}->{$_} ), "\n" for sort keys %{ $self->{_tsdata} };
  2         16  
  2         19  
256 2         161 close $ts;
257             }
258 2         23 return;
259             }
260              
261             sub _check {
262 2     2   27042 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
263 2         16 my ($result) = $data->{result}->results;
264 2 50       25 unless ( $result->{status} == 0 ) {
265 0   0     0 my $backend = $self->{backend} || 'CPANPLUS::YACSmoke';
266 0         0 warn "The specified perl '$self->{perl}' does not have backend '$backend' installed, aborting\n";
267 0         0 return;
268             }
269             App::SmokeBox::PerlVersion->version(
270             perl => $self->{perl},
271 2         15 event => '_perl_version',
272             session => $_[SESSION]->postback( '_perl_version' ),
273             );
274 2         225 return;
275             }
276              
277             sub _perl_version {
278 2     2   19847 my ($kernel,$self,$args) = @_[KERNEL,OBJECT,ARG1];
279 2         5 my $data = shift @{$args};
  2         8  
280 2         7 my ($version,$archname,$osvers) = @{ $data }{qw(version archname osvers)};
  2         7  
281 2 50 33     30 if ( $version and $archname and $osvers ) {
      33        
282 2         56 print "Perl Version: $version\nArchitecture: $archname\nOS Version: $osvers\n";
283 2         9 $kernel->post( $_, 'sbox_perl_info', $version, $archname, $osvers ) for @{ $self->{_sessions} };
  2         15  
284 2         211 $self->{_perlinfo} = [ $version, $archname ];
285 2         15 $self->{_tsprefix} = "[$version$archname]";
286 2 50       16 $self->{_epoch} = $self->{_tsdata}->{ $self->{_tsprefix} } unless $self->{noepoch};
287             }
288 2 50       12 if ( $self->{indices} ) {
289             $kernel->post( $self->{sbox}->session_id(), 'submit', event => '_indices', job =>
290             POE::Component::SmokeBox::Job->new(
291 2 50       21 ( $self->{backend} ? ( type => $self->{backend} ) : () ),
292             command => 'index',
293             ),
294             );
295 2         2716 return;
296             }
297 0         0 $kernel->yield( '_search' );
298 0         0 return;
299             }
300              
301             sub _indices {
302 2     2   23939 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
303 2         49 my ($result) = $data->{result}->results;
304 2 50       24 unless ( $result->{status} == 0 ) {
305 0   0     0 my $backend = $self->{backend} || 'CPANPLUS::YACSmoke';
306 0         0 warn "There was a problem with the reindexing\n";
307 0         0 return;
308             }
309 2         11 $kernel->yield( '_search' );
310 2         122 return;
311             }
312              
313             sub _search {
314 2     2   505 my ($kernel,$self) = @_[KERNEL,OBJECT];
315 2 50 33     31 if ( $self->{jobs} and ref $self->{jobs} eq 'ARRAY' ) {
316 2         5 foreach my $distro ( @{ $self->{jobs} } ) {
  2         12  
317 10         10723 print "Submitting: $distro\n";
318             $kernel->post( $self->{sbox}->session_id(), 'submit', event => '_smoke', job =>
319             POE::Component::SmokeBox::Job->new(
320             ( $self->{backend} ? ( type => $self->{backend} ) : () ),
321             command => 'smoke',
322             module => $distro,
323 10 50       90 ( $self->{nolog} ? ( no_log => 1 ) : () ),
    50          
324             ),
325             );
326             }
327             }
328 2 50       2451 if ( $self->{recent} ) {
329             POE::Component::SmokeBox::Recent->recent(
330             url => $self->{url} || CPANURL,
331             event => 'recent',
332             rss => $self->{rss},
333 0 0 0     0 ( defined $self->{_epoch} ? ( epoch => $self->{_epoch} ) : () ),
334             );
335             }
336 2 50       68 if ( $self->{package} ) {
337 0         0 warn "Doing a distro search, this may take a little while\n";
338             POE::Component::SmokeBox::Dists->distro(
339             event => 'dists',
340             search => $self->{package},
341 0   0     0 url => $self->{url} || CPANURL,
342             );
343             }
344 2 50       12 if ( $self->{author} ) {
345 0         0 warn "Doing an author search, this may take a little while\n";
346             POE::Component::SmokeBox::Dists->author(
347             event => 'dists',
348             search => $self->{author},
349 0   0     0 url => $self->{url} || CPANURL,
350             );
351             }
352 2 50       10 if ( $self->{phalanx} ) {
353 0         0 warn "Doing a phalanx search, this may take a little while\n";
354             POE::Component::SmokeBox::Dists->phalanx(
355             event => 'dists',
356 0   0     0 url => $self->{url} || CPANURL,
357             );
358             }
359 2 50       9 if ( $self->{random} ) {
360 0         0 warn "Doing a random search, this may take a little while\n";
361             POE::Component::SmokeBox::Dists->random(
362             event => 'dists',
363 0   0     0 url => $self->{url} || CPANURL,
364             );
365             }
366 2 50 33     62 return if !$self->{recent} and ( $self->{package} or $self->{author} or $self->{phalanx} or ( $self->{jobs} and ref $self->{jobs} eq 'ARRAY' ) );
      33        
367             POE::Component::SmokeBox::Recent->recent(
368             url => $self->{url} || CPANURL,
369             event => 'recent',
370             rss => $self->{rss},
371 0 0 0     0 ( defined $self->{_epoch} ? ( epoch => $self->{_epoch} ) : () ),
372             );
373 0         0 return;
374             }
375              
376             sub _submission {
377 0     0   0 my ($kernel,$self,$state,$data) = @_[KERNEL,OBJECT,STATE,ARG0];
378 0 0       0 if ( $data->{error} ) {
379 0         0 warn $data->{error}, "\n";
380 0         0 return;
381             }
382 0 0 0     0 if ( $state eq 'recent' and $self->{reverse} ) {
383 0         0 @{ $data->{$state} } = reverse @{ $data->{$state} };
  0         0  
  0         0  
384             }
385 0         0 foreach my $distro ( @{ $data->{$state} } ) {
  0         0  
386 0         0 print "Submitting: $distro\n";
387             $kernel->post( $self->{sbox}->session_id(), 'submit', event => '_smoke', job =>
388             POE::Component::SmokeBox::Job->new(
389             ( $self->{backend} ? ( type => $self->{backend} ) : () ),
390             command => 'smoke',
391             module => $distro,
392 0 0       0 ( $self->{nolog} ? ( no_log => 1 ) : () ),
    0          
393             ),
394             );
395             }
396 0         0 return;
397             }
398              
399             sub _smoke {
400 10     10   50171039 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
401 10         78 my $dist = $data->{job}->module();
402 10         1004 my ($result) = $data->{result}->results;
403 10         2140 print "Distribution: '$dist' finished with status '$result->{status}'\n";
404 10         42 $kernel->post( $_, 'sbox_smoke', $data ) for @{ $self->{_sessions} };
  10         76  
405 10         616 my $run_time = $result->{end_time} - $result->{start_time};
406 10 100       65 $self->{stats}->{max_run} = $run_time if $run_time > $self->{stats}->{max_run};
407 10 100       44 $self->{stats}->{min_run} = $run_time if $self->{stats}->{min_run} == 0;
408 10 50       81 $self->{stats}->{min_run} = $run_time if $run_time < $self->{stats}->{min_run};
409 10         30 $self->{stats}->{_sum} += $run_time;
410 10         34 $self->{stats}->{totaljobs}++;
411 10         48 $self->{stats}->{avg_run} = $self->{stats}->{_sum} / $self->{stats}->{totaljobs};
412 10 50       48 $self->{stats}->{idle}++ if $result->{idle_kill};
413 10 50       38 $self->{stats}->{excess}++ if $result->{excess_kill};
414 10         29 $self->{_jobs}--;
415 10         40 return;
416             }
417              
418             'smoke it!';
419              
420             __END__