Skip to content

Commit

Permalink
Add dump of shell commands to re-run failed examples (inspired by
Browse files Browse the repository at this point in the history
Cucumber).
  • Loading branch information
dchelimsky committed May 21, 2011
1 parent 5554b70 commit de6531b
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 127 deletions.
12 changes: 8 additions & 4 deletions lib/rspec/core/formatters/base_formatter.rb
Expand Up @@ -12,6 +12,13 @@ class BaseFormatter
attr_reader :example_count, :pending_count, :failure_count
attr_reader :failed_examples, :pending_examples

def self.relative_path(line)
line = line.sub(File.expand_path("."), ".")
line = line.sub(/\A([^:]+:\d+)$/, '\\1')
return nil if line == '-e:1'
line
end

def initialize(output)
@output = output || StringIO.new
@example_count = @pending_count = @failure_count = 0
Expand Down Expand Up @@ -113,10 +120,7 @@ def configuration

def backtrace_line(line)
return nil if configuration.cleaned_from_backtrace?(line)
line = line.sub(File.expand_path("."), ".")
line = line.sub(/\A([^:]+:\d+)$/, '\\1')
return nil if line == '-e:1'
line
self.class.relative_path(line)
end

def read_failed_line(exception, example)
Expand Down
12 changes: 12 additions & 0 deletions lib/rspec/core/formatters/base_text_formatter.rb
Expand Up @@ -39,6 +39,18 @@ def dump_summary(duration, example_count, failure_count, pending_count)
dump_profile if profile_examples? && failure_count == 0
output.puts "\nFinished in #{format_seconds(duration)} seconds\n"
output.puts colorise_summary(summary_line(example_count, failure_count, pending_count))
dump_commands_to_rerun_failed_examples
end

def dump_commands_to_rerun_failed_examples
return if failed_examples.empty?
output.puts
output.puts("Failed examples:")
output.puts

failed_examples.each do |example|
output.puts(red("rspec #{BaseFormatter::relative_path(example.location)}") + " " + grey("# #{example.full_description}"))
end
end

def dump_profile
Expand Down
249 changes: 126 additions & 123 deletions spec/rspec/core/formatters/base_text_formatter_spec.rb
@@ -1,89 +1,112 @@
require 'spec_helper'
require 'rspec/core/formatters/base_text_formatter'

module RSpec::Core::Formatters
describe RSpec::Core::Formatters::BaseTextFormatter do
let(:output) { StringIO.new }
let(:formatter) { RSpec::Core::Formatters::BaseTextFormatter.new(output) }

describe BaseTextFormatter do
let(:output) { StringIO.new }
let(:formatter) { RSpec::Core::Formatters::BaseTextFormatter.new(output) }
describe "#summary_line" do
it "with 0s outputs pluralized (excluding pending)" do
formatter.summary_line(0,0,0).should eq("0 examples, 0 failures")
end

describe "#summary_line" do
context "with 0s" do
it "outputs pluralized (excluding pending)" do
formatter.summary_line(0,0,0).should eq("0 examples, 0 failures")
end
end
it "with 1s outputs singular (including pending)" do
formatter.summary_line(1,1,1).should eq("1 example, 1 failure, 1 pending")
end

context "with 1s" do
it "outputs singular (including pending)" do
formatter.summary_line(1,1,1).should eq("1 example, 1 failure, 1 pending")
end
end
it "with 2s outputs pluralized (including pending)" do
formatter.summary_line(2,2,2).should eq("2 examples, 2 failures, 2 pending")
end
end

context "with 2s" do
it "outputs pluralized (including pending)" do
formatter.summary_line(2,2,2).should eq("2 examples, 2 failures, 2 pending")
end
describe "#dump_commands_to_rerun_failed_examples" do
it "includes command to re-run each failed example" do
group = RSpec::Core::ExampleGroup.describe("example group") do
it("fails") { fail }
end
line = __LINE__ - 2
group.run(formatter)
formatter.dump_commands_to_rerun_failed_examples
output.string.should include("rspec #{RSpec::Core::Formatters::BaseFormatter::relative_path("#{__FILE__}:#{line}")} # example group fails")
end
end

describe "#dump_failures" do
let(:group) { RSpec::Core::ExampleGroup.describe("group name") }
describe "#dump_failures" do
let(:group) { RSpec::Core::ExampleGroup.describe("group name") }

before { RSpec.configuration.stub(:color_enabled?) { false } }
before { RSpec.configuration.stub(:color_enabled?) { false } }

def run_all_and_dump_failures
group.run(formatter)
formatter.dump_failures
end
def run_all_and_dump_failures
group.run(formatter)
formatter.dump_failures
end

it "preserves formatting" do
group.example("example name") { "this".should eq("that") }
it "preserves formatting" do
group.example("example name") { "this".should eq("that") }

run_all_and_dump_failures
run_all_and_dump_failures

output.string.should =~ /group name example name/m
output.string.should =~ /(\s+)expected \"that\"\n\1 got \"this\"/m
end

output.string.should =~ /group name example name/m
output.string.should =~ /(\s+)expected \"that\"\n\1 got \"this\"/m
context "with an exception without a message" do
it "does not throw NoMethodError" do
exception_without_message = Exception.new()
exception_without_message.stub(:message) { nil }
group.example("example name") { raise exception_without_message }
expect { run_all_and_dump_failures }.not_to raise_error(NoMethodError)
end
end

context "with an exception without a message" do
it "does not throw NoMethodError" do
exception_without_message = Exception.new()
exception_without_message.stub(:message) { nil }
group.example("example name") { raise exception_without_message }
expect { run_all_and_dump_failures }.not_to raise_error(NoMethodError)
end
context "with an exception class other than RSpec" do
it "does not show the error class" do
group.example("example name") { raise NameError.new('foo') }
run_all_and_dump_failures
output.string.should =~ /NameError/m
end
end

context "with an exception class other than RSpec" do
it "does not show the error class" do
group.example("example name") { raise NameError.new('foo') }
run_all_and_dump_failures
output.string.should =~ /NameError/m
end
context "with a failed expectation (rspec-expectations)" do
it "does not show the error class" do
group.example("example name") { "this".should eq("that") }
run_all_and_dump_failures
output.string.should_not =~ /RSpec/m
end
end

context "with a failed expectation (rspec-expectations)" do
it "does not show the error class" do
group.example("example name") { "this".should eq("that") }
run_all_and_dump_failures
output.string.should_not =~ /RSpec/m
end
context "with a failed message expectation (rspec-mocks)" do
it "does not show the error class" do
group.example("example name") { "this".should_receive("that") }
run_all_and_dump_failures
output.string.should_not =~ /RSpec/m
end
end

context "with a failed message expectation (rspec-mocks)" do
it "does not show the error class" do
group.example("example name") { "this".should_receive("that") }
run_all_and_dump_failures
output.string.should_not =~ /RSpec/m
context 'for #share_examples_for' do
it 'outputs the name and location' do

share_examples_for 'foo bar' do
it("example name") { "this".should eq("that") }
end

line = __LINE__.next
group.it_should_behave_like('foo bar')

run_all_and_dump_failures

output.string.should include(
'Shared Example Group: "foo bar" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end

context 'for #share_examples_for' do
context 'that contains nested example groups' do
it 'outputs the name and location' do

share_examples_for 'foo bar' do
it("example name") { "this".should eq("that") }
describe 'nested group' do
it("example name") { "this".should eq("that") }
end
end

line = __LINE__.next
Expand All @@ -96,98 +119,78 @@ def run_all_and_dump_failures
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end
end
end

context 'that contains nested example groups' do
it 'outputs the name and location' do
share_examples_for 'foo bar' do
describe 'nested group' do
it("example name") { "this".should eq("that") }
end
end
context 'for #share_as' do
it 'outputs the name and location' do

line = __LINE__.next
group.it_should_behave_like('foo bar')
share_as :FooBar do
it("example name") { "this".should eq("that") }
end

run_all_and_dump_failures
line = __LINE__.next
group.send(:include, FooBar)

output.string.should include(
'Shared Example Group: "foo bar" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end
end
run_all_and_dump_failures

output.string.should include(
'Shared Example Group: "FooBar" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end

context 'for #share_as' do
context 'that contains nested example groups' do
it 'outputs the name and location' do

share_as :FooBar do
it("example name") { "this".should eq("that") }
share_as :NestedFoo do
describe 'nested group' do
describe 'hell' do
it("example name") { "this".should eq("that") }
end
end
end

line = __LINE__.next
group.send(:include, FooBar)
group.send(:include, NestedFoo)

run_all_and_dump_failures

output.string.should include(
'Shared Example Group: "FooBar" called from ' +
'Shared Example Group: "NestedFoo" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end

context 'that contains nested example groups' do
it 'outputs the name and location' do

share_as :NestedFoo do
describe 'nested group' do
describe 'hell' do
it("example name") { "this".should eq("that") }
end
end
end

line = __LINE__.next
group.send(:include, NestedFoo)

run_all_and_dump_failures

output.string.should include(
'Shared Example Group: "NestedFoo" called from ' +
"./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}"
)
end
end
end
end
end

describe "#dump_profile" do
before do
formatter.stub(:examples) do
group = RSpec::Core::ExampleGroup.describe("group") do
example("example")
end
group.run(double('reporter').as_null_object)
group.examples
describe "#dump_profile" do
before do
formatter.stub(:examples) do
group = RSpec::Core::ExampleGroup.describe("group") do
example("example")
end
group.run(double('reporter').as_null_object)
group.examples
end
end

it "names the example" do
formatter.dump_profile
output.string.should =~ /group example/m
end
it "names the example" do
formatter.dump_profile
output.string.should =~ /group example/m
end

it "prints the time" do
formatter.dump_profile
output.string.should =~ /0(\.\d+)? seconds/
end
it "prints the time" do
formatter.dump_profile
output.string.should =~ /0(\.\d+)? seconds/
end

it "prints the path" do
formatter.dump_profile
filename = __FILE__.split(File::SEPARATOR).last
it "prints the path" do
formatter.dump_profile
filename = __FILE__.split(File::SEPARATOR).last

output.string.should =~ /#{filename}\:#{__LINE__ - 21}/
end
output.string.should =~ /#{filename}\:#{__LINE__ - 21}/
end
end
end

0 comments on commit de6531b

Please sign in to comment.