File Coverage

blib/lib/App/plackbench.pm
Criterion Covered Total %
statement 116 117 99.1
branch 20 22 90.9
condition 10 15 66.6
subroutine 24 24 100.0
pod 3 3 100.0
total 173 181 95.5


line stmt bran cond sub pod time code
1             package App::plackbench;
2             $App::plackbench::VERSION = '0.7';
3 5     5   140045 use strict;
  5         12  
  5         191  
4 5     5   24 use warnings;
  5         15  
  5         272  
5 5     5   549 use autodie;
  5         22258  
  5         37  
6 5     5   32341 use v5.10;
  5         59  
7              
8 5     5   3158 use HTTP::Request qw();
  5         128866  
  5         241  
9 5     5   48 use List::Util qw( reduce );
  5         10  
  5         694  
10 5     5   2922 use Plack::Test qw();
  5         3801  
  5         135  
11 5     5   2755 use Plack::Util qw();
  5         78603  
  5         181  
12 5     5   40 use Scalar::Util qw( reftype );
  5         9  
  5         318  
13 5     5   2419 use Time::HiRes qw( gettimeofday tv_interval );
  5         6051  
  5         46  
14              
15 5     5   3700 use App::plackbench::Stats;
  5         18  
  5         889  
16              
17             my %attributes = (
18             app => \&_build_app,
19             tester => \&_build_tester,
20             count => 1,
21             warm => 0,
22             fixup => sub { [] },
23             post_data => undef,
24             psgi_path => undef,
25             uri => undef,
26             );
27             for my $attribute (keys %attributes) {
28             my $accessor = sub {
29 156     156   4469 my $self = shift;
30              
31             # $self is a coderef, so yes.. call $self on $self.
32 156         419 return $self->$self($attribute, @_);
33             };
34              
35 5     5   38 no strict 'refs';
  5         10  
  5         7066  
36             *$attribute = $accessor;
37             }
38              
39             sub new {
40 10     10 1 247097 my $class = shift;
41 10         60 my %stash = @_;
42              
43             # $self is a blessed coderef, which is a closure on %stash. I might end up
44             # replacing this with a more typical blessed hashref. But, I don't think
45             # it's as awful as it sounds.
46              
47             my $self = sub {
48 156     156   228 my $self = shift;
49 156         234 my $key = shift;
50              
51 156 100       383 $stash{$key} = shift if @_;
52              
53 156 100       364 if (!exists $stash{$key}) {
54 38         79 my $value = $attributes{$key};
55              
56             # If the default value is a subref, call it.
57 38 100 66     175 if (ref($value) && ref($value) eq 'CODE') {
58 23         67 $value = $self->$value();
59             }
60              
61 38         81552 $stash{$key} = $value;
62             }
63              
64 156         719 return $stash{$key};
65 10         66 };
66              
67 10         45 return bless $self, $class;
68             }
69              
70             sub _build_app {
71 9     9   17 my $self = shift;
72 9         39 return Plack::Util::load_psgi($self->psgi_path());
73             }
74              
75             sub _build_tester {
76 8     8   16 my $self = shift;
77 8         41 return Plack::Test->create($self->app());
78             }
79              
80             sub run {
81 11     11 1 37 my $self = shift;
82 11         31 my %args = @_;
83              
84 11         42 my $app = $self->app();
85 11         52 my $count = $self->count();
86              
87 11         55 my $requests = $self->_create_requests();
88              
89 11 100       44 if ( $self->warm() ) {
90 1         5 $self->_execute_request( $requests->[0] );
91             }
92              
93             # If it's possible to enable NYTProf, then do so now.
94 11 50       120 if ( DB->can('enable_profile') ) {
95 0         0 DB::enable_profile();
96             }
97              
98             my $stats = reduce {
99 44     44   72 my $request_number = $b % scalar(@{$requests});
  44         89  
100 44         101 my $request = $requests->[$request_number];
101              
102 44         117 my $elapsed = $self->_time_request( $request );
103 41         838 $a->insert($elapsed);
104 41         98 $a;
105 11         149 } App::plackbench::Stats->new(), ( 0 .. ( $count - 1 ) );
106              
107 8         83 $stats->finalize;
108 8         76 return $stats;
109             }
110              
111             sub _time_request {
112 44     44   70 my $self = shift;
113              
114 44         160 my @start = gettimeofday;
115 44         151 $self->_execute_request(@_);
116 41         200 return tv_interval( \@start );
117             }
118              
119             sub _create_requests {
120 11     11   25 my $self = shift;
121              
122 11         21 my @requests;
123 11 100       85 if ( $self->post_data() ) {
124             @requests = map {
125 3         11 my $req = HTTP::Request->new( POST => $self->uri() );
126 3         412 $req->content($_);
127 3         72 $req;
128 1         3 } @{ $self->post_data() };
  1         4  
129             }
130             else {
131 10         38 @requests = ( HTTP::Request->new( GET => $self->uri() ) );
132             }
133              
134 11         30155 $self->_fixup_requests(\@requests);
135              
136 11         28 return \@requests;
137             }
138              
139             sub _fixup_requests {
140 11     11   24 my $self = shift;
141 11         19 my $requests = shift;
142              
143 11         40 my $fixups = $self->fixup();
144 11 50       23 $fixups = [ grep { reftype($_) && reftype($_) eq 'CODE' } @{$fixups} ];
  5         71  
  11         29  
145              
146 11         23 for my $request (@{$requests}) {
  11         51  
147 13         79 $_->($request) for @{$fixups};
  13         66  
148             }
149              
150 11         319 return;
151             }
152              
153             sub add_fixup_from_file {
154 6     6 1 1589 my $self = shift;
155 6         13 my $file = shift;
156              
157 6         1815 my $sub = do $file;
158              
159 6 100       114 if (!$sub) {
160 2   66     38 die($@ || $!);
161             }
162              
163 4 100 66     50 if (!reftype($sub) || !reftype($sub) eq 'CODE') {
164 1         13 die("$file: does not return a subroutine reference");
165             }
166              
167 3         17 my $existing = $self->fixup();
168 3 100 66     28 if (!$existing || !reftype($existing) || reftype($existing) ne 'ARRAY') {
      66        
169 1         12 $self->fixup([]);
170             }
171              
172 3         7 push @{$self->fixup()}, $sub;
  3         8  
173              
174 3         11 return;
175             }
176              
177             sub _execute_request {
178 45     45   72 my $self = shift;
179 45         91 my $request = shift;
180 45         107 my $response = $self->tester->request($request);
181 45 100       76366 if ( $response->is_error() ) {
182 3         37 die "Request failed: " . $response->decoded_content;
183             }
184              
185 42         463 return;
186             }
187              
188             1;
189              
190             __END__