blob: c62850a5ae7dcd5018abc07fb5cc20d99d7330cc [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with this
# work for additional information regarding copyright ownership. The ASF
# licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helpers'))
describe Buildr.method(:struct) do
before do
@hash = { :foo=>'foo:jar', :bar=>'bar:jar' }
@struct = struct(@hash)
end
it 'should be object with key-value pairs' do
@struct.foo.should eql('foo:jar')
@struct.bar.should eql('bar:jar')
end
it 'should fail when requesting non-existent key' do
lambda { @struct.foobar }.should raise_error(NoMethodError)
end
it 'should return members when requested' do
@struct.members.map(&:to_s).sort.should eql(@hash.keys.map(&:to_s).sort)
end
it 'should return valued when requested' do
@struct.values.sort.should eql(@hash.values.sort)
end
end
describe Buildr.method(:write) do
it 'should create path' do
write 'foo/test'
File.directory?('foo').should be_true
File.exist?('foo/test').should be_true
end
it 'should write content to file' do
write 'test', 'content'
File.read('test').should eql('content')
end
it 'should retrieve content from block, if block given' do
write('test') { 'block' }
File.read('test').should eql('block')
end
it 'should write empty file if no content provided' do
write 'test'
File.read('test').should eql('')
end
it 'should return content as a string' do
write('test', 'content').should eql('content')
end
it 'should return empty string if no content provided' do
write('test').should eql('')
end
end
describe Buildr.method(:read) do
before do
write @file = 'test', @content = 'content'
end
it 'should return contents of named file' do
read(@file).should eql(@content)
end
it 'should yield to block if block given' do
read @file do |content|
content.should eql(@content)
end
end
it 'should return block response if block given' do
read(@file) { 5 }.should be(5)
end
end
describe Buildr.method(:download) do
before do
@content = 'we has download!'
@http = double('http')
@http.stub(:request).and_return(Net::HTTPNotModified.new(nil, nil, nil))
end
def tasks()
[ download('http://localhost/download'), download('downloaded'=>'http://localhost/download') ]
end
it 'should be a file task' do
tasks.each { |task| task.should be_kind_of(Rake::FileTask) }
end
it 'should accept a String and download from that URL' do
define 'foo' do
download('http://localhost/download').tap do |task|
task.source.should_receive(:read).and_yield [@content]
task.invoke
task.should contain(@content)
end
end
end
it 'should accept a URI and download from that URL' do
define 'foo' do
download(URI.parse('http://localhost/download')).tap do |task|
task.source.should_receive(:read).and_yield [@content]
task.invoke
task.should contain(@content)
end
end
end
it 'should accept a path and String and download from that URL' do
define 'foo' do
download('downloaded'=>'http://localhost/download').tap do |task|
task.source.should_receive(:read).and_yield [@content]
task.invoke
task.should contain(@content)
end
end
end
it 'should accept an artifact and String and download from that URL' do
define 'foo' do
artifact('com.example:library:jar:2.0').tap do |artifact|
download(artifact=>'http://localhost/download').source.should_receive(:read).and_yield [@content]
artifact.invoke
artifact.should contain(@content)
end
end
end
it 'should accept a path and URI and download from that URL' do
define 'foo' do
download('downloaded'=>URI.parse('http://localhost/download')).tap do |task|
task.source.should_receive(:read).and_yield [@content]
task.invoke
task.should contain(@content)
end
end
end
it 'should create path for download' do
define 'foo' do
download('path/downloaded'=>URI.parse('http://localhost/download')).tap do |task|
task.source.should_receive(:read).and_yield [@content]
task.invoke
task.should contain(@content)
end
end
end
it 'should fail if resource not found' do
tasks.each do |task|
task.source.should_receive(:read).and_raise URI::NotFoundError
lambda { task.invoke }.should raise_error(URI::NotFoundError)
end
tasks.last.should_not exist
end
it 'should fail on any other error' do
tasks.each do |task|
task.source.should_receive(:read).and_raise RuntimeError
lambda { task.invoke }.should raise_error(RuntimeError)
end
tasks.last.should_not exist
end
it 'should execute only if file does not already exist' do
define 'foo' do
download('downloaded'=>'http://localhost/download').tap do |task|
task.source.should_not_receive(:read)
write task.to_s, 'not really'
task.invoke
end
end
end
it 'should execute without a proxy if none specified' do
Net::HTTP.should_receive(:new).with('localhost', 80).twice.and_return(@http)
tasks.each(&:invoke)
end
it 'should pass Buildr proxy options' do
Buildr.options.proxy.http = 'http://proxy:8080'
Net::HTTP.should_receive(:new).with('localhost', 80, 'proxy', 8080, nil, nil).twice.and_return(@http)
tasks.each(&:invoke)
end
it 'should set HTTP proxy from HTTP_PROXY environment variable' do
ENV['HTTP_PROXY'] = 'http://proxy:8080'
Net::HTTP.should_receive(:new).with('localhost', 80, 'proxy', 8080, nil, nil).twice.and_return(@http)
tasks.each(&:invoke)
end
end
describe Buildr.method(:filter) do
def source
File.expand_path('src')
end
it 'should return a Filter for the source' do
filter(source).should be_kind_of(Filter)
end
it 'should use the source directory' do
filter(source).sources.should include(file(source))
end
it 'should use the source directories' do
dirs = ['first', 'second']
filter('first', 'second').sources.should include(*dirs.map { |dir| file(File.expand_path(dir)) })
end
it 'should accept a file task' do
task = file(source)
filter(task).sources.each { |source| source.should be(task) }
end
end
describe Buildr::Filter do
before do
@filter = Filter.new
1.upto(4) do |i|
write "src/file#{i}", "file#{i} raw"
end
@early = Time.now - 1000
end
it 'should respond to :from and return self' do
@filter.from('src').should be(@filter)
end
it 'should respond to :from and add source directory' do
lambda { @filter.from('src') }.should change { @filter.sources }
end
it 'should respond to :from and add source directories' do
dirs = ['first', 'second']
@filter.from(*dirs)
@filter.sources.should include(*dirs.map { |dir| file(File.expand_path(dir)) })
end
it 'should return source directories as file task' do
@filter.from('src').sources.each { |source| source.should be_kind_of(Rake::FileTask) }
end
it 'should return source directories as expanded path' do
@filter.from('src').sources.each { |source| source.to_s.should eql(File.expand_path('src')) }
end
it 'should respond to :into and return self' do
@filter.into('target').should be(@filter)
end
it 'should respond to :into and set target directory' do
lambda { @filter.into('src') }.should change { @filter.target }
@filter.into('target').target.should be(file(File.expand_path('target')))
end
it 'should return target directory as file task' do
@filter.into('target').target.should be_kind_of(Rake::FileTask)
end
it 'should return target directory as expanded path' do
@filter.into('target').target.to_s.should eql(File.expand_path('target'))
end
it 'should respond to :using and return self' do
@filter.using().should be(@filter)
end
it 'should respond to :using and set mapping from the argument' do
mapping = { 'foo'=>'bar' }
lambda { @filter.using mapping }.should change { @filter.mapping }.to(mapping)
end
it 'should respond to :using and set mapping from the block' do
@filter.using { 5 }.mapping.call.should be(5)
end
it 'should respond to :include and return self' do
@filter.include('file').should be(@filter)
end
it 'should respond to :include and use these inclusion patterns' do
@filter.from('src').into('target').include('file2', 'file3').run
Dir['target/*'].sort.should eql(['target/file2', 'target/file3'])
end
it 'should respond to :include with regular expressions and use these inclusion patterns' do
@filter.from('src').into('target').include(/file[2|3]/).run
Dir['target/*'].sort.should eql(['target/file2', 'target/file3'])
end
it 'should respond to :include with a Proc and use these inclusion patterns' do
@filter.from('src').into('target').include(lambda {|file| file[-1, 1].to_i%2 == 0}).run
Dir['target/*'].sort.should eql(['target/file2', 'target/file4'])
end
it 'should respond to :include with a FileTask and use these inclusion patterns' do
@filter.from('src').into('target').include(file('target/file2'), file('target/file4')).run
Dir['target/*'].sort.should eql(['target/file2', 'target/file4'])
end
it 'should respond to :exclude and return self' do
@filter.exclude('file').should be(@filter)
end
it 'should respond to :exclude and use these exclusion patterns' do
@filter.from('src').into('target').exclude('file2', 'file3').run
Dir['target/*'].sort.should eql(['target/file1', 'target/file4'])
end
it 'should respond to :exclude with regular expressions and use these exclusion patterns' do
@filter.from('src').into('target').exclude(/file[2|3]/).run
Dir['target/*'].sort.should eql(['target/file1', 'target/file4'])
end
it 'should respond to :exclude with a Proc and use these exclusion patterns' do
@filter.from('src').into('target').exclude(lambda {|file| file[-1, 1].to_i%2 == 0}).run
Dir['target/*'].sort.should eql(['target/file1', 'target/file3'])
end
it 'should respond to :exclude with a FileTask and use these exclusion patterns' do
@filter.from('src').into('target').exclude(file('target/file1'), file('target/file3')).run
Dir['target/*'].sort.should eql(['target/file2', 'target/file4'])
end
it 'should respond to :exclude with a FileTask, use these exclusion patterns and depend on those tasks' do
file1 = false
file2 = false
@filter.from('src').into('target').exclude(file('target/file1').enhance { file1 = true }, file('target/file3').enhance {file2 = true }).run
Dir['target/*'].sort.should eql(['target/file2', 'target/file4'])
@filter.target.invoke
file1.should be_true
file2.should be_true
end
it 'should copy files over' do
@filter.from('src').into('target').run
Dir['target/*'].sort.each do |file|
read(file).should eql("#{File.basename(file)} raw")
end
end
it 'should copy dot files over' do
write 'src/.config', '# configuration'
@filter.from('src').into('target').run
read('target/.config').should eql('# configuration')
end
it 'should copy empty directories as well' do
mkpath 'src/empty'
@filter.from('src').into('target').run
File.directory?('target/empty').should be_true
end
it 'should copy files from multiple source directories' do
4.upto(6) { |i| write "src2/file#{i}", "file#{i} raw" }
@filter.from('src', 'src2').into('target').run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} raw")
end
Dir['target/*'].should include(*(1..6).map { |i| "target/file#{i}" })
end
it 'should copy files recursively' do
mkpath 'src/path1' ; write 'src/path1/left'
mkpath 'src/path2' ; write 'src/path2/right'
@filter.from('src').into('target').run
Dir['target/**/*'].should include(*(1..4).map { |i| "target/file#{i}" })
Dir['target/**/*'].should include('target/path1/left', 'target/path2/right')
end
it 'should apply hash mapping using Maven style' do
1.upto(4) { |i| write "src/file#{i}", "file#{i} with ${key1} and ${key2}" }
@filter.from('src').into('target').using('key1'=>'value1', 'key2'=>'value2').run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} with value1 and value2")
end
end
it 'should not apply filters to binary files' do
["jpg", "jpeg", "gif", "png"].each { |ext| write "images/file.#{ext}", 'something' }
filter = @filter.from('images').into('target').using('key1'=>'value1', 'key2'=>'value2')
filter.instance_variable_get("@mapper").should_not_receive(:maven_transform)
filter.run
end
it 'should apply hash mapping using Ant style' do
1.upto(4) { |i| write "src/file#{i}", "file#{i} with @key1@ and @key2@" }
@filter.from('src').into('target').using(:ant, 'key1'=>'value1', 'key2'=>'value2').run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} with value1 and value2")
end
end
it 'should apply hash mapping using Ruby style' do
1.upto(4) { |i| write "src/file#{i}", "file#{i} with \#{key1} and \#{key2}" }
@filter.from('src').into('target').using(:ruby, 'key1'=>'value1', 'key2'=>'value2').run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} with value1 and value2")
end
end
it 'should use erb when given a binding' do
1.upto(4) { |i| write "src/file#{i}", "file#{i} with <%= key1 %> and <%= key2 * 2 %>" }
key1 = 'value1'
key2 = 12
@filter.from('src').into('target').using(binding).run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} with value1 and 24")
end
end
it 'should apply hash mapping using erb' do
1.upto(4) { |i| write "src/file#{i}", "file#{i} with <%= key1 %> and <%= key2 * 2 %>" }
@filter.from('src').into('target').using(:erb, 'key1'=>'value1', 'key2'=> 12).run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} with value1 and 24")
end
end
it 'should use an object binding when using erb' do
1.upto(4) { |i| write "src/file#{i}", "file#{i} with <%= key1 %> and <%= key2 * 2 %>" }
obj = Struct.new(:key1, :key2).new('value1', 12)
@filter.from('src').into('target').using(:erb, obj).run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} with value1 and 24")
end
end
it 'should use a given block context when using erb' do
1.upto(4) { |i| write "src/file#{i}", "file#{i} with <%= key1 %> and <%= key2 * 2 %>" }
key1 = 'value1'
key2 = 12
@filter.from('src').into('target').using(:erb){}.run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} with value1 and 24")
end
end
it 'should using Maven mapper by default' do
@filter.using('key1'=>'value1', 'key2'=>'value2').mapper.should eql(:maven)
end
it 'should apply hash mapping with boolean values' do
write "src/file", "${key1} and ${key2}"
@filter.from('src').into('target').using(:key1=>true, :key2=>false).run
read("target/file").should eql("true and false")
end
it 'should apply hash mapping using regular expression' do
1.upto(4) { |i| write "src/file#{i}", "file#{i} with #key1# and #key2#" }
@filter.from('src').into('target').using(/#(.*?)#/, 'key1'=>'value1', 'key2'=>'value2').run
Dir['target/*'].each do |file|
read(file).should eql("#{File.basename(file)} with value1 and value2")
end
end
it 'should apply proc mapping' do
@filter.from('src').into('target').using { |file, content| 'proc mapped' }.run
Dir['target/*'].each do |file|
read(file).should eql('proc mapped')
end
end
it 'should apply proc mapping with relative file name' do
@filter.from('src').into('target').using { |file, content| file.should =~ /^file\d$/ }.run
end
it 'should apply proc mapping with file content' do
@filter.from('src').into('target').using { |file, content| content.should =~ /^file\d raw/ }.run
end
it 'should make target directory' do
lambda { @filter.from('src').into('target').run }.should change { File.exist?('target') }.to(true)
end
it 'should touch target directory' do
mkpath 'target' ; File.utime @early, @early, 'target'
@filter.from('src').into('target').run
File.stat('target').mtime.should be_within(10).of(Time.now)
end
it 'should not touch target directory unless running' do
mkpath 'target' ; File.utime @early, @early, 'target'
@filter.from('src').into('target').exclude('*').run
File.mtime('target').should be_within(10).of(@early)
end
it 'should run only on new files' do
# Make source files older so they're not copied twice.
Dir['src/**/*'].each { |file| File.utime(@early, @early, file) }
@filter.from('src').into('target').run
@filter.from('src').into('target').using { |file, content| file.should eql('file2') }.run
end
it 'should return true when run copies any files' do
@filter.from('src').into('target').run.should be(true)
end
it 'should return false when run does not copy any files' do
# Make source files older so they're not copied twice.
Dir['src/**/*'].each { |file| File.utime(@early, @early, file) }
@filter.from('src').into('target').run
@filter.from('src').into('target').run.should be(false)
end
it 'should fail if source directory doesn\'t exist' do
lambda { Filter.new.from('srced').into('target').run }.should raise_error(RuntimeError, /doesn't exist/)
end
it 'should fail is target directory not set' do
lambda { Filter.new.from('src').run }.should raise_error(RuntimeError, /No target directory/)
end
it 'should copy read-only files as writeable' do
Dir['src/*'].each { |file| File.chmod(0444, file) }
@filter.from('src').into('target').run
Dir['target/*'].sort.each do |file|
File.readable?(file).should be_true
File.writable?(file).should be_true
(File.stat(file).mode & 0o200).should == 0o200
end
end
it 'should preserve mode bits except readable' do
mode = 0o600
Dir['src/*'].each { |file| File.chmod(mode, file) }
@filter.from('src').into('target').run
Dir['target/*'].sort.each do |file|
(File.stat(file).mode & mode).should == mode
end
end
end
describe Filter::Mapper do
module MooMapper
def moo_config(*args, &block)
raise ArgumentError, "Expected moo block" unless block_given?
{ :moos => args, :callback => block }
end
def moo_transform(content, path = nil)
content.gsub(/moo+/i) do |str|
moos = yield :moos # same than config[:moos]
moo = moos[str.size - 3] || str
config[:callback].call(moo)
end
end
end
it 'should allow plugable mapping types' do
mapper = Filter::Mapper.new.extend(MooMapper)
mapper.using(:moo, 'ooone', 'twoo') do |str|
i = nil; str.capitalize.gsub(/\w/) { |s| s.send( (i = !i) ? 'upcase' : 'downcase' ) }
end
mapper.transform('Moo cow, mooo cows singing mooooo').should == 'OoOnE cow, TwOo cows singing MoOoOo'
end
end
describe Buildr.method(:options) do
it 'should return an Options object' do
options.should be_kind_of(Options)
end
it 'should return an Options object each time' do
options.should be(options)
end
it 'should return the same Options object when called on Object, Buildr or Project' do
options.should be(Buildr.options)
define('foo') { options.should be(Buildr.options) }
end
end
describe Buildr::Options, 'proxy.exclude' do
before do
options.proxy.http = 'http://myproxy:8080'
options.proxy.exclude.clear
@domain = 'domain'
@host = "host.#{@domain}"
@uri = URI("http://#{@host}")
@no_proxy_args = [@host, 80]
@proxy_args = @no_proxy_args + ['myproxy', 8080, nil, nil]
@http = double('http')
@http.stub(:request).and_return(Net::HTTPNotModified.new(nil, nil, nil))
end
it 'should be an array' do
options.proxy.exclude.should be_empty
options.proxy.exclude = @domain
options.proxy.exclude.should include(@domain)
end
it 'should support adding to array' do
options.proxy.exclude << @domain
options.proxy.exclude.should include(@domain)
end
it 'should support resetting array' do
options.proxy.exclude = @domain
options.proxy.exclude = nil
options.proxy.exclude.should be_empty
end
it 'should use proxy when not excluded' do
Net::HTTP.should_receive(:new).with(*@proxy_args).and_return(@http)
@uri.read :proxy=>options.proxy
end
it 'should use proxy unless excluded' do
options.proxy.exclude = "not.#{@domain}"
Net::HTTP.should_receive(:new).with(*@proxy_args).and_return(@http)
@uri.read :proxy=>options.proxy
end
it 'should not use proxy if excluded' do
options.proxy.exclude = @host
Net::HTTP.should_receive(:new).with(*@no_proxy_args).and_return(@http)
@uri.read :proxy=>options.proxy
end
it 'should support multiple host names' do
options.proxy.exclude = ['optimus', 'prime']
Net::HTTP.should_receive(:new).with('optimus', 80).and_return(@http)
URI('http://optimus').read :proxy=>options.proxy
Net::HTTP.should_receive(:new).with('prime', 80).and_return(@http)
URI('http://prime').read :proxy=>options.proxy
Net::HTTP.should_receive(:new).with('bumblebee', *@proxy_args[1..-1]).and_return(@http)
URI('http://bumblebee').read :proxy=>options.proxy
end
it 'should support glob pattern on host name' do
options.proxy.exclude = "*.#{@domain}"
Net::HTTP.should_receive(:new).with(*@no_proxy_args).and_return(@http)
@uri.read :proxy=>options.proxy
end
end
describe Hash, '::from_java_properties' do
it 'should return hash' do
hash = Hash.from_java_properties(<<-PROPS)
name1=value1
name2=value2
PROPS
hash.should == {'name1'=>'value1', 'name2'=>'value2'}
end
it 'should ignore comments and empty lines' do
hash = Hash.from_java_properties(<<-PROPS)
name1=value1
name2=value2
PROPS
hash.should == {'name1'=>'value1', 'name2'=>'value2'}
end
it 'should allow multiple lines' do
hash = Hash.from_java_properties(<<-PROPS)
name1=start\
end
name2=first\
second\
third
PROPS
hash.should == {'name1'=>'start end', 'name2'=>'first second third'}
end
it 'should handle \t, \r, \n and \f' do
hash = Hash.from_java_properties(<<-PROPS)
name1=with\tand\r
name2=with\\nand\f
name3=double\\\\hash
PROPS
hash.should == {'name1'=>"with\tand", 'name2'=>"with\nand\f", 'name3'=>'double\hash'}
end
it 'should ignore whitespace' do
hash = Hash.from_java_properties('name1 = value1')
hash.should == {'name1'=>'value1'}
end
end
describe Hash, '#to_java_properties' do
it 'should return name/value pairs' do
props = {'name1'=>'value1', 'name2'=>'value2'}.to_java_properties
props.split("\n").size.should be(2)
props.split("\n").should include('name1=value1')
props.split("\n").should include('name2=value2')
end
it 'should handle \t, \r, \n and \f' do
props = {'name1'=>"with\tand\r", 'name2'=>"with\nand\f", 'name3'=>'double\hash'}.to_java_properties
props.split("\n").should include("name1=with\\tand\\r")
props.split("\n").should include("name2=with\\nand\\f")
props.split("\n").should include("name3=double\\\\hash")
end
end