File Coverage

inc/My/Module/Test.pm
Criterion Covered Total %
statement 108 193 55.9
branch 19 86 22.0
condition 2 23 8.7
subroutine 26 35 74.2
pod 11 14 78.5
total 166 351 47.2


line stmt bran cond sub pod time code
1             package My::Module::Test;
2              
3 4     4   550184 use 5.006002;
  4         19  
4              
5 4     4   30 use strict;
  4         10  
  4         150  
6 4     4   21 use warnings;
  4         9  
  4         302  
7              
8 4     4   23 use Exporter;
  4         9  
  4         388  
9              
10             our @ISA = qw{ Exporter };
11              
12 4     4   31 use HTTP::Date;
  4         8  
  4         447  
13 4     4   30 use HTTP::Status qw{ :constants };
  4         7  
  4         2516  
14 4     4   39 use Test::More 0.96; # For subtest
  4         103  
  4         38  
15              
16             our $VERSION = '0.180';
17              
18             # Set the following to zero if Space Track (or any other SSL host)
19             # starts using a certificate that can not be verified.
20             use constant VERIFY_HOSTNAME => defined $ENV{SPACETRACK_VERIFY_HOSTNAME}
21             ? $ENV{SPACETRACK_VERIFY_HOSTNAME}
22 4 50   4   1778 : 0;
  4         12  
  4         627  
23              
24             our @EXPORT = ## no critic (ProhibitAutomaticExportation)
25             qw{
26             is_error
27             is_error_or_skip
28             is_not_success
29             is_success
30             is_success_or_skip
31             last_modified
32             most_recent_http_response
33             not_defined
34             site_check
35             spacetrack_user
36             spacetrack_skip_no_prompt
37             skip_site
38             throws_exception
39             VERIFY_HOSTNAME
40             };
41              
42 4     4   70 use constant HASH_REF => ref {};
  4         7  
  4         399  
43 4     4   27 use constant REGEXP_REF => ref qr{};
  4         8  
  4         436  
44              
45 4     4   30 use constant NO_SPACE_TRACK_ACCOUNT => 'No Space-Track account provided';
  4         7  
  4         5894  
46              
47             # Deliberately not localized, to prevent unwanted settings from sneaking
48             # in from the user's identity file.
49             $Astro::SpaceTrack::SPACETRACK_IDENTITY_KEY = {
50             map { $_ => 1 } qw{ username password } };
51              
52             my $rslt;
53              
54             sub is_error { ## no critic (RequireArgUnpacking)
55 1     1 1 4 my ( $obj, $method, @args ) = @_;
56 1         3 my ( $code, $name ) = splice @args, -2, 2;
57 1         2 $rslt = eval { $obj->$method( @args ) };
  1         6  
58 1 50       41 $rslt or do {
59 0         0 @_ = ( "$name threw exception: $@" );
60 0         0 goto \&fail;
61             };
62 1         3 @_ = ( $rslt->code() == $code, $name );
63 1         10 goto &ok;
64             }
65              
66             sub is_error_or_skip { ## no critic (RequireArgUnpacking)
67 1     1 1 547 my ( $obj, $method, @args ) = @_;
68 1         2 local $Test::Builder::Level = $Test::Builder::Level + 1;
69 1         3 my ( $code, $name ) = splice @args, -2, 2;
70 1         2 $rslt = eval { $obj->$method( @args ) };
  1         6  
71 1 50       63 $rslt
72             or return fail "$name threw exception: $@";
73 1         7 my $got = $rslt->code();
74 1         19 __skip_if_server_error( $method, $got );
75 1         9 return cmp_ok $got, '==', $code, $name;
76             }
77              
78             sub is_not_success { ## no critic (RequireArgUnpacking)
79 1     1 1 4 my ( $obj, $method, @args ) = @_;
80 1         4 my $name = pop @args;
81 1         3 $rslt = eval { $obj->$method( @args ) };
  1         15  
82 1 50       64 $rslt or do {
83 0         0 @_ = ( "$name threw exception: $@" );
84 0         0 goto \&fail;
85             };
86 1         4 @_ = ( ! $rslt->is_success(), $name );
87 1         13 goto &ok;
88             }
89              
90             sub is_success { ## no critic (RequireArgUnpacking)
91 4     4 1 724 my ( $obj, $method, @args ) = @_;
92 4         10 my $name = pop @args;
93 4         25 $rslt = eval { $obj->$method( @args ) }
94 4 50       12 or do {
95 0         0 @_ = ( "$name threw exception: $@" );
96 0         0 chomp $_[0];
97 0         0 goto \&fail;
98             };
99 4 50       161 $rslt->is_success() or $name .= ": " . $rslt->status_line();
100 4         41 @_ = ( $rslt->is_success(), $name );
101 4         52 goto &ok;
102             }
103              
104             sub is_success_or_skip { ## no critic (RequireArgUnpacking)
105 27     27 0 22864 my ( $obj, $method, @args ) = @_;
106 27         128 local $Test::Builder::Level = $Test::Builder::Level + 1;
107 27         67 my $skip = pop @args;
108 27 50       174 $skip =~ m/ [^0-9] /smx
109             and fail "Skip number '$skip' not numeric";
110 27         57 my $name = pop @args;
111 27 50       53 $rslt = eval { $obj->$method( @args ) } or do {
  27         243  
112 0         0 fail "$name threw exception: $!" ;
113 0         0 skip "$method() threw exception", $skip;
114             };
115 27         221 __skip_if_server_error( $method, $rslt->code(), $skip );
116             ok $rslt->is_success(), $name
117 27 100       79 or do {
118 1         2162 diag $rslt->status_line();
119 1         942 skip "$method() failed", $skip;
120             };
121 26         12318 return 1;
122             }
123              
124              
125             sub last_modified {
126 0 0   0 1 0 $rslt
127             or return;
128 0         0 foreach my $hdr ( $rslt->header( 'Last-Modified' ) ) {
129 0         0 return str2time( $hdr );
130             }
131 0         0 return;
132             }
133              
134             sub most_recent_http_response {
135 1     1 1 3 return $rslt;
136             }
137              
138             sub not_defined {
139 7     7 1 28 @_ = ( ! defined $_[0], @_[1 .. $#_] );
140 7         18 goto &ok;
141             }
142              
143             # Prompt the user. DO NOT call this if $ENV{AUTOMATED_TESTING} is set.
144              
145             {
146             my ( $set_read_mode, $readkey_loaded );
147              
148             BEGIN {
149             eval {
150 4         657 require Term::ReadKey;
151 0         0 $set_read_mode = Term::ReadKey->can( 'ReadMode' );
152 0         0 $readkey_loaded = 1;
153 0         0 1;
154 4 50   4   19 } or $set_read_mode = sub {};
155              
156 4         15 local $@ = undef;
157 4         9 eval { ## no critic (RequireCheckingReturnValueOfEval)
158 4         20 require IO::Handle;
159 4         59 STDERR->autoflush( 1 );
160             };
161             }
162              
163             sub prompt {
164 0     0 0 0 my @args = @_;
165 0 0       0 my $opt = HASH_REF eq ref $args[0] ? shift @args : {};
166             $readkey_loaded
167             or not $opt->{password}
168 0 0 0     0 or push @args, '(ECHOED)';
169 0         0 print STDERR "@args: ";
170             # We're a test, and we're trying to be lightweight.
171             $opt->{password}
172 0 0       0 and $set_read_mode->( 2 );
173 0         0 my $input = ; ## no critic (ProhibitExplicitStdin)
174 0 0       0 if ( $opt->{password} ) {
175 0         0 $set_read_mode->( 0 );
176 0 0       0 $readkey_loaded
177             and print STDERR "\n\n";
178             }
179 0 0       0 defined $input
180             and chomp $input;
181 0         0 return $input;
182             }
183              
184             }
185              
186              
187             # Determine whether a given web site is to be skipped.
188              
189             {
190             my %info;
191             my %skip_site;
192             BEGIN {
193 4     4   2616 %info = (
194             'celestrak.org' => {
195             url => 'https://celestrak.org/',
196             },
197             'mike.mccants' => {
198             # url => 'http://www.prismnet.com/~mmccants/',
199             url => 'https://www.mmccants.org/',
200             },
201             'rod.sladen' => {
202             url => 'http://www.rod.sladen.org.uk/iridium.htm',
203             },
204             'www.amsat.org' => {
205             url => 'https://www.amsat.org/',
206             },
207             'www.space-track.org' => {
208             url => 'https://www.space-track.org/',
209             check => \&__spacetrack_skip,
210             }
211             );
212              
213 4 50       1708 if ( defined $ENV{ASTRO_SPACETRACK_SKIP_SITE} ) {
214 0         0 foreach my $site ( split qr{ \s* , \s* }smx,
215             $ENV{ASTRO_SPACETRACK_SKIP_SITE} ) {
216             exists $info{$site}{url}
217 0 0       0 and $skip_site{$site} = "$site skipped by user request";
218             }
219             }
220             }
221              
222             sub __site_to_check_uri {
223 2     2   6 my ( $site ) = @_;
224 2         31 return $info{$site}{url};
225             }
226              
227             sub __site_codes {
228 0     0   0 return sort keys %info;
229             }
230              
231             my $ua;
232              
233             sub set_skip {
234 0     0 1 0 my ( $site, $skip ) = @_;
235             exists $info{$site}{url}
236 0 0       0 or die "Programming error. '$site' unknown";
237 0         0 $skip_site{$site} = $skip;
238 0         0 return;
239             }
240              
241             sub site_check {
242 2     2 1 326266 my @sites = @_;
243 2 50       9 my @rslt = grep { defined $_ } map { _site_check( $_ ) } @sites
  2         24  
  2         7  
244             or return;
245 0         0 return join '; ', @rslt;
246             }
247              
248             sub _site_check {
249 2     2   7 my ( $site ) = @_;
250 2 50       11 exists $skip_site{$site} and return $skip_site{$site};
251 2 50       9 my $url = __site_to_check_uri( $site ) or do {
252 0         0 my $skip = "Programming error - No known url for '$site'";
253 0         0 diag( $skip );
254 0         0 return ( $skip_site{$site} = $skip );
255             };
256              
257             {
258 4     4   98 no warnings qw{ once };
  4         114  
  4         1612  
  2         5  
259             $Astro::SpaceTrack::Test::SKIP_SITES
260 2 50       9 and return ( $skip_site{$site} =
261             "$site skipped: $Astro::SpaceTrack::Test::SKIP_SITES"
262             );
263             }
264              
265 2   33     43 $ua ||= LWP::UserAgent->new(
266             agent => 'curl/7.77.0',
267             ssl_opts => { verify_hostname => VERIFY_HOSTNAME },
268             );
269 2         9669 my $rslt = $ua->get( $url );
270             $rslt->is_success()
271 2 50       1983728 or return ( $skip_site{$site} =
272             "$site not available: " . $rslt->status_line() );
273 2 50 33     42 if ( $info{$site}{check} and my $check = $info{$site}{check}->() ) {
274 0         0 return ( $skip_site{$site} = $check );
275             }
276 2         246 return ( $skip_site{$site} = undef );
277             }
278             }
279              
280             {
281             my @is_server_error;
282              
283             BEGIN {
284 4     4   23 foreach my $inx (
285             HTTP_INTERNAL_SERVER_ERROR,
286             ) {
287 4         4478 $is_server_error[$inx] = 1;
288             }
289             }
290              
291             sub __skip_if_server_error {
292 28     28   397 my ( $method, $code, $skip ) = @_;
293 28 50       96 $is_server_error[$code]
294             or return;
295 0   0       skip "$method() encountered server error $code", ( $skip || 0 ) + 1;
296             }
297             }
298              
299             sub __spacetrack_identity {
300             # The following needs to be armor-plated so that a compilation
301             # failure does not shut down the testing system (though maybe it
302             # should!)
303 0     0     local $@ = undef;
304 0           return eval { ## no critic (RequireCheckingReturnValueOfEval)
305 0           local @INC = @INC;
306 0           require blib;
307 0           blib->import();
308 0           require Astro::SpaceTrack;
309 0 0         -f Astro::SpaceTrack->__identity_file_name()
310             or return;
311             # Ad-hocery. Under Mac OS X the GPG machinery seems not to work in
312             # an SSH session; a dialog pops up which the originator of the
313             # session has no way to respond to. If the dialog is actually
314             # executed, the primary user's information gets clobbered. If
315             # the identity file is not binary, we assume we don't need GPG,
316             # because that is what Config::Identity assumes.
317             Astro::SpaceTrack->__identity_file_is_encrypted()
318             and $ENV{SSH_CONNECTION}
319 0 0 0       and return;
320 0           my $id = Astro::SpaceTrack->__spacetrack_identity();
321             defined $id->{username} && defined $id->{password} &&
322 0 0 0       "$id->{username}/$id->{password}";
323             };
324 0           return;
325             }
326              
327             {
328             my $spacetrack_auth;
329              
330             sub __spacetrack_skip {
331 0     0     my ( %arg ) = @_;
332             defined $spacetrack_auth
333 0 0         or $spacetrack_auth = $ENV{SPACETRACK_USER};
334 0 0 0       defined $spacetrack_auth
335             and $spacetrack_auth =~ m< \A [:/] \z >smx
336             and return NO_SPACE_TRACK_ACCOUNT;
337 0 0         $spacetrack_auth
338             and return;
339             $ENV{AUTOMATED_TESTING}
340 0 0         and return 'Automated testing and SPACETRACK_USER not set.';
341             $spacetrack_auth = __spacetrack_identity()
342 0 0         and do {
343             $arg{envir}
344 0 0         and $ENV{SPACETRACK_USER} = $spacetrack_auth; ## no critic (RequireLocalizedPunctuationVars)
345 0           return;
346             };
347             $arg{no_prompt}
348 0 0         and return $arg{no_prompt};
349 0 0         $^O eq 'VMS' and do {
350 0           warn <<'EOD';
351              
352             Several tests will be skipped because you have not provided logical
353             name SPACETRACK_USER. This should be set to your Space Track username
354             and password, separated by a slash ("/") character.
355              
356             EOD
357 0           return 'No Space-Track account provided.';
358             };
359 0           warn <<'EOD';
360              
361             Several tests require the username and password of a registered Space
362             Track user. Because you have not provided environment variable
363             SPACETRACK_USER, you will be prompted for this information. The password
364             will be echoed unless Term::ReadKey is installed and supports ReadMode.
365             If you leave either username or password blank, the tests will be
366             skipped.
367              
368             If you set environment variable SPACETRACK_USER to your Space Track
369             username and password, separated by a slash ("/") character, that
370             username and password will be used, and you will not be prompted.
371              
372             You may also supress prompts by setting the AUTOMATED_TESTING
373             environment variable to any value Perl takes as true. This is
374             equivalent to not specifying a username, and tests that require a
375             username will be skipped.
376              
377             EOD
378              
379             my $user = prompt( 'Space-Track username' )
380             and my $pass = prompt( { password => 1 }, 'Space-Track password' )
381 0 0 0       or do {
382 0           $ENV{SPACETRACK_USER} = '/'; ## no critic (RequireLocalizedPunctuationVars)
383 0           return NO_SPACE_TRACK_ACCOUNT;
384             };
385 0           $ENV{SPACETRACK_USER} = $spacetrack_auth = "$user/$pass"; ## no critic (RequireLocalizedPunctuationVars)
386 0           return;
387             }
388             }
389              
390             sub spacetrack_skip_no_prompt {
391 0     0 0   my $skip;
392             $ENV{SPACETRACK_TEST_LIVE}
393 0 0         or plan skip_all => 'SPACETRACK_TEST_LIVE not set';
394 0 0         defined( $skip = __spacetrack_skip(
395             envir => 1,
396             no_prompt => NO_SPACE_TRACK_ACCOUNT,
397             )
398             ) and plan skip_all => $skip;
399 0           return;
400             }
401              
402             sub spacetrack_user {
403 0     0 1   __spacetrack_skip( envir => 1 );
404 0           return;
405             }
406              
407             sub throws_exception { ## no critic (RequireArgUnpacking)
408 0     0 1   my ( $obj, $method, @args ) = @_;
409 0           my $name = pop @args;
410 0           my $exception = pop @args;
411 0 0         REGEXP_REF eq ref $exception
412             or $exception = qr{\A$exception};
413 0           $rslt = eval { $obj->$method( @args ) }
414 0 0         and do {
415 0           @_ = ( "$name throw no exception. Status: " .
416             $rslt->status_line() );
417 0           goto &fail;
418             };
419 0           @_ = ( $@, $exception, $name );
420 0           goto &like;
421             }
422              
423              
424             1;
425              
426             __END__