File Coverage

blib/lib/App/SimpleScan/Plugin/LinkCheck.pm
Criterion Covered Total %
statement 33 111 29.7
branch 0 24 0.0
condition 0 6 0.0
subroutine 8 21 38.1
pod 4 4 100.0
total 45 166 27.1


line stmt bran cond sub pod time code
1             package App::SimpleScan::Plugin::LinkCheck;
2              
3             $VERSION = '1.03';
4              
5 3     3   108638 use warnings;
  3         9  
  3         206  
6 3     3   18 use strict;
  3         7  
  3         134  
7 3     3   19 use Carp;
  3         11  
  3         452  
8              
9 3     3   22 use Scalar::Util qw(looks_like_number);
  3         7  
  3         548  
10 3     3   4252 use Text::Balanced qw(extract_quotelike extract_multiple);
  3         74534  
  3         376  
11              
12             sub import {
13 3     3   31 no strict 'refs';
  3         6  
  3         9102  
14 3     3   39 *{caller() . '::_do_has_link'} = \&_do_has_link;
  3         22  
15 3         8 *{caller() . '::_do_no_link'} = \&_do_no_link;
  3         16  
16 3         20 *{caller() . '::link_condition'} = \&link_condition;
  3         14  
17 3         7 *{caller() . '::_link_conditions'} = \&_link_conditions;
  3         14  
18 3         8 *{caller() . '::_add_link_condition'} = \&_add_link_condition;
  3         21  
19              
20 3         5 *{caller() . '::_extract_quotelike_args'} =
  3         44  
21             \&_extract_quotelike_args;
22             }
23              
24             sub pragmas {
25 0     0 1   return ['has_link', \&_do_has_link],
26             ['no_link', \&_do_no_link],
27             ['forget_link', \&_do_forget_link],
28             ['forget_all_links', \&_do_forget_all];
29             }
30              
31             sub init {
32 0     0 1   my($class, $app) = @_;
33 0           $app->{Link_conditions} = {};
34             }
35              
36             sub _do_forget_all {
37 0     0     my($self, $args) = @_;
38 0           $self->{Link_conditions} = {};
39             }
40              
41             sub _do_forget_link {
42 0     0     my($self, $args) = @_;
43 0           my @links = $self->_extract_quotelike_args($args);
44 0           for my $link (@links) {
45 0           delete $self->{Link_conditions}->{$link};
46             }
47             }
48              
49             sub _do_has_link {
50 0     0     my($self, $args) = @_;
51 0           my($name, $compare, $count);
52 0 0         if (!defined $args) {
53 0           $self->stack_code( qq(fail "No arguments for %%has_link";\n) );
54 0           $self->test_count( $self->test_count() + 1 );
55 0           return;
56             }
57             else {
58             # Extract strings and backticked strings and just plain words.
59             # We explicitly junk anything past the first three items.
60 0           ($name, $compare, $count) = $self->_extract_quotelike_args($args);
61             }
62 0           $self->_add_link_condition( { name=>$name, compare=>$compare, count=>$count } );
63             }
64              
65             sub _do_no_link {
66 0     0     my($self, $args) = @_;
67 0 0         if (!defined $args) {
68 0           $self->stack_code( qq(fail "No arguments for %%no_link";\n) );
69 0           $self->test_count( $self->test_count() + 1 );
70             }
71             else {
72 0           my ($name) = $self->_extract_quotelike_args($args);
73 0           $self->_do_has_link(qq($name == 0));
74             }
75             }
76              
77             sub _link_conditions {
78 0     0     my ($self) = shift;
79 0 0         return wantarray ? @{ $self->{Link_conditions} } : $self->{Link_conditions};
  0            
80             }
81              
82             sub _add_link_condition {
83 0     0     my ($self, $condition) = @_;
84 0           push @{ $self->{Link_conditions}->{ $condition->{name} } }, $condition;
  0            
85             }
86              
87             sub filters {
88 0     0 1   return \&filter;
89             }
90              
91             sub filter {
92 0     0 1   my($self, @code) = @_;
93             # If we've recursed because of the stack_code in this method, just exit.
94              
95 0 0         return unless defined $self->_link_conditions;
96 0           my $test_count = 0;
97              
98 0           for my $link_name (keys %{$self->_link_conditions()} ) {
  0            
99 0           for my $link_condition ( @{ $self->{Link_conditions}->{$link_name} } ) {
  0            
100 0           my $compare = $link_condition->{compare};
101 0           my $count = $link_condition->{count};
102 0           my $name = $link_condition->{name};
103            
104 0           my $not_bogus = 1;
105 0           my %have_a;
106              
107             # name alone is "at least one link with this name"
108 0 0 0       if (defined $name and (! defined $compare) and (! defined $count) ) {
      0        
109 0           $compare = ">";
110 0           $count = "0";
111             }
112              
113             # Name is always defined, or we'd never have gotten here.
114 0           $name = _dequote($name);
115              
116             # comparison is always defined: either we fixed it just above (because
117             # it was missing altogether), or it's there (but possibly bad).
118 0 0         if (! grep {$compare eq $_} qw(== > < >= <= !=) ) {
  0            
119 0           push @code, qq(fail "$compare is not a legal comparison operator (use < > <= >= == !=)";\n);
120 0           $test_count++;
121 0           $not_bogus = 0;
122             }
123              
124 0 0         if (!defined($count)) {
    0          
125 0           push @code, qq(fail "Missing count";\n);
126 0           $test_count++;
127 0           $not_bogus = 0;
128             }
129             elsif (! looks_like_number($count) ) {
130 0           push @code, qq(fail "$count doesn't look like a legal number to me";\n);
131 0           $test_count++;
132 0           $not_bogus = 0;
133             }
134              
135 0 0         if ($not_bogus) {
136 0           my $last_testspec = $self->get_current_spec;
137 0           $last_testspec->comment( qq('$name' link count $compare $count) );
138              
139 0           push @code, qq(cmp_ok scalar \@{[mech()->find_all_links(text=>qq($name))]}, qq($compare), qq($count), "'$name' link count $compare $count";\n);
140 0           $test_count++;
141 0           @code = _snapshot_hack($self, @code);
142             }
143             }
144             }
145 0           $self->test_count($self->test_count() + $test_count);
146 0           return @code;
147             }
148              
149             sub _snapshot_hack {
150             # Snapshot MUST be called for every test stacked.
151 0     0     my ($self, @code) = @_;
152 0 0         if ($self->can('snapshot')) {
153 0           return &App::SimpleScan::Plugin::Snapshot::filter($self, @code);
154             }
155             else {
156 0           return @code;
157             }
158             }
159              
160             sub _extract_quotelike_args {
161             # Extract strings and backticked strings and just plain words.
162 0     0     my ($self, $string) = @_;
163              
164             # extract_quotelike complains if no quotelike strings were found.
165             # Shut this up.
166 3     3   22 no warnings;
  3         7  
  3         769  
167              
168             # The result of the extract multiple is to give us the whitespace
169             # between words and strings with leading whitespace before the
170             # first word of quotelike strings. Confused? This is what happens:
171             #
172             # for the string
173             # a test `backquoted' "just quoted"
174             # we get
175             # 'a'
176             # ' '
177             # 'test'
178             # ' `backquoted'
179             # `backquoted`
180             # ' '
181             # ' "just'
182             # '"just quoted"'
183             #
184             # We do NOT use grep because if one of the arguments evaluates to
185             # zero, it won't get saved.
186 0           my @wanted;
187 0           foreach my $item
188             (extract_multiple($string, [qr/[^'"`\s]+/,\&extract_quotelike])) {
189 0 0         push @wanted, _dequote($item) if $item !~ /^\s/;
190             }
191 0           return @wanted;
192             }
193              
194             sub _dequote {
195 0     0     my $string = shift;
196 0 0         $string = eval $string if $string =~ /^(['"]).*(\1)$/;
197 0           return $string;
198             }
199              
200              
201             1; # Magic true value required at end of module
202             __END__