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     157   1178 use strict;
  102         195  
  102         2663  
3 102     102   529 use warnings;
  102         238  
  102         3292  
4              
5 102     102   574 use Scalar::Util qw/reftype refaddr/;
  102         196  
  102         6493  
6 102     102   518 use Carp qw/croak confess carp/;
  102         194  
  102         5421  
7              
8 102     102   525 use Test::Stream::Sync;
  102         222  
  102         3018  
9              
10 102     102   56956 use Test::Stream::Table qw/table/;
  102         343  
  102         903  
11              
12 102     102   4047 use Test::Stream::Context qw/context/;
  102         206  
  102         737  
13 102         730 use Test::Stream::Util qw{
14             protect
15             get_stash
16             parse_symbol
17             update_mask
18             render_ref
19 102     102   584 };
  102         199  
20              
21 102     102   550 use Test::Stream::Exporter;
  102         199  
  102         703  
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   668 no Test::Stream::Exporter;
  102         189  
  102         473  
35              
36             sub set_encoding {
37 3     3 1 17 my $enc = shift;
38 3         21 my $format = Test::Stream::Sync->stack->top->format;
39              
40 3 100 66     21 unless ($format && eval { $format->can('encoding') }) {
  2         22  
41 1 50       5 $format = '' unless defined $format;
42 1         168 croak "Unable to set encoding on formatter '$format'";
43             }
44              
45 2         12 $format->encoding($enc);
46             }
47              
48             sub pass {
49 3     3 1 15 my ($name) = @_;
50 3         16 my $ctx = context();
51 3         22 $ctx->ok(1, $name);
52 3         18 $ctx->release;
53 3         16 return 1;
54             }
55              
56             sub fail {
57 2     2 1 20 my ($name, @diag) = @_;
58 2         12 my $ctx = context();
59 2         12 $ctx->ok(0, $name, \@diag);
60 2         11 $ctx->release;
61 2         8 return 0;
62             }
63              
64             sub ok($;$@) {
65 747     747 1 10232 my ($bool, $name, @diag) = @_;
66 747         2418 my $ctx = context();
67 747         3612 $ctx->ok($bool, $name, \@diag);
68 747         2554 $ctx->release;
69 745 100       3239 return $bool ? 1 : 0;
70             }
71              
72             sub ref_is($$;$@) {
73 28     28 1 152 my ($got, $exp, $name, @diag) = @_;
74 28         176 my $ctx = context();
75              
76 28 100       81 $got = '' unless defined $got;
77 28 100       74 $exp = '' unless defined $exp;
78              
79 28         41 my $bool = 0;
80 28 100       133 if (!ref($got)) {
    100          
81 2         11 $ctx->ok(0, $name, ["First argument '$got' is not a reference", @diag]);
82             }
83             elsif(!ref($exp)) {
84 2         13 $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         95 $bool = refaddr($got) == refaddr($exp);
89 24         157 $ctx->ok($bool, $name, ["'$got' is not the same reference as '$exp'", @diag]);
90             }
91              
92 28         135 $ctx->release;
93 28 100       127 return $bool ? 1 : 0;
94             }
95              
96             sub ref_is_not($$;$) {
97 6     6 1 27 my ($got, $exp, $name, @diag) = @_;
98 6         16 my $ctx = context();
99              
100 6 100       18 $got = '' unless defined $got;
101 6 100       15 $exp = '' unless defined $exp;
102              
103 6         8 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         12 $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         20 $ctx->release;
117 6 100       26 return $bool ? 1 : 0;
118             }
119              
120             sub diag {
121 11     11 1 94 my $ctx = context();
122 11         72 $ctx->diag( join '', @_ );
123 11         86 $ctx->release;
124             }
125              
126             sub note {
127 5     5 1 29 my $ctx = context();
128 5         27 $ctx->note( join '', @_ );
129 5         33 $ctx->release;
130             }
131              
132             sub BAIL_OUT {
133 3     3 1 19 my ($reason) = @_;
134 3         11 my $ctx = context();
135 3         17 $ctx->bail($reason);
136 0 0       0 $ctx->release if $ctx;
137             }
138              
139             sub skip_all {
140 4     4 1 22 my ($reason) = @_;
141 4         16 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 81 my ($max) = @_;
148 11         66 my $ctx = context();
149 11         55 $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 699 my $ctx = context();
156 99         605 $ctx->hub->finalize($ctx->debug, 1);
157 98         921 $ctx->release;
158             }
159              
160             sub todo {
161 6     6 1 43 my $reason = shift;
162 6         12 my $code = shift;
163              
164 6         21 my $ctx = context();
165 6         31 my $todo = $ctx->hub->set_todo($reason);
166 6         24 $ctx->release;
167              
168 6 100       27 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         14 push @_ => $todo;
173 3         16 goto &$code;
174             }
175              
176             sub skip {
177 2     2 1 17 my ($why, $num) = @_;
178 2   100     10 $num ||= 1;
179 2         8 my $ctx = context();
180 2         13 $ctx->debug->set_skip($why);
181 2         24 $ctx->ok(1, "skipped test") for 1 .. $num;
182 2         10 $ctx->debug->set_skip(undef);
183 2         15 $ctx->release;
184 102     102   609 no warnings 'exiting';
  102         206  
  102         30687  
185 2         10 last SKIP;
186             }
187              
188             BEGIN {
189 102     102   330 for my $op (qw/isa can DOES/) {
190             my $sub = sub($;@) {
191 168     168   1125 my ($thing, @items) = @_;
192 168         613 my $ctx = context();
193              
194 168         723 my $file = $ctx->debug->file;
195 168         585 my $line = $ctx->debug->line;
196              
197 168         405 my @bad;
198 168         380 for my $item (@items) {
199 280         807 my $bool;
200 280 100   280   1911 protect { eval qq/#line $line "$file"\n\$bool = \$thing->$op(\$item); 1/ || die $@ };
  280         11821  
201 279 100       1723 next if $bool;
202              
203 6         13 push @bad => $item;
204             }
205              
206             $ctx->ok(
207             !@bad,
208             @items == 1 ? "$thing\->$op('$items[0]')" : "$thing\->$op(...)",
209 167 100       1893 [map { "Failed: $thing\->$op('$_')" } @bad],
  6         26  
210             );
211              
212 167         745 $ctx->release;
213              
214 167         702 return !@bad;
215 306         1631 };
216 102     102   600 no strict 'refs';
  102         204  
  102         4578  
217 306         527 *{$op . "_ok"} = $sub;
  306         101189  
218             }
219             }
220              
221             sub ref_ok($;$$) {
222 13     13 1 60 my ($thing, $wanttype, $name) = @_;
223 13         41 my $ctx = context();
224              
225 13         39 my $gottype = reftype($thing);
226              
227 13 100       35 if (!$gottype) {
228 2         12 $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     60 if ($wanttype && $gottype ne $wanttype) {
234 1         8 $ctx->ok(0, $name, ["'$thing' is not a '$wanttype' reference"]);
235 1         4 $ctx->release;
236 1         4 return 0;
237             }
238              
239 10         34 $ctx->ok(1, $name);
240 10         36 $ctx->release;
241 10         35 return 1;
242             }
243              
244             sub _imported {
245 35     35   82 my $caller = shift;
246              
247 35         170 my $stash = get_stash($caller);
248 35         69 my @missing;
249 35         94 for my $item (@_) {
250 252         717 my ($name, $type) = parse_symbol($item);
251              
252 252 100       1020 if(my $glob = $stash->{$name}) {
253 212         250 my $val = *{$glob}{$type};
  212         562  
254 212 100       846 next if defined $val;
255             }
256              
257 42         95 push @missing => $item;
258             }
259              
260 35         119 return @missing;
261             }
262              
263             sub imported_ok {
264 25     25 1 251 my $caller = caller;
265              
266 25         147 my $ctx = context();
267              
268 25         143 my @missing = _imported($caller, @_);
269              
270 25         167 $ctx->ok(!@missing, "Imported expected symbols", [map { "'$_' was not imported." } @missing]);
  1         14  
271              
272 25         148 $ctx->release;
273              
274 25         127 return !@missing;
275             }
276              
277             sub not_imported_ok {
278 10     10 1 58 my $caller = caller;
279              
280 10         40 my $ctx = context();
281              
282 10         46 my %missing = map {$_ => 1} _imported($caller, @_);
  41         108  
283              
284 10         28 my @found = grep { !$missing{$_} } @_;
  42         100  
285              
286 10         56 $ctx->ok(!@found, "Did not import symbols", [map { "'$_' was imported." } @found]);
  1         12  
287              
288 10         48 $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 156 my ($got, $op, $exp, $name, @diag) = @_;
326              
327 13         43 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         41 my ($pkg, $file, $line) = caller;
332              
333 13         35 my $type = $OPS{$op};
334 13 100       37 if (!$type) {
335 2         350 carp "operator '$op' is not supported (you can add it to %Test::Stream::Plugin::Core::OPS)";
336 2         6 $type = 'unsupported';
337             }
338              
339 13         70 local ($@, $!, $SIG{__DIE__});
340              
341 13         19 my $test;
342 13         870 my $lived = eval <<" EOT";
343             #line $line "(eval in cmp_ok) $file"
344             \$test = (\$got $op \$exp);
345             1;
346             EOT
347 13         296 my $error = $@;
348 13 100       49 $ctx->send_event('Exception', error => $error) unless $lived;
349              
350 13 100 66     60 if ($test && $lived) {
351 7         26 $ctx->ok(1, $name);
352 7         27 $ctx->release;
353 7         49 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         10 my ($display_got, $display_exp);
362 6 100       27 if($type eq 'str') {
    100          
363 2 50       9 $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       12 $display_got = defined($got) ? sprintf("%D", $got) : undef;
368 2 50       13 $display_exp = defined($exp) ? sprintf("%D", $exp) : undef;
369             }
370             else { # Well, we did what we could.
371 2         5 $display_got = $got;
372 2         3 $display_exp = $exp;
373             }
374              
375 6 100       39 my $got_ref = ref($got) ? render_ref($got) : $got;
376 6 100       21 my $exp_ref = ref($exp) ? render_ref($exp) : $exp;
377              
378 6         11 my @table;
379 6   66     49 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       22 if ($show_both) {
386 3 100       29 @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       30 @table = table(
396             header => ['got', 'op', 'check'],
397             rows => [[$display_got, $op, $lived ? $display_exp : '']],
398             );
399             }
400              
401 6         91 $ctx->ok(0, $name, [@table, @diag]);
402 6         21 $ctx->release;
403 6         46 return 0;
404             }
405              
406             1;
407              
408             __END__