File Coverage

blib/lib/Test/Stream/Plugin/Core.pm
Criterion Covered Total %
statement 214 216 99.0
branch 63 72 87.5
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     102   725 use strict;
  102         127  
  102         2386  
3 102     102   311 use warnings;
  102         109  
  102         2113  
4              
5 102     102   307 use Scalar::Util qw/reftype refaddr/;
  102         136  
  102         4755  
6 102     102   362 use Carp qw/croak confess carp/;
  102         104  
  102         3862  
7              
8 102     102   367 use Test::Stream::Sync();
  102         130  
  102         1596  
9              
10 102     102   33596 use Test::Stream::Table qw/table/;
  102         170  
  102         538  
11              
12 102     102   2480 use Test::Stream::Context qw/context/;
  102         137  
  102         506  
13 102         1260 use Test::Stream::Util qw{
14             protect
15             get_stash
16             parse_symbol
17             update_mask
18             render_ref
19 102     102   420 };
  102         126  
20              
21 102     102   430 use Test::Stream::Exporter qw/import default_exports/;
  102         113  
  102         462  
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   481 no Test::Stream::Exporter;
  102         114  
  102         1181  
35              
36             sub set_encoding {
37 3     3 1 17 my $enc = shift;
38 3         22 my $format = Test::Stream::Sync->stack->top->format;
39              
40 3 100 66     20 unless ($format && eval { $format->can('encoding') }) {
  2         19  
41 1 50       6 $format = '' unless defined $format;
42 1         196 croak "Unable to set encoding on formatter '$format'";
43             }
44              
45 2         10 $format->encoding($enc);
46             }
47              
48             sub pass {
49 3     3 1 11 my ($name) = @_;
50 3         8 my $ctx = context();
51 3         12 $ctx->ok(1, $name);
52 3         11 $ctx->release;
53 3         10 return 1;
54             }
55              
56             sub fail {
57 2     2 1 13 my ($name, @diag) = @_;
58 2         6 my $ctx = context();
59 2         8 $ctx->ok(0, $name, \@diag);
60 2         7 $ctx->release;
61 2         7 return 0;
62             }
63              
64             sub ok($;$@) {
65 751     751 1 6124 my ($bool, $name, @diag) = @_;
66 751         1582 my $ctx = context();
67 751         1887 $ctx->ok($bool, $name, \@diag);
68 751         1953 $ctx->release;
69 749 100       2232 return $bool ? 1 : 0;
70             }
71              
72             sub ref_is($$;$@) {
73 28     28 1 117 my ($got, $exp, $name, @diag) = @_;
74 28         61 my $ctx = context();
75              
76 28 100       67 $got = '' unless defined $got;
77 28 100       50 $exp = '' unless defined $exp;
78              
79 28         29 my $bool = 0;
80 28 100       84 if (!ref($got)) {
    100          
81 2         15 $ctx->ok(0, $name, ["First argument '$got' is not a reference", @diag]);
82             }
83             elsif(!ref($exp)) {
84 2         14 $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         71 $bool = refaddr($got) == refaddr($exp);
89 24         130 $ctx->ok($bool, $name, ["'$got' is not the same reference as '$exp'", @diag]);
90             }
91              
92 28         82 $ctx->release;
93 28 100       97 return $bool ? 1 : 0;
94             }
95              
96             sub ref_is_not($$;$) {
97 6     6 1 24 my ($got, $exp, $name, @diag) = @_;
98 6         16 my $ctx = context();
99              
100 6 100       18 $got = '' unless defined $got;
101 6 100       13 $exp = '' unless defined $exp;
102              
103 6         9 my $bool = 0;
104 6 100       17 if (!ref($got)) {
    100          
105 2         14 $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         15 $ctx->ok($bool, $name, ["'$got' is the same reference as '$exp'", @diag]);
114             }
115              
116 6         16 $ctx->release;
117 6 100       23 return $bool ? 1 : 0;
118             }
119              
120             sub diag {
121 11     11 1 89 my $ctx = context();
122 11         47 $ctx->diag( join '', @_ );
123 11         52 $ctx->release;
124             }
125              
126             sub note {
127 5     5 1 22 my $ctx = context();
128 5         26 $ctx->note( join '', @_ );
129 5         32 $ctx->release;
130             }
131              
132             sub BAIL_OUT {
133 3     3 1 15 my ($reason) = @_;
134 3         11 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 17 my ($reason) = @_;
141 4         15 my $ctx = context();
142 4         20 $ctx->plan(0, SKIP => $reason);
143 0 0       0 $ctx->release if $ctx;
144             }
145              
146             sub plan {
147 11     11 1 76303 my ($max) = @_;
148 11         50 my $ctx = context();
149 11         149 $ctx->plan($max);
150 11         121 $ctx->release;
151             }
152              
153             update_mask('*', '*', __PACKAGE__ . '::done_testing', {lock => 1});
154             sub done_testing {
155 99     99 1 763 my $ctx = context();
156 99         432 $ctx->hub->finalize($ctx->debug, 1);
157 98         380 $ctx->release;
158             }
159              
160             sub todo {
161 6     6 1 48 my $reason = shift;
162 6         11 my $code = shift;
163              
164 6         21 my $ctx = context();
165 6         27 my $todo = $ctx->hub->set_todo($reason);
166 6         22 $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         9 push @_ => $todo;
173 3         15 goto &$code;
174             }
175              
176             sub skip {
177 2     2 1 17 my ($why, $num) = @_;
178 2   100     13 $num ||= 1;
179 2         8 my $ctx = context();
180 2         13 $ctx->debug->set_skip($why);
181 2         28 $ctx->ok(1, "skipped test") for 1 .. $num;
182 2         11 $ctx->debug->set_skip(undef);
183 2         16 $ctx->release;
184 102     102   492 no warnings 'exiting';
  102         127  
  102         20970  
185 2         12 last SKIP;
186             }
187              
188             # For easier grepping
189             # sub isa_ok is defined here
190             # sub can_ok is defined here
191             # sub DOES_ok is defined here
192             BEGIN {
193 102     102   262 for my $op (qw/isa can DOES/) {
194             my $sub = sub($;@) {
195 174     174   801 my ($thing, @items) = @_;
196 174         672 my $ctx = context();
197              
198 174         508 my $file = $ctx->debug->file;
199 174         401 my $line = $ctx->debug->line;
200              
201 174         208 my @bad;
202 174         268 for my $item (@items) {
203 286         580 my $bool;
204 286     286   1452 protect { eval qq/#line $line "$file"\n\$bool = \$thing->$op(\$item); 1/ };
  286         9848  
205 286 100       1216 next if $bool;
206              
207 12         29 push @bad => $item;
208             }
209              
210 174         479 my $name = render_ref($thing);
211              
212             $ctx->ok(
213             !@bad,
214             @items == 1 ? "$name\->$op('$items[0]')" : "$name\->$op(...)",
215 174 100       1333 [map { "Failed: $name\->$op('$_')" } @bad],
  12         68  
216             );
217              
218 174         524 $ctx->release;
219              
220 174         522 return !@bad;
221 306         951 };
222 102     102   415 no strict 'refs';
  102         114  
  102         3367  
223 306         372 *{$op . "_ok"} = $sub;
  306         62392  
224             }
225             }
226              
227             sub ref_ok($;$$) {
228 13     13 1 56 my ($thing, $wanttype, $name) = @_;
229 13         29 my $ctx = context();
230              
231 13         41 my $gotname = render_ref($thing);
232 13         29 my $gottype = reftype($thing);
233              
234 13 100       31 if (!$gottype) {
235 2         13 $ctx->ok(0, $name, ["'$gotname' is not a reference"]);
236 2         7 $ctx->release;
237 2         8 return 0;
238             }
239              
240 11 100 66     50 if ($wanttype && $gottype ne $wanttype) {
241 1         9 $ctx->ok(0, $name, ["'$gotname' is not a '$wanttype' reference"]);
242 1         4 $ctx->release;
243 1         4 return 0;
244             }
245              
246 10         32 $ctx->ok(1, $name);
247 10         30 $ctx->release;
248 10         25 return 1;
249             }
250              
251             sub _imported {
252 35     35   53 my $caller = shift;
253              
254 35         128 my $stash = get_stash($caller);
255 35         38 my @missing;
256 35         68 for my $item (@_) {
257 253         388 my ($name, $type) = parse_symbol($item);
258              
259 253 100       651 if(my $glob = $stash->{$name}) {
260 213         148 my $val = *{$glob}{$type};
  213         325  
261 213 100       472 next if defined $val;
262             }
263              
264 42         51 push @missing => $item;
265             }
266              
267 35         96 return @missing;
268             }
269              
270             sub imported_ok {
271 25     25 1 470 my $caller = caller;
272              
273 25         150 my $ctx = context();
274              
275 25         96 my @missing = _imported($caller, @_);
276              
277 25         188 $ctx->ok(!@missing, "Imported expected symbols", [map { "'$_' was not imported." } @missing]);
  1         7  
278              
279 25         103 $ctx->release;
280              
281 25         93 return !@missing;
282             }
283              
284             sub not_imported_ok {
285 10     10 1 40 my $caller = caller;
286              
287 10         23 my $ctx = context();
288              
289 10         29 my %missing = map {$_ => 1} _imported($caller, @_);
  41         68  
290              
291 10         20 my @found = grep { !$missing{$_} } @_;
  42         66  
292              
293 10         34 $ctx->ok(!@found, "Did not import symbols", [map { "'$_' was imported." } @found]);
  1         7  
294              
295 10         34 $ctx->release;
296              
297 10         35 return !@found;
298             }
299              
300             our %OPS = (
301             '==' => 'num',
302             '!=' => 'num',
303             '>=' => 'num',
304             '<=' => 'num',
305             '>' => 'num',
306             '<' => 'num',
307             '<=>' => 'num',
308              
309             'eq' => 'str',
310             'ne' => 'str',
311             'gt' => 'str',
312             'lt' => 'str',
313             'ge' => 'str',
314             'le' => 'str',
315             'cmp' => 'str',
316             '!~' => 'str',
317             '=~' => 'str',
318              
319             '&&' => 'logic',
320             '||' => 'logic',
321             'xor' => 'logic',
322             'or' => 'logic',
323             'and' => 'logic',
324             '//' => 'logic',
325              
326             '&' => 'bitwise',
327             '|' => 'bitwise',
328              
329             '~~' => 'match',
330             );
331             sub cmp_ok($$$;$@) {
332 13     13 1 60 my ($got, $op, $exp, $name, @diag) = @_;
333              
334 13         37 my $ctx = context();
335              
336             # warnings and syntax errors should report to the cmp_ok call, not the test
337             # context, they may not be the same.
338 13         40 my ($pkg, $file, $line) = caller;
339              
340 13         29 my $type = $OPS{$op};
341 13 100       27 if (!$type) {
342 2         327 carp "operator '$op' is not supported (you can add it to %Test::Stream::Plugin::Core::OPS)";
343 2         5 $type = 'unsupported';
344             }
345              
346 13         72 local ($@, $!, $SIG{__DIE__});
347              
348 13         13 my $test;
349 13         901 my $lived = eval <<" EOT";
350             #line $line "(eval in cmp_ok) $file"
351             \$test = (\$got $op \$exp);
352             1;
353             EOT
354 13         317 my $error = $@;
355 13 100       44 $ctx->send_event('Exception', error => $error) unless $lived;
356              
357 13 100 66     55 if ($test && $lived) {
358 7         24 $ctx->ok(1, $name);
359 7         25 $ctx->release;
360 7         48 return 1;
361             }
362              
363             # Uhg, it failed, do roughly the same thing Test::More did to try and show
364             # diagnostics, but make it better by showing both the overloaded and
365             # unoverloaded form if overloading is in play. Also unoverload numbers,
366             # Test::More only unoverloaded strings.
367              
368 6         8 my ($display_got, $display_exp);
369 6 100       22 if($type eq 'str') {
    100          
370 2 50       9 $display_got = defined($got) ? "$got" : undef;
371 2 50       16 $display_exp = defined($exp) ? "$exp" : undef;
372             }
373             elsif($type eq 'num') {
374 2 50       13 $display_got = defined($got) ? sprintf("%D", $got) : undef;
375 2 50       13 $display_exp = defined($exp) ? sprintf("%D", $exp) : undef;
376             }
377             else { # Well, we did what we could.
378 2         4 $display_got = $got;
379 2         3 $display_exp = $exp;
380             }
381              
382 6 100       25 my $got_ref = ref($got) ? render_ref($got) : $got;
383 6 100       16 my $exp_ref = ref($exp) ? render_ref($exp) : $exp;
384              
385 6         8 my @table;
386 6   66     45 my $show_both = (
387             (defined($got) && $got_ref ne "$display_got")
388             ||
389             (defined($exp) && $exp_ref ne "$display_exp")
390             );
391              
392 6 100       15 if ($show_both) {
393 3 100       23 @table = table(
394             header => ['type', 'got', 'op', 'check'],
395             rows => [
396             [$type, $display_got, $op, $lived ? $display_exp : ''],
397             ['orig', $got_ref, '', $exp_ref],
398             ],
399             );
400             }
401             else {
402 3 100       23 @table = table(
403             header => ['got', 'op', 'check'],
404             rows => [[$display_got, $op, $lived ? $display_exp : '']],
405             );
406             }
407              
408 6         66 $ctx->ok(0, $name, [@table, @diag]);
409 6         13 $ctx->release;
410 6         40 return 0;
411             }
412              
413             1;
414              
415             __END__