Skip to content

Instantly share code, notes, and snippets.

@thinkerbot
Created September 8, 2011 02:10
Show Gist options
  • Save thinkerbot/1202439 to your computer and use it in GitHub Desktop.
Save thinkerbot/1202439 to your computer and use it in GitHub Desktop.
Proposed shell test syntax with PTY
class Agent
attr_reader :stdin
attr_reader :stdout
def initialize(stdin, stdout)
@stdin = stdin
@stdout = stdout
end
def run(inputs, max_run_time=nil)
timer = Timer.new(max_run_time)
timer.start
inputs.each do |prompt, input, timeout, callback|
# pos - sets mark at current time + timeout (up to max)
# neg - preserves current mark
# nil - sets mark to end time
timer.set_mark(timeout)
buffer = read_until(prompt, timer.time_to_mark)
if block_given?
yield buffer
end
if callback
callback.call(buffer)
end
write(input)
end
timer.stop # returns time elapsed
end
def read_until(prompt, timeout)
if timeout < 0
# how should this be handled?
end
buffer = ''
while true
if !IO.select([stdin],nil,nil,timeout)
raise "timeout:\n#{buffer}"
end
if stdin.eof?
break
end
# Use readpartial instead of read because it will not block if the
# length is not fully available.
#
# Use readpartial+select instead of read_nonblock to avoid polling
# in a tight loop.
buffer << stdin.readpartial(1024)
if prompt && buffer =~ prompt
break
end
end
buffer
end
def write(input)
if input
stdout.print input
end
end
end
class Session
attr_reader :cmd
attr_reader :inputs
def initialize(cmd)
@cmd = cmd
@inputs = []
end
def on(prompt, input, timeout=nil, &callback)
inputs << [prompt, input, timeout, callback]
self
end
def run(max_run_time, &block)
PTY.spawn(cmd) do |stdin,stdout,pid|
begin
agent = Agent.new(stdin, stdout)
agent.run(inputs, max_run_time, &block)
rescue
Process.kill(9, pid)
raise
ensure
Process.wait(pid)
end
end
end
end
assert_script %{
$ command # [timeout before next command]
output
$ command
multi
line
output
$ multi
> line
> command
output
$ commands with
$ no output
$ command
with single line :..: arbitrary
$ command
with multi line :...:
arbitrary
$ command
with escape sequence :.:..:.:
arbitrary
$ command
with :.(A|a)rbitrary.: regexp
}
# guesses line up to password as expect, timeout same as command
# adjusted for each so that the total time is no longer than given
assert_script %{
$ su root -- whoami
Password: {{password}}
root
}
# same assumptions as above, but resolve as a logical name
assert_script %{
$ su root -- whoami
Password: {{password}}
root
}, :expect => {
"password" => "linecook"
}
assert_script %{
$ su root -- whoami
Password: {{password}}
root
}, :expect => {
"password" => [/Password: /, "linecook", 100]
}
assert_script %{
$ echo 'Password: {{password}}'
Password: \{{password}}
}
assert_script %{
$ echo 'Password: \{{password}}'
Password: \\\{{password}}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment