File Coverage

blib/lib/App/SmokeBox/Mini.pm
Criterion Covered Total %
statement 224 284 78.8
branch 50 118 42.3
condition 13 62 20.9
subroutine 36 40 90.0
pod 1 1 100.0
total 324 505 64.1


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