Created
September 8, 2011 02:10
-
-
Save thinkerbot/1202439 to your computer and use it in GitHub Desktop.
Proposed shell test syntax with PTY
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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