File Coverage

blib/lib/Test/Stream/Plugin/Core.pm
Criterion Covered Total %
statement 212 214 99.0
branch 65 74 87.8
condition 10 14 71.4
subroutine 34 34 100.0
pod 18 18 100.0
total 339 354 95.7


line stmt bran cond sub pod time code
1             package Test::Stream::Plugin::Core;
2 102     184   1135 use strict;
  102         196  
  102         2878  
3 102     102   518 use warnings;
  102         194  
  102         3153  
4              
5 102     102   566 use Scalar::Util qw/reftype refaddr/;
  102         201  
  102         6419  
6 102     102   532 use Carp qw/croak confess carp/;
  102         199  
  102         5486  
7              
8 102     102   534 use Test::Stream::Sync;
  102         222  
  102         3076  
9              
10 102     102   56949 use Test::Stream::Table qw/table/;
  102         305  
  102         925  
11              
12 102     102   4114 use Test::Stream::Context qw/context/;
  102         205  
  102         797  
13 102         723 use Test::Stream::Util qw{
14             protect
15             get_stash
16             parse_symbol
17             update_mask
18             render_ref
19 102     102   547 };
  102         204  
20              
21 102     102   545 use Test::Stream::Exporter;
  102         204  
  102         697  
22             default_exports qw{
23             ok pass fail
24             diag note
25             plan skip_all done_testing
26             BAIL_OUT
27             todo skip
28             can_ok isa_ok DOES_ok ref_ok
29             imported_ok not_imported_ok
30             ref_is ref_is_not
31             set_encoding
32             cmp_ok
33             };
34 102     102   652 no Test::Stream::Exporter;
  102         192  
  102         459  
35              
36             sub set_encoding {
37 3     3 1 13 my $enc = shift;
38 3         23 my $format = Test::Stream::Sync->stack->top->format;
39              
40 3 100 66     19 unless ($format && eval { $format->can('encoding') }) {
  2         20  
41 1 50       5 $format = '' unless defined $format;
42 1         185 croak "Unable to set encoding on formatter '$format'";
43             }
44              
45 2         11 $format->encoding($enc);
46             }
47              
48             sub pass {
49 3     3 1 12 my ($name) = @_;
50 3         13 my $ctx = context();
51 3         16 $ctx->ok(1, $name);
52 3         12 $ctx->release;
53 3         11 return 1;
54             }
55              
56             sub fail {
57 2     2 1 14 my ($name, @diag) = @_;
58 2         7 my $ctx = context();
59 2         8 $ctx->ok(0, $name, \@diag);
60 2         8 $ctx->release;
61 2         6 return 0;
62             }
63              
64             sub ok($;$@) {
65 747     747 1 9756 my ($bool, $name, @diag) = @_;
66 747         2259 my $ctx = context();
67 747         3576 $ctx->ok($bool, $name, \@diag);
68 747         2480 $ctx->release;
69 745 100       3088 return $bool ? 1 : 0;
70             }
71              
72             sub ref_is($$;$@) {
73 28     28 1 145 my ($got, $exp, $name, @diag) = @_;
74 28         138 my $ctx = context();
75              
76 28 100       83 $got = '' unless defined $got;
77 28 100       63 $exp = '' unless defined $exp;
78              
79 28         41 my $bool = 0;
80 28 100       110 if (!ref($got)) {
    100          
81 2         13 $ctx->ok(0, $name, ["First argument '$got' is not a reference", @diag]);
82             }
83             elsif(!ref($exp)) {
84 2         10 $ctx->ok(0, $name, ["Second argument '$exp' is not a reference", @diag]);
85             }
86             else {
87             # Don't let overloading mess with us.
88 24         83 $bool = refaddr($got) == refaddr($exp);
89 24         152 $ctx->ok($bool, $name, ["'$got' is not the same reference as '$exp'", @diag]);
90             }
91              
92 28         101 $ctx->release;
93 28 100       127 return $bool ? 1 : 0;
94             }
95              
96             sub ref_is_not($$;$) {
97 6     6 1 32 my ($got, $exp, $name, @diag) = @_;
98 6         16 my $ctx = context();
99              
100 6 100       16 $got = '' unless defined $got;
101 6 100       15 $exp = '' unless defined $exp;
102              
103 6         9 my $bool = 0;
104 6 100       19 if (!ref($got)) {
    100          
105 2         11 $ctx->ok(0, $name, ["First argument '$got' is not a reference", @diag]);
106             }
107             elsif(!ref($exp)) {
108 2         14 $ctx->ok(0, $name, ["Second argument '$exp' is not a reference", @diag]);
109             }
110             else {
111             # Don't let overloading mess with us.
112 2         9 $bool = refaddr($got) != refaddr($exp);
113 2         13 $ctx->ok($bool, $name, ["'$got' is the same reference as '$exp'", @diag]);
114             }
115              
116 6         17 $ctx->release;
117 6 100       25 return $bool ? 1 : 0;
118             }
119              
120             sub diag {
121 11     11 1 91 my $ctx = context();
122 11         129 $ctx->diag( join '', @_ );
123 11         72 $ctx->release;
124             }
125              
126             sub note {
127 5     5 1 24 my $ctx = context();
128 5         26 $ctx->note( join '', @_ );
129 5         32 $ctx->release;
130             }
131              
132             sub BAIL_OUT {
133 3     3 1 21 my ($reason) = @_;
134 3         10 my $ctx = context();
135 3         16 $ctx->bail($reason);
136 0 0       0 $ctx->release if $ctx;
137             }
138              
139             sub skip_all {
140 4     4 1 21 my ($reason) = @_;
141 4         15 my $ctx = context();
142 4         22 $ctx->plan(0, SKIP => $reason);
143 0 0       0 $ctx->release if $ctx;
144             }
145              
146             sub plan {
147 11     11 1 76 my ($max) = @_;
148 11         63 my $ctx = context();
149 11         62 $ctx->plan($max);
150 11         141 $ctx->release;
151             }
152              
153             update_mask('*', '*', __PACKAGE__ . '::done_testing', {lock => 1});
154             sub done_testing {
155 99     99 1 717 my $ctx = context();
156 99         603 $ctx->hub->finalize($ctx->debug, 1);
157 98         892 $ctx->release;
158             }
159              
160             sub todo {
161 6     6 1 41 my $reason = shift;
162 6         10 my $code = shift;
163              
164 6         23 my $ctx = context();
165 6         24 my $todo = $ctx->hub->set_todo($reason);
166 6         25 $ctx->release;
167              
168 6 100       24 return $todo unless $code;
169              
170             # tail-end recursion to remove this stack frame from the stack trace.
171             # We push $todo onto @_ so that it is not destroyed until the sub returns.
172 3         8 push @_ => $todo;
173 3         17 goto &$code;
174             }
175              
176             sub skip {
177 2     2 1 15 my ($why, $num) = @_;
178 2   100     11 $num ||= 1;
179 2         7 my $ctx = context();
180 2         12 $ctx->debug->set_skip($why);
181 2         24 $ctx->ok(1, "skipped test") for 1 .. $num;
182 2         9 $ctx->debug->set_skip(undef);
183 2         15 $ctx->release;
184 102     102   617 no warnings 'exiting';
  102         206  
  102         30441  
185 2         10 last SKIP;
186             }
187              
188             BEGIN {
189 102     102   319 for my $op (qw/isa can DOES/) {
190             my $sub = sub($;@) {
191 168     168   1088 my ($thing, @items) = @_;
192 168         594 my $ctx = context();
193              
194 168         715 my $file = $ctx->debug->file;
195 168         606 my $line = $ctx->debug->line;
196              
197 168         307 my @bad;
198 168         377 for my $item (@items) {
199 280         809 my $bool;
200 280 100   280   1817 protect { eval qq/#line $line "$file"\n\$bool = \$thing->$op(\$item); 1/ || die $@ };
  280         11340  
201 279 100       1737 next if $bool;
202              
203 6         15 push @bad => $item;
204             }
205              
206             $ctx->ok(
207             !@bad,
208             @items == 1 ? "$thing\->$op('$items[0]')" : "$thing\->$op(...)",
209 167 100       1876 [map { "Failed: $thing\->$op('$_')" } @bad],
  6         28  
210             );
211              
212 167         690 $ctx->release;
213              
214 167         674 return !@bad;
215 306         1633 };
216 102     102   592 no strict 'refs';
  102         205  
  102         4461  
217 306         498 *{$op . "_ok"} = $sub;
  306         101328  
218             }
219             }
220              
221             sub ref_ok($;$$) {
222 13     13 1 65 my ($thing, $wanttype, $name) = @_;
223 13         39 my $ctx = context();
224              
225 13         55 my $gottype = reftype($thing);
226              
227 13 100       35 if (!$gottype) {
228 2         10 $ctx->ok(0, $name, ["'$thing' is not a reference"]);
229 2         7 $ctx->release;
230 2         6 return 0;
231             }
232              
233 11 100 66     103 if ($wanttype && $gottype ne $wanttype) {
234 1         9 $ctx->ok(0, $name, ["'$thing' is not a '$wanttype' reference"]);
235 1         5 $ctx->release;
236 1         4 return 0;
237             }
238              
239 10         38 $ctx->ok(1, $name);
240 10         32 $ctx->release;
241 10         35 return 1;
242             }
243              
244             sub _imported {
245 35     35   79 my $caller = shift;
246              
247 35         178 my $stash = get_stash($caller);
248 35         68 my @missing;
249 35         97 for my $item (@_) {
250 252         646 my ($name, $type) = parse_symbol($item);
251              
252 252 100       1033 if(my $glob = $stash->{$name}) {
253 212         262 my $val = *{$glob}{$type};
  212         519  
254 212 100       838 next if defined $val;
255             }
256              
257 42         90 push @missing => $item;
258             }
259              
260 35         119 return @missing;
261             }
262              
263             sub imported_ok {
264 25     25 1 245 my $caller = caller;
265              
266 25         143 my $ctx = context();
267              
268 25         125 my @missing = _imported($caller, @_);
269              
270 25         170 $ctx->ok(!@missing, "Imported expected symbols", [map { "'$_' was not imported." } @missing]);
  1         13  
271              
272 25         131 $ctx->release;
273              
274 25         127 return !@missing;
275             }
276              
277             sub not_imported_ok {
278 10     10 1 55 my $caller = caller;
279              
280 10         37 my $ctx = context();
281              
282 10         38 my %missing = map {$_ => 1} _imported($caller, @_);
  41         111  
283              
284 10         32 my @found = grep { !$missing{$_} } @_;
  42         103  
285              
286 10         55 $ctx->ok(!@found, "Did not import symbols", [map { "'$_' was imported." } @found]);
  1         14  
287              
288 10         39 $ctx->release;
289              
290 10         47 return !@found;
291             }
292              
293             our %OPS = (
294             '==' => 'num',
295             '!=' => 'num',
296             '>=' => 'num',
297             '<=' => 'num',
298             '>' => 'num',
299             '<' => 'num',
300             '<=>' => 'num',
301              
302             'eq' => 'str',
303             'ne' => 'str',
304             'gt' => 'str',
305             'lt' => 'str',
306             'ge' => 'str',
307             'le' => 'str',
308             'cmp' => 'str',
309             '!~' => 'str',
310             '=~' => 'str',
311              
312             '&&' => 'logic',
313             '||' => 'logic',
314             'xor' => 'logic',
315             'or' => 'logic',
316             'and' => 'logic',
317             '//' => 'logic',
318              
319             '&' => 'bitwise',
320             '|' => 'bitwise',
321              
322             '~~' => 'match',
323             );
324             sub cmp_ok($$$;$@) {
325 13     13 1 72 my ($got, $op, $exp, $name, @diag) = @_;
326              
327 13         39 my $ctx = context();
328              
329             # warnings and syntax errors should report to the cmp_ok call, not the test
330             # context, they may not be the same.
331 13         38 my ($pkg, $file, $line) = caller;
332              
333 13         36 my $type = $OPS{$op};
334 13 100       33 if (!$type) {
335 2         401 carp "operator '$op' is not supported (you can add it to %Test::Stream::Plugin::Core::OPS)";
336 2         5 $type = 'unsupported';
337             }
338              
339 13         64 local ($@, $!, $SIG{__DIE__});
340              
341 13         19 my $test;
342 13         930 my $lived = eval <<" EOT";
343             #line $line "(eval in cmp_ok) $file"
344             \$test = (\$got $op \$exp);
345             1;
346             EOT
347 13         284 my $error = $@;
348 13 100       42 $ctx->send_event('Exception', error => $error) unless $lived;
349              
350 13 100 66     56 if ($test && $lived) {
351 7         23 $ctx->ok(1, $name);
352 7         22 $ctx->release;
353 7         64 return 1;
354             }
355              
356             # Uhg, it failed, do roughly the same thing Test::More did to try and show
357             # diagnostics, but make it better by showing both the overloaded and
358             # unoverloaded form if overloading is in play. Also unoverload numbers,
359             # Test::More only unoverloaded strings.
360              
361 6         8 my ($display_got, $display_exp);
362 6 100       23 if($type eq 'str') {
    100          
363 2 50       7 $display_got = defined($got) ? "$got" : undef;
364 2 50       11 $display_exp = defined($exp) ? "$exp" : undef;
365             }
366             elsif($type eq 'num') {
367 2 50       11 $display_got = defined($got) ? sprintf("%D", $got) : undef;
368 2 50       10 $display_exp = defined($exp) ? sprintf("%D", $exp) : undef;
369             }
370             else { # Well, we did what we could.
371 2         4 $display_got = $got;
372 2         5 $display_exp = $exp;
373             }
374              
375 6 100       34 my $got_ref = ref($got) ? render_ref($got) : $got;
376 6 100       22 my $exp_ref = ref($exp) ? render_ref($exp) : $exp;
377              
378 6         9 my @table;
379 6   66     47 my $show_both = (
380             (defined($got) && $got_ref ne "$display_got")
381             ||
382             (defined($exp) && $exp_ref ne "$display_exp")
383             );
384              
385 6 100       19 if ($show_both) {
386 3 100       30 @table = table(
387             header => ['type', 'got', 'op', 'check'],
388             rows => [
389             [$type, $display_got, $op, $lived ? $display_exp : ''],
390             ['orig', $got_ref, '', $exp_ref],
391             ],
392             );
393             }
394             else {
395 3 100       27 @table = table(
396             header => ['got', 'op', 'check'],
397             rows => [[$display_got, $op, $lived ? $display_exp : '']],
398             );
399             }
400              
401 6         88 $ctx->ok(0, $name, [@table, @diag]);
402 6         20 $ctx->release;
403 6         46 return 0;
404             }
405              
406             1;
407              
408             __END__