File Coverage

blib/lib/Test2/Tools/Numeric.pm
Criterion Covered Total %
statement 73 73 100.0
branch 10 10 100.0
condition 8 13 61.5
subroutine 19 19 100.0
pod 7 7 100.0
total 117 122 95.9


line stmt bran cond sub pod time code
1             package Test2::Tools::Numeric;
2              
3 2     2   131743 use 5.010;
  2         6  
4 2     2   8 use strict;
  2         3  
  2         37  
5 2     2   7 use warnings;
  2         5  
  2         50  
6              
7 2     2   7 use Test2::API qw( context );
  2         3  
  2         88  
8 2     2   418 use Test2::Tools::Subtest;
  2         357  
  2         100  
9 2     2   420 use Test2::Tools::ClassicCompare;
  2         40318  
  2         138  
10              
11 2     2   10 use Scalar::Util qw( looks_like_number );
  2         2  
  2         111  
12              
13             =head1 NAME
14              
15             Test2::Tools::Numeric - Test functions for common numeric tests
16              
17             =head1 VERSION
18              
19             Version 0.01_01
20              
21             =cut
22              
23             our $VERSION = '0.01_01';
24              
25 2     2   8 use base 'Exporter';
  2         2  
  2         1197  
26              
27             our @EXPORT_OK = qw(
28             is_number
29             is_integer
30             is_positive_integer
31             is_nonnegative_integer
32             cmp_integer_ok
33             is_even
34             is_odd
35             );
36              
37             our @EXPORT = @EXPORT_OK;
38              
39             =head1 SYNOPSIS
40              
41             use Test2::Tools::Numeric;
42              
43             my @array = get_widgets();
44             is_even( scalar @a, '@array must have an even number of widgets' );
45              
46             =head1 WHY TEST2::TOOLS::NUMERIC?
47              
48             Test2::Tools::Numeric is designed to make your code more readable,
49             based on the idea that reading English is easier and less prone to
50             misinterpretation than reading Perl, and less prone to error by
51             reducing common cut & paste tasks.
52              
53             Conside either of these two tests:
54              
55             ok( $x % 2 == 0 );
56             is( $x % 2, 0 );
57              
58             What are they doing? They're testing that C<$x> is an even number.
59             It's a common expression that most programmers can easily identify.
60             Most any programmer will see that and think "Aha, it's testing to
61             see if it's an even number."
62              
63             Better still to make it explicitly clear, in English, what you're
64             trying to accomplish:
65              
66             is_even( $x );
67              
68             Test2::Tools::Numeric also does more stringent checking than the
69             common quick tests that we put in. These tests will all pass. You
70             probably don't want them to.
71              
72             for my $x ( undef, 'foo', {}, [] ) {
73             ok( $x % 2 == 0 );
74             }
75              
76             Here's another one that will pass, albeit with warnings, even though
77             it's undoubtedly a mistake:
78              
79             my %hash = ( foo => 1, bar => 2, bat => 3 );
80             cmp_ok( %hash, '>', 0 );
81              
82             Why does it pass? Because the stringification of that hash is "3/8"
83             and in a numeric context that becomes 3.
84              
85             Test2::Tools::Numeric is based on the idea that the reader should
86             be able to tell as much from English as possible without having to
87             decipher code, and to have extra safety checks that you might not
88             consider.
89              
90             =head1 EXPORT
91              
92             All functions in this module are exported by default.
93              
94             =head1 NUMERIC SUBROUTINES
95              
96             =head2 is_number( $n [, $name ] )
97              
98             Tests that C<$n> is what Perl considers to be a number.
99              
100             =cut
101              
102             sub is_number($;$) {
103 128     128 1 113105 my $n = shift;
104 128         111 my $name = shift;
105              
106 128         172 my $ctx = context();
107              
108 128   100     6823 my $n_desc = $n // 'undef';
109              
110 128         199 my $ok = looks_like_number( $n );
111 128         289 $ctx->ok( $ok, $name );
112 128         9048 $ctx->release();
113              
114 128         981 return $ok;
115             }
116              
117             =head2 is_integer( $n [, $name ] )
118              
119             Tests if C<$n> is an integer.
120              
121             The following are integers:
122              
123             1
124             -1
125             +1
126             0E0
127             9E14
128             -9E14
129              
130             The following are not:
131              
132             string representations of integers
133             1.
134             1.0
135             'abc'
136             ''
137             undef
138             Any reference
139              
140             =cut
141              
142             sub is_integer($;$) {
143 113     113 1 14363 my $n = shift;
144 113   50     371 my $name = shift // '';
145              
146             return subtest_buffered "is_integer( $name )" => sub {
147 113     113   18706 my $ctx = context();
148              
149 113         6508 my $ok = is_number( $n, 'is_integer needs a number' );
150 113 100       575 if ( $ok ) {
151 79         381 $ok = ($n =~ /^[-+]?\d+(?:E\d+)?$/);
152 79         235 $ctx->ok( $ok, "is_integer( $n, $name )" );
153             }
154              
155 113         4158 $ctx->release();
156 113         496 };
157             }
158              
159              
160             =head2 cmp_integer_ok( $got, $op, $expected [, $name ] )
161              
162             Tests that both C<$got> and C<$expected> are valid integers, and match
163             the comparator C<$op>.
164              
165             This is a strengthened version of C. With normal C,
166             you can get back unexpected values that still match, such as:
167              
168             cmp_ok( '', '==', 0 ); # Passes
169             cmp_ok( undef, '==', 0 ); # Passes
170             cmp_ok( 'abc', '==', 0 ); # Passes
171             cmp_ok( 'abc', '==', 'xyz' ); # Passes
172              
173             These will all throw various warnings if the C pragma is on,
174             but the tests will still pass.
175              
176             C is more stringent and will catch accidental passes.
177              
178             cmp_integer_ok( '', '==', 0 ); # Fails
179             cmp_integer_ok( undef, '==', 0 ); # Fails
180              
181             It also checks that your comparator is valid.
182              
183             cmp_integer_ok( 0, 'eq', 0 ); # Fails because 'eq' isn't valid for integers
184              
185             =cut
186              
187             my %valid_integer_op = map { $_ => 1 } qw( == != > >= < <= );
188              
189             sub cmp_integer_ok($$$;$) {
190 49     49 1 13965 my $got = shift;
191 49         45 my $op = shift;
192 49         34 my $expected = shift;
193 49   50     156 my $name = shift // '';
194              
195             return subtest_buffered "cmp_integer_ok( $name )" => sub {
196 49     49   7881 my $ctx = context();
197              
198 49         2569 my $ok = $valid_integer_op{ $op };
199 49         147 $ctx->ok( $ok, "$op is a valid integer operator" );
200              
201 49 100       2650 if ( $ok ) {
202 44 100 66     64 is_integer( $got )
203             and
204             is_integer( $expected )
205             and
206             cmp_ok( $got, $op, $expected );
207             }
208              
209 49         12673 $ctx->release();
210 49         222 };
211             }
212              
213              
214             =head2 is_positive_integer( $n [, $name ] )
215              
216             Verifies that C<$n> is an integer, and greater than zero.
217              
218             =cut
219              
220             sub is_positive_integer($;$) {
221 14     14 1 10198 my $n = shift;
222 14         15 my $name = shift;
223              
224 14         22 return cmp_integer_ok( $n, '>', 0, $name );
225             }
226              
227              
228             =head2 is_nonnegative_integer( $n [, $name ] )
229              
230             Verifies that C<$n> is an integer, and greater than or equal to zero.
231              
232             =cut
233              
234             sub is_nonnegative_integer($;$) {
235 14     14 1 9954 my $n = shift;
236 14         12 my $name = shift;
237              
238 14         23 return cmp_integer_ok( $n, '>=', 0, $name );
239             }
240              
241              
242             =head2 is_even( $n [, $name ] )
243              
244             Checks whether the number C<$n> is an integer and is divisible by two.
245              
246             =cut
247              
248             sub is_even($;$) {
249 15     15 1 11035 my $n = shift;
250 15   50     51 my $name = shift // '';
251              
252             return subtest_buffered "is_even( $name )" => sub {
253 15     15   2349 my $ctx = context();
254 15         758 my $ok = is_integer( $n );
255 15 100       3877 if ( $ok ) {
256 7         24 $ctx->ok( $n % 2 == 0, 'Is it divisible by two?' );
257             }
258              
259 15         438 $ctx->release;
260 15         98 };
261             }
262              
263              
264             =head2 is_odd( $n [, $name ] )
265              
266             Checks whether the number C<$n> is an integer and is not divisible by two.
267              
268             =cut
269              
270             sub is_odd($;$) {
271 17     17 1 11890 my $n = shift;
272 17   50     63 my $name = shift // '';
273              
274             return subtest_buffered "is_odd( $name )" => sub {
275 17     17   2670 my $ok = is_integer( $n );
276 17 100       4484 if ( $ok ) {
277 9         16 my $ctx = context();
278 9         497 $ctx->ok( $n % 2 == 1, 'Is it NOT divisible by two?' );
279 9         744 $ctx->release();
280             }
281 17         66 };
282             }
283              
284              
285             =head1 AUTHOR
286              
287             Andy Lester, C<< >>
288              
289             =head1 BUGS
290              
291             Please report any bugs or feature requests to
292             C,
293             or through the web interface at
294             L.
295             I will be notified, and then you'll automatically be notified of
296             progress on your bug as I make changes.
297              
298             =head1 SUPPORT
299              
300             You can find documentation for this module with the perldoc command.
301              
302             perldoc Test2::Tools::Numeric
303              
304             You can also look for information at:
305              
306             =over 4
307              
308             =item * MetaCPAN
309              
310             L
311              
312             =item * Search CPAN
313              
314             L
315              
316             =item * RT: CPAN's request tracker (report bugs here)
317              
318             L
319              
320             =item * AnnoCPAN: Annotated CPAN documentation
321              
322             L
323              
324             =item * CPAN Ratings
325              
326             L
327              
328             =back
329              
330             =head1 ACKNOWLEDGEMENTS
331              
332             None yet.
333              
334             =head1 LICENSE AND COPYRIGHT
335              
336             Copyright 2016 Andy Lester.
337              
338             This program is free software; you can redistribute it and/or modify it
339             under the terms of the the Artistic License (2.0).
340              
341             =cut
342              
343             1; # End of Test2::Tools::Numeric