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