File Coverage

blib/lib/Test/Uses.pm
Criterion Covered Total %
statement 66 72 91.6
branch 27 34 79.4
condition 5 6 83.3
subroutine 11 12 91.6
pod 2 2 100.0
total 111 126 88.1


line stmt bran cond sub pod time code
1             package Test::Uses;
2             BEGIN {
3 3     3   79324 $Test::Uses::VERSION = '0.01';
4             }
5              
6             # ABSTRACT: Test sources for presence/absence of particular modules
7              
8 3     3   27 use strict;
  3         8  
  3         96  
9 3     3   15 use warnings;
  3         5  
  3         86  
10              
11 3     3   15 use Carp;
  3         6  
  3         304  
12 3     3   2914 use PPI;
  3         516186  
  3         161  
13              
14 3     3   31 use Test::Builder;
  3         6  
  3         136  
15              
16             sub import {
17 3     3   30 my ($self) = @_;
18 3         9 my $caller = caller;
19             {
20 3     3   14 no strict 'refs';
  3         6  
  3         2144  
  3         6  
21 3         6 *{$caller.'::uses_ok'} = \&uses_ok;
  3         18  
22 3         5 *{$caller.'::avoids_ok'} = \&avoids_ok;
  3         2351  
23             }
24             }
25              
26             my $tb = Test::Builder->new();
27              
28             sub uses_ok {
29 13     13 1 18104 my($file, $module, $name) = @_;
30              
31 13         49 _verify($file, $module, $name);
32             }
33              
34             sub avoids_ok {
35 5     5 1 9183 my($file, $module, $name) = @_;
36              
37 5         78 _verify($file, {-avoids => $module}, $name);
38             }
39              
40             sub _verify {
41 18     18   38 my ($file, $descriptor, $name) = @_;
42            
43 18 100       98 my $includes = (ref($descriptor) eq 'HASH') ? $descriptor->{-uses} : $descriptor;
44 18 100       74 my $excludes = (ref($descriptor) eq 'HASH') ? $descriptor->{-avoids} : [];
45 18 100       88 $includes = [$includes] unless (ref($includes) eq 'ARRAY');
46 18 100       77 $excludes = [$excludes] unless (ref($excludes) eq 'ARRAY');
47            
48             # First go through the code, and build an array containing all the modules
49             # referenced. This could be smarter, but it handles the use stuff OK.
50             # We could use a hash, but real code doesn't often use a module more than
51             # once.
52            
53 18         38 my @modules = ();
54 18         147 my $document = PPI::Document->new($file);
55 18         337258 my $requires = $document->find('PPI::Statement::Include');
56 18 50       106710 if ($requires) {
57 18         58 foreach my $declaration (@$requires) {
58 156         1616 my $keyword = $declaration->find_first('PPI::Token::Word');
59 156         24245 $declaration->remove_child($keyword);
60 156         6827 my $module = $declaration->find_first('PPI::Token::Word');
61 156 100 66     29166 if ($keyword && $module) {
62 139         422 push @modules, $module->content();
63             }
64             }
65             }
66            
67             # We need to satisfy all the includes and all the excludes against this hash.
68             # It's also a good idea to generate feedback when needed.
69            
70 18         211 my @missing = ();
71 18         36 my @found = ();
72 18         53 foreach my $entry (@$includes) {
73 20 100       75 next unless ($entry);
74 15 100       38 if (! grep { (ref($entry) eq 'Regexp') ? $_ =~ $entry : $_ eq $entry } @modules) {
  115 100       342  
75 2         9 push @missing, $entry;
76             }
77             }
78 18         50 foreach my $entry (@$excludes) {
79 11 100       34 next unless ($entry);
80 10 100       19 if (grep { (ref($entry) eq 'Regexp') ? $_ =~ $entry : $_ eq $entry } @modules) {
  80 100       220  
81 3         55 push @found, $entry;
82             }
83             }
84            
85 18         55 local $Test::Builder::Level = $Test::Builder::Level + 1;
86            
87 18   100     214 my $result = $tb->ok(! @found && ! @missing, $name);
88 18 100       10509 if (@missing) {
89 2         29 $tb->diag("$file was missing: ".join(", ", map { "$_" } @missing));
  2         25  
90             }
91 18 100       124 if (@found) {
92 2         5 $tb->diag("$file contained: ".join(", ", map { "$_" } @found));
  3         20  
93             }
94            
95 18         196 return $result;
96             }
97              
98             # This marks when we satisfy a descriptor, regardless of whether it is in a uses
99             # or an avoids sense.
100              
101             sub _matches {
102 0     0     my ($modules, $descriptor) = @_;
103            
104 0 0         $descriptor = [$descriptor] unless (ref($descriptor) eq 'ARRAY');
105 0           foreach my $entry (@$descriptor) {
106 0 0         if (grep { (ref($entry) eq 'Regexp') ? $_ =~ $entry : $_ eq $entry } @$modules) {
  0 0          
107 0           return 0;
108             }
109             }
110             }
111              
112             1;
113              
114             =head1 NAME
115              
116             Test::Uses
117              
118             =head1 SYNOPSIS
119              
120             use Test::More tests => $Num_Tests;
121             use Test::Uses;
122            
123             uses_ok($myperlfile, 'strict', "$myperlfile is properly strict");
124             uses_ok($myperlfile, 'File::Spec', "$myperlfile uses File::Spec");
125             uses_ok($myperlfile, qr/^Test::/, "$myperlfile actually does some testing");
126             uses_ok($myperlfile, { -avoids => [qr/^Win32::/],
127             -uses => ['strict', qr/^Test::/] },
128             "$myperlfile does all sorts of stuff, and avoids Win32 modules");
129            
130             avoids_ok($myperlfile, qr/^Win32::/, "Quick way of saying }
131             avoids_ok($myperlfile, ['bytes', qr/^Win32::/], "Quick way of saying we avoid grubby stuff
132            
133             =head1 DESCRIPTION
134              
135             This is a test helper module, so it is designed to be used in cooperation with
136             other test modules, such as Test::Simple and Test::More.
137              
138             The module helps you check through a bunch of code for references to modules
139             you either (a) want to use, or (b) want to avoid. The module reads and parses
140             the file (using L, and, therefore, dependencies of the file are not checked).
141             the syntactic check has some advantages. Because no actual code is loaded, it is
142             safe to use as a test.
143              
144             One of the best reasons for using this, is to handle code where your production
145             environment may limit use of modules. This test allows you to avoid modules that
146             you know are going to cause problems, by adding test cases which fail when people
147             write code that uses them.
148              
149             Because pragmas are invoked similarly, you can also detect use of "bad" pragmas.
150              
151             Note that a pragma turned off (e.g., "no bytes") still counts as using the
152             pragma, and will be found as a use by this module. This seemed more sensible
153             to me, as in virtually all cases, using "no" loads the component and requires it
154             to function, and this is generally what you are trying to find using these tests.
155              
156             Test::Uses is not the same as L or L,
157             which checks that these modules can be L()d successfully.
158              
159             =head1 FUNCTIONS
160              
161             =head2 uses_ok($filename, $module, $testname);
162              
163             This test succeeds of the passed file does use this particular module. This looks
164             for a use statement referring to this module. The module specification can be one
165             of the following:
166              
167             =over 4
168              
169             =item *
170              
171             A string module name
172              
173             =item *
174              
175             A regular expression value, based on the qr// quoting
176              
177             =item *
178              
179             An arrayref of multiple values, all of which should be satisfied
180              
181             =item *
182              
183             A hashref of specifications, keyed by -uses and -avoids. All the
184             -uses specifications must be met, and none of the -avoids specifications
185             must be present
186              
187             =back
188              
189             =head2 avoids_ok($filename, $module, $testname);
190              
191             A convenient shortcut for:
192              
193             uses_ok($filename, {-avoids => $module}, $testname);
194              
195             =head1 TODO
196              
197             This module is based on L, and uses it to parse the text. This might
198             well change at some point.
199              
200             =over 4
201              
202             =item
203              
204             Add some handling for require, at least when the cases are obvious
205              
206             =item
207              
208             Add some handling for test cases, such as use_ok
209              
210             =back
211              
212             =head1 AUTHOR
213              
214             Stuart Watt Estuart@morungos.comE
215              
216             =head1 COPYRIGHT
217              
218             Copyright 2010 by the authors.
219              
220             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
221              
222             =head1 SEE ALSO
223              
224             L is used to parse the module.
225              
226             =cut