Class: ShellTest::ShellMethods::Agent

Inherits:
Object
  • Object
show all
Defined in:
lib/shell_test/shell_methods/agent.rb

Overview

Agent wraps a PTY master-slave pair and provides methods for doing reads and writes. The expect method is inspired by the IO.expect method in the stdlib, but tailored to allow for better timeout control.

Defined Under Namespace

Classes: ReadError, WriteError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(master, slave, timer) ⇒ Agent



23
24
25
26
27
# File 'lib/shell_test/shell_methods/agent.rb', line 23

def initialize(master, slave, timer)
  @master = master
  @slave  = slave
  @timer  = timer
end

Instance Attribute Details

#masterObject (readonly)

The pty master



11
12
13
# File 'lib/shell_test/shell_methods/agent.rb', line 11

def master
  @master
end

#slaveObject (readonly)

The pty slave



14
15
16
# File 'lib/shell_test/shell_methods/agent.rb', line 14

def slave
  @slave
end

#timerObject (readonly)

The timer managing read timeouts. The timer ensures that the timeouts used by expect are never negative, or nil to indicate no timeout.

If implementing a custom timer, note that timeouts are set on the timer using timer.timeout= and retrieved via timer.timeout.



21
22
23
# File 'lib/shell_test/shell_methods/agent.rb', line 21

def timer
  @timer
end

Instance Method Details

#closeObject

Closes the master and slave.



108
109
110
111
112
113
114
115
# File 'lib/shell_test/shell_methods/agent.rb', line 108

def close
  unless master.closed?
    master.close
  end
  unless slave.closed?
    slave.close
  end
end

#expect(regexp, timeout = nil) ⇒ Object

Reads from the slave until the regexp is matched and returns the resulting string. If regexp is a String, then it is converted into a regexp assuming it’s a literal prompt (ie /^regexp\z/ - where the regexp string is Regexp escaped). If regexp is nil then expect reads until the slave eof.

A timeout may be given. If the slave doesn’t produce the expected string within the timeout then expect raises a ReadError. A ReadError will be also be raised if the slave eof is reached before the regexp matches.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/shell_test/shell_methods/agent.rb', line 45

def expect(regexp, timeout=nil)
  if regexp.kind_of?(String)
    regexp = /#{Regexp.escape(regexp)}\z/
  end

  timer.timeout = timeout

  buffer = ''
  while true
    timeout = timer.timeout

    # Use read+select instead of read_nonblock to avoid polling in a
    # tight loop.  Don't bother with readpartial and partial lengths. It
    # would be an optimization, especially because the regexp matches
    # each loop, but adds complexity because expect could read past the
    # end of the regexp and it is unlikely to be necessary in test
    # scenarios (ie this is not meant to be a general solution).
    unless IO.select([slave],nil,nil,timeout)
      msg = "timeout waiting for %s after %.2fs" % [regexp ? regexp.inspect : 'EOF', timeout]
      raise ReadError.new(msg, buffer)
    end

    begin
      c = slave.read(1)
    rescue Errno::EIO
      # see notes in Utils#spawn
      c = nil
    end

    if c.nil?
      if regexp.nil?
        break
      else
        raise ReadError.new("end of file reached", buffer)
      end
    end

    buffer << c

    if regexp && buffer =~ regexp
      break
    end
  end
  buffer
end

#on(regexp, input, timeout = nil) ⇒ Object



29
30
31
32
33
# File 'lib/shell_test/shell_methods/agent.rb', line 29

def on(regexp, input, timeout=nil)
  output = expect(regexp, timeout)
  write(input)
  output
end

#read(timeout = nil) ⇒ Object

Read to the end of the slave. Raises a ReadError if the slave eof is not reached within the timeout.



93
94
95
# File 'lib/shell_test/shell_methods/agent.rb', line 93

def read(timeout=nil)
  expect nil, timeout
end

#write(input, timeout = nil) ⇒ Object

Writes to the master. A timeout may be given. If the master doesn’t become available for writing within the timeout then write raises an WriteError.



100
101
102
103
104
105
# File 'lib/shell_test/shell_methods/agent.rb', line 100

def write(input, timeout=nil)
  unless IO.select(nil,[master],nil,timeout)
    raise WriteError.new("timeout waiting for master")
  end
  master.print input
end