# Copyright (c) 2006-2012 Nick Sieger # See the file LICENSE.txt included with the distribution for # software license details. require 'ci/reporter/core' module CI module Reporter module RSpecFormatters begin require 'rspec/core/formatters/base_formatter' require 'rspec/core/formatters/progress_formatter' require 'rspec/core/formatters/documentation_formatter' BaseFormatter = ::RSpec::Core::Formatters::BaseFormatter ProgressFormatter = ::RSpec::Core::Formatters::ProgressFormatter DocFormatter = ::RSpec::Core::Formatters::DocumentationFormatter # See https://github.com/nicksieger/ci_reporter/issues/76 and # https://github.com/nicksieger/ci_reporter/issues/80 require 'rspec/version' RSpec_2_12_0_bug = (::RSpec::Version::STRING == '2.12.0' && !BaseFormatter.instance_methods(false).map(&:to_s).include?("format_backtrace")) rescue LoadError => first_error begin require 'spec/runner/formatter/progress_bar_formatter' require 'spec/runner/formatter/specdoc_formatter' BaseFormatter = ::Spec::Runner::Formatter::BaseFormatter ProgressFormatter = ::Spec::Runner::Formatter::ProgressBarFormatter DocFormatter = ::Spec::Runner::Formatter::SpecdocFormatter rescue LoadError raise first_error end end end # Wrapper around a RSpec error or failure to be used by the test suite to interpret results. class RSpecFailure attr_reader :exception def initialize(failure) @failure = failure @exception = failure.exception end def failure? @failure.expectation_not_met? end def error? !failure? end def name() exception.class.name end def message() exception.message end def location() (exception.backtrace || ["No backtrace available"]).join("\n") end end class RSpec2Failure < RSpecFailure def initialize(example, formatter) @formatter = formatter @example = example if @example.respond_to?(:execution_result) @exception = @example.execution_result[:exception] || @example.execution_result[:exception_encountered] else @exception = @example.metadata[:execution_result][:exception] end end def name @exception.class.name end def message @exception.message end def failure? exception.is_a?(::RSpec::Expectations::ExpectationNotMetError) end def location output = [] output.push "#{exception.class.name << ":"}" unless exception.class.name =~ /RSpec/ output.push @exception.message format_metadata = RSpecFormatters::RSpec_2_12_0_bug ? @example.metadata : @example [@formatter.format_backtrace(@exception.backtrace, format_metadata)].flatten.each do |backtrace_info| backtrace_info.lines.each do |line| output.push " #{line}" end end output.join "\n" end end # Custom +RSpec+ formatter used to hook into the spec runs and capture results. class RSpec attr_accessor :report_manager attr_accessor :formatter def initialize(*args) @formatter ||= RSpecFormatters::ProgressFormatter.new(*args) @report_manager = ReportManager.new("spec") @suite = nil end # rspec 0.9 def add_behaviour(name) @formatter.add_behaviour(name) new_suite(name) end # Compatibility with rspec < 1.2.4 def add_example_group(example_group) @formatter.add_example_group(example_group) new_suite(description_for(example_group)) end # rspec >= 1.2.4 def example_group_started(example_group) @formatter.example_group_started(example_group) new_suite(description_for(example_group)) end def example_started(name_or_example) @formatter.example_started(name_or_example) spec = TestCase.new @suite.testcases << spec spec.start end def example_failed(name_or_example, *rest) @formatter.example_failed(name_or_example, *rest) # In case we fail in before(:all) example_started(name_or_example) if @suite.testcases.empty? if name_or_example.respond_to?(:execution_result) # RSpec 2 failure = RSpec2Failure.new(name_or_example, @formatter) else failure = RSpecFailure.new(rest[1]) # example_failed(name, counter, failure) in RSpec 1 end spec = @suite.testcases.last spec.finish spec.name = description_for(name_or_example) spec.failures << failure end def example_passed(name_or_example) @formatter.example_passed(name_or_example) spec = @suite.testcases.last spec.finish spec.name = description_for(name_or_example) end def example_pending(*args) @formatter.example_pending(*args) name = description_for(args[0]) spec = @suite.testcases.last spec.finish spec.name = "#{name} (PENDING)" spec.skipped = true end def dump_summary(*args) @formatter.dump_summary(*args) write_report @formatter.dump_failures end def respond_to?(*args) @formatter.respond_to?(*args) end # Pass through other methods to RSpec formatter for compatibility def method_missing(meth,*args,&block) @formatter.send(meth,*args,&block) end private def description_for(name_or_example) if name_or_example.respond_to?(:full_description) name_or_example.full_description elsif name_or_example.respond_to?(:metadata) name_or_example.metadata[:example_group][:full_description] elsif name_or_example.respond_to?(:description) name_or_example.description else "UNKNOWN" end end def write_report if @suite @suite.finish @report_manager.write_report(@suite) end end def new_suite(name) write_report if @suite @suite = TestSuite.new name @suite.start end end class RSpecDoc < RSpec def initialize(*args) @formatter = RSpecFormatters::DocFormatter.new(*args) super end end class RSpecBase < RSpec def initialize(*args) @formatter = RSpecFormatters::BaseFormatter.new(*args) super end end end end