File Coverage

blib/lib/Test/Stream/Plugin/Compare.pm
Criterion Covered Total %
statement 213 213 100.0
branch 94 94 100.0
condition 8 8 100.0
subroutine 57 57 100.0
pod 28 31 90.3
total 400 403 99.2


line stmt bran cond sub pod time code
1             package Test::Stream::Plugin::Compare;
2 100     100   744 use strict;
  100         95  
  100         2249  
3 100     100   285 use warnings;
  100         103  
  100         2160  
4              
5 100     100   310 use Test::Stream::Exporter qw/import default_exports exports/;
  100         91  
  100         464  
6             default_exports qw/is like/;
7             exports qw{
8             match mismatch validator
9             hash array object meta number string
10             in_set not_in_set check_set
11             item field call prop check
12             end filter_items
13             T F D DNE FDNE
14             event
15             exact_ref
16             };
17 100     100   389 no Test::Stream::Exporter;
  100         103  
  100         285  
18              
19 100     100   340 use Carp qw/croak/;
  100         111  
  100         3638  
20 100     100   334 use Scalar::Util qw/reftype blessed/;
  100         99  
  100         3960  
21              
22 100     100   30603 use Test::Stream::Compare qw/compare get_build push_build pop_build build/;
  100         150  
  100         318  
23 100     100   491 use Test::Stream::Context qw/context/;
  100         118  
  100         563  
24 100     100   376 use Test::Stream::Util qw/rtype/;
  100         118  
  100         287  
25              
26 100     100   36185 use Test::Stream::Compare::Array();
  100         157  
  100         1575  
27 100     100   33899 use Test::Stream::Compare::Custom();
  100         154  
  100         1421  
28 100     100   33490 use Test::Stream::Compare::Event();
  100         199  
  100         1493  
29 100     100   34789 use Test::Stream::Compare::Hash();
  100         159  
  100         1506  
30 100     100   409 use Test::Stream::Compare::Meta();
  100         114  
  100         1054  
31 100     100   33879 use Test::Stream::Compare::Number();
  100         182  
  100         1518  
32 100     100   485 use Test::Stream::Compare::Object();
  100         112  
  100         1101  
33 100     100   33133 use Test::Stream::Compare::Pattern();
  100         154  
  100         1410  
34 100     100   32955 use Test::Stream::Compare::Ref();
  100         169  
  100         1493  
35 100     100   32421 use Test::Stream::Compare::Regex();
  100         158  
  100         1430  
36 100     100   32719 use Test::Stream::Compare::Scalar();
  100         160  
  100         1432  
37 100     100   33588 use Test::Stream::Compare::Set();
  100         179  
  100         1605  
38 100     100   32070 use Test::Stream::Compare::String();
  100         173  
  100         1482  
39 100     100   32750 use Test::Stream::Compare::Undef();
  100         181  
  100         1391  
40 100     100   32332 use Test::Stream::Compare::Value();
  100         159  
  100         1477  
41 100     100   32449 use Test::Stream::Compare::Wildcard();
  100         174  
  100         164175  
42              
43             sub is($$;$@) {
44 1450     1450 1 8168965 my ($got, $exp, $name, @diag) = @_;
45 1450         2762 my $ctx = context();
46              
47 1450         3859 my $delta = compare($got, $exp, \&strict_convert);
48              
49 1450 100       2483 if ($delta) {
50 78         182 $ctx->ok(0, $name, [$delta->table, @diag]);
51             }
52             else {
53 1372         3121 $ctx->ok(1, $name);
54             }
55              
56 1450         3340 $ctx->release;
57 1450         3564 return !$delta;
58             }
59              
60             sub like($$;$@) {
61 434     434 1 1141 my ($got, $exp, $name, @diag) = @_;
62 434         1002 my $ctx = context();
63              
64 434         1256 my $delta = compare($got, $exp, \&relaxed_convert);
65              
66 434 100       901 if ($delta) {
67 4         13 $ctx->ok(0, $name, [$delta->table, @diag]);
68             }
69             else {
70 430         1136 $ctx->ok(1, $name);
71             }
72              
73 434         1124 $ctx->release;
74 434         1070 return !$delta;
75             }
76              
77 4     4 1 33 sub meta(&) { build('Test::Stream::Compare::Meta', @_) }
78 10     10 1 58 sub hash(&) { build('Test::Stream::Compare::Hash', @_) }
79 199     199 1 1162 sub array(&) { build('Test::Stream::Compare::Array', @_) }
80 125     125 1 558 sub object(&) { build('Test::Stream::Compare::Object', @_) }
81              
82             my $FDNE = Test::Stream::Compare::Custom->new(code => sub { $_ ? 0 : 1 }, name => 'FALSE', operator => 'FALSE() || !exists');
83             my $DNE = Test::Stream::Compare::Custom->new(code => sub { my %p = @_; $p{exists} ? 0 : 1 }, name => '', operator => '!exists');
84             my $F = Test::Stream::Compare::Custom->new(code => sub { my %p = @_; $p{got} ? 0 : $p{exists} }, name => 'FALSE', operator => 'FALSE()');
85             my $T = Test::Stream::Compare::Custom->new(code => sub { $_ ? 1 : 0 }, name => 'TRUE', operator => 'TRUE()');
86             my $D = Test::Stream::Compare::Custom->new(code => sub { defined $_ ? 1 : 0 }, name => 'DEFINED', operator => 'DEFINED()');
87              
88 62     62 1 300 sub T() { $T }
89 32     32 1 97 sub F() { $F }
90 6     6 1 16 sub D() { $D }
91 53     53 1 273 sub DNE() { $DNE }
92 6     6 1 16 sub FDNE() { $FDNE }
93              
94 5213     5213 0 7791 sub strict_convert { convert($_[0], 1) }
95 2295     2295 0 3425 sub relaxed_convert { convert($_[0], 0) }
96              
97             sub exact_ref($) {
98 19     19 1 82 my @caller = caller;
99 19         97 return Test::Stream::Compare::Ref->new(
100             file => $caller[1],
101             lines => [$caller[2]],
102             input => $_[0],
103             );
104             }
105              
106             sub match($) {
107 30     30 1 165 my @caller = caller;
108 30         139 return Test::Stream::Compare::Pattern->new(
109             file => $caller[1],
110             lines => [$caller[2]],
111             pattern => $_[0],
112             );
113             }
114              
115             sub mismatch($) {
116 4     4 1 31 my @caller = caller;
117 4         24 return Test::Stream::Compare::Pattern->new(
118             file => $caller[1],
119             lines => [$caller[2]],
120             negate => 1,
121             pattern => $_[0],
122             );
123             }
124              
125             sub validator {
126 109     109 1 479 my $code = pop;
127 109         99 my $cname = pop;
128 109         71 my $op = pop;
129              
130 109         223 my @caller = caller;
131 109         346 return Test::Stream::Compare::Custom->new(
132             file => $caller[1],
133             lines => [$caller[2]],
134             code => $code,
135             name => $cname,
136             operator => $op,
137             );
138             }
139              
140             sub number($;@) {
141 2     2 1 6 my ($num, @args) = @_;
142 2         6 my @caller = caller;
143 2         15 return Test::Stream::Compare::Number->new(
144             file => $caller[1],
145             lines => [$caller[2]],
146             input => $num,
147             @args,
148             );
149             }
150              
151             sub string($;@) {
152 2     2 1 6 my ($str, @args) = @_;
153 2         5 my @caller = caller;
154 2         11 return Test::Stream::Compare::String->new(
155             file => $caller[1],
156             lines => [$caller[2]],
157             input => $str,
158             @args,
159             );
160             }
161              
162             sub filter_items(&) {
163 5 100   5 1 33 my $build = get_build() or croak "No current build!";
164              
165 4 100       95 croak "'$build' does not support filters"
166             unless $build->can('add_filter');
167              
168 3 100       138 croak "'filter_items' should only ever be called in void context"
169             if defined wantarray;
170              
171 2         8 $build->add_filter(@_);
172             }
173              
174             sub end() {
175 139 100   139 1 471 my $build = get_build() or croak "No current build!";
176              
177 138 100       503 croak "'$build' does not support 'ending'"
178             unless $build->can('ending');
179              
180 137 100       307 croak "'end' should only ever be called in void context"
181             if defined wantarray;
182              
183 136         360 $build->set_ending(1);
184             }
185              
186             sub call($$) {
187 1074     1074 1 1650 my ($name, $expect) = @_;
188 1074 100       1446 my $build = get_build() or croak "No current build!";
189              
190 1073 100       2280 croak "'$build' does not support method calls"
191             unless $build->can('add_call');
192              
193 1072 100       1496 croak "'call' should only ever be called in void context"
194             if defined wantarray;
195              
196 1071         1718 my @caller = caller;
197 1071         2549 $build->add_call(
198             $name,
199             Test::Stream::Compare::Wildcard->new(
200             expect => $expect,
201             file => $caller[1],
202             lines => [$caller[2]],
203             ),
204             );
205             }
206              
207             sub prop($$) {
208 217     217 1 480 my ($name, $expect) = @_;
209 217 100       350 my $build = get_build() or croak "No current build!";
210              
211 216 100       612 croak "'$build' does not support meta-checks"
212             unless $build->can('add_prop');
213              
214 215 100       409 croak "'prop' should only ever be called in void context"
215             if defined wantarray;
216              
217 214         416 my @caller = caller;
218 214         678 $build->add_prop(
219             $name,
220             Test::Stream::Compare::Wildcard->new(
221             expect => $expect,
222             file => $caller[1],
223             lines => [$caller[2]],
224             ),
225             );
226             }
227              
228             sub item($;$) {
229 196     196 1 298 my @args = @_;
230 196         150 my $expect = pop @args;
231              
232 196 100       313 my $build = get_build() or croak "No current build!";
233              
234 195 100       511 croak "'$build' does not support array item checks"
235             unless $build->can('add_item');
236              
237 194 100       353 croak "'item' should only ever be called in void context"
238             if defined wantarray;
239              
240 193         334 my @caller = caller;
241 193         457 push @args => Test::Stream::Compare::Wildcard->new(
242             expect => $expect,
243             file => $caller[1],
244             lines => [$caller[2]],
245             );
246              
247 193         382 $build->add_item(@args);
248             }
249              
250             sub field($$) {
251 80     80 1 238 my ($name, $expect) = @_;
252              
253 80 100       123 my $build = get_build() or croak "No current build!";
254              
255 79 100       310 croak "'$build' does not support hash field checks"
256             unless $build->can('add_field');
257              
258 78 100       200 croak "'field' should only ever be called in void context"
259             if defined wantarray;
260              
261 77         141 my @caller = caller;
262 77         289 $build->add_field(
263             $name,
264             Test::Stream::Compare::Wildcard->new(
265             expect => $expect,
266             file => $caller[1],
267             lines => [$caller[2]],
268             ),
269             );
270             }
271              
272             sub check($) {
273 18     18 1 45 my ($check) = @_;
274              
275 18 100       28 my $build = get_build() or croak "No current build!";
276              
277 17 100       120 croak "'$build' is not a check-set"
278             unless $build->can('add_check');
279              
280 16 100       95 croak "'check' should only ever be called in void context"
281             if defined wantarray;
282              
283 15         23 my @caller = caller;
284 15         51 my $wc = Test::Stream::Compare::Wildcard->new(
285             expect => $check,
286             file => $caller[1],
287             lines => [$caller[2]],
288             );
289              
290 15         26 $build->add_check($wc);
291             }
292              
293 4     4 1 18 sub check_set { return _build_set('all' => @_) }
294 13     13 1 35 sub in_set { return _build_set('any' => @_) }
295 4     4 1 15 sub not_in_set { return _build_set('none' => @_) }
296              
297             sub _build_set {
298 21     21   23 my $redux = shift;
299 21         18 my ($builder) = @_;
300 21   100     68 my $btype = reftype($builder) || '';
301              
302 21         13 my $set;
303 21 100       32 if ($btype eq 'CODE') {
304 7         16 $set = build('Test::Stream::Compare::Set', $builder);
305 6         20 $set->set_builder($builder);
306             }
307             else {
308 14         42 $set = Test::Stream::Compare::Set->new(checks => [@_]);
309             }
310              
311 20         47 $set->set_reduction($redux);
312 20         60 return $set;
313             }
314              
315             sub event($;$) {
316 301     301 1 1807 my ($intype, $spec) = @_;
317              
318 301         660 my @caller = caller;
319              
320 301 100       684 croak "type is required" unless $intype;
321              
322 300         281 my $type;
323 300 100       580 if ($intype =~ m/^\+(.*)$/) {
324 1         3 $type = $1;
325             }
326             else {
327 299         550 $type = "Test::Stream::Event::$intype";
328             }
329              
330 300         250 my $event;
331 300 100       1231 if (!$spec) {
    100          
    100          
332 1         5 $event = Test::Stream::Compare::Event->new(
333             etype => $intype,
334             file => $caller[1],
335             lines => [$caller[2]],
336             );
337             }
338             elsif (!ref $spec) {
339 1         81 croak "'$spec' is not a valid event specification"
340             }
341             elsif (reftype($spec) eq 'CODE') {
342 117         298 $event = build('Test::Stream::Compare::Event', $spec);
343 117         334 $event->set_etype($intype),
344             $event->set_builder($spec);
345             }
346             else {
347 181         810 my $refcheck = Test::Stream::Compare::Hash->new(
348             inref => $spec,
349             file => $caller[1],
350             lines => [$caller[2]],
351             );
352 181         691 $event = Test::Stream::Compare::Event->new(
353             refcheck => $refcheck,
354             file => $caller[1],
355             lines => [$caller[2]],
356             etype => $intype,
357             );
358             }
359              
360 299         1140 $event->add_prop('blessed' => $type);
361              
362 299 100       540 return $event if defined wantarray;
363              
364 294 100       554 my $build = get_build() or croak "No current build!";
365 293         697 $build->add_item($event);
366             }
367              
368             sub convert {
369 9184     9184 0 10438 my ($thing, $strict) = @_;
370              
371 9184 100       13280 return Test::Stream::Compare::Undef->new()
372             unless defined $thing;
373              
374 8649 100 100     40657 if ($thing && blessed($thing) && $thing->isa('Test::Stream::Compare')) {
      100        
375 2715 100       8076 return $thing unless $thing->isa('Test::Stream::Compare::Wildcard');
376 1667         3081 my $newthing = convert($thing->expect, $strict);
377 1667 100       3105 $newthing->set_builder($thing->builder) unless $newthing->builder;
378 1667 100       9527 $newthing->set_file($thing->_file) unless $newthing->_file;
379 1667 100       9767 $newthing->set_lines($thing->_lines) unless $newthing->_lines;
380 1667         9508 return $newthing;
381             }
382              
383 5934         10920 my $type = rtype($thing);
384              
385 5934 100       11381 return Test::Stream::Compare::Array->new(inref => $thing, $strict ? (ending => 1) : ())
    100          
386             if $type eq 'ARRAY';
387              
388 5228 100       8494 return Test::Stream::Compare::Hash->new(inref => $thing, $strict ? (ending => 1) : ())
    100          
389             if $type eq 'HASH';
390              
391 4612 100       6172 unless ($strict) {
392 1475 100       3532 return Test::Stream::Compare::Pattern->new(
393             pattern => $thing,
394             stringify_got => 1,
395             ) if $type eq 'REGEXP';
396              
397 956 100       1439 return Test::Stream::Compare::Custom->new(code => $thing)
398             if $type eq 'CODE';
399             }
400              
401 4062 100       5161 return Test::Stream::Compare::Regex->new(input => $thing)
402             if $type eq 'REGEXP';
403              
404 4058 100       5045 if ($type eq 'SCALAR') {
405 9         26 my $nested = convert($$thing, $strict);
406 9         28 return Test::Stream::Compare::Scalar->new(item => $nested)
407             }
408              
409 4049 100       5357 return Test::Stream::Compare::Ref->new(input => $thing)
410             if $type;
411              
412             # is() will assume string and use 'eq'
413 3906         9435 return Test::Stream::Compare::String->new(input => $thing);
414             }
415              
416             1;
417              
418             __END__