File Coverage

blib/lib/Test/Clustericious/Command.pm
Criterion Covered Total %
statement 168 222 75.6
branch 30 64 46.8
condition 6 22 27.2
subroutine 35 42 83.3
pod 0 8 0.0
total 239 358 66.7


line stmt bran cond sub pod time code
1             package Test::Clustericious::Command;
2              
3 13     13   5887 use strict;
  13         137  
  13         472  
4 13     13   74 use warnings;
  13         25  
  13         320  
5 13     13   285 use 5.010001;
  13         48  
6 13     13   3947 use Test2::Plugin::FauxHomeDir;
  13         807198  
  13         94  
7 13     13   19777 use File::Glob qw( bsd_glob );
  13         34  
  13         1222  
8 13     13   90 use base qw( Exporter );
  13         33  
  13         1681  
9 13     13   93 use Exporter qw( import );
  13         38  
  13         446  
10 13     13   3533 use Mojo::Loader;
  13         1552882  
  13         844  
11 13     13   4389 use Path::Class qw( file dir );
  13         206247  
  13         860  
12 13     13   8665 use Env qw( @PERL5LIB @PATH );
  13         24429  
  13         84  
13 13     13   7107 use Capture::Tiny qw( capture );
  13         64386  
  13         818  
14 13     13   4137 use File::Which qw( which );
  13         11330  
  13         722  
15 13     13   92 use File::Glob qw( bsd_glob );
  13         24  
  13         15589  
16 13     13   3361 use YAML::XS ();
  13         33614  
  13         380  
17 13     13   94 use File::Temp qw( tempdir );
  13         29  
  13         736  
18 13     13   82 use Test2::API qw( context );
  13         24  
  13         24228  
19              
20             # ABSTRACT: Test Clustericious commands
21             our $VERSION = '1.27'; # VERSION
22              
23              
24             our @EXPORT = qw( extract_data mirror requires run_ok generate_port note_file clean_file create_symlink );
25             our @EXPORT_OK = @EXPORT;
26             our %EXPORT_TAGS = ( all => \@EXPORT );
27              
28             unshift @INC, dir(bsd_glob '~/lib')->stringify;
29             unshift @PERL5LIB, map { dir($_)->absolute->stringify } @INC;
30             unshift @PATH, dir(bsd_glob '~/bin')->stringify;
31              
32             sub _can_execute_in_tmp
33             {
34 11     11   89 my $script = file( tempdir( CLEANUP => 1 ), 'mytest' );
35 11         9977 $script->spew("#!$^X\nexit 0");
36 11         2876 chmod 0755, "$script";
37 11         431 my $exit;
38 11     11   323 capture { system "$script", "okay"; $exit = $? };
  11         10118  
  11         51000  
39 11         7779 $exit == 0;
40             }
41              
42             sub requires
43             {
44 11     11 0 1704 my($command, $num) = @_;
45              
46 11         47 my $ctx = context();
47 11 50       1272 $ctx->plan( 0, 'SKIP', 'test requires execute in tmp') unless __PACKAGE__->_can_execute_in_tmp;
48              
49 11 100       92 unless(defined $command)
50             {
51 8 50       118 $ctx->plan( $num ) if defined $num;
52 8         5621 $ctx->release;
53 8         344 return;
54             }
55              
56 3 50       45 if($command =~ /^(.*)\.conf$/)
57             {
58 3         20 my $name = $1;
59 3 50 33     27 if(defined $ENV{CLUSTERICIOUS_COMMAND_TEST} && -r $ENV{CLUSTERICIOUS_COMMAND_TEST})
60             {
61             my $config = do {
62 0         0 require Clustericious::Config;
63 0         0 my $config = Clustericious::Config->new($ENV{CLUSTERICIOUS_COMMAND_TEST});
64 0         0 my %config = %$config;
65 0         0 \%config;
66 0         0 }->{$name};
67 0 0       0 $ctx->plan( 0, 'SKIP', "developer test not configured" ) unless defined $config;
68            
69 0 0       0 unshift @PATH, $config->{path} if defined $config->{path};
70 0         0 unshift @PATH, dir(bsd_glob '~/bin')->stringify;
71 0         0 $ENV{$_} = $config->{env}->{$_} for keys %{ $config->{env} };
  0         0  
72 0   0     0 $command = $config->{exe} // $name;
73             }
74             else
75             {
76 3         35 $ctx->plan( 0, 'SKIP', "developer only test" );
77             }
78             }
79              
80 0 0       0 if(which $command)
81             {
82 0 0       0 $ctx->plan( $num ) if defined $num;
83             }
84             else
85             {
86 0         0 $ctx->plan( 0, 'SKIP', "test requires $command to be in the PATH" );
87             }
88 0         0 $ctx->release;
89             }
90              
91             sub extract_data
92             {
93 7     7 0 233 my(@values) = @_;
94 7         31 my $caller = caller;
95 7 50       54 Mojo::Loader::load_class($caller) unless $caller eq 'main';
96 7         71 my $all = Mojo::Loader::data_section $caller;
97            
98 7         1156 my $ctx = context();
99            
100 7         860 foreach my $name (sort keys %$all)
101             {
102 18         1329 my $file = file(bsd_glob('~'), $name);
103 18         2121 my $dir = $file->parent;
104 18 100       145 unless(-d $dir)
105             {
106 13         405 $ctx->note("[extract] DIR $dir");
107 13         4517 $dir->mkpath(0,0700);
108             }
109 18 50       2151 unless(-f $file)
110             {
111 18 100       823 $ctx->note("[extract] FILE $file@{[ $name =~ m{^bin/} ? ' (*)' : '']}");
  18         793  
112            
113 18 100       4753 if($name =~ m{^bin/})
114             {
115 9         31 my $content = $all->{$name};
116 9         65 $content =~ s{^#!/usr/bin/perl}{#!$^X};
117 9         66 $file->spew($content);
118 9         1997 chmod 0700, "$file";
119             }
120             else
121             {
122 9         48 $file->spew($all->{$name});
123             }
124             }
125             }
126            
127 7         1182 $ctx->release;
128             }
129              
130             sub mirror
131             {
132 8 50   8 0 324 my($src, $dst) = map { ref($_) ? $_ : dir($_) } @_;
  16         497  
133            
134 8         317 my $ctx = context();
135              
136 8 50       731 $dst = dir(bsd_glob('~'), $dst) unless $dst->is_absolute;
137            
138 8 100       851 unless(-d $dst)
139             {
140 3         105 $ctx->note("[mirror ] DIR $dst");
141 3         1019 $dst->mkpath(0,0700);
142             }
143            
144 8         649 foreach my $child ($src->children)
145             {
146 20 50       4107 if($child->is_dir)
147             {
148 0         0 mirror($child, $dst->subdir($child->basename));
149             }
150             else
151             {
152 20         102 my $dst = $dst->file($child->basename);
153 20 50       1344 unless(-f $dst)
154             {
155 20 100       796 if(-x $child)
156             {
157 5         196 $ctx->note("[mirror ] FILE $dst (*)");
158 5         1662 my $content = scalar $child->slurp;
159 5         902 $content =~ s{^#!/usr/bin/perl}{#!$^X};
160 5         33 $dst->spew($content);
161 5         858 chmod 0700, "$dst";
162             }
163             else
164             {
165 15         484 $ctx->note("[mirror ] FILE $dst");
166 15         4784 $dst->spew(scalar $child->slurp);
167 15         4875 chmod 0600, "$dst";
168             }
169             }
170             }
171             }
172            
173 8         360 $ctx->release;
174             }
175              
176             sub run_ok
177             {
178 31     31 0 29191 my(@cmd) = @_;
179            
180             # Yath set some environment variables which confuse a subprocess
181             # for when we are testing the use of prove, etc
182 31         2358 local %ENV = %ENV;
183 31         483 delete $ENV{$_} for grep /^T2_/, keys %ENV;
184            
185 31     31   1728 my($out, $err, $error, $exit) = capture { system @cmd; ($!,$?) };
  31         23817239  
  31         2230  
186            
187 31   33     51589 my $ok = ($exit != -1) && ! ($exit & 128);
188            
189 31         428 my $ctx = context();
190            
191 31         6393 $ctx->ok($ok, "run: @cmd");
192 31 50       11558 $ctx->diag(" @cmd failed") unless $ok;
193 31 50       137 $ctx->diag(" - execute failed: $error") if $exit == -1;
194 31 50       151 $ctx->diag(" - died from signal: " . ($exit & 128)) if $exit & 128;
195              
196 31         851 my $run = Test::Clustericious::Command::Run->new(
197             cmd => \@cmd,
198             out => $out, err => $err, exit => $exit >> 8,
199             );
200            
201 31         289 $ctx->release;
202            
203 31         1223 $run;
204             }
205              
206             sub generate_port
207             {
208 0     0 0 0 require IO::Socket::INET;
209 0         0 IO::Socket::INET->new(Listen => 5, LocalAddr => "127.0.0.1")->sockport;
210             }
211              
212             sub note_file
213             {
214 0     0 0 0 my $ctx = context();
215 0         0 foreach my $file (sort map { file $_ } map { bsd_glob "~/$_" } @_)
  0         0  
  0         0  
216             {
217 0         0 $ctx->note("[content] $file");
218 0         0 $ctx->note(scalar $file->slurp);
219             }
220 0         0 $ctx->release;
221             }
222              
223             sub clean_file
224             {
225 0     0 0 0 foreach my $file (sort map { file $_ } map { bsd_glob "~/$_" } @_)
  0         0  
  0         0  
226             {
227 0         0 $file->remove;
228             }
229             }
230              
231             sub create_symlink
232             {
233 0     0 0 0 my($old,$new) = map { file(bsd_glob('~'), $_) } @_;
  0         0  
234 0 0       0 $new->remove if -f $new;
235 0         0 my $ctx = context();
236 0         0 $ctx->note("[symlink] $old => $new");
237 0         0 $ctx->release;
238 13     13   3663 use autodie;
  13         155200  
  13         106  
239 0         0 symlink "$old", "$new";
240 0         0 %Clustericious::Config::singletons = ();
241             }
242              
243             package Test::Clustericious::Command::Run;
244              
245 13     13   101506 use Test2::API qw( context );
  13         51  
  13         8674  
246              
247             sub new
248             {
249 31     31   351 my($class, %args) = @_;
250 31         166 bless \%args, $class;
251             }
252              
253 0   0 0   0 sub cmd { @{ shift->{cmd} // [] } }
  0         0  
254 73     73   766 sub out { shift->{out} }
255 46     46   303 sub err { shift->{err} }
256 62     62   440 sub exit { shift->{exit} }
257              
258             sub exit_is
259             {
260 31     31   2869 my($self, $value, $name) = @_;
261 31   33     345 $name //= "exit with $value";
262 31         111 my $ctx = context();
263 31         2580 $ctx->ok($self->exit eq $value, $name);
264 31 50       5830 unless($self->exit == $value)
265             {
266 0 0       0 $ctx->diag("[cmd]\n", join(' ', $self->cmd)) if $self->cmd;
267 0 0       0 $ctx->diag("[out]\n", $self->out) if $self->out;
268 0 0       0 $ctx->diag("[err]\n", $self->err) if $self->err;
269             }
270 31         153 $ctx->release;
271 31         892 $self;
272             }
273              
274             sub note
275             {
276 29     29   393 my($self) = @_;
277 29         107 my $ctx = context();
278 29 100       2370 $ctx->note("[out]\n" . $self->out) if $self->out;
279 29 100       11053 $ctx->note("[err]\n" . $self->err) if $self->err;
280 29         4403 $ctx->release;
281 29         765 $self;
282             }
283              
284             sub diag
285             {
286 0     0   0 my($self) = @_;
287 0         0 my $ctx = context();
288 0 0       0 $ctx->diag("[out]\n" . $self->out) if $self->out;
289 0 0       0 $ctx->diag("[err]\n" . $self->err) if $self->err;
290 0         0 $ctx->release;
291 0         0 $self;
292             }
293              
294             sub out_like
295             {
296 17     17   304 my($self, $pattern, $name) = @_;
297              
298 17         62 my $ctx = context();
299 17   50     1415 $name ||= "output matches";
300 17         72 $ctx->ok($self->out =~ $pattern, $name);
301 17         3082 $ctx->release;
302              
303 17         491 $self;
304             }
305              
306             sub out_unlike
307             {
308 1     1   23 my($self, $pattern, $name) = @_;
309              
310 1         6 my $ctx = context();
311 1   50     114 $name ||= "output does not match";
312 1         4 $ctx->ok($self->out !~ $pattern, $name);
313 1         126 $ctx->release;
314              
315 1         36 $self;
316             }
317              
318             sub err_like
319             {
320 6     6   153 my($self, $pattern, $name) = @_;
321              
322 6         31 my $ctx = context();
323 6   50     463 $name ||= "error matches";
324 6         38 $ctx->ok($self->err =~ $pattern, $name);
325 6         1134 $ctx->release;
326              
327 6         158 $self;
328             }
329              
330             sub err_unlike
331             {
332 0     0   0 my($self, $pattern, $name) = @_;
333              
334 0         0 my $ctx = context();
335 0   0     0 $name ||= "error does not match";
336 0         0 $ctx->unlike($self->err, $pattern, $name);
337 0         0 $ctx->release;
338            
339 0         0 $self;
340             }
341              
342             sub tap
343             {
344 4     4   87 my($self, $sub) = @_;
345 4         21 $sub->($self);
346 4         8018 $self;
347             }
348              
349             1;
350              
351             __END__
352              
353             =pod
354              
355             =encoding UTF-8
356              
357             =head1 NAME
358              
359             Test::Clustericious::Command - Test Clustericious commands
360              
361             =head1 VERSION
362              
363             version 1.27
364              
365             =head1 SYNOPSIS
366              
367             use Test::Clustericious::Command;
368              
369             =head1 DESCRIPTION
370              
371             This is currently a private module used internally by L<Clustericious>. This may change in the future,
372             but for now you should not depend on it providing any functionality.
373              
374             =head1 AUTHOR
375              
376             Original author: Brian Duggan
377              
378             Current maintainer: Graham Ollis E<lt>plicease@cpan.orgE<gt>
379              
380             Contributors:
381              
382             Curt Tilmes
383              
384             Yanick Champoux
385              
386             =head1 COPYRIGHT AND LICENSE
387              
388             This software is copyright (c) 2013 by NASA GSFC.
389              
390             This is free software; you can redistribute it and/or modify it under
391             the same terms as the Perl 5 programming language system itself.
392              
393             =cut