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.
Files changed (103) hide show
  1. data/CHANGELOG.rdoc +42 -0
  2. data/Manifest +101 -0
  3. data/README.rdoc +110 -0
  4. data/Rakefile +26 -0
  5. data/THANKS.rdoc +16 -0
  6. data/lib/net/ssh.rb +199 -0
  7. data/lib/net/ssh/authentication/agent.rb +175 -0
  8. data/lib/net/ssh/authentication/constants.rb +18 -0
  9. data/lib/net/ssh/authentication/key_manager.rb +169 -0
  10. data/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  11. data/lib/net/ssh/authentication/methods/hostbased.rb +71 -0
  12. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +66 -0
  13. data/lib/net/ssh/authentication/methods/password.rb +39 -0
  14. data/lib/net/ssh/authentication/methods/publickey.rb +92 -0
  15. data/lib/net/ssh/authentication/pageant.rb +176 -0
  16. data/lib/net/ssh/authentication/session.rb +127 -0
  17. data/lib/net/ssh/buffer.rb +339 -0
  18. data/lib/net/ssh/buffered_io.rb +149 -0
  19. data/lib/net/ssh/config.rb +173 -0
  20. data/lib/net/ssh/connection/channel.rb +625 -0
  21. data/lib/net/ssh/connection/constants.rb +33 -0
  22. data/lib/net/ssh/connection/session.rb +569 -0
  23. data/lib/net/ssh/connection/term.rb +178 -0
  24. data/lib/net/ssh/errors.rb +85 -0
  25. data/lib/net/ssh/key_factory.rb +85 -0
  26. data/lib/net/ssh/known_hosts.rb +129 -0
  27. data/lib/net/ssh/loggable.rb +61 -0
  28. data/lib/net/ssh/packet.rb +102 -0
  29. data/lib/net/ssh/prompt.rb +93 -0
  30. data/lib/net/ssh/proxy/errors.rb +14 -0
  31. data/lib/net/ssh/proxy/http.rb +94 -0
  32. data/lib/net/ssh/proxy/socks4.rb +70 -0
  33. data/lib/net/ssh/proxy/socks5.rb +128 -0
  34. data/lib/net/ssh/service/forward.rb +267 -0
  35. data/lib/net/ssh/test.rb +89 -0
  36. data/lib/net/ssh/test/channel.rb +129 -0
  37. data/lib/net/ssh/test/extensions.rb +152 -0
  38. data/lib/net/ssh/test/kex.rb +44 -0
  39. data/lib/net/ssh/test/local_packet.rb +51 -0
  40. data/lib/net/ssh/test/packet.rb +81 -0
  41. data/lib/net/ssh/test/remote_packet.rb +38 -0
  42. data/lib/net/ssh/test/script.rb +157 -0
  43. data/lib/net/ssh/test/socket.rb +59 -0
  44. data/lib/net/ssh/transport/algorithms.rb +384 -0
  45. data/lib/net/ssh/transport/cipher_factory.rb +72 -0
  46. data/lib/net/ssh/transport/constants.rb +30 -0
  47. data/lib/net/ssh/transport/hmac.rb +31 -0
  48. data/lib/net/ssh/transport/hmac/abstract.rb +48 -0
  49. data/lib/net/ssh/transport/hmac/md5.rb +12 -0
  50. data/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  51. data/lib/net/ssh/transport/hmac/none.rb +15 -0
  52. data/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  53. data/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  54. data/lib/net/ssh/transport/identity_cipher.rb +40 -0
  55. data/lib/net/ssh/transport/kex.rb +13 -0
  56. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +208 -0
  57. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +77 -0
  58. data/lib/net/ssh/transport/openssl.rb +128 -0
  59. data/lib/net/ssh/transport/packet_stream.rb +230 -0
  60. data/lib/net/ssh/transport/server_version.rb +61 -0
  61. data/lib/net/ssh/transport/session.rb +262 -0
  62. data/lib/net/ssh/transport/state.rb +170 -0
  63. data/lib/net/ssh/verifiers/lenient.rb +30 -0
  64. data/lib/net/ssh/verifiers/null.rb +12 -0
  65. data/lib/net/ssh/verifiers/strict.rb +53 -0
  66. data/lib/net/ssh/version.rb +60 -0
  67. data/net-ssh.gemspec +56 -0
  68. data/setup.rb +1585 -0
  69. data/test/authentication/methods/common.rb +28 -0
  70. data/test/authentication/methods/test_abstract.rb +51 -0
  71. data/test/authentication/methods/test_hostbased.rb +108 -0
  72. data/test/authentication/methods/test_keyboard_interactive.rb +98 -0
  73. data/test/authentication/methods/test_password.rb +50 -0
  74. data/test/authentication/methods/test_publickey.rb +123 -0
  75. data/test/authentication/test_agent.rb +205 -0
  76. data/test/authentication/test_key_manager.rb +100 -0
  77. data/test/authentication/test_session.rb +93 -0
  78. data/test/common.rb +106 -0
  79. data/test/configs/exact_match +8 -0
  80. data/test/configs/wild_cards +14 -0
  81. data/test/connection/test_channel.rb +452 -0
  82. data/test/connection/test_session.rb +483 -0
  83. data/test/test_all.rb +6 -0
  84. data/test/test_buffer.rb +336 -0
  85. data/test/test_buffered_io.rb +63 -0
  86. data/test/test_config.rb +78 -0
  87. data/test/test_key_factory.rb +67 -0
  88. data/test/transport/hmac/test_md5.rb +34 -0
  89. data/test/transport/hmac/test_md5_96.rb +25 -0
  90. data/test/transport/hmac/test_none.rb +34 -0
  91. data/test/transport/hmac/test_sha1.rb +34 -0
  92. data/test/transport/hmac/test_sha1_96.rb +25 -0
  93. data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  94. data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  95. data/test/transport/test_algorithms.rb +302 -0
  96. data/test/transport/test_cipher_factory.rb +163 -0
  97. data/test/transport/test_hmac.rb +34 -0
  98. data/test/transport/test_identity_cipher.rb +40 -0
  99. data/test/transport/test_packet_stream.rb +433 -0
  100. data/test/transport/test_server_version.rb +55 -0
  101. data/test/transport/test_session.rb +312 -0
  102. data/test/transport/test_state.rb +173 -0
  103. metadata +222 -0
@@ -0,0 +1,8 @@
1
+ Host other.host
2
+ Compression no
3
+ Port 1231
4
+
5
+ Host test.host
6
+ Compression yes
7
+ ForwardAgent yes
8
+ Port 1234
@@ -0,0 +1,14 @@
1
+ Host test.*
2
+ Port 1234
3
+ Compression no
4
+
5
+ Host tes?.host
6
+ Port 4321
7
+ ForwardAgent yes
8
+
9
+ Host *.hos?
10
+ IdentityFile ~/.ssh/id_dsa
11
+ Compression yes
12
+
13
+ Host k*.host
14
+ RekeyLimit 1G
@@ -0,0 +1,452 @@
1
+ require 'common'
2
+ require 'net/ssh/connection/channel'
3
+
4
+ module Connection
5
+
6
+ class TestChannel < Test::Unit::TestCase
7
+ include Net::SSH::Connection::Constants
8
+
9
+ def teardown
10
+ connection.test!
11
+ end
12
+
13
+ def test_constructor_should_set_defaults
14
+ assert_equal 0x10000, channel.local_maximum_packet_size
15
+ assert_equal 0x20000, channel.local_maximum_window_size
16
+ assert channel.pending_requests.empty?
17
+ end
18
+
19
+ def test_channel_properties
20
+ channel[:hello] = "some value"
21
+ assert_equal "some value", channel[:hello]
22
+ end
23
+
24
+ def test_exec_should_be_syntactic_sugar_for_a_channel_request
25
+ channel.expects(:send_channel_request).with("exec", :string, "ls").yields
26
+ found_block = false
27
+ channel.exec("ls") { found_block = true }
28
+ assert found_block, "expected block to be passed to send_channel_request"
29
+ end
30
+
31
+ def test_subsystem_should_be_syntactic_sugar_for_a_channel_request
32
+ channel.expects(:send_channel_request).with("subsystem", :string, "sftp").yields
33
+ found_block = false
34
+ channel.subsystem("sftp") { found_block = true }
35
+ assert found_block, "expected block to be passed to send_channel_request"
36
+ end
37
+
38
+ def test_request_pty_with_invalid_option_should_raise_error
39
+ assert_raises(ArgumentError) do
40
+ channel.request_pty(:bogus => "thing")
41
+ end
42
+ end
43
+
44
+ def test_request_pty_without_options_should_use_defaults
45
+ channel.expects(:send_channel_request).with("pty-req", :string, "xterm",
46
+ :long, 80, :long, 24, :long, 640, :long, 480, :string, "\0").yields
47
+ found_block = false
48
+ channel.request_pty { found_block = true }
49
+ assert found_block, "expected block to be passed to send_channel_request"
50
+ end
51
+
52
+ def test_request_pty_with_options_should_honor_options
53
+ channel.expects(:send_channel_request).with("pty-req", :string, "vanilla",
54
+ :long, 60, :long, 15, :long, 400, :long, 200, :string, "\5\0\0\0\1\0")
55
+ channel.request_pty :term => "vanilla", :chars_wide => 60, :chars_high => 15,
56
+ :pixels_wide => 400, :pixels_high => 200, :modes => { 5 => 1 }
57
+ end
58
+
59
+ def test_send_data_should_append_to_channels_output_buffer
60
+ channel.send_data("hello")
61
+ assert_equal "hello", channel.output.to_s
62
+ channel.send_data("world")
63
+ assert_equal "helloworld", channel.output.to_s
64
+ end
65
+
66
+ def test_close_before_channel_has_been_confirmed_should_do_nothing
67
+ assert !channel.closing?
68
+ channel.close
69
+ assert !channel.closing?
70
+ end
71
+
72
+ def test_close_should_set_closing_and_send_message
73
+ channel.do_open_confirmation(0, 100, 100)
74
+ assert !channel.closing?
75
+
76
+ connection.expect { |t,packet| assert_equal CHANNEL_CLOSE, packet.type }
77
+ channel.close
78
+
79
+ assert channel.closing?
80
+ end
81
+
82
+ def test_close_while_closing_should_do_nothing
83
+ test_close_should_set_closing_and_send_message
84
+ assert_nothing_raised { channel.close }
85
+ end
86
+
87
+ def test_process_when_process_callback_is_not_set_should_just_enqueue_data
88
+ channel.expects(:enqueue_pending_output)
89
+ channel.process
90
+ end
91
+
92
+ def test_process_when_process_callback_is_set_should_yield_self_before_enqueuing_data
93
+ channel.expects(:enqueue_pending_output).never
94
+ channel.on_process { |ch| ch.expects(:enqueue_pending_output).once }
95
+ channel.process
96
+ end
97
+
98
+ def test_enqueue_pending_output_should_have_no_effect_if_channel_has_not_been_confirmed
99
+ channel.send_data("hello")
100
+ assert_nothing_raised { channel.enqueue_pending_output }
101
+ end
102
+
103
+ def test_enqueue_pending_output_should_have_no_effect_if_there_is_no_output
104
+ channel.do_open_confirmation(0, 100, 100)
105
+ assert_nothing_raised { channel.enqueue_pending_output }
106
+ end
107
+
108
+ def test_enqueue_pending_output_should_not_enqueue_more_than_output_length
109
+ channel.do_open_confirmation(0, 100, 100)
110
+ channel.send_data("hello world")
111
+
112
+ connection.expect do |t,packet|
113
+ assert_equal CHANNEL_DATA, packet.type
114
+ assert_equal 0, packet[:local_id]
115
+ assert_equal 11, packet[:data].length
116
+ end
117
+
118
+ channel.enqueue_pending_output
119
+ end
120
+
121
+ def test_enqueue_pending_output_should_not_enqueue_more_than_max_packet_length_at_once
122
+ channel.do_open_confirmation(0, 100, 8)
123
+ channel.send_data("hello world")
124
+
125
+ connection.expect do |t,packet|
126
+ assert_equal CHANNEL_DATA, packet.type
127
+ assert_equal 0, packet[:local_id]
128
+ assert_equal "hello wo", packet[:data]
129
+
130
+ t.expect do |t,packet|
131
+ assert_equal CHANNEL_DATA, packet.type
132
+ assert_equal 0, packet[:local_id]
133
+ assert_equal "rld", packet[:data]
134
+ end
135
+ end
136
+
137
+ channel.enqueue_pending_output
138
+ end
139
+
140
+ def test_enqueue_pending_output_should_not_enqueue_more_than_max_window_size
141
+ channel.do_open_confirmation(0, 8, 100)
142
+ channel.send_data("hello world")
143
+
144
+ connection.expect do |t,packet|
145
+ assert_equal CHANNEL_DATA, packet.type
146
+ assert_equal 0, packet[:local_id]
147
+ assert_equal "hello wo", packet[:data]
148
+ end
149
+
150
+ channel.enqueue_pending_output
151
+ end
152
+
153
+ def test_on_data_with_block_should_set_callback
154
+ flag = false
155
+ channel.on_data { flag = !flag }
156
+ channel.do_data("")
157
+ assert(flag, "callback should have been invoked")
158
+ channel.on_data
159
+ channel.do_data("")
160
+ assert(flag, "callback should have been removed")
161
+ end
162
+
163
+ def test_on_extended_data_with_block_should_set_callback
164
+ flag = false
165
+ channel.on_extended_data { flag = !flag }
166
+ channel.do_extended_data(0, "")
167
+ assert(flag, "callback should have been invoked")
168
+ channel.on_extended_data
169
+ channel.do_extended_data(0, "")
170
+ assert(flag, "callback should have been removed")
171
+ end
172
+
173
+ def test_on_process_with_block_should_set_callback
174
+ flag = false
175
+ channel.on_process { flag = !flag }
176
+ channel.process
177
+ assert(flag, "callback should have been invoked")
178
+ channel.on_process
179
+ channel.process
180
+ assert(flag, "callback should have been removed")
181
+ end
182
+
183
+ def test_on_close_with_block_should_set_callback
184
+ flag = false
185
+ channel.on_close { flag = !flag }
186
+ channel.do_close
187
+ assert(flag, "callback should have been invoked")
188
+ channel.on_close
189
+ channel.do_close
190
+ assert(flag, "callback should have been removed")
191
+ end
192
+
193
+ def test_on_eof_with_block_should_set_callback
194
+ flag = false
195
+ channel.on_eof { flag = !flag }
196
+ channel.do_eof
197
+ assert(flag, "callback should have been invoked")
198
+ channel.on_eof
199
+ channel.do_eof
200
+ assert(flag, "callback should have been removed")
201
+ end
202
+
203
+ def test_do_request_for_unhandled_request_should_do_nothing_if_not_wants_reply
204
+ channel.do_open_confirmation(0, 100, 100)
205
+ assert_nothing_raised { channel.do_request "exit-status", false, nil }
206
+ end
207
+
208
+ def test_do_request_for_unhandled_request_should_send_CHANNEL_FAILURE_if_wants_reply
209
+ channel.do_open_confirmation(0, 100, 100)
210
+ connection.expect { |t,packet| assert_equal CHANNEL_FAILURE, packet.type }
211
+ channel.do_request "keepalive@openssh.com", true, nil
212
+ end
213
+
214
+ def test_do_request_for_handled_request_should_invoke_callback_and_do_nothing_if_returns_true_and_not_wants_reply
215
+ channel.do_open_confirmation(0, 100, 100)
216
+ flag = false
217
+ channel.on_request("exit-status") { flag = true; true }
218
+ assert_nothing_raised { channel.do_request "exit-status", false, nil }
219
+ assert flag, "callback should have been invoked"
220
+ end
221
+
222
+ def test_do_request_for_handled_request_should_invoke_callback_and_do_nothing_if_fails_and_not_wants_reply
223
+ channel.do_open_confirmation(0, 100, 100)
224
+ flag = false
225
+ channel.on_request("exit-status") { flag = true; raise Net::SSH::ChannelRequestFailed }
226
+ assert_nothing_raised { channel.do_request "exit-status", false, nil }
227
+ assert flag, "callback should have been invoked"
228
+ end
229
+
230
+ def test_do_request_for_handled_request_should_invoke_callback_and_send_CHANNEL_SUCCESS_if_returns_true_and_wants_reply
231
+ channel.do_open_confirmation(0, 100, 100)
232
+ flag = false
233
+ channel.on_request("exit-status") { flag = true; true }
234
+ connection.expect { |t,p| assert_equal CHANNEL_SUCCESS, p.type }
235
+ assert_nothing_raised { channel.do_request "exit-status", true, nil }
236
+ assert flag, "callback should have been invoked"
237
+ end
238
+
239
+ def test_do_request_for_handled_request_should_invoke_callback_and_send_CHANNEL_FAILURE_if_returns_false_and_wants_reply
240
+ channel.do_open_confirmation(0, 100, 100)
241
+ flag = false
242
+ channel.on_request("exit-status") { flag = true; raise Net::SSH::ChannelRequestFailed }
243
+ connection.expect { |t,p| assert_equal CHANNEL_FAILURE, p.type }
244
+ assert_nothing_raised { channel.do_request "exit-status", true, nil }
245
+ assert flag, "callback should have been invoked"
246
+ end
247
+
248
+ def test_send_channel_request_without_callback_should_not_want_reply
249
+ channel.do_open_confirmation(0, 100, 100)
250
+ connection.expect do |t,p|
251
+ assert_equal CHANNEL_REQUEST, p.type
252
+ assert_equal 0, p[:local_id]
253
+ assert_equal "exec", p[:request]
254
+ assert_equal false, p[:want_reply]
255
+ assert_equal "ls", p[:request_data].read_string
256
+ end
257
+ channel.send_channel_request("exec", :string, "ls")
258
+ assert channel.pending_requests.empty?
259
+ end
260
+
261
+ def test_send_channel_request_with_callback_should_want_reply
262
+ channel.do_open_confirmation(0, 100, 100)
263
+ connection.expect do |t,p|
264
+ assert_equal CHANNEL_REQUEST, p.type
265
+ assert_equal 0, p[:local_id]
266
+ assert_equal "exec", p[:request]
267
+ assert_equal true, p[:want_reply]
268
+ assert_equal "ls", p[:request_data].read_string
269
+ end
270
+ callback = Proc.new {}
271
+ channel.send_channel_request("exec", :string, "ls", &callback)
272
+ assert_equal [callback], channel.pending_requests
273
+ end
274
+
275
+ def test_do_open_confirmation_should_set_remote_parameters
276
+ channel.do_open_confirmation(1, 2, 3)
277
+ assert_equal 1, channel.remote_id
278
+ assert_equal 2, channel.remote_window_size
279
+ assert_equal 2, channel.remote_maximum_window_size
280
+ assert_equal 3, channel.remote_maximum_packet_size
281
+ end
282
+
283
+ def test_do_open_confirmation_should_call_open_confirmation_callback
284
+ flag = false
285
+ channel { flag = true }
286
+ assert !flag, "callback should not have been invoked yet"
287
+ channel.do_open_confirmation(1,2,3)
288
+ assert flag, "callback should have been invoked"
289
+ end
290
+
291
+ def test_do_open_confirmation_with_session_channel_should_invoke_agent_forwarding_if_agent_forwarding_requested
292
+ connection :forward_agent => true
293
+ forward = mock("forward")
294
+ forward.expects(:agent).with(channel)
295
+ connection.expects(:forward).returns(forward)
296
+ channel.do_open_confirmation(1,2,3)
297
+ end
298
+
299
+ def test_do_open_confirmation_with_non_session_channel_should_not_invoke_agent_forwarding_even_if_agent_forwarding_requested
300
+ connection :forward_agent => true
301
+ channel :type => "direct-tcpip"
302
+ connection.expects(:forward).never
303
+ channel.do_open_confirmation(1,2,3)
304
+ end
305
+
306
+ def test_do_window_adjust_should_adjust_remote_window_size_by_the_given_amount
307
+ channel.do_open_confirmation(0, 1000, 1000)
308
+ assert_equal 1000, channel.remote_window_size
309
+ assert_equal 1000, channel.remote_maximum_window_size
310
+ channel.do_window_adjust(500)
311
+ assert_equal 1500, channel.remote_window_size
312
+ assert_equal 1500, channel.remote_maximum_window_size
313
+ end
314
+
315
+ def test_do_data_should_update_local_window_size
316
+ assert_equal 0x20000, channel.local_maximum_window_size
317
+ assert_equal 0x20000, channel.local_window_size
318
+ channel.do_data("here is some data")
319
+ assert_equal 0x20000, channel.local_maximum_window_size
320
+ assert_equal 0x1FFEF, channel.local_window_size
321
+ end
322
+
323
+ def test_do_extended_data_should_update_local_window_size
324
+ assert_equal 0x20000, channel.local_maximum_window_size
325
+ assert_equal 0x20000, channel.local_window_size
326
+ channel.do_extended_data(1, "here is some data")
327
+ assert_equal 0x20000, channel.local_maximum_window_size
328
+ assert_equal 0x1FFEF, channel.local_window_size
329
+ end
330
+
331
+ def test_do_data_when_local_window_size_drops_below_threshold_should_trigger_WINDOW_ADJUST_message
332
+ channel.do_open_confirmation(0, 1000, 1000)
333
+ assert_equal 0x20000, channel.local_maximum_window_size
334
+ assert_equal 0x20000, channel.local_window_size
335
+
336
+ connection.expect do |t,p|
337
+ assert_equal CHANNEL_WINDOW_ADJUST, p.type
338
+ assert_equal 0, p[:local_id]
339
+ assert_equal 0x20000, p[:extra_bytes]
340
+ end
341
+
342
+ channel.do_data("." * 0x10001)
343
+ assert_equal 0x40000, channel.local_maximum_window_size
344
+ assert_equal 0x2FFFF, channel.local_window_size
345
+ end
346
+
347
+ def test_do_failure_should_grab_next_pending_request_and_call_it
348
+ result = nil
349
+ channel.pending_requests << Proc.new { |*args| result = args }
350
+ channel.do_failure
351
+ assert_equal [channel, false], result
352
+ assert channel.pending_requests.empty?
353
+ end
354
+
355
+ def test_do_success_should_grab_next_pending_request_and_call_it
356
+ result = nil
357
+ channel.pending_requests << Proc.new { |*args| result = args }
358
+ channel.do_success
359
+ assert_equal [channel, true], result
360
+ assert channel.pending_requests.empty?
361
+ end
362
+
363
+ def test_active_should_be_true_when_channel_appears_in_channel_list
364
+ connection.channels[channel.local_id] = channel
365
+ assert channel.active?
366
+ end
367
+
368
+ def test_active_should_be_false_when_channel_is_not_in_channel_list
369
+ assert !channel.active?
370
+ end
371
+
372
+ def test_wait_should_block_while_channel_is_active?
373
+ channel.expects(:active?).times(3).returns(true,true,false)
374
+ channel.wait
375
+ end
376
+
377
+ def test_eof_bang_should_send_eof_to_server
378
+ channel.do_open_confirmation(0, 1000, 1000)
379
+ connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type }
380
+ channel.eof!
381
+ end
382
+
383
+ def test_eof_bang_should_not_send_eof_if_eof_was_already_declared
384
+ channel.do_open_confirmation(0, 1000, 1000)
385
+ connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type }
386
+ channel.eof!
387
+ assert_nothing_raised { channel.eof! }
388
+ end
389
+
390
+ def test_eof_q_should_return_true_if_eof_declared
391
+ channel.do_open_confirmation(0, 1000, 1000)
392
+ connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type }
393
+
394
+ assert !channel.eof?
395
+ channel.eof!
396
+ assert channel.eof?
397
+ end
398
+
399
+ def test_send_data_should_raise_exception_if_eof_declared
400
+ channel.do_open_confirmation(0, 1000, 1000)
401
+ connection.expect { |t,p| assert_equal CHANNEL_EOF, p.type }
402
+ channel.eof!
403
+ assert_raises(EOFError) { channel.send_data("die! die! die!") }
404
+ end
405
+
406
+ private
407
+
408
+ class MockConnection
409
+ attr_reader :logger
410
+ attr_reader :options
411
+ attr_reader :channels
412
+
413
+ def initialize(options={})
414
+ @expectation = nil
415
+ @options = options
416
+ @channels = {}
417
+ end
418
+
419
+ def expect(&block)
420
+ @expectation = block
421
+ end
422
+
423
+ def send_message(msg)
424
+ raise "#{msg.to_s.inspect} recieved but no message was expected" unless @expectation
425
+ packet = Net::SSH::Packet.new(msg.to_s)
426
+ callback, @expectation = @expectation, nil
427
+ callback.call(self, packet)
428
+ end
429
+
430
+ alias loop_forever loop
431
+ def loop(&block)
432
+ loop_forever { break unless block.call }
433
+ end
434
+
435
+ def test!
436
+ raise "expected a packet but none were sent" if @expectation
437
+ end
438
+ end
439
+
440
+ def connection(options={})
441
+ @connection ||= MockConnection.new(options)
442
+ end
443
+
444
+ def channel(options={}, &block)
445
+ @channel ||= Net::SSH::Connection::Channel.new(connection(options),
446
+ options[:type] || "session",
447
+ options[:local_id] || 0,
448
+ &block)
449
+ end
450
+ end
451
+
452
+ end