| 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__ |