File Coverage

blib/lib/Test/Numeric.pm
Criterion Covered Total %
statement 90 98 91.8
branch 53 62 85.4
condition 26 42 61.9
subroutine 20 22 90.9
pod 10 10 100.0
total 199 234 85.0


line stmt bran cond sub pod time code
1 8     8   38087 use strict;
  8         20  
  8         391  
2 8     8   43 use warnings;
  8         15  
  8         486  
3              
4             package Test::Numeric;
5              
6             our $VERSION = '0.3';
7              
8 8     8   54 use Test::Builder;
  8         13  
  8         12967  
9             require Exporter;
10             our @ISA = qw(Exporter);
11             our @EXPORT = qw(
12             is_number isnt_number
13             is_integer isnt_integer
14             is_even is_odd
15             is_formatted isnt_formatted
16             is_money isnt_money
17             );
18              
19             my $Test = Test::Builder->new;
20              
21             sub import {
22 7     7   60 my ($self) = shift;
23 7         24 my $pack = caller;
24              
25 7         38 $Test->exported_to($pack);
26 7         78 $Test->plan(@_);
27              
28 7         5496 $self->export_to_level( 1, $self, $_ ) for @EXPORT;
29             }
30              
31             =head1 NAME
32              
33             Test::Numeric - Testing utilities for numbers.
34              
35             =head1 SYNOPSIS
36              
37             use Test::Numeric tests => 8;
38              
39             # The following functions are all exported by Test::Numeric
40              
41             =for example
42 1     1   1173 use Test::Numeric;
  1         3  
  1         6  
43              
44             =for example begin
45              
46             is_number '12.34e56', "valid number";
47             is_number '-12.34E56', "valid number";
48             isnt_number 'test', "not a number";
49              
50             is_even 2, "an even number";
51             is_odd 3, "an odd number";
52            
53             is_integer '123', 'an integer';
54             isnt_integer '123.45', 'not an integer';
55            
56             is_formatted '1-.2', '123.45';
57             isnt_formatted '1-.2', '123.4';
58              
59             =for example end
60              
61             =head1 DESCRIPTION
62              
63             This is a simple testing module that lets you do several tests on
64             numbers. You can check that it is a number, check that it is an
65             integer, check if they are odd or even and finally check if they are
66             of a certain form.
67              
68             =cut
69              
70             ################################################################################
71              
72             sub _test_number {
73 181     181   12817 my $number = shift;
74              
75 181 100 66     903 return 0 unless defined $number && length $number;
76              
77             # Accept obviously right things.
78 179 100       846 return 1 if $number =~ m/^\d+$/;
79              
80             # Throw out obviously wrong things.
81 36 100       158 return 0 if $number =~ m/[^+\-\.eE0-9]/;
82              
83             # Split the number into parts.
84 29         135 my ( $num, $e, $exp ) = split /(e|E)/, $number, 2;
85              
86             # Check that the exponent is valid.
87 29 100       63 if ($e) { return 0 unless $exp =~ m/^[+\-]?\d+$/; }
  12 100       59  
88              
89             # Check the number.
90 27 100       91 return 0 unless $num =~ m/\d/;
91 24 100       112 return 0 unless $num =~ m/^[+\-]?\d*\.?\d*$/;
92              
93 19         86 return 1;
94             }
95              
96             =pod
97              
98             =over 4
99              
100             =item is_number
101              
102             is_number $number, $name;
103              
104             C tests whether C<$number> is a number. The number can be
105             positive or negative, it can have a formatted point and an
106             exponent. These are all valid numbers: 1, 23, 0.34, .34, -12.34e56
107              
108             =item isnt_number
109              
110             The opposite of C.
111              
112             =cut
113              
114             sub is_number {
115 2     2 1 1933 my ( $test, $name ) = @_;
116 2         6 $Test->ok( _test_number($test), $name );
117             }
118              
119             sub isnt_number {
120 2     2 1 2624 my ( $test, $name ) = @_;
121 2         6 $Test->ok( !_test_number($test), $name );
122             }
123              
124             ################################################################################
125              
126             sub _test_integer {
127 150     150   194 my $number = shift;
128 150 100       230 return undef unless _test_number($number);
129 145 100       962 return 1 if $number =~ m/^[+\-]?\d+\.?0*$/;
130             #return int($number) == $number;
131 2         14 return 0;
132             }
133              
134             sub is_integer {
135 0     0 1 0 my ( $test, $name ) = @_;
136 0         0 my $result = _test_integer( $test );
137 0 0       0 $Test->diag("The value given is not a number - failing test.")
138             unless defined $result;
139 0   0     0 $Test->ok( defined $result && $result, $name );
140             }
141              
142             sub isnt_integer {
143 0     0 1 0 my ( $test, $name ) = @_;
144 0         0 my $result = _test_integer( $test );
145 0 0       0 $Test->diag("The value given is not a number - failing test.")
146             unless defined $result;
147 0   0     0 $Test->ok( defined $result && ! $result, $name );
148             }
149              
150             =pod
151              
152             =item is_integer
153              
154             is_integer $number, $name;
155              
156             C tests if C<$number> is an integer, ie a whole
157             number. Fails if the number is not a number r not a number at all.
158              
159             =item isnt_integer
160              
161             The opposite of C. Note that C will fail if
162             the number is not a number. So 'abc' may not be an integer but
163             C will still fail.
164              
165             =cut
166              
167             ################################################################################
168              
169             sub _test_even {
170 12     12   5024 my $number = shift;
171 12 100       26 return undef unless _test_integer($number);
172 10 100       64 return $number % 2 == 0 ? 1 : 0;
173             }
174              
175             sub _test_odd {
176 12     12   4525 my $number = shift;
177 12 100       29 return undef unless _test_integer($number);
178 10 100       60 return $number % 2 == 0 ? 0 : 1;
179             }
180              
181             =pod
182              
183             =item is_even
184              
185             is_even $number, $name;
186              
187             C tests if the number given is even. Fails for non-integers. Zero is even.
188              
189             =item is_odd
190              
191             As C, but for odd numbers.
192              
193             =cut
194              
195             sub is_even {
196 2     2 1 1782 my ( $test, $name ) = @_;
197 2         6 my $result = _test_even( $test );
198 2 50       7 $Test->diag('The number in not an integer - failing test.')
199             unless defined $result;
200 2   66     17 $Test->ok( defined $result && $result, $name );
201             }
202              
203             sub is_odd {
204 2     2 1 2577 my ( $test, $name ) = @_;
205 2         7 my $result = _test_odd( $test );
206 2 50       9 $Test->diag('The number in not an integer - failing test.')
207             unless defined $result;
208 2   66     14 $Test->ok( defined $result && $result, $name );
209             }
210              
211             ################################################################################
212              
213             sub _split_format_error {
214 2     2   4 my $format = shift;
215 2         8 $Test->diag("The format '$format' is not valid");
216 2         149 return 0;
217             }
218              
219             sub _split_format {
220 41     41   3075 my $format = shift;
221 41         62 my @returns = ();
222              
223 41         106 my ( $pre, $suf ) = split /\./, $format, 2;
224              
225 41         73 foreach my $arg ( $pre, $suf ) {
226 77 100 66     339 return _split_format_error($format) unless defined $arg && length $arg;
227              
228 74         204 my ( $min, $sep, $max ) = split /(\-)/, $arg, 2;
229              
230 74 100 100     243 unless ( defined $max && length $max ) {
231 67 100       120 $max = $sep ? undef: $min;
232             }
233              
234 74 100 66     122 return _split_format_error($format)
235             unless _test_integer($min) && $min >= 0;
236              
237 71 100 66     301 if ( defined $max && length $max ) {
238 52 50 33     72 return _split_format_error($format)
239             unless _test_integer($max) && $max >= $min;
240             }
241              
242 71         177 push @returns, $min, $max;
243             }
244              
245 35         268 return @returns;
246             }
247              
248             sub _test_formatted {
249 33     33   4391 my $format = shift;
250 33         119 my $number = shift;
251              
252 33         56 my ( $pre_min, $pre_max, $suf_min, $suf_max ) = _split_format($format);
253 33 100       71 return undef unless defined $suf_min;
254              
255 31 50       69 my ( $pre_len, $suf_len ) = map { defined $_ ? length $_ : 0 } split /\./,
  62         142  
256             $number, 2;
257              
258 31 50       74 return 0 unless $pre_len >= $pre_min;
259 31 100       72 return 0 unless $suf_len >= $suf_min;
260              
261 24 100       51 if ( defined $pre_max ) { return 0 unless $pre_len <= $pre_max }
  16 100       42  
262 20 100       38 if ( defined $suf_max ) { return 0 unless $suf_len <= $suf_max }
  17 100       44  
263              
264 16         53 return 1;
265             }
266              
267             sub is_formatted {
268 3     3 1 3162 my ( $format, $test, $name ) = @_;
269 3         8 my $result = _test_formatted( $format, $test );
270 3   100     19 $Test->ok( defined $result && $result, $name );
271             }
272              
273             sub isnt_formatted {
274 3     3 1 3341 my ( $format, $test, $name ) = @_;
275 3         8 my $result = _test_formatted( $format, $test );
276 3   100     22 $Test->ok( defined $result && !$result, $name );
277             }
278              
279             =pod
280              
281             =item is_formatted
282              
283             is_formatted $format, $number, $name;
284              
285             C allows you to test that the number complies with a
286             certain format. C<$format> tells the function what to check for and is
287             of the form C where C
 and C are the number of 
288             digits before and after the decimal point. They are either just a
289             number ( eg. '3.2' for something like 123.12 ) or a range (
290             eg. '3.1-2' ) for either 123.1 or 123.12 ).
291              
292             The range can be open-ended, for example '0-.2' will match any number
293             of digits before the decimal place, and exactly two after.
294              
295             If the format is incorrect then the test will fail and a warning printed.
296              
297             This test is intended for things such as id numbers where the number must be something like C<000123>.
298              
299             =item isnt_formatted
300              
301             The same as is_formatted but negated.
302              
303             =cut
304              
305             sub is_money {
306 6     6 1 5173 my ( $test, $name ) = @_;
307 6         14 my $result = _test_formatted( '0-.2', $test );
308 6   66     30 $Test->ok( defined $result && $result, $name );
309             }
310              
311             sub isnt_money {
312 6     6 1 5724 my ( $test, $name ) = @_;
313 6         11 my $result = _test_formatted( '0-.2', $test );
314 6   66     577 $Test->ok( defined $result && ! $result, $name );
315             }
316              
317             =pod
318              
319             =item is_money
320              
321             is_money $number, $name;
322              
323             This is a conveniance function to test if the value looks like money,
324             ie has a format of C<0-.2> - which is tw decimal points. Internally it
325             just calls is_formatted with the correct format.
326              
327             =item isnt_money
328              
329             The opposite of C.
330              
331             =back
332              
333             =head1 TODO
334              
335             =over
336              
337             =item *
338              
339             Create appropriate test names if none is given.
340              
341             =item *
342              
343             Add tests to see if a number looks like hex, octal, binary etc.
344              
345             =back
346              
347             =head1 AUTHOR
348              
349             Edmund von der Burg
350              
351             Bug reports, patches, suggestions etc are all welcomed.
352              
353             =head1 COPYRIGHT
354              
355             Copyright 2004 by Edmund von der Burg .
356              
357             This program is free software; you can redistribute it and/or modify
358             it under the same terms as Perl itself.
359              
360             See http://www.perl.com/perl/misc/Artistic.html
361              
362             =head1 SEE ALSO
363              
364             L for testing basics, L for the module on
365             which this one is built.
366              
367             =cut
368              
369             1;