File Coverage

blib/lib/Test/Pod/LinkCheck.pm
Criterion Covered Total %
statement 224 384 58.3
branch 106 242 43.8
condition 7 24 29.1
subroutine 25 31 80.6
pod 2 2 100.0
total 364 683 53.2


line stmt bran cond sub pod time code
1             #
2             # This file is part of Test-Pod-LinkCheck
3             #
4             # This software is copyright (c) 2014 by Apocalypse.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9 3     3   63247 use strict; use warnings;
  3     3   6  
  3         115  
  3         13  
  3         4  
  3         190  
10             package Test::Pod::LinkCheck;
11             # git description: release-0.007-17-gea77aa8
12             $Test::Pod::LinkCheck::VERSION = '0.008';
13             our $AUTHORITY = 'cpan:APOCAL';
14              
15             # ABSTRACT: Tests POD for invalid links
16              
17             # Import the modules we need
18 3     3   1761 use Moose 1.01;
  3         1406712  
  3         24  
19 3     3   24409 use Test::Pod 1.44 ();
  3         98805  
  3         100  
20 3     3   1966 use App::PodLinkCheck::ParseLinks 4;
  3         10155  
  3         108  
21              
22             # setup our tests and etc
23 3     3   23 use Test::Builder 0.94;
  3         61  
  3         123  
24             my $Test = Test::Builder->new;
25              
26             # export our 2 subs
27 3     3   17 use parent qw( Exporter );
  3         5  
  3         26  
28             our @EXPORT_OK = qw( pod_ok all_pod_ok );
29              
30             #pod =attr check_cpan
31             #pod
32             #pod If enabled, this module will check the CPAN module database to see if a link is a valid CPAN module or not. It uses the backend
33             #pod defined in L</cpan_backend> to do the actual searching.
34             #pod
35             #pod If disabled, it will resolve links based on locally installed modules. If it isn't installed it will be an error!
36             #pod
37             #pod The default is: true
38             #pod
39             #pod =cut
40              
41             has 'check_cpan' => (
42             is => 'rw',
43             isa => 'Bool',
44             default => 1,
45             );
46              
47             {
48 3     3   307 use Moose::Util::TypeConstraints 1.01;
  3         66  
  3         27  
49              
50             #pod =attr cpan_backend
51             #pod
52             #pod Selects the CPAN backend to use for querying modules. The available ones are: CPANPLUS, CPAN, CPANSQLite, MetaDB, MetaCPAN, and CPANIDX.
53             #pod
54             #pod The default is: CPANPLUS
55             #pod
56             #pod The backends were tested and verified against those versions. Older versions should work, but is untested!
57             #pod CPANPLUS v0.9010
58             #pod CPAN v1.9402
59             #pod CPAN::SQLite v0.199
60             #pod CPAN::Common::Index::MetaDB v0.005
61             #pod MetaCPAN::API::Tiny v1.131730
62             #pod MetaCPAN::Client v1.007001
63             #pod LWP::UserAgent v6.06
64             #pod
65             #pod =cut
66              
67             has 'cpan_backend' => (
68             is => 'rw',
69             isa => enum( [ qw( CPANPLUS CPAN CPANSQLite MetaDB MetaCPAN CPANIDX ) ] ),
70             default => 'CPANPLUS',
71             trigger => \&_clean_cpan_backend,
72             );
73              
74             sub _clean_cpan_backend {
75 8     8   16 my $self = shift;
76 8         233 $self->_cache->{'cpan'} = {};
77 8         232 $self->_backend_err( 0 );
78             }
79             }
80              
81             #pod =attr cpan_backend_auto
82             #pod
83             #pod Enable to automatically try the CPAN backends to find an available one. It will try the backends in the order defined in L</cpan_backend>
84             #pod
85             #pod If no backend is available, it will disable the L</check_cpan> attribute and enable the L</cpan_section_err> attribute.
86             #pod
87             #pod The default is: true
88             #pod
89             #pod =cut
90              
91             has 'cpan_backend_auto' => (
92             is => 'rw',
93             isa => 'Bool',
94             default => 1,
95             );
96              
97             #pod =attr cpan_section_err
98             #pod
99             #pod If enabled, a link pointing to a CPAN module's specific section is treated as an error if it isn't installed.
100             #pod
101             #pod The default is: false
102             #pod
103             #pod =cut
104              
105             has 'cpan_section_err' => (
106             is => 'rw',
107             isa => 'Bool',
108             default => 0,
109             );
110              
111             #pod =attr verbose
112             #pod
113             #pod If enabled, this module will print extra diagnostics for the links it's checking.
114             #pod
115             #pod The default is: copy $ENV{HARNESS_IS_VERBOSE} or $ENV{TEST_VERBOSE} or false
116             #pod
117             #pod =cut
118              
119             has 'verbose' => (
120             is => 'rw',
121             isa => 'Bool',
122             default => sub { defined $ENV{HARNESS_IS_VERBOSE} ? $ENV{HARNESS_IS_VERBOSE} : ( defined $ENV{TEST_VERBOSE} ? $ENV{TEST_VERBOSE} : 0 ) },
123             );
124              
125             # holds the cached results of link look-ups
126             has '_cache' => (
127             is => 'ro',
128             isa => 'HashRef',
129             default => sub { return {
130             'cpan' => {},
131             'man' => {},
132             'pod' => {},
133             'section' => {},
134             } },
135             );
136              
137             # is the backend good to use?
138             has '_backend_err' => (
139             is => 'rw',
140             isa => 'Bool',
141             default => 0,
142             trigger => \&_clean_backend_err,
143             );
144              
145             sub _clean_backend_err {
146 13     13   17 my $self = shift;
147 13         15 my $new = shift;
148              
149             # Only clean if an error happened
150 13 100       270 if ( $new ) {
151 4         103 $self->_cache->{'cpan'} = {};
152             }
153             }
154              
155             #pod =method pod_ok
156             #pod
157             #pod Accepts the filename to check, and an optional test name.
158             #pod
159             #pod This method will pass the test if there is no POD links present in the POD or if all links are not an error. Furthermore, if the POD was
160             #pod malformed as reported by L<Pod::Simple>, the test will fail and not attempt to check the links.
161             #pod
162             #pod When it fails, this will show any failing links as diagnostics. Also, some extra information is printed if verbose is enabled.
163             #pod
164             #pod The default test name is: "LinkCheck test for FILENAME"
165             #pod
166             #pod =cut
167              
168             sub pod_ok {
169 20     20 1 148 my $self = shift;
170 20         31 my $file = shift;
171              
172 20 50       60 if ( ! ref $self ) { # Allow function call
173 0         0 $file = $self;
174 0         0 $self = __PACKAGE__->new;
175             }
176              
177 20 50       52 my $name = @_ ? shift : "LinkCheck test for $file";
178              
179 20 50       413 if ( ! -f $file ) {
180 0         0 $Test->ok( 0, $name );
181              
182 0 0       0 if ( $self->verbose ) {
183 0         0 $Test->diag( "Extra: " );
184 0         0 $Test->diag( " * '$file' does not exist?" );
185             }
186              
187 0         0 return 0;
188             }
189              
190             # Parse the POD!
191 20         207 my $parser = App::PodLinkCheck::ParseLinks->new( {} );
192 20         1754 my $output;
193              
194             # Override some options that the podlinkcheck subclass "helpfully" set for us...
195 20         90 $parser->output_string( \$output );
196 20         2163 $parser->complain_stderr( 0 );
197 20         146 $parser->no_errata_section( 0 );
198 20         109 $parser->no_whining( 0 );
199              
200             # numerous reports on RT show this blowing up often :(
201 20         94 eval { $parser->parse_file( $file ) };
  20         94  
202 20 50       25779 if ( $@ ) {
203 0         0 $Test->ok( 0, $name );
204 0 0       0 $Test->diag( "Unable to parse $file => $@" ) if $self->verbose;
205 0         0 return 0;
206             }
207              
208             # is POD well-formed?
209 20 100       78 if ( $parser->any_errata_seen ) {
210 1         9 $Test->ok( 0, $name );
211              
212 1 50       137 if ( $self->verbose ) {
213 0         0 $Test->diag( "Extra: " );
214 0         0 $Test->diag( " * Unable to parse POD in '$file'" );
215              
216             # TODO ugly, but there is no other way to get at it?
217             ## no critic ( ProhibitAccessOfPrivateData )
218 0         0 foreach my $l ( keys %{ $parser->{'errata'} } ) {
  0         0  
219 0         0 $Test->diag( " * errors seen in line $l:" );
220 0         0 $Test->diag( " * $_" ) for @{ $parser->{'errata'}{$l} };
  0         0  
221             }
222             }
223              
224 1         17 return 0;
225             }
226              
227             # Did we see POD in the file?
228 19 50       130 if ( $parser->doc_has_started ) {
229 19         185 my( $err, $diag ) = $self->_analyze( $parser );
230              
231 19 100       50 if ( scalar @$err > 0 ) {
232 7         88 $Test->ok( 0, $name );
233 7         1400 $Test->diag( "Erroneous links: " );
234 7         297 $Test->diag( " * $_" ) for @$err;
235              
236 7 50 33     550 if ( $self->verbose and @$diag ) {
237 0         0 $Test->diag( "Extra: " );
238 0         0 $Test->diag( " * $_" ) for @$diag;
239             }
240              
241 7         272 return 0;
242             } else {
243 12         117 $Test->ok( 1, $name );
244              
245 12 50 33     2256 if ( $self->verbose and @$diag ) {
246 0         0 $Test->diag( "Extra: " );
247 0         0 $Test->diag( " * $_" ) for @$diag;
248             }
249             }
250             } else {
251 0         0 $Test->ok( 1, $name );
252              
253 0 0       0 if ( $self->verbose ) {
254 0         0 $Test->diag( "Extra: " );
255 0         0 $Test->diag( " * There is no POD in '$file' ?" );
256             }
257             }
258              
259 12         337 return 1;
260             }
261              
262             #pod =method all_pod_ok
263             #pod
264             #pod Accepts an optional array of files to check. By default it uses all POD files in your distribution.
265             #pod
266             #pod This method is what you will usually run. Every file is passed to the L</pod_ok> function. This also sets the
267             #pod test plan to be the number of files.
268             #pod
269             #pod =cut
270              
271             sub all_pod_ok {
272 0     0 1 0 my $self = shift;
273 0 0       0 my @files = @_ ? @_ : Test::Pod::all_pod_files();
274              
275 0 0 0     0 if ( ! defined $self or ! ref $self ) { # Allow function call
276 0 0       0 unshift( @files, $self ) if defined $self;
277 0         0 $self = __PACKAGE__->new;
278             }
279              
280 0         0 $Test->plan( tests => scalar @files );
281              
282 0         0 my $ok = 1;
283 0         0 foreach my $file ( @files ) {
284 0 0       0 $self->pod_ok( $file ) or undef $ok;
285             }
286              
287 0         0 return $ok;
288             }
289              
290             sub _analyze {
291 19     19   31 my( $self, $parser ) = @_;
292              
293 19         59 my $file = $parser->source_filename;
294 19         146 my $links = $parser->links_arrayref;
295 19         113 my $own_sections = $parser->sections_hashref;
296 19         89 my( @errors, @diag );
297              
298 19         47 foreach my $l ( @$links ) {
299             ## no critic ( ProhibitAccessOfPrivateData )
300 17         44 my( $type, $to, $section, $linenum, $column ) = @$l;
301 17 0       749 push( @diag, "$file:$linenum:$column - Checking link type($type) to(" . ( defined $to ? $to : '' ) . ") " .
    0          
    50          
302             "section(" . ( defined $section ? $section : '' ) . ")" ) if $self->verbose;
303              
304             # What kind of link?
305 17 100       69 if ( $type eq 'man' ) {
    50          
306 2 50       9 if ( ! $self->_known_manpage( $to ) ) {
307 2         33 push( @errors, "$file:$linenum:$column - Unknown link type(man) to($to)" );
308             }
309             } elsif ( $type eq 'pod' ) {
310             # do we have a to/section?
311 15 100       36 if ( defined $to ) {
312 4 50       10 if ( defined $section ) {
313             # Do we have this file installed?
314 0 0       0 if ( ! $self->_known_podlink( $to, $section ) ) {
315             # Is it a CPAN module?
316 0         0 my $res = $self->_known_cpan( $to );
317 0 0       0 if ( defined $res ) {
318 0 0       0 if ( $res ) {
319             # if true, treat cpan sections as errors because we can't verify if section exists
320 0 0       0 if ( $self->cpan_section_err ) {
321 0         0 push( @errors, "$file:$linenum:$column - Unable to verify link type(pod) to($to) section($section) because the module isn't installed" );
322             } else {
323 0         0 push( @diag, "$file:$linenum:$column - Unable to verify link type(pod) to($to) section($section) because the module isn't installed" );
324             }
325             } else {
326 0         0 push( @errors, "$file:$linenum:$column - Unknown link type(pod) to($to) section($section) - module doesn't exist in CPAN" );
327             }
328             } else {
329 0         0 push( @errors, "$file:$linenum:$column - Unknown link type(pod) to($to) section($section) - unable to check CPAN" );
330             }
331             }
332             } else {
333             # Is it a perlfunc reference?
334 4 100       24 if ( ! $self->_known_perlfunc( $to ) ) {
335             # Do we have this file installed?
336 3 100       25 if ( ! $self->_known_podfile( $to ) ) {
337             # Sometimes we find a manpage but not the pod...
338 2 50       15 if ( ! $self->_known_manpage( $to ) ) {
339             # Is it a CPAN module?
340 2         18 my $res = $self->_known_cpan( $to );
341 2 50       8 if ( defined $res ) {
342 2 100       11 if ( ! $res ) {
343             # Check for internal section
344 1 50       4 if ( exists $own_sections->{ $to } ) {
345 0         0 push( @diag, "$file:$linenum:$column - Link type(pod) to($to) looks like an internal section link - recommend 'L</$to>'" );
346             } else {
347 1         15 push( @errors, "$file:$linenum:$column - Unknown link type(pod) to($to) - module doesn't exist in CPAN" );
348             }
349             }
350             } else {
351             # Check for internal section
352 0 0       0 if ( exists $own_sections->{ $to } ) {
353 0         0 push( @diag, "$file:$linenum:$column - Link type(pod) to($to) looks like an internal section link - recommend 'L</$to>'" );
354             } else {
355 0         0 push( @errors, "$file:$linenum:$column - Unknown link type(pod) to($to) - unable to check CPAN" );
356             }
357             }
358             }
359             }
360             }
361             }
362             } else {
363 11 50       27 if ( defined $section ) {
364 11 100       47 if ( ! exists $own_sections->{ $section } ) {
365 4         32 push( @errors, "$file:$linenum:$column - Unknown link type(pod) to() section($section) - section doesn't exist!" );
366             }
367             } else {
368             # no to/section eh?
369 0         0 push( @errors, "$file:$linenum:$column - Malformed link type(pod) to() section()" );
370             }
371             }
372             } else {
373             # unknown type?
374 0 0       0 push( @errors, "$file:$linenum:$column - Unknown link type($type) to(" . ( defined $to ? $to : '' ) . ") section(" . ( defined $section ? $section : '' ) . ")" );
    0          
375             }
376             }
377              
378 19         98 return( \@errors, \@diag );
379             }
380              
381             sub _known_perlfunc {
382 4     4   7 my( $self, $func ) = @_;
383 4         161 my $cache = $self->_cache->{'func'};
384             # $Test->diag( "perlfunc check for $func" ) if $self->verbose;
385 4 50       19 if ( ! exists $cache->{ $func } ) {
386             # TODO this sucks, but Pod::Perldoc can't do it because it expects to be ran in the console...
387 4         35 require Capture::Tiny;
388             $cache->{ $func } = Capture::Tiny::capture_merged( sub {
389 4     4   932094 system( 'perldoc -f ' . $func );
390 4         108 } );
391              
392             # We need at least 5 newlines to guarantee a real perlfunc
393             # apoc@blackhole:~$ perldoc -f foobar
394             # No documentation for perl function `foobar' found
395 4 100       3835 if ( ( $cache->{ $func } =~ tr/\n// ) > 5 ) {
396 1         6 $cache->{ $func } = 1;
397             } else {
398 3         11 $cache->{ $func } = 0;
399             }
400             }
401              
402 4         38 return $cache->{ $func };
403             }
404              
405             sub _known_manpage {
406 4     4   12 my( $self, $page ) = @_;
407 4         129 my $cache = $self->_cache->{'man'};
408             # $Test->diag( "manpage check for $page" ) if $self->verbose;
409 4 50       14 if ( ! exists $cache->{ $page } ) {
410 4         6 my @manargs;
411 4 100       18 if ( $page =~ /(.+)\s*\((.+)\)$/ ) {
412 2         6 @manargs = ($2, $1);
413             } else {
414 2         8 @manargs = ($page);
415             }
416              
417 4         689 require Capture::Tiny;
418             $cache->{ $page } = Capture::Tiny::capture_merged( sub {
419 4     4   36644 system( 'man', @manargs );
420 4         6286 } );
421              
422             # We need at least 5 newlines to guarantee a real manpage
423 4 50       3992 if ( ( $cache->{ $page } =~ tr/\n// ) > 5 ) {
424 0         0 $cache->{ $page } = 1;
425             } else {
426 4         29 $cache->{ $page } = 0;
427             }
428             }
429              
430 4         38 return $cache->{ $page };
431             }
432              
433             sub _known_podfile {
434 3     3   10 my( $self, $link ) = @_;
435 3         281 my $cache = $self->_cache->{'pod'};
436             # $Test->diag( "podfile check for $link" ) if $self->verbose;
437 3 50       14 if ( ! exists $cache->{ $link } ) {
438             # Is it a plain POD file?
439 3         32 require Pod::Find;
440 3         2582 my $filename = Pod::Find::pod_where( {
441             '-inc' => 1,
442             }, $link );
443 3 100       78 if ( defined $filename ) {
444 1         5 $cache->{ $link } = $filename;
445             } else {
446             # It might be a script...
447 2         18 require File::Spec;
448 2         6 require Config;
449 2         42 foreach my $dir ( split /\Q$Config::Config{'path_sep'}/o, $ENV{'PATH'} ) {
450 14         70 $filename = File::Spec->catfile( $dir, $link );
451 14 50       125 if ( -e $filename ) {
452 0         0 $cache->{ $link } = $filename;
453 0         0 last;
454             }
455             }
456 2 50       16 if ( ! exists $cache->{ $link } ) {
457 2         6 $cache->{ $link } = 0;
458             }
459             }
460             }
461              
462 3         18 return $cache->{ $link };
463             }
464              
465             sub _known_cpan {
466 26     26   13333 my( $self, $module ) = @_;
467              
468             # Sanity check - we use '.' as the actual cache placeholder...
469 26 50       98 if ( $module eq '.' ) {
470 0         0 die 'sanity check';
471             }
472              
473             # Do we even check CPAN?
474 26 50       1091 if ( ! $self->check_cpan ) {
475 0 0       0 $Test->diag( "skipping cpan check for $module due to config" ) if $self->verbose;
476 0         0 return;
477             }
478              
479             # Did the backend encounter an error?
480 26 100       790 if ( $self->_backend_err ) {
481 9 50       218 $Test->diag( "skipping cpan check for $module due to backend error" ) if $self->verbose;
482 9         18 return;
483             }
484              
485             # $Test->diag( "cpan check for $module" ) if $self->verbose;
486              
487             # is the answer cached already?
488 17 100       501 if ( exists $self->_cache->{'cpan'}{ $module } ) {
489 6         238 return $self->_cache->{'cpan'}{ $module };
490             }
491              
492             # Select the backend?
493 11 100       330 if ( $self->cpan_backend eq 'CPANIDX' ) {
    100          
    100          
    100          
    100          
    50          
494 2         8 return $self->_known_cpan_cpanidx( $module );
495             } elsif ( $self->cpan_backend eq 'MetaCPAN' ) {
496 2         12 return $self->_known_cpan_metacpan( $module );
497             } elsif ( $self->cpan_backend eq 'MetaDB' ) {
498 1         43 return $self->_known_cpan_metadb( $module );
499             } elsif ( $self->cpan_backend eq 'CPANPLUS' ) {
500 3         16 return $self->_known_cpan_cpanplus( $module );
501             } elsif ( $self->cpan_backend eq 'CPAN' ) {
502 2         7 return $self->_known_cpan_cpan( $module );
503             } elsif ( $self->cpan_backend eq 'CPANSQLite' ) {
504 1         6 return $self->_known_cpan_cpansqlite( $module );
505             } else {
506 0         0 die "Unknown backend: " . $self->cpan_backend;
507             }
508             }
509              
510             sub _known_cpan_cpanidx {
511 2     2   5 my( $self, $module ) = @_;
512 2         73 my $cache = $self->_cache->{'cpan'};
513             # $Test->diag( "cpan:CPANIDX check for $module" ) if $self->verbose;
514 2 100       11 if ( ! exists $cache->{'.'} ) {
515 1         2 eval {
516             # Wacky format so dzil will not autoprereq it
517 1         10 require 'HTTP/Tiny.pm';
518 1         10 $cache->{'.'} = HTTP::Tiny->new;
519             };
520 1 50       124 if ( $@ ) {
521 0 0       0 $Test->diag( "Unable to load HTTP::Tiny - $@" ) if $self->verbose;
522 0         0 eval {
523 0         0 require 'LWP/UserAgent.pm';
524 0         0 $cache->{'.'} = LWP::UserAgent->new( keep_alive => 1 );
525             };
526 0 0       0 if ( $@ ) {
527 0 0       0 $Test->diag( "Unable to load LWP::UserAgent - $@" ) if $self->verbose;
528 0 0       0 if ( $self->cpan_backend_auto ) {
529 0 0       0 $Test->diag( "Unable to use any CPAN backend, disabling searches!" ) if $self->verbose;
530 0         0 $self->check_cpan( 0 );
531 0         0 $self->cpan_section_err( 1 );
532             } else {
533 0         0 $self->_backend_err( 1 );
534             }
535 0         0 return;
536             }
537             }
538             }
539              
540 2         5 eval {
541 2         55 my $res = $cache->{'.'}->get("http://cpanidx.org/cpanidx/json/mod/$module");
542 2 50       34597 if ( ref( $res ) ne 'HASH' ? $res->is_success : $res->{success} ) {
    50          
543             # Did we get a hit?
544             # apoc@box:~$ perl -MHTTP::Tiny -MData::Dumper::Concise -e 'print Dumper( HTTP::Tiny->new->get("http://cpanidx.org/cpanidx/json/mod/POE")->{content} )'
545             # "[\n {\n \"dist_vers\" : \"1.365\",\n \"dist_name\" : \"POE\",\n \"cpan_id\" : \"RCAPUTO\",\n \"mod_vers\" : \"1.365\",\n \"dist_file\" : \"R/RC/RCAPUTO/POE-1.365.tar.gz\",\n \"mod_name\" : \"POE\"\n }\n]\n"
546             # apoc@box:~$ perl -MHTTP::Tiny -MData::Dumper::Concise -e 'print Dumper( HTTP::Tiny->new->get("http://cpanidx.org/cpanidx/json/mod/Floo::Bar")->{content} )'
547             # "[]\n"
548 2 50       14 if ( length( ref( $res ) ne 'HASH' ? $res->decoded_content : $res->{content} ) > 5 ) {
    100          
549 1         8 $cache->{$module} = 1;
550             } else {
551 1         6 $cache->{$module} = 0;
552             }
553             } else {
554 0         0 die "HTTP return non-success";
555             }
556             };
557 2 50       14 if ( $@ ) {
558 0 0       0 $Test->diag( "Unable to find $module on CPANIDX: $@" ) if $self->verbose;
559 0         0 $self->_backend_err( 1 );
560 0         0 return;
561             }
562 2         11 return $cache->{$module};
563             }
564              
565             sub _known_cpan_metacpan {
566 2     2   4 my( $self, $module ) = @_;
567 2         61 my $cache = $self->_cache->{'cpan'};
568             # $Test->diag( "cpan:MetaCPAN check for $module" ) if $self->verbose;
569             # init the backend ( and set some options )
570 2 100       16 if ( ! exists $cache->{'.'} ) {
571 1         2 eval {
572             # Wacky format so dzil will not autoprereq it
573 1         177 require 'MetaCPAN/API/Tiny.pm';
574              
575 0         0 $cache->{'.'} = MetaCPAN::API::Tiny->new;
576             };
577 1 50       5 if ( $@ ) {
578 1 50       36 $Test->diag( "Unable to load MetaCPAN::API::Tiny - $@" ) if $self->verbose;
579 1         1 eval {
580 1         833 require 'MetaCPAN/Client.pm';
581              
582 1         185519 $cache->{'.'} = MetaCPAN::Client->new;
583             };
584 1 50       1940 if ( $@ ) {
585 0 0       0 $Test->diag( "Unable to load MetaCPAN::Client - $@" ) if $self->verbose;
586 0 0       0 if ( $self->cpan_backend_auto ) {
587 0         0 $self->cpan_backend( 'CPANIDX' );
588 0         0 return $self->_known_cpan_cpanidx( $module );
589             } else {
590 0         0 $self->_backend_err( 1 );
591 0         0 return;
592             }
593             }
594             }
595             }
596              
597             # API::Tiny just dies on bad modules...
598 2 50       3 eval { $cache->{$module} = defined $cache->{'.'}->module( $module ) ? 1 : 0 };
  2         11  
599 2 100       165870 if ( $@ ) {
600 1 50       77 $Test->diag( "Unable to find $module on MetaCPAN: $@" ) if $self->verbose;
601 1         4 $cache->{$module} = 0;
602             }
603 2         17 return $cache->{$module};
604             }
605              
606             sub _known_cpan_metadb {
607 1     1   3 my( $self, $module ) = @_;
608 1         29 my $cache = $self->_cache->{'cpan'};
609             # $Test->diag( "cpan:MetaDB check for $module" ) if $self->verbose;
610             # init the backend ( and set some options )
611 1 50       6 if ( ! exists $cache->{'.'} ) {
612 1         2 eval {
613             # Wacky format so dzil will not autoprereq it
614 1         157 require 'CPAN/Common/Index/MetaDB.pm';
615              
616 0         0 $cache->{'.'} = CPAN::Common::Index::MetaDB->new;
617             };
618 1 50       5 if ( $@ ) {
619 1 50       32 $Test->diag( "Unable to load MetaDB - $@" ) if $self->verbose;
620 1 50       28 if ( $self->cpan_backend_auto ) {
621 0         0 $self->cpan_backend( 'MetaCPAN' );
622 0         0 return $self->_known_cpan_metacpan( $module );
623             } else {
624 1         27 $self->_backend_err( 1 );
625 1         2 return;
626             }
627             }
628             }
629              
630 0 0       0 $cache->{$module} = defined $cache->{'.'}->search_packages( { 'package' => $module } ) ? 1 : 0;
631 0         0 return $cache->{$module};
632             }
633              
634             sub _known_cpan_cpanplus {
635 3     3   6 my( $self, $module ) = @_;
636 3         73 my $cache = $self->_cache->{'cpan'};
637             # $Test->diag( "cpan:CPANPLUS check for $module" ) if $self->verbose;
638             # init the backend ( and set some options )
639 3 50       12 if ( ! exists $cache->{'.'} ) {
640 3         8 eval {
641             # Wacky format so dzil will not autoprereq it
642 3         607 require 'CPANPLUS/Backend.pm'; require 'CPANPLUS/Configure.pm';
  0         0  
643              
644 0         0 my $cpanconfig = CPANPLUS::Configure->new;
645 0         0 $cpanconfig->set_conf( 'verbose' => 0 );
646 0         0 $cpanconfig->set_conf( 'no_update' => 1 );
647              
648             # ARGH, CPANIDX doesn't work well with this kind of search...
649             # TODO check if it's still true?
650 0 0       0 if ( $cpanconfig->get_conf( 'source_engine' ) =~ /CPANIDX/ ) {
651 0         0 $cpanconfig->set_conf( 'source_engine' => 'CPANPLUS::Internals::Source::Memory' );
652             }
653              
654             # silence CPANPLUS!
655 0         0 eval "no warnings 'redefine'; sub Log::Message::store { return }";
656 0     0   0 local $SIG{'__WARN__'} = sub { return };
  0         0  
657 0         0 $cache->{'.'} = CPANPLUS::Backend->new( $cpanconfig );
658             };
659 3 50       17 if ( $@ ) {
660 3 50       134 $Test->diag( "Unable to load CPANPLUS - $@" ) if $self->verbose;
661 3 100       126 if ( $self->cpan_backend_auto ) {
662 2         68 $self->cpan_backend( 'CPAN' );
663 2         7 return $self->_known_cpan_cpan( $module );
664             } else {
665 1         27 $self->_backend_err( 1 );
666 1         3 return;
667             }
668             }
669             }
670              
671 0         0 my $result;
672 0     0   0 eval { local $SIG{'__WARN__'} = sub { return }; $result = $cache->{'.'}->parse_module( 'module' => $module ) };
  0         0  
  0         0  
  0         0  
673 0 0       0 if ( $@ ) {
674 0 0       0 $Test->diag( "Unable to use CPANPLUS - $@" ) if $self->verbose;
675 0 0       0 if ( $self->cpan_backend_auto ) {
676 0         0 $self->cpan_backend( 'CPAN' );
677 0         0 return $self->_known_cpan_cpan( $module );
678             } else {
679 0         0 $self->_backend_err( 1 );
680 0         0 return;
681             }
682             }
683 0 0       0 if ( defined $result ) {
684 0         0 $cache->{ $module } = 1;
685             } else {
686 0         0 $cache->{ $module } = 0;
687             }
688              
689 0         0 return $cache->{ $module };
690             }
691              
692             sub _known_cpan_cpan {
693 4     4   9 my( $self, $module ) = @_;
694 4         100 my $cache = $self->_cache->{'cpan'};
695             # $Test->diag( "cpan:CPAN check for $module" ) if $self->verbose;
696             # init the backend ( and set some options )
697 4 100       15 if ( ! exists $cache->{'.'} ) {
698 3         4 eval {
699             # Wacky format so dzil will not autoprereq it
700 3         15318 require 'CPAN.pm';
701              
702             # TODO this code stolen from App::PodLinkCheck
703             # not sure how far back this will work, maybe only 5.8.0 up
704 3 100 66     487804 if ( ! $CPAN::Config_loaded && CPAN::HandleConfig->can( 'load' ) ) {
705             # fake $loading to avoid running the CPAN::FirstTime dialog -- is this the right way to do that?
706 2         12 local $CPAN::HandleConfig::loading = 1;
707 2         20 CPAN::HandleConfig->load;
708             }
709              
710             # figure out the access method
711 3 100 66     871 if ( defined $CPAN::META && %$CPAN::META ) {
    50          
712             # works already!
713             } elsif ( ! CPAN::Index->can('read_metadata_cache') ) {
714             # Argh, we can't use it...
715 0         0 die "Unable to use CPAN.pm metadata cache!";
716             } else {
717             # try the .cpan/Metadata even if CPAN::SQLite is installed, just in
718             # case the SQLite is not up-to-date or has not been used yet
719 2         14 local $CPAN::Config->{use_sqlite} = $CPAN::Config->{use_sqlite} = 0; # stupid used once warning...
720 2         46 CPAN::Index->read_metadata_cache;
721 2 50 33     3488118 if ( defined $CPAN::META && %$CPAN::META ) {
722             # yay, works!
723             } else {
724 0         0 die "Unable to use CPAN.pm metadata cache!";
725             }
726             }
727              
728             # Cache is ready
729 3         22 $cache->{'.'} = $CPAN::META->{'readwrite'}->{'CPAN::Module'};
730             };
731 3 50       14 if ( $@ ) {
732 0 0       0 $Test->diag( "Unable to load CPAN - $@" ) if $self->verbose;
733 0 0       0 if ( $self->cpan_backend_auto ) {
734 0         0 $self->cpan_backend( 'CPANSQLite' );
735 0         0 return $self->_known_cpan_cpansqlite( $module );
736             } else {
737 0         0 $self->_backend_err( 1 );
738 0         0 return;
739             }
740             }
741             }
742              
743 4 100       31 if ( exists $cache->{'.'}{ $module } ) {
744 2         11 $cache->{ $module } = 1;
745             } else {
746 2         5 $cache->{ $module } = 0;
747             }
748              
749 4         33 return $cache->{ $module };
750             }
751              
752             sub _known_cpan_cpansqlite {
753 1     1   2 my( $self, $module ) = @_;
754 1         26 my $cache = $self->_cache->{'cpan'};
755             # $Test->diag( "cpan:CPANSQLite check for $module" ) if $self->verbose;
756             # init the backend ( and set some options )
757 1 50       5 if ( ! exists $cache->{'.'} ) {
758 1         2 eval {
759             # Wacky format so dzil will not autoprereq it
760 1         9 require 'CPAN.pm'; require 'CPAN/SQLite.pm';
  1         222  
761              
762             # TODO this code stolen from App::PodLinkCheck
763             # not sure how far back this will work, maybe only 5.8.0 up
764 0 0 0     0 if ( ! $CPAN::Config_loaded && CPAN::HandleConfig->can( 'load' ) ) {
765             # fake $loading to avoid running the CPAN::FirstTime dialog -- is this the right way to do that?
766 0         0 local $CPAN::HandleConfig::loading = 1;
767 0         0 CPAN::HandleConfig->load;
768             }
769              
770 0         0 $cache->{'.'} = CPAN::SQLite->new;
771             };
772 1 50       5 if ( $@ ) {
773 1 50       36 $Test->diag( "Unable to load CPANSQLite - $@" ) if $self->verbose;
774 1 50       32 if ( $self->cpan_backend_auto ) {
775 0         0 $self->cpan_backend( 'MetaDB' );
776 0         0 return $self->_known_cpan_metadb( $module );
777             } else {
778 1         26 $self->_backend_err( 1 );
779 1         2 return;
780             }
781             }
782             }
783              
784 0           my $result;
785 0     0     eval { local $SIG{'__WARN__'} = sub { return }; $result = $cache->{'.'}->query( 'mode' => 'module', name => $module, max_results => 1 ); };
  0            
  0            
  0            
786 0 0         if ( $@ ) {
787 0 0         $Test->diag( "Unable to use CPANSQLite - $@" ) if $self->verbose;
788 0 0         if ( $self->cpan_backend_auto ) {
789 0           $self->cpan_backend( 'MetaDB' );
790 0           return $self->_known_cpan_metadb( $module );
791             } else {
792 0           $self->_backend_err( 1 );
793 0           return;
794             }
795             }
796 0 0         if ( $result ) {
797 0           $cache->{ $module } = 1;
798             } else {
799 0           $cache->{ $module } = 0;
800             }
801              
802 0           return $cache->{ $module };
803             }
804              
805             sub _known_podlink {
806 0     0     my( $self, $link, $section ) = @_;
807             # $Test->diag( "podlink check for $link - $section" ) if $self->verbose;
808             # First of all, does the file exists?
809 0           my $filename = $self->_known_podfile( $link );
810 0 0         return 0 if ! defined $filename;
811              
812             # Okay, get the sections in the file and see if the link matches
813 0           my $file_sections = $self->_known_podsections( $filename );
814 0 0 0       if ( defined $file_sections and exists $file_sections->{ $section } ) {
815 0           return 1;
816             } else {
817 0           return 0;
818             }
819             }
820              
821             sub _known_podsections {
822 0     0     my( $self, $filename ) = @_;
823 0           my $cache = $self->_cache->{'sections'};
824             # $Test->diag( "podsections check for $filename" ) if $self->verbose;
825 0 0         if ( ! exists $cache->{ $filename } ) {
826             # Okay, get the sections in the file
827 0           require App::PodLinkCheck::ParseSections;
828 0           my $parser = App::PodLinkCheck::ParseSections->new( {} );
829              
830             # numerous reports on RT show this blowing up often :(
831 0           eval { $parser->parse_file( $filename ) };
  0            
832 0 0         if ( $@ ) {
833 0 0         $Test->diag( "Unable to parse $filename => $@" ) if $self->verbose;
834 0           $cache->{ $filename } = undef;
835             } else {
836 0           $cache->{ $filename } = $parser->sections_hashref;
837             }
838             }
839              
840 0           return $cache->{ $filename };
841             }
842              
843             # from Moose::Manual::BestPractices
844 3     3   24157 no Moose;
  3         8  
  3         28  
845             __PACKAGE__->meta->make_immutable;
846              
847             1;
848              
849             __END__
850              
851             =pod
852              
853             =encoding UTF-8
854              
855             =for :stopwords Apocalypse cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee
856             diff irc mailto metadata placeholders metacpan CPAN foo OO backend env
857             CPANPLUS CPANSQLite http
858              
859             =head1 NAME
860              
861             Test::Pod::LinkCheck - Tests POD for invalid links
862              
863             =head1 VERSION
864              
865             This document describes v0.008 of Test::Pod::LinkCheck - released November 01, 2014 as part of Test-Pod-LinkCheck.
866              
867             =head1 SYNOPSIS
868              
869             #!/usr/bin/perl
870             use strict; use warnings;
871              
872             use Test::More;
873              
874             eval "use Test::Pod::LinkCheck";
875             if ( $@ ) {
876             plan skip_all => 'Test::Pod::LinkCheck required for testing POD';
877             } else {
878             Test::Pod::LinkCheck->new->all_pod_ok;
879             }
880              
881             =head1 DESCRIPTION
882              
883             This module looks for any links in your POD and verifies that they point to a valid resource. It uses the L<Pod::Simple> parser
884             to analyze the pod files and look at their links. In a nutshell, it looks for C<LE<lt>FooE<gt>> links and makes sure that Foo
885             exists. It also recognizes section links, C<LE<lt>/SYNOPSISE<gt>> for example. Also, manpages are resolved and checked.
886              
887             This module does B<NOT> check "http" links like C<LE<lt>http://www.google.comE<gt>> in your pod. For that, please check
888             out L<Test::Pod::No404s>.
889              
890             Normally, you wouldn't want this test to be run during end-user installation because they might not have the modules installed! It is
891             HIGHLY recommended that this be used only for module authors' RELEASE_TESTING phase. To do that, just modify the synopsis to
892             add an env check :)
893              
894             This module normally uses the OO method to run tests, but you can use the functional style too. Just explicitly ask for the C<all_pod_ok> or
895             C<pod_ok> function to be imported when you use this module.
896              
897             #!/usr/bin/perl
898             use strict; use warnings;
899             use Test::Pod::LinkCheck qw( all_pod_ok );
900             all_pod_ok();
901              
902             =head1 ATTRIBUTES
903              
904             =head2 check_cpan
905              
906             If enabled, this module will check the CPAN module database to see if a link is a valid CPAN module or not. It uses the backend
907             defined in L</cpan_backend> to do the actual searching.
908              
909             If disabled, it will resolve links based on locally installed modules. If it isn't installed it will be an error!
910              
911             The default is: true
912              
913             =head2 cpan_backend
914              
915             Selects the CPAN backend to use for querying modules. The available ones are: CPANPLUS, CPAN, CPANSQLite, MetaDB, MetaCPAN, and CPANIDX.
916              
917             The default is: CPANPLUS
918              
919             The backends were tested and verified against those versions. Older versions should work, but is untested!
920             CPANPLUS v0.9010
921             CPAN v1.9402
922             CPAN::SQLite v0.199
923             CPAN::Common::Index::MetaDB v0.005
924             MetaCPAN::API::Tiny v1.131730
925             MetaCPAN::Client v1.007001
926             LWP::UserAgent v6.06
927              
928             =head2 cpan_backend_auto
929              
930             Enable to automatically try the CPAN backends to find an available one. It will try the backends in the order defined in L</cpan_backend>
931              
932             If no backend is available, it will disable the L</check_cpan> attribute and enable the L</cpan_section_err> attribute.
933              
934             The default is: true
935              
936             =head2 cpan_section_err
937              
938             If enabled, a link pointing to a CPAN module's specific section is treated as an error if it isn't installed.
939              
940             The default is: false
941              
942             =head2 verbose
943              
944             If enabled, this module will print extra diagnostics for the links it's checking.
945              
946             The default is: copy $ENV{HARNESS_IS_VERBOSE} or $ENV{TEST_VERBOSE} or false
947              
948             =head1 METHODS
949              
950             =head2 pod_ok
951              
952             Accepts the filename to check, and an optional test name.
953              
954             This method will pass the test if there is no POD links present in the POD or if all links are not an error. Furthermore, if the POD was
955             malformed as reported by L<Pod::Simple>, the test will fail and not attempt to check the links.
956              
957             When it fails, this will show any failing links as diagnostics. Also, some extra information is printed if verbose is enabled.
958              
959             The default test name is: "LinkCheck test for FILENAME"
960              
961             =head2 all_pod_ok
962              
963             Accepts an optional array of files to check. By default it uses all POD files in your distribution.
964              
965             This method is what you will usually run. Every file is passed to the L</pod_ok> function. This also sets the
966             test plan to be the number of files.
967              
968             =head1 NOTES
969              
970             =head2 backend
971              
972             This module uses the L<CPANPLUS> and L<CPAN> modules as the backend to verify valid CPAN modules. Please make sure that the backend you
973             choose is properly configured before running this! This means the index is updated, permissions is set, and whatever else the backend
974             needs to properly function. If you don't want to use them please disable the L</check_cpan> attribute.
975              
976             If this module fails to check CPAN modules or the testsuite fails, it's probably because of the above reason.
977              
978             =head2 CPAN module sections
979              
980             One limitation of this module is that it can't check for valid sections on CPAN modules if they aren't installed. If you want that to be an
981             error, please enable the L</cpan_section_err> attribute.
982              
983             =head1 SEE ALSO
984              
985             Please see those modules/websites for more information related to this module.
986              
987             =over 4
988              
989             =item *
990              
991             L<App::PodLinkCheck|App::PodLinkCheck>
992              
993             =item *
994              
995             L<Pod::Checker|Pod::Checker>
996              
997             =item *
998              
999             L<Test::Pod::No404s|Test::Pod::No404s>
1000              
1001             =back
1002              
1003             =head1 SUPPORT
1004              
1005             =head2 Perldoc
1006              
1007             You can find documentation for this module with the perldoc command.
1008              
1009             perldoc Test::Pod::LinkCheck
1010              
1011             =head2 Websites
1012              
1013             The following websites have more information about this module, and may be of help to you. As always,
1014             in addition to those websites please use your favorite search engine to discover more resources.
1015              
1016             =over 4
1017              
1018             =item *
1019              
1020             MetaCPAN
1021              
1022             A modern, open-source CPAN search engine, useful to view POD in HTML format.
1023              
1024             L<http://metacpan.org/release/Test-Pod-LinkCheck>
1025              
1026             =item *
1027              
1028             Search CPAN
1029              
1030             The default CPAN search engine, useful to view POD in HTML format.
1031              
1032             L<http://search.cpan.org/dist/Test-Pod-LinkCheck>
1033              
1034             =item *
1035              
1036             RT: CPAN's Bug Tracker
1037              
1038             The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN.
1039              
1040             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Test-Pod-LinkCheck>
1041              
1042             =item *
1043              
1044             AnnoCPAN
1045              
1046             The AnnoCPAN is a website that allows community annotations of Perl module documentation.
1047              
1048             L<http://annocpan.org/dist/Test-Pod-LinkCheck>
1049              
1050             =item *
1051              
1052             CPAN Ratings
1053              
1054             The CPAN Ratings is a website that allows community ratings and reviews of Perl modules.
1055              
1056             L<http://cpanratings.perl.org/d/Test-Pod-LinkCheck>
1057              
1058             =item *
1059              
1060             CPAN Forum
1061              
1062             The CPAN Forum is a web forum for discussing Perl modules.
1063              
1064             L<http://cpanforum.com/dist/Test-Pod-LinkCheck>
1065              
1066             =item *
1067              
1068             CPANTS
1069              
1070             The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution.
1071              
1072             L<http://cpants.cpanauthors.org/dist/overview/Test-Pod-LinkCheck>
1073              
1074             =item *
1075              
1076             CPAN Testers
1077              
1078             The CPAN Testers is a network of smokers who run automated tests on uploaded CPAN distributions.
1079              
1080             L<http://www.cpantesters.org/distro/T/Test-Pod-LinkCheck>
1081              
1082             =item *
1083              
1084             CPAN Testers Matrix
1085              
1086             The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms.
1087              
1088             L<http://matrix.cpantesters.org/?dist=Test-Pod-LinkCheck>
1089              
1090             =item *
1091              
1092             CPAN Testers Dependencies
1093              
1094             The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution.
1095              
1096             L<http://deps.cpantesters.org/?module=Test::Pod::LinkCheck>
1097              
1098             =back
1099              
1100             =head2 Email
1101              
1102             You can email the author of this module at C<APOCAL at cpan.org> asking for help with any problems you have.
1103              
1104             =head2 Internet Relay Chat
1105              
1106             You can get live help by using IRC ( Internet Relay Chat ). If you don't know what IRC is,
1107             please read this excellent guide: L<http://en.wikipedia.org/wiki/Internet_Relay_Chat>. Please
1108             be courteous and patient when talking to us, as we might be busy or sleeping! You can join
1109             those networks/channels and get help:
1110              
1111             =over 4
1112              
1113             =item *
1114              
1115             irc.perl.org
1116              
1117             You can connect to the server at 'irc.perl.org' and join this channel: #perl-help then talk to this person for help: Apocalypse.
1118              
1119             =item *
1120              
1121             irc.freenode.net
1122              
1123             You can connect to the server at 'irc.freenode.net' and join this channel: #perl then talk to this person for help: Apocal.
1124              
1125             =item *
1126              
1127             irc.efnet.org
1128              
1129             You can connect to the server at 'irc.efnet.org' and join this channel: #perl then talk to this person for help: Ap0cal.
1130              
1131             =back
1132              
1133             =head2 Bugs / Feature Requests
1134              
1135             Please report any bugs or feature requests by email to C<bug-test-pod-linkcheck at rt.cpan.org>, or through
1136             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-Pod-LinkCheck>. You will be automatically notified of any
1137             progress on the request by the system.
1138              
1139             =head2 Source Code
1140              
1141             The code is open to the world, and available for you to hack on. Please feel free to browse it and play
1142             with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull
1143             from your repository :)
1144              
1145             L<https://github.com/apocalypse/perl-test-pod-linkcheck>
1146              
1147             git clone git://github.com/apocalypse/perl-test-pod-linkcheck.git
1148              
1149             =head1 AUTHOR
1150              
1151             Apocalypse <APOCAL@cpan.org>
1152              
1153             =head1 COPYRIGHT AND LICENSE
1154              
1155             This software is copyright (c) 2014 by Apocalypse.
1156              
1157             This is free software; you can redistribute it and/or modify it under
1158             the same terms as the Perl 5 programming language system itself.
1159              
1160             The full text of the license can be found in the
1161             F<LICENSE> file included with this distribution.
1162              
1163             =head1 DISCLAIMER OF WARRANTY
1164              
1165             THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
1166             APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
1167             HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
1168             OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
1169             THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1170             PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
1171             IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
1172             ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
1173              
1174             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
1175             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
1176             THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
1177             GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
1178             USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
1179             DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
1180             PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
1181             EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
1182             SUCH DAMAGES.
1183              
1184             =cut