You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by vb...@apache.org on 2008/09/23 21:14:06 UTC

svn commit: r698306 - in /incubator/buildr/trunk: lib/buildr/java/bdd.rb lib/buildr/java/jtestr_result_handler.rb lib/buildr/java/jtestr_runner.rb.erb lib/buildr/java/test_result.rb spec/java/bdd_spec.rb

Author: vborja
Date: Tue Sep 23 12:14:06 2008
New Revision: 698306

URL: http://svn.apache.org/viewvc?rev=698306&view=rev
Log:
Created an rspec YAMLFormatter so that buildr can know which tests failed.

Removed ci_reporter dependency

Added:
    incubator/buildr/trunk/lib/buildr/java/test_result.rb
Removed:
    incubator/buildr/trunk/lib/buildr/java/jtestr_result_handler.rb
Modified:
    incubator/buildr/trunk/lib/buildr/java/bdd.rb
    incubator/buildr/trunk/lib/buildr/java/jtestr_runner.rb.erb
    incubator/buildr/trunk/spec/java/bdd_spec.rb

Modified: incubator/buildr/trunk/lib/buildr/java/bdd.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/buildr/java/bdd.rb?rev=698306&r1=698305&r2=698306&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/java/bdd.rb (original)
+++ incubator/buildr/trunk/lib/buildr/java/bdd.rb Tue Sep 23 12:14:06 2008
@@ -13,8 +13,8 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-require 'yaml'
 require 'buildr/java/tests'
+require 'buildr/java/test_result'
 
 module Buildr
 
@@ -44,6 +44,7 @@
   end
 
   module TestFramework::JRubyBased
+    extend self
 
     VERSION = '1.1.4' unless const_defined?('VERSION')
 
@@ -64,7 +65,11 @@
 
     module ClassMethods
       def dependencies
-        super + (RUBY_PLATFORM[/java/] ? [] : JRubyBased.dependencies)
+        deps = super
+        unless RUBY_PLATFORM[/java/] && TestFramework::JRubyBased.jruby_installed?
+          deps |= TestFramework::JRubyBased.dependencies
+        end
+        deps
       end
     end
 
@@ -73,11 +78,17 @@
         ( ENV['JRUBY_HOME'] || File.expand_path("~/.jruby") )
     end
 
+    def jruby_installed?
+      !Dir.glob(File.join(jruby_home, 'lib', 'jruby*.jar')).empty?
+    end
+
     def jruby(*args)
       java_args = ["org.jruby.Main", *args]
       java_args << {} unless Hash === args.last
       cmd_options = java_args.last
       project = cmd_options.delete(:project)
+      cmd_options[:classpath] ||= []
+      Dir.glob(File.join(jruby_home, 'lib', '*.jar')) { |jar| cmd_options[:classpath] << jar }
       cmd_options[:java_args] ||= []
       cmd_options[:java_args] << "-Xmx512m" unless cmd_options[:java_args].detect {|a| a =~ /^-Xmx/}
       cmd_options[:properties] ||= {}
@@ -93,12 +104,48 @@
       yield config if block_given?
       Java.org.jruby.Ruby.newInstance config
     end
+
+    def jruby_gem
+      %{
+       require 'jruby'
+       def JRuby.gem(name, version = '>0', *args)
+          require 'rbconfig'
+          jruby_home = Config::CONFIG['prefix']
+          expected_version = '#{TestFramework::JRubyBased.version}'
+          unless JRUBY_VERSION >= expected_version
+            fail "Expected JRuby version \#{expected_version} installed at \#{jruby_home} but got \#{JRUBY_VERSION}"
+          end
+          if Dir.glob(File.join(jruby_home, 'lib', 'jruby*.jar')).empty?
+            require 'jruby/extract'
+            JRuby::Extract.new.extract
+          end
+          require 'rubygems'
+          begin
+            Kernel.gem name, version
+          rescue LoadError, Gem::LoadError => e
+            require 'rubygems/gem_runner'
+            Gem.manage_gems
+            args = ['install', name, '--version', version] + args
+            Gem::GemRunner.new.run(args)
+            Kernel.gem name, version
+          end
+       end
+      }
+    end
     
   end
-  
+
+  # <a href="http://rspec.info">RSpec</a> is the defacto BDD framework for ruby.
+  # To test your project with RSpec use:
+  #   test.using :rspec
+  #
+  #
+  # Support the following options:
+  # * :properties -- Hash of properties passed to the test suite.
+  # * :java_args -- Arguments passed to the JVM.
   class RSpec < TestFramework::JavaBDD
     @lang = :ruby
-    @bdd_dir = :spec    
+    @bdd_dir = :spec
 
     include TestFramework::JRubyBased
   
@@ -118,18 +165,54 @@
     end
 
     def run(tests, dependencies) #:nodoc:
-      #jruby_home or fail "To use RSpec you must either run on JRuby or have JRUBY_HOME set"
+      dependencies |= [task.compile.target.to_s]
+      
       cmd_options = task.options.only(:properties, :java_args)
-      #dependencies.push *Dir.glob(File.join(jruby_home, "lib/*.jar")) if RUBY_PLATFORM =~ /java/
-      cmd_options.update :classpath => dependencies, :project => task.project
+      cmd_options.update :classpath => dependencies, :project => task.project, :name => 'RSpec'
 
       report_dir = task.report_to.to_s
       FileUtils.rm_rf report_dir
       ENV['CI_REPORTS'] = report_dir
 
-      jruby '-Ilib', '-S', 'spec', '--require', 'ci/reporter/rake/rspec_loader',
-        '--format', 'CI::Reporter::RSpecDoc', tests, cmd_options.merge(:name => 'RSpec')
-      tests
+      result_file = File.join(report_dir, 'result.yaml')
+
+      requires = task.options[:requires] || []
+      requires.push 'spec', File.join(File.dirname(__FILE__), 'test_result')
+      gems = task.options[:gems] || {}
+      argv = task.options[:args] || [ '--format', 'progress' ]
+      argv.push '--format', "Buildr::TestFramework::TestResult::RSpec:#{result_file}"
+      argv.push *tests
+
+      runner = %{
+        #{ jruby_gem }
+        JRuby.gem('rspec')
+        #{ dependencies.inspect }.each { |dep| $CLASSPATH << dep }
+        #{ gems.inspect }.each { |ary| JRuby.gem(*ary.flatten) }
+        #{ requires.inspect }.each { |rb| Kernel.require rb }
+        Buildr::TestFramework::TestResult.for_rspec
+        parser = ::Spec::Runner::OptionParser.new(STDERR, STDOUT)
+        parser.order!(#{argv.inspect})
+        $rspec_options = parser.options
+        ::Spec::Runner::CommandLine.run($rspec_options)
+      }
+
+      runner_file = task.project.path_to(:target, :spec, 'rspec_runner.rb')
+      Buildr.write runner_file, runner
+
+      if /java/ === RUBY_PLATFORM
+        runtime = new_runtime :current_directory => runner_file.pathmap('%d')
+        runtime.getLoadService.require runner_file
+      else
+        begin
+          jruby runner_file, tests, cmd_options
+        ensure
+          FileUtils.cp_r task.project.path_to(nil), '/tmp/foo'
+        end
+      end
+      
+      result = YAML.load(File.read(result_file))
+      raise result if Exception === result
+      result.succeeded
     end
 
   end
@@ -203,7 +286,7 @@
     end
 
     def tests(dependencies) #:nodoc:
-      dependencies += [task.compile.target.to_s]
+      dependencies |= [task.compile.target.to_s]
       types = { :story => STORY_PATTERN, :rspec => RSpec::TESTS_PATTERN,
                 :testunit => TESTUNIT_PATTERN, :expect => EXPECT_PATTERN }
       tests = types.keys.inject({}) { |h, k| h[k] = []; h }
@@ -220,9 +303,21 @@
     end
 
     def run(tests, dependencies) #:nodoc:
-      dependencies += [task.compile.target.to_s]
+      dependencies |= [task.compile.target.to_s]
+
+      result_file = File.join(task.report_to.to_s, 'result.yaml')
+
+      requires = task.options[:requires] || []
+      requires.push 'spec', 'jtestr', File.join(File.dirname(__FILE__), 'test_result')
+      gems = task.options[:gems] || {}
+      argv = task.options[:args] || [ '--format', 'progress' ]
+      argv.push '--format', "Buildr::TestFramework::TestResult::RSpec:#{result_file}"
+      argv.push *tests
+
+      report_dir = task.report_to.to_s
+      FileUtils.rm_rf report_dir
+      ENV['CI_REPORTS'] = report_dir
       
-      yaml_report = File.join(task.report_to.to_s, 'result.yaml')
       spec_dir = task.project.path_to(:source, :spec, :ruby)
 
       runner_file = task.project.path_to(:target, :spec, 'jtestr_runner.rb')
@@ -237,11 +332,12 @@
         cmd_options = task.options.only(:properties, :java_args)
         cmd_options.update(:classpath => dependencies, :project => task.project)
         jruby runner_file, cmd_options.merge(:name => 'JtestR')
+        FileUtils.cp_r task.project.path_to(nil), '/tmp/foo'
       end
 
-      report = YAML::load(File.read(yaml_report))
-      raise (Array(report[:error][:message]) + report[:error][:backtrace]).join("\n") if report[:error]
-      report[:success]
+      result = YAML::load(File.read(result_file))
+      raise result if Exception === result
+      result.succeeded
     end
     
   end

Modified: incubator/buildr/trunk/lib/buildr/java/jtestr_runner.rb.erb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/buildr/java/jtestr_runner.rb.erb?rev=698306&r1=698305&r2=698306&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/java/jtestr_runner.rb.erb (original)
+++ incubator/buildr/trunk/lib/buildr/java/jtestr_runner.rb.erb Tue Sep 23 12:14:06 2008
@@ -26,9 +26,14 @@
 # <%= user_config %>
                   
 begin
-  require 'jruby'
+  <%= jruby_gem %>
+  JRuby.gem('rspec')
+
   <%= dependencies.map(&:to_s).inspect %>.each { |dep| $CLASSPATH << dep }
-  require '<%= File.join(File.dirname(runner_erb), "jtestr_result_handler") %>'
+  <%= gems.inspect %>.each { |ary| JRuby.gem(*ary.flatten) }
+  <%= requires.inspect %>.each { |rb| Kernel.require rb }
+
+  Buildr::TestFramework::TestResult.for_jtestr
 
   jtestr = JtestR::TestRunner.new
 
@@ -68,33 +73,34 @@
     expectations <%= ts.inspect %>
   
     ignore __FILE__
-  
+
     if File.file?(<%= user_config.inspect %>)
       ignore <%= user_config.inspect %>
       load <%= user_config.inspect %>
     end
   end # config
 
-  args =  [ '<%= spec_dir %>' ] # the directory to search for jtestr files
-  args << JtestR::SimpleLogger::ERR # log level
-  args << JtestR::GenericResultHandler::QUIET # verbose level
-  <% if options[:output] == false %>
-    args << StringIO.new # output
-  <% elsif options[:output].kind_of?(String) %>
-    args << File.open('<%= options[:output] %>')
-  <% else %>
-    args << STDOUT # output
-  <% end %>
-  args << [] # groups_to_run
-  args << JtestR::YAMLResultHandler # result handler
-
-  JtestR::YAMLResultHandler.report_to('<%= yaml_report %>')
-  JtestR::YAMLResultHandler.tests = <%= tests.inspect %>
+  args =  [ '<%= spec_dir %>', # the directory to search for jtestr files
+            JtestR::SimpleLogger::ERR, # log level
+            JtestR::GenericResultHandler::QUIET, #output level 
+            StringIO.new, # output STDOUT
+            [], # groups to run 
+            Buildr::TestFramework::TestResult::JtestR, # result handler
+            []  # classpath
+          ]
+
+  rspec_parser = ::Spec::Runner::OptionParser.new(STDERR, STDOUT)
+  rspec_parser.order!(<%= argv.inspect %>)
+  Buildr::TestFramework::TestResult::JtestR.options = rspec_parser.options
+  
   jtestr.run *args
 
 rescue => e
-  File.open('<%= yaml_report %>', "w") do |f| 
-    f.write YAML.dump({:error => { :message => e.message, :backtrace => e.backtrace}})
+  puts e, *e.backtrace
+  require 'fileutils'
+  FileUtils.mkdir_p(File.dirname('<%= result_file %>'))
+  File.open('<%= result_file %>', "w") do |f|
+    f.write YAML.dump(Buildr::TestFramework::TestResult::Error.new(e.message, e.backtrace))
   end
 end
 

Added: incubator/buildr/trunk/lib/buildr/java/test_result.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/buildr/java/test_result.rb?rev=698306&view=auto
==============================================================================
--- incubator/buildr/trunk/lib/buildr/java/test_result.rb (added)
+++ incubator/buildr/trunk/lib/buildr/java/test_result.rb Tue Sep 23 12:14:06 2008
@@ -0,0 +1,207 @@
+# 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 'yaml'
+
+module Buildr #:nodoc:
+  module TestFramework
+    
+    # A class used by buildr for jruby based frameworks, so that buildr can know 
+    # which tests succeeded/failed.
+    class TestResult
+
+      class Error < ::Exception
+        attr_reader :message, :backtrace
+        def initialize(message, backtrace)
+          @message = message
+          @backtrace = backtrace
+        end
+      end
+      
+      class << self
+        def for_rspec
+          unless const_defined?(:RSpec)
+            require 'spec/runner/formatter/base_formatter' # lazy loading only when using Rspec
+            cls = Class.new(Spec::Runner::Formatter::BaseFormatter) { include YamlFormatter }
+            const_set :RSpec, cls
+          end
+        end
+      
+        def for_jtestr
+          unless const_defined?(:JtestR)
+            for_rspec
+            require 'jtestr' # lazy loading only when using JtestR
+            cls = Class.new { include RSpecResultHandler }
+            const_set :JtestR, cls
+          end
+        end
+      end
+
+      attr_accessor :failed, :succeeded
+
+      def initialize
+        @failed, @succeeded = [], []
+      end
+
+      module YamlFormatter
+        attr_reader :result
+        
+        def start(example_count)
+          super
+          @result = TestResult.new
+        end
+
+        def close
+          files = options.files
+          failure_from_bt = lambda do |ary|
+            test = nil
+            ary.find do |bt|
+              bt = bt.split(':').first.strip
+              test = bt if files.include?(bt)
+            end
+            test
+          end
+          options.reporter.instance_variable_get(:@failures).each do |failure|
+            result.failed << files.delete(failure_from_bt[failure.exception.backtrace])
+          end
+          result.succeeded |= files
+          
+          FileUtils.mkdir_p(File.dirname(where))
+          File.open(where, 'w') { |f| f.puts YAML.dump(result) }
+        end
+      end # YamlFormatter
+
+      
+      # A JtestR ResultHandler
+      # Using this handler we can use RSpec formatters, like html/ci_reporter with JtestR
+      # Created for YamlFormatter
+      module RSpecResultHandler
+        def self.included(mod)
+          mod.extend ClassMethods
+          super
+        end
+
+        module ClassMethods
+          # an rspec reporter used to proxy events to rspec formatters
+          attr_reader :reporter
+
+          def options=(options)
+            @reporter = Spec::Runner::Reporter.new(options)            
+          end
+
+          def before
+            reporter.start(reporter.options.files.size)
+          end
+
+          def after
+            reporter.end
+            reporter.dump
+          end
+        end
+
+        module ExampleMethods
+          attr_accessor :name, :description, :__full_description
+        end
+
+        def reporter
+          self.class.reporter
+        end
+
+        attr_accessor :example_group, :current_example, :current_failure
+
+        def initialize(name, desc, *args)
+          self.example_group = ::Spec::Example::ExampleGroup.new(desc)
+          reporter.add_example_group(example_group)
+        end
+
+        def starting
+        end
+
+        def ending
+        end
+
+        def add_fault(fault)
+          self.current_failure = fault
+        end
+
+        def add_pending(pending)
+        end
+
+        def starting_single(name = nil)
+          self.current_failure = nil
+          self.current_example = Object.new
+          current_example.extend ::Spec::Example::ExampleMethods
+          current_example.extend ExampleMethods
+          desc = name.to_s[/(.*)\(/] ? $1 : name.to_s
+          current_example.description = desc
+          current_example.__full_description = "#{example_group.description} #{desc}"
+          reporter.example_started(current_example)
+        end
+
+        def succeed_single(name = nil)
+          fail_unless_current(name)
+          reporter.example_finished(current_example)
+        end
+
+        def fail_single(name = nil)
+          fail_unless_current(name)
+          reporter.failure(current_example, current_error)
+        end
+
+        def error_single(name = nil)
+          fail_unless_current(name)
+          reporter.example_finished(current_example, current_error)
+        end
+
+        def pending_single(name = nil)
+          fail_unless_current(name)
+          error = ::Spec::Example::ExamplePendingError.new(name)
+          reporter.example_finished(current_example, error)
+        end
+
+      private
+        def fail_unless_current(name)
+          fail "Expected #{name.inspect} to be current example but was #{current_example.description}" unless current_example.description == name.to_s
+        end
+
+        def current_error
+          fault = current_failure
+          case fault
+          when nil
+            nil
+          when Test::Unit::Failure
+            Error.new(fault.message, fault.location)
+          when Test::Unit::Error, Expectations::Results::Error, Spec::Runner::Reporter::Failure
+            fault.exception
+          when Expectations::Results
+            fault
+          else
+            if fault.respond_to?(:test_header)
+              fault.test_header[/\((.+)\)/]
+              test = $1.to_s
+              self.class.add_failure(test)
+            elsif fault.respond_to?(:method)
+              test = fault.method.test_class.name
+              self.class.add_failure(test)
+            end
+          end
+        end
+
+        
+      end # RSpecResultHandler
+      
+    end # TestResult
+  end
+end

Modified: incubator/buildr/trunk/spec/java/bdd_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/java/bdd_spec.rb?rev=698306&r1=698305&r2=698306&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/java/bdd_spec.rb (original)
+++ incubator/buildr/trunk/spec/java/bdd_spec.rb Tue Sep 23 12:14:06 2008
@@ -32,18 +32,22 @@
     foo { test.framework.should eql(:rspec) }
   end
 
-  it 'should include src/spec/ruby/**/*_spec.rb' do
-    verbose true
-    foo do 
-      spec = _('src/spec/ruby/some_spec.rb')
-      write spec, ''
-      test.invoke
-      test.tests.should include(spec)
+  it 'should run rspecs' do
+    success = File.expand_path('src/spec/ruby/success_spec.rb')
+    write(success, 'describe("success") { it("is true") { nil.should be_nil } }')
+    failure = File.expand_path('src/spec/ruby/failure_spec.rb')
+    write(failure, 'describe("failure") { it("is false") { true.should == false } }')
+    error = File.expand_path('src/spec/ruby/error_spec.rb')
+    write(error, 'describe("error") { it("raises") { eval("lambda") } }')
+    foo do
+      lambda { test.invoke }.should raise_error(/Tests failed/)
+      test.tests.should include(success, failure, error)
+      test.failed_tests.should include(failure, error)
+      test.passed_tests.should include(success)
     end
   end
 
-
-end if RUBY_PLATFORM =~ /java/ || ENV['JRUBY_HOME'] # RSpec
+end if true || RUBY_PLATFORM =~ /java/ || ENV['JRUBY_HOME'] # RSpec
 
 describe Buildr::JtestR do
 
@@ -237,6 +241,8 @@
     write(failure, 'describe("failure") { it("is false") { true.should == false } }')
     error = File.expand_path('src/spec/ruby/error_spec.rb')
     write(error, 'describe("error") { it("raises") { eval("lambda") } }')
+    pending =  File.expand_path('src/spec/ruby/pending_spec.rb')
+    write(pending, 'describe("peding") { it "is not implemented" }')
     foo do
       lambda { test.invoke }.should raise_error(/Tests failed/)
       test.tests.should include(success, failure, error)