File Coverage

blib/lib/Test/Stream/Util.pm
Criterion Covered Total %
statement 178 194 91.7
branch 50 66 75.7
condition 36 58 62.0
subroutine 35 36 97.2
pod 10 12 83.3
total 309 366 84.4


line stmt bran cond sub pod time code
1             package Test::Stream::Util;
2 109     109   698 use strict;
  109         113  
  109         2469  
3 109     109   331 use warnings;
  109         95  
  109         2551  
4              
5 109     109   2305 use Test::Stream::Capabilities qw/CAN_THREAD/;
  109         113  
  109         471  
6 109     109   398 use Scalar::Util qw/reftype blessed refaddr/;
  109         136  
  109         5145  
7 109     109   364 use Carp qw/croak/;
  109         125  
  109         3288  
8 109     109   378 use B();
  109         126  
  109         1826  
9              
10 109     109   38588 use Test::Stream::Exporter qw/import export_to exports/;
  109         180  
  109         702  
11             exports qw{
12             try protect
13              
14             get_tid USE_THREADS
15              
16             pkg_to_file
17              
18             get_stash
19              
20             sig_to_slot slot_to_sig
21             parse_symbol
22              
23             term_size
24              
25             rtype render_ref
26              
27             set_sub_name
28             CAN_SET_SUB_NAME
29             sub_info
30             sub_name
31             rename_anon_sub
32              
33             update_mask
34             };
35 109     109   526 no Test::Stream::Exporter;
  109         125  
  109         341  
36              
37             BEGIN {
38 109     109   1420 local ($@, $!, $SIG{__DIE__});
39 109         820 my $have_sub_util = eval { require Sub::Util; 1 };
  109         50173  
  109         21805  
40 109         143 my $have_sub_name = eval { require Sub::Name; 1 };
  109         41575  
  109         47350  
41              
42 109 50       3968 my $set_subname = $have_sub_util ? Sub::Util->can('set_subname') : undef;
43 109 50       2559 my $subname = $have_sub_name ? Sub::Name->can('subname') : undef;
44              
45 109   50     1623 *set_sub_name = $set_subname || $subname || sub { croak "Cannot set sub name" };
46              
47 109 50 33     1564 if($set_subname || $subname) {
48 109         24807 *CAN_SET_SUB_NAME = sub() { 1 };
49             }
50             else {
51 0         0 *CAN_SET_SUB_NAME = sub() { 0 };
52             }
53             }
54              
55             sub sub_name {
56 7     7 0 26 my ($sub) = @_;
57              
58 7 100 100     366 croak "sub_name requires a coderef as its only argument"
      100        
59             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
60              
61 4         14 my $cobj = B::svref_2object($sub);
62 4         14 my $name = $cobj->GV->NAME;
63 4         22 return $name;
64             }
65              
66             sub rename_anon_sub {
67 3706     3706 0 4174 my ($name, $sub, $caller) = @_;
68 3706   33     5817 $caller ||= caller();
69              
70 3706 50 33     22259 croak "sub_name requires a coderef as its second argument"
      33        
71             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
72              
73 3706         10005 my $cobj = B::svref_2object($sub);
74 3706         11777 my $orig = $cobj->GV->NAME;
75 3706 100       15515 return unless $orig =~ m/__ANON__$/;
76 3500         21907 set_sub_name("${caller}::${name}", $sub);
77             }
78              
79             sub update_mask {
80 787     787 1 2126 my ($file, $line, $name, $mask) = @_;
81              
82 109     109   653 no warnings 'once';
  109         111  
  109         8781  
83 787         1781 my $masks = \%Trace::Mask::MASKS;
84 109     109   1044 use warnings 'once';
  109         1359  
  109         17133  
85              
86             # Get existing ref, if any
87 787         6654 my $ref = $masks->{$file}->{$line}->{$name};
88              
89             # No ref, easy!
90 787 100       131030 return $masks->{$file}->{$line}->{$name} = {%$mask}
91             unless $ref;
92              
93             # Merge new mask into old
94 22         109 %$ref = (%$ref, %$mask);
95 22         49 return;
96             }
97              
98             sub _manual_protect(&) {
99 2     2   12 my $code = shift;
100              
101 2         6 rename_anon_sub('protect', $code, caller) if CAN_SET_SUB_NAME;
102              
103 2         3 my ($ok, $error);
104             {
105 2         2 my ($msg, $no) = ($@, $!);
  2         9  
106 2   100     2 $ok = eval {
107 109     109   1002 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
108             $code->();
109             1
110             } || 0;
111 2   100     15 $error = $@ || "Error was squashed!\n";
112 2         4 ($@, $!) = ($msg, $no);
113             }
114 2 100       6 die $error unless $ok;
115 1         2 return $ok;
116             }
117              
118             sub _local_protect(&) {
119 328     328   434 my $code = shift;
120              
121 328         827 rename_anon_sub('protect', $code, caller) if CAN_SET_SUB_NAME;
122              
123 328         389 my ($ok, $error);
124             {
125 328         302 local ($@, $!);
  328         1014  
126 328   100     390 $ok = eval {
127 109     109   989 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
128             $code->();
129             1
130             } || 0;
131 328   100     1559 $error = $@ || "Error was squashed!\n";
132             }
133 328 100       609 die $error unless $ok;
134 326         554 return $ok;
135             }
136              
137             sub _manual_try(&;@) {
138 2     2   12 my $code = shift;
139 2         3 my $args = \@_;
140 2         2 my $error;
141             my $ok;
142              
143 2         5 rename_anon_sub('try', $code, caller) if CAN_SET_SUB_NAME;
144              
145             {
146 2         3 my ($msg, $no) = ($@, $!);
  2         9  
147 2         6 my $die = delete $SIG{__DIE__};
148              
149 2   100     3 $ok = eval {
150 109     109   388 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
151             $code->(@$args);
152             1
153             } || 0;
154 2 100       13 unless($ok) {
155 1   50     2 $error = $@ || "Error was squashed!\n";
156             }
157              
158 2         5 ($@, $!) = ($msg, $no);
159 2 50       4 if ($die) {
160 0         0 $SIG{__DIE__} = $die;
161             }
162             else {
163 2         4 delete $SIG{__DIE__};
164             }
165             }
166              
167 2         5 return ($ok, $error);
168             }
169              
170             sub _local_try(&;@) {
171 3374     3374   3221 my $code = shift;
172 3374         3493 my $args = \@_;
173 3374         2719 my $error;
174             my $ok;
175              
176 3374         8892 rename_anon_sub('try', $code, caller) if CAN_SET_SUB_NAME;
177              
178             {
179 3374         3547 local ($@, $!, $SIG{__DIE__});
  3374         16165  
180 3374   100     3673 $ok = eval {
181 109     109   1953 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
182             $code->(@$args);
183             1
184             } || 0;
185 3364 100       22016 unless($ok) {
186 379   50     1605 $error = $@ || "Error was squashed!\n";
187             }
188             }
189              
190 3364         7720 return ($ok, $error);
191             }
192              
193             # Older versions of perl have a nasty bug on win32 when localizing a variable
194             # before forking or starting a new thread. So for those systems we use the
195             # non-local form. When possible though we use the faster 'local' form.
196             BEGIN {
197 109 50 33 109   2525 if ($^O eq 'MSWin32' && $] < 5.020002) {
198 0         0 *protect = \&_manual_protect;
199 0         0 *try = \&_manual_try;
200             }
201             else {
202 109         400 *protect = \&_local_protect;
203 109         16604 *try = \&_local_try;
204             }
205             }
206              
207             BEGIN {
208 109     109   1410 if(CAN_THREAD) {
209             if ($INC{'threads.pm'}) {
210             # Threads are already loaded, so we do not need to check if they
211             # are loaded each time
212             *USE_THREADS = sub() { 1 };
213             *get_tid = sub { threads->tid() };
214             }
215             else {
216             # :-( Need to check each time to see if they have been loaded.
217             *USE_THREADS = sub { $INC{'threads.pm'} ? 1 : 0 };
218             *get_tid = sub { $INC{'threads.pm'} ? threads->tid() : 0 };
219             }
220             }
221             else {
222             # No threads, not now, not ever!
223 109         242 *USE_THREADS = sub() { 0 };
224 109         10618 *get_tid = sub() { 0 };
225             }
226             }
227              
228             sub pkg_to_file {
229 1921     1921 1 2053 my $pkg = shift;
230 1921         1600 my $file = $pkg;
231 1921         14303 $file =~ s{(::|')}{/}g;
232 1921         2050 $file .= '.pm';
233 1921         3691 return $file;
234             }
235              
236             sub get_stash {
237 35     35 1 47 my $pkg = shift;
238 109     109   1064 no strict 'refs';
  109         728  
  109         29316  
239 35         44 return \%{"$pkg\::"};
  35         134  
240             }
241              
242             my %SIG_TABLE = (
243             '&' => 'CODE',
244             '%' => 'HASH',
245             '@' => 'ARRAY',
246             '$' => 'SCALAR',
247             '*' => 'GLOB',
248             );
249             my %SLOT_TABLE = reverse %SIG_TABLE;
250              
251 5     5 1 21 sub sig_to_slot { $SIG_TABLE{$_[0]} }
252 20     20 1 47 sub slot_to_sig { $SLOT_TABLE{$_[0]} }
253              
254             sub parse_symbol {
255 613     613 1 524 my ($sym) = @_;
256              
257 613         660 my $sig = substr($sym, 0, 1);
258 613 100       1237 return ($sym, 'CODE') unless $SIG_TABLE{$sig};
259 337         361 my $name = substr($sym, 1);
260              
261 337 50       469 my $slot = $SIG_TABLE{$sig} or croak "'$sig' is not a supported sigil";
262              
263 337         666 return ($name, $slot);
264             }
265              
266             BEGIN {
267 109     109   1298 my ($ok, $err) = try { require Term::ReadKey };
  109         17680  
268 109   33     479 $ok &&= Term::ReadKey->can('GetTerminalSize');
269 109 50       58174 *USE_TERM_READKEY = $ok ? sub() { 1 } : sub() { 0 };
270             };
271              
272             sub term_size {
273 110 100   110 1 502 return $ENV{TS_TERM_SIZE} if $ENV{TS_TERM_SIZE};
274 15         44 return 80 unless USE_TERM_READKEY;
275 0         0 my $total;
276             try {
277 0     0   0 my @warnings;
278 0         0 local $SIG{__WARN__} = sub { push @warnings => @_ };
  0         0  
279 0         0 ($total) = Term::ReadKey::GetTerminalSize(*STDOUT);
280 0         0 @warnings = grep { $_ !~ m/Unable to get Terminal Size/ } @warnings;
  0         0  
281 0         0 warn @warnings;
282 0         0 };
283 0 0       0 return 80 if !$total;
284 0 0       0 return 80 if $total < 80;
285 0         0 return $total;
286             }
287              
288             sub rtype {
289 6525     6525 1 6400 my ($thing) = @_;
290 6525 100       8729 return '' unless defined $thing;
291              
292 6524         5921 my $rf = ref $thing;
293 6524         7949 my $rt = reftype $thing;
294              
295 6524 100 66     19268 return '' unless $rf || $rt;
296 2575 100       6518 return 'REGEXP' if $rf =~ m/Regex/i;
297 2036 50       2650 return 'REGEXP' if $rt =~ m/Regex/i;
298 2036   50     5197 return $rt || '';
299             }
300              
301             sub render_ref {
302 248     248 1 572 my ($in) = @_;
303              
304 248 100       478 return 'undef' unless defined($in);
305              
306 247         422 my $type = rtype($in);
307 247 100       569 return "$in" unless $type;
308              
309             # Look past overloading
310 206   100     634 my $class = blessed($in) || '';
311 206         1066 my $it = sprintf('0x%x', refaddr($in));
312 206         361 my $ref = "$type($it)";
313              
314 206 100       428 return $ref unless $class;
315 167         424 return "$class=$ref";
316             }
317              
318             sub sub_info {
319 326     326 1 344 my ($sub, @all_lines) = @_;
320 326         428 my %in = map {$_ => 1} @all_lines;
  4         11  
321              
322 326 50 33     1835 croak "sub_info requires a coderef as its first argument"
      33        
323             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
324              
325 326         706 my $cobj = B::svref_2object($sub);
326 326         982 my $name = $cobj->GV->NAME;
327 326         699 my $file = $cobj->FILE;
328 326         944 my $package = $cobj->GV->STASH->NAME;
329              
330 326         709 my $op = $cobj->START;
331 326         520 while ($op) {
332 21086 100       34081 push @all_lines => $op->line if $op->can('line');
333 21086 100       30867 last unless $op->can('next');
334 20760         40085 $op = $op->next;
335             }
336              
337 326         277 my ($start, $end, @lines);
338 326 50       538 if (@all_lines) {
339 326         627 @all_lines = sort { $a <=> $b } @all_lines;
  2903         1884  
340 326         393 ($start, $end) = ($all_lines[0], $all_lines[-1]);
341              
342             # Adjust start and end for the most common case of a multi-line block with
343             # parens on the lines before and after.
344 326 100       560 if ($start < $end) {
345 216 50 66     710 $start-- unless $start <= 1 || $in{$start};
346 216 100       373 $end++ unless $in{$end};
347             }
348 326         504 @lines = ($start, $end);
349             }
350              
351             return {
352 326         2234 ref => $sub,
353             cobj => $cobj,
354             name => $name,
355             file => $file,
356             package => $package,
357             start_line => $start,
358             end_line => $end,
359             all_lines => \@all_lines,
360             lines => \@lines,
361             };
362             }
363              
364             1;
365              
366             __END__