File Coverage

blib/lib/TAP/Parser/SourceHandler/Worker.pm
Criterion Covered Total %
statement 96 139 69.0
branch 16 54 29.6
condition 3 11 27.2
subroutine 17 20 85.0
pod 7 7 100.0
total 139 231 60.1


line stmt bran cond sub pod time code
1             package TAP::Parser::SourceHandler::Worker;
2              
3 1     1   1047 use strict;
  1         2  
  1         41  
4 1     1   1353 use Getopt::Long;
  1         13522  
  1         6  
5 1     1   1191 use Sys::Hostname;
  1         1395  
  1         63  
6 1     1   1226 use IO::Socket::INET;
  1         18136  
  1         9  
7 1     1   1983 use IO::Select;
  1         1915  
  1         49  
8              
9 1     1   7 use vars (qw($VERSION @ISA));
  1         2  
  1         53  
10              
11 1     1   5 use TAP::Parser::SourceHandler ();
  1         3  
  1         16  
12 1     1   1107 use TAP::Parser::IteratorFactory ();
  1         1441  
  1         21  
13 1     1   671 use TAP::Parser::Iterator::Worker ();
  1         4  
  1         25  
14 1     1   1085 use TAP::Parser::SourceHandler::Perl ();
  1         6905  
  1         25  
15 1     1   704 use TAP::Parser::Iterator::Stream::Selectable ();
  1         3  
  1         1073  
16             @ISA = 'TAP::Parser::SourceHandler';
17              
18             TAP::Parser::IteratorFactory->register_handler(__PACKAGE__);
19              
20             =head1 NAME
21              
22             TAP::Parser::SourceHandler::Worker - Stream TAP from an L or a GLOB.
23              
24             =head1 VERSION
25              
26             Version 0.08
27              
28             =cut
29              
30             $VERSION = '0.08';
31              
32             =head3 C<@workers>
33              
34             Class static variable to keep track of workers.
35              
36             =cut
37              
38             my @workers = ();
39              
40             =head3 C<$number_of_workers>
41              
42             Class static variable to keep track of number of workers.
43              
44             =cut
45              
46             my $number_of_workers;
47              
48             =head3 C<$listener>
49              
50             Class static variable to store the worker listener.
51              
52             =cut
53              
54             my $listener;
55              
56             =head3 C<$use_local_public_ip>
57              
58             Class static variable to flag the local public ip is needed.
59             Some of the home network might not have name server setup. Therefore,
60             the public local ip is needed.
61              
62             =cut
63              
64             my $use_local_public_ip;
65              
66             =head3 C<$local_public_ip>
67              
68             Class static variable to store the local public ip is needed.
69             Some of the home network might not have name server setup. Therefore,
70             the public local ip is needed.
71              
72             =cut
73              
74             my $local_public_ip;
75              
76             =head3 C<$sync_type>
77              
78             Syncronize the source directory that will be used for testing to the remote
79             host with the directory specified on the variable C<$destination_dir>.
80              
81             Currently it only support syncronize type of C.
82              
83             =cut
84              
85             my $sync_type;
86              
87             =head3 C<$destination_dir>
88              
89             Syncronize the source to destination directory.
90              
91             If it is not specified, it will be created with L.
92              
93             =cut
94              
95             my $destination_dir;
96              
97             =head3 C
98              
99             my $vote = $class->can_handle( $source );
100              
101             Casts the following votes:
102              
103             Vote the same way as the L
104             but with 0.01 higher than perl source.
105              
106             =cut
107              
108             sub can_handle {
109 6     6 1 4254 my ( $class, $src ) = @_;
110 6         23 my $vote = TAP::Parser::SourceHandler::Perl->can_handle($src);
111 6 100       103 return 0 unless ($vote);
112 5 50       15 if ( $src->{config} ) {
113 5         5 my @config_keys = keys %{ $src->{config} };
  5         16  
114 5 50       16 if ( scalar(@config_keys) == 1 ) {
115              
116             #LSF: If it is detach, we just run everythings.
117 0 0       0 if ( $src->{config}->{ $config_keys[0] }->{detach} ) {
118 0         0 $vote = 0.90;
119             }
120             }
121             }
122              
123             #LSF: If it is a subclass, we will add 0.01 for each level of subclass.
124 5         9 my $package = __PACKAGE__;
125 5         7 my $tmp = $class;
126 5         40 $tmp =~ s/^$package//;
127 5         12 my @number = split '::', $tmp;
128              
129 5         29 return $vote + ( 1 + scalar(@number) ) * 0.01;
130             }
131              
132             =head1 SYNOPSIS
133              
134             =cut
135              
136             =head3 C
137              
138             my $iterator = $class->make_iterator( $source );
139              
140             Returns a new L for the source.
141              
142             =cut
143              
144             sub make_iterator {
145 1     1 1 1135 my ( $class, $source, $retry ) = @_;
146              
147 1         6 my $worker = $class->get_a_worker($source);
148              
149 1 50       167731 if ($worker) {
    0          
150 1         14 $worker->autoflush(1);
151 1         82 $worker->print( ${ $source->raw } . "\n" );
  1         163  
152 1         287 return TAP::Parser::Iterator::Stream::Selectable->new(
153             { handle => $worker } );
154             }
155             elsif ( !$retry ) {
156              
157             #LSF: Let check the worker.
158 0         0 my @active_workers = $class->get_active_workers();
159              
160             #unless(@active_workers) {
161             # die "failed to find any worker.\n";
162             #}
163 0         0 @workers = @active_workers;
164              
165             #LSF: Retry one more time.
166 0         0 return $class->make_iterator( $source, 1 );
167             }
168              
169             #LSF: Pass through everything now.
170 0         0 return;
171             }
172              
173             =head3 C
174              
175             my $worker = $class->get_a_worker();
176              
177             Returns a new workder L
178              
179             =cut
180              
181             sub get_a_worker {
182 1     1 1 3 my $class = shift;
183 1         2 my $source = shift;
184 1         3 my $package = __PACKAGE__;
185 1         2 my $tmp = $class;
186 1         18 $tmp =~ s/^$package//;
187 1         4 my $option_name = 'Worker' . $tmp;
188 1   50     10 $number_of_workers = $source->{config}->{$option_name}->{number_of_workers}
189             || 1;
190 1         3 my $startup = $source->{config}->{$option_name}->{start_up};
191 1         4 my $teardown = $source->{config}->{$option_name}->{tear_down};
192 1         3 my $error_log = $source->{config}->{$option_name}->{error_log};
193 1         5 my $detach = $source->{config}->{$option_name}->{detach};
194 1         3 my $sync_type = $source->{config}->{$option_name}->{sync_type};
195 1         4 my $source_dir = $source->{config}->{$option_name}->{source_dir};
196 1         2 my $destination_dir = $source->{config}->{$option_name}->{destination_dir};
197 1         3 my %args = ();
198 1 50       4 $args{start_up} = $startup if ($startup);
199 1 50       5 $args{tear_down} = $teardown if ($teardown);
200 1 50       3 $args{detach} = $detach if ($detach);
201 1 50       4 $args{sync_type} = $sync_type if ($sync_type);
202 1 50       4 $args{source_dir} = $source_dir if ($source_dir);
203 1 50       4 $args{destination_dir} = $destination_dir if ($destination_dir);
204 1 50       5 $args{error_log} = $error_log if ($error_log);
205 1         3 $args{switches} = $source->{switches};
206 1 50       4 $args{test_args} = $source->{test_args} if ( $source->{test_args} );
207              
208 1 50       6 if ( @workers < $number_of_workers ) {
209 1         5 my $listener = $class->listener;
210 1 50 33     6 if ( $use_local_public_ip && !$local_public_ip ) {
211 0         0 require Net::Address::IP::Local;
212 0         0 $local_public_ip = Net::Address::IP::Local->public;
213             }
214              
215 1   33     20 my $spec = (
216             $local_public_ip
217             || (
218             $listener->sockhost eq '0.0.0.0'
219             ? hostname
220             : $listener->sockhost
221             )
222             )
223             . ':'
224             . $listener->sockport;
225 1         120 my $iterator_class = $class->iterator_class;
226 1     1   10 eval "use $iterator_class;";
  1         2  
  1         26  
  1         108  
227 1         4 $args{spec} = $spec;
228 1         14 my $iterator = $class->iterator_class->new( \%args );
229 1         21 push @workers, $iterator;
230             }
231 1         45 return $listener->accept();
232             }
233              
234             =head3 C
235              
236             my $listener = $class->listener();
237              
238             Returns worker listener L
239              
240             =cut
241              
242             sub listener {
243 1     1 1 3 my $class = shift;
244 1 50       3 unless ($listener) {
245 1         13 $listener = IO::Socket::INET->new(
246             Listen => 5,
247             Proto => 'tcp',
248             Timeout => 40,
249             );
250             }
251 1         402 return $listener;
252             }
253              
254             =head3 C
255              
256             The class of iterator to use, override if you're sub-classing. Defaults
257             to L.
258              
259             =cut
260              
261 1     1   8 use constant iterator_class => 'TAP::Parser::Iterator::Worker';
  1         2  
  1         600  
262              
263             =head3 C
264              
265             Returns list of workers.
266              
267             =cut
268              
269             sub workers {
270 0     0 1   return @workers;
271             }
272              
273             =head3 C
274            
275             my @active_workers = $class->get_active_workers;
276              
277             Returns list of active workers.
278              
279             =cut
280              
281             sub get_active_workers {
282 0     0 1   my $class = shift;
283 0           my @workers = $class->workers;
284 0 0         return unless (@workers);
285 0           my @active;
286 0           for my $worker (@workers) {
287 0 0 0       next unless ( $worker && $worker->{sel} );
288 0           my @handles = $worker->{sel}->can_read();
289 0           for my $handle (@handles) {
290 0 0         if ( $handle == $worker->{err} ) {
291 0           my $error = '';
292 0 0         if ( $handle->read( $error, 640000 ) ) {
293 0           chomp($error);
294 0           print STDERR "Worker with error [$error].\n";
295              
296             #LSF: Close the handle.
297 0           $handle->close();
298 0           $worker = undef;
299 0           last;
300             }
301             }
302             }
303 0 0         push @active, $worker if ($worker);
304             }
305 0           return @active;
306             }
307              
308             =head3 C
309            
310             Setup the worker specific options.
311              
312             my @active_workers = $class->load_options($app_prove_object, \@ARGV);
313              
314             Returns boolean.
315              
316             =cut
317              
318             sub load_options {
319 0     0 1   my $class = shift;
320 0           my ( $app, $args ) = @_;
321             {
322 0           local @ARGV = @$args;
  0            
323 0           Getopt::Long::Configure(qw(no_ignore_case bundling pass_through));
324              
325             # Don't add coderefs to GetOptions
326 0 0         GetOptions(
327             'use-local-public-ip' => \$use_local_public_ip,
328             'sync-test-env=s' => \$sync_type,
329             'destination-dir=s' => \$destination_dir
330             ) or croak('Unable to continue');
331 0 0         if ($sync_type) {
332 0 0         if ( $sync_type eq 'rsync' ) {
333 0           require File::Rsync;
334 0 0         unless ($destination_dir) {
335 0           require File::Temp;
336 0           $destination_dir = File::Temp::tempdir( CLEANUP => 1 );
337             }
338              
339             #LSF: This might not support with different directory separator.
340 0 0         unless ( $destination_dir =~ /\/$/ ) {
341 0           $destination_dir .= '/';
342             }
343             }
344             else {
345 0           die "not able to sync on the remote with type "
346             . $sync_type
347             . ".\nCurrently, only the rsync type is supported.\n";
348             }
349             }
350             }
351 0           return 1;
352             }
353              
354             1;
355              
356             __END__