expectr 0.9.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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