Author: vborja
Date: Tue Sep 23 12:14:17 2008
New Revision: 698308

URL: http://svn.apache.org/viewvc?rev=698308&view=rev
Log:
Added a custom RSpec formatter using yaml so that buildr can know the succeeded 
tests.
Extracted common logic for RSpec and JTestr into module JRubyBased.

Modified:
    incubator/buildr/trunk/lib/buildr/java/bdd.rb
    incubator/buildr/trunk/lib/buildr/java/jtestr_runner.rb.erb
    incubator/buildr/trunk/lib/buildr/java/test_result.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=698308&r1=698307&r2=698308&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/java/bdd.rb (original)
+++ incubator/buildr/trunk/lib/buildr/java/bdd.rb Tue Sep 23 12:14:17 2008
@@ -73,6 +73,39 @@
       end
     end
 
+
+    def run(tests, dependencies)
+      dependencies |= [task.compile.target.to_s]
+      
+      spec_dir = task.project.path_to(:source, :spec, :ruby)
+      report_dir = task.report_to.to_s
+      FileUtils.rm_rf report_dir
+      FileUtils.mkdir_p report_dir
+      ENV['CI_REPORTS'] = report_dir
+
+      runner = runner_config
+      runner.content = runner_content(binding)
+      
+      Buildr.write(runner.file, runner.content)
+      FileUtils.rm_f runner.result
+      
+      if RUBY_PLATFORM[/java/] && !options.fork
+        runtime = new_runtime
+        
runtime.getObject.java.lang.System.getProperties().putAll(options[:properties] 
|| {})
+        runtime.getLoadService.require runner.file
+      else
+        cmd_options = task.options.only(:properties, :java_args)
+        cmd_options.update(:classpath => dependencies, :project => 
task.project)
+        jruby runner.file, tests, cmd_options
+      end
+      
+      result = YAML.load(File.read(runner.result))
+      if Exception === result
+        raise [result.message, result.backtrace].flatten.join("\n")
+      end
+      result.succeeded
+    end
+
     def jruby_home
       @jruby_home ||= RUBY_PLATFORM =~ /java/ ? Config::CONFIG['prefix'] : 
         ( ENV['JRUBY_HOME'] || File.expand_path("~/.jruby") )
@@ -82,6 +115,8 @@
       !Dir.glob(File.join(jruby_home, 'lib', 'jruby*.jar')).empty?
     end
 
+  protected
+    
     def jruby(*args)
       java_args = ["org.jruby.Main", *args]
       java_args << {} unless Hash === args.last
@@ -104,7 +139,7 @@
       yield config if block_given?
       Java.org.jruby.Ruby.newInstance config
     end
-
+    
     def jruby_gem
       %{
        require 'jruby'
@@ -132,6 +167,22 @@
        end
       }
     end
+
+    def runner_config(runner = OpenStruct.new)
+      [:requires, :gems, :output, :format].each do |key|
+        runner.send("#{key}=", options[key])
+      end
+      runner.html_report ||= File.join(task.report_to.to_s, 'report.html')
+      runner.result ||= task.project.path_to(:target, :spec, 'result.yaml')
+      runner.file ||= task.project.path_to(:target, :spec, 'runner.rb')
+      runner.requires ||= []
+      runner.requires.unshift File.join(File.dirname(__FILE__), 'test_result')
+      runner.gems ||= {}
+      runner.rspec ||= ['--format', 'progress', '--format', 
"html:#{runner.html_report}"]
+      runner.format.each { |format| runner.rspec << '--format' << format } if 
runner.format
+      runner.rspec.push '--format', 
"Buildr::TestFramework::TestResult::YamlFormatter:#{runner.result}"
+      runner
+    end    
     
   end
 
@@ -141,8 +192,15 @@
   #
   #
   # Support the following options:
+  # * :gems       -- A hash of gems to install before running the tests.
+  #                  The keys of this hash are the gem name, the value must be 
the required version.
+  # * :requires   -- A list of ruby files to require before running the specs
+  #                  Mainly used if an rspec format needs to require some file.
+  # * :format     -- A list of valid Rspec --format option values. (defaults 
to 'progress')
+  # * :output     -- File path to output dump. @false@ to supress output
+  # * :fork       -- Create a new JavaVM to run the tests on
   # * :properties -- Hash of properties passed to the test suite.
-  # * :java_args -- Arguments passed to the JVM.
+  # * :java_args  -- Arguments passed to the JVM.
   class RSpec < TestFramework::JavaBDD
     @lang = :ruby
     @bdd_dir = :spec
@@ -164,55 +222,36 @@
       end
     end
 
-    def run(tests, dependencies) #:nodoc:
-      dependencies |= [task.compile.target.to_s]
-      
-      cmd_options = task.options.only(:properties, :java_args)
-      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
-
-      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})
+    def runner_config
+      runner = super
+      runner.gems.update 'rspec' => '>0'
+      runner.requires.unshift 'spec'
+      runner
+    end
+
+    def runner_content(binding)
+      runner_erb = %q{
+        <%= jruby_gem %>
+        <%= dependencies.inspect %>.each { |dep| $CLASSPATH << dep }
+        <%= runner.gems.inspect %>.each { |ary| JRuby.gem(*ary.flatten) }
+        <%= runner.requires.inspect %>.each { |rb| Kernel.require rb }
+        <% if runner.output == false %>
+          output = StringIO.new
+        <% elsif runner.output.kind_of?(String) %>
+          output = File.open(<%= result.output.inspect %>, 'w')
+        <% else %>
+          output = STDOUT
+        <% end %>
+        parser = ::Spec::Runner::OptionParser.new(output, output)
+        argv = <%= runner.rspec.inspect %> || []
+        argv.push *<%= tests.inspect %>
+        parser.order!(argv)
         $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'
+        Buildr::TestFramework::TestResult::Error.guard('<%= runner.file %>') do
+          ::Spec::Runner::CommandLine.run($rspec_options)
         end
-      end
-      
-      result = YAML.load(File.read(result_file))
-      raise result if Exception === result
-      result.succeeded
+      }
+      Filter::Mapper.new(:erb, binding).result(runner_erb)
     end
 
   end
@@ -223,10 +262,17 @@
   #
   #
   # Support the following options:
-  # * :config -- path to JtestR config file. defaults to 
@spec/ruby/jtestr_config.rb@
-  # * :output -- path to JtestR output dump. @false@ to supress output
+  # Support the following options:
+  # * :config     -- path to JtestR config file. defaults to 
@spec/ruby/jtestr_config.rb@
+  # * :gems       -- A hash of gems to install before running the tests.
+  #                  The keys of this hash are the gem name, the value must be 
the required version.
+  # * :requires   -- A list of ruby files to require before running the specs
+  #                  Mainly used if an rspec format needs to require some file.
+  # * :format     -- A list of valid Rspec --format option values. (defaults 
to 'progress')
+  # * :output     -- File path to output dump. @false@ to supress output
+  # * :fork       -- Create a new JavaVM to run the tests on
   # * :properties -- Hash of properties passed to the test suite.
-  # * :java_args -- Arguments passed to the JVM.
+  # * :java_args  -- Arguments passed to the JVM.
   class JtestR < TestFramework::JavaBDD
     @lang = :ruby
     @bdd_dir = :spec    
@@ -297,47 +343,19 @@
         tests[type.first] << rb if type
       end
       @jtestr_tests = tests
-      tests = tests.values.flatten
-      tests << nil if File.exist?(user_config)
-      tests
+      tests.values.flatten
     end
 
-    def run(tests, dependencies) #:nodoc:
-      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
-      
-      spec_dir = task.project.path_to(:source, :spec, :ruby)
+    def runner_config
+      runner = super
+      runner.gems.update 'rspec' => '>0'
+      runner.requires.unshift 'spec', 'jtestr'
+      runner
+    end
 
-      runner_file = task.project.path_to(:target, :spec, 'jtestr_runner.rb')
+    def runner_content(binding)
       runner_erb = File.join(File.dirname(__FILE__), 'jtestr_runner.rb.erb')
-      runner = Filter::Mapper.new(:erb, binding).result(File.read(runner_erb))
-      Buildr.write runner_file, runner
-
-      if /java/ === RUBY_PLATFORM
-        runtime = new_runtime :current_directory => runner_file.pathmap('%d')
-        runtime.getLoadService.require runner_file
-      else
-        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
-
-      result = YAML::load(File.read(result_file))
-      raise result if Exception === result
-      result.succeeded
+      Filter::Mapper.new(:erb, binding).result(File.read(runner_erb), 
runner_erb)
     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=698308&r1=698307&r2=698308&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:17 2008
@@ -24,19 +24,16 @@
 # |_| |___|____|___|  PREFER TO EDIT JtestR CONFIGURATION FILE:
 #
 # <%= user_config %>
-                  
+
 begin
-  <%= jruby_gem %>
-  JRuby.gem('rspec')
 
+  <%= jruby_gem %>
   <%= dependencies.map(&:to_s).inspect %>.each { |dep| $CLASSPATH << dep }
-  <%= gems.inspect %>.each { |ary| JRuby.gem(*ary.flatten) }
-  <%= requires.inspect %>.each { |rb| Kernel.require rb }
-
-  Buildr::TestFramework::TestResult.for_jtestr
-
+  <%= runner.gems.inspect %>.each { |ary| JRuby.gem(*ary.flatten) }
+  <%= runner.requires.inspect %>.each { |rb| Kernel.require rb }
+  
   jtestr = JtestR::TestRunner.new
-
+  
   class << jtestr
     def config(&block)
       @config_blocks ||= []
@@ -49,62 +46,69 @@
       config.each { |block| @configuration.instance_eval(&block) }
     end
   end
-
+  
   jtestr.config do 
     classpath [] # already loaded
     add_common_classpath false
-  
+    
     <% ts = ( @jtestr_tests[:junit] & tests ).map { |c| 'Java.' + c } %>
     junit [<%= ts.join(', ') %>]
-  
+    
     <% ts = ( @jtestr_tests[:testng] & tests ).map { |c| 'Java.' + c } %>
     testng [<%= ts.join(', ') %>]
-  
+    
     <% ts = @jtestr_tests[:testunit] & tests %>
     test_unit <%= ts.inspect %>
-  
+      
     <% ts = @jtestr_tests[:story] & tests %>
     story <%= ts.inspect %>
-  
+      
     <% ts = @jtestr_tests[:rspec] & tests %>
     rspec <%= ts.inspect %>
-  
+      
     <% ts = @jtestr_tests[:expect] & tests %>
     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
-            JtestR::SimpleLogger::ERR, # log level
-            JtestR::GenericResultHandler::QUIET, #output level 
-            StringIO.new, # output STDOUT
-            [], # groups to run 
-            Buildr::TestFramework::TestResult::JtestR, # result handler
-            []  # classpath
-          ]
+  fake_out = StringIO.new
 
-  rspec_parser = ::Spec::Runner::OptionParser.new(STDERR, STDOUT)
-  rspec_parser.order!(<%= argv.inspect %>)
-  Buildr::TestFramework::TestResult::JtestR.options = rspec_parser.options
+  <% if runner.output == false %>
+     output = fake_out
+  <% elsif runner.output.kind_of?(String) %>
+     output = File.open(<%= result.output.inspect %>, 'w')
+  <% else %>
+     output = STDOUT
+  <% end %>
+  
+  
+  args = [ '<%= spec_dir %>', # the directory to search for jtestr files
+           JtestR::SimpleLogger::ERR, # log level
+           JtestR::GenericResultHandler::QUIET, #output level 
+           fake_out, # output STDOUT
+           [], # groups to run 
+           Buildr::TestFramework::TestResult::RSpecResultHandler, # result 
handler
+           []  # classpath
+         ]
+  
+  argv = <%= runner.rspec.inspect %> || []
+  argv.push *<%= tests.inspect %>
+  Buildr::TestFramework::TestResult::RSpecResultHandler.init(argv, output, 
output)
   
   jtestr.run *args
 
 rescue => e
-  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
+  Buildr::TestFramework::TestResult::Error.dump_yaml('<%= runner.result %>', 
e) rescue \
+  puts "-[--- ERROR ---]-", e.class, e.message, *e.backtrace
 end
-
-
+  
 # Local Variables:
 # mode: ruby
 # End:
+  

Modified: 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=698308&r1=698307&r2=698308&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/java/test_result.rb (original)
+++ incubator/buildr/trunk/lib/buildr/java/test_result.rb Tue Sep 23 12:14:17 
2008
@@ -28,38 +28,49 @@
           @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
+
+        def self.dump_yaml(file, e)
+          require 'fileutils'
+          FileUtils.mkdir_p(File.dirname(file))
+          File.open(file, 'w') { |f| f.puts(YAML.dump(Error.new(e.message, 
e.backtrace))) }
         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
+
+        def self.guard(file)
+          begin 
+            yield
+          rescue
+            dump_yaml(file)
           end
         end
       end
-
+      
       attr_accessor :failed, :succeeded
 
       def initialize
         @failed, @succeeded = [], []
       end
 
-      module YamlFormatter
+      # An Rspec formatter used by buildr
+      class YamlFormatter
         attr_reader :result
+
+        attr_accessor :example_group, :options, :where
         
+        def initialize(options, where)
+          @options = options
+          @where = where
+        end
+        
+        %w[ example_started example_passed example_failed example_pending
+            start_dump dump_failure dump_summary dump_pending ].each do |meth|
+          module_eval "def #{meth}(*args); end"
+        end
+
+        def add_example_group(example_group)
+          @example_group = example_group
+        end
+
         def start(example_count)
-          super
           @result = TestResult.new
         end
 
@@ -83,22 +94,57 @@
         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
+      class RSpecResultHandler
+
+        # Workaround for http://jira.codehaus.org/browse/JTESTR-68
+        module TestNGResultHandlerMixin
+          def onTestSuccess(test_result)
+            @result_handler.succeed_single(test_result.name)
+          end
         end
 
-        module ClassMethods
+        class BacktraceTweaker
+          attr_reader :ignore_patterns
+          def initialize
+            @ignore_patterns = 
::Spec::Runner::QuietBacktraceTweaker::IGNORE_PATTERNS.dup
+            # ignore jruby/jtestr backtrace
+            ignore_patterns << /org\.jruby\.javasupport\.JavaMethod\./
+            ignore_patterns << /jtestr.*\.jar!/i << /runner\.rb/
+          end
+          
+          def clean_up_double_slashes(line)
+            line.gsub!('//','/')
+          end
+
+          def tweak_backtrace(error)
+            return if error.backtrace.nil?
+            error.backtrace.collect! do |line|
+              clean_up_double_slashes(line)
+              ignore_patterns.each do |ignore|
+                if line =~ ignore
+                  line = nil
+                  break
+                end
+              end
+              line
+            end
+            error.backtrace.compact!
+          end
+        end
+        
+        class << self
           # an rspec reporter used to proxy events to rspec formatters
           attr_reader :reporter
 
-          def options=(options)
-            @reporter = Spec::Runner::Reporter.new(options)            
+          def init(argv = [], out = STDOUT, err = STDERR)
+            ::JtestR::TestNGResultHandler.module_eval { include 
TestNGResultHandlerMixin }
+            rspec_parser = ::Spec::Runner::OptionParser.new(err, out)
+            rspec_parser.order!(argv)
+            rspec_parser.options.backtrace_tweaker = BacktraceTweaker.new
+            @reporter = Spec::Runner::Reporter.new(rspec_parser.options)
           end
 
           def before
@@ -123,6 +169,13 @@
 
         def initialize(name, desc, *args)
           self.example_group = ::Spec::Example::ExampleGroup.new(desc)
+          example_group.extend ExampleMethods
+          example_group.name = name.to_s
+          if example_group.name[/Spec/]
+            example_group.description = desc.to_s
+          else
+            example_group.description = name.to_s
+          end
           reporter.add_example_group(example_group)
         end
 
@@ -144,40 +197,42 @@
           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}"
+          name = name.to_s
+          name[/\((pen?ding|error|failure|success)\)?$/]
+          name = $`
+          current_example.description = name
+          if example_group.name[/Spec/]
+            current_example.__full_description = "#{example_group.description} 
#{name}"
+          else
+            current_example.__full_description = "#{example_group.name}: 
#{name}"
+          end
           reporter.example_started(current_example)
+          #puts "STARTED #{name} #{current_example.__full_description}"
         end
 
         def succeed_single(name = nil)
-          fail_unless_current(name)
-          reporter.example_finished(current_example)
+          #puts "SUCC SINGLE #{name}"
+          reporter.example_finished(current_example, nil)
         end
-
+        
         def fail_single(name = nil)
-          fail_unless_current(name)
-          reporter.failure(current_example, current_error)
+          #puts "FAIL SINGLE #{name}"
+          reporter.example_finished(current_example, current_error)
         end
 
         def error_single(name = nil)
-          fail_unless_current(name)
+          #puts "ERR SINGLE #{name}"
           reporter.example_finished(current_example, current_error)
         end
 
         def pending_single(name = nil)
-          fail_unless_current(name)
+          #puts "PEND SINGLE #{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
+        def current_error(fault = current_failure)
           case fault
           when nil
             nil
@@ -186,22 +241,37 @@
           when Test::Unit::Error, Expectations::Results::Error, 
Spec::Runner::Reporter::Failure
             fault.exception
           when Expectations::Results
-            fault
+            file = fault.file
+            line = fault.line
+            Error.new(fault.message, ["#{fault.file}:#{fault.line}"])
           else
             if fault.respond_to?(:test_header)
               fault.test_header[/\((.+)\)/]
-              test = $1.to_s
-              self.class.add_failure(test)
+              test_cls, test_meth = $1.to_s, $`.to_s
+              exception = fault.exception
+              (class << exception; self; end).module_eval do
+                define_method(:backtrace) do 
+                  (["#{test_cls}:in `#{test_meth}'"] + stackTrace).map { |s| 
s.to_s }
+                end
+              end
+              exception
             elsif fault.respond_to?(:method)
-              test = fault.method.test_class.name
-              self.class.add_failure(test)
+              test_cls, test_meth = fault.method.test_class.name, 
fault.method.method_name
+              exception = fault.throwable
+              (class << exception; self; end).module_eval do
+                define_method(:backtrace) do 
+                  (["#{test_cls}:in `#{test_meth}'"] + stackTrace).map { |s| 
s.to_s }
+                end
+              end
+              exception
+            else
+              raise "Cannot handle fault #{fault.class}: #{fault.inspect}"
             end
           end
         end
 
-        
       end # RSpecResultHandler
-      
+
     end # TestResult
   end
 end


Reply via email to