You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by as...@apache.org on 2008/01/05 11:07:08 UTC

svn commit: r609118 [2/2] - in /incubator/buildr/trunk: ./ lib/ lib/core/ lib/java/ spec/

Modified: incubator/buildr/trunk/spec/compile_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/compile_spec.rb?rev=609118&r1=609117&r2=609118&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/compile_spec.rb (original)
+++ incubator/buildr/trunk/spec/compile_spec.rb Sat Jan  5 02:06:53 2008
@@ -1,654 +1,500 @@
-require File.join(File.dirname(__FILE__), 'sandbox')
+require File.join(File.dirname(__FILE__), 'spec_helpers')
 
 
-describe Buildr::CompileTask do
-  before do
-    # Test files to compile and target directory to compile into.
-    @src_dir = 'src/java'
-    @sources = ['Test1.java', 'Test2.java'].map { |f| File.join(@src_dir, f) }.
+module CompilerHelper
+  def compile_task
+    @compile_task ||= define('foo').compile.using(:javac)
+  end
+
+  def sources
+    @sources ||= ['Test1.java', 'Test2.java'].map { |f| File.join('src/java', f) }.
       each { |src| write src, "class #{src.pathmap('%n')} {}" }
-    # You can supply a relative path, but a full path is used everywhere else.
-    @target = File.expand_path('classes')
-    @compile = define('foo').compile.using(:javac)
   end
 
+  def jars
+    @jars ||= begin
+      write 'src/main/java/Dependency.java', 'class Dependency { }'
+      define 'jars', :version=>'1.0' do
+        compile.into('dependency')
+        package(:jar, :id=>'jar1')
+        package(:jar, :id=>'jar2')
+      end
+      project('jars').packages.each(&:invoke).map(&:to_s)
+    end
+  end
+end
+
+
+describe Buildr::CompileTask do
+  include CompilerHelper
+
   it 'should respond to from() and return self' do
-    @compile.from(@sources).should be(@compile)
+    compile_task.from(sources).should be(compile_task)
   end
 
   it 'should respond to from() and add sources' do
-    @compile.from @sources, @src_dir
-    @compile.sources.should eql(@sources + [@src_dir])
+    compile_task.from sources, File.dirname(sources.first)
+    compile_task.sources.should eql(sources + [File.dirname(sources.first)])
   end
 
   it 'should respond to with() and return self' do
-    @compile.with('test.jar').should be(@compile)
+    compile_task.with('test.jar').should be(compile_task)
   end
 
   it 'should respond to with() and add classpath dependencies' do
     jars = (1..3).map { |i| "test#{i}.jar" }
-    @compile.with *jars
-    @compile.classpath.should eql(artifacts(jars))
+    compile_task.with *jars
+    compile_task.classpath.should eql(artifacts(jars))
   end
 
   it 'should respond to into() and return self' do
-    @compile.into(@target).should be(@compile)
+    compile_task.into('code').should be(compile_task)
   end
 
   it 'should respond to into() and create file task' do
-    @compile.from(@sources).into(@target)
-    lambda { file(@target).invoke }.should run_task('foo:compile')
+    compile_task.from(sources).into('code')
+    lambda { file('code').invoke }.should run_task('foo:compile')
   end
 
   it 'should respond to using() and return self' do
-    @compile.using(:source=>'1.4').should eql(@compile)
+    compile_task.using(:source=>'1.4').should eql(compile_task)
   end
 
-  it 'should respond to using() and set value options' do
-    @compile.using(:source=>'1.4', 'target'=>'1.5')
-    @compile.options.source.should eql('1.4')
-    @compile.options.target.should eql('1.5')
+  it 'should respond to using() and set options' do
+    compile_task.using(:source=>'1.4', 'target'=>'1.5')
+    compile_task.options.source.should eql('1.4')
+    compile_task.options.target.should eql('1.5')
   end
 
-  it 'should compile only once' do
-    @compile.from(@sources).into(@target)
-    lambda { file(@target).invoke }.should run_task('foo:compile')
-    lambda { @compile.invoke }.should_not run_task('foo:compile')
-  end
-
-  it 'should compile if there are source files to compile' do
-    lambda { @compile.from(@sources).into(@target).invoke }.should run_task('foo:compile')
+  it 'should attempt to identify compiler' do
+    Compiler.compilers.first.should_receive(:identify?).at_least(:once)
+    define('foo')
   end
 
-  it 'should compile if directory has source files to compile' do
-    lambda { @compile.from(@src_dir).into(@target).invoke }.should run_task('foo:compile')
+  it 'should only support existing compilers' do
+    lambda { define('foo') { compile.using(:unknown) } }.should raise_error(ArgumentError, /unknown compiler/i)
   end
 
-  it 'should timestamp target directory if specified' do
-    time = Time.now - 10
-    mkpath @target
-    File.utime(time, time, @target)
-    @compile.into(@target).timestamp.should be_close(time, 1)
+  it 'should only allow setting the compiler once' do
+    lambda { define('foo') { compile.using(:javac).using(:scalac) } }.should raise_error(RuntimeError, /already selected/i)
   end
 end
 
 
-describe Buildr::CompileTask, 'compiler' do
-  it 'should be nil if not identified' do
+describe Buildr::CompileTask, '#compiler' do
+  it 'should be nil if no compiler identifier' do
     define('foo').compile.compiler.should be_nil
   end
   
-  it 'should be set by calling using' do
+  it 'should return the selected compiler' do
     define('foo') { compile.using(:javac) }
     project('foo').compile.compiler.should eql(:javac)
   end
 
-  it 'should only support existing compiler' do
-    lambda { define('foo') { compile.using(:unknown) } }.should raise_error(ArgumentError, /unknown compiler/i)
-  end
-
-  it 'should only allow setting the compiler once' do
-    lambda { define('foo') { compile.using(:javac).using(:scalac) } }.should raise_error(RuntimeError, /already selected/i)
-  end
-
-  it 'should identify itself from source directories' do
-    write 'src/main/java/Test.java', 'class Test {}' 
-    define('foo').compile.compiler.should eql(:javac)
+  it 'should attempt to identify compiler if sources are specified' do
+    define 'foo' do
+      Compiler.compilers.first.should_receive(:identify?)
+      compile.from('sources').compiler
+    end
   end
+end
 
-  it 'should identify itself form project paths' do
-    write 'bar/Test.java', 'class Test {}' 
-    define('foo').compile.compiler.should be_nil
-    define('bar') { compile.from 'bar' }.compile.compiler.should eql(:javac)
-  end
 
-  it 'should have no source/target mapping unless selected' do
-    define('foo')
-    project('foo').compile.sources.should be_empty
-    project('foo').compile.target.should be_nil
+describe Buildr::CompileTask, '#language' do
+  it 'should be nil if no compiler identifier' do
+    define('foo').compile.language.should be_nil
   end
-
-  it 'should set target directory' do
+  
+  it 'should return the appropriate language' do
     define('foo') { compile.using(:javac) }
-    project('foo').compile.sources.should be_empty
-    project('foo').compile.target.to_s.should eql(File.expand_path('target/classes'))
-  end
-
-  it 'should not over-ride exisitng source and target directories' do
-    define('foo') { compile.from('foo/java').into('classes').using(:javac) }
-    project('foo').compile.sources.should eql(['foo/java'])
-    project('foo').compile.target.to_s.should eql(File.expand_path('classes'))
+    project('foo').compile.language.should eql(:java)
   end
-
 end
 
 
-describe Buildr::CompileTask, 'sources' do
-  before do
-    @src_dir = 'src/java'
-    @sources = ['Test1.java', 'Test2.java'].map { |f| File.join(@src_dir, f) }.
-      each { |src| write src, "class #{src.pathmap('%n')} {}" }
-    @compile = define('foo').compile.using(:javac)
-    # Test files to compile and target directory to compile into.
+describe Buildr::CompileTask, '#sources' do
+  include CompilerHelper
+
+  it 'should be empty if no sources in default directory' do
+    compile_task.sources.should be_empty
   end
 
-  it 'should be empty' do
-    @compile.sources.should be_empty
+  it 'should point to default directory if it contains sources' do
+    write 'src/main/java', ''
+    compile_task.sources.first.should point_to_path('src/main/java')
   end
 
   it 'should be an array' do
-    @compile.sources += @sources
-    @compile.sources.should eql(@sources)
+    compile_task.sources += sources
+    compile_task.sources.should eql(sources)
   end
 
   it 'should allow files' do
-    @compile.from(@sources).into('classes').invoke
-    @sources.each { |src| file(src.pathmap('classes/%n.class')).should exist }
+    compile_task.from(sources).into('classes').invoke
+    sources.each { |src| file(src.pathmap('classes/%n.class')).should exist }
   end
 
   it 'should allow directories' do
-    @compile.from(@src_dir).into('classes').invoke
-    @sources.each { |src| file(src.pathmap('classes/%n.class')).should exist }
-  end
-
-  it 'should require file or directory to exist' do
-    lambda { @compile.from('empty').into('classes').invoke }.should raise_error(RuntimeError, /Don't know how to build/)
-  end
-
-  it 'should require at least one file to compile' do
-    mkpath 'empty'
-    lambda { @compile.from('empty').into('classes').invoke }.should_not run_task('foo:compile')
+    compile_task.from(File.dirname(sources.first)).into('classes').invoke
+    sources.each { |src| file(src.pathmap('classes/%n.class')).should exist }
   end
 
   it 'should allow tasks' do
-    lambda { @compile.from(file(@src_dir)).into('classes').invoke }.should run_task('foo:compile')
+    lambda { compile_task.from(file(sources.first)).into('classes').invoke }.should run_task('foo:compile')
   end
 
   it 'should act as prerequisites' do
     file('src2') { |task| task('prereq').invoke ; mkpath task.name }
-    lambda { @compile.from('src2').into('classes').invoke }.should run_task('prereq')
-  end
-
-  it 'should force compilation if no bytecode' do
-    lambda { @compile.from(@sources).into(Dir.pwd).invoke }.should run_task('foo:compile')
-  end
-
-  it 'should force compilation if newer than bytecode' do
-    # Simulate class files that are older than source files.
-    time = Time.now
-    @sources.each { |src| File.utime(time + 1, time + 1, src) }.
-      map { |src| src.pathmap('%n').ext('.class') }.
-      each { |kls| write kls ; File.utime(time, time, kls) }
-    lambda { @compile.from(@sources).into(Dir.pwd).invoke }.should run_task('foo:compile')
-  end
-
-  it 'should not force compilation if older than bytecode' do
-    # When everything has the same timestamp, nothing is compiled again.
-    time = Time.now
-    @sources.each { |src| File.utime(time, time, src) }.
-      map { |src| src.pathmap('%n').ext('.class') }.
-      each { |kls| write kls ; File.utime(time, time, kls) }
-    lambda { @compile.from(@sources).into(Dir.pwd).invoke }.should_not run_task('foo:compile')
+    lambda { compile_task.from('src2').into('classes').invoke }.should run_task('prereq')
   end
 end
 
 
-describe Buildr::CompileTask, 'dependencies' do
-  before do
-    @compile = define('foo').compile.using(:javac)
-    @sources = ['Test1.java', 'Test2.java'].
-      each { |src| write src, "class #{src.pathmap('%n')} {}" }
-    define('to_jar').compile.using(:javac).from(@sources).into(Dir.pwd).invoke
-    @jars = [ 'test1.jar', 'test2.jar' ]. # javac can't cope with empty jars
-     each { |jar| zip(jar).include(@sources.map { |src| src.ext('class') }).invoke }
-  end
+describe Buildr::CompileTask, '#dependencies' do
+  include CompilerHelper
 
   it 'should be empty' do
-    @compile.dependencies.should be_empty
+    compile_task.dependencies.should be_empty
   end
 
   it 'should be an array' do
-    @compile.dependencies += @jars
-    @compile.dependencies.should eql(@jars)
+    compile_task.dependencies += jars
+    compile_task.dependencies.should eql(jars)
   end
 
   it 'should allow files' do
-    @compile.from(@sources).with(@jars).into('classes').invoke
-    @sources.each { |src| file(src.pathmap('classes/%n.class')).should exist }
+    compile_task.from(sources).with(jars).into('classes').invoke
+    sources.each { |src| file(src.pathmap('classes/%n.class')).should exist }
   end
 
   it 'should allow tasks' do
-    @compile.from(@sources).with(file(@jars.first)).into('classes').invoke
+    compile_task.from(sources).with(file(jars.first)).into('classes').invoke
   end
 
   it 'should allow artifacts' do
-    artifact('group:id:jar:1.0') { |task| mkpath File.dirname(task.to_s) ; cp @jars.first, task.to_s }
-    @compile.from(@sources).with('group:id:jar:1.0').into('classes').invoke
+    artifact('group:id:jar:1.0') { |task| mkpath File.dirname(task.to_s) ; cp jars.first.to_s, task.to_s }
+    compile_task.from(sources).with('group:id:jar:1.0').into('classes').invoke
   end
 
   it 'should allow projects' do
     define('bar', :version=>'1', :group=>'self') { package :jar }
-    @compile.with project('bar')
-    @compile.dependencies.should eql(project('bar').packages)
-  end
-
-  it 'should require file to exist' do
-    lambda { @compile.from(@sources).with('no-such.jar').into('classes').invoke }.should \
-      raise_error(RuntimeError, /Don't know how to build/)
-  end
-
-  it 'should act as prerequisites' do
-    file(File.expand_path('no-such.jar')) { |task| task('prereq').invoke }
-    lambda { @compile.from(@sources).with('no-such.jar').into('classes').invoke }.should run_tasks(['prereq', 'foo:compile'])
-  end
-
-  it 'should include as classpath dependency' do
-    src = file('TestOfTest1.java') { |task| write task.to_s, 'class TestOfTest1 { Test1 _var; }' }
-    lambda { @compile.from(src).with(@jars).into('classes').invoke }.should run_task('foo:compile')
-  end
-
-  it 'should force compilation if newer than bytecode' do
-    # On my machine the times end up the same, so need to push sources in the past.
-    @sources.each { |src| File.utime(Time.now - 10, Time.now - 10, src.ext('.class')) }
-    lambda { @compile.from(@sources).with(@jars).into(Dir.pwd).invoke }.should run_task('foo:compile')
+    compile_task.with project('bar')
+    compile_task.dependencies.should eql(project('bar').packages)
   end
 
-  it 'should not force compilation if not newer than bytecode' do
-    # Push sources/classes into the future so they're both newer than classpath, but not each other.
-    @sources.map { |src| [src, src.ext('.class')] }.flatten.each { |f| File.utime(Time.now + 10, Time.now + 10, f) }
-    lambda { @compile.from(@sources).with(@jars).into(Dir.pwd).invoke }.should_not run_task('foo:compile')
-  end 
-
   it 'should be accessible as classpath' do
-    lambda { @compile.classpath = @jars }.should change(@compile, :dependencies).to(@jars)
-    lambda { @compile.dependencies = [] }.should change(@compile, :classpath).to([])
+    lambda { compile_task.classpath = jars }.should change(compile_task, :dependencies).to(jars)
+    lambda { compile_task.dependencies = [] }.should change(compile_task, :classpath).to([])
   end
 
 end
 
 
-describe Buildr::CompileTask, 'target' do
-  before do
-    @compile = define('foo').compile.using(:javac)
-    write 'Test.java', 'class Test {}'
-  end
+describe Buildr::CompileTask, '#target' do
+  include CompilerHelper
 
   it 'should be a file task' do
-    @compile.from('Test.java').into('classes')
-    @compile.target.should be_kind_of(Rake::FileTask)
-  end
-
-  it 'should set to full path' do
-    @compile.into('classes').target.to_s.should eql(File.expand_path('classes'))
+    compile_task.from(@sources).into('classes')
+    compile_task.target.should be_kind_of(Rake::FileTask)
   end
 
   it 'should accept a task' do
-    task = file(File.expand_path('classes'))
-    @compile.into(task).target.should be(task)
+    task = file('classes')
+    compile_task.into(task).target.should be(task)
   end
 
   it 'should create dependency in file task when set' do
-    @compile.from('Test.java').into('classes')
-    lambda { file(File.expand_path('classes')).invoke }.should run_task('foo:compile')
+    compile_task.from(sources).into('classes')
+    lambda { file('classes').invoke }.should run_task('foo:compile')
   end
+end
 
-  it 'should exist after compilation' do
-    lambda { @compile.from('Test.java').into('classes').invoke }.should run_task('foo:compile')
-    FileList['classes/*'].should == ['classes/Test.class']
-  end
 
-  it 'should be touched if anything compiled' do
-    mkpath 'classes' ; File.utime(Time.now - 100, Time.now - 100, 'classes')
-    lambda { @compile.from('Test.java').into('classes').invoke }.should run_task('foo:compile')
-    File.stat('classes').mtime.should be_close(Time.now, 2)
-  end
+describe Buildr::CompileTask, '#options' do
+  include CompilerHelper
 
-  it 'should not be touched if failed to compile' do
-    mkpath 'classes' ; File.utime(Time.now - 10, Time.now - 10, 'classes')
-    Java.should_receive(:javac).and_raise
-    lambda { @compile.from('Test.java').into('classes').invoke }.should raise_error
-    File.stat('classes').mtime.should be_close(Time.now - 10, 2)
+  it 'should have getter and setter methods' do
+    compile_task.options.foo = 'bar'
+    compile_task.options.foo.should eql('bar')
   end
-end
-
-
-=begin
-describe Buildr::CompileTask, 'options' do
-  before do
-    @default = {:debug=>true, :deprecation=>false, :lint=>false, :warnings=>false, :other=>nil, :source=>nil, :target=>nil}
+  
+  it 'should have bracket accessors' do
+    compile_task.options[:foo] = 'bar'
+    compile_task.options[:foo].should eql('bar')
   end
 
-  it 'should set warnings option to false by default' do
-    define('foo').compile.options.warnings.should be_false
+  it 'should map from bracket accessor to get/set accessor' do
+    compile_task.options[:foo] = 'bar'
+    compile_task.options.foo.should eql('bar')
   end
 
-  it 'should set wranings option to true when running with --verbose option' do
-    verbose true
-    define('foo').compile.options.warnings.should be_true
+  it 'should be independent of parent' do
+    define 'foo' do
+      compile.using(:javac, :source=>'1.4')
+      define 'bar' do
+        compile.using(:javac, :source=>'1.5')
+      end
+    end
+    project('foo').compile.options.source.should eql('1.4')
+    project('foo:bar').compile.options.source.should eql('1.5')
   end
+end
 
-  it 'should use -nowarn argument unless warnings is true' do
-    define('foo').compile.using(:warnings=>true).javac_args.should_not include('-nowarn')
-    define('bar').compile.using(:warnings=>false).javac_args.should include('-nowarn')
-  end
 
-  it 'should use -verbose argument only when running with --trace option' do
-    define('foo').compile.javac_args.should_not include('-verbose')
-    Rake.application.options.trace = true
-    define('bar').compile.javac_args.should include('-verbose')
-  end
+describe Buildr::CompileTask, '#invoke' do
+  include CompilerHelper
 
-  it 'should set debug option to true by default' do
-    define('foo').compile.options.debug.should be_true
+  it 'should compile into target directory' do
+    compile_task.from(sources).into('code').invoke
+    Dir['code/*.class'].should_not be_empty
   end
 
-  it 'should set debug option to false based on Buildr.options' do
-    Buildr.options.debug = false
-    define('foo').compile.options.debug.should be_false
+  it 'should compile only once' do
+    compile_task.from(sources)
+    lambda { compile_task.target.invoke }.should run_task('foo:compile')
+    lambda { compile_task.invoke }.should_not run_task('foo:compile')
   end
 
-  it 'should set debug option to false based on debug environment variable' do
-    ENV['debug'] = 'no'
-    define('foo').compile.options.debug.should be_false
+  it 'should compile if there are source files to compile' do
+    lambda { compile_task.from(sources).invoke }.should run_task('foo:compile')
   end
 
-  it 'should set debug option to false based on DEBUG environment variable' do
-    ENV['DEBUG'] = 'no'
-    define('foo').compile.options.debug.should be_false
+  it 'should not compile unless there are source files to compile' do
+    lambda { compile_task.invoke }.should_not run_task('foo:compile')
   end
 
-  it 'should use -g argument only when running in debug mode' do
-    define('foo').compile.using(:debug=>true).javac_args.should include('-g')
-    define('bar').compile.using(:debug=>false).javac_args.should_not include('-g')
+  it 'should require source file or directory to exist' do
+    lambda { compile_task.from('empty').into('classes').invoke }.should raise_error(RuntimeError, /Don't know how to build/)
   end
 
-  it 'should set deprecation option to false by default' do
-    define('foo').compile.options.deprecation.should be_false
+  it 'should run all source files as prerequisites' do
+    file(mkpath('src')).should_receive :invoke_prerequisites
+    compile_task.from('src').invoke
   end
 
-  it 'should use -deprecation argument if deprecation option set' do
-    define('foo').compile.using(:deprecation=>true).javac_args.should include('-deprecation')
-    define('bar').compile.using(:deprecation=>false).javac_args.should_not include('-deprecation')
+  it 'should require dependencies to exist' do
+    lambda { compile_task.from(sources).with('no-such.jar').into('classes').invoke }.should \
+      raise_error(RuntimeError, /Don't know how to build/)
   end
 
-  it 'should not set source and target options by default' do
-    define('foo').compile.options.source.should be_nil
-    define('bar').compile.options.target.should be_nil
+  it 'should run all dependencies as prerequisites' do
+    file(File.expand_path('no-such.jar')) { |task| task('prereq').invoke }
+    lambda { compile_task.from(sources).with('no-such.jar').into('classes').invoke }.should run_tasks(['prereq', 'foo:compile'])
   end
 
-  it 'should use -source nn argument if source option set' do
-    define('foo').compile.javac_args.should_not include('-source')
-    define('bar').compile.using(:source=>'1.5').javac_args.should include('-source', '1.5')
+  it 'should force compilation if no target' do
+    lambda { compile_task.from(sources).invoke }.should run_task('foo:compile')
   end
 
-  it 'should use -target nn argument if target option set' do
-    define('foo').compile.javac_args.should_not include('-target')
-    define('bar').compile.using(:target=>'1.5').javac_args.should include('-target', '1.5')
+  it 'should force compilation if target empty' do
+    mkpath compile_task.target.to_s
+    lambda { compile_task.from(sources).invoke }.should run_task('foo:compile')
   end
 
-  it 'should set lint option false by default' do
-    define('foo').compile.options.lint.should be_false
+  it 'should force compilation if sources newer than compiled' do
+    # Simulate class files that are older than source files.
+    time = Time.now
+    sources.each { |src| File.utime(time + 1, time + 1, src) }
+    sources.map { |src| src.pathmap("#{compile_task.target}/%n.class") }.
+      each { |kls| write kls ; File.utime(time, time, kls) }
+    lambda { compile_task.from(sources).invoke }.should run_task('foo:compile')
   end
 
-  it 'should use -Xlint argument based on lint option' do
-    define('foo').compile.javac_args.should_not include('-Xlint')
-    define('true').compile.using(:lint=>true).javac_args.should include('-Xlint')
-    define('all').compile.using(:lint=>'all').javac_args.should include('-Xlint:all')
-    define('select').compile.using(:lint=>['path', 'serial']).javac_args.should include('-Xlint:path,serial')
+  it 'should not force compilation if sources older than compiled' do
+    # When everything has the same timestamp, nothing is compiled again.
+    time = Time.now
+    sources.map { |src| src.pathmap("#{compile_task.target}/%n.class") }.
+      each { |kls| write kls ; File.utime(time, time, kls) }
+    lambda { compile_task.from(sources).invoke }.should_not run_task('foo:compile')
   end
 
-  it 'should pass other option as remaining arguments' do
-    define('none').compile.javac_args.should eql(['-nowarn', '-g'])
-    define('one').compile.using(:other=>'-foo').javac_args.should include('-foo')
-    define('array').compile.using(:other=>['-foo', '-bar']).javac_args.should include('-foo', '-bar')
-    define('hash').compile.using(:other=>{ 'foo'=>'name', 'bar'=>'value'}).javac_args.should include('-foo', 'name', '-bar', 'value')
+  it 'should force compilation if dependencies newer than compiled' do
+    # On my machine the times end up the same, so need to push dependencies in the past.
+    time = Time.now
+    sources.map { |src| src.pathmap("#{compile_task.target}/%n.class") }.
+      each { |kls| write kls ; File.utime(time, time, kls) }
+    jars.each { |jar| File.utime(time + 1, time + 1, jar) }
+    lambda { compile_task.from(sources).with(jars).invoke }.should run_task('foo:compile')
   end
 
-  it 'should accept unknown option'
-  it 'should complain about unknown option'
-
-
-  it "should pass to javac" do
-    src = "Test.java"
-    write src, "class Test {}"
-    Java.should_receive(:javac) do |*args|
-      args.last[:javac_args].should include("-nowarn")
-      args.last[:javac_args].join(" ").should include("-source 1.5")
-    end
-    CompileTask.define_task("compiling").from(src).into("classes").using(:source=>"1.5").invoke
+  it 'should not force compilation if dependencies older than compiled' do
+    time = Time.now
+    sources.map { |src| src.pathmap("#{compile_task.target}/%n.class") }.
+      each { |kls| write kls ; File.utime(time, time, kls) }
+    jars.each { |jar| File.utime(time - 1, time - 1, jar) }
+    lambda { compile_task.from(sources).with(jars).invoke }.should_not run_task('foo:compile')
   end
 
-  after do
-    Buildr.options.debug = nil
-    ENV.delete "debug"
-    ENV.delete "DEBUG"
+  it 'should timestamp target directory if specified' do
+    time = Time.now - 10
+    mkpath compile_task.target.to_s
+    File.utime(time, time, compile_task.target.to_s)
+    compile_task.timestamp.should be_close(time, 1)
+  end
+
+  it 'should touch target if anything compiled' do
+    mkpath compile_task.target.to_s
+    File.utime(Time.now - 1, Time.now - 1, compile_task.target.to_s)
+    compile_task.from(sources).invoke
+    File.stat(compile_task.target.to_s).mtime.should be_close(Time.now, 2)
+  end
+
+  it 'should not touch target if nothing compiled' do
+    mkpath compile_task.target.to_s
+    File.utime(Time.now - 1, Time.now - 1, compile_task.target.to_s)
+    Java.should_receive(:javac).and_raise(RuntimeError)
+    compile_task.from(sources).invoke rescue nil
+    File.stat(compile_task.target.to_s).mtime.should be_close(Time.now, 2)
   end
 end
-=end
 
 
-def accessor_task_spec(name)
-  it 'should be a task' do
-    define 'foo'
-    project('foo').send(name).should be_a_kind_of(Rake::Task)
+describe 'accessor task', :shared=>true do
+  it 'should return a task' do
+    define('foo').send(@task_name).should be_kind_of(Rake::Task)
   end
 
   it 'should always return the same task' do
-    task = nil
-    define('foo') { task = self.send(name) }
-    project('foo').send(name).should be(task)
+    task_name, task = @task_name, nil
+    define('foo') { task = self.send(task_name) }
+    project('foo').send(task_name).should be(task)
   end
 
-  it 'should be unique for project' do
+  it 'should be unique for the project' do
     define('foo') { define 'bar' }
-    project('foo').send(name).should_not be(project('foo:bar').send(name))
+    project('foo').send(@task_name).should_not eql(project('foo:bar').send(@task_name))
   end
 
-  it 'should have a project:#{name} name' do
+  it 'should be named after the project' do
     define('foo') { define 'bar' }
-    project('foo').send(name).name.should eql("foo:#{name}")
-    project('foo:bar').send(name).name.should eql("foo:bar:#{name}")
+    project('foo:bar').send(@task_name).name.should eql("foo:bar:#{@task_name}")
   end
-
 end
 
 
 describe Project, '#compile' do
-  accessor_task_spec :compile
-
-  def make_sources()
-    write 'src/main/java/Test.java', 'class Test {}'
-  end
-
-  it 'should be a compile task' do
-    define 'foo'
-    project('foo').compile.should be_instance_of(CompileTask)
-  end
-
-  it 'should inherit options from parent' do
-    define 'foo' do
-      compile.options.source = '1.5'
-      define 'bar'
-    end
-    project('foo:bar').compile.options.source = '1.5'
-  end
-
-  it 'should accept options independently of parent' do
-    define 'foo' do
-      compile.options.source = '1.5'
-      define 'bar' do
-        compile.options.source = '1.6'
-      end
-    end
-    project('foo').compile.options.source = '1.4'
-    project('foo:bar').compile.options.source = '1.5'
-  end
+  before { @task_name = 'compile' }
+  it_should_behave_like 'accessor task'
 
-  it 'should not inherit options from local task' do
-    class << task('compile')
-      def options ; fail ; end
-    end
-    lambda { define('foo') { compile.options } }.should_not raise_error
+  it 'should return a compile task' do
+    define('foo').compile.should be_instance_of(CompileTask)
   end
 
-  it 'should accept source files' do
+  it 'should accept sources and add to source list' do
     define('foo') { compile('file1', 'file2') }
-    project('foo').compile.sources.should eql(['file1', 'file2'])
-  end
-
-  it 'should accept block' do
-    make_sources
-    task 'action'
-    define('foo') { compile { task('action').invoke } }
-    lambda { project('foo').compile.invoke }.should run_tasks(['foo:compile', 'action'])
+    project('foo').compile.sources.should include('file1', 'file2')
   end
 
-  it 'should set source directory to src/main/java' do
-    make_sources
-    define 'foo'
-    project('foo').compile.sources.should include(File.expand_path('src/main/java'))  
-  end
-
-  it 'should not set source directory unless exists' do
-    define 'foo'
-    project('foo').compile.sources.should be_empty
-  end
-
-  it 'should set target directory to target/classes' do
-    make_sources
-    define 'foo'
-    project('foo').compile.target.to_s.should eql(File.expand_path('target/classes'))
-  end
-
-  it 'should create file task for target directory' do
-    make_sources
-    define 'foo'
-    file(File.expand_path('target/classes')).prerequisites.should include(project('foo').compile)
-  end
-
-  it 'should execute resources task if compiling' do
+  it 'should accept block and enhance task' do
     write 'src/main/java/Test.java', 'class Test {}'
-    write 'src/main/resources/resource', 'resource'
-    define('foo') { resources }
-    lambda { project('foo').compile.invoke }.should run_task('foo:resources')
+    action = task('action')
+    define('foo') { compile { action.invoke } }
+    lambda { project('foo').compile.invoke }.should run_tasks('foo:compile', action)
   end
 
-  it 'should always execute resources task' do
-    define('foo') { resources }
+  it 'should execute resources task' do
+    define 'foo'
     lambda { project('foo').compile.invoke }.should run_task('foo:resources')
   end
 
   it 'should be recursive' do
     write 'bar/src/main/java/Test.java', 'class Test {}'
-    define('foo') { define('bar') { compile } }
+    define('foo') { define 'bar' }
     lambda { project('foo').compile.invoke }.should run_task('foo:bar:compile')
   end
 
-  it 'should be a local task' do
-    write 'src/main/java/Test.java', 'class Test {}'
+  it 'sould be a local task' do
     write 'bar/src/main/java/Test.java', 'class Test {}'
     define('foo') { define 'bar' }
-    lambda { in_original_dir(project('foo:bar').base_dir) { task('compile').invoke } }.should run_task('foo:bar:compile').but_not('foo:compile')
+    lambda do
+      in_original_dir project('foo:bar').base_dir do
+        task('compile').invoke
+      end
+    end.should run_task('foo:bar:compile').but_not('foo:compile')
   end
 
-  it 'should execute from build' do
+  it 'should run from build task' do
     write 'bar/src/main/java/Test.java', 'class Test {}'
-    define('foo') { define('bar') { compile } }
+    define('foo') { define 'bar' }
     lambda { task('build').invoke }.should run_task('foo:bar:compile')
   end
 
-  it 'should not copy files from src/main/java to target' do
-    write 'src/main/java/Test.java', 'class Test {}'
-    write 'src/main/java/properties', 'copy=yes'
-    define('foo').task('build').invoke
-    Dir.glob("#{project('foo').compile.target}/**/*").should eql([File.expand_path('target/classes/Test.class')])
-  end
-
   it 'should clean after itself' do
-    mkpath 'target'
-    define 'foo'
-    lambda { task('clean').invoke }.should change { File.exist?('target') }.to(false)
+    mkpath 'code'
+    define('foo') { compile.into('code') }
+    lambda { task('clean').invoke }.should change { File.exist?('code') }.to(false)
   end
 end
 
 
 describe Project, '#resources' do
-  accessor_task_spec :resources
+  before { @task_name = 'resources' }
+  it_should_behave_like 'accessor task'
 
-  def make_resources()
-    @resources = [ 'resource1', 'resource2', '.config' ]
-    @resources.each { |res| write "src/main/resources/#{res}", res }
+  it 'should return a resources task' do
+    define('foo').resources.should be_instance_of(ResourcesTask)
   end
 
   it 'should provide a filter' do
-    define 'foo'
-    project('foo').resources.filter.should be_instance_of(Filter)
+    define('foo').resources.filter.should be_instance_of(Filter)
+  end
+
+  it 'should include src/main/resources as source directory' do
+    write 'src/main/resources/test'
+    define('foo').resources.sources.first.should point_to_path('src/main/resources')
   end
 
   it 'should accept prerequisites' do
     tasks = ['task1', 'task2'].each { |name| task(name) }
-    define('foo') { resources *tasks }
-    lambda { project('foo').resources.invoke }.should run_tasks(*tasks)
+    define('foo') { resources 'task1', 'task2' }
+    lambda { project('foo').resources.invoke }.should run_tasks('task1', 'task2')
   end
 
-  it 'should accept block' do
-    make_resources
-    task 'action'
-    define('foo') { resources { task('action').invoke } }
-    lambda { project('foo').resources.invoke }.should run_task('action')
+  it 'should respond to from and add additional sources' do
+    write 'src/main/resources/original'
+    write 'extra/spicy'
+    define('foo') { resources.from 'extra' }
+    project('foo').resources.invoke
+    FileList['target/resources/*'].sort.should  == ['target/resources/original', 'target/resources/spicy']
   end
 
-  it 'should set target directory by default' do
-    make_resources
-    define 'foo'
-    project('foo').resources.filter.target.to_s.should eql(project('foo').path_to('target/resources'))
+  it 'should pass include pattern to filter' do
+    3.times { |i| write "src/main/resources/test#{i + 1}" }
+    define('foo') { resources.include('test2') }
+    project('foo').resources.invoke
+    FileList['target/resources/*'].should  == ['target/resources/test2']
   end
 
-  it 'should use target directory if specified' do
-    define 'foo' do
-      compile.into 'the_classes'
-      resources.filter.into 'the_resources'
-    end
-    project('foo').resources.filter.target.to_s.should eql(File.expand_path('the_resources'))
+  it 'should pass exclude pattern to filter' do
+    3.times { |i| write "src/main/resources/test#{i + 1}" }
+    define('foo') { resources.exclude('test2') }
+    project('foo').resources.invoke
+    FileList['target/resources/*'].sort.should  == ['target/resources/test1', 'target/resources/test3']
   end
 
-  it 'should create file task for target directory' do
-    make_resources
-    task 'filtering'
-    define('foo') do
-      class << resources.filter
-        def run() ; task('filtering').invoke ; end
-      end
-    end
-    lambda { file(File.expand_path('target/resources')).invoke }.should run_task('filtering')
+  it 'should accept block and enhance task' do
+    action = task('action')
+    define('foo') { resources { action.invoke } }
+    lambda { project('foo').resources.invoke }.should run_tasks('foo:resources', action)
   end
 
-  it 'should include all files in the resources directory' do
-    make_resources
-    define 'foo'
-    project('foo').resources.invoke
-    FileList['target/resources/{*,.*}'].reject { |f| File.directory?(f) }.map { |f| File.read(f) }.sort.should == @resources.sort
+  it 'should set target directory to target/resources' do
+    define('foo').resources.target.to_s.should point_to_path('target/resources')
   end
 
-  it 'should always execute resources task when compiling' do
-    define('foo') { resources }
-    lambda { project('foo').compile.invoke }.should run_task('foo:resources')
+  it 'should use provided target directoy' do
+    define('foo') { resources.filter.into('the_resources') }
+    project('foo').resources.target.to_s.should point_to_path('the_resources')
   end
 
-  it 'should respond to from and add additional directories' do
-    make_resources
-    mkpath 'extra' ; write 'extra/special'
-    define('foo') { resources.from 'extra' }
-    project('foo').resources.invoke
-    FileList['target/resources/{*,.*}'].should include(*@resources.map { |file| "target/resources/#{file}" })
-    FileList['target/resources/{*,.*}'].should include('target/resources/special')
+  it 'should create file task for target directory' do
+    define('foo').resources.should_receive(:execute)
+    project('foo').file('target/resources').invoke
   end
 
-  it 'should work with directories other than resources' do
-    mkpath 'extra' ; write 'extra/special'
-    define('foo') { resources.from 'extra' }
-    project('foo').resources.invoke
-    FileList['target/resources/**'].should include('target/resources/special')
+  it 'should not be recursive' do
+    define('foo') { define 'bar' }
+    lambda { project('foo').resources.invoke }.should_not run_task('foo:bar:resources')
   end
 
   it 'should use current profile for filtering'
@@ -656,28 +502,23 @@
 
 
 describe Project, '#javadoc' do
-  accessor_task_spec :javadoc
+  before { @task_name = 'javadoc' }
+  it_should_behave_like 'accessor task'
 
-  def make_sources(dir = nil)
-    @src_dir = (dir ? "#{dir}/" : '') + 'src/main/java/pkg'
-    @sources = (1..3).map { |i| "Test#{i}" }.
-      each { |name| write "#{@src_dir}/#{name}.java", "package pkg; public class #{name}{}" }.
-      map { |name| File.expand_path("#{@src_dir}/#{name}.java") }
-  end
-
-  it 'should always set target directory' do
-    define 'foo'
-    project('foo').javadoc.target.should_not be_nil
+  def sources
+    @sources ||= (1..3).map { |i| "Test#{i}" }.
+      each { |name| write "src/main/java/foo/#{name}.java", "package foo; public class #{name}{}" }.
+      map { |name| "src/main/java/foo/#{name}.java" }
   end
 
   it 'should set target directory to target/javadoc' do
-    define 'foo'
-    project('foo').javadoc.target.to_s.should eql(File.expand_path('target/javadoc'))
+    define('foo').javadoc.target.to_s.should point_to_path('target/javadoc')
   end
 
   it 'should create file task for target directory' do
-    define 'foo'
-    file(File.expand_path('target/javadoc')).prerequisites.should include(project('foo').javadoc)
+    define('foo')
+    project('foo').javadoc.should_receive(:invoke_prerequisites)
+    project('foo').file('target/javadoc').invoke
   end
 
   it 'should respond to into() and return self' do
@@ -686,10 +527,10 @@
     task.should be(project('foo').javadoc)
   end
 
-  it 'should respond to into() and change target' do
+  it 'should respond to info() and change target directory' do
     define('foo') { javadoc.into('docs') }
-    project('foo').javadoc.target.to_s.should eql(File.expand_path('docs'))
-    file(File.expand_path('docs')).prerequisites.should include(project('foo').javadoc)
+    project('foo').javadoc.should_receive(:invoke_prerequisites)
+    file('docs').invoke
   end
 
   it 'should respond to from() and return self' do
@@ -698,36 +539,35 @@
     task.should be(project('foo').javadoc)
   end
 
-  it 'should respond to from() and add source file' do
+  it 'should respond to from() and add sources' do
     define('foo') { javadoc.from 'srcs' }
     project('foo').javadoc.source_files.should include('srcs')
   end
 
   it 'should respond to from() and add file task' do
     define('foo') { javadoc.from file('srcs') }
-    project('foo').javadoc.source_files.should include(File.expand_path('srcs'))
+    project('foo').javadoc.source_files.first.should point_to_path('srcs')
+  end
+
+  it 'should respond to from() and add project\'s sources and dependencies' do
+    write 'bar/src/main/java/Test.java'
+    define 'foo' do
+      define('bar') { compile.with 'group:id:jar:1.0' }
+      javadoc.from project('foo:bar')
+    end
+    project('foo').javadoc.source_files.first.should point_to_path('bar/src/main/java/Test.java')
+    project('foo').javadoc.classpath.map(&:to_spec).should include('group:id:jar:1.0')
   end
 
   it 'should generate javadocs from project' do
-    make_sources
+    sources
     define 'foo'
-    project('foo').javadoc.source_files.sort.should == @sources.sort
+    project('foo').javadoc.source_files.sort.should == sources.sort.map { |f| File.expand_path(f) }
   end
 
-  it 'should generate javadocs from project using its classpath' do
-    make_sources
+  it 'should include compile dependencies' do
     define('foo') { compile.with 'group:id:jar:1.0' }
-    project('foo').javadoc.classpath.map(&:to_spec).should eql(['group:id:jar:1.0'])
-  end
-
-  it 'should respond to from() and add compile sources and dependencies' do
-    make_sources 'bar'
-    define 'foo' do
-      define('bar') { compile.with 'group:id:jar:1.0' }
-      javadoc.from project('foo:bar')
-    end
-    project('foo').javadoc.source_files.sort.should == @sources.sort
-    project('foo').javadoc.classpath.map(&:to_spec).should eql(['group:id:jar:1.0'])
+    project('foo').javadoc.classpath.map(&:to_spec).should include('group:id:jar:1.0')
   end
 
   it 'should respond to include() and return self' do
@@ -735,10 +575,8 @@
   end
 
   it 'should respond to include() and add files' do
-    make_sources 'bar'
-    define 'foo'
-    project('foo').javadoc.include @sources.first
-    project('foo').javadoc.source_files.sort.should == [@sources.first]
+    define('foo').javadoc.include sources.first
+    project('foo').javadoc.source_files.sort.should == [sources.first]
   end
 
   it 'should respond to exclude() and return self' do
@@ -746,10 +584,9 @@
   end
 
   it 'should respond to exclude() and ignore files' do
-    make_sources
-    define 'foo'
-    project('foo').javadoc.exclude @sources.first
-    project('foo').javadoc.source_files.sort.should == @sources[1..-1]
+    sources
+    define('foo').javadoc.exclude sources.first
+    project('foo').javadoc.source_files.sort.should == sources[1..-1].map { |f| File.expand_path(f) }
   end
 
   it 'should respond to using() and return self' do
@@ -769,40 +606,244 @@
 
   it 'should pick -windowtitle from project description' do
     desc 'My App'
-    define 'foo'
-    project('foo').javadoc.options[:windowtitle].should eql('My App')
+    define('foo').javadoc.options[:windowtitle].should eql('My App')
   end
 
   it 'should produce documentation' do
-    make_sources
-    define 'foo'
-    suppress_stdout { project('foo').javadoc.invoke }
-    (1..3).map { |i| "target/javadoc/pkg/Test#{i}.html" }.each { |f| file(f).should exist }
+    sources
+    define('foo').javadoc.invoke
+    (1..3).map { |i| "target/javadoc/foo/Test#{i}.html" }.each { |f| file(f).should exist }
   end
 
   it 'should fail on error' do
     write 'Test.java', 'class Test {}'
     define('foo') { javadoc.include 'Test.java' }
-    suppress_stdout do
-      lambda { project('foo').javadoc.invoke }.should raise_error(RuntimeError, /Failed to generate Javadocs/)
-    end
+    lambda { project('foo').javadoc.invoke }.should raise_error(RuntimeError, /Failed to generate Javadocs/)
   end
 
   it 'should be local task' do
-    make_sources
-    make_sources 'bar'
-    define('foo') { define 'bar' }
-    lambda { in_original_dir project('foo:bar').base_dir do
-      suppress_stdout { task('javadoc').invoke }
-    end }.should run_task('foo:bar:javadoc').but_not('foo:javadoc')
+    define('foo') { define('bar') }
+    project('foo:bar').javadoc.should_receive(:invoke_prerequisites)
+    in_original_dir(project('foo:bar').base_dir) { task('javadoc').invoke }
   end
 
   it 'should not recurse' do
-    make_sources
-    make_sources 'bar'
     define('foo') { define 'bar' }
-    lambda { suppress_stdout { task('javadoc').invoke } }.should run_task('foo:javadoc').but_not('foo:bar:javadoc')
+    project('foo:bar').javadoc.should_not_receive(:invoke_prerequisites)
+    project('foo').javadoc.invoke
+  end
+end
+
+
+describe 'javac compiler' do
+  it 'should identify itself from source directories' do
+    write 'src/main/java/Test.java', 'class Test {}' 
+    define('foo').compile.compiler.should eql(:javac)
+  end
+
+  it 'should report the language as :java' do
+    define('foo').compile.using(:javac).language.should eql(:java)
+  end
+
+  it 'should set the target directory to target/classes' do
+    define 'foo' do
+      lambda { compile.using(:javac) }.should change { compile.target.to_s }.to(File.expand_path('target/classes'))
+    end
+  end
+
+  it 'should not override existing target directory' do
+    define 'foo' do
+      compile.into('classes')
+      lambda { compile.using(:javac) }.should_not change { compile.target }
+    end
   end
 
-  it 'should include prerequisites from compile task'
+  it 'should not change existing list of sources' do
+    define 'foo' do
+      compile.from('sources')
+      lambda { compile.using(:javac) }.should_not change { compile.sources }
+    end
+  end
+
+  it 'should include as classpath dependency' do
+    write 'src/dependency/Dependency.java', 'class Dependency {}'
+    define 'dependency', :version=>'1.0' do
+      compile.from('src/dependency').into('target/dependency')
+      package(:jar)
+    end
+    write 'src/test/DependencyTest.java', 'class DependencyTest { Dependency _var; }'
+    lambda { define('foo').compile.from('src/test').with(project('dependency')).invoke }.should run_task('foo:compile')
+  end
+end
+
+
+describe 'javac compiler options' do
+  def compile_task
+    @compile_task ||= define('foo').compile.using(:javac)
+  end
+
+  def javac_args
+    Compiler::Javac.new.send(:javac_args_from, compile_task.options)
+  end
+
+  it 'should set warnings option to false by default' do
+    compile_task.options.warnings.should be_false
+  end
+
+  it 'should set wranings option to true when running with --verbose option' do
+    verbose true
+    compile_task.options.warnings.should be_true
+  end
+
+  it 'should use -nowarn argument when warnings is false' do
+    compile_task.using(:warnings=>false)
+    javac_args.should include('-nowarn') 
+  end
+
+  it 'should not use -nowarn argument when warnings is true' do
+    compile_task.using(:warnings=>true)
+    javac_args.should_not include('-nowarn') 
+  end
+
+  it 'should not use -verbose argument by default' do
+    javac_args.should_not include('-verbose') 
+  end
+
+  it 'should use -verbose argument when running with --trace option' do
+    trace true
+    javac_args.should include('-verbose') 
+  end
+
+  it 'should set debug option to true by default' do
+    compile_task.options.debug.should be_true
+  end
+
+  it 'should set debug option to false based on Buildr.options' do
+    Buildr.options.debug = false
+    compile_task.options.debug.should be_false
+  end
+
+  it 'should set debug option to false based on debug environment variable' do
+    ENV['debug'] = 'no'
+    compile_task.options.debug.should be_false
+  end
+
+  it 'should set debug option to false based on DEBUG environment variable' do
+    ENV['DEBUG'] = 'no'
+    compile_task.options.debug.should be_false
+  end
+
+  it 'should use -g argument when debug option is true' do
+    compile_task.using(:debug=>true)
+    javac_args.should include('-g')
+  end
+
+  it 'should not use -g argument when debug option is false' do
+    compile_task.using(:debug=>false)
+    javac_args.should_not include('-g')
+  end
+
+  it 'should set deprecation option to false by default' do
+    compile_task.options.deprecation.should be_false
+  end
+
+  it 'should use -deprecation argument when deprecation is true' do
+    compile_task.using(:deprecation=>true)
+    javac_args.should include('-deprecation')
+  end
+
+  it 'should not use -deprecation argument when deprecation is false' do
+    compile_task.using(:deprecation=>false)
+    javac_args.should_not include('-deprecation')
+  end
+
+  it 'should not set source option by default' do
+    compile_task.options.source.should be_nil
+    javac_args.should_not include('-source')
+  end
+
+  it 'should not set target option by default' do
+    compile_task.options.target.should be_nil
+    javac_args.should_not include('-target')
+  end
+
+  it 'should use -source nn argument if source option set' do
+    compile_task.using(:source=>'1.5')
+    javac_args.should include('-source', '1.5')
+  end
+
+  it 'should use -target nn argument if target option set' do
+    compile_task.using(:target=>'1.5')
+    javac_args.should include('-target', '1.5')
+  end
+
+  it 'should set lint option to false by default' do
+    compile_task.options.lint.should be_false
+  end
+
+  it 'should use -lint argument if lint option is true' do
+    compile_task.using(:lint=>true)
+    javac_args.should include('-Xlint')
+  end
+
+  it 'should use -lint argument with value of option' do
+    compile_task.using(:lint=>'all')
+    javac_args.should include('-Xlint:all')
+  end
+
+  it 'should use -lint argument with value of option as array' do
+    compile_task.using(:lint=>['path', 'serial'])
+    javac_args.should include('-Xlint:path,serial')
+  end
+
+  it 'should not set other option by default' do
+    compile_task.options.other.should be_nil
+  end
+
+  it 'should pass other argument if other option is string' do
+    compile_task.using(:other=>'-Xprint')
+    javac_args.should include('-Xprint')
+  end
+
+  it 'should pass other argument if other option is array' do
+    compile_task.using(:other=>['-Xstdout', 'msgs'])
+    javac_args.should include('-Xstdout', 'msgs')
+  end
+
+  it 'should complain about options it doesn\'t know' do
+    write 'source/Test.java', 'class Test {}'
+    compile_task.using(:unknown=>'option')
+    lambda { compile_task.from('source').invoke }.should raise_error(ArgumentError, /no such option/i)
+  end
+
+  it 'should inherit options from parent' do
+    define 'foo' do
+      compile.using(:warnings=>true, :debug=>true, :deprecation=>true, :source=>'1.5', :target=>'1.4')
+      define 'bar' do
+        compile.using(:javac)
+        compile.options.warnings.should be_true
+        compile.options.debug.should be_true
+        compile.options.deprecation.should be_true
+        compile.options.source.should eql('1.5')
+        compile.options.target.should eql('1.4')
+      end
+    end
+  end
+
+  it 'should only inherit options it knows' do
+    define 'foo' do
+      compile.using(:warnings=>true, :errors=>true)
+      define 'bar' do
+        compile.using(:javac)
+        compile.options.warnings.should be_true
+        compile.options.errors.should be_nil
+      end
+    end
+  end
+
+  after do
+    Buildr.options.debug = nil
+    ENV.delete "debug"
+    ENV.delete "DEBUG"
+  end
 end

Modified: incubator/buildr/trunk/spec/packaging_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/packaging_spec.rb?rev=609118&r1=609117&r2=609118&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/packaging_spec.rb (original)
+++ incubator/buildr/trunk/spec/packaging_spec.rb Sat Jan  5 02:06:53 2008
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), 'sandbox')
+require File.join(File.dirname(__FILE__), 'spec_helpers')
 
 
 describe Project, "#group" do
@@ -106,7 +106,7 @@
   it "should include LICENSE file if found" do
     write "LICENSE"
     define "foo"
-    project("foo").meta_inf.map(&:to_s).should eql([File.expand_path("LICENSE")])
+    project("foo").meta_inf.first.should point_to_path("LICENSE")
   end
 
   it "should be empty unless LICENSE exists" do
@@ -117,7 +117,7 @@
   it "should inherit from parent project" do
     write "LICENSE"
     define("foo") { define "bar" }
-    project("foo:bar").meta_inf.map(&:to_s).should eql([File.expand_path("LICENSE")])
+    project("foo:bar").meta_inf.first.should point_to_path("LICENSE")
   end
 
   it "should expect LICENSE file parent project" do
@@ -176,10 +176,14 @@
     pkg.type.should eql(:war)
   end
 
-  it "should assume :jar package type unless specified" do
-    pkg = nil
-    define("foo", :version=>"1.0") { pkg = package }
-    pkg.type.should eql(:jar)
+  it "should assume :zip package type unless specified" do
+    define("foo", :version=>"1.0")
+    project('foo').package.type.should eql(:zip)
+  end
+
+  it 'should infer packaging type from compiler' do
+    define("foo", :version=>"1.0") { compile.using(:javac) }
+    project('foo').package.type.should eql(:jar)
   end
 
   it "should default to no classifier" do
@@ -243,12 +247,11 @@
     define("foo", :version=>"1.0") do
       package(:war)
       package(:jar, :id=>"bar")
-      package(:jar, :classifier=>"srcs")
+      package(:zip, :classifier=>"srcs")
     end
-    project("foo").packages.map(&:to_s).should eql([
-      File.expand_path("target/foo-1.0.war"),
-      File.expand_path("target/bar-1.0.jar"),
-      File.expand_path("target/foo-1.0-srcs.jar")])
+    project("foo").packages[0].should point_to_path("target/foo-1.0.war")
+    project("foo").packages[1].should point_to_path("target/bar-1.0.jar")
+    project("foo").packages[2].should point_to_path("target/foo-1.0-srcs.zip")
   end
 
   it "should create prerequisite for package task" do
@@ -330,7 +333,7 @@
       define("bar") { mkpath "bar/target/classes" ; package }
     end
     task("package").invoke
-    FileList["**/target/*.jar"].map.sort.should == ["bar/target/foo-bar-1.0.jar", "target/foo-1.0.jar"]
+    FileList["**/target/*.zip"].map.sort.should == ["bar/target/foo-bar-1.0.zip", "target/foo-1.0.zip"]
   end
 end
 
@@ -343,8 +346,8 @@
     end
     in_original_dir project("foo:bar").base_dir do
       task("install").invoke
-      artifacts("foo:foo:jar:1.0", "foo:foo:pom:1.0").each { |t| t.should_not exist }
-      artifacts("foo:foo-bar:jar:1.0", "foo:foo-bar:pom:1.0").each { |t| t.should exist }
+      artifacts("foo:foo:zip:1.0", "foo:foo:pom:1.0").each { |t| t.should_not exist }
+      artifacts("foo:foo-bar:zip:1.0", "foo:foo-bar:pom:1.0").each { |t| t.should exist }
     end
   end
 
@@ -354,7 +357,7 @@
       define("bar") { mkpath "bar/target/classes" ; package }
     end
     task("install").invoke
-    artifacts("foo:foo:jar:1.0", "foo:foo:pom:1.0", "foo:foo-bar:jar:1.0", "foo:foo-bar:pom:1.0").each { |t| t.should exist }
+    artifacts("foo:foo:zip:1.0", "foo:foo:pom:1.0", "foo:foo-bar:zip:1.0", "foo:foo-bar:pom:1.0").each { |t| t.should exist }
   end
 
   it "should create package in local repository" do
@@ -364,9 +367,9 @@
     end
     task("install").invoke
     FileList[repositories.local + "/**/*"].reject { |f| File.directory?(f) }.sort.should == [
-      File.expand_path("foo/foo/1.0/foo-1.0.jar", repositories.local),
+      File.expand_path("foo/foo/1.0/foo-1.0.zip", repositories.local),
       File.expand_path("foo/foo/1.0/foo-1.0.pom", repositories.local),
-      File.expand_path("foo/foo-bar/1.0/foo-bar-1.0.jar", repositories.local),
+      File.expand_path("foo/foo-bar/1.0/foo-bar-1.0.zip", repositories.local),
       File.expand_path("foo/foo-bar/1.0/foo-bar-1.0.pom", repositories.local)].sort
   end
 end
@@ -382,7 +385,7 @@
     in_original_dir project("foo:bar").base_dir do
       task("uninstall").invoke
       FileList[repositories.local + "/**/*"].reject { |f| File.directory?(f) }.sort.should == [
-        File.expand_path("foo/foo/1.0/foo-1.0.jar", repositories.local),
+        File.expand_path("foo/foo/1.0/foo-1.0.zip", repositories.local),
         File.expand_path("foo/foo/1.0/foo-1.0.pom", repositories.local)].sort
     end
   end

Modified: incubator/buildr/trunk/spec/project_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/project_spec.rb?rev=609118&r1=609117&r2=609118&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/project_spec.rb (original)
+++ incubator/buildr/trunk/spec/project_spec.rb Sat Jan  5 02:06:53 2008
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), 'sandbox')
+require File.join(File.dirname(__FILE__), 'spec_helpers')
 
 
 describe Project do
@@ -120,13 +120,12 @@
 
 describe Project, '#base_dir' do
   it 'should be pwd if not specified' do
-    foo = define('foo')
-    foo.base_dir.should eql(Dir.pwd)
+    define('foo').base_dir.should eql(Dir.pwd)
   end
 
   it 'should come from property, if specified' do
     foo = define('foo', :base_dir=>'tmp')
-    foo.base_dir.should eql(File.expand_path('tmp'))
+    foo.base_dir.should point_to_path('tmp')
   end
 
   it 'should be expanded path' do
@@ -135,8 +134,8 @@
   end
 
   it 'should be relative to parent project' do
-    define('foo') { define 'bar' }
-    project('foo:bar').base_dir.should eql(File.join(project('foo').base_dir, 'bar'))
+    define('foo') { define('bar') { define 'baz' } }
+    project('foo:bar:baz').base_dir.should point_to_path('bar/baz')
   end
 
   it 'should be settable only if not read' do
@@ -246,36 +245,41 @@
 
 
 describe Project, '#path_to' do
-  before do
-    @project = define('foo') { define('bar') }
-    @base_dir = @project.base_dir
+  it 'should return absolute paths as is' do
+    define('foo').path_to('/tmp').should eql('/tmp')
   end
 
-  it 'should return absolute paths as is' do
-    @project.path_to('/tmp').should eql('/tmp')
+  it 'should resolve empty path to project\'s base directory' do
+    define('foo').path_to.should eql(project('foo').base_dir)
   end
 
   it 'should resolve relative paths' do
-    @project.path_to().should eql(@project.base_dir)
-    @project.path_to('tmp').should eql("#{@base_dir}/tmp")
+    define('foo').path_to('tmp').should eql(File.expand_path('tmp'))
   end
 
   it 'should accept multiple arguments' do
-    @project.path_to('foo', 'bar').should eql("#{@base_dir}/foo/bar")
+    define('foo').path_to('foo', 'bar').should eql(File.expand_path('foo/bar'))
   end
 
   it 'should handle relative paths' do
-    @project.path_to('..', 'bar').should eql(File.join(@base_dir.pathmap('%d'), 'bar'))
+    define('foo').path_to('..', 'bar').should eql(File.expand_path('../bar'))
   end
 
   it 'should resolve symbols using layout' do
-    @project.layout[:foo] = 'bar'
-    @project.path_to(:foo).should eql(File.join(@base_dir, 'bar'))
-    @project.path_to(:foo, 'tmp').should eql(File.join(@base_dir, 'bar', 'tmp'))
+    define('foo').layout[:foo] = 'bar'
+    project('foo').path_to(:foo).should eql(File.expand_path('bar'))
+    project('foo').path_to(:foo, 'tmp').should eql(File.expand_path('bar/tmp'))
   end
 
   it 'should resolve path for sub-project' do
-    project('foo:bar').path_to('foo').should eql(File.join(project('foo:bar').base_dir, 'foo'))
+    define('foo') { define 'bar' }
+    project('foo:bar').path_to('foo').should eql(File.expand_path('foo', project('foo:bar').base_dir))
+  end
+
+  it 'should be idempotent for relative paths' do
+    define 'foo'
+    path = project('foo').path_to('bar')
+    project('foo').path_to(path).should eql(path)
   end
 end
 
@@ -668,7 +672,7 @@
 
   it 'should create file task relative to project definition' do
     define('foo') { define 'bar' }
-    project('foo:bar').file('baz').name.should eql(File.expand_path('bar/baz'))
+    project('foo:bar').file('baz').name.should point_to_path('bar/baz')
   end
 
   it 'should execute task exactly once' do

Modified: incubator/buildr/trunk/spec/sandbox.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/sandbox.rb?rev=609118&r1=609117&r2=609118&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/sandbox.rb (original)
+++ incubator/buildr/trunk/spec/sandbox.rb Sat Jan  5 02:06:53 2008
@@ -1,341 +1,99 @@
-# This file is required twice when running spec test/*.
-unless defined?(Buildr)
-
-  require 'rubygems'
-  #require "rake"
-  $LOAD_PATH.unshift File.expand_path("#{File.dirname(__FILE__)}/../lib")
-  require File.join(File.dirname(__FILE__), "../lib", "buildr.rb")
-
-
-  # The local repository we use for testing is void of any artifacts, which will break given
-  # that the code requires several artifacts. So we establish them first using the real local
-  # repository and cache these across test cases.
-  repositories.remote << "http://repo1.maven.org/maven2"
-  Java.load # Anything added to the classpath.
-  artifacts(TestFramework.frameworks.values.map(&:requires).flatten).each { |a| file(a).invoke }
-  task("buildr:initialize").invoke
-
-
-  module Buildr
-
-    module Matchers
-
-      include Checks::Matchers
-
-      module ::Kernel #:nodoc:
-        def warn(message)
-          $warning ||= []
-          $warning << message
-        end
-
-        def warn_deprecated_with_capture(message)
-          verbose(true) { warn_deprecated_without_capture message }
-        end
-        alias_method_chain :warn_deprecated, :capture
-      end
-
-      class WarningMatcher
-        def initialize(message)
-          @expect = message
-        end
-
-        def matches?(target)
-          $warning = []
-          target.call
-          return Regexp === @expect ? $warning.join("\n") =~ @expect : $warning.include?(@expect.to_s)
-        end
-
-        def failure_message()
-          $warning ? "Expected warning #{@expect.source}, found #{$warning}" : "Expected warning #{@expect.source}, no warning issued"
-        end
-      end
-
-      # Tests if a warning was issued. You can use a string or regular expression.
-      #
-      # For example:
-      #   lambda { warn "ze test" }.should warn_that(/ze test/)
-      def warn_that(message)
-        WarningMatcher.new message
-      end
-
-
-      class ::Rake::Task
-        def execute_with_a_record(args)
-          $executed ||= []
-          $executed << name
-          execute_without_a_record args
-        end
-        alias_method_chain :execute, :a_record
-      end
-
-      class InvokeMatcher
-        def initialize(*tasks)
-          @expecting = tasks.map { |task| [task].flatten.map(&:to_s) }
-        end
-
-        def matches?(target)
-          $executed = []
-          target.call
-          return false unless all_ran?
-          return !@but_not.any_ran? if @but_not
-          return true
-        end
-
-        def failure_message()
-          return @but_not.negative_failure_message if all_ran? && @but_not
-          "Expected the tasks #{expected} to run, but #{remaining} did not run, or not in the order we expected them to."
-        end
-
-        def negative_failure_message()
-          if all_ran?
-            "Expected the tasks #{expected} to not run, but they all ran."
-          else
-            "Expected the tasks #{expected} to not run, and all but #{remaining} ran."
-          end 
-        end
-
-        def but_not(*tasks)
-          @but_not = InvokeMatcher.new(*tasks)
-          self
-        end
-
-      protected
-
-        def expected()
-          @expecting.map { |tests| tests.join("=>") }.join(", ")
-        end
-
-        def remaining()
-          @remaining.map { |tests| tests.join("=>") }.join(", ")
-        end
-
-        def all_ran?()
-          @remaining ||= $executed.inject(@expecting) do |expecting, executed|
-            expecting.map { |tasks| tasks.first == executed ? tasks[1..-1] : tasks }.reject(&:empty?)
-          end
-          @remaining.empty?
-        end
-
-        def any_ran?()
-          all_ran?
-          @remaining.size < @expecting.size
-        end
-
-      end
-
-      # Tests that all the tasks ran, in the order specified. Can also be used to test that some
-      # tasks and not others ran.
-      #
-      # Takes a list of arguments. Each argument can be a task name, matching only if that task ran.
-      # Each argument can be an array of task names, matching only if all these tasks ran in that order.
-      # So run_tasks("foo", "bar") expects foo and bar to run in any order, but run_task(["foo", "bar"])
-      # expects foo to run before bar.
-      #
-      # You can call but_not on the matchers to specify that certain tasks must not execute.
-      #
-      # For example:
-      #   # Either task
-      #   lambda { task("compile").invoke }.should run_tasks("compile", "resources")
-      #   # In that order
-      #   lambda { task("build").invoke }.should run_tasks(["compile", "test"])
-      #   # With exclusion
-      #   lambda { task("build").invoke }.should run_tasks("compile").but_not("install")
-      def run_tasks(*tasks)
-        InvokeMatcher.new *tasks
-      end
-
-      # Tests that a task ran. Similar to run_tasks, but accepts a single task name.
-      #
-      # For example:
-      #   lambda { task("build").invoke }.should run_task("test")
-      def run_task(task)
-        InvokeMatcher.new task.to_a.first
-      end
-
-      class UriPathMatcher
-        def initialize(re)
-          @expression = re
-        end
-
-        def matches?(uri)
-          @uri = uri
-          uri.path =~ @expression
-        end
-
-        def description
-          "URI with path matching #{@expression}"
-        end
-      end
-      
-      # Matches a parsed URI's path against the given regular expression
-      def uri(re)
-        UriPathMatcher.new(re)
-      end
-
-    end
-
-
-    module Helpers
-
-      def suppress_stdout()
-        stdout = $stdout
-        $stdout = StringIO.new
-        begin
-          yield
-        ensure
-          $stdout = stdout
-        end
-      end
-
-      def dryrun()
-        Rake.application.options.dryrun = true
-        begin
-          suppress_stdout { yield }
-        ensure
-          Rake.application.options.dryrun = false
-        end
-      end
-
-      # We run tests with tracing off. Then things break. And we need to figure out what went wrong.
-      # So just use trace() as you would use verbose() to find and squash the bug.
-      def trace(value = nil)
-        old_value = Rake.application.options.trace
-        Rake.application.options.trace = value unless value.nil?
-        if block_given?
-          begin
-            yield
-          ensure
-            Rake.application.options.trace = old_value
-          end
-        end
-        Rake.application.options.trace
-      end
-
-      # Change the Rakefile original directory, faking invocation from a different directory.
-      def in_original_dir(dir)
-        begin
-          original_dir = Rake.application.original_dir
-          Rake.application.instance_eval { @original_dir = File.expand_path(dir) }
-          yield
-        ensure
-          Rake.application.instance_eval { @original_dir = original_dir }
-        end 
-      end
-
-
-      # Buildr's define method creates a project definition but does not evaluate it
-      # (that happens once the Rakefile is loaded), and we include Buildr's define in
-      # the test context so we can use it without prefixing with Buildr. This just patches
-      # define to evaluate the project definition before returning it.
-      def define(name, properties = nil, &block) #:yields:project
-        Project.define(name, properties, &block).tap { |project| project.invoke }
-      end
-
-    end
-
+# The local repository we use for testing is void of any artifacts, which will break given
+# that the code requires several artifacts. So we establish them first using the real local
+# repository and cache these across test cases.
+repositories.remote << 'http://repo1.maven.org/maven2'
+Java.load # Anything added to the classpath.
+artifacts(TestFramework.frameworks.map(&:requires).flatten).each { |a| file(a).invoke }
+task('buildr:initialize').invoke
+
+# We need to run all tests inside a sandbox, tacking a snapshot of Rake/Buildr before the test,
+# and restoring everything to its previous state after the test. Damn state changes.
+module Sandbox
+
+  def self.included(spec)
+    spec.before(:each) { sandbox }
+    spec.after(:each) { reset }
+  end
 
-    # We need to run all tests inside a sandbox, tacking a snapshot of Rake/Buildr before the test,
-    # and restoring everything to its previous state after the test. Damn state changes.
-    module Sandbox
-
-      def sandbox()
-        @sandbox = {}
-        # During teardown we get rid of all the tasks and start with a clean slate.
-        # Unfortunately, we also get rid of tasks we need, like build, clean, etc.
-        # Here we capture them in their original form, recreated during teardown.
-        @sandbox[:tasks] = Rake.application.tasks.collect do |original|
-          prerequisites = original.prerequisites.clone
-          actions = original.instance_eval { @actions }.clone
-          lambda do
-            original.class.send(:define_task, original.name=>prerequisites).tap do |task|
-              task.comment = original.comment
-              actions.each { |action| task.enhance &action }
-            end
-          end
+  def sandbox
+    @sandbox = {}
+    # During teardown we get rid of all the tasks and start with a clean slate.
+    # Unfortunately, we also get rid of tasks we need, like build, clean, etc.
+    # Here we capture them in their original form, recreated during teardown.
+    @sandbox[:tasks] = Rake.application.tasks.collect do |original|
+      prerequisites = original.prerequisites.clone
+      actions = original.instance_eval { @actions }.clone
+      lambda do
+        original.class.send(:define_task, original.name=>prerequisites).tap do |task|
+          task.comment = original.comment
+          actions.each { |action| task.enhance &action }
         end
-        @sandbox[:rules] = Rake.application.instance_variable_get(:@rules).clone
-
-        # Create a temporary directory where we can create files, e.g,
-        # for projects, compilation. We need a place that does not depend
-        # on the current directory.
-        @test_dir = File.expand_path("tmp", File.dirname(__FILE__))
-        FileUtils.mkpath @test_dir
-        # Move to the work directory and make sure Rake thinks of it as the Rakefile directory.
-        @sandbox[:pwd] = Dir.pwd
-        Dir.chdir @test_dir
-        @sandbox[:original_dir] = Rake.application.original_dir 
-        Rake.application.instance_eval { @original_dir = Dir.pwd }
-        Rake.application.instance_eval { @rakefile = File.join(Dir.pwd, 'buildfile') }
-        
-        # Later on we'll want to lose all the on_define created during the test.
-        @sandbox[:on_define] = Project.class_eval { (@on_define || []).dup }
-        @sandbox[:layout] = Layout.default.clone
-
-        # Create a local repository we can play with. However, our local repository will be void
-        # of some essential artifacts (e.g. JUnit artifacts required by build task), so we create
-        # these first (see above) and keep them across test cases.
-        @sandbox[:artifacts] = Artifact.class_eval { @artifacts }.clone
-        Buildr.repositories.local = File.join(@test_dir, "repository")
-
-        @sandbox[:env_keys] = ENV.keys
-        ["DEBUG", "TEST", "HTTP_PROXY", "USER"].each { |k| ENV.delete(k) ; ENV.delete(k.downcase) }
-
-        # Don't output crap to the console.
-        trace false
-        verbose false
       end
-
-      # Call this from teardown.
-      def reset()
-        # Remove testing local repository, and reset all repository settings.
-        Buildr.repositories.local = nil
-        Buildr.repositories.remote = nil
-        Buildr.repositories.release_to = nil
-        Buildr.options.proxy.http = nil
-        Buildr.instance_eval { @profiles = nil }
-
-        # Get rid of all the projects and the on_define blocks we used.
-        Project.clear
-        on_define = @sandbox[:on_define]
-        Project.class_eval { @on_define = on_define }
-        Layout.default = @sandbox[:layout].clone
-
-        # Switch back Rake directory.
-        Dir.chdir @sandbox[:pwd]
-        original_dir = @sandbox[:original_dir]
-        Rake.application.instance_eval { @original_dir = original_dir }
-        FileUtils.rm_rf @test_dir
-
-        # Get rid of all the tasks and restore the default tasks.
-        Rake::Task.clear
-        @sandbox[:tasks].each { |block| block.call }
-        Rake.application.instance_variable_set :@rules, @sandbox[:rules]
-
-        # Get rid of all artifacts and out test directory.
-        @sandbox[:artifacts].tap { |artifacts| Artifact.class_eval { @artifacts = artifacts } }
-
-        # Restore options.
-        Buildr.options.test = nil
-        (ENV.keys - @sandbox[:env_keys]).each { |key| ENV.delete key }
-      end
-
     end
+    @sandbox[:rules] = Rake.application.instance_variable_get(:@rules).clone
 
+    # Create a temporary directory where we can create files, e.g,
+    # for projects, compilation. We need a place that does not depend
+    # on the current directory.
+    @test_dir = File.expand_path('tmp', File.dirname(__FILE__))
+    FileUtils.mkpath @test_dir
+    # Move to the work directory and make sure Rake thinks of it as the Rakefile directory.
+    @sandbox[:pwd] = Dir.pwd
+    Dir.chdir @test_dir
+    @sandbox[:original_dir] = Rake.application.original_dir 
+    Rake.application.instance_eval { @original_dir = Dir.pwd }
+    Rake.application.instance_eval { @rakefile = File.expand_path('buildfile') }
+    
+    # Later on we'll want to lose all the on_define created during the test.
+    @sandbox[:on_define] = Project.class_eval { (@on_define || []).dup }
+    @sandbox[:layout] = Layout.default.clone
+
+    # Create a local repository we can play with. However, our local repository will be void
+    # of some essential artifacts (e.g. JUnit artifacts required by build task), so we create
+    # these first (see above) and keep them across test cases.
+    @sandbox[:artifacts] = Artifact.class_eval { @artifacts }.clone
+    Buildr.repositories.local = File.join(@test_dir, 'repository')
+
+    @sandbox[:env_keys] = ENV.keys
+    ['DEBUG', 'TEST', 'HTTP_PROXY', 'USER'].each { |k| ENV.delete(k) ; ENV.delete(k.downcase) }
+
+    # Don't output crap to the console.
+    trace false
+    verbose false
   end
 
-  # Allow using matchers within the project definition.
-  class Buildr::Project
-    include ::Spec::Matchers, ::Buildr::Matchers
-  end
-
-  Spec::Runner.configure do |config|
-    # Make all Buildr methods accessible from test cases, and add various helper methods.
-    config.include Buildr, Buildr::Helpers, Buildr::Matchers
-
-    # Sanbdox Rake/Buildr for each test.
-    config.include Buildr::Sandbox
-    config.before(:each) { sandbox }
-    config.after(:each) { reset }
+  # Call this from teardown.
+  def reset
+    # Remove testing local repository, and reset all repository settings.
+    Buildr.repositories.local = nil
+    Buildr.repositories.remote = nil
+    Buildr.repositories.release_to = nil
+    Buildr.options.proxy.http = nil
+    Buildr.instance_eval { @profiles = nil }
+
+    # Get rid of all the projects and the on_define blocks we used.
+    Project.clear
+    on_define = @sandbox[:on_define]
+    Project.class_eval { @on_define = on_define }
+    Layout.default = @sandbox[:layout].clone
+
+    # Switch back Rake directory.
+    Dir.chdir @sandbox[:pwd]
+    original_dir = @sandbox[:original_dir]
+    Rake.application.instance_eval { @original_dir = original_dir }
+    FileUtils.rm_rf @test_dir
+
+    # Get rid of all the tasks and restore the default tasks.
+    Rake::Task.clear
+    @sandbox[:tasks].each { |block| block.call }
+    Rake.application.instance_variable_set :@rules, @sandbox[:rules]
+
+    # Get rid of all artifacts and out test directory.
+    @sandbox[:artifacts].tap { |artifacts| Artifact.class_eval { @artifacts = artifacts } }
+
+    # Restore options.
+    Buildr.options.test = nil
+    (ENV.keys - @sandbox[:env_keys]).each { |key| ENV.delete key }
   end
 
 end

Added: incubator/buildr/trunk/spec/spec_helpers.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/spec_helpers.rb?rev=609118&view=auto
==============================================================================
--- incubator/buildr/trunk/spec/spec_helpers.rb (added)
+++ incubator/buildr/trunk/spec/spec_helpers.rb Sat Jan  5 02:06:53 2008
@@ -0,0 +1,258 @@
+# This file gets loaded twice when running 'spec spec/*' and not with pleasent results,
+# so ignore the second attempt to load it.
+unless $LOADED_FEATURES.include?(__FILE__)
+  require 'rubygems'
+  $LOAD_PATH.unshift File.expand_path('../lib', File.dirname(__FILE__))
+  require 'buildr'
+  require File.expand_path('sandbox', File.dirname(__FILE__))
+
+  module SpecHelpers
+
+    include Checks::Matchers
+
+    module ::Kernel #:nodoc:
+      def warn(message)
+        $warning ||= []
+        $warning << message
+      end
+
+      def warn_deprecated_with_capture(message)
+        verbose(true) { warn_deprecated_without_capture message }
+      end
+      alias_method_chain :warn_deprecated, :capture
+    end
+
+    class WarningMatcher
+      def initialize(message)
+        @expect = message
+      end
+
+      def matches?(target)
+        $warning = []
+        target.call
+        return Regexp === @expect ? $warning.join('\n') =~ @expect : $warning.include?(@expect.to_s)
+      end
+
+      def failure_message
+        $warning ? "Expected warning #{@expect.source}, found #{$warning}" : "Expected warning #{@expect.source}, no warning issued"
+      end
+    end
+
+    # Tests if a warning was issued. You can use a string or regular expression.
+    #
+    # For example:
+    #   lambda { warn 'ze test' }.should warn_that(/ze test/)
+    def warn_that(message)
+      WarningMatcher.new message
+    end
+
+
+    class ::Rake::Task
+      def execute_with_a_record(args)
+        $executed ||= []
+        $executed << name
+        execute_without_a_record args
+      end
+      alias_method_chain :execute, :a_record
+    end
+
+    class InvokeMatcher
+      def initialize(*tasks)
+        @expecting = tasks.map { |task| [task].flatten.map(&:to_s) }
+      end
+
+      def matches?(target)
+        $executed = []
+        target.call
+        return false unless all_ran?
+        return !@but_not.any_ran? if @but_not
+        return true
+      end
+
+      def failure_message
+        return @but_not.negative_failure_message if all_ran? && @but_not
+        "Expected the tasks #{expected} to run, but #{remaining} did not run, or not in the order we expected them to."
+      end
+
+      def negative_failure_message
+        if all_ran?
+          "Expected the tasks #{expected} to not run, but they all ran."
+        else
+          "Expected the tasks #{expected} to not run, and all but #{remaining} ran."
+        end 
+      end
+
+      def but_not(*tasks)
+        @but_not = InvokeMatcher.new(*tasks)
+        self
+      end
+
+    protected
+
+      def expected
+        @expecting.map { |tests| tests.join('=>') }.join(', ')
+      end
+
+      def remaining
+        @remaining.map { |tests| tests.join('=>') }.join(', ')
+      end
+
+      def all_ran?
+        @remaining ||= $executed.inject(@expecting) do |expecting, executed|
+          expecting.map { |tasks| tasks.first == executed ? tasks[1..-1] : tasks }.reject(&:empty?)
+        end
+        @remaining.empty?
+      end
+
+      def any_ran?
+        all_ran?
+        @remaining.size < @expecting.size
+      end
+
+    end
+
+    # Tests that all the tasks ran, in the order specified. Can also be used to test that some
+    # tasks and not others ran.
+    #
+    # Takes a list of arguments. Each argument can be a task name, matching only if that task ran.
+    # Each argument can be an array of task names, matching only if all these tasks ran in that order.
+    # So run_tasks('foo', 'bar') expects foo and bar to run in any order, but run_task(['foo', 'bar'])
+    # expects foo to run before bar.
+    #
+    # You can call but_not on the matchers to specify that certain tasks must not execute.
+    #
+    # For example:
+    #   # Either task
+    #   lambda { task('compile').invoke }.should run_tasks('compile', 'resources')
+    #   # In that order
+    #   lambda { task('build').invoke }.should run_tasks(['compile', 'test'])
+    #   # With exclusion
+    #   lambda { task('build').invoke }.should run_tasks('compile').but_not('install')
+    def run_tasks(*tasks)
+      InvokeMatcher.new *tasks
+    end
+
+    # Tests that a task ran. Similar to run_tasks, but accepts a single task name.
+    #
+    # For example:
+    #   lambda { task('build').invoke }.should run_task('test')
+    def run_task(task)
+      InvokeMatcher.new task.to_a.first
+    end
+
+    class UriPathMatcher
+      def initialize(re)
+        @expression = re
+      end
+
+      def matches?(uri)
+        @uri = uri
+        uri.path =~ @expression
+      end
+
+      def description
+        "URI with path matching #{@expression}"
+      end
+    end
+    
+    # Matches a parsed URI's path against the given regular expression
+    def uri(re)
+      UriPathMatcher.new(re)
+    end
+
+
+    class AbsolutePathMatcher
+      def initialize(path)
+        @expected = File.expand_path(path.to_s)
+      end
+
+      def matches?(path)
+        @provided = File.expand_path(path.to_s)
+        @provided == @expected
+      end
+
+      def failure_message
+        "Expected path #{@expected}, but found path #{@provided}"
+      end
+
+      def negative_failure_message
+        "Expected a path other than #{@expected}"
+      end
+    end
+
+    def point_to_path(path)
+      AbsolutePathMatcher.new(path)
+    end
+
+
+    def suppress_stdout
+      stdout = $stdout
+      $stdout = StringIO.new
+      begin
+        yield
+      ensure
+        $stdout = stdout
+      end
+    end
+
+    def dryrun
+      Rake.application.options.dryrun = true
+      begin
+        suppress_stdout { yield }
+      ensure
+        Rake.application.options.dryrun = false
+      end
+    end
+
+    # We run tests with tracing off. Then things break. And we need to figure out what went wrong.
+    # So just use trace() as you would use verbose() to find and squash the bug.
+    def trace(value = nil)
+      old_value = Rake.application.options.trace
+      Rake.application.options.trace = value unless value.nil?
+      if block_given?
+        begin
+          yield
+        ensure
+          Rake.application.options.trace = old_value
+        end
+      end
+      Rake.application.options.trace
+    end
+
+    # Change the Rakefile original directory, faking invocation from a different directory.
+    def in_original_dir(dir)
+      begin
+        original_dir = Rake.application.original_dir
+        Rake.application.instance_eval { @original_dir = File.expand_path(dir) }
+        yield
+      ensure
+        Rake.application.instance_eval { @original_dir = original_dir }
+      end 
+    end
+
+
+    # Buildr's define method creates a project definition but does not evaluate it
+    # (that happens once the Rakefile is loaded), and we include Buildr's define in
+    # the test context so we can use it without prefixing with Buildr. This just patches
+    # define to evaluate the project definition before returning it.
+    def define(name, properties = nil, &block) #:yields:project
+      Project.define(name, properties, &block).tap { |project| project.invoke }
+    end
+
+  end
+
+
+  # Allow using matchers within the project definition.
+  class Buildr::Project
+    include ::Spec::Matchers, SpecHelpers
+  end
+
+
+  Spec::Runner.configure do |config|
+    # Make all Buildr methods accessible from test cases, and add various helper methods.
+    config.include Buildr, SpecHelpers
+
+    # Sanbdox Rake/Buildr for each test.
+    config.include Sandbox
+  end
+end

Modified: incubator/buildr/trunk/spec/test_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/test_spec.rb?rev=609118&r1=609117&r2=609118&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/test_spec.rb (original)
+++ incubator/buildr/trunk/spec/test_spec.rb Sat Jan  5 02:06:53 2008
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), 'sandbox')
+require File.join(File.dirname(__FILE__), 'spec_helpers')
 
 
 describe Buildr::TestTask do
@@ -75,13 +75,13 @@
 
   it "should respond to :with and add artifacfs to compile task dependencies" do
     define("foo") { test.with "test.jar", "acme:example:jar:1.0" }
-    project("foo").test.compile.dependencies.should include(File.expand_path("test.jar"))
+    project("foo").test.compile.dependencies.first.should point_to_path("test.jar")
     project("foo").test.compile.dependencies.should include(artifact("acme:example:jar:1.0"))
   end
 
   it "should respond to :with and add artifacfs to task dependencies" do
     define("foo") { test.with "test.jar", "acme:example:jar:1.0" }
-    project("foo").test.dependencies.should include(File.expand_path("test.jar"))
+    project("foo").test.dependencies.first.should point_to_path("test.jar")
     project("foo").test.dependencies.should include(artifact("acme:example:jar:1.0"))
   end
 
@@ -200,7 +200,7 @@
   end
 
   it "should fail if only one test fails" do
-    TestFramework.frameworks[:junit].stub!(:run).and_return [@tests.last]
+    TestFramework.instance_variable_get(:@frameworks)[:junit].stub!(:run).and_return [@tests.last]
     lambda { project("foo").test.invoke }.should raise_error(RuntimeError, /Tests failed/)
   end
 
@@ -215,7 +215,7 @@
     @tests = ["FailingTest1", "FailingTest2"]
     define "foo"
     project("foo").test.stub!(:files).and_return @tests
-    TestFramework.frameworks[:junit].stub!(:run).and_return @tests
+    TestFramework.instance_variable_get(:@frameworks)[:junit].stub!(:run).and_return @tests
   end
 
   it "should fail" do
@@ -530,7 +530,7 @@
 
   it "should include the compiled target in its dependencies" do
     define("foo") { compile.into "odd" }
-    project("foo").test.compile.dependencies.should include(project("foo").file("odd"))
+    project("foo").test.compile.dependencies.should include(file('odd'))
   end
 
   it "should include the test framework artifacts in its dependencies" do

Modified: incubator/buildr/trunk/spec/transport_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/transport_spec.rb?rev=609118&r1=609117&r2=609118&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/transport_spec.rb (original)
+++ incubator/buildr/trunk/spec/transport_spec.rb Sat Jan  5 02:06:53 2008
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), 'sandbox')
+require File.join(File.dirname(__FILE__), 'spec_helpers')
 
 
 describe URI, "#download" do