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