File Coverage

inc/My/Module/Test.pm
Criterion Covered Total %
statement 107 192 55.7
branch 20 86 23.2
condition 3 23 13.0
subroutine 26 34 76.4
pod 11 14 78.5
total 167 349 47.8


line stmt bran cond sub pod time code
1             package My::Module::Test;
2              
3 7     7   483268 use 5.006002;
  7         28  
4              
5 7     7   45 use strict;
  7         19  
  7         177  
6 7     7   49 use warnings;
  7         28  
  7         235  
7              
8 7     7   51 use Exporter;
  7         17  
  7         485  
9              
10             our @ISA = qw{ Exporter };
11              
12 7     7   62 use HTTP::Date;
  7         27  
  7         499  
13 7     7   59 use HTTP::Status qw{ :constants };
  7         32  
  7         3565  
14 7     7   50 use Test::More 0.96; # For subtest
  7         106  
  7         43  
15              
16             our $VERSION = '0.162_01';
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 7 50   7   1832 : 0;
  7         23  
  7         801  
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 7     7   59 use constant HASH_REF => ref {};
  7         20  
  7         480  
43 7     7   48 use constant REGEXP_REF => ref qr{};
  7         24  
  7         1904  
44              
45 7     7   50 use constant NO_SPACE_TRACK_ACCOUNT => 'No Space-Track account provided';
  7         14  
  7         11618  
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 3 my ( $obj, $method, @args ) = @_;
56 1         3 my ( $code, $name ) = splice @args, -2, 2;
57 1         2 $rslt = eval { $obj->$method( @args ) };
  1         7  
58 1 50       52 $rslt or do {
59 0         0 @_ = ( "$name threw exception: $@" );
60 0         0 goto \&fail;
61             };
62 1         4 @_ = ( $rslt->code() == $code, $name );
63 1         16 goto &ok;
64             }
65              
66             sub is_error_or_skip { ## no critic (RequireArgUnpacking)
67 1     1 1 519 my ( $obj, $method, @args ) = @_;
68 1         4 local $Test::Builder::Level = $Test::Builder::Level + 1;
69 1         5 my ( $code, $name ) = splice @args, -2, 2;
70 1         2 $rslt = eval { $obj->$method( @args ) };
  1         6  
71 1 50       33 $rslt
72             or return fail "$name threw exception: $@";
73 1         4 my $got = $rslt->code();
74 1         13 __skip_if_server_error( $method, $got );
75 1         7 return cmp_ok $got, '==', $code, $name;
76             }
77              
78             sub is_not_success { ## no critic (RequireArgUnpacking)
79 1     1 1 7 my ( $obj, $method, @args ) = @_;
80 1         2 my $name = pop @args;
81 1         2 $rslt = eval { $obj->$method( @args ) };
  1         4  
82 1 50       47 $rslt or do {
83 0         0 @_ = ( "$name threw exception: $@" );
84 0         0 goto \&fail;
85             };
86 1         3 @_ = ( ! $rslt->is_success(), $name );
87 1         11 goto &ok;
88             }
89              
90             sub is_success { ## no critic (RequireArgUnpacking)
91 4     4 1 528 my ( $obj, $method, @args ) = @_;
92 4         9 my $name = pop @args;
93 4         21 $rslt = eval { $obj->$method( @args ) }
94 4 50       5 or do {
95 0         0 @_ = ( "$name threw exception: $@" );
96 0         0 chomp $_[0];
97 0         0 goto \&fail;
98             };
99 4 50       131 $rslt->is_success() or $name .= ": " . $rslt->status_line();
100 4         95 @_ = ( $rslt->is_success(), $name );
101 4         37 goto &ok;
102             }
103              
104             sub is_success_or_skip { ## no critic (RequireArgUnpacking)
105 33     33 0 17603 my ( $obj, $method, @args ) = @_;
106 33         163 local $Test::Builder::Level = $Test::Builder::Level + 1;
107 33         71 my $skip = pop @args;
108 33 50       157 $skip =~ m/ [^0-9] /smx
109             and fail "Skip number '$skip' not numeric";
110 33         107 my $name = pop @args;
111 33 50       64 $rslt = eval { $obj->$method( @args ) } or do {
  33         175  
112 0         0 fail "$name threw exception: $!" ;
113 0         0 skip "$method() threw exception", $skip;
114             };
115 33         158 __skip_if_server_error( $method, $rslt->code(), $skip );
116             ok $rslt->is_success(), $name
117 33 50       90 or do {
118 0         0 diag $rslt->status_line();
119 0         0 skip "$method() failed", $skip;
120             };
121 33         17534 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 4 return $rslt;
136             }
137              
138             sub not_defined {
139 7     7 1 30 @_ = ( ! defined $_[0], @_[1 .. $#_] );
140 7         21 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 7         1030 require Term::ReadKey;
151 0         0 $set_read_mode = Term::ReadKey->can( 'ReadMode' );
152 0         0 $readkey_loaded = 1;
153 0         0 1;
154 7 50   7   30 } or $set_read_mode = sub {};
155              
156 7         34 local $@ = undef;
157 7         16 eval { ## no critic (RequireCheckingReturnValueOfEval)
158 7         33 require IO::Handle;
159 7         87 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 7     7   3027 %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 7 50       2047 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 7     7   17 my ( $site ) = @_;
224 7         41 return $info{$site}{url};
225             }
226              
227             my $ua;
228              
229             sub set_skip {
230 0     0 1 0 my ( $site, $skip ) = @_;
231             exists $info{$site}{url}
232 0 0       0 or die "Programming error. '$site' unknown";
233 0         0 $skip_site{$site} = $skip;
234 0         0 return;
235             }
236              
237             sub site_check {
238 7     7 1 1002 my @sites = @_;
239 7 100       27 my @rslt = grep { defined $_ } map { _site_check( $_ ) } @sites
  7         161  
  7         26  
240             or return;
241 1         5 return join '; ', @rslt;
242             }
243              
244             sub _site_check {
245 7     7   20 my ( $site ) = @_;
246 7 50       29 exists $skip_site{$site} and return $skip_site{$site};
247 7 50       27 my $url = __site_to_check_uri( $site ) or do {
248 0         0 my $skip = "Programming error - No known url for '$site'";
249 0         0 diag( $skip );
250 0         0 return ( $skip_site{$site} = $skip );
251             };
252              
253             {
254 7     7   57 no warnings qw{ once };
  7         22  
  7         1535  
  7         15  
255             $Astro::SpaceTrack::Test::SKIP_SITES
256 7 50       27 and return ( $skip_site{$site} =
257             "$site skipped: $Astro::SpaceTrack::Test::SKIP_SITES"
258             );
259             }
260              
261 7   66     115 $ua ||= LWP::UserAgent->new(
262             agent => 'curl/7.77.0',
263             ssl_opts => { verify_hostname => VERIFY_HOSTNAME },
264             );
265 7         14660 my $rslt = $ua->get( $url );
266             $rslt->is_success()
267 7 100       3909810 or return ( $skip_site{$site} =
268             "$site not available: " . $rslt->status_line() );
269 6 50 33     130 if ( $info{$site}{check} and my $check = $info{$site}{check}->() ) {
270 0         0 return ( $skip_site{$site} = $check );
271             }
272 6         218 return ( $skip_site{$site} = undef );
273             }
274             }
275              
276             {
277             my @is_server_error;
278              
279             BEGIN {
280 7     7   37 foreach my $inx (
281             HTTP_INTERNAL_SERVER_ERROR,
282             ) {
283 7         6486 $is_server_error[$inx] = 1;
284             }
285             }
286              
287             sub __skip_if_server_error {
288 34     34   468 my ( $method, $code, $skip ) = @_;
289 34 50       105 $is_server_error[$code]
290             or return;
291 0   0       skip "$method() encountered server error $code", ( $skip || 0 ) + 1;
292             }
293             }
294              
295             sub __spacetrack_identity {
296             # The following needs to be armor-plated so that a compilation
297             # failure does not shut down the testing system (though maybe it
298             # should!)
299 0     0     local $@ = undef;
300 0           return eval { ## no critic (RequireCheckingReturnValueOfEval)
301 0           local @INC = @INC;
302 0           require blib;
303 0           blib->import();
304 0           require Astro::SpaceTrack;
305 0 0         -f Astro::SpaceTrack->__identity_file_name()
306             or return;
307             # Ad-hocery. Under Mac OS X the GPG machinery seems not to work in
308             # an SSH session; a dialog pops up which the originator of the
309             # session has no way to respond to. If the dialog is actually
310             # executed, the primary user's information gets clobbered. If
311             # the identity file is not binary, we assume we don't need GPG,
312             # because that is what Config::Identity assumes.
313             Astro::SpaceTrack->__identity_file_is_encrypted()
314             and $ENV{SSH_CONNECTION}
315 0 0 0       and return;
316 0           my $id = Astro::SpaceTrack->__spacetrack_identity();
317             defined $id->{username} && defined $id->{password} &&
318 0 0 0       "$id->{username}/$id->{password}";
319             };
320 0           return;
321             }
322              
323             {
324             my $spacetrack_auth;
325              
326             sub __spacetrack_skip {
327 0     0     my ( %arg ) = @_;
328             defined $spacetrack_auth
329 0 0         or $spacetrack_auth = $ENV{SPACETRACK_USER};
330 0 0 0       defined $spacetrack_auth
331             and $spacetrack_auth =~ m< \A [:/] \z >smx
332             and return NO_SPACE_TRACK_ACCOUNT;
333 0 0         $spacetrack_auth
334             and return;
335             $ENV{AUTOMATED_TESTING}
336 0 0         and return 'Automated testing and SPACETRACK_USER not set.';
337             $spacetrack_auth = __spacetrack_identity()
338 0 0         and do {
339             $arg{envir}
340 0 0         and $ENV{SPACETRACK_USER} = $spacetrack_auth; ## no critic (RequireLocalizedPunctuationVars)
341 0           return;
342             };
343             $arg{no_prompt}
344 0 0         and return $arg{no_prompt};
345 0 0         $^O eq 'VMS' and do {
346 0           warn <<'EOD';
347              
348             Several tests will be skipped because you have not provided logical
349             name SPACETRACK_USER. This should be set to your Space Track username
350             and password, separated by a slash ("/") character.
351              
352             EOD
353 0           return 'No Space-Track account provided.';
354             };
355 0           warn <<'EOD';
356              
357             Several tests require the username and password of a registered Space
358             Track user. Because you have not provided environment variable
359             SPACETRACK_USER, you will be prompted for this information. The password
360             will be echoed unless Term::ReadKey is installed and supports ReadMode.
361             If you leave either username or password blank, the tests will be
362             skipped.
363              
364             If you set environment variable SPACETRACK_USER to your Space Track
365             username and password, separated by a slash ("/") character, that
366             username and password will be used, and you will not be prompted.
367              
368             You may also supress prompts by setting the AUTOMATED_TESTING
369             environment variable to any value Perl takes as true. This is
370             equivalent to not specifying a username, and tests that require a
371             username will be skipped.
372              
373             EOD
374              
375             my $user = prompt( 'Space-Track username' )
376             and my $pass = prompt( { password => 1 }, 'Space-Track password' )
377 0 0 0       or do {
378 0           $ENV{SPACETRACK_USER} = '/'; ## no critic (RequireLocalizedPunctuationVars)
379 0           return NO_SPACE_TRACK_ACCOUNT;
380             };
381 0           $ENV{SPACETRACK_USER} = $spacetrack_auth = "$user/$pass"; ## no critic (RequireLocalizedPunctuationVars)
382 0           return;
383             }
384             }
385              
386             sub spacetrack_skip_no_prompt {
387 0     0 0   my $skip;
388             $ENV{SPACETRACK_TEST_LIVE}
389 0 0         or plan skip_all => 'SPACETRACK_TEST_LIVE not set';
390 0 0         defined( $skip = __spacetrack_skip(
391             envir => 1,
392             no_prompt => NO_SPACE_TRACK_ACCOUNT,
393             )
394             ) and plan skip_all => $skip;
395 0           return;
396             }
397              
398             sub spacetrack_user {
399 0     0 1   __spacetrack_skip( envir => 1 );
400 0           return;
401             }
402              
403             sub throws_exception { ## no critic (RequireArgUnpacking)
404 0     0 1   my ( $obj, $method, @args ) = @_;
405 0           my $name = pop @args;
406 0           my $exception = pop @args;
407 0 0         REGEXP_REF eq ref $exception
408             or $exception = qr{\A$exception};
409 0           $rslt = eval { $obj->$method( @args ) }
410 0 0         and do {
411 0           @_ = ( "$name throw no exception. Status: " .
412             $rslt->status_line() );
413 0           goto &fail;
414             };
415 0           @_ = ( $@, $exception, $name );
416 0           goto &like;
417             }
418              
419              
420             1;
421              
422             __END__