File Coverage

blib/lib/Test2/Tools/ClassicCompare.pm
Criterion Covered Total %
statement 164 179 91.6
branch 36 52 69.2
condition 4 12 33.3
subroutine 36 36 100.0
pod 6 10 60.0
total 246 289 85.1


line stmt bran cond sub pod time code
1             package Test2::Tools::ClassicCompare;
2 159     159   1847 use strict;
  159         365  
  159         4392  
3 159     159   788 use warnings;
  159         415  
  159         8079  
4              
5             our $VERSION = '0.000153';
6              
7             our @EXPORT = qw/is is_deeply isnt like unlike cmp_ok/;
8 159     159   948 use base 'Exporter';
  159         329  
  159         15522  
9              
10 159     159   1082 use Carp qw/carp/;
  159         335  
  159         8111  
11 159     159   1000 use Scalar::Util qw/reftype/;
  159         351  
  159         7975  
12              
13 159     159   2159 use Test2::API qw/context/;
  159         146673  
  159         7587  
14 159     159   3167 use Test2::Compare qw/compare strict_convert/;
  159         411  
  159         8276  
15 159     159   993 use Test2::Util::Ref qw/rtype render_ref/;
  159         336  
  159         7736  
16 159     159   3223 use Test2::Util::Table qw/table/;
  159         320  
  159         1336  
17              
18 159     159   3233 use Test2::Compare::Array();
  159         339  
  159         2254  
19 159     159   2919 use Test2::Compare::Bag();
  159         335  
  159         2023  
20 159     159   2692 use Test2::Compare::Custom();
  159         334  
  159         2168  
21 159     159   2753 use Test2::Compare::Event();
  159         349  
  159         2785  
22 159     159   2972 use Test2::Compare::Hash();
  159         447  
  159         2965  
23 159     159   782 use Test2::Compare::Meta();
  159         317  
  159         2849  
24 159     159   2765 use Test2::Compare::Number();
  159         397  
  159         2829  
25 159     159   965 use Test2::Compare::Object();
  159         345  
  159         2839  
26 159     159   2797 use Test2::Compare::OrderedSubset();
  159         363  
  159         4259  
27 159     159   2799 use Test2::Compare::Pattern();
  159         377  
  159         2654  
28 159     159   2639 use Test2::Compare::Ref();
  159         373  
  159         3964  
29 159     159   2703 use Test2::Compare::Regex();
  159         332  
  159         2601  
30 159     159   2646 use Test2::Compare::Scalar();
  159         323  
  159         2769  
31 159     159   2681 use Test2::Compare::Set();
  159         323  
  159         2491  
32 159     159   2697 use Test2::Compare::String();
  159         369  
  159         2701  
33 159     159   2750 use Test2::Compare::Undef();
  159         349  
  159         2672  
34 159     159   2702 use Test2::Compare::Wildcard();
  159         374  
  159         213197  
35              
36             sub is($$;$@) {
37 7     7 1 474 my ($got, $exp, $name, @diag) = @_;
38 7         20 my $ctx = context();
39              
40 7         654 my @caller = caller;
41              
42 7         30 my $delta = compare($got, $exp, \&is_convert);
43              
44 7 100       33 if ($delta) {
45 3         10 $ctx->fail($name, $delta->diag, @diag);
46             }
47             else {
48 4         13 $ctx->ok(1, $name);
49             }
50              
51 7         1694 $ctx->release;
52 7         212 return !$delta;
53             }
54              
55             sub isnt($$;$@) {
56 4     4 1 50 my ($got, $exp, $name, @diag) = @_;
57 4         11 my $ctx = context();
58              
59 4         328 my @caller = caller;
60              
61 4         14 my $delta = compare($got, $exp, \&isnt_convert);
62              
63 4 100       17 if ($delta) {
64 1         5 $ctx->fail($name, $delta->diag, @diag);
65             }
66             else {
67 3         9 $ctx->ok(1, $name);
68             }
69              
70 4         967 $ctx->release;
71 4         147 return !$delta;
72             }
73              
74             sub is_convert {
75 7     7 0 15 my ($thing) = @_;
76 7 100       32 return Test2::Compare::Undef->new()
77             unless defined $thing;
78 5         25 return Test2::Compare::String->new(input => $thing);
79             }
80              
81             sub isnt_convert {
82 4     4 0 9 my ($thing) = @_;
83 4 50       13 return Test2::Compare::Undef->new()
84             unless defined $thing;
85 4         18 my $str = Test2::Compare::String->new(input => $thing, negate => 1);
86             }
87              
88             sub like($$;$@) {
89 4     4 1 42 my ($got, $exp, $name, @diag) = @_;
90 4         12 my $ctx = context();
91              
92 4         349 my $delta = compare($got, $exp, \&like_convert);
93              
94 4 100       16 if ($delta) {
95 1         5 $ctx->fail($name, $delta->diag, @diag);
96             }
97             else {
98 3         11 $ctx->ok(1, $name);
99             }
100              
101 4         962 $ctx->release;
102 4         117 return !$delta;
103             }
104              
105             sub unlike($$;$@) {
106 3     3 1 44 my ($got, $exp, $name, @diag) = @_;
107 3         10 my $ctx = context();
108              
109 3         250 my $delta = compare($got, $exp, \&unlike_convert);
110              
111 3 100       12 if ($delta) {
112 1         4 $ctx->fail($name, $delta->diag, @diag);
113             }
114             else {
115 2         9 $ctx->ok(1, $name);
116             }
117              
118 3         695 $ctx->release;
119 3         87 return !$delta;
120             }
121              
122             sub like_convert {
123 4     4 0 8 my ($thing) = @_;
124 4         28 return Test2::Compare::Pattern->new(
125             pattern => $thing,
126             stringify_got => 1,
127             );
128             }
129              
130             sub unlike_convert {
131 3     3 0 5 my ($thing) = @_;
132 3         12 return Test2::Compare::Pattern->new(
133             negate => 1,
134             stringify_got => 1,
135             pattern => $thing,
136             );
137             }
138              
139             sub is_deeply($$;$@) {
140 5     5 1 290 my ($got, $exp, $name, @diag) = @_;
141 5         22 my $ctx = context();
142              
143 5         17078 my @caller = caller;
144              
145 5         30 my $delta = compare($got, $exp, \&strict_convert);
146              
147 5 50       34 if ($delta) {
148             # Temporary thing.
149 0         0 my $count = 0;
150 0         0 my $implicit = 0;
151 0         0 my @deltas = ($delta);
152 0         0 while (my $d = shift @deltas) {
153 0         0 my $add = $d->children;
154 0 0 0     0 push @deltas => @$add if $add && @$add;
155 0 0       0 next if $d->verified;
156 0         0 $count++;
157 0 0 0     0 $implicit++ if $d->note && $d->note eq 'implicit end';
158             }
159              
160 0 0       0 if ($implicit == $count) {
161 0         0 $ctx->ok(1, $name);
162 0 0       0 my $meth = $ENV{AUTHOR_TESTING} ? 'throw' : 'alert';
163 0         0 my $type = $delta->render_check;
164 0         0 $ctx->$meth(
165             join "\n",
166             "!!! NOTICE OF BEHAVIOR CHANGE !!!",
167             "This test uses at least 1 $type check without using end() or etc().",
168             "The exising behavior is to default to etc() when inside is_deeply().",
169             "The new behavior is to default to end().",
170             "This test will soon start to fail with the following diagnostics:",
171             $delta->diag->as_string,
172             "",
173             );
174             }
175             else {
176 0         0 $ctx->fail($name, $delta->diag, @diag);
177             }
178             }
179             else {
180 5         28 $ctx->ok(1, $name);
181             }
182              
183 5         2440 $ctx->release;
184 5         196 return !$delta;
185             }
186              
187             our %OPS = (
188             '==' => 'num',
189             '!=' => 'num',
190             '>=' => 'num',
191             '<=' => 'num',
192             '>' => 'num',
193             '<' => 'num',
194             '<=>' => 'num',
195              
196             'eq' => 'str',
197             'ne' => 'str',
198             'gt' => 'str',
199             'lt' => 'str',
200             'ge' => 'str',
201             'le' => 'str',
202             'cmp' => 'str',
203             '!~' => 'str',
204             '=~' => 'str',
205              
206             '&&' => 'logic',
207             '||' => 'logic',
208             'xor' => 'logic',
209             'or' => 'logic',
210             'and' => 'logic',
211             '//' => 'logic',
212              
213             '&' => 'bitwise',
214             '|' => 'bitwise',
215              
216             '~~' => 'match',
217             );
218             sub cmp_ok($$$;$@) {
219 15     15 1 1636 my ($got, $op, $exp, $name, @diag) = @_;
220              
221 15         39 my $ctx = context();
222              
223             # Warnings and syntax errors should report to the cmp_ok call, not the test
224             # context. They may not be the same.
225 15         1258 my ($pkg, $file, $line) = caller;
226              
227 15         49 my $type = $OPS{$op};
228 15 100       41 if (!$type) {
229 2         450 carp "operator '$op' is not supported (you can add it to %Test2::Tools::ClassicCompare::OPS)";
230 2         6 $type = 'unsupported';
231             }
232              
233 15         66 local ($@, $!, $SIG{__DIE__});
234              
235 15         24 my $test;
236 15         1024 my $lived = eval <<" EOT";
237             #line $line "(eval in cmp_ok) $file"
238             \$test = (\$got $op \$exp);
239             1;
240             EOT
241 15         361 my $error = $@;
242 15 100       44 $ctx->send_event('Exception', error => $error) unless $lived;
243              
244 15 100 66     525 if ($test && $lived) {
245 7         29 $ctx->ok(1, $name);
246 7         1810 $ctx->release;
247 7         296 return 1;
248             }
249              
250             # Ugh, it failed. Do roughly the same thing Test::More did to try and show
251             # diagnostics, but make it better by showing both the overloaded and
252             # unoverloaded form if overloading is in play. Also unoverload numbers,
253             # Test::More only unoverloaded strings.
254              
255 8         16 my ($display_got, $display_exp);
256 8 100       38 if($type eq 'str') {
    100          
257 2 50       6 $display_got = defined($got) ? "$got" : undef;
258 2 50       9 $display_exp = defined($exp) ? "$exp" : undef;
259             }
260             elsif($type eq 'num') {
261 4 50       17 $display_got = defined($got) ? $got + 0 : undef;
262 4 50       13 $display_exp = defined($exp) ? $exp + 0 : undef;
263             }
264             else { # Well, we did what we could.
265 2         4 $display_got = $got;
266 2         4 $display_exp = $exp;
267             }
268              
269 8 100       27 my $got_ref = ref($got) ? render_ref($got) : $got;
270 8 100       21 my $exp_ref = ref($exp) ? render_ref($exp) : $exp;
271              
272 8         14 my @table;
273 8   66     59 my $show_both = (
274             (defined($got) && $got_ref ne "$display_got")
275             ||
276             (defined($exp) && $exp_ref ne "$display_exp")
277             );
278              
279 8 100       21 if ($show_both) {
280 4 100       35 @table = table(
281             header => ['TYPE', 'GOT', 'OP', 'CHECK'],
282             rows => [
283             [$type, $display_got, $op, $lived ? $display_exp : ''],
284             ['orig', $got_ref, '', $exp_ref],
285             ],
286             );
287             }
288             else {
289 4 100       25 @table = table(
290             header => ['GOT', 'OP', 'CHECK'],
291             rows => [[$display_got, $op, $lived ? $display_exp : '']],
292             );
293             }
294              
295 8         35603 $ctx->ok(0, $name, [join("\n", @table), @diag]);
296 8         5835 $ctx->release;
297 8         268 return 0;
298             }
299              
300              
301             1;
302              
303             __END__