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   1151 use strict;
  109         203  
  109         2678  
3 109     109   533 use warnings;
  109         183  
  109         2940  
4              
5 109     109   3623 use Test::Stream::Capabilities qw/CAN_THREAD/;
  109         196  
  109         658  
6 109     109   540 use Scalar::Util qw/reftype blessed refaddr/;
  109         195  
  109         6892  
7 109     109   519 use Carp qw/croak/;
  109         199  
  109         4681  
8 109     109   513 use B;
  109         216  
  109         4959  
9              
10 109     109   61977 use Test::Stream::Exporter qw/import export_to exports/;
  109         282  
  109         665  
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   679 no Test::Stream::Exporter;
  109         199  
  109         511  
36              
37             BEGIN {
38 109     109   2306 local ($@, $!, $SIG{__DIE__});
39 109         1429 my $have_sub_util = eval { require Sub::Util; 1 };
  109         87702  
  109         27733  
40 109         229 my $have_sub_name = eval { require Sub::Name; 1 };
  109         35543  
  0         0  
41              
42 109 50       5198 my $set_subname = $have_sub_util ? Sub::Util->can('set_subname') : undef;
43 109 50       3648 my $subname = $have_sub_name ? Sub::Name->can('subname') : undef;
44              
45 109   50     3961 *set_sub_name = $set_subname || $subname || sub { croak "Cannot set sub name" };
46              
47 109 50 33     2732 if($set_subname || $subname) {
48 109         28453 *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         19 my $name = $cobj->GV->NAME;
63 4         25 return $name;
64             }
65              
66             sub rename_anon_sub {
67 3665     3665 0 7795 my ($name, $sub, $caller) = @_;
68 3665   33     8898 $caller ||= caller();
69              
70 3665 50 33     32625 croak "sub_name requires a coderef as its second argument"
      33        
71             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
72              
73 3665         13219 my $cobj = B::svref_2object($sub);
74 3665         18763 my $orig = $cobj->GV->NAME;
75 3665 100       18295 return unless $orig =~ m/__ANON__$/;
76 3460         30474 set_sub_name("${caller}::${name}", $sub);
77             }
78              
79             sub update_mask {
80 787     787 1 1977 my ($file, $line, $name, $mask) = @_;
81              
82 109     109   2883 no warnings 'once';
  109         8316  
  109         15945  
83 787         2721 my $masks = \%Trace::Mask::MASKS;
84 109     109   1637 use warnings 'once';
  109         218  
  109         19086  
85              
86             # Get existing ref, if any
87 787         7774 my $ref = $masks->{$file}->{$line}->{$name};
88              
89             # No ref, easy!
90 787 100       193579 return $masks->{$file}->{$line}->{$name} = {%$mask}
91             unless $ref;
92              
93             # Merge new mask into old
94 22         144 %$ref = (%$ref, %$mask);
95 22         78 return;
96             }
97              
98             sub _manual_protect(&) {
99 2     2   14 my $code = shift;
100              
101 2         7 rename_anon_sub('protect', $code, caller) if CAN_SET_SUB_NAME;
102              
103 2         5 my ($ok, $error);
104             {
105 2         3 my ($msg, $no) = ($@, $!);
  2         9  
106 2   100     4 $ok = eval {
107 109     109   3831 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   578 my $code = shift;
120              
121 319         1152 rename_anon_sub('protect', $code, caller) if CAN_SET_SUB_NAME;
122              
123 319         617 my ($ok, $error);
124             {
125 319         446 local ($@, $!);
  319         1195  
126 319   100     545 $ok = eval {
127 109     109   2593 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
128             $code->();
129             1
130             } || 0;
131 319   100     2099 $error = $@ || "Error was squashed!\n";
132             }
133 319 100       751 die $error unless $ok;
134 316         777 return $ok;
135             }
136              
137             sub _manual_try(&;@) {
138 2     2   13 my $code = shift;
139 2         3 my $args = \@_;
140 2         5 my $error;
141             my $ok;
142              
143 2         7 rename_anon_sub('try', $code, caller) if CAN_SET_SUB_NAME;
144              
145             {
146 2         4 my ($msg, $no) = ($@, $!);
  2         10  
147 2         8 my $die = delete $SIG{__DIE__};
148              
149 2   100     4 $ok = eval {
150 109     109   4882 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       4 if ($die) {
160 0         0 $SIG{__DIE__} = $die;
161             }
162             else {
163 2         8 delete $SIG{__DIE__};
164             }
165             }
166              
167 2         8 return ($ok, $error);
168             }
169              
170             sub _local_try(&;@) {
171 3342     3342   6312 my $code = shift;
172 3342         6418 my $args = \@_;
173 3342         5854 my $error;
174             my $ok;
175              
176 3342         14191 rename_anon_sub('try', $code, caller) if CAN_SET_SUB_NAME;
177              
178             {
179 3342         6043 local ($@, $!, $SIG{__DIE__});
  3342         20840  
180 3342   100     6416 $ok = eval {
181 109     109   1696 BEGIN { update_mask(__FILE__, __LINE__ + 1, '*', {hide => 3}) }
182             $code->(@$args);
183             1
184             } || 0;
185 3332 100       33503 unless($ok) {
186 379   50     2028 $error = $@ || "Error was squashed!\n";
187             }
188             }
189              
190 3332         11812 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   4063 if ($^O eq 'MSWin32' && $] < 5.020002) {
198 0         0 *protect = \&_manual_protect;
199 0         0 *try = \&_manual_try;
200             }
201             else {
202 109         301 *protect = \&_local_protect;
203 109         20866 *try = \&_local_try;
204             }
205             }
206              
207             BEGIN {
208 109     109   461 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         1277 *USE_THREADS = sub() { 0 };
224 109         12774 *get_tid = sub() { 0 };
225             }
226             }
227              
228             sub pkg_to_file {
229 1910     1910 1 3195 my $pkg = shift;
230 1910         2626 my $file = $pkg;
231 1910         19341 $file =~ s{(::|')}{/}g;
232 1910         3416 $file .= '.pm';
233 1910         5534 return $file;
234             }
235              
236             sub get_stash {
237 35     35 1 73 my $pkg = shift;
238 109     109   1724 no strict 'refs';
  109         2446  
  109         44614  
239 35         66 return \%{"$pkg\::"};
  35         164  
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 85 sub slot_to_sig { $SLOT_TABLE{$_[0]} }
253              
254             sub parse_symbol {
255 612     612 1 988 my ($sym) = @_;
256              
257 612         990 my $sig = substr($sym, 0, 1);
258 612 100       1978 return ($sym, 'CODE') unless $SIG_TABLE{$sig};
259 337         547 my $name = substr($sym, 1);
260              
261 337 50       752 my $slot = $SIG_TABLE{$sig} or croak "'$sig' is not a supported sigil";
262              
263 337         1073 return ($name, $slot);
264             }
265              
266             BEGIN {
267 109     109   2941 my ($ok, $err) = try { require Term::ReadKey };
  109         37400  
268 109   33     610 $ok &&= Term::ReadKey->can('GetTerminalSize');
269 109 50       81446 *USE_TERM_READKEY = $ok ? sub() { 1 } : sub() { 0 };
270             };
271              
272             sub term_size {
273 109 100   109 1 714 return $ENV{TS_TERM_SIZE} if $ENV{TS_TERM_SIZE};
274 15         59 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 8633 my ($thing) = @_;
290 6221 100       12571 return '' unless defined $thing;
291              
292 6220         8853 my $rf = ref $thing;
293 6220         11984 my $rt = reftype $thing;
294              
295 6220 100 66     27466 return '' unless $rf || $rt;
296 2367 100       8238 return 'REGEXP' if $rf =~ m/Regex/i;
297 1847 50       3829 return 'REGEXP' if $rt =~ m/Regex/i;
298 1847   50     7074 return $rt || '';
299             }
300              
301             sub render_ref {
302 59     59 1 97 my ($in) = @_;
303              
304 59         115 my $type = rtype($in);
305 59 50       160 return "$in" unless $type;
306              
307             # Look past overloading
308 59   100     262 my $class = blessed($in) || '';
309 59         267 my $it = sprintf('0x%x', refaddr($in));
310 59         128 my $ref = "$type($it)";
311              
312 59 100       221 return $ref unless $class;
313 32         140 return "$class=$ref";
314             }
315              
316             sub sub_info {
317 324     324 1 607 my ($sub, @all_lines) = @_;
318 324         611 my %in = map {$_ => 1} @all_lines;
  4         13  
319              
320 324 50 33     2629 croak "sub_info requires a coderef as its first argument"
      33        
321             unless $sub && ref($sub) && reftype($sub) eq 'CODE';
322              
323 324         929 my $cobj = B::svref_2object($sub);
324 324         1419 my $name = $cobj->GV->NAME;
325 324         1070 my $file = $cobj->FILE;
326 324         1601 my $package = $cobj->GV->STASH->NAME;
327              
328 324         1177 my $op = $cobj->START;
329 324         786 while ($op) {
330 20616 100       62068 push @all_lines => $op->line if $op->can('line');
331 20616 100       58108 last unless $op->can('next');
332 20292         82256 $op = $op->next;
333             }
334              
335 324         430 my ($start, $end, @lines);
336 324 50       779 if (@all_lines) {
337 324         818 @all_lines = sort { $a <=> $b } @all_lines;
  2851         3438  
338 324         659 ($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       791 if ($start < $end) {
343 214 50 66     965 $start-- unless $start <= 1 || $in{$start};
344 214 100       570 $end++ unless $in{$end};
345             }
346 324         731 @lines = ($start, $end);
347             }
348              
349             return {
350 324         3232 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__