File Coverage

blib/lib/Test/Statsd.pm
Criterion Covered Total %
statement 78 83 93.9
branch 8 12 66.6
condition 3 7 42.8
subroutine 16 16 100.0
pod 0 6 0.0
total 105 124 84.6


line stmt bran cond sub pod time code
1             package Test::Statsd;
2             {
3             $Test::Statsd::VERSION = '0.17';
4             }
5              
6 12     12   80758 use 5.008;
  12         52  
  12         494  
7 12     12   70 use strict;
  12         26  
  12         460  
8 12     12   62 use warnings;
  12         40  
  12         482  
9              
10 12     12   29528 use AnyEvent;
  12         155492  
  12         488  
11 12     12   15648 use AnyEvent::Strict;
  12         320946  
  12         390  
12 12     12   18458 use AnyEvent::Handle;
  12         250754  
  12         480  
13 12     12   15936 use AnyEvent::Socket;
  12         247798  
  12         2060  
14 12     12   13458 use IO::Socket::INET ();
  12         303326  
  12         360  
15 12     12   23316 use Time::HiRes;
  12         45852  
  12         102  
16              
17             sub new {
18 12     12 0 6392 my ($class, $opt) = @_;
19 12   33     102 $class = ref $class || $class;
20 12   50     48 $opt ||= {};
21 12         74 my $self = {
22             binary => $opt->{binary},
23             config => $opt->{config},
24             _statsd_pid => undef,
25             };
26 12         60 bless $self, $class;
27             }
28              
29             # A read callback (read_cb) can optionally be used in special
30             # cases when you don't want the TCP server to be shut down
31             # when the first flush data is received (see delete-idle-stats test
32             # for an example).
33              
34             sub wait_and_collect_flush_data {
35 6     6 0 453 my ($self, $port, $read_cb) = @_;
36              
37 6         75 $self->{_flush_data} = "";
38              
39             # Pretend to be a carbon/graphite daemon
40 6   50     191 $port ||= 40003;
41              
42 6         19 my $srv;
43 6         686 my $cv = AE::cv;
44              
45             $srv = tcp_server undef, $port, sub {
46 8     8   9414434 my ($fh, $host, $port) = @_;
47 8         28 my $hdl;
48             $hdl = AnyEvent::Handle->new(
49             fh => $fh,
50             on_error => sub {
51 0         0 warn "Socket error: $!\n";
52 0         0 $_[0]->destroy
53             },
54             on_read => sub {
55 8         7014 my ($ae_handle) = @_;
56             # Store graphite data into a private object member
57 8         80 $self->{_flush_data} .= $ae_handle->rbuf;
58 8 100       121 if ($read_cb) {
59 4         33 $read_cb->($hdl, $cv, $self->{_flush_data});
60             # We need to clear the received data now, or our
61             # reader will be surprised receiving the old + new
62             # buffer in the n > 1 round.
63 4         3302 $self->{_flush_data} = "";
64 4         34 $ae_handle->{rbuf} = "";
65             } else {
66             # Calling send() on the condvar stops the TCP server
67 4         58 $cv->send();
68             }
69             },
70 2         578 on_eof => sub { $hdl->destroy },
71 8         358 );
72 6         2955 };
73 6         3030 $cv->recv();
74 6         1029 return $self->{_flush_data};
75             }
76              
77             sub hashify {
78 8     8 0 9183 my ($self, $str) = @_;
79 8         179 my @lines = split m{\r?\n}, $str;
80 8         22 my $stats;
81 8         35 for (@lines) {
82 64         407 $_ =~ s{^ \s* (\S*) \s* $}{$1}x;
83 64 50       143 next unless defined;
84 64         178 my ($key, $val, $ts) = split;
85 64         215 $stats->{$key} = $val;
86             }
87 8         37 return $stats;
88             }
89              
90             sub start_statsd {
91 12     12 0 120 my ($self) = @_;
92              
93 12         26118 my $pid = fork;
94 12 50       1421 if (! defined $pid) {
    100          
95 0         0 die "Fork failed: $! Aborting.";
96             }
97              
98             # Child
99             elsif ($pid == 0) {
100 6         1099 my @binary = split " ", $self->{binary};
101 6         109 my $config = $self->{config};
102 6         0 exec @binary, $config, '2>&1 1>/dev/null';
103             }
104              
105             # Parent
106             else {
107 6         339 $self->{_statsd_pid} = $pid;
108             # Allow for child statsd to start up
109 6         3021481 Time::HiRes::usleep(500_000);
110             }
111             }
112              
113             sub stop_statsd {
114 6     6 0 3873 my ($self) = @_;
115              
116 6         22 my $pid = $self->{_statsd_pid};
117 6 50       37 if (! $pid) {
118 0         0 die "Statsd was never started?";
119             }
120              
121 6 50       193 if (! kill(15, $pid)) {
122 0         0 die "Failed to stop statsd (pid: $pid). "
123             . "Please do something manually ($!)";
124             }
125              
126 6         257 return 1;
127             }
128              
129             sub send_udp {
130 105     105 0 5831 my ($self, $host, $port, $payload) = @_;
131              
132 105         5196 my $sock = IO::Socket::INET->new(
133             Proto => 'udp',
134             PeerAddr => $host,
135             PeerPort => $port,
136             );
137              
138 105         103139 my $len = $sock->send($payload);
139 105         17999 $sock->close();
140              
141 105         22820 return $len == length($payload);
142             }
143              
144             1;
145              
146             =pod
147              
148             =head1 NAME
149              
150             Test::Statsd - Test harness for any statsd server daemon
151              
152             =head1 DESCRIPTION
153              
154             Embeds the logic to perform integration tests of any statsd
155             daemon that can be launched from the command line.
156              
157             Usage:
158              
159             my $t = Test::Statsd->new({
160             binary => './bin/statsd',
161             config => './bin/sample-config.json'
162             });
163              
164             # Brings up the statsd server in the background
165             # with the specified configuration, and stores its pid
166             $t->start_statsd();
167              
168            
169             =cut