ddollar-net-ssh 2.0.1
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/CHANGELOG.rdoc +42 -0
- data/Manifest +101 -0
- data/README.rdoc +110 -0
- data/Rakefile +26 -0
- data/THANKS.rdoc +16 -0
- data/lib/net/ssh.rb +199 -0
- data/lib/net/ssh/authentication/agent.rb +175 -0
- data/lib/net/ssh/authentication/constants.rb +18 -0
- data/lib/net/ssh/authentication/key_manager.rb +169 -0
- data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
- data/lib/net/ssh/authentication/methods/hostbased.rb +71 -0
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +66 -0
- data/lib/net/ssh/authentication/methods/password.rb +39 -0
- data/lib/net/ssh/authentication/methods/publickey.rb +92 -0
- data/lib/net/ssh/authentication/pageant.rb +176 -0
- data/lib/net/ssh/authentication/session.rb +127 -0
- data/lib/net/ssh/buffer.rb +339 -0
- data/lib/net/ssh/buffered_io.rb +149 -0
- data/lib/net/ssh/config.rb +173 -0
- data/lib/net/ssh/connection/channel.rb +625 -0
- data/lib/net/ssh/connection/constants.rb +33 -0
- data/lib/net/ssh/connection/session.rb +569 -0
- data/lib/net/ssh/connection/term.rb +178 -0
- data/lib/net/ssh/errors.rb +85 -0
- data/lib/net/ssh/key_factory.rb +85 -0
- data/lib/net/ssh/known_hosts.rb +129 -0
- data/lib/net/ssh/loggable.rb +61 -0
- data/lib/net/ssh/packet.rb +102 -0
- data/lib/net/ssh/prompt.rb +93 -0
- data/lib/net/ssh/proxy/errors.rb +14 -0
- data/lib/net/ssh/proxy/http.rb +94 -0
- data/lib/net/ssh/proxy/socks4.rb +70 -0
- data/lib/net/ssh/proxy/socks5.rb +128 -0
- data/lib/net/ssh/service/forward.rb +267 -0
- data/lib/net/ssh/test.rb +89 -0
- data/lib/net/ssh/test/channel.rb +129 -0
- data/lib/net/ssh/test/extensions.rb +152 -0
- data/lib/net/ssh/test/kex.rb +44 -0
- data/lib/net/ssh/test/local_packet.rb +51 -0
- data/lib/net/ssh/test/packet.rb +81 -0
- data/lib/net/ssh/test/remote_packet.rb +38 -0
- data/lib/net/ssh/test/script.rb +157 -0
- data/lib/net/ssh/test/socket.rb +59 -0
- data/lib/net/ssh/transport/algorithms.rb +384 -0
- data/lib/net/ssh/transport/cipher_factory.rb +72 -0
- data/lib/net/ssh/transport/constants.rb +30 -0
- data/lib/net/ssh/transport/hmac.rb +31 -0
- data/lib/net/ssh/transport/hmac/abstract.rb +48 -0
- data/lib/net/ssh/transport/hmac/md5.rb +12 -0
- data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
- data/lib/net/ssh/transport/hmac/none.rb +15 -0
- data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
- data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
- data/lib/net/ssh/transport/identity_cipher.rb +40 -0
- data/lib/net/ssh/transport/kex.rb +13 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +77 -0
- data/lib/net/ssh/transport/openssl.rb +128 -0
- data/lib/net/ssh/transport/packet_stream.rb +230 -0
- data/lib/net/ssh/transport/server_version.rb +61 -0
- data/lib/net/ssh/transport/session.rb +262 -0
- data/lib/net/ssh/transport/state.rb +170 -0
- data/lib/net/ssh/verifiers/lenient.rb +30 -0
- data/lib/net/ssh/verifiers/null.rb +12 -0
- data/lib/net/ssh/verifiers/strict.rb +53 -0
- data/lib/net/ssh/version.rb +60 -0
- data/net-ssh.gemspec +56 -0
- data/setup.rb +1585 -0
- data/test/authentication/methods/common.rb +28 -0
- data/test/authentication/methods/test_abstract.rb +51 -0
- data/test/authentication/methods/test_hostbased.rb +108 -0
- data/test/authentication/methods/test_keyboard_interactive.rb +98 -0
- data/test/authentication/methods/test_password.rb +50 -0
- data/test/authentication/methods/test_publickey.rb +123 -0
- data/test/authentication/test_agent.rb +205 -0
- data/test/authentication/test_key_manager.rb +100 -0
- data/test/authentication/test_session.rb +93 -0
- data/test/common.rb +106 -0
- data/test/configs/exact_match +8 -0
- data/test/configs/wild_cards +14 -0
- data/test/connection/test_channel.rb +452 -0
- data/test/connection/test_session.rb +483 -0
- data/test/test_all.rb +6 -0
- data/test/test_buffer.rb +336 -0
- data/test/test_buffered_io.rb +63 -0
- data/test/test_config.rb +78 -0
- data/test/test_key_factory.rb +67 -0
- data/test/transport/hmac/test_md5.rb +34 -0
- data/test/transport/hmac/test_md5_96.rb +25 -0
- data/test/transport/hmac/test_none.rb +34 -0
- data/test/transport/hmac/test_sha1.rb +34 -0
- data/test/transport/hmac/test_sha1_96.rb +25 -0
- data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
- data/test/transport/test_algorithms.rb +302 -0
- data/test/transport/test_cipher_factory.rb +163 -0
- data/test/transport/test_hmac.rb +34 -0
- data/test/transport/test_identity_cipher.rb +40 -0
- data/test/transport/test_packet_stream.rb +433 -0
- data/test/transport/test_server_version.rb +55 -0
- data/test/transport/test_session.rb +312 -0
- data/test/transport/test_state.rb +173 -0
- metadata +222 -0
@@ -0,0 +1,483 @@
|
|
1
|
+
require 'common'
|
2
|
+
require 'net/ssh/connection/session'
|
3
|
+
|
4
|
+
module Connection
|
5
|
+
|
6
|
+
class TestSession < Test::Unit::TestCase
|
7
|
+
include Net::SSH::Connection::Constants
|
8
|
+
|
9
|
+
def test_constructor_should_set_defaults
|
10
|
+
assert session.channels.empty?
|
11
|
+
assert session.pending_requests.empty?
|
12
|
+
assert_equal({ socket => nil }, session.listeners)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_on_open_channel_should_register_block_with_given_channel_type
|
16
|
+
flag = false
|
17
|
+
session.on_open_channel("testing") { flag = true }
|
18
|
+
assert_not_nil session.channel_open_handlers["testing"]
|
19
|
+
session.channel_open_handlers["testing"].call
|
20
|
+
assert flag, "callback should have been invoked"
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_forward_should_create_and_cache_instance_of_forward_service
|
24
|
+
assert_instance_of Net::SSH::Service::Forward, session.forward
|
25
|
+
assert_equal session.forward.object_id, session.forward.object_id
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_listen_to_without_callback_should_add_argument_as_listener
|
29
|
+
io = stub("io")
|
30
|
+
session.listen_to(io)
|
31
|
+
assert session.listeners.key?(io)
|
32
|
+
assert_nil session.listeners[io]
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_listen_to_should_add_argument_to_listeners_list_if_block_is_given
|
36
|
+
io = stub("io", :pending_write? => true)
|
37
|
+
flag = false
|
38
|
+
session.listen_to(io) { flag = true }
|
39
|
+
assert !flag, "callback should not be invoked immediately"
|
40
|
+
assert session.listeners.key?(io)
|
41
|
+
session.listeners[io].call
|
42
|
+
assert flag, "callback should have been invoked"
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_stop_listening_to_should_remove_argument_from_listeners
|
46
|
+
io = stub("io", :pending_write? => true)
|
47
|
+
|
48
|
+
session.listen_to(io)
|
49
|
+
assert session.listeners.key?(io)
|
50
|
+
|
51
|
+
session.stop_listening_to(io)
|
52
|
+
assert !session.listeners.key?(io)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_send_message_should_enqueue_message_at_transport_layer
|
56
|
+
packet = P(:byte, REQUEST_SUCCESS)
|
57
|
+
session.send_message(packet)
|
58
|
+
assert_equal packet.to_s, socket.write_buffer
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_open_channel_defaults_should_use_session_channel
|
62
|
+
flag = false
|
63
|
+
channel = session.open_channel { flag = true }
|
64
|
+
assert !flag, "callback should not be invoked immediately"
|
65
|
+
channel.do_open_confirmation(1,2,3)
|
66
|
+
assert flag, "callback should have been invoked"
|
67
|
+
assert_equal "session", channel.type
|
68
|
+
assert_equal 0, channel.local_id
|
69
|
+
assert_equal channel, session.channels[channel.local_id]
|
70
|
+
|
71
|
+
packet = P(:byte, CHANNEL_OPEN, :string, "session", :long, channel.local_id,
|
72
|
+
:long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size)
|
73
|
+
assert_equal packet.to_s, socket.write_buffer
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_open_channel_with_type_should_use_type
|
77
|
+
channel = session.open_channel("direct-tcpip")
|
78
|
+
assert_equal "direct-tcpip", channel.type
|
79
|
+
packet = P(:byte, CHANNEL_OPEN, :string, "direct-tcpip", :long, channel.local_id,
|
80
|
+
:long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size)
|
81
|
+
assert_equal packet.to_s, socket.write_buffer
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_open_channel_with_extras_should_append_extras_to_packet
|
85
|
+
channel = session.open_channel("direct-tcpip", :string, "other.host", :long, 1234)
|
86
|
+
packet = P(:byte, CHANNEL_OPEN, :string, "direct-tcpip", :long, channel.local_id,
|
87
|
+
:long, channel.local_maximum_window_size, :long, channel.local_maximum_packet_size,
|
88
|
+
:string, "other.host", :long, 1234)
|
89
|
+
assert_equal packet.to_s, socket.write_buffer
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_send_global_request_without_callback_should_not_expect_reply
|
93
|
+
packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, false)
|
94
|
+
session.send_global_request("testing")
|
95
|
+
assert_equal packet.to_s, socket.write_buffer
|
96
|
+
assert session.pending_requests.empty?
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_send_global_request_with_callback_should_expect_reply
|
100
|
+
packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, true)
|
101
|
+
proc = Proc.new {}
|
102
|
+
session.send_global_request("testing", &proc)
|
103
|
+
assert_equal packet.to_s, socket.write_buffer
|
104
|
+
assert_equal [proc], session.pending_requests
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_send_global_request_with_extras_should_append_extras_to_packet
|
108
|
+
packet = P(:byte, GLOBAL_REQUEST, :string, "testing", :bool, false, :string, "other.host", :long, 1234)
|
109
|
+
session.send_global_request("testing", :string, "other.host", :long, 1234)
|
110
|
+
assert_equal packet.to_s, socket.write_buffer
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_process_should_exit_immediately_if_block_is_false
|
114
|
+
session.channels[0] = stub("channel", :closing? => false)
|
115
|
+
session.channels[0].expects(:process).never
|
116
|
+
process_times(0)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_process_should_exit_after_processing_if_block_is_true_then_false
|
120
|
+
session.channels[0] = stub("channel", :closing? => false)
|
121
|
+
session.channels[0].expects(:process)
|
122
|
+
IO.expects(:select).never
|
123
|
+
process_times(2)
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_process_should_not_process_channels_that_are_closing
|
127
|
+
session.channels[0] = stub("channel", :closing? => true)
|
128
|
+
session.channels[0].expects(:process).never
|
129
|
+
IO.expects(:select).never
|
130
|
+
process_times(2)
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_global_request_packets_should_be_silently_handled_if_no_handler_exists_for_them
|
134
|
+
transport.return(GLOBAL_REQUEST, :string, "testing", :bool, false)
|
135
|
+
process_times(2)
|
136
|
+
assert transport.queue.empty?
|
137
|
+
assert !socket.pending_write?
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_global_request_packets_should_be_auto_replied_to_even_if_no_handler_exists
|
141
|
+
transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true)
|
142
|
+
process_times(2)
|
143
|
+
assert_equal P(:byte, REQUEST_FAILURE).to_s, socket.write_buffer
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_global_request_handler_should_not_trigger_auto_reply_if_no_reply_is_wanted
|
147
|
+
flag = false
|
148
|
+
session.on_global_request("testing") { flag = true }
|
149
|
+
assert !flag, "callback should not be invoked yet"
|
150
|
+
transport.return(GLOBAL_REQUEST, :string, "testing", :bool, false)
|
151
|
+
process_times(2)
|
152
|
+
assert transport.queue.empty?
|
153
|
+
assert !socket.pending_write?
|
154
|
+
assert flag, "callback should have been invoked"
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_global_request_handler_returning_true_should_trigger_success_auto_reply
|
158
|
+
flag = false
|
159
|
+
session.on_global_request("testing") { flag = true }
|
160
|
+
transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true)
|
161
|
+
process_times(2)
|
162
|
+
assert_equal P(:byte, REQUEST_SUCCESS).to_s, socket.write_buffer
|
163
|
+
assert flag
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_global_request_handler_returning_false_should_trigger_failure_auto_reply
|
167
|
+
flag = false
|
168
|
+
session.on_global_request("testing") { flag = true; false }
|
169
|
+
transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true)
|
170
|
+
process_times(2)
|
171
|
+
assert_equal P(:byte, REQUEST_FAILURE).to_s, socket.write_buffer
|
172
|
+
assert flag
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_global_request_handler_returning_sent_should_not_trigger_auto_reply
|
176
|
+
flag = false
|
177
|
+
session.on_global_request("testing") { flag = true; :sent }
|
178
|
+
transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true)
|
179
|
+
process_times(2)
|
180
|
+
assert !socket.pending_write?
|
181
|
+
assert flag
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_global_request_handler_returning_other_value_should_raise_error
|
185
|
+
session.on_global_request("testing") { "bug" }
|
186
|
+
transport.return(GLOBAL_REQUEST, :string, "testing", :bool, true)
|
187
|
+
assert_raises(RuntimeError) { process_times(2) }
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_request_success_packets_should_invoke_next_pending_request_with_true
|
191
|
+
result = nil
|
192
|
+
session.pending_requests << Proc.new { |*args| result = args }
|
193
|
+
transport.return(REQUEST_SUCCESS)
|
194
|
+
process_times(2)
|
195
|
+
assert_equal [true, P(:byte, REQUEST_SUCCESS)], result
|
196
|
+
assert session.pending_requests.empty?
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_request_failure_packets_should_invoke_next_pending_request_with_false
|
200
|
+
result = nil
|
201
|
+
session.pending_requests << Proc.new { |*args| result = args }
|
202
|
+
transport.return(REQUEST_FAILURE)
|
203
|
+
process_times(2)
|
204
|
+
assert_equal [false, P(:byte, REQUEST_FAILURE)], result
|
205
|
+
assert session.pending_requests.empty?
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_channel_open_packet_without_corresponding_channel_open_handler_should_result_in_channel_open_failure
|
209
|
+
transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20000, :long, 0x10000)
|
210
|
+
process_times(2)
|
211
|
+
assert_equal P(:byte, CHANNEL_OPEN_FAILURE, :long, 14, :long, 3, :string, "unknown channel type auth-agent", :string, "").to_s, socket.write_buffer
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_channel_open_packet_with_corresponding_handler_should_result_in_channel_open_failure_when_handler_returns_an_error
|
215
|
+
transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20000, :long, 0x10000)
|
216
|
+
session.on_open_channel "auth-agent" do |s, ch, p|
|
217
|
+
raise Net::SSH::ChannelOpenFailed.new(1234, "we iz in ur channelz!")
|
218
|
+
end
|
219
|
+
process_times(2)
|
220
|
+
assert_equal P(:byte, CHANNEL_OPEN_FAILURE, :long, 14, :long, 1234, :string, "we iz in ur channelz!", :string, "").to_s, socket.write_buffer
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_channel_open_packet_with_corresponding_handler_should_result_in_channel_open_confirmation_when_handler_succeeds
|
224
|
+
transport.return(CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20001, :long, 0x10001)
|
225
|
+
result = nil
|
226
|
+
session.on_open_channel("auth-agent") { |*args| result = args }
|
227
|
+
process_times(2)
|
228
|
+
assert_equal P(:byte, CHANNEL_OPEN_CONFIRMATION, :long, 14, :long, 0, :long, 0x20000, :long, 0x10000).to_s, socket.write_buffer
|
229
|
+
assert_not_nil(ch = session.channels[0])
|
230
|
+
assert_equal [session, ch, P(:byte, CHANNEL_OPEN, :string, "auth-agent", :long, 14, :long, 0x20001, :long, 0x10001)], result
|
231
|
+
assert_equal 0, ch.local_id
|
232
|
+
assert_equal 14, ch.remote_id
|
233
|
+
assert_equal 0x20001, ch.remote_maximum_window_size
|
234
|
+
assert_equal 0x10001, ch.remote_maximum_packet_size
|
235
|
+
assert_equal 0x20000, ch.local_maximum_window_size
|
236
|
+
assert_equal 0x10000, ch.local_maximum_packet_size
|
237
|
+
assert_equal "auth-agent", ch.type
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_channel_open_failure_should_remove_channel_and_tell_channel_that_open_failed
|
241
|
+
session.channels[1] = stub("channel")
|
242
|
+
session.channels[1].expects(:do_open_failed).with(1234, "some reason")
|
243
|
+
transport.return(CHANNEL_OPEN_FAILURE, :long, 1, :long, 1234, :string, "some reason", :string, "lang tag")
|
244
|
+
process_times(2)
|
245
|
+
assert session.channels.empty?
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_channel_open_confirmation_packet_should_be_routed_to_corresponding_channel
|
249
|
+
channel_at(14).expects(:do_open_confirmation).with(1234, 0x20001, 0x10001)
|
250
|
+
transport.return(CHANNEL_OPEN_CONFIRMATION, :long, 14, :long, 1234, :long, 0x20001, :long, 0x10001)
|
251
|
+
process_times(2)
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_channel_window_adjust_packet_should_be_routed_to_corresponding_channel
|
255
|
+
channel_at(14).expects(:do_window_adjust).with(5000)
|
256
|
+
transport.return(CHANNEL_WINDOW_ADJUST, :long, 14, :long, 5000)
|
257
|
+
process_times(2)
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_channel_request_packet_should_be_routed_to_corresponding_channel
|
261
|
+
channel_at(14).expects(:do_request).with("testing", false, Net::SSH::Buffer.new)
|
262
|
+
transport.return(CHANNEL_REQUEST, :long, 14, :string, "testing", :bool, false)
|
263
|
+
process_times(2)
|
264
|
+
end
|
265
|
+
|
266
|
+
def test_channel_data_packet_should_be_routed_to_corresponding_channel
|
267
|
+
channel_at(14).expects(:do_data).with("bring it on down")
|
268
|
+
transport.return(CHANNEL_DATA, :long, 14, :string, "bring it on down")
|
269
|
+
process_times(2)
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_channel_extended_data_packet_should_be_routed_to_corresponding_channel
|
273
|
+
channel_at(14).expects(:do_extended_data).with(1, "bring it on down")
|
274
|
+
transport.return(CHANNEL_EXTENDED_DATA, :long, 14, :long, 1, :string, "bring it on down")
|
275
|
+
process_times(2)
|
276
|
+
end
|
277
|
+
|
278
|
+
def test_channel_eof_packet_should_be_routed_to_corresponding_channel
|
279
|
+
channel_at(14).expects(:do_eof).with()
|
280
|
+
transport.return(CHANNEL_EOF, :long, 14)
|
281
|
+
process_times(2)
|
282
|
+
end
|
283
|
+
|
284
|
+
def test_channel_success_packet_should_be_routed_to_corresponding_channel
|
285
|
+
channel_at(14).expects(:do_success).with()
|
286
|
+
transport.return(CHANNEL_SUCCESS, :long, 14)
|
287
|
+
process_times(2)
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_channel_failure_packet_should_be_routed_to_corresponding_channel
|
291
|
+
channel_at(14).expects(:do_failure).with()
|
292
|
+
transport.return(CHANNEL_FAILURE, :long, 14)
|
293
|
+
process_times(2)
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_channel_close_packet_should_be_routed_to_corresponding_channel_and_channel_should_be_closed_and_removed
|
297
|
+
channel_at(14).expects(:do_close).with()
|
298
|
+
session.channels[14].expects(:close).with()
|
299
|
+
transport.return(CHANNEL_CLOSE, :long, 14)
|
300
|
+
process_times(2)
|
301
|
+
assert session.channels.empty?
|
302
|
+
end
|
303
|
+
|
304
|
+
def test_multiple_pending_dispatches_should_be_dispatched_together
|
305
|
+
channel_at(14).expects(:do_eof).with()
|
306
|
+
session.channels[14].expects(:do_success).with()
|
307
|
+
transport.return(CHANNEL_SUCCESS, :long, 14)
|
308
|
+
transport.return(CHANNEL_EOF, :long, 14)
|
309
|
+
process_times(2)
|
310
|
+
end
|
311
|
+
|
312
|
+
def test_writers_without_pending_writes_should_not_be_considered_for_select
|
313
|
+
IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]])
|
314
|
+
session.process
|
315
|
+
end
|
316
|
+
|
317
|
+
def test_writers_with_pending_writes_should_be_considered_for_select
|
318
|
+
socket.enqueue("laksdjflasdkf")
|
319
|
+
IO.expects(:select).with([socket],[socket],nil,nil).returns([[],[],[]])
|
320
|
+
session.process
|
321
|
+
end
|
322
|
+
|
323
|
+
def test_ready_readers_should_be_filled
|
324
|
+
socket.expects(:recv).returns("this is some data")
|
325
|
+
IO.expects(:select).with([socket],[],nil,nil).returns([[socket],[],[]])
|
326
|
+
session.process
|
327
|
+
assert_equal [socket], session.listeners.keys
|
328
|
+
end
|
329
|
+
|
330
|
+
def test_ready_readers_that_cant_be_filled_should_be_removed
|
331
|
+
socket.expects(:recv).returns("")
|
332
|
+
socket.expects(:close)
|
333
|
+
IO.expects(:select).with([socket],[],nil,nil).returns([[socket],[],[]])
|
334
|
+
session.process
|
335
|
+
assert session.listeners.empty?
|
336
|
+
end
|
337
|
+
|
338
|
+
def test_ready_readers_that_are_registered_with_a_block_should_call_block_instead_of_fill
|
339
|
+
io = stub("io", :pending_write? => false)
|
340
|
+
flag = false
|
341
|
+
session.stop_listening_to(socket) # so that we only have to test the presence of a single IO object
|
342
|
+
session.listen_to(io) { flag = true }
|
343
|
+
IO.expects(:select).with([io],[],nil,nil).returns([[io],[],[]])
|
344
|
+
session.process
|
345
|
+
assert flag, "callback should have been invoked"
|
346
|
+
end
|
347
|
+
|
348
|
+
def test_ready_writers_should_call_send_pending
|
349
|
+
socket.enqueue("laksdjflasdkf")
|
350
|
+
socket.expects(:send).with("laksdjflasdkf", 0).returns(13)
|
351
|
+
IO.expects(:select).with([socket],[socket],nil,nil).returns([[],[socket],[]])
|
352
|
+
session.process
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_process_should_call_rekey_as_needed
|
356
|
+
transport.expects(:rekey_as_needed)
|
357
|
+
IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]])
|
358
|
+
session.process
|
359
|
+
end
|
360
|
+
|
361
|
+
def test_loop_should_call_process_until_process_returns_false
|
362
|
+
IO.stubs(:select).with([socket],[],nil,nil).returns([[],[],[]])
|
363
|
+
session.expects(:process).with(nil).times(4).returns(true,true,true,false).yields
|
364
|
+
n = 0
|
365
|
+
session.loop { n += 1 }
|
366
|
+
assert_equal 4, n
|
367
|
+
end
|
368
|
+
|
369
|
+
def test_exec_should_open_channel_and_configure_default_callbacks
|
370
|
+
prep_exec("ls", :stdout, "data packet", :stderr, "extended data packet")
|
371
|
+
|
372
|
+
call = :first
|
373
|
+
session.exec "ls" do |channel, type, data|
|
374
|
+
if call == :first
|
375
|
+
assert_equal :stdout, type
|
376
|
+
assert_equal "data packet", data
|
377
|
+
call = :second
|
378
|
+
elsif call == :second
|
379
|
+
assert_equal :stderr, type
|
380
|
+
assert_equal "extended data packet", data
|
381
|
+
call = :third
|
382
|
+
else
|
383
|
+
flunk "should never get here, call == #{call.inspect}"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
session.loop
|
388
|
+
assert_equal :third, call
|
389
|
+
end
|
390
|
+
|
391
|
+
def test_exec_without_block_should_use_print_to_display_result
|
392
|
+
prep_exec("ls", :stdout, "data packet", :stderr, "extended data packet")
|
393
|
+
$stdout.expects(:print).with("data packet")
|
394
|
+
$stderr.expects(:print).with("extended data packet")
|
395
|
+
|
396
|
+
session.exec "ls"
|
397
|
+
session.loop
|
398
|
+
end
|
399
|
+
|
400
|
+
def test_exec_bang_should_block_until_command_finishes
|
401
|
+
prep_exec("ls", :stdout, "some data")
|
402
|
+
called = false
|
403
|
+
session.exec! "ls" do |channel, type, data|
|
404
|
+
called = true
|
405
|
+
assert_equal :stdout, type
|
406
|
+
assert_equal "some data", data
|
407
|
+
end
|
408
|
+
assert called
|
409
|
+
end
|
410
|
+
|
411
|
+
def test_exec_bang_without_block_should_return_data_as_string
|
412
|
+
prep_exec("ls", :stdout, "some data")
|
413
|
+
assert_equal "some data", session.exec!("ls")
|
414
|
+
end
|
415
|
+
|
416
|
+
private
|
417
|
+
|
418
|
+
def prep_exec(command, *data)
|
419
|
+
transport.mock_enqueue = true
|
420
|
+
transport.expect do |t, p|
|
421
|
+
assert_equal CHANNEL_OPEN, p.type
|
422
|
+
t.return(CHANNEL_OPEN_CONFIRMATION, :long, p[:remote_id], :long, 0, :long, 0x20000, :long, 0x10000)
|
423
|
+
t.expect do |t, p2|
|
424
|
+
assert_equal CHANNEL_REQUEST, p2.type
|
425
|
+
assert_equal "exec", p2[:request]
|
426
|
+
assert_equal true, p2[:want_reply]
|
427
|
+
assert_equal "ls", p2.read_string
|
428
|
+
|
429
|
+
t.return(CHANNEL_SUCCESS, :long, p[:remote_id])
|
430
|
+
|
431
|
+
0.step(data.length-1, 2) do |index|
|
432
|
+
type = data[index]
|
433
|
+
datum = data[index+1]
|
434
|
+
|
435
|
+
if type == :stdout
|
436
|
+
t.return(CHANNEL_DATA, :long, p[:remote_id], :string, datum)
|
437
|
+
else
|
438
|
+
t.return(CHANNEL_EXTENDED_DATA, :long, p[:remote_id], :long, 1, :string, datum)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
t.return(CHANNEL_CLOSE, :long, p[:remote_id])
|
443
|
+
t.expect { |t,p| assert_equal CHANNEL_CLOSE, p.type }
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
module MockSocket
|
449
|
+
# so that we can easily test the contents that were enqueued, without
|
450
|
+
# worrying about all the packet stream overhead
|
451
|
+
def enqueue_packet(message)
|
452
|
+
enqueue(message.to_s)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
def socket
|
457
|
+
@socket ||= begin
|
458
|
+
socket ||= Object.new
|
459
|
+
socket.extend(Net::SSH::Transport::PacketStream)
|
460
|
+
socket.extend(MockSocket)
|
461
|
+
socket
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
def channel_at(local_id)
|
466
|
+
session.channels[local_id] = stub("channel", :process => true, :closing? => false)
|
467
|
+
end
|
468
|
+
|
469
|
+
def transport(options={})
|
470
|
+
@transport ||= MockTransport.new(options.merge(:socket => socket))
|
471
|
+
end
|
472
|
+
|
473
|
+
def session(options={})
|
474
|
+
@session ||= Net::SSH::Connection::Session.new(transport, options)
|
475
|
+
end
|
476
|
+
|
477
|
+
def process_times(n)
|
478
|
+
i = 0
|
479
|
+
session.process { (i += 1) < n }
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
end
|