File Coverage

blib/lib/Test/TraceCalls.pm
Criterion Covered Total %
statement 23 46 50.0
branch 0 8 0.0
condition n/a
subroutine 9 15 60.0
pod 0 2 0.0
total 32 71 45.0


line stmt bran cond sub pod time code
1 1     1   67768 use strict;
  1         2  
  1         32  
2 1     1   6 use warnings;
  1         2  
  1         131  
3              
4             package Test::TraceCalls;
5              
6             our $AUTHORITY = 'cpan:TOBYINK';
7             our $VERSION = '0.001';
8              
9 1     1   7 use constant ACTIVE => $ENV{'PERL_TRACE_CALLS'};
  1         2  
  1         110  
10              
11             BEGIN {
12 1     1   186 eval q{
13             use match::simple ();
14             use Carp ();
15             use File::Spec ();
16             use FindBin ();
17             use Hook::AfterRuntime ();
18             use JSON::PP ();
19             use Sub::Util 1.40 ();
20             1;
21             } || die($@) if ACTIVE;
22             };
23              
24             our %CALL;
25              
26             sub import {
27 0     0     my $me = shift;
28 0           my $caller = caller;
29 0           my (%opts) = @_;
30             &Hook::AfterRuntime::after_runtime(
31 0     0     sub { $me->setup_for($caller, %opts) },
32 0           ) if ACTIVE;
33             }
34              
35             sub setup_for {
36 0     0 0   my $me = shift;
37 0           my ($caller, %opts) = @_;
38             $opts{match} = sub {
39 0     0     local $_ = shift;
40 1 0   1   691 !/^_/ and /\p{Ll}/;
  1         15  
  1         15  
  0            
41 0 0         } unless exists $opts{match};
42 1     1   21332 no strict 'refs';
  1         3  
  1         142  
43             my @names =
44             grep match::simple::match($_, $opts{match}),
45             grep !/::$/,
46 0           sort keys %{"$caller\::"};
  0            
47 0           $me->wrap_sub($caller, $_) for @names;
48             }
49              
50             sub wrap_sub {
51 0     0 0   my $me = shift;
52 1     1   8 no strict 'refs';
  1         2  
  1         28  
53 1     1   6 no warnings 'redefine';
  1         2  
  1         409  
54 0           my ($package, $sub) = @_;
55 0 0         ($package, $sub) = (/^(.+)::([^:]+)$/ =~ $package)
56             if !defined $sub;
57 0 0         my $code = \&{"$package\::$sub"} or return;
  0            
58             my $newcode =
59             Sub::Util::set_prototype Sub::Util::prototype($code),
60             Sub::Util::set_subname Sub::Util::subname($code),
61 0     0     sub { ++$CALL{$package}{$sub}; goto $code };
  0            
  0            
62 0           *{"$package\::$sub"} = $newcode;
  0            
63             }
64              
65             END {
66 1     1   574653 if (ACTIVE) {
67             my $JSON = 'JSON::PP'->new->pretty(1)->canonical(1);
68             my $map = $JSON->encode(\%CALL);
69            
70             my $outfile = 'File::Spec'->catfile(
71             $FindBin::RealBin,
72             $FindBin::RealScript . ".map",
73             );
74             my $already = 0;
75            
76             if (-f $outfile) {
77             my $slurped = do {
78             local $/; my $fh;
79             open($fh, '<', $outfile) ? <$fh> : undef;
80             };
81             $already++ if $slurped eq $map;
82             }
83            
84             if (!$already) {
85             open my $outfh, '>', $outfile
86             or Carp::croak("Cannot open $outfile for output: $!");
87             print {$outfh} $map
88             or Carp::croak("Cannot write to $outfile: $!");
89             close $outfh
90             or Carp::croak("Cannot close $outfile: $!");
91             }
92             };
93             }
94              
95             1;
96              
97             __END__