Skip to content

tkluck/Expect.jl

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

95 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Expect.jl: Synchronous communication with interactive programs

Expect.jl allows to spawn interactive applications and control them by communicating through their standard input/output streams.

Expect.jl is similar to D. Libes's Expect and many other Expect-like modules. It can be used to automate interactive applications such as shells, perform software testing or drive test harnesses easily.

Warning

This is a work-in-progress, the API is subject to change without notice. Suggestions about API design are highly appreciated.

The idea behind Expect is simple: commands are spawned with their I/O descriptors attached to the current process. You write to the command's standard input, then you wait for a set of known replies from it's output.

If the program you're communicating with was already meant to be controlled through a serial protocol, then using readandwrite directly is recommended as it avoids some overhead when spawning the process.

Expect differs from readandwrite in three major ways:

  • Commands are spawned under a pseudo-TTY interface, forcing libc-based programs to flush their output buffer at each line.
  • Reading is performed for you until a set of possible conditions is met.
  • Reading raises an ExpectTimeout exception when it blocks for longer than the requested threshold.

Using ftp to retrieve a remote file list:

julia> # Start a new process
julia> using Expect;
julia> proc = ExpectProc(`ftp ftp.scene.org`, 16);
julia> # Wait for the prompt
julia> expect!(proc, "> ");
julia> # Send "ls" and wait for prompt
julia> println(proc, "ls");
julia> expect!(proc, "> ");
julia> # Collect all the output since the last prompt
julia> list = proc.before;
julia> println(list);
drwxrwsr-x  11 redhound ftpadm       4096 Mar  2 20:57 incoming
drwx------   2 redhound ftpadm      16384 Feb 24 12:58 lost+found
-rw-r--r--   1 redhound ftpadm   16060223 Mar 22 01:15 ls-lR
-rw-r--r--   1 redhound ftpadm    2723919 Mar 22 01:15 ls-lR.gz
drwxrwsr-x  12 redhound ftpadm       4096 Nov 26  2013 mirrors
drwxrwsr-x   8 redhound ftpadm       4096 Mar  4 18:25 pub
-rw-r--r--   1 redhound ftpadm        547 Nov  9  2013 welcome.msg

Some highlights about the example:

  • The first argument to ExpectProc is just a regular Cmd object.
  • You don't have to worry about buffering.
  • An ExpectProc handle can be read or written to using the standard I/O functions such as readbytes! or println.
  • proc.before contains all the command's output since the last expect! match (excluding the match itself).

The last point can be clarified with the following example:

julia> using Expect;
julia> proc = ExpectProc(`echo "a b c "`, 16);
julia> expect!(proc, " ")
"a"
julia> expect!(proc, " ")
"b"
julia> expect!(proc, " ")
"c"

For convenience, expect! already returns the contents of proc.before when given a single pattern to match.

expect! however is normally used with a list of possible matches to perform. In this scenario, the index that matched first will be returned. The matched string itself is also available in proc.match. This is useful to perform conditional processing depending on the command's output:

using Expect
proc = ExpectProc(`interpreter`, 16)
println(proc, "perform")
idx = expect!(proc, ["> ", "ERROR: "])
if idx == 2
    # error occurred ...
end

The matches themselves can be regular strings or Regex objects. When a Regex is used, the content of proc.match contains a match object for the element that matched.

See tests/runtests.jl for more usage examples.

ExpectProc(cmd, timeout; env, encoding="utf8", pty=true):

Constructs a new ExpectProc object.

cmd:the Cmd command to be spawned.
timeout:default communication timeout.
env:environment for the command (defaults as a copy of the current)
encoding:I/O encoding (currently limited to utf8)
pty:request allocation of pty

expect!(proc, vector; timeout):

Read the standard output of the program until one of the strings/regular expressions specified in vector matches. The index of the element that matched first is returned. Matches are searched in sequential order.

When timeout is specified, it overrides the default timeout specified in the constructor. A value of Inf waits indefinitely.

proc.before is reset at each call to contain all the standard output before the match.

proc.match contains either a string or a match object for the element that matched.

expect!(proc, element; timeout):

Read the standard output of the program until the string/regular expressions specified in element matches. The content of proc.before is returned.

with_timeout!(func, proc, timeout):

Modify the default read timeout within the context of func. Normally used with the do syntax:

proc = ExpectProc(`command`, 1)
with_timeout!(proc, Inf) do
  # no read timeout within this context
end

ExpectTimeout:

Reading from the command stalled for the specified number of seconds without matching any pattern. Reading can continue.

ExpectEOF:

The output ended without matching any of the specified patterns.
"Expect.jl" is distributed under the MIT license (see LICENSE.rst).
Copyright(c) 2014-2017 by wave++ "Yuri D'Elia" <wavexx@thregr.org>.

About

Synchronous communication with interactive programs

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Julia 100.0%