File Coverage

blib/lib/TAP/Harness/Metrics.pm
Criterion Covered Total %
statement 80 103 77.6
branch 23 40 57.5
condition 9 13 69.2
subroutine 14 15 93.3
pod 3 8 37.5
total 129 179 72.0


line stmt bran cond sub pod time code
1             package TAP::Harness::Metrics;
2 1     1   916 use parent qw/TAP::Harness/;
  1         2  
  1         10  
3              
4 1     1   15621 use strict;
  1         3  
  1         23  
5 1     1   5 use warnings;
  1         4  
  1         72  
6              
7             our $VERSION='0.0.2';
8              
9 1     1   7 use Carp qw/confess/;
  1         2  
  1         63  
10 1     1   6 use Fcntl qw/:flock/;
  1         2  
  1         1764  
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             #
20             type =>'file',
21             append =>1,
22             outfile=>'/tmp/metrics-tests.txt',
23             # format =>'tsv',
24             #
25             module=>undef,
26             f =>'save',
27             );
28             my @configurable=(qw/prefix sep subdepth label allowed rollup/); # not fully "enforced"
29              
30             sub verifyCallback {
31 1     1 0 7 my ($module,$f)=@_;
32 1 50       53 if(!$module) { confess("'module' must be provided") }
  0         0  
33 1 50       4 if(!$f) { confess("'f' must be non-empty") }
  0         0  
34 1         71 eval "require $module;";
35 1 50       4 if($@) { confess($@) }
  0         0  
36 1         12 my $cb=$module->can($f);
37 1 50       5 if(!$cb) { confess("${module}::${f} not available") }
  0         0  
38 1         6 return $cb;
39             }
40              
41             sub new {
42 5     5 1 4648 my ($ref,@opt)=@_;
43 5   33     36 my $class=ref($ref)||$ref;
44 5         26 my $self=$class->SUPER::new(@opt);
45 5         23939 while(my ($k,$v)=each(%options)) { $$self{$k}=$v }
  55         177  
46 5 100       26 if($$self{type} eq 'module') {
47 1         6 $$self{modulef}=verifyCallback($$self{module},$$self{f});
48 1 50       5 if(my $cfg=$$self{module}->can('configureHarness')) {
49 1         6 my %config=&$cfg();
50 1         12 foreach my $k (grep {exists($config{$_})} @configurable) { $options{$k}=$$self{$k}=$config{$k} }
  6         14  
  5         16  
51             }
52             }
53 5         13 $$self{parser_class}='TAP::Parser::Metrics';
54 5         48 return $self;
55             }
56              
57             sub make_parser {
58 10     10 1 36833 my ($self,@args)=@_;
59 10         58 my ($parser,$session)=$self->SUPER::make_parser(@args);
60 10     10   4259 $parser->configure(callback=>sub { $self->save(@_) });
  10         78  
61 10         155 return ($parser,$session);
62             }
63              
64             sub import {
65 5     5   1107 my ($class,$type,@opt)=@_;
66 5   50     22 $type//='file';
67 5 100       28 if($type eq 'module') { unshift(@opt,'module') }
  1         3  
68 5         152 %options=(%options,@opt,type=>$type);
69 5         88 return 1;
70             }
71              
72             sub name {
73 32     32 1 154 my ($self,%event)=@_;
74 32         48 my @path=@{$event{path}};
  32         105  
75 32 100 66     198 if(defined($$self{subdepth})&&($$self{subdepth}>=0)) { splice(@path,$$self{subdepth}) }
  15         33  
76 32 100       578 my @name=map {s/[^$$self{allowed}]//sgr} (($$self{prefix}?$$self{prefix}:()),$event{file},@path,($$self{label}?$event{label}:()));
  95 100       617  
77 32         200 return join($$self{sep},@name);
78             }
79              
80             sub collate {
81 10     10 0 32 my ($self,@metrics)=@_;
82 10         21 my (%res,%count);
83 10         136 foreach my $event (@metrics) {
84 32         182 my $name=$self->name(%$event);
85 32         168 $count{$name}++;
86 32 50       86 if($$self{rollup}) { $res{$name}+=$$event{pass} }
  0         0  
87 32   100     180 else { $res{$name}//=1; $res{$name}&&=$$event{pass} }
  32   100     164  
88             }
89 10 50       38 if($$self{rollup}) { foreach my $k (keys %res) { $res{$k}/=$count{$k} } }
  0         0  
  0         0  
90 10         57 return %res;
91             }
92              
93             sub save {
94 10     10 0 83 my ($self,@metrics)=@_;
95 10         61 my %metrics=$self->collate(@metrics);
96 10 50       47 if($$self{type} eq 'file') { $self->saveFile(%metrics) }
  0         0  
97 10 100       41 if($$self{type} eq 'module') { &{$$self{modulef}}(%metrics) }
  1         18  
  1         24  
98 10 100       68 if($$self{type} eq 'stderr') { $self->printMetrics(%metrics) }
  9         44  
99 10         54 return;
100             }
101              
102             sub printMetrics {
103 9     9 0 64 my ($self,%metrics)=@_;
104 9 50       34 if(!%metrics) { return }
  0         0  
105 9         47 while(my ($name,$pass)=each %metrics) { print STDERR join("\t",'METRIC:',$pass,$name),"\n" }
  19         194  
106 9         58 return;
107             }
108              
109             sub saveFile {
110 0     0 0   my ($self,%metrics)=@_;
111 0 0         if(!%metrics) { return }
  0            
112 0 0         my $append=($$self{append}?'>>':'>');
113 0 0         open(my $fh,$append,$$self{outfile}) or return;
114 0           my $countdown=5;
115 0           while(!flock($fh,LOCK_EX)) {
116 0 0         if($countdown--) { sleep(2) }
  0            
117 0           else { return }
118             }
119 0           while(my ($name,$pass)=each %metrics) { print $fh join("\t",$pass,$name),"\n" }
  0            
120 0           flock($fh,LOCK_UN);
121 0           return;
122             }
123              
124             1;
125              
126             __END__