expectr 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/expectr.rb CHANGED
@@ -1,290 +1,287 @@
1
- # = expectr.rb
2
- #
3
- # Copyright (c) Chris Wuest <chris@chriswuest.com>
4
- # Expectr is freely distributable under the terms of an MIT-style license.
5
- # See COPYING or http://www.opensource.org/licenses/mit-license.php.
6
-
7
- begin
8
- require 'pty'
9
- rescue LoadError
10
- require 'popen4'
11
- end
12
-
1
+ require 'pty'
13
2
  require 'timeout'
14
3
  require 'thread'
15
4
 
16
- # Fixes specifically for Ruby 1.8
17
- if RUBY_VERSION =~ /^1.8/
18
- # Enforcing encoding is not needed in 1.8 (probably.) So, we'll define
19
- # String#encode! to do nothing, for interoperability.
20
- class String #:nodoc:
21
- def encode!(encoding)
22
- end
23
- end
24
-
25
- # In Ruby 1.8, we want to ignore SIGCHLD. This is for two reasons:
26
- # * SIGCHLD will be sent (and cause exceptions) for every Expectr object
27
- # created
28
- # * As John Carter documented in his RExpect library, calls to files which
29
- # do not exist can cause odd and unexpected behavior.
30
- trap 'CHLD', Proc.new { nil }
31
- end
5
+ require 'expectr/error'
32
6
 
33
- # == Description
34
- # Expectr is an implementation of the Expect library in ruby (see
35
- # http://expect.nist.gov).
7
+ # Public: Expectr is an API to the functionality of Expect (see
8
+ # http://expect.nist.gov) implemented in ruby.
36
9
  #
37
10
  # Expectr contrasts with Ruby's built-in Expect class by avoiding tying in
38
- # with IO and instead creating a new object entirely to allow for more
39
- # fine-grained control over the execution and display of the program being
11
+ # with the IO class, instead creating a new object entirely to allow for more
12
+ # grainular control over the execution and display of the program being
40
13
  # run.
41
14
  #
42
- # == Examples
43
- # === Simple task automation
44
- #
45
- # Connect via telnet to remote.example.com, run my_command, and return the
46
- # output
47
- #
48
- # exp = Expectr.new "telnet remote.example.com"
49
- # exp.expect "username:"
50
- # exp.send "example\r"
51
- # exp.expect "password:"
52
- # exp.send "my_password\r"
53
- # exp.expect "%"
54
- # exp.send "my_command\r"
55
- # exp.expect "%"
56
- # exp.send "logout"
57
- #
58
- # output = exp.discard
59
- #
60
- # === Interactive control
61
- # Silently connect via ssh to remote.example.com, log in automatically, then
62
- # relinquish control to the user. Expect slow networking, so increase
63
- # timeout.
15
+ # Examples
64
16
  #
65
- # exp = Expectr.new "ssh remote.example.com", :timeout=>45, :flush_buffer=>false
17
+ # # SSH Login to another machine
18
+ # exp = Expectr.new('ssh user@example.com')
19
+ # exp.expect("Password:")
20
+ # exp.send('password')
21
+ # exp.interact!(blocking: true)
66
22
  #
67
- # match = exp.expect /password|yes\/no/
68
- # case match.to_s
69
- # when /password/
70
- # exp.send "my_password\r"
71
- # when /yes\/no/
72
- # exp.send "yes\r"
73
- # exp.expect /password/
74
- # exp.send "my_password\r"
75
- # else
76
- # puts "Cannot connect to remote.example.com!"
77
- # die
23
+ # # See if a web server is running on the local host, react accordingly
24
+ # exp = Expectr.new('netstat -ntl|grep ":80 " && echo "WEB"', timeout: 1)
25
+ # if exp.expeect("WEB")
26
+ # # Do stuff if we see 'WEB' in the output
27
+ # else
28
+ # # Do other stuff
78
29
  # end
79
- #
80
- # exp.expect "$"
81
- # exp.interact
82
- #
83
30
  class Expectr
84
- # Amount of time in seconds a call to +expect+ may last (default 30)
85
- attr_accessor :timeout
86
- # Size of buffer in bytes to attempt to read in at once (default 8 KiB)
87
- attr_accessor :buffer_size
88
- # Whether to flush program output to STDOUT (default true)
89
- attr_accessor :flush_buffer
90
- # PID of running process
91
- attr_reader :pid
92
- # Active buffer to match against
93
- attr_reader :buffer
94
- # Buffer passed since last +expect+ match
95
- attr_reader :discard
31
+ # Public: Gets/sets the number of seconds a call to Expectr#expect may last
32
+ attr_accessor :timeout
33
+ # Public: Gets/sets the number of bytes to use for the internal buffer
34
+ attr_accessor :buffer_size
35
+ # Public: Gets/sets whether to constrain the buffer to the buffer size
36
+ attr_accessor :constrain
37
+ # Public: Gets/sets whether to flush program output to STDOUT
38
+ attr_accessor :flush_buffer
39
+ # Public: Returns the PID of the running process
40
+ attr_reader :pid
41
+ # Public: Returns the active buffer to match against
42
+ attr_reader :buffer
43
+ # Public: Returns the buffer discarded by the latest call to Expectr#expect
44
+ attr_reader :discard
96
45
 
97
- #
98
- # === Synopsis
99
- #
100
- # Expectr.new(cmd, args)
101
- #
102
- # === Arguments
103
- # +cmd+::
104
- # Command to be executed (String or File)
105
- # +args+::
106
- # Hash of modifiers for Expectr. Meaningful values are:
107
- # * :buffer_size::
108
- # Amount of data to read at a time. Default 8 KiB
109
- # * :flush_buffer::
110
- # Flush buffer to STDOUT during execution? Default true
111
- # * :timeout::
112
- # Timeout in seconds for each +expect+ call. Default 30
113
- #
114
- # === Description
115
- #
116
- # Spawn +cmd+ and attach to STDIN and STDOUT for new process. Fall back
117
- # to using Open4 if PTY is not present (this is the case on Windows
118
- # implementations of ruby.
46
+ # Public: Initialize a new Expectr object.
47
+ # Spawns a sub-process and attaches to STDIN and STDOUT for the new process.
119
48
  #
120
- def initialize(cmd, args={})
121
- raise ArgumentError, "String or File expected, was given #{cmd.class}" unless cmd.kind_of? String or cmd.kind_of? File
122
- cmd = cmd.path if cmd.kind_of? File
49
+ # cmd - A String or File referencing the application to launch
50
+ # args - A Hash used to specify options for the new object (default: {}):
51
+ # :timeout - Number of seconds that a call to Expectr#expect has
52
+ # to complete (default: 30)
53
+ # :flush_buffer - Whether to flush output of the process to the
54
+ # console (default: true)
55
+ # :buffer_size - Number of bytes to attempt to read from sub-process
56
+ # at a time. If :constrain is true, this will be the
57
+ # maximum size of the internal buffer as well.
58
+ # (default: 8192)
59
+ # :constrain - Whether to constrain the internal buffer from the
60
+ # sub-process to :buffer_size (default: false)
61
+ def initialize(cmd, args={})
62
+ unless cmd.kind_of? String or cmd.kind_of? File
63
+ raise ArgumentError, "String or File expected"
64
+ end
65
+
66
+ cmd = cmd.path if cmd.kind_of? File
67
+
68
+ @buffer = ''.encode("UTF-8")
69
+ @discard = ''.encode("UTF-8")
70
+
71
+ @timeout = args[:timeout] || 30
72
+ @flush_buffer = args[:flush_buffer].nil? ? true : args[:flush_buffer]
73
+ @buffer_size = args[:buffer_size] || 8192
74
+ @constrain = args[:constrain] || false
75
+
76
+ @out_mutex = Mutex.new
77
+ @out_update = false
78
+ @interact = false
79
+
80
+ @stdout,@stdin,@pid = PTY.spawn(cmd)
81
+
82
+ Thread.new do
83
+ while @pid > 0
84
+ unless select([@stdout], nil, nil, @timeout).nil?
85
+ buf = ''.encode("UTF-8")
86
+
87
+ begin
88
+ @stdout.sysread(@buffer_size, buf)
89
+ rescue Errno::EIO #Application went away.
90
+ @pid = 0
91
+ break
92
+ end
93
+
94
+ print_buffer(buf)
123
95
 
124
- args[0] = {} unless args[0]
125
- @buffer = String.new
126
- @discard = String.new
127
- @timeout = args[:timeout] || 30
128
- @flush_buffer = args[:flush_buffer].nil? ? true : args[:flush_buffer]
129
- @buffer_size = args[:buffer_size] || 8192
130
- @out_mutex = Mutex.new
131
- @out_update = false
96
+ @out_mutex.synchronize do
97
+ @buffer << buf
98
+ if @buffer.length > @buffer_size && @constrain
99
+ @buffer = @buffer[-@buffer_size..-1]
100
+ end
101
+ @out_update = true
102
+ end
103
+ end
104
+ end
105
+ end
132
106
 
133
- [@buffer, @discard].each {|x| x.encode! "UTF-8" }
107
+ Thread.new do
108
+ Process.wait @pid
109
+ @pid = 0
110
+ end
111
+ end
134
112
 
135
- if defined? PTY
136
- @stdout,@stdin,@pid = PTY.spawn cmd
137
- else
138
- cmd << " 2>&1" if cmd[/2\s*>/].nil?
139
- @pid, @stdin, @stdout, stderr = Open4::popen4 cmd
140
- end
113
+ # Public: Relinquish control of the running process to the controlling
114
+ # terminal, acting as a pass-through for the life of the process. SIGINT
115
+ # will be caught and sent to the application as "\C-c".
116
+ #
117
+ # args - A Hash used to specify options to be used for interaction (default:
118
+ # {}):
119
+ # :flush_buffer - explicitly set @flush_buffer to the value specified
120
+ # :blocking - Whether to block on this call or allow code
121
+ # execution to continue (default: false)
122
+ #
123
+ # Returns the interaction Thread
124
+ def interact!(args = {})
125
+ raise ProcessError if @interact
141
126
 
142
- Thread.new do
143
- while @pid > 0
144
- unless select([@stdout], nil, nil, @timeout).nil?
145
- buf = ''
127
+ blocking = args[:blocking] || false
128
+ @flush_buffer = args[:flush_buffer].nil? ? true : args[:flush_buffer]
129
+ @interact = true
146
130
 
147
- begin
148
- @stdout.sysread(@buffer_size, buf)
149
- rescue Errno::EIO #Application went away.
150
- @pid = 0
151
- break
152
- end
131
+ # Save our old tty settings and set up our new environment
132
+ old_tty = `stty -g`
133
+ `stty -icanon min 1 time 0 -echo`
153
134
 
154
- buf.encode! "UTF-8"
155
- print_buffer buf
135
+ # SIGINT should be set along to the program
136
+ oldtrap = trap 'INT' do
137
+ send "\C-c"
138
+ end
139
+
140
+ interact = Thread.new do
141
+ input = ''.encode("UTF-8")
142
+ while @pid > 0 && @interact
143
+ if select([STDIN], nil, nil, 1)
144
+ c = STDIN.getc.chr
145
+ send c unless c.nil?
146
+ end
147
+ end
156
148
 
157
- @out_mutex.synchronize do
158
- @buffer << buf
159
- @out_update = true
160
- end
161
- end
162
- end
163
- end
149
+ trap 'INT', oldtrap
150
+ `stty #{old_tty}`
151
+ @interact = false
152
+ end
164
153
 
165
- Thread.new do
166
- Process.wait @pid
167
- @pid = 0
168
- end
169
- end
154
+ blocking ? interact.join : interact
155
+ end
170
156
 
171
- #
172
- # Clear output buffer
173
- #
174
- def clear_buffer
175
- @out_mutex.synchronize do
176
- @buffer = ''
177
- @out_update = false
178
- end
179
- end
157
+ # Public: Report whether or not current Expectr object is in interact mode
158
+ #
159
+ # Returns true or false
160
+ def interact?
161
+ @interact
162
+ end
163
+
164
+ # Public: Cause the current Expectr object to drop out of interact mode
165
+ #
166
+ # Returns nothing.
167
+ def leave!
168
+ @interact=false
169
+ end
180
170
 
181
- #
182
- # === Synopsis
183
- #
184
- # Expectr#interact
185
- #
186
- # === Description
187
- #
188
- # Relinquish control of the running process to the controlling terminal,
189
- # acting simply as a pass-through for the life of the process.
190
- #
191
- # Interrupts should be caught and sent to the application.
192
- #
193
- def interact
194
- oldtrap = trap 'INT' do
195
- send "\C-c"
196
- end
171
+ # Public: Kill the running process, raise ProcessError if the pid isn't > 1
172
+ #
173
+ # signal - Symbol, String, or Fixnum representing the signal to send to the
174
+ # running process. (default: :TERM)
175
+ #
176
+ # Returns true if the process was successfully killed, false otherwise
177
+ def kill!(signal=:TERM)
178
+ raise ProcessError unless @pid > 0
179
+ (Process::kill(signal.to_sym, @pid) == 1)
180
+ end
197
181
 
198
- @flush_buffer = true
199
- old_tty = `stty -g`
200
- `stty -icanon min 1 time 0 -echo`
201
-
202
- in_thread = Thread.new do
203
- input = ''
204
- while @pid > 0
205
- if select([STDIN], nil, nil, 1)
206
- send STDIN.getc.chr
207
- end
208
- end
209
- end
182
+ # Public: Send input to the active process
183
+ #
184
+ # str - String to be sent to the active process
185
+ #
186
+ # Returns nothing.
187
+ # Raises Expectr::ProcessError if the process isn't running
188
+ def send(str)
189
+ begin
190
+ @stdin.syswrite str
191
+ rescue Errno::EIO #Application went away.
192
+ @pid = 0
193
+ end
194
+ raise Expectr::ProcessError unless @pid > 0
195
+ end
210
196
 
211
- in_thread.join
212
- trap 'INT', oldtrap
213
- `stty #{old_tty}`
214
- return nil
215
- end
216
- alias :interact! :interact
197
+ # Public: Wraps Expectr#send, appending a newline to the end of the string
198
+ #
199
+ # str - String to be sent to the active process (default: '')
200
+ #
201
+ # Returns nothing.
202
+ def puts(str = '')
203
+ send str + "\n"
204
+ end
217
205
 
218
- #
219
- # Send +str+ to application
220
- #
221
- def send(str)
222
- begin
223
- @stdin.syswrite str
224
- rescue Errno::EIO #Application went away.
225
- @pid = 0
226
- end
227
- raise ArgumentError unless @pid > 0
228
- end
206
+ # Public: Begin a countdown and search for a given String or Regexp in the
207
+ # output buffer.
208
+ #
209
+ # pattern - String or Regexp representing what we want to find
210
+ # recoverable - Denotes whether failing to match the pattern should cause the
211
+ # method to raise an exception (default: false)
212
+ #
213
+ # Examples
214
+ #
215
+ # exp.expect("this should exist")
216
+ # # => MatchData
217
+ #
218
+ # exp.expect("this should exist") do
219
+ # # ...
220
+ # end
221
+ #
222
+ # exp.expect(/not there/)
223
+ # # Raises Timeout::Error
224
+ #
225
+ # exp.expect(/not there/, true)
226
+ # # => nil
227
+ #
228
+ # Returns a MatchData object once a match is found if no block is given
229
+ # Yields the MatchData object representing the match
230
+ # Raises TypeError if something other than a String or Regexp is given
231
+ # Raises Timeout::Error if a match isn't found in time, unless recoverable
232
+ def expect(pattern, recoverable = false)
233
+ match = nil
229
234
 
230
- #
231
- # === Synopsis
232
- #
233
- # Expectr#expect /regexp/, recoverable=false
234
- # Expectr#expect "String", recoverable=true
235
- #
236
- # === Arguments
237
- #
238
- # +pattern+::
239
- # String or regexp to match against
240
- # +recoverable+::
241
- # Determines if execution can continue after a timeout
242
- #
243
- # === Description
244
- #
245
- # Wait +timeout+ seconds to match +pattern+ in +buffer+. If timeout is
246
- # reached, raise an error unless +recoverable+ is true.
247
- #
248
- def expect(pattern, recoverable = false)
249
- match = nil
235
+ case pattern
236
+ when String
237
+ pattern = Regexp.new(Regexp.quote(pattern))
238
+ when Regexp
239
+ else
240
+ raise TypeError, "Pattern class should be String or Regexp"
241
+ end
250
242
 
251
- case pattern
252
- when String
253
- pattern = Regexp.new(Regexp.quote(pattern))
254
- when Regexp
255
- else raise TypeError, "Pattern class should be String or Regexp, passed: #{pattern.class}"
256
- end
243
+ begin
244
+ Timeout::timeout(@timeout) do
245
+ while match.nil?
246
+ if @out_update
247
+ @out_mutex.synchronize do
248
+ match = pattern.match @buffer
249
+ @out_update = false
250
+ end
251
+ end
252
+ sleep 0.1
253
+ end
254
+ end
257
255
 
258
- begin
259
- Timeout::timeout(@timeout) do
260
- while match.nil?
261
- if @out_update
262
- @out_mutex.synchronize do
263
- match = pattern.match @buffer
264
- @out_update = false
265
- end
266
- end
267
- sleep 0.1
268
- end
269
- end
256
+ @out_mutex.synchronize do
257
+ @discard = @buffer[0..match.begin(0)-1]
258
+ @buffer = @buffer[match.end(0)..-1]
259
+ @out_update = true
260
+ end
261
+ rescue Timeout::Error => details
262
+ raise details unless recoverable
263
+ end
270
264
 
271
- @out_mutex.synchronize do
272
- @discard = @buffer[0..match.begin(0)-1]
273
- @buffer = @buffer[match.end(0)..-1]
274
- @out_update = true
275
- end
276
- rescue Timeout::Error => details
277
- raise details unless recoverable
278
- end
265
+ block_given? ? yield(match) : match
266
+ end
279
267
 
280
- return match
281
- end
268
+ # Public: Clear output buffer
269
+ #
270
+ # Returns nothing.
271
+ def clear_buffer!
272
+ @out_mutex.synchronize do
273
+ @buffer = ''.encode("UTF-8")
274
+ @out_update = false
275
+ end
276
+ end
282
277
 
283
- #
284
- # Print buffer to STDOUT only if +flush_buffer+ is true
285
- #
286
- def print_buffer(buf)
287
- print buf if @flush_buffer
288
- STDOUT.flush
289
- end
278
+ # Internal: Print buffer to STDOUT if @flush_buffer is true
279
+ #
280
+ # buf - String to be printed to STDOUT
281
+ #
282
+ # Returns nothing.
283
+ def print_buffer(buf)
284
+ print buf if @flush_buffer
285
+ STDOUT.flush unless STDOUT.sync
286
+ end
290
287
  end
@@ -0,0 +1,5 @@
1
+ class Expectr
2
+ # Public: Error to denote a problem with the running Process associated with
3
+ # the current Expectr object
4
+ class ProcessError < StandardError; end
5
+ end
@@ -1,3 +1,3 @@
1
1
  class Expectr
2
- VERSION = '0.9.1'
2
+ VERSION = '1.0.0'
3
3
  end
data/test/test_core.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'helper'
2
+
3
+ class CoreTests < Test::Unit::TestCase
4
+ # For the purpose of testing, we will assume we are working within a POSIX
5
+ # environment.
6
+ def setup
7
+ @exp = Expectr.new("ls /dev", :flush_buffer => false, :timeout => 1,
8
+ :buffer_size => 4096)
9
+ end
10
+
11
+ def test_object_consistency
12
+ assert_equal false, @exp.flush_buffer
13
+ assert_equal 1, @exp.timeout
14
+ assert_equal 4096, @exp.buffer_size
15
+ end
16
+
17
+ # POSIX specifies /dev/console, /dev/null and /dev/tty must exist.
18
+ def test_match_sets_discard
19
+ assert_not_equal nil, @exp.expect(/null/)
20
+ assert_not_equal '', @exp.discard
21
+ end
22
+
23
+ def test_match_failure
24
+ assert_raises(Timeout::Error) { @exp.expect(/ThisFileShouldNotExist/) }
25
+ assert_nothing_raised { @exp.expect(/ThisFileShouldNotExist/, true) }
26
+ end
27
+
28
+ def test_clear_buffer
29
+ sleep 1
30
+ assert_not_equal @exp.buffer, ''
31
+ @exp.clear_buffer!
32
+ assert_equal '', @exp.buffer
33
+ end
34
+
35
+ def test_pid_set
36
+ assert @exp.pid > 0
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ require 'helper'
2
+
3
+ class InitializationTests < Test::Unit::TestCase
4
+ def test_spawn_with_file
5
+ assert_nothing_raised { exp = Expectr.new(File.new("/bin/ls"), :flush_buffer => false) }
6
+ end
7
+
8
+ def test_spawn_with_string
9
+ assert_nothing_raised { exp = Expectr.new(File.new("/bin/ls"), :flush_buffer => false) }
10
+ end
11
+
12
+ # lib/expectr.rb's permissions should hopefully be set to 0644
13
+ def test_spawn_failures
14
+ assert_raises(Errno::ENOENT) { exp = Expectr.new("lib/ThisFileShouldNotExist", :flush_buffer => false) }
15
+ assert_raises(Errno::EACCES) { exp = Expectr.new("lib/expectr.rb", :flush_buffer => false) }
16
+ end
17
+ end
@@ -0,0 +1,114 @@
1
+ require 'helper'
2
+
3
+ class InteractionTest < Test::Unit::TestCase
4
+ # Assume that bc(1) exists on the system for these tests
5
+ def setup
6
+ @exp = Expectr.new("bc", :flush_buffer => false, :timeout => 1)
7
+ end
8
+
9
+ def test_send_and_expect
10
+ assert_nothing_raised do
11
+ @exp.send("300+21\n")
12
+ @exp.expect("321")
13
+ @exp.puts("quit")
14
+ end
15
+ end
16
+
17
+ def test_expect_with_block
18
+ assert_nothing_raised do
19
+ @exp.send("300+21\n")
20
+ @exp.expect("321") { |m| m.nil? ? raise(ArgumentError) : true }
21
+ end
22
+
23
+ assert_raises(TimeoutError) do
24
+ @exp.send("300+21\n")
25
+ @exp.expect("xxx") { |m| m.nil? ? raise(ArgumentError) : true }
26
+ end
27
+
28
+ assert_raises(ArgumentError) do
29
+ @exp.send("300+21\n")
30
+ @exp.expect("xxx", true) { |m| m.nil? ? raise(ArgumentError) : true }
31
+ end
32
+ end
33
+
34
+ def test_send_to_terminated_fails
35
+ @exp.send("quit\n")
36
+ sleep 2
37
+ assert_raises(Expectr::ProcessError) { @exp.send("test\n") }
38
+ end
39
+
40
+ def test_interact_sets_appropriate_flags
41
+ [
42
+ Thread.new {
43
+ assert_equal false, @exp.interact?
44
+
45
+ sleep 0.5
46
+ @exp.interact!.join
47
+ },
48
+ Thread.new {
49
+ sleep 1
50
+ assert_equal true, @exp.flush_buffer
51
+ assert_equal true, @exp.interact?
52
+
53
+ @exp.flush_buffer = false
54
+ @exp.send("quit\n")
55
+ }
56
+ ].each {|x| x.join}
57
+ end
58
+
59
+ def test_interact_mode
60
+ [
61
+ Thread.new {
62
+ sleep 0.5
63
+ @exp.interact!.join
64
+ },
65
+ Thread.new {
66
+ sleep 1
67
+ @exp.flush_buffer = false
68
+ @exp.send("300+21\n")
69
+ @exp.send("quit\n")
70
+ }
71
+ ].each {|x| x.join}
72
+
73
+ assert_not_nil @exp.expect(/321/)
74
+ end
75
+
76
+ def test_leaving_interact_mode
77
+ [
78
+ Thread.new {
79
+ sleep 0.5
80
+ @exp.interact!.join
81
+ },
82
+ Thread.new {
83
+ sleep 1
84
+ @exp.flush_buffer = false
85
+ assert_nothing_raised { @exp.leave! }
86
+ assert_equal false, @exp.interact?
87
+ @exp.send("quit\n")
88
+ }
89
+ ].each {|x| x.join}
90
+ end
91
+
92
+ def test_blocking_interact_mode
93
+ [
94
+ Thread.new {
95
+ sleep 0.5
96
+ @exp.interact!(blocking: true)
97
+ },
98
+ Thread.new {
99
+ sleep 1
100
+ @exp.flush_buffer = false
101
+ @exp.send("300+21\n")
102
+ @exp.send("quit\n")
103
+ }
104
+ ].each {|x| x.join}
105
+
106
+ assert_not_nil @exp.expect(/321/)
107
+ end
108
+
109
+ def test_kill_process
110
+ assert_equal true, @exp.kill!
111
+ assert_equal 0, @exp.pid
112
+ assert_raises(Expectr::ProcessError) { @exp.send("test\n") }
113
+ end
114
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expectr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-19 00:00:00.000000000 Z
12
+ date: 2012-10-30 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Expectr is an interface to the functionality of Expect in Ruby
15
15
  email: chris@chriswuest.com
@@ -23,9 +23,12 @@ files:
23
23
  - Rakefile
24
24
  - expectr.gemspec
25
25
  - lib/expectr.rb
26
+ - lib/expectr/error.rb
26
27
  - lib/expectr/version.rb
27
28
  - test/helper.rb
28
- - test/test_expectr.rb
29
+ - test/test_core.rb
30
+ - test/test_initialization.rb
31
+ - test/test_interaction.rb
29
32
  homepage: http://github.com/cwuest/expectr
30
33
  licenses:
31
34
  - MIT
@@ -52,4 +55,6 @@ signing_key:
52
55
  specification_version: 3
53
56
  summary: Expect for Ruby
54
57
  test_files:
55
- - test/test_expectr.rb
58
+ - test/test_core.rb
59
+ - test/test_initialization.rb
60
+ - test/test_interaction.rb
data/test/test_expectr.rb DELETED
@@ -1,81 +0,0 @@
1
- require 'helper'
2
-
3
- class TestExpectr < Test::Unit::TestCase
4
- def setup
5
- @exp = Expectr.new "ls /bin", :flush_buffer => false, :timeout => 2, :buffer_size => 4096
6
- end
7
-
8
- def test_execution
9
- assert_equal @exp.flush_buffer, false
10
- assert_equal @exp.timeout, 2
11
- assert_equal @exp.buffer_size, 4096
12
- end
13
-
14
- def test_match
15
- assert_not_equal @exp.expect(/sh/), nil
16
- assert_not_equal @exp.discard, ''
17
- end
18
-
19
- def test_match_failure
20
- assert_raises(Timeout::Error) { @exp.expect /ThisFileShouldNotExist/ }
21
- assert_nothing_raised { @exp.expect /ThisFileShouldNotExist/, true }
22
- end
23
-
24
- def test_send
25
- exp = Expectr.new "bc", :flush_buffer => false
26
- exp.send "20+301\n"
27
- exp.expect /321/
28
- end
29
-
30
- def test_send_to_terminated
31
- exp = Expectr.new "ls", :flush_buffer => false
32
- sleep 1
33
- assert_raises(ArgumentError) { exp.send "test\n" }
34
- end
35
-
36
- def test_clear_buffer
37
- sleep 1
38
- assert_not_equal @exp.buffer, ''
39
- @exp.clear_buffer
40
- assert_equal @exp.buffer, ''
41
- end
42
-
43
- def test_pid_set
44
- assert @exp.pid > 0
45
- end
46
-
47
- def test_interact
48
- unless RUBY_VERSION =~ /1.8/
49
- exp = Expectr.new "bc", :flush_buffer => false
50
- [
51
- Thread.new {
52
- sleep 1
53
- exp.interact
54
- },
55
- Thread.new {
56
- sleep 2
57
- assert_equal exp.flush_buffer, true
58
- exp.flush_buffer = false
59
- exp.send "300+21\n"
60
- exp.send "quit\n"
61
- }
62
- ].each {|x| x.join}
63
-
64
- assert_not_nil exp.expect /321/
65
- end
66
- end
67
-
68
- def test_create_with_file
69
- assert_nothing_raised { exp = Expectr.new File.new("/bin/ls"), :flush_buffer => false }
70
- end
71
-
72
- def test_executable
73
- assert_nothing_raised { exp = Expectr.new "/bin/ls", :flush_buffer => false }
74
-
75
- # Ruby 1.8's PTY allows execution of non-executable/nonexistent files without complaint
76
- unless RUBY_VERSION =~ /1.8/
77
- assert_raises(Errno::ENOENT) { exp = Expectr.new "/bin/ThisFileShouldNotExist", :flush_buffer => false }
78
- assert_raises(Errno::EACCES) { exp = Expectr.new "lib/expectr.rb", :flush_buffer => false }
79
- end
80
- end
81
- end