net-ssh-multi 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.
@@ -0,0 +1,152 @@
1
+ require 'common'
2
+ require 'net/ssh/multi/channel'
3
+
4
+ class ChannelTest < Test::Unit::TestCase
5
+ def test_each_should_iterate_over_each_component_channel
6
+ channels = [c1 = mock('channel'), c2 = mock('channel'), c3 = mock('channel')]
7
+ channel = Net::SSH::Multi::Channel.new(mock('session'), channels)
8
+ result = []
9
+ channel.each { |c| result << c }
10
+ assert_equal channels, result
11
+ end
12
+
13
+ def test_property_accessors
14
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [])
15
+ channel[:foo] = "hello"
16
+ assert_equal "hello", channel[:foo]
17
+ channel['bar'] = "goodbye"
18
+ assert_equal "goodbye", channel['bar']
19
+ assert_nil channel[:bar]
20
+ assert_nil channel['foo']
21
+ end
22
+
23
+ def test_exec_should_delegate_to_component_channels
24
+ c1, c2, results = mock('channel'), mock('channel'), []
25
+ c1.expects(:exec).with('ls -l').yields(c1)
26
+ c2.expects(:exec).with('ls -l').yields(c2)
27
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
28
+ assert_equal channel, channel.exec('ls -l') { |c| results << c }
29
+ assert_equal [c1, c2], results
30
+ end
31
+
32
+ def test_request_pty_should_delegate_to_component_channels
33
+ c1, c2, results = mock('channel'), mock('channel'), []
34
+ c1.expects(:request_pty).with(:foo => 5).yields(c1)
35
+ c2.expects(:request_pty).with(:foo => 5).yields(c2)
36
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
37
+ assert_equal channel, channel.request_pty(:foo => 5) { |c| results << c }
38
+ assert_equal [c1, c2], results
39
+ end
40
+
41
+ def test_send_data_should_delegate_to_component_channels
42
+ c1, c2 = mock('channel'), mock('channel')
43
+ c1.expects(:send_data).with("hello\n")
44
+ c2.expects(:send_data).with("hello\n")
45
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
46
+ assert_equal channel, channel.send_data("hello\n")
47
+ end
48
+
49
+ def test_active_should_be_true_if_all_component_channels_are_active
50
+ c1, c2, c3 = stub('channel', :active? => true), stub('channel', :active? => true), stub('channel', :active? => true)
51
+ channel = Net::SSH::Multi::Channel.new(stub('session'), [c1, c2, c3])
52
+ assert channel.active?
53
+ end
54
+
55
+ def test_active_should_be_true_if_any_component_channels_are_active
56
+ c1, c2, c3 = stub('channel', :active? => true), stub('channel', :active? => false), stub('channel', :active? => false)
57
+ channel = Net::SSH::Multi::Channel.new(stub('session'), [c1, c2, c3])
58
+ assert channel.active?
59
+ end
60
+
61
+ def test_active_should_be_false_if_no_component_channels_are_active
62
+ c1, c2, c3 = stub('channel', :active? => false), stub('channel', :active? => false), stub('channel', :active? => false)
63
+ channel = Net::SSH::Multi::Channel.new(stub('session'), [c1, c2, c3])
64
+ assert !channel.active?
65
+ end
66
+
67
+ def test_wait_should_block_until_active_is_false
68
+ channel = Net::SSH::Multi::Channel.new(MockSession.new, [])
69
+ channel.expects(:active?).times(4).returns(true,true,true,false)
70
+ assert_equal channel, channel.wait
71
+ end
72
+
73
+ def test_close_should_delegate_to_component_channels
74
+ c1, c2 = mock('channel'), mock('channel')
75
+ c1.expects(:close)
76
+ c2.expects(:close)
77
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
78
+ assert_equal channel, channel.close
79
+ end
80
+
81
+ def test_eof_bang_should_delegate_to_component_channels
82
+ c1, c2 = mock('channel'), mock('channel')
83
+ c1.expects(:eof!)
84
+ c2.expects(:eof!)
85
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
86
+ assert_equal channel, channel.eof!
87
+ end
88
+
89
+ def test_on_data_should_delegate_to_component_channels
90
+ c1, c2, results = mock('channel'), mock('channel'), []
91
+ c1.expects(:on_data).yields(c1)
92
+ c2.expects(:on_data).yields(c2)
93
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
94
+ assert_equal channel, channel.on_data { |c| results << c }
95
+ assert_equal [c1, c2], results
96
+ end
97
+
98
+ def test_on_extended_data_should_delegate_to_component_channels
99
+ c1, c2, results = mock('channel'), mock('channel'), []
100
+ c1.expects(:on_extended_data).yields(c1)
101
+ c2.expects(:on_extended_data).yields(c2)
102
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
103
+ assert_equal channel, channel.on_extended_data { |c| results << c }
104
+ assert_equal [c1, c2], results
105
+ end
106
+
107
+ def test_on_process_should_delegate_to_component_channels
108
+ c1, c2, results = mock('channel'), mock('channel'), []
109
+ c1.expects(:on_process).yields(c1)
110
+ c2.expects(:on_process).yields(c2)
111
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
112
+ assert_equal channel, channel.on_process { |c| results << c }
113
+ assert_equal [c1, c2], results
114
+ end
115
+
116
+ def test_on_close_should_delegate_to_component_channels
117
+ c1, c2, results = mock('channel'), mock('channel'), []
118
+ c1.expects(:on_close).yields(c1)
119
+ c2.expects(:on_close).yields(c2)
120
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
121
+ assert_equal channel, channel.on_close { |c| results << c }
122
+ assert_equal [c1, c2], results
123
+ end
124
+
125
+ def test_on_eof_should_delegate_to_component_channels
126
+ c1, c2, results = mock('channel'), mock('channel'), []
127
+ c1.expects(:on_eof).yields(c1)
128
+ c2.expects(:on_eof).yields(c2)
129
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
130
+ assert_equal channel, channel.on_eof { |c| results << c }
131
+ assert_equal [c1, c2], results
132
+ end
133
+
134
+ def test_on_request_should_delegate_to_component_channels
135
+ c1, c2, results = mock('channel'), mock('channel'), []
136
+ c1.expects(:on_request).with("exit-status").yields(c1)
137
+ c2.expects(:on_request).with("exit-status").yields(c2)
138
+ channel = Net::SSH::Multi::Channel.new(mock('session'), [c1, c2])
139
+ assert_equal channel, channel.on_request("exit-status") { |c| results << c }
140
+ assert_equal [c1, c2], results
141
+ end
142
+
143
+ private
144
+
145
+ class MockSession
146
+ def loop
147
+ while true do
148
+ return if !yield(self)
149
+ end
150
+ end
151
+ end
152
+ end
data/test/common.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require 'mocha'
@@ -0,0 +1,20 @@
1
+ require 'common'
2
+ require 'net/ssh/multi'
3
+
4
+ class MultiTest < Test::Unit::TestCase
5
+ def test_start_with_block_should_yield_session_and_then_close
6
+ Net::SSH::Multi::Session.any_instance.expects(:loop)
7
+ Net::SSH::Multi::Session.any_instance.expects(:close)
8
+ yielded = false
9
+ Net::SSH::Multi.start do |session|
10
+ yielded = true
11
+ assert_instance_of Net::SSH::Multi::Session, session
12
+ end
13
+ end
14
+
15
+ def test_start_without_block_should_return_open_session
16
+ Net::SSH::Multi::Session.any_instance.expects(:loop).never
17
+ Net::SSH::Multi::Session.any_instance.expects(:close).never
18
+ assert_instance_of Net::SSH::Multi::Session, Net::SSH::Multi.start
19
+ end
20
+ end
@@ -0,0 +1,256 @@
1
+ require 'common'
2
+ require 'net/ssh/multi/server'
3
+
4
+ class ServerTest < Test::Unit::TestCase
5
+ def setup
6
+ @master = stub('multi-session', :default_user => "bob")
7
+ end
8
+
9
+ def test_accessor_without_properties_should_access_empty_hash
10
+ assert_nil server('host')[:foo]
11
+ end
12
+
13
+ def test_accessor_with_properties_should_access_properties
14
+ assert_equal "hello", server('host', :properties => { :foo => "hello" })[:foo]
15
+ end
16
+
17
+ def test_port_should_return_22_by_default
18
+ assert_equal 22, server('host').port
19
+ end
20
+
21
+ def test_port_should_return_given_port_when_present
22
+ assert_equal 1234, server('host', :port => 1234).port
23
+ end
24
+
25
+ def test_port_should_return_parsed_port_when_present
26
+ assert_equal 1234, server('host:1234', :port => 1235).port
27
+ end
28
+
29
+ def test_user_should_return_default_user_by_default
30
+ assert_equal "bob", server('host').user
31
+ end
32
+
33
+ def test_user_should_return_given_user_when_present
34
+ assert_equal "jim", server('host', :user => "jim").user
35
+ end
36
+
37
+ def test_user_should_return_parsed_user_when_present
38
+ assert_equal "jim", server('jim@host', :user => "john").user
39
+ end
40
+
41
+ def test_equivalence_when_host_and_user_and_port_match
42
+ s1 = server('user@host:1234')
43
+ s2 = server('user@host:1234')
44
+ assert s1.eql?(s2)
45
+ assert_equal s1.hash, s2.hash
46
+ assert s1 == s2
47
+ end
48
+
49
+ def test_equivalence_when_host_mismatch
50
+ s1 = server('user@host1:1234')
51
+ s2 = server('user@host2:1234')
52
+ assert !s1.eql?(s2)
53
+ assert_not_equal s1.hash, s2.hash
54
+ assert s1 != s2
55
+ end
56
+
57
+ def test_equivalence_when_port_mismatch
58
+ s1 = server('user@host:1234')
59
+ s2 = server('user@host:1235')
60
+ assert !s1.eql?(s2)
61
+ assert_not_equal s1.hash, s2.hash
62
+ assert s1 != s2
63
+ end
64
+
65
+ def test_equivalence_when_user_mismatch
66
+ s1 = server('user1@host:1234')
67
+ s2 = server('user2@host:1234')
68
+ assert !s1.eql?(s2)
69
+ assert_not_equal s1.hash, s2.hash
70
+ assert s1 != s2
71
+ end
72
+
73
+ def test_to_s_should_include_user_and_host
74
+ assert_equal "user@host", server('user@host').to_s
75
+ end
76
+
77
+ def test_to_s_should_include_user_and_host_and_port_when_port_is_given
78
+ assert_equal "user@host:1234", server('user@host:1234').to_s
79
+ end
80
+
81
+ def test_gateway_should_be_nil_by_default
82
+ assert_nil server('host').gateway
83
+ end
84
+
85
+ def test_gateway_should_be_set_with_the_via_value
86
+ gateway = mock('gateway')
87
+ assert_equal gateway, server('host', :via => gateway).gateway
88
+ end
89
+
90
+ def test_session_with_default_argument_should_not_instantiate_session
91
+ assert_nil server('host').session
92
+ end
93
+
94
+ def test_session_with_true_argument_should_instantiate_and_cache_session
95
+ srv = server('host')
96
+ session = expect_connection_to(srv)
97
+ assert_equal session, srv.session(true)
98
+ assert_equal session, srv.session(true)
99
+ assert_equal session, srv.session
100
+ end
101
+
102
+ def test_session_that_cannot_authenticate_adds_host_to_exception_message
103
+ srv = server('host')
104
+ Net::SSH.expects(:start).with('host', 'bob', {}).raises(Net::SSH::AuthenticationFailed.new('bob'))
105
+
106
+ begin
107
+ srv.new_session
108
+ flunk
109
+ rescue Net::SSH::AuthenticationFailed => e
110
+ assert_equal "bob@host", e.message
111
+ end
112
+ end
113
+
114
+ def test_close_channels_when_session_is_not_open_should_not_do_anything
115
+ assert_nothing_raised { server('host').close_channels }
116
+ end
117
+
118
+ def test_close_channels_when_session_is_open_should_iterate_over_open_channels_and_close_them
119
+ srv = server('host')
120
+ session = expect_connection_to(srv)
121
+ c1 = mock('channel', :close => nil)
122
+ c2 = mock('channel', :close => nil)
123
+ c3 = mock('channel', :close => nil)
124
+ session.expects(:channels).returns(1 => c1, 2 => c2, 3 => c3)
125
+ assert_equal session, srv.session(true)
126
+ srv.close_channels
127
+ end
128
+
129
+ def test_close_when_session_is_not_open_should_not_do_anything
130
+ assert_nothing_raised { server('host').close }
131
+ end
132
+
133
+ def test_close_when_session_is_open_should_close_session
134
+ srv = server('host')
135
+ session = expect_connection_to(srv)
136
+ session.expects(:close)
137
+ @master.expects(:server_closed).with(srv)
138
+ assert_equal session, srv.session(true)
139
+ srv.close
140
+ end
141
+
142
+ def test_busy_should_be_false_when_session_is_not_open
143
+ assert !server('host').busy?
144
+ end
145
+
146
+ def test_busy_should_be_false_when_session_is_not_busy
147
+ srv = server('host')
148
+ session = expect_connection_to(srv)
149
+ session.expects(:busy?).returns(false)
150
+ srv.session(true)
151
+ assert !srv.busy?
152
+ end
153
+
154
+ def test_busy_should_be_true_when_session_is_busy
155
+ srv = server('host')
156
+ session = expect_connection_to(srv)
157
+ session.expects(:busy?).returns(true)
158
+ srv.session(true)
159
+ assert srv.busy?
160
+ end
161
+
162
+ def test_preprocess_should_be_nil_when_session_is_not_open
163
+ assert_nil server('host').preprocess
164
+ end
165
+
166
+ def test_preprocess_should_return_result_of_session_preprocess
167
+ srv = server('host')
168
+ session = expect_connection_to(srv)
169
+ session.expects(:preprocess).returns(:result)
170
+ srv.session(true)
171
+ assert_equal :result, srv.preprocess
172
+ end
173
+
174
+ def test_readers_should_return_empty_array_when_session_is_not_open
175
+ assert_equal [], server('host').readers
176
+ end
177
+
178
+ def test_readers_should_return_all_listeners_when_session_is_open
179
+ srv = server('host')
180
+ session = expect_connection_to(srv)
181
+ io1, io2, io3, io4 = Reader.new, Reader.new, Reader.new, Reader.new
182
+ session.expects(:listeners).returns(io1 => 2, io2 => 4, io3 => 6, io4 => 8)
183
+ srv.session(true)
184
+ assert_equal [io1, io2, io3, io4], srv.readers.sort
185
+ end
186
+
187
+ def test_writers_should_return_empty_array_when_session_is_not_open
188
+ assert_equal [], server('host').writers
189
+ end
190
+
191
+ def test_writers_should_return_all_listeners_that_are_pending_writes_when_session_is_open
192
+ srv = server('host')
193
+ session = expect_connection_to(srv)
194
+ listeners = { Reader.new(true) => 1, MockIO.new => 2,
195
+ MockIO.new => 3, Reader.new => 4, Reader.new(true) => 5 }
196
+ session.expects(:listeners).returns(listeners)
197
+ srv.session(true)
198
+ assert_equal 2, srv.writers.length
199
+ end
200
+
201
+ def test_postprocess_should_return_true_when_session_is_not_open
202
+ assert_equal true, server('host').postprocess([], [])
203
+ end
204
+
205
+ def test_postprocess_should_call_session_postprocess_with_ios_belonging_to_session
206
+ srv = server('host')
207
+ session = expect_connection_to(srv)
208
+ session.expects(:listeners).returns(1 => 2, 3 => 4, 5 => 6, 7 => 8)
209
+ session.expects(:postprocess).with([1,3], [7]).returns(:result)
210
+ srv.session(true)
211
+ assert_equal :result, srv.postprocess([1,11,3], [18,14,7,12])
212
+ end
213
+
214
+ private
215
+
216
+ class MockIO
217
+ include Comparable
218
+
219
+ @@identifier = 0
220
+
221
+ attr_reader :id
222
+
223
+ def initialize
224
+ @id = (@@identifier += 1)
225
+ end
226
+
227
+ def <=>(io)
228
+ @id <=> io.id
229
+ end
230
+
231
+ def closed?
232
+ false
233
+ end
234
+ end
235
+
236
+ class Reader < MockIO
237
+ def initialize(ready=false)
238
+ super()
239
+ @ready = ready
240
+ end
241
+
242
+ def pending_write?
243
+ @ready
244
+ end
245
+ end
246
+
247
+ def server(host, options={})
248
+ Net::SSH::Multi::Server.new(@master, host, options)
249
+ end
250
+
251
+ def expect_connection_to(server)
252
+ session = {}
253
+ @master.expects(:next_session).with(server).returns(session)
254
+ return session
255
+ end
256
+ end
@@ -0,0 +1,128 @@
1
+ require 'common'
2
+ require 'net/ssh/multi/server'
3
+ require 'net/ssh/multi/session_actions'
4
+
5
+ class SessionActionsTest < Test::Unit::TestCase
6
+ class SessionActionsContainer
7
+ include Net::SSH::Multi::SessionActions
8
+
9
+ attr_reader :servers
10
+
11
+ def initialize
12
+ @servers = []
13
+ end
14
+
15
+ def default_user
16
+ "user"
17
+ end
18
+
19
+ def use(h, o={})
20
+ server = Net::SSH::Multi::Server.new(self, h, o)
21
+ servers << server
22
+ server
23
+ end
24
+ end
25
+
26
+ def setup
27
+ @session = SessionActionsContainer.new
28
+ end
29
+
30
+ def test_busy_should_be_true_if_any_server_is_busy
31
+ srv1, srv2, srv3 = @session.use('h1'), @session.use('h2'), @session.use('h3')
32
+ srv1.stubs(:busy?).returns(false)
33
+ srv2.stubs(:busy?).returns(false)
34
+ srv3.stubs(:busy?).returns(true)
35
+ assert @session.busy?
36
+ end
37
+
38
+ def test_busy_should_be_false_if_all_servers_are_not_busy
39
+ srv1, srv2, srv3 = @session.use('h1'), @session.use('h2'), @session.use('h3')
40
+ srv1.stubs(:busy?).returns(false)
41
+ srv2.stubs(:busy?).returns(false)
42
+ srv3.stubs(:busy?).returns(false)
43
+ assert !@session.busy?
44
+ end
45
+
46
+ def test_send_global_request_should_delegate_to_sessions
47
+ s1 = mock('ssh')
48
+ s2 = mock('ssh')
49
+ s1.expects(:send_global_request).with("a", "b", "c").yields
50
+ s2.expects(:send_global_request).with("a", "b", "c").yields
51
+ @session.expects(:sessions).returns([s1, s2])
52
+ calls = 0
53
+ @session.send_global_request("a", "b", "c") { calls += 1 }
54
+ assert_equal 2, calls
55
+ end
56
+
57
+ def test_open_channel_should_delegate_to_sessions_and_set_accessors_on_each_channel_and_return_multi_channel
58
+ srv1 = @session.use('h1')
59
+ srv2 = @session.use('h2')
60
+ s1 = { :server => srv1 }
61
+ s2 = { :server => srv2 }
62
+ c1 = { :stub => :value }
63
+ c2 = {}
64
+ c1.stubs(:connection).returns(s1)
65
+ c2.stubs(:connection).returns(s2)
66
+ @session.expects(:sessions).returns([s1, s2])
67
+ s1.expects(:open_channel).with("session").yields(c1).returns(c1)
68
+ s2.expects(:open_channel).with("session").yields(c2).returns(c2)
69
+ results = []
70
+ channel = @session.open_channel do |c|
71
+ results << c
72
+ end
73
+ assert_equal [c1, c2], results
74
+ assert_equal "h1", c1[:host]
75
+ assert_equal "h2", c2[:host]
76
+ assert_equal srv1, c1[:server]
77
+ assert_equal srv2, c2[:server]
78
+ assert_instance_of Net::SSH::Multi::Channel, channel
79
+ assert_equal [c1, c2], channel.channels
80
+ end
81
+
82
+ def test_exec_should_raise_exception_if_channel_cannot_exec_command
83
+ c = { :host => "host" }
84
+ @session.expects(:open_channel).yields(c).returns(c)
85
+ c.expects(:exec).with('something').yields(c, false)
86
+ assert_raises(RuntimeError) { @session.exec("something") }
87
+ end
88
+
89
+ def test_exec_with_block_should_pass_data_and_extended_data_to_block
90
+ c = { :host => "host" }
91
+ @session.expects(:open_channel).yields(c).returns(c)
92
+ c.expects(:exec).with('something').yields(c, true)
93
+ c.expects(:on_data).yields(c, "stdout")
94
+ c.expects(:on_extended_data).yields(c, 1, "stderr")
95
+ c.expects(:on_request)
96
+ results = {}
97
+ @session.exec("something") do |c, stream, data|
98
+ results[stream] = data
99
+ end
100
+ assert_equal({:stdout => "stdout", :stderr => "stderr"}, results)
101
+ end
102
+
103
+ def test_exec_without_block_should_write_data_and_extended_data_lines_to_stdout_and_stderr
104
+ c = { :host => "host" }
105
+ @session.expects(:open_channel).yields(c).returns(c)
106
+ c.expects(:exec).with('something').yields(c, true)
107
+ c.expects(:on_data).yields(c, "stdout 1\nstdout 2\n")
108
+ c.expects(:on_extended_data).yields(c, 1, "stderr 1\nstderr 2\n")
109
+ c.expects(:on_request)
110
+ $stdout.expects(:puts).with("[host] stdout 1\n")
111
+ $stdout.expects(:puts).with("[host] stdout 2")
112
+ $stderr.expects(:puts).with("[host] stderr 1\n")
113
+ $stderr.expects(:puts).with("[host] stderr 2")
114
+ @session.exec("something")
115
+ end
116
+
117
+ def test_exec_should_capture_exit_status_of_process
118
+ c = { :host => "host" }
119
+ @session.expects(:open_channel).yields(c).returns(c)
120
+ c.expects(:exec).with('something').yields(c, true)
121
+ c.expects(:on_data)
122
+ c.expects(:on_extended_data)
123
+ c.expects(:on_request).with("exit-status").yields(c, Net::SSH::Buffer.from(:long, 127))
124
+ @session.exec("something")
125
+ assert_equal 127, c[:exit_status]
126
+ end
127
+
128
+ end