File Coverage

blib/lib/TAP/Harness/Metrics.pm
Criterion Covered Total %
statement 101 121 83.4
branch 30 46 65.2
condition 12 16 75.0
subroutine 16 17 94.1
pod 3 10 30.0
total 162 210 77.1


line stmt bran cond sub pod time code
1             package TAP::Harness::Metrics;
2 1     1   920 use parent qw/TAP::Harness/;
  1         3  
  1         9  
3              
4 1     1   15909 use strict;
  1         3  
  1         27  
5 1     1   7 use warnings;
  1         5  
  1         94  
6              
7             our $VERSION='0.0.4';
8              
9 1     1   6 use Carp qw/confess/;
  1         4  
  1         78  
10 1     1   7 use Fcntl qw/:flock/;
  1         2  
  1         2049  
11              
12             my %options=(
13             prefix =>'PREFIX',
14             sep =>'.',
15             subdepth =>1,
16             label =>0,
17             allowed =>'-._/A-Za-z0-9',
18             rollup =>0,
19             bubble =>1,
20             #
21             type =>'file',
22             append =>1,
23             outfile=>'/tmp/metrics-tests.txt',
24             # format =>'tsv',
25             #
26             module=>undef,
27             f =>'save',
28             );
29             my @configurable=(qw/prefix sep subdepth label allowed rollup/); # not fully "enforced"
30              
31             sub verifyCallback {
32 1     1 0 8 my ($module,$f)=@_;
33 1 50       7 if(!$module) { confess("'module' must be provided") }
  0         0  
34 1 50       4 if(!$f) { confess("'f' must be non-empty") }
  0         0  
35 1         90 eval "require $module;";
36 1 50       5 if($@) { confess($@) }
  0         0  
37 1         17 my $cb=$module->can($f);
38 1 50       7 if(!$cb) { confess("${module}::${f} not available") }
  0         0  
39 1         6 return $cb;
40             }
41              
42             sub new {
43 8     8 1 7938 my ($ref,@opt)=@_;
44 8   33     81 my $class=ref($ref)||$ref;
45 8         43 my $self=$class->SUPER::new(@opt);
46 8         27344 while(my ($k,$v)=each(%options)) { $$self{$k}=$v }
  96         357  
47 8 100       50 if($$self{type} eq 'module') {
48 1         8 $$self{modulef}=verifyCallback($$self{module},$$self{f});
49 1 50       9 if(my $cfg=$$self{module}->can('configureHarness')) {
50 1         378 my %config=&$cfg();
51 1         28 foreach my $k (grep {exists($config{$_})} @configurable) { $options{$k}=$$self{$k}=$config{$k} }
  6         21  
  5         18  
52             }
53             }
54 8         24 $$self{parser_class}='TAP::Parser::Metrics';
55 8         109 return $self;
56             }
57              
58             sub make_parser {
59 22     22 1 38942 my ($self,@args)=@_;
60 22         117 my ($parser,$session)=$self->SUPER::make_parser(@args);
61 22     22   7675 $parser->configure(callback=>sub { $self->save(@_) });
  22         215  
62 22         228 return ($parser,$session);
63             }
64              
65             sub import {
66 8     8   1966 my ($class,$type,@opt)=@_;
67 8   50     36 $type//='file';
68 8 100       43 if($type eq 'module') { unshift(@opt,'module') }
  1         6  
69 8         345 %options=(%options,@opt,type=>$type);
70 8         198 return 1;
71             }
72              
73             sub name {
74 120     120 1 466 my ($self,%event)=@_;
75 120         226 my @path=@{$event{path}};
  120         282  
76 120 100 66     2352 if(defined($$self{subdepth})&&($$self{subdepth}>=0)) { splice(@path,$$self{subdepth}) }
  30         72  
77 120 100 100     593 my @name=map {s/[^$$self{allowed}]//sgr} (($$self{prefix}?$$self{prefix}:()),$event{file},@path,($$self{label}&&defined($event{label})?$event{label}:()));
  359 100       1629  
78 120         763 return join($$self{sep},@name);
79             }
80              
81             sub bubbled {
82 62     62 0 219 my ($self,%event)=@_;
83 62 100       104 if(!@{$event{path}}) { return }
  62         162  
  4         15  
84 58         90 $event{label}=pop(@{$event{path}});
  58         132  
85 58         172 return $self->name(%event);
86             }
87              
88             sub collateRollup {
89 2     2 0 149 my ($self,@metrics)=@_;
90 2         14 my (%res,%count);
91 2         32 foreach my $event (@metrics) {
92 18 100       48 if(defined($$event{label})) {
93 12 100       65 foreach my $name ($self->name(%$event), ($$self{label}?$self->bubbled(%$event):())) {
94 18         54 $count{$name}++; $res{$name}+=$$event{pass} } }
  18         43  
95             else {
96 6         16 local($$self{label})=0;
97 6         25 foreach my $name ($self->bubbled(%$event)) {
98 6         24 $count{$name}++; $res{$name}+=$$event{pass} } }
  6         26  
99             }
100 2         7 foreach my $k (keys %res) { $res{$k}/=$count{$k} }
  14         35  
101 2         24 return %res;
102             }
103              
104             sub collate {
105 22     22 0 61 my ($self,@metrics)=@_;
106 22         43 my (%res,%count);
107 22 100       93 if($$self{rollup}) { return $self->collateRollup(@metrics) }
  2         24  
108 20         100 foreach my $event (@metrics) {
109 50 50       277 foreach my $name ($self->name(%$event), ($$self{bubble}?$self->bubbled(%$event):())) {
110 96   100     335 $count{$name}++; $res{$name}//=1; $res{$name}&&=$$event{pass} } }
  96   100     343  
  96         355  
111 20         109 return %res;
112             }
113              
114             sub save {
115 22     22 0 104 my ($self,@metrics)=@_;
116 22         184 my %metrics=$self->collate(@metrics);
117 22 50       105 if($$self{type} eq 'file') { $self->saveFile(%metrics) }
  0         0  
118 22 100       78 if($$self{type} eq 'module') { &{$$self{modulef}}(%metrics) }
  1         4  
  1         17  
119 22 100       93 if($$self{type} eq 'stderr') { $self->printMetrics(%metrics) }
  21         107  
120 22         104 return;
121             }
122              
123             sub printMetrics {
124 21     21 0 63 my ($self,%metrics)=@_;
125 21 50       57 if(!%metrics) { return }
  0         0  
126 21         96 while(my ($name,$pass)=each %metrics) { print STDERR join("\t",'METRIC:',$pass,$name),"\n" }
  52         505  
127 21         50 return;
128             }
129              
130             sub saveFile {
131 0     0 0   my ($self,%metrics)=@_;
132 0 0         if(!%metrics) { return }
  0            
133 0 0         my $append=($$self{append}?'>>':'>');
134 0 0         open(my $fh,$append,$$self{outfile}) or return;
135 0           my $countdown=5;
136 0           while(!flock($fh,LOCK_EX)) {
137 0 0         if($countdown--) { sleep(2) }
  0            
138 0           else { return }
139             }
140 0           while(my ($name,$pass)=each %metrics) { print $fh join("\t",$pass,$name),"\n" }
  0            
141 0           flock($fh,LOCK_UN);
142 0           return;
143             }
144              
145             1;
146              
147             __END__