# Refer: https://github.com/rspec/rspec-core/blob/cf8dfdc76586a9bb09da0bd03f0da3f2cf8fc580/spec/support/formatter_support.rb module FormatterSupport def run_example_specs_with_formatter(formatter_option, options={}, &block) output = run_rspec_with_formatter(formatter_option, options.merge(:extra_options => ["spec/rspec/core/resources/formatter_specs.rb"]), &block) return output unless options.fetch(:normalize_output, true) output = normalize_durations(output) caller_line = RSpec::Core::Metadata.relative_path(caller.first) output.lines.reject do |line| # remove the direct caller as that line is different for the summary output backtraces line.include?(caller_line) || # ignore scirpt/rspec_with_simplecov because we don't usually have it locally but # do have it on travis line.include?("script/rspec_with_simplecov") || # this line varies a bit depending on how you run the specs (via `rake` vs `rspec`) line.include?('/exe/rspec:') end.join end def run_rspec_with_formatter(formatter, options={}) extra_options = options.fetch(:extra_options) { [] } options = RSpec::Core::ConfigurationOptions.new([ "--format", formatter, "--order", "defined", *extra_options ]) err, out = StringIO.new, StringIO.new err.set_encoding("utf-8") if err.respond_to?(:set_encoding) runner = RSpec::Core::Runner.new(options) configuration = runner.configuration configuration.backtrace_formatter.exclusion_patterns << /rspec_with_simplecov/ configuration.backtrace_formatter.inclusion_patterns = [] yield runner if block_given? runner.run(err, out) out.string end def normalize_durations(output) output.gsub(/(?:\d+ minutes? )?\d+(?:\.\d+)?(s| seconds?)/) do |dur| suffix = $1 == "s" ? "s" : " seconds" "n.nnnn#{suffix}" end end if RUBY_VERSION.to_f < 1.9 def expected_summary_output_for_example_specs <<-EOS.gsub(/^\s+\|/, '').chomp |Pending: (Failures listed here are expected and do not affect your suite's status) | | 1) pending spec with no implementation is pending | # Not yet implemented | # ./spec/rspec/core/resources/formatter_specs.rb:11 | | 2) pending command with block format with content that would fail is pending | # No reason given | Failure/Error: expect(1).to eq(2) | | expected: 2 | got: 1 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:18 | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' | # ./spec/support/sandboxing.rb:14 | # ./spec/support/sandboxing.rb:7 | |Failures: | | 1) pending command with block format behaves like shared is marked as pending but passes FIXED | Expected pending 'No reason given' to fail. No Error was raised. | Shared Example Group: "shared" called from ./spec/rspec/core/resources/formatter_specs.rb:22 | # ./spec/rspec/core/resources/formatter_specs.rb:4 | | 2) failing spec fails | Failure/Error: expect(1).to eq(2) | | expected: 2 | got: 1 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:33 | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' | # ./spec/support/sandboxing.rb:14 | # ./spec/support/sandboxing.rb:7 | | 3) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: Unable to find (erb) to read failed line | | RuntimeError: | foo | # (erb):1 | | 4) a failing spec with odd backtraces fails with a backtrace containing an erb file | Failure/Error: Unable to find /foo.html.erb to read failed line | | Exception: | Exception | # /foo.html.erb:1:in `
': foo (RuntimeError) | | 5) a failing spec with odd backtraces with a `nil` backtrace raises | Failure/Error: Unable to find matching line from backtrace | | RuntimeError: | boom | |Finished in n.nnnn seconds (files took n.nnnn seconds to load) |8 examples, 5 failures, 2 pending | |Failed examples: | |rspec ./spec/rspec/core/resources/formatter_specs.rb:4 # pending command with block format behaves like shared is marked as pending but passes |rspec ./spec/rspec/core/resources/formatter_specs.rb:32 # failing spec fails |rspec ./spec/rspec/core/resources/formatter_specs.rb:38 # a failing spec with odd backtraces fails with a backtrace that has no file |rspec ./spec/rspec/core/resources/formatter_specs.rb:44 # a failing spec with odd backtraces fails with a backtrace containing an erb file |rspec ./spec/rspec/core/resources/formatter_specs.rb:62 # a failing spec with odd backtraces with a `nil` backtrace raises EOS end else def expected_summary_output_for_example_specs <<-EOS.gsub(/^\s+\|/, '').chomp |Pending: (Failures listed here are expected and do not affect your suite's status) | | 1) pending spec with no implementation is pending | # Not yet implemented | # ./spec/rspec/core/resources/formatter_specs.rb:11 | | 2) pending command with block format with content that would fail is pending | # No reason given | Failure/Error: expect(1).to eq(2) | | expected: 2 | got: 1 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:18:in `block (3 levels) in ' | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' | |Failures: | | 1) pending command with block format behaves like shared is marked as pending but passes FIXED | Expected pending 'No reason given' to fail. No Error was raised. | Shared Example Group: "shared" called from ./spec/rspec/core/resources/formatter_specs.rb:22 | # ./spec/rspec/core/resources/formatter_specs.rb:4 | | 2) failing spec fails | Failure/Error: expect(1).to eq(2) | | expected: 2 | got: 1 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:33:in `block (2 levels) in ' | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' | | 3) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: ERB.new("<%= raise 'foo' %>").result | | RuntimeError: | foo | # (erb):1:in `
' | # ./spec/rspec/core/resources/formatter_specs.rb:41:in `block (2 levels) in ' | # ./spec/support/formatter_support.rb:39:in `run_rspec_with_formatter' | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' | # ./spec/support/sandboxing.rb:14:in `block (3 levels) in ' | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' | | 4) a failing spec with odd backtraces fails with a backtrace containing an erb file | Failure/Error: Unable to find /foo.html.erb to read failed line | | Exception: | Exception | # /foo.html.erb:1:in `
': foo (RuntimeError) | | 5) a failing spec with odd backtraces with a `nil` backtrace raises | Failure/Error: Unable to find matching line from backtrace | | RuntimeError: | boom | |Finished in n.nnnn seconds (files took n.nnnn seconds to load) |8 examples, 5 failures, 2 pending | |Failed examples: | |rspec ./spec/rspec/core/resources/formatter_specs.rb:4 # pending command with block format behaves like shared is marked as pending but passes |rspec ./spec/rspec/core/resources/formatter_specs.rb:32 # failing spec fails |rspec ./spec/rspec/core/resources/formatter_specs.rb:38 # a failing spec with odd backtraces fails with a backtrace that has no file |rspec ./spec/rspec/core/resources/formatter_specs.rb:44 # a failing spec with odd backtraces fails with a backtrace containing an erb file |rspec ./spec/rspec/core/resources/formatter_specs.rb:62 # a failing spec with odd backtraces with a `nil` backtrace raises EOS end end def send_notification type, notification reporter.notify type, notification end def reporter @reporter ||= setup_reporter end def setup_reporter(*streams) config.add_formatter described_class, *streams @formatter = config.formatters.first @reporter = config.reporter end def setup_profiler config.profile_examples = true reporter.setup_profiler end def formatter_output @formatter_output ||= StringIO.new end def config @configuration ||= begin config = RSpec::Core::Configuration.new config.output_stream = formatter_output config end end def configure yield config end def formatter @formatter ||= begin setup_reporter @formatter end end def new_example(metadata = {}) metadata = metadata.dup result = RSpec::Core::Example::ExecutionResult.new result.started_at = ::Time.now result.record_finished(metadata.delete(:status) { :passed }, ::Time.now) result.exception = Exception.new if result.status == :failed instance_double(RSpec::Core::Example, :description => "Example", :full_description => "Example", :example_group => group, :execution_result => result, :location => "", :location_rerun_argument => "", :metadata => { :shared_group_inclusion_backtrace => [] }.merge(metadata) ) end def examples(n) Array.new(n) { new_example } end def group group = class_double "RSpec::Core::ExampleGroup", :description => "Group" allow(group).to receive(:parent_groups) { [group] } group end def start_notification(count) ::RSpec::Core::Notifications::StartNotification.new count end def stop_notification ::RSpec::Core::Notifications::ExamplesNotification.new reporter end def example_notification(specific_example = new_example) ::RSpec::Core::Notifications::ExampleNotification.for specific_example end def group_notification group_to_notify = group ::RSpec::Core::Notifications::GroupNotification.new group_to_notify end def message_notification(message) ::RSpec::Core::Notifications::MessageNotification.new message end def null_notification ::RSpec::Core::Notifications::NullNotification end def seed_notification(seed, used = true) ::RSpec::Core::Notifications::SeedNotification.new seed, used end def failed_examples_notification ::RSpec::Core::Notifications::ExamplesNotification.new reporter end def summary_notification(duration, examples, failed, pending, time) ::RSpec::Core::Notifications::SummaryNotification.new duration, examples, failed, pending, time end def profile_notification(duration, examples, number) ::RSpec::Core::Notifications::ProfileNotification.new duration, examples, number, reporter.instance_variable_get('@profiler').example_groups end end if RSpec::Support::RubyFeatures.module_prepends_supported? module RSpec::Core class Reporter module EnforceRSpecNotificationsListComplete def notify(event, *args) return super if caller_locations(1, 1).first.label =~ /publish/ return super if RSPEC_NOTIFICATIONS.include?(event) raise "#{event.inspect} must be added to `RSPEC_NOTIFICATIONS`" end end prepend EnforceRSpecNotificationsListComplete end end end