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

Commit 358733a

Browse files
committed
When filtering, do not consider declaration lines from other files.
Before this, our logic for line number filtering asked `RSpec.world` for the preceding declaration line of a particular line number, *without* passing it a file name, which meant that it looked at the declaration line numbers from _all_ files. This was prone to producing weird filtering behavior. Fixes #2136.
1 parent 7154a0d commit 358733a

File tree

7 files changed

+97
-12
lines changed

7 files changed

+97
-12
lines changed

Changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ Bug Fixes:
55

66
* Prevent a `TypeError` from occuring when running via the rake task when
77
Ruby crashes. (Patrik Wenger, #2161)
8+
* Only consider example and group declaration lines from a specific file
9+
when applying line number filtering, instead of considering all
10+
declaration lines from all spec files. (Myron Marston, #2170)
811

912
### 3.5.0.beta1 / 2016-02-06
1013
[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.4.2...v3.5.0.beta1)

lib/rspec/core/example_group.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ def self.reset_memoized
347347
@descendant_filtered_examples = nil
348348
@_descendants = nil
349349
@parent_groups = nil
350-
@declaration_line_numbers = nil
350+
@declaration_locations = nil
351351
end
352352

353353
# Adds an example to the example group
@@ -613,10 +613,10 @@ def self.for_filtered_examples(reporter, &block)
613613
end
614614

615615
# @private
616-
def self.declaration_line_numbers
617-
@declaration_line_numbers ||= [metadata[:line_number]] +
618-
examples.map { |e| e.metadata[:line_number] } +
619-
FlatMap.flat_map(children, &:declaration_line_numbers)
616+
def self.declaration_locations
617+
@declaration_locations ||= [Metadata.location_tuple_from(metadata)] +
618+
examples.map { |e| Metadata.location_tuple_from(e.metadata) } +
619+
FlatMap.flat_map(children, &:declaration_locations)
620620
end
621621

622622
# @return [String] the unique id of this example group. Pass

lib/rspec/core/metadata.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ def self.id_from(metadata)
106106
"#{metadata[:rerun_file_path]}[#{metadata[:scoped_id]}]"
107107
end
108108

109+
# @private
110+
def self.location_tuple_from(metadata)
111+
[metadata[:absolute_file_path], metadata[:line_number]]
112+
end
113+
109114
# @private
110115
# Used internally to populate metadata hashes with computed keys
111116
# managed by RSpec.

lib/rspec/core/world.rb

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,12 @@ def all_examples
9696
# @api private
9797
#
9898
# Find line number of previous declaration.
99-
def preceding_declaration_line(_file_name, filter_line)
100-
declaration_line_numbers.sort.inject(nil) do |highest_prior_declaration_line, line|
101-
line <= filter_line ? line : highest_prior_declaration_line
99+
def preceding_declaration_line(absolute_file_name, filter_line)
100+
line_numbers = descending_declaration_line_numbers_by_file.fetch(absolute_file_name) do
101+
return nil
102102
end
103+
104+
line_numbers.find { |num| num <= filter_line }
103105
end
104106

105107
# @private
@@ -179,8 +181,22 @@ def announce_exclusion_filter(announcements)
179181

180182
private
181183

182-
def declaration_line_numbers
183-
@declaration_line_numbers ||= FlatMap.flat_map(example_groups, &:declaration_line_numbers)
184+
def descending_declaration_line_numbers_by_file
185+
@descending_declaration_line_numbers_by_file ||= begin
186+
declaration_locations = FlatMap.flat_map(example_groups, &:declaration_locations)
187+
hash_of_arrays = Hash.new { |h, k| h[k] = [] }
188+
189+
# TODO: change `inject` to `each_with_object` when we drop 1.8.7 support.
190+
line_nums_by_file = declaration_locations.inject(hash_of_arrays) do |hash, (file_name, line_number)|
191+
hash[file_name] << line_number
192+
hash
193+
end
194+
195+
line_nums_by_file.each_value do |list|
196+
list.sort!
197+
list.reverse!
198+
end
199+
end
184200
end
185201

186202
def fail_if_config_and_cli_options_invalid

spec/integration/filtering_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,33 @@ def run_rerun_command_for_failing_spec
9696
run_command "spec/a_spec.rb:13 -fd" # selecting :if => false example
9797
expect(last_cmd_stdout).to include("0 examples, 0 failures").and exclude("ex 1", "ex 2", "ex 3", "ex 4", "ex 5")
9898
end
99+
100+
it 'works correctly when line numbers align with a shared example group line number from another file' do
101+
write_file_formatted 'spec/support/shared_examples_with_matching_line.rb', "
102+
# line 1
103+
# line 2
104+
# line 3
105+
RSpec.shared_examples_for 'shared examples' do # line 4
106+
it 'fails' do # line 5
107+
fail 'shared example'
108+
end
109+
end
110+
"
111+
112+
write_file_formatted 'spec/some_spec.rb', "
113+
require File.expand_path('../support/shared_examples_with_matching_line', __FILE__) # line 1
114+
RSpec.describe 'A group' do # line 2
115+
it_behaves_like 'shared examples' # line 3
116+
# line 4
117+
it 'passes' do # line 5
118+
expect(1).to eq(1)
119+
end
120+
end
121+
"
122+
123+
run_command "spec/some_spec.rb:5"
124+
expect(last_cmd_stdout).to include("1 example, 0 failures")
125+
end
99126
end
100127

101128
context "passing a line-number-filtered file and a non-filtered file" do

spec/rspec/core/example_group_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1878,7 +1878,7 @@ def foo; end
18781878
def group_ids group
18791879
ids = []
18801880
['descendant_filtered_examples', 'descendants',
1881-
'parent_groups', 'declaration_line_numbers', 'before_context_ivars'].each do |method|
1881+
'parent_groups', 'declaration_locations', 'before_context_ivars'].each do |method|
18821882
ids << group.send(method).object_id
18831883
end
18841884
ids

spec/rspec/core/world_spec.rb

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def preceding_declaration_line(line_num)
119119
end
120120
end
121121

122-
context "with two examples and the second example is registered first" do
122+
context "with two groups and the second example is registered first" do
123123
let(:second_group_declaration_line) { second_group.metadata[:line_number] }
124124

125125
before do
@@ -131,6 +131,40 @@ def preceding_declaration_line(line_num)
131131
expect(preceding_declaration_line(second_group_declaration_line)).to eq(second_group_declaration_line)
132132
end
133133
end
134+
135+
context "with groups from multiple files registered" do
136+
another_file = File.join(__FILE__, "another_spec_file.rb")
137+
138+
let(:group_from_another_file) do
139+
instance_eval <<-EOS, another_file, 1
140+
RSpec.describe("third group") do
141+
142+
example("inside of a gropu")
143+
144+
end
145+
EOS
146+
end
147+
148+
before do
149+
world.register(group)
150+
world.register(group_from_another_file)
151+
end
152+
153+
it "returns nil if given a file name with no declarations" do
154+
expect(world.preceding_declaration_line("/some/other/file.rb", 100_000)).to eq(nil)
155+
end
156+
157+
it "considers only declaration lines from the provided files", :aggregate_failures do
158+
expect(world.preceding_declaration_line(another_file, 1)).to eq(1)
159+
expect(world.preceding_declaration_line(another_file, 2)).to eq(1)
160+
expect(world.preceding_declaration_line(another_file, 3)).to eq(3)
161+
expect(world.preceding_declaration_line(another_file, 4)).to eq(3)
162+
expect(world.preceding_declaration_line(another_file, 5)).to eq(3)
163+
expect(world.preceding_declaration_line(another_file, 100_000)).to eq(3)
164+
165+
expect(world.preceding_declaration_line(__FILE__, group_declaration_line + 1)).to eq(group_declaration_line)
166+
end
167+
end
134168
end
135169

136170
describe '#source_cache' do

0 commit comments

Comments
 (0)