buildr 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -71,7 +71,7 @@ module Buildr
71
71
 
72
72
  def initialize(xml) #:nodoc:
73
73
  @project = XmlSimple.xml_in(xml)
74
- @parent = POM.load(pom_to_hash(project["parent"].first)) if project["parent"]
74
+ @parent = POM.load(pom_to_hash(project["parent"].first).merge(:type=>'pom')) if project['parent']
75
75
  end
76
76
 
77
77
  # :call-seq:
@@ -201,6 +201,7 @@ module Buildr
201
201
  ant.classpath :path=>dependencies.join(File::PATH_SEPARATOR)
202
202
  (options[:properties] || []).each { |key, value| ant.sysproperty :key=>key, :value=>value }
203
203
  (options[:environment] || []).each { |key, value| ant.env :key=>key, :value=>value }
204
+ Array(options[:java_args]).each { |value| ant.jvmarg :value=>value }
204
205
  ant.formatter :type=>'plain'
205
206
  ant.formatter :type=>'plain', :usefile=>false # log test
206
207
  ant.formatter :type=>'xml'
@@ -24,8 +24,8 @@ module Buildr
24
24
  CMP_REGEX = Gem::Requirement::OP_RE.dup
25
25
  CMP_CHARS = CMP_PROCS.keys.join
26
26
  BOOL_CHARS = '\|\&\!'
27
- VER_CHARS = '\w\.'
28
-
27
+ VER_CHARS = '\w\.\-'
28
+
29
29
  class << self
30
30
  # is +str+ a version string?
31
31
  def version?(str)
@@ -496,13 +496,25 @@ module Buildr
496
496
 
497
497
  # Return an artifact spec without the version part.
498
498
  def unversioned_spec
499
- to_spec[/^([a-zA-Z._-]+(:[a-zA-Z._-]+){2,3})/]
499
+ str = to_spec
500
+ return nil if str =~ /^:+/
501
+ ary = str.split(':')
502
+ ary = ary[0...-1] if ary.size > 3
503
+ ary.join(':')
500
504
  end
501
505
 
502
506
  class << self
503
507
  # Return an artifact spec without the version part.
504
508
  def unversioned_spec(spec)
505
- spec.to_s[/^([a-zA-Z._-]+(:[a-zA-Z._-]+){2,3})/] || new(spec).unversioned_spec
509
+ str = spec.to_s
510
+ return nil if str =~ /^:+/
511
+ ary = str.split(':')
512
+ ary = ary[0...-1] if ary.size > 3
513
+ if ary.size > 2
514
+ ary.join(':')
515
+ else
516
+ new(spec).unversioned_spec
517
+ end
506
518
  end
507
519
  end
508
520
  end
@@ -65,14 +65,39 @@ namespace 'apache' do
65
65
  target = args.incubating ? "people.apache.org:/www/www.apache.org/dist/incubator/#{spec.name}/#{spec.version}-incubating" :
66
66
  "people.apache.org:/www/www.apache.org/dist/#{spec.name}/#{spec.version}"
67
67
  puts 'Uploading packages to Apache distro ...'
68
- sh 'rsync', '--progress', 'published/distro/*', target
68
+ host, remote_dir = target.split(':')
69
+ sh 'ssh', host, 'rm', '-rf', remote_dir rescue nil
70
+ sh 'ssh', host, 'mkdir', remote_dir
71
+ sh 'rsync', '--progress', '--recursive', 'published/distro/', target
69
72
  puts 'Done'
70
73
  end
71
74
 
72
-
73
- task 'distro-links'=>['staged/site', 'apache:sign'] do |task, args|
75
+ task 'distro-links'=>'staged/distro' do |task, args|
74
76
  url = args.incubating ? "http://www.apache.org/dist/incubator/#{spec.name}/#{spec.version}-incubating" :
75
77
  "http://www.apache.org/dist/#{spec.name}/#{spec.version}"
78
+ rows = FileList['staged/distro/*.{gem,tgz,zip}'].map { |pkg|
79
+ name, md5 = File.basename(pkg), MD5.file(pkg).to_s
80
+ %{| "#{name}":#{url}/#{name} | "#{md5}":#{url}/#{name}.md5 | "Sig":#{url}/#{name}.asc |}
81
+ }
82
+ textile = <<-TEXTILE
83
+ h3. #{spec.name} #{spec.version}#{args.incubating && "-incubating"} (#{Time.now.strftime('%Y-%m-%d')})
84
+
85
+ |_. Package |_. MD5 Checksum |_. PGP |
86
+ #{rows.join("\n")}
87
+
88
+ p>. ("Release signing keys":#{url}/KEYS)
89
+ TEXTILE
90
+ file_name = 'doc/pages/download.textile'
91
+ print "Adding download links to #{file_name} ... "
92
+ modified = File.read(file_name).sub(/h2.*binaries.*source.*/i) { |header| "#{header}\n\n#{textile}" }
93
+ File.open file_name, 'w' do |file|
94
+ file.write modified
95
+ end
96
+ puts 'Done'
97
+ end
98
+
99
+ =begin
100
+ task 'distro-links'=>['staged/site', 'apache:sign'] do |task, args|
76
101
  rows = FileList['staged/distro/*.{gem,tgz,zip}'].map { |pkg|
77
102
  name, md5 = File.basename(pkg), File.read("#{pkg}.md5").split.first
78
103
  <<-HTML
@@ -97,9 +122,9 @@ namespace 'apache' do
97
122
  file.write modified
98
123
  end
99
124
  end
125
+ =end
100
126
 
101
- file 'staged/site'=>'site' do
102
- mkpath 'staged'
127
+ file 'staged/site'=>['distro-links', 'staged', 'site'] do
103
128
  rm_rf 'staged/site'
104
129
  cp_r 'site', 'staged'
105
130
  end
@@ -109,7 +134,7 @@ namespace 'apache' do
109
134
  target = args.incubating ? "people.apache.org:/www/incubator.apache.org/#{spec.name}" :
110
135
  "people.apache.org:/www/#{spec.name}.apache.org"
111
136
  puts 'Uploading Apache Web site ...'
112
- sh 'rsync', '--progress', '--recursive', '--delete', 'published/distro/site/', target
137
+ sh 'rsync', '--progress', '--recursive', '--delete', 'published/site/', target
113
138
  puts 'Done'
114
139
  end
115
140
 
@@ -117,7 +142,7 @@ end
117
142
 
118
143
 
119
144
  task 'stage:check'=>['apache:license', 'apache:check']
120
- task 'stage:prepare'=>['staged/distro', 'staged/site', 'apache:distro-links'] do |task|
145
+ task 'stage:prepare'=>['staged/distro', 'staged/site'] do |task|
121
146
  # Since this requires input (passphrase), do it at the very end.
122
147
  task.enhance do
123
148
  task('apache:sign').invoke
@@ -40,7 +40,8 @@ namespace 'changelog' do
40
40
  end
41
41
 
42
42
  task 'wrapup'=>'CHANGELOG' do
43
- next_version = spec.version.to_ints.zip([0, 0, 1]).map { |a| a.inject(0) { |t,i| t + i } }.join('.')
43
+ next_version = spec.version.to_s.split('.').map { |v| v.to_i }.
44
+ zip([0, 0, 1]).map { |a| a.inject(0) { |t,i| t + i } }.join('.')
44
45
  print 'Adding new entry to CHANGELOG ... '
45
46
  modified = "#{next_version} (Pending)\n\n" + File.read('CHANGELOG')
46
47
  File.open 'CHANGELOG', 'w' do |file|
@@ -38,9 +38,11 @@ task 'release'=>['release:prepare', 'release:publish', 'release:wrapup']
38
38
 
39
39
 
40
40
  task 'next_version' do
41
+ next_version = spec.version.to_s.split('.').map { |v| v.to_i }.
42
+ zip([0, 0, 1]).map { |a| a.inject(0) { |t,i| t + i } }.join('.')
43
+
41
44
  ver_file = "lib/#{spec.name}.rb"
42
45
  if File.exist?(ver_file)
43
- next_version = spec.version.to_ints.zip([0, 0, 1]).map { |a| a.inject(0) { |t,i| t + i } }.join('.')
44
46
  print "Updating #{ver_file} to next version number (#{next_version}) ... "
45
47
  modified = File.read(ver_file).sub(/(VERSION\s*=\s*)(['"])(.*)\2/) { |line| "#{$1}#{$2}#{next_version}#{$2}" }
46
48
  File.open ver_file, 'w' do |file|
@@ -48,6 +50,16 @@ task 'next_version' do
48
50
  end
49
51
  puts 'Done'
50
52
  end
53
+
54
+ spec_file = "#{spec.name}.gemspec"
55
+ if File.exist?(spec_file)
56
+ print "Updating #{spec_file} to next version number (#{next_version}) ... "
57
+ modified = File.read(spec_file).sub(/(s(?:pec)?\.version\s*=\s*)(['"])(.*)\2/) { |line| "#{$1}#{$2}#{next_version}#{$2}" }
58
+ File.open spec_file, 'w' do |file|
59
+ file.write modified
60
+ end
61
+ puts 'Done'
62
+ end
51
63
  end
52
64
 
53
65
  task 'release:wrapup'=>'next_version'
@@ -29,14 +29,21 @@ end
29
29
 
30
30
  namespace 'rubyforge' do
31
31
 
32
- task 'release'=>'published' do |task|
32
+ file 'published/rubyforge'=>'published' do
33
+ mkdir 'published/rubyforge'
34
+ FileList['published/distro/*.{gem,tgz,zip}'].each do |pkg|
35
+ cp pkg, 'published/rubyforge/' + File.basename(pkg).sub(/-incubating/, '')
36
+ end
37
+ end
38
+
39
+ task 'release'=>'published/rubyforge' do |task|
33
40
  changes = FileList['published/CHANGES'].first
34
- files = FileList['published/*.{gem,tgz,zip}'].exclude(changes).existing
41
+ files = FileList['published/rubyforge/*.{gem,tgz,zip}'].exclude(changes).existing
35
42
  print "Uploading #{spec.version} to RubyForge ... "
36
43
  rubyforge = RubyForge.new
37
44
  rubyforge.login
38
45
  rubyforge.userconfig.merge!('release_changes'=>changes, 'preformatted' => true) if changes
39
- rubyforge.add_release spec.rubyforge_project.downcase, spec.name.downcase, spec.version, *files
46
+ rubyforge.add_release spec.rubyforge_project.downcase, spec.name.downcase, spec.version.to_s, *files
40
47
  puts 'Done'
41
48
  end
42
49
 
@@ -17,7 +17,7 @@
17
17
 
18
18
 
19
19
  require 'rubygems/source_info_cache'
20
-
20
+ require 'stringio' # for Gem::RemoteFetcher
21
21
 
22
22
  def windows?
23
23
  Config::CONFIG['host_os'] =~ /windows|cygwin|bccwin|cygwin|djgpp|mingw|mswin|wince/i
@@ -38,11 +38,12 @@ end
38
38
 
39
39
  def install_gem(name, ver_requirement = nil)
40
40
  dep = Gem::Dependency.new(name, ver_requirement)
41
+ rb_bin = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
41
42
  if Gem::SourceIndex.from_installed_gems.search(dep).empty?
42
- spec = Gem::SourceInfoCache.search(dep).last
43
+ spec = Gem::SourceInfoCache.search(dep, true, true).last
43
44
  fail "#{dep} not found in local or remote repository!" unless spec
44
45
  puts "Installing #{spec} ..."
45
- args = [Config::CONFIG['ruby_install_name'], '-S', 'gem', 'install', spec.name, '-v', spec.version.to_s]
46
+ args = [rb_bin, '-S', 'gem', 'install', spec.name, '-v', spec.version.to_s]
46
47
  args.unshift('sudo', 'env', 'JAVA_HOME=' + ENV['JAVA_HOME']) unless windows?
47
48
  sh *args
48
49
  end
@@ -210,6 +210,11 @@ describe Buildr, 'settings' do
210
210
  Buildr.settings.user.should == { 'foo'=>'bar' }
211
211
  end
212
212
 
213
+ it 'should return loaded settings.yml file' do
214
+ write 'home/.buildr/settings.yml', 'foo: bar'
215
+ Buildr.settings.user.should == { 'foo'=>'bar' }
216
+ end
217
+
213
218
  it 'should fail if settings.yaml file is not a hash' do
214
219
  write 'home/.buildr/settings.yaml', 'foo bar'
215
220
  lambda { Buildr.settings.user }.should raise_error(RuntimeError, /expecting.*settings.yaml/i)
@@ -231,6 +236,11 @@ describe Buildr, 'settings' do
231
236
  Buildr.settings.build.should == { 'foo'=>'bar' }
232
237
  end
233
238
 
239
+ it 'should return loaded build.yml file' do
240
+ write 'build.yml', 'foo: bar'
241
+ Buildr.settings.build.should == { 'foo'=>'bar' }
242
+ end
243
+
234
244
  it 'should fail if build.yaml file is not a hash' do
235
245
  write 'build.yaml', 'foo bar'
236
246
  lambda { Buildr.settings.build }.should raise_error(RuntimeError, /expecting.*build.yaml/i)
@@ -255,6 +265,14 @@ describe Buildr, 'settings' do
255
265
  Buildr.settings.profiles.should == { 'development'=> { 'foo'=>'bar' } }
256
266
  end
257
267
 
268
+ it 'should return loaded profiles.yml file' do
269
+ write 'profiles.yml', <<-YAML
270
+ development:
271
+ foo: bar
272
+ YAML
273
+ Buildr.settings.profiles.should == { 'development'=> { 'foo'=>'bar' } }
274
+ end
275
+
258
276
  it 'should fail if profiles.yaml file is not a hash' do
259
277
  write 'profiles.yaml', 'foo bar'
260
278
  lambda { Buildr.settings.profiles }.should raise_error(RuntimeError, /expecting.*profiles.yaml/i)
@@ -42,7 +42,6 @@ describe 'ArchiveTask', :shared=>true do
42
42
 
43
43
  def create_for_merge
44
44
  zip(@archive + '.src').include(@files).tap do |task|
45
- task.invoke
46
45
  yield task
47
46
  end
48
47
  end
@@ -424,6 +424,15 @@ describe Buildr, '#artifact' do
424
424
  artifact = artifact('group:id:jar:1.0').from('test.jar')
425
425
  lambda { artifact.invoke }.should change { File.exist?(artifact.to_s) }.to(true)
426
426
  end
427
+
428
+ it 'should reference artifacts defined on build.yaml by using ruby symbols' do
429
+ write 'build.yaml', <<-YAML
430
+ artifacts:
431
+ j2ee: geronimo-spec:geronimo-spec-j2ee:jar:1.4-rc4
432
+ YAML
433
+ Buildr.application.load_artifacts
434
+ artifact(:j2ee).to_s.pathmap('%f').should == 'geronimo-spec-j2ee-1.4-rc4.jar'
435
+ end
427
436
  end
428
437
 
429
438
 
@@ -411,10 +411,6 @@ describe Buildr::Filter do
411
411
  @filter.from('src').into('target').run.should be(false)
412
412
  end
413
413
 
414
- it 'should fail is source directory not set' do
415
- lambda { Filter.new.into('target').run }.should raise_error(RuntimeError, /No source directory/)
416
- end
417
-
418
414
  it 'should fail if source directory doesn\'t exist' do
419
415
  lambda { Filter.new.from('srced').into('target').run }.should raise_error(RuntimeError, /doesn't exist/)
420
416
  end
@@ -0,0 +1,38 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ require File.join(File.dirname(__FILE__), 'spec_helpers')
17
+
18
+ describe ENV, 'JAVA_HOME on OS X' do
19
+ before do
20
+ @old_home, ENV['JAVA_HOME'] = ENV['JAVA_HOME'], nil
21
+ Config::CONFIG.should_receive(:[]).with('host_os').and_return('darwin0.9')
22
+ end
23
+
24
+ it 'should point to default JVM' do
25
+ load File.expand_path('../lib/buildr/java.rb')
26
+ ENV['JAVA_HOME'].should == '/System/Library/Frameworks/JavaVM.framework/Home'
27
+ end
28
+
29
+ it 'should use value of environment variable if specified' do
30
+ ENV['JAVA_HOME'] = '/System/Library/Frameworks/JavaVM.specified'
31
+ load File.expand_path('../lib/buildr/java.rb')
32
+ ENV['JAVA_HOME'].should == '/System/Library/Frameworks/JavaVM.specified'
33
+ end
34
+
35
+ after do
36
+ ENV['JAVA_HOME'] = @old_home
37
+ end
38
+ end
@@ -179,6 +179,18 @@ describe Buildr::JUnit do
179
179
  project('foo').test.invoke
180
180
  end
181
181
 
182
+ it 'should pass environment to JVM' do
183
+ write 'src/test/java/EnvironmentTest.java', <<-JAVA
184
+ public class EnvironmentTest extends junit.framework.TestCase {
185
+ public void testEnvironment() {
186
+ assertEquals("value", System.getenv("NAME"));
187
+ }
188
+ }
189
+ JAVA
190
+ define('foo').test.using :environment=>{ 'NAME'=>'value' }
191
+ project('foo').test.invoke
192
+ end
193
+
182
194
  it 'should set current directory' do
183
195
  mkpath 'baz'
184
196
  expected = File.expand_path('baz')
@@ -374,11 +374,13 @@ describe Buildr::Project, '#test' do
374
374
 
375
375
  it 'should inherit options from parent project' do
376
376
  define 'foo' do
377
- test.using :fail_on_failure=>false, :fork=>:each, :properties=>{ :foo=>'bar' }
377
+ test.using :fail_on_failure=>false, :fork=>:each, :properties=>{ :foo=>'bar' }, :environment=>{ 'config'=>'config.yaml' }
378
378
  define 'bar' do
379
+ test.using :junit
379
380
  test.options[:fail_on_failure].should be_false
380
381
  test.options[:fork].should == :each
381
382
  test.options[:properties][:foo].should == 'bar'
383
+ test.options[:environment]['config'].should == 'config.yaml'
382
384
  end
383
385
  end
384
386
  end
@@ -386,11 +388,13 @@ describe Buildr::Project, '#test' do
386
388
  it 'should clone options from parent project' do
387
389
  define 'foo' do
388
390
  define 'bar' do
389
- test.using :fail_on_failure=>false, :fork=>:each, :properties=>{ :foo=>'bar' }
391
+ test.using :fail_on_failure=>false, :fork=>:each, :properties=>{ :foo=>'bar' }, :environment=>{ 'config'=>'config.yaml' }
392
+ test.using :junit
390
393
  end.invoke
391
394
  test.options[:fail_on_failure].should be_true
392
395
  test.options[:fork].should == :once
393
- test.options[:other].should be_nil
396
+ test.options[:properties].should be_empty
397
+ test.options[:environment].should be_empty
394
398
  end
395
399
  end
396
400
  end
@@ -454,6 +458,7 @@ end
454
458
  describe Buildr::Project, 'test:resources' do
455
459
  it 'should ignore resources unless they exist' do
456
460
  define('foo').test.resources.sources.should be_empty
461
+ project('foo').test.resources.target.should be_nil
457
462
  end
458
463
 
459
464
  it 'should pick resources from src/test/resources if found' do
@@ -467,6 +472,11 @@ describe Buildr::Project, 'test:resources' do
467
472
  file('targeted/test/resources/foo').should contain('Foo')
468
473
  end
469
474
 
475
+ it 'should create target directory even if no files to copy' do
476
+ define('foo').test.resources.filter.into('resources')
477
+ lambda { file(File.expand_path('resources')).invoke }.should change { File.exist?('resources') }.to(true)
478
+ end
479
+
470
480
  it 'should execute alongside compile task' do
471
481
  task 'action'
472
482
  define('foo') { test.resources { task('action').invoke } }
@@ -221,7 +221,9 @@ describe URI::HTTP, '#read' do
221
221
  @proxy = 'http://john:smith@myproxy:8080'
222
222
  @domain = 'domain'
223
223
  @host_domain = "host.#{@domain}"
224
- @uri = URI("http://#{@host_domain}")
224
+ @path = "/foo/bar/baz"
225
+ @query = "?query"
226
+ @uri = URI("http://#{@host_domain}#{@path}#{@query}")
225
227
  @no_proxy_args = [@host_domain, 80]
226
228
  @proxy_args = @no_proxy_args + ['myproxy', 8080, 'john', 'smith']
227
229
  @http = mock('http')
@@ -297,4 +299,190 @@ describe URI::HTTP, '#read' do
297
299
  request.should_receive(:basic_auth).with('john', 'secret')
298
300
  URI("http://john:secret@#{@host_domain}").read
299
301
  end
302
+
303
+ it 'should include the query part when performing HTTP GET' do
304
+ # should this test be generalized or shared with any other URI subtypes?
305
+ Net::HTTP.stub!(:new).and_return(@http)
306
+ Net::HTTP::Get.should_receive(:new).with(/#{Regexp.escape(@query)}$/, nil)
307
+ @uri.read
308
+ end
309
+
310
+ end
311
+
312
+
313
+ describe URI::HTTP, '#write' do
314
+ before do
315
+ @content = 'Readme. Please!'
316
+ @uri = URI('http://john:secret@host.domain/foo/bar/baz.jar')
317
+ @http = mock('Net::HTTP')
318
+ @http.stub!(:request).and_return(Net::HTTPOK.new(nil, nil, nil))
319
+ Net::HTTP.stub!(:new).and_return(@http)
320
+ end
321
+
322
+ it 'should open connection to HTTP server' do
323
+ Net::HTTP.should_receive(:new).with('host.domain', 80).and_return(@http)
324
+ @uri.write @content
325
+ end
326
+
327
+ it 'should use HTTP basic authentication' do
328
+ @http.should_receive(:request) do |request|
329
+ request['authorization'].should == ('Basic ' + ['john:secret'].pack('m').delete("\r\n"))
330
+ Net::HTTPOK.new(nil, nil, nil)
331
+ end
332
+ @uri.write @content
333
+ end
334
+
335
+ it 'should use HTTPS if applicable' do
336
+ Net::HTTP.should_receive(:new).with('host.domain', 443).and_return(@http)
337
+ @http.should_receive(:use_ssl=).with(true)
338
+ URI(@uri.to_s.sub(/http/, 'https')).write @content
339
+ end
340
+
341
+ it 'should upload file with PUT request' do
342
+ @http.should_receive(:request) do |request|
343
+ request.should be_kind_of(Net::HTTP::Put)
344
+ Net::HTTPOK.new(nil, nil, nil)
345
+ end
346
+ @uri.write @content
347
+ end
348
+
349
+ it 'should set Content-Length header' do
350
+ @http.should_receive(:request) do |request|
351
+ request.content_length.should == @content.size
352
+ Net::HTTPOK.new(nil, nil, nil)
353
+ end
354
+ @uri.write @content
355
+ end
356
+
357
+ it 'should set Content-MD5 header' do
358
+ @http.should_receive(:request) do |request|
359
+ request['Content-MD5'].should == Digest::MD5.hexdigest(@content)
360
+ Net::HTTPOK.new(nil, nil, nil)
361
+ end
362
+ @uri.write @content
363
+ end
364
+
365
+ it 'should send entire content' do
366
+ @http.should_receive(:request) do |request|
367
+ body_stream = request.body_stream
368
+ body_stream.read(1024).should == @content
369
+ body_stream.read(1024).should be_nil
370
+ Net::HTTPOK.new(nil, nil, nil)
371
+ end
372
+ @uri.write @content
373
+ end
374
+
375
+ it 'should fail on 4xx response' do
376
+ @http.should_receive(:request) do |request|
377
+ Net::HTTPBadRequest.new(nil, nil, nil)
378
+ end
379
+ lambda { @uri.write @content }.should raise_error(RuntimeError, /failed to upload/i)
380
+ end
381
+
382
+ it 'should fail on 5xx response' do
383
+ @http.should_receive(:request) do |request|
384
+ Net::HTTPServiceUnavailable.new(nil, nil, nil)
385
+ end
386
+ lambda { @uri.write @content }.should raise_error(RuntimeError, /failed to upload/i)
387
+ end
388
+
389
+ end
390
+
391
+
392
+ describe URI::SFTP, '#read' do
393
+ before do
394
+ @uri = URI('sftp://john:secret@localhost/path/readme')
395
+ @content = 'Readme. Please!'
396
+
397
+ @ssh_session = mock('Net::SSH::Session')
398
+ @sftp_session = mock('Net::SFTP::Session')
399
+ @file_factory = mock('Net::SFTP::Operations::FileFactory')
400
+ Net::SSH.stub!(:start).with('localhost', 'john', :password=>'secret', :port=>22) do
401
+ Net::SFTP::Session.should_receive(:new).with(@ssh_session).and_yield(@sftp_session).and_return(@sftp_session)
402
+ @sftp_session.should_receive(:connect!).and_return(@sftp_session)
403
+ @sftp_session.should_receive(:loop)
404
+ @sftp_session.should_receive(:file).with.and_return(@file_factory)
405
+ @file_factory.stub!(:open)
406
+ @ssh_session.should_receive(:close)
407
+ @ssh_session
408
+ end
409
+ end
410
+
411
+ it 'should open connection to SFTP server' do
412
+ @uri.read
413
+ end
414
+
415
+ it 'should open file for reading' do
416
+ @file_factory.should_receive(:open).with('/path/readme', 'r')
417
+ @uri.read
418
+ end
419
+
420
+ it 'should read contents of file and return it' do
421
+ file = mock('Net::SFTP::Operations::File')
422
+ file.should_receive(:read).with(an_instance_of(Numeric)).once.and_return(@content, nil)
423
+ @file_factory.should_receive(:open).with('/path/readme', 'r').and_yield(file)
424
+ @uri.read.should eql(@content)
425
+ end
426
+
427
+ it 'should read contents of file and pass it to block' do
428
+ file = mock('Net::SFTP::Operations::File')
429
+ file.should_receive(:read).with(an_instance_of(Numeric)).once.and_return(@content, nil)
430
+ @file_factory.should_receive(:open).with('/path/readme', 'r').and_yield(file)
431
+ content = ''
432
+ @uri.read do |chunk|
433
+ content << chunk
434
+ end
435
+ content.should eql(@content)
436
+ end
437
+ end
438
+
439
+
440
+ describe URI::SFTP, '#write' do
441
+ before do
442
+ @uri = URI('sftp://john:secret@localhost/path/readme')
443
+ @content = 'Readme. Please!'
444
+
445
+ @ssh_session = mock('Net::SSH::Session')
446
+ @sftp_session = mock('Net::SFTP::Session')
447
+ @file_factory = mock('Net::SFTP::Operations::FileFactory')
448
+ Net::SSH.stub!(:start).with('localhost', 'john', :password=>'secret', :port=>22) do
449
+ Net::SFTP::Session.should_receive(:new).with(@ssh_session).and_yield(@sftp_session).and_return(@sftp_session)
450
+ @sftp_session.should_receive(:connect!).and_return(@sftp_session)
451
+ @sftp_session.should_receive(:loop)
452
+ @sftp_session.stub!(:mkdir)
453
+ @sftp_session.should_receive(:file).with.and_return(@file_factory)
454
+ @file_factory.stub!(:open)
455
+ @ssh_session.should_receive(:close)
456
+ @ssh_session
457
+ end
458
+ end
459
+
460
+ it 'should open connection to SFTP server' do
461
+ @uri.write @content
462
+ end
463
+
464
+ it 'should create path recursively on SFTP server' do
465
+ @sftp_session.should_receive(:mkdir).ordered.with('', {})
466
+ @sftp_session.should_receive(:mkdir).ordered.with('/path', {})
467
+ @uri.write @content
468
+ end
469
+
470
+ it 'should only create paths that don\'t exist' do
471
+ @sftp_session.should_receive(:realpath).any_number_of_times
472
+ @sftp_session.should_not_receive(:mkdir)
473
+ @uri.write @content
474
+ end
475
+
476
+ it 'should open file for writing' do
477
+ @file_factory.should_receive(:open).with('/path/readme', 'w')
478
+ @uri.write @content
479
+ end
480
+
481
+ it 'should write contents to file' do
482
+ file = mock('Net::SFTP::Operations::File')
483
+ file.should_receive(:write).with(@content)
484
+ @file_factory.should_receive(:open).with('/path/readme', 'w').and_yield(file)
485
+ @uri.write @content
486
+ end
487
+
300
488
  end