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