Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit e4e9bd0

Browse files
committed
Use id in rerun command when the location identifies multiple examples.
1 parent 37ab540 commit e4e9bd0

File tree

7 files changed

+110
-31
lines changed

7 files changed

+110
-31
lines changed

lib/rspec/core/example.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,26 @@ def inspect_output
9292
inspect_output
9393
end
9494

95-
# Returns the argument that can be passed to the `rspec` command to rerun this example.
96-
def rerun_argument
97-
loaded_spec_files = RSpec.configuration.loaded_spec_files
95+
# Returns the location-based argument that can be passed to the `rspec` command to rerun this example.
96+
def location_rerun_argument
97+
@location_rerun_argument ||= begin
98+
loaded_spec_files = RSpec.configuration.loaded_spec_files
9899

99-
Metadata.ascending(metadata) do |meta|
100-
return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
100+
Metadata.ascending(metadata) do |meta|
101+
return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path])
102+
end
101103
end
102104
end
103105

106+
# Returns the location-based argument that can be passed to the `rspec` command to rerun this example.
107+
#
108+
# @deprecated Use {#location_rerun_argument} instead.
109+
# @note If there are multiple examples identified by this location, they will use {#id}
110+
# to rerun instead, but this method will still return the location (that's why it is deprecated!).
111+
def rerun_argument
112+
location_rerun_argument
113+
end
114+
104115
# @return [String] the unique id of this example. Pass
105116
# this at the command line to re-run this exact example.
106117
def id

lib/rspec/core/notifications.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ def colorized_totals_line(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
485485
def colorized_rerun_commands(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
486486
"\nFailed examples:\n\n" +
487487
failed_examples.map do |example|
488-
colorizer.wrap("rspec #{example.rerun_argument}", RSpec.configuration.failure_color) + " " +
488+
colorizer.wrap("rspec #{rerun_argument_for(example)}", RSpec.configuration.failure_color) + " " +
489489
colorizer.wrap("# #{example.full_description}", RSpec.configuration.detail_color)
490490
end.join("\n")
491491
end
@@ -515,6 +515,25 @@ def fully_formatted(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
515515

516516
formatted
517517
end
518+
519+
private
520+
521+
def rerun_argument_for(example)
522+
location = example.location_rerun_argument
523+
duplicate_rerun_locations.include?(location) ? example.id : location
524+
end
525+
526+
def duplicate_rerun_locations
527+
@duplicate_rerun_locations ||= begin
528+
locations = RSpec.world.all_examples.map(&:location_rerun_argument)
529+
530+
Set.new.tap do |s|
531+
locations.group_by { |l| l }.each do |l, ls|
532+
s << l if ls.count > 1
533+
end
534+
end
535+
end
536+
end
518537
end
519538

520539
# The `ProfileNotification` holds information about the results of running a

lib/rspec/core/world.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ def example_count(groups=example_groups)
8888
inject(0) { |a, e| a + e.filtered_examples.size }
8989
end
9090

91+
# @api private
92+
def all_examples
93+
flattened_groups = FlatMap.flat_map(example_groups) { |g| g.descendants }
94+
FlatMap.flat_map(flattened_groups) { |g| g.examples }
95+
end
96+
9197
# @api private
9298
#
9399
# Find line number of previous declaration.

spec/integration/filtering_spec.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66

77
it 'prints a rerun command for shared examples in external files that works to rerun' do
88
write_file "spec/support/shared_examples.rb", """
9-
RSpec.shared_examples 'a failing example' do
10-
example { expect(1).to eq(2) }
9+
RSpec.shared_examples 'with a failing example' do
10+
example { expect(1).to eq(2) } # failing
11+
example { expect(2).to eq(2) } # passing
1112
end
1213
"""
1314

1415
write_file "spec/host_group_spec.rb", """
1516
load File.expand_path('../support/shared_examples.rb', __FILE__)
1617
1718
RSpec.describe 'A group with shared examples' do
18-
include_examples 'a failing example'
19+
include_examples 'with a failing example'
1920
end
2021
2122
RSpec.describe 'A group with a passing example' do
@@ -24,7 +25,7 @@
2425
"""
2526

2627
run_command ""
27-
expect(last_cmd_stdout).to include("2 examples, 1 failure")
28+
expect(last_cmd_stdout).to include("3 examples, 1 failure")
2829
run_rerun_command_for_failing_spec
2930
expect(last_cmd_stdout).to include("1 example, 1 failure")
3031
# There was originally a bug when doing it again...

spec/rspec/core/formatters/base_text_formatter_spec.rb

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,33 +30,48 @@
3030
expect(output.string).to match("2 examples, 2 failures, 2 pending")
3131
end
3232

33-
it "includes command to re-run each failed example" do
34-
example_group = RSpec.describe("example group") do
35-
it("fails") { fail }
33+
describe "rerun command for failed examples" do
34+
it "uses the location to identify the example" do
35+
example_group = RSpec.describe("example group") do
36+
it("fails") { fail }
37+
end
38+
line = __LINE__ - 2
39+
40+
expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails")
3641
end
37-
line = __LINE__ - 2
3842

39-
expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails")
40-
end
43+
context "for an example defined in an file required by the user rather than loaded by rspec" do
44+
it "looks through ancestor metadata to find a workable re-run command" do
45+
line = __LINE__ + 1
46+
example_group = RSpec.describe("example group") do
47+
# Using eval in order to make it think this got defined in an external file.
48+
instance_eval "it('fails') { fail }", "some/external/file.rb", 1
49+
end
4150

42-
context "for an example defined in an file required by the user rather than loaded by rspec" do
43-
it "looks through ancestor metadata to find a workable re-run command" do
44-
line = __LINE__ + 1
45-
example_group = RSpec.describe("example group") do
46-
# Using eval in order to make it think this got defined in an external file.
47-
instance_eval "it('fails') { fail }", "some/external/file.rb", 1
51+
expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails")
4852
end
53+
end
4954

50-
expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails")
55+
context "for an example that is not uniquely identified by the location" do
56+
it "uses the id instead" do
57+
example_group = RSpec.describe("example group") do
58+
1.upto(2) do |i|
59+
it("compares #{i} against 2") { expect(i).to eq(2) }
60+
end
61+
end
62+
63+
expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}[1:1]")} # example group compares 1 against 2")
64+
end
5165
end
52-
end
5366

54-
def output_from_running(example_group)
55-
allow(RSpec.configuration).to receive(:loaded_spec_files) { RSpec::Core::Set.new([File.expand_path(__FILE__)]) }
56-
example_group.run(reporter)
57-
examples = example_group.examples
58-
send_notification :dump_summary, summary_notification(1, examples, examples, [], 0)
59-
output.string
67+
def output_from_running(example_group)
68+
allow(RSpec.configuration).to receive(:loaded_spec_files) { RSpec::Core::Set.new([File.expand_path(__FILE__)]) }
69+
example_group.run(reporter)
70+
examples = example_group.examples
71+
failed = examples.select { |e| e.execution_result.status == :failed }
72+
send_notification :dump_summary, summary_notification(1, examples, failed, [], 0)
73+
output.string
74+
end
6075
end
6176
end
6277

spec/rspec/core/world_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,33 @@ module RSpec::Core
2323
end
2424
end
2525

26+
describe "#all_examples" do
27+
it "contains all examples from all levels of nesting" do
28+
RSpec.describe do
29+
example("ex1")
30+
31+
context "nested" do
32+
example("ex2")
33+
34+
context "nested" do
35+
example("ex3")
36+
example("ex4")
37+
end
38+
end
39+
40+
example("ex5")
41+
end
42+
43+
RSpec.describe do
44+
example("ex6")
45+
end
46+
47+
expect(RSpec.world.all_examples.map(&:description)).to match_array(%w[
48+
ex1 ex2 ex3 ex4 ex5 ex6
49+
])
50+
end
51+
end
52+
2653
describe "#preceding_declaration_line (again)" do
2754
let(:group) do
2855
RSpec.describe("group") do

spec/support/formatter_support.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def example
214214
:full_description => "Example",
215215
:execution_result => result,
216216
:location => "",
217-
:rerun_argument => "",
217+
:location_rerun_argument => "",
218218
:metadata => {
219219
:shared_group_inclusion_backtrace => []
220220
}

0 commit comments

Comments
 (0)