File Coverage

blib/lib/Expect/Simple.pm
Criterion Covered Total %
statement 72 86 83.7
branch 22 40 55.0
condition 3 9 33.3
subroutine 12 18 66.6
pod 9 9 100.0
total 118 162 72.8


line stmt bran cond sub pod time code
1             package Expect::Simple;
2              
3 1     1   88992 use strict;
  1         3  
  1         43  
4 1     1   5 use warnings;
  1         2  
  1         31  
5              
6 1     1   6 use Carp;
  1         7  
  1         108  
7 1     1   20449 use Expect;
  1         151077  
  1         3922  
8              
9             our $VERSION = '0.04';
10              
11              
12             sub new {
13 2     2 1 126 my $proto = shift;
14 2   33     19 my $class = ref($proto) || $proto;
15 2         47 my $obj = {
16             Timeout => 1000,
17             Debug => 0,
18             Verbose => 0,
19             Prompt => undef,
20             DisconnectCmd => undef,
21             Cmd => undef,
22             RawPty => 0,
23             };
24              
25 2         5 bless ($obj, $class);
26              
27 2 50       10 my $attr = shift or
28             croak( __PACKAGE__, ': must specify some attributes!' );
29              
30 2         4 while( my ( $attr, $val ) = each %{$attr} )
  10         36  
31             {
32 8 50       24 croak( __PACKAGE__, ": attribute error : `$attr' is not recognized" )
33             unless exists $obj->{$attr};
34              
35 8         17 $obj->{$attr} = $val;
36             }
37              
38              
39             # ensure all the attribures are set
40 2         9 foreach ( keys %$obj )
41             {
42 14 50       35 croak( __PACKAGE__, ": must specify attribute `$_'" )
43             unless defined $obj->{$_};
44             }
45              
46             # rework prompt
47 2         8 $obj->{Prompt} = [ 'ARRAY' eq ref $obj->{Prompt} ?
48 2 50       17 @{$obj->{Prompt}} : $obj->{Prompt} ];
49              
50              
51 2         4 eval { $obj->_connect; };
  2         12  
52 2 50       12 if ( $@ )
53             {
54 0         0 chomp $@;
55 0         0 croak (__PACKAGE__, ': ', $@);
56             }
57              
58 2         29 return $obj;
59             }
60              
61             # _connect - start up the cmd
62             #
63             # creates an Expect object which talks to the specified command. It dies with
64             # an appropriate message upon error.
65              
66             sub _connect
67             {
68 2     2   4 my $obj = shift;
69              
70 2 50       13 print STDERR "Running command..."
71             if $obj->{Verbose};
72              
73             # don't store the connection in the object until we're sure it's
74             # up. otherwise DESTROY will try to disconnect, which won't work...
75 2         6 delete $obj->{_conn};
76 2         18 my $conn = Expect->new();
77              
78 2 50       2514 $conn->raw_pty(1) if $obj->{RawPty};
79              
80             # Expect docs say that upon failure of spawn, one can get the error
81             # from the next call to expect(). Unfortunately, that message is
82             # generated by a die in the forked child process, which means it has
83             # propagated up through the eval in Expect::Simple::new and
84             # had lots of cruft slapped onto it, front and back.
85             #
86             # instead, I use a horrible hack to grab the (undocumented) warning
87             # emitted by Expect if $^W is turned on.
88              
89             {
90 2         68 my $error;
  2         4  
91 2         10 local $^W = 1;
92 2     0   22 local $SIG{__WARN__} = sub { chomp( $error = $_[0]); return; };
  0         0  
  0         0  
93              
94 1         7 my $success =
95             $conn->spawn( 'ARRAY' eq ref($obj->{Cmd})
96 2 100       23 ? @{$obj->{Cmd}}
97             : $obj->{Cmd} );
98              
99 2 50       58914 if ( ! $success )
100             {
101 0         0 chomp $error;
102 0         0 die( "error spawning command: $error\n" );
103             }
104             }
105              
106 2 50       19 print STDERR "done.\n"
107             if $obj->{Verbose};
108              
109 2         39 $conn->debug( $obj->{Debug} );
110              
111 2 50       128 $conn->log_stdout( $obj->{Verbose} > 3 ? 1 : 0 );
112              
113 2         73 $obj->{_conn} = $conn;
114 2 50       10 $obj->_expect( @{$obj->{Prompt}} )
  2         33  
115             or die( __PACKAGE__, ": couldn't find prompt\n");
116             }
117              
118             sub _disconnect
119             {
120 2     2   5 my $obj = shift;
121              
122             return unless
123 2 50 33     32 $obj->{_conn} && !defined $obj->{_conn}->exitstatus;
124              
125 2 50       126 print STDERR "Disconnecting.\n"
126             if $obj->{Verbose};
127              
128 2         16 $obj->{_conn}->print( $obj->{DisconnectCmd}, "\n" );
129 2         153 $obj->_expect( 'the unexpected' );
130 2 50       14 croak( __PACKAGE__, ": disconnection error" )
131             unless $obj->{_conn}->exp_error =~ /^(2|3)/;
132              
133 2         88 $obj->{_conn} = undef;
134             }
135              
136              
137             # send( @commands )
138             #
139             # send commands to the server. each command is sent independently.
140             # it waits for the prompt to indicate success.
141             #
142             # it croaks if there was an error. $obj->error returns
143             # the results of the communication to
144             # the server which caused the error.
145              
146             sub send
147             {
148 4     4 1 2452 my $obj = shift;
149              
150 4         18 foreach ( @_ )
151             {
152 4 50 33     28 print STDERR "Sending `$_'\n"
153             if $obj->{Verbose} && ! $obj->{_conn}->log_stdout;
154 4         32 $obj->{_conn}->print( $_, "\n");
155              
156 4 50       570 $obj->_expect( @{$obj->{Prompt}} ) ||
  4         22  
157             croak( __PACKAGE__, ": couldn't find prompt after send");
158             }
159             }
160              
161              
162             # _expect( @match_patterns )
163             #
164             # match output of the server.The error message is massaged to
165             # make it more obvious.
166             #
167             # it returns 1 upon success, undef if there was an error.
168              
169             sub _expect
170             {
171 8     8   15 my $obj = shift;
172              
173 8         65 my $match = $obj->{_conn}->expect( $obj->{Timeout}, @_ );
174              
175 8         91377 $obj->{_error} = undef;
176              
177 8 100       41 unless ( defined $match )
178             {
179 2         26 local $_ = $obj->{_conn}->exp_error;
180              
181 2 50       80 if ( /^1/ )
    50          
182             {
183 0         0 $obj->{_error} = 'connection timed out';
184             }
185             elsif ( /^(2|3)/ )
186             {
187 2         5 $obj->{_error} = 'connection unexpectedly terminated';
188             }
189             else
190             {
191 0         0 my ( $errno, $errmsg) = /(\d):(.*)/;
192              
193 0         0 $obj->{_error} = "error in communications: $errmsg";
194             }
195              
196 2         37 return undef;
197             }
198              
199 6         53 1;
200             }
201              
202 0     0 1 0 sub error { shift()->{_error} }
203 0     0 1 0 sub error_expect { shift()->{_conn}->exp_error }
204 0     0 1 0 sub match_idx { shift()->{_conn}->exp_match_number }
205 1     1 1 632 sub match_str { shift()->{_conn}->exp_match }
206 4     4 1 94 sub before { shift()->{_conn}->exp_before }
207 0     0 1 0 sub after { shift()->{_conn}->exp_after }
208 0     0 1 0 sub expect_handle{ shift()->{_conn} }
209              
210 2     2   4582 sub DESTROY { shift()->_disconnect }
211              
212              
213             # Autoload methods go after =cut, and are processed by the autosplit program.
214              
215             1;
216             __END__
217              
218             =head1 NAME
219              
220             Expect::Simple - wrapper around the Expect module
221              
222             =head1 SYNOPSIS
223              
224             use Expect::Simple;
225              
226             my $obj = new Expect::Simple
227             { Cmd => [ dmcoords => 'verbose=1', "infile=$infile"],
228             Prompt => [ -re => 'dmcoords>:\s+' ],
229             DisconnectCmd => 'q',
230             Verbose => 0,
231             Debug => 0,
232             Timeout => 100
233             };
234              
235             $obj->send( $cmd );
236             print $obj->before;
237             print $obj->after;
238             print $obj->match_str, "\n";
239             print $obj->match_idx, "\n";
240             print $obj->error_expect;
241             print $obj->error;
242              
243             $expect_object = $obj->expect_handle;
244              
245             =head1 DESCRIPTION
246              
247             C<Expect::Simple> is a wrapper around the C<Expect> module which
248             should suffice for simple applications. It hides most of the
249             C<Expect> machinery; the C<Expect> object is available for tweaking if
250             need be.
251              
252             Generally, one starts by creating an B<Expect::Simple> object using
253             B<new>. This will start up the target program, and will wait until
254             one of the specified prompts is output by the target. At that point
255             the caller should B<send()> commands to the program; the results are
256             available via the B<before>, B<after>, B<match_str>, and B<match_idx>
257             methods. Since B<Expect> simulates a terminal, there will be extra
258             C<\r> characters at the end of each line in the result (on UNIX at
259             least). This is easily fixed:
260              
261             ($res = $obj->before) =~ tr/\r//d;
262             @lines = split( "\n", $res );
263              
264             This is B<not> done automatically.
265              
266              
267             Exceptions will be thrown on error (match with C</Expect::Simple/>).
268             Errors from B<Expect> are available via the B<error_expect> method.
269             More human readable errors are available via the B<error> method.
270              
271             The connection is automatically broken (by sending the specified
272             disconnect command to the target) when the B<Expect::Simple> object is
273             destroyed.
274              
275              
276             =head2 Methods
277              
278             =over 8
279              
280             =item new
281              
282             $obj = Expect::Simple->new( \%attr );
283              
284             This creates a new object, starting up the program with which to
285             communicate (using the B<Expect> B<spawn> method) and waiting for a
286             prompt. The passed hash reference must contain at least the
287             B<Prompt>, B<DisconnectCmd>, and B<Cmd> elements. The available
288             attributes are:
289              
290             =over 8
291              
292             =item Cmd
293              
294             Cmd => $command,
295             Cmd => [ $command, $arg1, $arg2, ... ],
296              
297             The command to which to connect. The passed command may either be a
298             scalar or an array.
299              
300             =item Prompt
301              
302             This specifies one or more prompts to scan for. For a single prompt,
303             the value may be a scalar; for more, or for matching of regular
304             expressions, it should be an array reference. For example,
305              
306             Prompt => 'prompt1> ',
307             Prompt => [ 'prompt1> ', 'prompt2> ', -re => 'prompt\d+>\s+' ]
308              
309             All prompts are taken literally, unless immediately preceded by a C<-re> flag,
310             in which case they are regular expressions.
311              
312             =item DisconnectCmd
313              
314             This is the command to be sent to the target program which will cause
315             it to exit.
316              
317             =item RawPty
318              
319             If set, then underlying B<Expect> object's pty mode is set to raw mode
320             (see B<Expect::raw_pty()>).
321              
322             =item Timeout
323              
324             The time in seconds to wait until giving up on the target program
325             responding. This is used during program startup and when any commands
326             are sent to the program. It defaults to 1000 seconds.
327              
328             =item Debug
329              
330             The value is passed to B<Expect> via its B<debug> method.
331              
332             =item Verbose
333              
334             This results in various messages printed to the STDERR stream.
335             If greater than 3, it turns on B<Expect>'s logging to STDOUT (via
336             the B<log_stdout> B<Expect> method.
337              
338              
339             =back
340              
341             =item send
342              
343             $obj->send( $cmd );
344             $obj->send( @cmds );
345              
346             Send one or more commands to the target. After each command is sent,
347             it waits for a prompt from the target. Only the output resulting from
348             the last command is available via the B<after>, B<before>, etc. methods.
349              
350             =item match_idx
351              
352             This returns a unary based index indicating which prompt (in the list
353             of prompts specified via the C<Prompt> attribute to the B<new> method)
354             was received after the last command was sent. It will be undef if
355             none was returned.
356              
357             =item match_str
358              
359             This returns the prompt which was matched after the last command was sent.
360              
361             =item before
362              
363             This returns the string received before the prompt. If no prompt was seen,
364             it returns all output accumulated. This is usually what the caller wants
365             to parse. Note that the first line will (usually) be the command that
366             was sent to the target, because of echoing. Check this out to be sure!
367              
368             =item after
369              
370             This returns the 'after' string. Please read the B<Expect> docs for more
371             enlightenment.
372              
373             =item error
374              
375             This returns a cleaned up, more humanly readable version of the errors
376             from B<Expect>. It'll be undef if there was no error.
377              
378             =item error_expect
379              
380             This returns the original B<Expect> error.
381              
382             =item expect_handle
383              
384             This returns the B<Expect> object, in case further tweaking is necessary.
385              
386             =back
387              
388              
389             =head1 BUGS
390              
391             If the command to be run does not exist (or not in the current
392             execution path), it's quite possible that the B<new> method will not
393             throw an exception. It's up to the caller to make sure that the command
394             will run! There's no known workaround for this.
395              
396             =head1 LICENSE
397              
398             This software is released under the GNU General Public License. You
399             may find a copy at
400              
401             http://www.fsf.org/copyleft/gpl.html
402              
403             =head1 AUTHOR
404              
405             Diab Jerius (djerius@cpan.org)
406              
407             =cut