File Coverage

blib/lib/Test/DBGp.pm
Criterion Covered Total %
statement 21 93 22.5
branch 0 38 0.0
condition 0 6 0.0
subroutine 7 20 35.0
pod 0 12 0.0
total 28 169 16.5


line stmt bran cond sub pod time code
1             package Test::DBGp;
2              
3 1     1   431 use strict;
  1         1  
  1         23  
4 1     1   4 use warnings;
  1         1  
  1         36  
5              
6             =head1 NAME
7              
8             Test::DBGp - Test helpers for debuggers using the DBGp protocol
9              
10             =head1 SYNOPSIS
11              
12             use Test::DBGp;
13              
14             dbgp_listen();
15              
16             # start program under debugger
17              
18             dbgp_wait_connection($EXPECTED_APPID);
19              
20             dbgp_command_is(['step_into'], {
21             reason => 'ok',
22             status => 'break',
23             command => 'step_into',
24             });
25              
26             =head1 DESCRIPTION
27              
28             Various helpers to write tests for modules dealing with the DBGp
29             debugger protocol.
30              
31             =cut
32              
33             our $VERSION = '0.05';
34              
35 1     1   491 use Test::Differences;
  1         11600  
  1         59  
36 1     1   425 use IO::Socket;
  1         13862  
  1         3  
37 1     1   743 use File::Spec::Functions;
  1         506  
  1         56  
38 1     1   602 use File::Temp;
  1         7276  
  1         59  
39 1     1   5 use Cwd;
  1         1  
  1         721  
40              
41             require Exporter; *import = \&Exporter::import;
42              
43             our @EXPORT = qw(
44             dbgp_response_cmp
45             dbgp_parsed_response_cmp
46             dbgp_init_is
47             dbgp_command_is
48              
49             dbgp_listen
50             dbgp_listening_port
51             dbgp_listening_path
52             dbgp_stop_listening
53             dbgp_wait_connection
54              
55             dbgp_send_command
56             );
57              
58             my ($LISTEN, $CLIENT, $INIT, $SEQ, $PORT, $PATH);
59              
60             sub dbgp_response_cmp {
61 0     0 0   local $Test::Builder::Level = $Test::Builder::Level + 1;
62              
63 0           require DBGp::Client::Parser;
64              
65 0           my ($xml, $expected) = @_;
66 0           my $res = DBGp::Client::Parser::parse($xml);
67 0           my $cmp = _extract_command_data($res, $expected);
68              
69 0           eq_or_diff($cmp, $expected);
70             }
71              
72             sub dbgp_parsed_response_cmp {
73 0     0 0   local $Test::Builder::Level = $Test::Builder::Level + 1;
74              
75 0           my ($res, $expected) = @_;
76 0           my $cmp = _extract_command_data($res, $expected);
77              
78 0           eq_or_diff($cmp, $expected);
79             }
80              
81             sub _extract_command_data {
82 0     0     my ($res, $expected) = @_;
83              
84 0 0         if (!ref $expected) {
    0          
    0          
85 0           return $res;
86             } elsif (ref $expected eq 'HASH') {
87             return {
88             map {
89 0           $_ => _extract_command_data($res->$_, $expected->{$_})
  0            
90             } keys %$expected
91             };
92             } elsif (ref $expected eq 'ARRAY') {
93 0 0         return $res if ref $res ne 'ARRAY';
94             return [
95             ( map {
96 0           _extract_command_data($res->[$_], $expected->[$_])
  0            
97             } 0 .. $#$expected ),
98             ( ("") x ($#$res - $#$expected) ),
99             ];
100             } else {
101 0           die "Can't extract ", ref $expected, "value";
102             }
103             }
104              
105             sub dbgp_listen {
106 0 0   0 0   if ($^O eq 'MSWin32') {
107 0           dbgp_listen_tcp();
108             } else {
109 0           dbgp_listen_unix();
110             }
111             }
112              
113             sub dbgp_listen_tcp {
114 0 0   0 0   return if $LISTEN;
115              
116 0 0         for my $port (!$PORT ? (17000 .. 19000) : ($PORT)) {
117 0           $LISTEN = IO::Socket::INET->new(
118             Listen => 1,
119             LocalAddr => '127.0.0.1',
120             LocalPort => $port,
121             Proto => 'tcp',
122             Timeout => 2,
123             );
124 0 0         next unless $LISTEN;
125              
126 0           $PORT = $port;
127 0           $PATH = undef;
128 0           last;
129             }
130              
131 0 0         die "Unable to open a listening socket in the 17000 - 19000 port range"
132             unless $LISTEN;
133             }
134              
135             sub dbgp_listen_unix {
136 0 0   0 0   return if $LISTEN;
137              
138 0           my $path = $PATH;
139 0 0         if (!$path) {
140 0           $path = File::Spec::Functions::rel2abs('dbgp.sock', Cwd::getcwd());
141              
142 0 0         if (length($path) >= 90) { # arbitrary, should be low enough
143 0           my $tempdir = File::Temp::tempdir(CLEANUP => 1);
144 0           $path = File::Spec::Functions::rel2abs('dbgp.sock', $tempdir);
145             }
146             }
147 0 0         unlink $path if -S $path;
148 0 0         return if -e $path;
149              
150 0           $LISTEN = IO::Socket::UNIX->new(
151             Local => $path,
152             Listen => 1,
153             );
154 0           $PORT = undef;
155 0           $PATH = $path;
156              
157 0 0         die "Unable to open a listening socket on '$path'"
158             unless $LISTEN;
159             }
160              
161             sub dbgp_stop_listening {
162 0     0 0   close $LISTEN;
163 0           $LISTEN = undef;
164             }
165              
166 0     0 0   sub dbgp_listening_port { $PORT }
167 0     0 0   sub dbgp_listening_path { $PATH }
168              
169             sub dbgp_wait_connection {
170 0     0 0   my ($pid, $reject) = @_;
171 0           my $conn = $LISTEN->accept;
172              
173 0 0         die "Did not receive any connection from the debugged program: ", $LISTEN->error
174             unless $conn;
175              
176 0 0         if ($reject) {
177 0           close $conn;
178 0           return;
179             }
180              
181 0           require DBGp::Client::Stream;
182 0           require DBGp::Client::Parser;
183              
184 0           $CLIENT = DBGp::Client::Stream->new(socket => $conn);
185              
186             # consume initialization line
187 0           $INIT = DBGp::Client::Parser::parse($CLIENT->get_line);
188              
189 0 0 0       die "We got connected with the wrong debugged program"
190             if $INIT->appid != $pid || $INIT->language ne 'Perl';
191             }
192              
193             sub dbgp_send_command {
194 0     0 0   my ($command, @args) = @_;
195              
196 0           $CLIENT->put_line($command, '-i', ++$SEQ, @args);
197 0           my $res = DBGp::Client::Parser::parse($CLIENT->get_line);
198              
199 0 0 0       die 'Mismatched transaction IDs: got ', $res->transaction_id,
200             ' expected ', $SEQ
201             if $res && $res->transaction_id != $SEQ;
202              
203 0           return $res;
204             }
205              
206             sub dbgp_init_is {
207 0     0 0   local $Test::Builder::Level = $Test::Builder::Level + 1;
208              
209 0           my ($expected) = @_;
210 0           my $cmp = _extract_command_data($INIT, $expected);
211              
212 0           eq_or_diff($cmp, $expected);
213             }
214              
215             sub dbgp_command_is {
216 0     0 0   local $Test::Builder::Level = $Test::Builder::Level + 1;
217              
218 0           my ($command, $expected) = @_;
219 0           my $res = dbgp_send_command(@$command);
220 0           my $cmp = _extract_command_data($res, $expected);
221              
222 0           eq_or_diff($cmp, $expected);
223             }
224              
225             1;
226              
227             __END__