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.66';
3             #ABSTRACT: the guts of the minismokebox command
4              
5 10     10   155344 use strict;
  10         14  
  10         266  
6 10     10   35 use warnings;
  10         15  
  10         246  
7 10     10   5470 use Pod::Usage;
  10         398297  
  10         1298  
8 10     10   4983 use Config::Tiny;
  10         7367  
  10         259  
9 10     10   49 use File::Spec;
  10         15  
  10         203  
10 10     10   33 use File::Path qw[mkpath];
  10         15  
  10         532  
11 10     10   39 use Cwd;
  10         10  
  10         447  
12 10     10   6108 use Getopt::Long;
  10         75419  
  10         49  
13 10     10   6121 use Time::Duration qw(duration_exact);
  10         13745  
  10         617  
14 10     10   4042 use Module::Pluggable search_path => ['App::SmokeBox::Mini::Plugin'];
  10         83026  
  10         62  
15 10     10   5039 use Module::Load;
  10         7923  
  10         48  
16 10     10   459 use if ( $^O eq 'linux' ), 'POE::Kernel', { loop => 'POE::XS::Loop::EPoll' };
  10         13  
  10         57  
17 10     10   666579 use unless ( $^O =~ /^(?:linux|MSWin32|darwin)$/ ), 'POE::Kernel', { loop => 'POE::XS::Loop::Poll' };
  10         110  
  10         84  
18 10     10   481 use if ( scalar grep { $^O eq $_ } qw(MSWin32 darwin) ), 'POE::Kernel', { loop => 'POE::Loop::Event' };
  10         15  
  10         19  
  20         104  
19 10     10   4684 use POE;
  10         4477  
  10         47  
20 10     10   41551 use POE::Component::SmokeBox;
  10         479680  
  10         293  
21 10     10   69 use POE::Component::SmokeBox::Smoker;
  10         13  
  10         139  
22 10     10   33 use POE::Component::SmokeBox::Job;
  10         10  
  10         1965  
23 10     10   5615 use POE::Component::SmokeBox::Dists;
  10         826398  
  10         267  
24 10     10   4733 use POE::Component::SmokeBox::Recent;
  10         785097  
  10         538  
25 10     10   4341 use App::SmokeBox::PerlVersion;
  10         8544  
  10         284  
26              
27 10     10   60 use constant CPANURL => 'ftp://cpan.cpantesters.org/CPAN/';
  10         18  
  10         25027  
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   32775 && 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   413 my $smokebox_dir = File::Spec->catdir( _smokebox_dir(), '.smokebox' );
49 3 50       62 return unless -d $smokebox_dir;
50 3         30 my $conf_file = File::Spec->catfile( $smokebox_dir, 'minismokebox' );
51 3 50       40 return unless -e $conf_file;
52 3         49 my $Config = Config::Tiny->read( $conf_file );
53 3         502 my @config;
54 3 50       19 if ( defined $Config->{_} ) {
55 3         9 my $root = delete $Config->{_};
56 3         13 @config = map { $_, $root->{$_} } grep { exists $root->{$_} }
  12         40  
  36         45  
57             qw(debug perl indices recent backend url home nolog rss random noepoch perlenv);
58             }
59 3 100       8 push @config, 'sections', $Config if scalar keys %{ $Config };
  3         22  
60 3         33 return @config;
61             }
62              
63             sub _read_ts_data {
64 2     2   7 my $timestamp = File::Spec->catfile( _smokebox_dir(), '.smokebox', 'timestamp' );
65 2         5 my %data;
66 2 50       56 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       7 return %data if wantarray;
78 2         15 return \%data;
79             }
80              
81             sub _get_jobs_from_file {
82 3   50 3   177 my $jobs = shift || return;
83 3 50       67 unless ( open JOBS, "< $jobs" ) {
84 0         0 warn "Could not open '$jobs' '$!'\n";
85 0         0 return;
86             }
87 3         12 my @jobs;
88 3         48 while () {
89 15         15 chomp;
90 15         38 push @jobs, $_;
91             }
92 3         16 close JOBS;
93 3         16 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 1879 my $package = shift;
109 2         17 my %config = _read_config();
110 2         3 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       89 ) or pod2usage(2);
132              
133 2 50       1974 _display_version() if $version;
134              
135 2 50 33     18 $config{perl} = $^X unless $config{perl} and -e $config{perl};
136 2 50       12 $ENV{PERL5_SMOKEBOX_DEBUG} = 1 if $config{debug};
137 2         17 $ENV{AUTOMATED_TESTING} = 1; # We need this because some backends do not set it.
138 2         11 $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     42 if ( $config{jobs} and -e $config{jobs} ) {
142 2         12 my @jobs = _get_jobs_from_file( $config{jobs} );
143 2 50       8 $config{jobs} = \@jobs if scalar @jobs;
144             }
145              
146 2   50     18 my $env = delete $config{sections}->{ENVIRONMENT} || { };
147              
148 2         349 print "Running minismokebox with options:\n";
149             printf("%-20s %s\n", $_, $config{$_})
150 2         12 for grep { defined $config{$_} } qw(debug indices perl jobs backend author package
  30         559  
151             phalanx reverse url home nolog random noepoch perlenv);
152 2 50       6 if ( keys %{ $env } ) {
  2         22  
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     12 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     10 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         11 my $self = bless \%config, $package;
167              
168 2         7 $self->{_tsdata} = _read_ts_data();
169              
170 2         7 $self->{env} = $env;
171 2 50       6 $self->{env}->{HOME} = $self->{home} if $self->{home};
172             $self->{env}->{PERL5LIB} = $ENV{PERL5LIB}
173 2 0 33     9 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         42  
180             ),
181             ],
182             );
183              
184 2         5567 $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         160 $poe_kernel->run();
193 2         2353 return 1;
194             }
195              
196             sub _start {
197 2     2   323 my ($kernel,$self) = @_[KERNEL,OBJECT];
198 2         8 $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       18 ( $self->{backend} ? ( type => $self->{backend} ) : () ),
203             command => 'check',
204             ),
205             );
206             $self->{stats} = {
207 2         1716 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         19 foreach my $plugin ( $self->plugins() ) {
218 0         0 load $plugin;
219 0         0 $plugin->init( $self->{sections} );
220             }
221 2         639 return;
222             }
223              
224             sub _child {
225 4     4   158611 my ($kernel,$self,$reason,$child) = @_[KERNEL,OBJECT,ARG0,ARG1];
226 4 100       42 return unless $reason eq 'create';
227 2         8 push @{ $self->{_sessions} }, $child->ID();
  2         46  
228 2         41 $kernel->detach_child( $child );
229 2         157 return;
230             }
231              
232             sub _stop {
233 2     2   363 my ($kernel,$self) = @_[KERNEL,OBJECT];
234 2         19 $kernel->call( $self->{sbox}->session_id(), 'shutdown' );
235 2         383 my $finish = time();
236 2         24 my $cumulative = duration_exact( $finish - $self->{stats}->{started} );
237 2         111 my @stats = map { $self->{stats}->{$_} } qw(totaljobs idle excess avg_run min_run max_run);
  12         22  
238 2         5 $kernel->call( $_, 'sbox_stop', $self->{stats}->{started}, $finish, @stats ) for @{ $self->{_sessions} };
  2         18  
239 2         63 $stats[$_] = duration_exact( $stats[$_] ) for 3 .. 5;
240 2         565 print "minismokebox started at: \t", scalar localtime($self->{stats}->{started}), "\n";
241 2         225 print "minismokebox finished at: \t", scalar localtime($finish), "\n";
242 2         154 print "minismokebox ran for: \t", $cumulative, "\n";
243 2         206 print "minismokebox tot jobs:\t", $stats[0], "\n";
244 2 50       14 print "minismokebox idle kills:\t", $stats[1], "\n" if $stats[1];
245 2 50       8 print "minismokebox excess kills:\t", $stats[2], "\n" if $stats[2];
246 2         140 print "minismokebox avg run: \t", $stats[3], "\n";
247 2         205 print "minismokebox min run: \t", $stats[4], "\n";
248 2         152 print "minismokebox max run: \t", $stats[5], "\n";
249 2 50       13 return if $self->{noepoch};
250 2         17 my $smokebox_dir = File::Spec->catdir( _smokebox_dir(), '.smokebox' );
251 2 50       41 mkpath( $smokebox_dir ) unless -d $smokebox_dir;
252             {
253 2         6 $self->{_tsdata}->{ $self->{_tsprefix} } = $self->{stats}->{started};
  2         11  
254 2 50       180 open my $ts, '>', File::Spec->catfile( $smokebox_dir, 'timestamp' ) or die "Could not open 'timestamp': $!\n";
255 2         6 print {$ts} join('', $_, $self->{_tsdata}->{$_} ), "\n" for sort keys %{ $self->{_tsdata} };
  2         11  
  2         16  
256 2         108 close $ts;
257             }
258 2         14 return;
259             }
260              
261             sub _check {
262 2     2   58239 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
263 2         14 my ($result) = $data->{result}->results;
264 2 50       20 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         17 event => '_perl_version',
272             session => $_[SESSION]->postback( '_perl_version' ),
273             );
274 2         175 return;
275             }
276              
277             sub _perl_version {
278 2     2   18908 my ($kernel,$self,$args) = @_[KERNEL,OBJECT,ARG1];
279 2         5 my $data = shift @{$args};
  2         5  
280 2         5 my ($version,$archname,$osvers) = @{ $data }{qw(version archname osvers)};
  2         6  
281 2 50 33     31 if ( $version and $archname and $osvers ) {
      33        
282 2         607 print "Perl Version: $version\nArchitecture: $archname\nOS Version: $osvers\n";
283 2         7 $kernel->post( $_, 'sbox_perl_info', $version, $archname, $osvers ) for @{ $self->{_sessions} };
  2         20  
284 2         127 $self->{_perlinfo} = [ $version, $archname ];
285 2         12 $self->{_tsprefix} = "[$version$archname]";
286 2 50       15 $self->{_epoch} = $self->{_tsdata}->{ $self->{_tsprefix} } unless $self->{noepoch};
287             }
288 2 50       11 if ( $self->{indices} ) {
289             $kernel->post( $self->{sbox}->session_id(), 'submit', event => '_indices', job =>
290             POE::Component::SmokeBox::Job->new(
291 2 50       16 ( $self->{backend} ? ( type => $self->{backend} ) : () ),
292             command => 'index',
293             ),
294             );
295 2         2099 return;
296             }
297 0         0 $kernel->yield( '_search' );
298 0         0 return;
299             }
300              
301             sub _indices {
302 2     2   23437 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
303 2         47 my ($result) = $data->{result}->results;
304 2 50       19 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         10 $kernel->yield( '_search' );
310 2         64 return;
311             }
312              
313             sub _search {
314 2     2   329 my ($kernel,$self) = @_[KERNEL,OBJECT];
315 2 50 33     29 if ( $self->{jobs} and ref $self->{jobs} eq 'ARRAY' ) {
316 2         3 foreach my $distro ( @{ $self->{jobs} } ) {
  2         11  
317 10         7101 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       60 ( $self->{nolog} ? ( no_log => 1 ) : () ),
    50          
324             ),
325             );
326             }
327             }
328 2 50       1458 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       106 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       9 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       9 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       8 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     44 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   50535801 my ($kernel,$self,$data) = @_[KERNEL,OBJECT,ARG0];
401 10         89 my $dist = $data->{job}->module();
402 10         768 my ($result) = $data->{result}->results;
403 10         2426 print "Distribution: '$dist' finished with status '$result->{status}'\n";
404 10         29 $kernel->post( $_, 'sbox_smoke', $data ) for @{ $self->{_sessions} };
  10         89  
405 10         459 my $run_time = $result->{end_time} - $result->{start_time};
406 10 100       70 $self->{stats}->{max_run} = $run_time if $run_time > $self->{stats}->{max_run};
407 10 100       47 $self->{stats}->{min_run} = $run_time if $self->{stats}->{min_run} == 0;
408 10 50       80 $self->{stats}->{min_run} = $run_time if $run_time < $self->{stats}->{min_run};
409 10         24 $self->{stats}->{_sum} += $run_time;
410 10         24 $self->{stats}->{totaljobs}++;
411 10         45 $self->{stats}->{avg_run} = $self->{stats}->{_sum} / $self->{stats}->{totaljobs};
412 10 50       34 $self->{stats}->{idle}++ if $result->{idle_kill};
413 10 50       32 $self->{stats}->{excess}++ if $result->{excess_kill};
414 10         26 $self->{_jobs}--;
415 10         47 return;
416             }
417              
418             'smoke it!';
419              
420             __END__