File Coverage

blib/lib/Test/Stream/Util.pm
Criterion Covered Total %
statement 176 193 91.1
branch 47 64 73.4
condition 36 58 62.0
subroutine 35 36 97.2
pod 10 12 83.3
total 304 363 83.7


line stmt bran cond sub pod time code
1             package Test::Stream::Util;
2 109     109   1335 use strict;
  109         169  
  109         2731  
3 109     109   536 use warnings;
  109         171  
  109         2987  
4              
5 109     109   3545 use Test::Stream::Capabilities qw/CAN_THREAD/;
  109         193  
  109         656  
6 109     109   550 use Scalar::Util qw/reftype blessed refaddr/;
  109         187  
  109         6947  
7 109     109   525 use Carp qw/croak/;
  109         199  
  109         4729  
8 109     109   531 use B;
  109         188  
  109         5032  
9              
10 109     109   61465 use Test::Stream::Exporter qw/import export_to exports/;
  109         265  
  109         667  
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   649 no Test::Stream::Exporter;
  109         194  
  109         454  
36              
37             BEGIN {
38 109     109   2270 local ($@, $!, $SIG{__DIE__});
39 109         1385 my $have_sub_util = eval { require Sub::Util; 1 };
  109         89336  
  109         28498  
40 109         1252 my $have_sub_name = eval { require Sub::Name; 1 };
  109         36486  
  0         0  
41              
42 109 50       5077 my $set_subname = $have_sub_util ? Sub::Util->can('set_subname') : undef;
43 109 50       2545 my $subname = $have_sub_name ? Sub::Name->can('subname') : undef;
44              
45 109   50     2695 *set_sub_name = $set_subname || $subname || sub { croak "Cannot set sub name" };
46              
47 109 50 33     3721 if($set_subname || $subname) {
48 109         29776 *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 24 my ($sub) = @_;
57              
58 7 100 100     413 croak "sub_name requires a coderef as its only argument"
      100        
59             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
60              
61 4         12 my $cobj = B::svref_2object($sub);
62 4         20 my $name = $cobj->GV->NAME;
63 4         26 return $name;
64             }
65              
66             sub rename_anon_sub {
67 3665     3665 0 7594 my ($name, $sub, $caller) = @_;
68 3665   33     9384 $caller ||= caller();
69              
70 3665 50 33     32285 croak "sub_name requires a coderef as its second argument"
      33        
71             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
72              
73 3665         14895 my $cobj = B::svref_2object($sub);
74 3665         18462 my $orig = $cobj->GV->NAME;
75 3665 100       18628 return unless $orig =~ m/__ANON__$/;
76 3460         31665 set_sub_name("${caller}::${name}", $sub);
77             }
78              
79             sub update_mask {
80 787     787 1 3201 my ($file, $line, $name, $mask) = @_;
81              
82 109     109   7077 no warnings 'once';
  109         3014  
  109         13428  
83 787         1586 my $masks = \%Trace::Mask::MASKS;
84 109     109   1618 use warnings 'once';
  109         1285  
  109         20915  
85              
86             # Get existing ref, if any
87 787         10087 my $ref = $masks->{$file}->{$line}->{$name};
88              
89             # No ref, easy!
90 787 100       194679 return $masks->{$file}->{$line}->{$name} = {%$mask}
91             unless $ref;
92              
93             # Merge new mask into old
94 22         169 %$ref = (%$ref, %$mask);
95 22         89 return;
96             }
97              
98             sub _manual_protect(&) {
99 2     2   14 my $code = shift;
100              
101 2         8 rename_anon_sub('protect', $code, caller) if CAN_SET_SUB_NAME;
102              
103 2         4 my ($ok, $error);
104             {
105 2         3 my ($msg, $no) = ($@, $!);
  2         9  
106 2   100     3 $ok = eval {
107 109     109   2601 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
108             $code->();
109             1
110             } || 0;
111 2   100     19 $error = $@ || "Error was squashed!\n";
112 2         6 ($@, $!) = ($msg, $no);
113             }
114 2 100       8 die $error unless $ok;
115 1         3 return $ok;
116             }
117              
118             sub _local_protect(&) {
119 319     319   572 my $code = shift;
120              
121 319         1187 rename_anon_sub('protect', $code, caller) if CAN_SET_SUB_NAME;
122              
123 319         625 my ($ok, $error);
124             {
125 319         487 local ($@, $!);
  319         1273  
126 319   100     640 $ok = eval {
127 109     109   1555 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
128             $code->();
129             1
130             } || 0;
131 319   100     2109 $error = $@ || "Error was squashed!\n";
132             }
133 319 100       773 die $error unless $ok;
134 316         786 return $ok;
135             }
136              
137             sub _manual_try(&;@) {
138 2     2   13 my $code = shift;
139 2         5 my $args = \@_;
140 2         3 my $error;
141             my $ok;
142              
143 2         8 rename_anon_sub('try', $code, caller) if CAN_SET_SUB_NAME;
144              
145             {
146 2         4 my ($msg, $no) = ($@, $!);
  2         9  
147 2         7 my $die = delete $SIG{__DIE__};
148              
149 2   100     4 $ok = eval {
150 109     109   2725 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
151             $code->(@$args);
152             1
153             } || 0;
154 2 100       16 unless($ok) {
155 1   50     4 $error = $@ || "Error was squashed!\n";
156             }
157              
158 2         7 ($@, $!) = ($msg, $no);
159 2 50       6 if ($die) {
160 0         0 $SIG{__DIE__} = $die;
161             }
162             else {
163 2         9 delete $SIG{__DIE__};
164             }
165             }
166              
167 2         6 return ($ok, $error);
168             }
169              
170             sub _local_try(&;@) {
171 3342     3342   6353 my $code = shift;
172 3342         6315 my $args = \@_;
173 3342         4815 my $error;
174             my $ok;
175              
176 3342         16992 rename_anon_sub('try', $code, caller) if CAN_SET_SUB_NAME;
177              
178             {
179 3342         6260 local ($@, $!, $SIG{__DIE__});
  3342         21895  
180 3342   100     5960 $ok = eval {
181 109     109   1646 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
182             $code->(@$args);
183             1
184             } || 0;
185 3332 100       36219 unless($ok) {
186 379   50     2209 $error = $@ || "Error was squashed!\n";
187             }
188             }
189              
190 3332         12071 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   5383 if ($^O eq 'MSWin32' && $] < 5.020002) {
198 0         0 *protect = \&_manual_protect;
199 0         0 *try = \&_manual_try;
200             }
201             else {
202 109         310 *protect = \&_local_protect;
203 109         21190 *try = \&_local_try;
204             }
205             }
206              
207             BEGIN {
208 109     109   480 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         264 *USE_THREADS = sub() { 0 };
224 109         12850 *get_tid = sub() { 0 };
225             }
226             }
227              
228             sub pkg_to_file {
229 1910     1910 1 3300 my $pkg = shift;
230 1910         2621 my $file = $pkg;
231 1910         19394 $file =~ s{(::|')}{/}g;
232 1910         3422 $file .= '.pm';
233 1910         5587 return $file;
234             }
235              
236             sub get_stash {
237 35     35 1 81 my $pkg = shift;
238 109     109   2629 no strict 'refs';
  109         1530  
  109         43673  
239 35         65 return \%{"$pkg\::"};
  35         174  
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 28 sub sig_to_slot { $SIG_TABLE{$_[0]} }
252 20     20 1 80 sub slot_to_sig { $SLOT_TABLE{$_[0]} }
253              
254             sub parse_symbol {
255 612     612 1 932 my ($sym) = @_;
256              
257 612         1092 my $sig = substr($sym, 0, 1);
258 612 100       1992 return ($sym, 'CODE') unless $SIG_TABLE{$sig};
259 337         571 my $name = substr($sym, 1);
260              
261 337 50       788 my $slot = $SIG_TABLE{$sig} or croak "'$sig' is not a supported sigil";
262              
263 337         1103 return ($name, $slot);
264             }
265              
266             BEGIN {
267 109     109   3834 my ($ok, $err) = try { require Term::ReadKey };
  109         37992  
268 109   33     609 $ok &&= Term::ReadKey->can('GetTerminalSize');
269 109 50       81345 *USE_TERM_READKEY = $ok ? sub() { 1 } : sub() { 0 };
270             };
271              
272             sub term_size {
273 109 100   109 1 810 return $ENV{TS_TERM_SIZE} if $ENV{TS_TERM_SIZE};
274 15         63 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 6221     6221 1 8799 my ($thing) = @_;
290 6221 100       12940 return '' unless defined $thing;
291              
292 6220         9356 my $rf = ref $thing;
293 6220         12374 my $rt = reftype $thing;
294              
295 6220 100 66     28606 return '' unless $rf || $rt;
296 2367 100       8351 return 'REGEXP' if $rf =~ m/Regex/i;
297 1847 50       3912 return 'REGEXP' if $rt =~ m/Regex/i;
298 1847   50     7302 return $rt || '';
299             }
300              
301             sub render_ref {
302 59     59 1 92 my ($in) = @_;
303              
304 59         135 my $type = rtype($in);
305 59 50       152 return "$in" unless $type;
306              
307             # Look past overloading
308 59   100     257 my $class = blessed($in) || '';
309 59         257 my $it = sprintf('0x%x', refaddr($in));
310 59         141 my $ref = "$type($it)";
311              
312 59 100       214 return $ref unless $class;
313 32         140 return "$class=$ref";
314             }
315              
316             sub sub_info {
317 324     324 1 567 my ($sub, @all_lines) = @_;
318 324         622 my %in = map {$_ => 1} @all_lines;
  4         13  
319              
320 324 50 33     2762 croak "sub_info requires a coderef as its first argument"
      33        
321             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
322              
323 324         958 my $cobj = B::svref_2object($sub);
324 324         1393 my $name = $cobj->GV->NAME;
325 324         1114 my $file = $cobj->FILE;
326 324         1613 my $package = $cobj->GV->STASH->NAME;
327              
328 324         1210 my $op = $cobj->START;
329 324         814 while ($op) {
330 20616 100       62901 push @all_lines => $op->line if $op->can('line');
331 20616 100       57691 last unless $op->can('next');
332 20292         82700 $op = $op->next;
333             }
334              
335 324         453 my ($start, $end, @lines);
336 324 50       771 if (@all_lines) {
337 324         804 @all_lines = sort { $a <=> $b } @all_lines;
  2851         3281  
338 324         607 ($start, $end) = ($all_lines[0], $all_lines[-1]);
339              
340             # Adjust start and end for the most common case of a multi-line block with
341             # parens on the lines before and after.
342 324 100       810 if ($start < $end) {
343 214 50 66     981 $start-- unless $start <= 1 || $in{$start};
344 214 100       595 $end++ unless $in{$end};
345             }
346 324         747 @lines = ($start, $end);
347             }
348              
349             return {
350 324         3235 ref => $sub,
351             cobj => $cobj,
352             name => $name,
353             file => $file,
354             package => $package,
355             start_line => $start,
356             end_line => $end,
357             all_lines => \@all_lines,
358             lines => \@lines,
359             };
360             }
361              
362             1;
363              
364             __END__