net-scp 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/test/common.rb ADDED
@@ -0,0 +1,138 @@
1
+ require 'test/unit'
2
+ require 'mocha'
3
+
4
+ begin
5
+ gem 'net-ssh', ">= 2.0.0"
6
+ require 'net/ssh'
7
+ rescue LoadError
8
+ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../net-ssh/lib"
9
+
10
+ begin
11
+ require 'net/ssh'
12
+ require 'net/ssh/version'
13
+ raise LoadError, "wrong version" unless Net::SSH::Version::STRING >= '1.99.0'
14
+ rescue LoadError => e
15
+ abort "could not load net/ssh v2 (#{e.inspect})"
16
+ end
17
+ end
18
+
19
+ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
20
+
21
+ require 'net/scp'
22
+ require 'net/ssh/test'
23
+
24
+ class Net::SSH::Test::Channel
25
+ def gets_ok
26
+ gets_data "\0"
27
+ end
28
+
29
+ def sends_ok
30
+ sends_data "\0"
31
+ end
32
+ end
33
+
34
+ class Net::SCP::TestCase < Test::Unit::TestCase
35
+ include Net::SSH::Test
36
+
37
+ def default_test
38
+ # do nothing, this is just a hacky-hack to work around Test::Unit's
39
+ # insistence that all TestCase subclasses have at least one test
40
+ # method defined.
41
+ end
42
+
43
+ protected
44
+
45
+ def prepare_file(path, contents="", mode=0666, mtime=Time.now, atime=Time.now)
46
+ entry = FileEntry.new(path, contents, mode, mtime, atime)
47
+ entry.stub!
48
+ entry
49
+ end
50
+
51
+ def prepare_directory(path, mode=0777, mtime=Time.now, atime=Time.now)
52
+ directory = DirectoryEntry.new(path, mode, mtime, atime)
53
+ yield directory if block_given?
54
+ directory.stub!
55
+ end
56
+
57
+ class FileEntry
58
+ attr_reader :path, :contents, :mode, :mtime, :atime, :io
59
+
60
+ def initialize(path, contents, mode=0666, mtime=Time.now, atime=Time.now)
61
+ @path, @contents, @mode = path, contents, mode
62
+ @mtime, @atime = mtime, atime
63
+ end
64
+
65
+ def name
66
+ @name ||= File.basename(path)
67
+ end
68
+
69
+ def stub!
70
+ stat = Mocha::Mock.new("file::stat")
71
+ stat.stubs(:size => contents.length, :mode => mode, :mtime => mtime, :atime => atime, :directory? => false)
72
+
73
+ File.stubs(:stat).with(path).returns(stat)
74
+ File.stubs(:directory?).with(path).returns(false)
75
+ File.stubs(:file?).with(path).returns(true)
76
+ File.stubs(:open).with(path, "rb").returns(StringIO.new(contents))
77
+
78
+ @io = StringIO.new
79
+ File.stubs(:new).with(path, File::CREAT|File::TRUNC|File::RDWR, mode).returns(io)
80
+ end
81
+ end
82
+
83
+ class DirectoryEntry
84
+ attr_reader :path, :mode, :mtime, :atime
85
+ attr_reader :entries
86
+
87
+ def initialize(path, mode=0777, mtime=Time.now, atime=Time.now)
88
+ @path, @mode = path, mode
89
+ @mtime, @atime = mtime, atime
90
+ @entries = []
91
+ end
92
+
93
+ def name
94
+ @name ||= File.basename(path)
95
+ end
96
+
97
+ def file(name, *args)
98
+ (entries << FileEntry.new(File.join(path, name), *args)).last
99
+ end
100
+
101
+ def directory(name, *args)
102
+ entry = DirectoryEntry.new(File.join(path, name), *args)
103
+ yield entry if block_given?
104
+ (entries << entry).last
105
+ end
106
+
107
+ def stub!
108
+ Dir.stubs(:mkdir).with { |*a| a.first == path }
109
+
110
+ stat = Mocha::Mock.new("file::stat")
111
+ stat.stubs(:size => 1024, :mode => mode, :mtime => mtime, :atime => atime, :directory? => true)
112
+
113
+ File.stubs(:stat).with(path).returns(stat)
114
+ File.stubs(:directory?).with(path).returns(true)
115
+ File.stubs(:file?).with(path).returns(false)
116
+ Dir.stubs(:entries).with(path).returns(%w(. ..) + entries.map { |e| e.name }.sort)
117
+
118
+ entries.each { |e| e.stub! }
119
+ end
120
+ end
121
+
122
+ def expect_scp_session(arguments)
123
+ story do |session|
124
+ channel = session.opens_channel
125
+ channel.sends_exec "scp #{arguments}"
126
+ yield channel if block_given?
127
+ channel.sends_eof
128
+ channel.gets_exit_status
129
+ channel.gets_eof
130
+ channel.gets_close
131
+ channel.sends_close
132
+ end
133
+ end
134
+
135
+ def scp(options={})
136
+ @scp ||= Net::SCP.new(connection(options))
137
+ end
138
+ end
data/test/test_all.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir.chdir(File.dirname(__FILE__)) do
2
+ Dir['**/test_*.rb'].each { |file| require(file) }
3
+ end
@@ -0,0 +1,142 @@
1
+ require 'common'
2
+
3
+ class TestDownload < Net::SCP::TestCase
4
+ def test_download_file_should_transfer_file
5
+ file = prepare_file("/path/to/local.txt", "a" * 1234)
6
+
7
+ expect_scp_session "-f /path/to/remote.txt" do |channel|
8
+ simple_download(channel)
9
+ end
10
+
11
+ assert_scripted { scp.download!("/path/to/remote.txt", "/path/to/local.txt") }
12
+ assert_equal "a" * 1234, file.io.string
13
+ end
14
+
15
+ def test_download_with_preserve_should_send_times
16
+ file = prepare_file("/path/to/local.txt", "a" * 1234, 0644, Time.at(1234567890, 123456), Time.at(12121212, 232323))
17
+
18
+ expect_scp_session "-f -p /path/to/remote.txt" do |channel|
19
+ channel.sends_ok
20
+ channel.gets_data "T1234567890 123456 12121212 232323\n"
21
+ simple_download(channel, 0644)
22
+ end
23
+
24
+ File.expects(:utime).with(Time.at(12121212, 232323), Time.at(1234567890, 123456), "/path/to/local.txt")
25
+ assert_scripted { scp.download!("/path/to/remote.txt", "/path/to/local.txt", :preserve => true) }
26
+ assert_equal "a" * 1234, file.io.string
27
+ end
28
+
29
+ def test_download_with_progress_callback_should_invoke_callback
30
+ prepare_file("/path/to/local.txt", "a" * 3000 + "b" * 3000 + "c" * 3000 + "d" * 3000)
31
+
32
+ expect_scp_session "-f /path/to/remote.txt" do |channel|
33
+ channel.sends_ok
34
+ channel.gets_data "C0666 12000 remote.txt\n"
35
+ channel.sends_ok
36
+ channel.gets_data "a" * 3000
37
+ channel.inject_remote_delay!
38
+ channel.gets_data "b" * 3000
39
+ channel.inject_remote_delay!
40
+ channel.gets_data "c" * 3000
41
+ channel.inject_remote_delay!
42
+ channel.gets_data "d" * 3000
43
+ channel.gets_ok
44
+ channel.sends_ok
45
+ end
46
+
47
+ calls = []
48
+ progress = Proc.new { |ch, *args| calls << args }
49
+
50
+ assert_scripted do
51
+ scp.download!("/path/to/remote.txt", "/path/to/local.txt", &progress)
52
+ end
53
+
54
+ assert_equal ["/path/to/local.txt", 0, 12000], calls.shift
55
+ assert_equal ["/path/to/local.txt", 3000, 12000], calls.shift
56
+ assert_equal ["/path/to/local.txt", 6000, 12000], calls.shift
57
+ assert_equal ["/path/to/local.txt", 9000, 12000], calls.shift
58
+ assert_equal ["/path/to/local.txt", 12000, 12000], calls.shift
59
+ assert calls.empty?
60
+ end
61
+
62
+ def test_download_io_with_recursive_should_raise_error
63
+ expect_scp_session "-f -r /path/to/remote.txt"
64
+ assert_raises(Net::SCP::Error) { scp.download!("/path/to/remote.txt", StringIO.new, :recursive => true) }
65
+ end
66
+
67
+ def test_download_io_with_preserve_should_ignore_preserve
68
+ expect_scp_session "-f -p /path/to/remote.txt" do |channel|
69
+ simple_download(channel)
70
+ end
71
+
72
+ io = StringIO.new
73
+ assert_scripted { scp.download!("/path/to/remote.txt", io, :preserve => true) }
74
+ assert_equal "a" * 1234, io.string
75
+ end
76
+
77
+ def test_download_io_should_transfer_data
78
+ expect_scp_session "-f /path/to/remote.txt" do |channel|
79
+ simple_download(channel)
80
+ end
81
+
82
+ io = StringIO.new
83
+ assert_scripted { scp.download!("/path/to/remote.txt", io) }
84
+ assert_equal "a" * 1234, io.string
85
+ end
86
+
87
+ def test_download_bang_without_target_should_return_string
88
+ expect_scp_session "-f /path/to/remote.txt" do |channel|
89
+ simple_download(channel)
90
+ end
91
+
92
+ assert_scripted do
93
+ assert_equal "a" * 1234, scp.download!("/path/to/remote.txt")
94
+ end
95
+ end
96
+
97
+ def test_download_directory_without_recursive_should_raise_error
98
+ expect_scp_session "-f /path/to/remote" do |channel|
99
+ channel.sends_ok
100
+ channel.gets_data "D0755 0 remote\n"
101
+ end
102
+
103
+ assert_raises(Net::SCP::Error) { scp.download!("/path/to/remote") }
104
+ end
105
+
106
+ def test_download_directory_should_create_directory_and_files_locally
107
+ file = nil
108
+ prepare_directory "/path/to/local" do |dir|
109
+ dir.directory "remote" do |dir2|
110
+ dir2.directory "sub" do |dir3|
111
+ file = dir3.file "remote.txt", ""
112
+ end
113
+ end
114
+ end
115
+
116
+ expect_scp_session "-f -r /path/to/remote" do |channel|
117
+ channel.sends_ok
118
+ channel.gets_data "D0755 0 remote\n"
119
+ channel.sends_ok
120
+ channel.gets_data "D0755 0 sub\n"
121
+ simple_download(channel)
122
+ channel.gets_data "E\n"
123
+ channel.sends_ok
124
+ channel.gets_data "E\n"
125
+ channel.sends_ok
126
+ end
127
+
128
+ scp.download!("/path/to/remote", "/path/to/local", :recursive => true, :ssh => { :verbose => :debug })
129
+ assert_equal "a" * 1234, file.io.string
130
+ end
131
+
132
+ private
133
+
134
+ def simple_download(channel, mode=0666)
135
+ channel.sends_ok
136
+ channel.gets_data "C%04o 1234 remote.txt\n" % mode
137
+ channel.sends_ok
138
+ channel.gets_data "a" * 1234
139
+ channel.gets_ok
140
+ channel.sends_ok
141
+ end
142
+ end
data/test/test_scp.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'common'
2
+
3
+ class TestSCP < Net::SCP::TestCase
4
+ def test_start_without_block_should_return_scp_instance
5
+ ssh = stub('session', :logger => nil)
6
+ Net::SSH.expects(:start).
7
+ with("remote.host", "username", :password => "foo").
8
+ returns(ssh)
9
+
10
+ ssh.expects(:close).never
11
+ scp = Net::SCP.start("remote.host", "username", :password => "foo")
12
+ assert_instance_of Net::SCP, scp
13
+ assert_equal ssh, scp.session
14
+ end
15
+
16
+ def test_start_with_block_should_yield_scp_and_close_ssh_session
17
+ ssh = stub('session', :logger => nil)
18
+ Net::SSH.expects(:start).
19
+ with("remote.host", "username", :password => "foo").
20
+ returns(ssh)
21
+
22
+ ssh.expects(:loop)
23
+ ssh.expects(:close)
24
+
25
+ yielded = false
26
+ Net::SCP.start("remote.host", "username", :password => "foo") do |scp|
27
+ yielded = true
28
+ assert_instance_of Net::SCP, scp
29
+ assert_equal ssh, scp.session
30
+ end
31
+
32
+ assert yielded
33
+ end
34
+
35
+ def test_self_upload_should_instatiate_scp_and_invoke_synchronous_upload
36
+ scp = stub('scp')
37
+ scp.expects(:upload!).with("/path/to/local", "/path/to/remote", :recursive => true)
38
+
39
+ Net::SCP.expects(:start).
40
+ with("remote.host", "username", :password => "foo").
41
+ yields(scp)
42
+
43
+ Net::SCP.upload!("remote.host", "username", "/path/to/local", "/path/to/remote",
44
+ :ssh => { :password => "foo" }, :recursive => true)
45
+ end
46
+
47
+ def test_self_download_should_instatiate_scp_and_invoke_synchronous_download
48
+ scp = stub('scp')
49
+ scp.expects(:download!).with("/path/to/remote", "/path/to/local", :recursive => true).returns(:result)
50
+
51
+ Net::SCP.expects(:start).
52
+ with("remote.host", "username", :password => "foo").
53
+ yields(scp)
54
+
55
+ result = Net::SCP.download!("remote.host", "username", "/path/to/remote", "/path/to/local",
56
+ :ssh => { :password => "foo" }, :recursive => true)
57
+
58
+ assert_equal :result, result
59
+ end
60
+ end
@@ -0,0 +1,241 @@
1
+ require 'common'
2
+
3
+ class TestUpload < Net::SCP::TestCase
4
+ def test_upload_file_should_transfer_file
5
+ prepare_file("/path/to/local.txt", "a" * 1234)
6
+
7
+ expect_scp_session "-t /path/to/remote.txt" do |channel|
8
+ channel.gets_ok
9
+ channel.sends_data "C0666 1234 local.txt\n"
10
+ channel.gets_ok
11
+ channel.sends_data "a" * 1234
12
+ channel.sends_ok
13
+ channel.gets_ok
14
+ end
15
+
16
+ assert_scripted { scp.upload!("/path/to/local.txt", "/path/to/remote.txt") }
17
+ end
18
+
19
+ def test_upload_file_with_preserve_should_send_times
20
+ prepare_file("/path/to/local.txt", "a" * 1234, 0666, Time.at(1234567890, 123456), Time.at(1234543210, 345678))
21
+
22
+ expect_scp_session "-t -p /path/to/remote.txt" do |channel|
23
+ channel.gets_ok
24
+ channel.sends_data "T1234567890 123456 1234543210 345678\n"
25
+ channel.gets_ok
26
+ channel.sends_data "C0666 1234 local.txt\n"
27
+ channel.gets_ok
28
+ channel.sends_data "a" * 1234
29
+ channel.sends_ok
30
+ channel.gets_ok
31
+ end
32
+
33
+ assert_scripted { scp.upload!("/path/to/local.txt", "/path/to/remote.txt", :preserve => true) }
34
+ end
35
+
36
+ def test_upload_file_with_progress_callback_should_invoke_callback
37
+ prepare_file("/path/to/local.txt", "a" * 3000 + "b" * 3000 + "c" * 3000 + "d" * 3000)
38
+
39
+ expect_scp_session "-t /path/to/remote.txt" do |channel|
40
+ channel.gets_ok
41
+ channel.sends_data "C0666 12000 local.txt\n"
42
+ channel.gets_ok
43
+ channel.sends_data "a" * 3000
44
+ channel.sends_data "b" * 3000
45
+ channel.sends_data "c" * 3000
46
+ channel.sends_data "d" * 3000
47
+ channel.sends_ok
48
+ channel.gets_ok
49
+ end
50
+
51
+ calls = []
52
+ progress = Proc.new do |ch, name, sent, total|
53
+ calls << [name, sent, total]
54
+ end
55
+
56
+ assert_scripted do
57
+ scp.upload!("/path/to/local.txt", "/path/to/remote.txt", :chunk_size => 3000, &progress)
58
+ end
59
+
60
+ assert_equal ["/path/to/local.txt", 0, 12000], calls.shift
61
+ assert_equal ["/path/to/local.txt", 3000, 12000], calls.shift
62
+ assert_equal ["/path/to/local.txt", 6000, 12000], calls.shift
63
+ assert_equal ["/path/to/local.txt", 9000, 12000], calls.shift
64
+ assert_equal ["/path/to/local.txt", 12000, 12000], calls.shift
65
+ assert calls.empty?
66
+ end
67
+
68
+ def test_upload_io_with_recursive_should_ignore_recursive
69
+ expect_scp_session "-t -r /path/to/remote.txt" do |channel|
70
+ channel.gets_ok
71
+ channel.sends_data "C0640 1234 remote.txt\n"
72
+ channel.gets_ok
73
+ channel.sends_data "a" * 1234
74
+ channel.sends_ok
75
+ channel.gets_ok
76
+ end
77
+
78
+ io = StringIO.new("a" * 1234)
79
+ assert_scripted { scp.upload!(io, "/path/to/remote.txt", :recursive => true) }
80
+ end
81
+
82
+ def test_upload_io_with_preserve_should_ignore_preserve
83
+ expect_scp_session "-t -p /path/to/remote.txt" do |channel|
84
+ channel.gets_ok
85
+ channel.sends_data "C0640 1234 remote.txt\n"
86
+ channel.gets_ok
87
+ channel.sends_data "a" * 1234
88
+ channel.sends_ok
89
+ channel.gets_ok
90
+ end
91
+
92
+ io = StringIO.new("a" * 1234)
93
+ assert_scripted { scp.upload!(io, "/path/to/remote.txt", :preserve => true) }
94
+ end
95
+
96
+ def test_upload_io_should_transfer_data
97
+ expect_scp_session "-t /path/to/remote.txt" do |channel|
98
+ channel.gets_ok
99
+ channel.sends_data "C0640 1234 remote.txt\n"
100
+ channel.gets_ok
101
+ channel.sends_data "a" * 1234
102
+ channel.sends_ok
103
+ channel.gets_ok
104
+ end
105
+
106
+ io = StringIO.new("a" * 1234)
107
+ assert_scripted { scp.upload!(io, "/path/to/remote.txt") }
108
+ end
109
+
110
+ def test_upload_io_with_mode_should_honor_mode_as_permissions
111
+ expect_scp_session "-t /path/to/remote.txt" do |channel|
112
+ channel.gets_ok
113
+ channel.sends_data "C0666 1234 remote.txt\n"
114
+ channel.gets_ok
115
+ channel.sends_data "a" * 1234
116
+ channel.sends_ok
117
+ channel.gets_ok
118
+ end
119
+
120
+ io = StringIO.new("a" * 1234)
121
+ assert_scripted { scp.upload!(io, "/path/to/remote.txt", :mode => 0666) }
122
+ end
123
+
124
+ def test_upload_directory_without_recursive_should_error
125
+ prepare_directory("/path/to/local")
126
+
127
+ expect_scp_session("-t /path/to/remote") do |channel|
128
+ channel.gets_ok
129
+ end
130
+
131
+ assert_raises(Net::SCP::Error) { scp.upload!("/path/to/local", "/path/to/remote") }
132
+ end
133
+
134
+ def test_upload_empty_directory_should_create_directory_and_finish
135
+ prepare_directory("/path/to/local")
136
+
137
+ expect_scp_session("-t -r /path/to/remote") do |channel|
138
+ channel.gets_ok
139
+ channel.sends_data "D0777 0 local\n"
140
+ channel.gets_ok
141
+ channel.sends_data "E\n"
142
+ channel.gets_ok
143
+ end
144
+
145
+ assert_scripted { scp.upload!("/path/to/local", "/path/to/remote", :recursive => true) }
146
+ end
147
+
148
+ def test_upload_directory_should_recursively_create_and_upload_items
149
+ prepare_directory("/path/to/local") do |d|
150
+ d.file "hello.txt", "hello world\n"
151
+ d.directory "others" do |d2|
152
+ d2.file "data.dat", "abcdefghijklmnopqrstuvwxyz"
153
+ end
154
+ d.file "zoo.doc", "going to the zoo\n"
155
+ end
156
+
157
+ expect_scp_session("-t -r /path/to/remote") do |channel|
158
+ channel.gets_ok
159
+ channel.sends_data "D0777 0 local\n"
160
+ channel.gets_ok
161
+ channel.sends_data "C0666 12 hello.txt\n"
162
+ channel.gets_ok
163
+ channel.sends_data "hello world\n"
164
+ channel.sends_ok
165
+ channel.gets_ok
166
+ channel.sends_data "D0777 0 others\n"
167
+ channel.gets_ok
168
+ channel.sends_data "C0666 26 data.dat\n"
169
+ channel.gets_ok
170
+ channel.sends_data "abcdefghijklmnopqrstuvwxyz"
171
+ channel.sends_ok
172
+ channel.gets_ok
173
+ channel.sends_data "E\n"
174
+ channel.gets_ok
175
+ channel.sends_data "C0666 17 zoo.doc\n"
176
+ channel.gets_ok
177
+ channel.sends_data "going to the zoo\n"
178
+ channel.sends_ok
179
+ channel.gets_ok
180
+ channel.sends_data "E\n"
181
+ channel.gets_ok
182
+ end
183
+
184
+ assert_scripted { scp.upload!("/path/to/local", "/path/to/remote", :recursive => true) }
185
+ end
186
+
187
+ def test_upload_directory_with_preserve_should_send_times_for_all_items
188
+ prepare_directory("/path/to/local", 0755, Time.at(17171717, 191919), Time.at(18181818, 101010)) do |d|
189
+ d.file "hello.txt", "hello world\n", 0640, Time.at(12345, 67890), Time.at(234567, 890)
190
+ d.directory "others", 0770, Time.at(112233, 4455), Time.at(22334455, 667788) do |d2|
191
+ d2.file "data.dat", "abcdefghijklmnopqrstuvwxyz", 0600, Time.at(13579135, 13131), Time.at(7654321, 654321)
192
+ end
193
+ d.file "zoo.doc", "going to the zoo\n", 0444, Time.at(12121212, 131313), Time.at(23232323, 242424)
194
+ end
195
+
196
+ expect_scp_session("-t -r -p /path/to/remote") do |channel|
197
+ channel.gets_ok
198
+ channel.sends_data "T17171717 191919 18181818 101010\n"
199
+ channel.gets_ok
200
+ channel.sends_data "D0755 0 local\n"
201
+ channel.gets_ok
202
+ channel.sends_data "T12345 67890 234567 890\n"
203
+ channel.gets_ok
204
+ channel.sends_data "C0640 12 hello.txt\n"
205
+ channel.gets_ok
206
+ channel.sends_data "hello world\n"
207
+ channel.sends_ok
208
+ channel.gets_ok
209
+ channel.sends_data "T112233 4455 22334455 667788\n"
210
+ channel.gets_ok
211
+ channel.sends_data "D0770 0 others\n"
212
+ channel.gets_ok
213
+ channel.sends_data "T13579135 13131 7654321 654321\n"
214
+ channel.gets_ok
215
+ channel.sends_data "C0600 26 data.dat\n"
216
+ channel.gets_ok
217
+ channel.sends_data "abcdefghijklmnopqrstuvwxyz"
218
+ channel.sends_ok
219
+ channel.gets_ok
220
+ channel.sends_data "E\n"
221
+ channel.gets_ok
222
+ channel.sends_data "T12121212 131313 23232323 242424\n"
223
+ channel.gets_ok
224
+ channel.sends_data "C0444 17 zoo.doc\n"
225
+ channel.gets_ok
226
+ channel.sends_data "going to the zoo\n"
227
+ channel.sends_ok
228
+ channel.gets_ok
229
+ channel.sends_data "E\n"
230
+ channel.gets_ok
231
+ end
232
+
233
+ assert_scripted { scp.upload!("/path/to/local", "/path/to/remote", :preserve => true, :recursive => true) }
234
+ end
235
+
236
+ def test_upload_should_not_block
237
+ prepare_file("/path/to/local.txt", "data")
238
+ story { |s| s.opens_channel(false) }
239
+ assert_scripted { scp.upload("/path/to/local.txt", "/path/to/remote.txt") }
240
+ end
241
+ end