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

When filtering, do not consider declaration lines from other files. #2170

Merged
merged 5 commits into from
Feb 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Bug Fixes:

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

### 3.5.0.beta1 / 2016-02-06
[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.4.2...v3.5.0.beta1)
Expand Down
10 changes: 5 additions & 5 deletions lib/rspec/core/example_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ def self.reset_memoized
@descendant_filtered_examples = nil
@_descendants = nil
@parent_groups = nil
@declaration_line_numbers = nil
@declaration_locations = nil
end

# Adds an example to the example group
Expand Down Expand Up @@ -613,10 +613,10 @@ def self.for_filtered_examples(reporter, &block)
end

# @private
def self.declaration_line_numbers
@declaration_line_numbers ||= [metadata[:line_number]] +
examples.map { |e| e.metadata[:line_number] } +
FlatMap.flat_map(children, &:declaration_line_numbers)
def self.declaration_locations
@declaration_locations ||= [Metadata.location_tuple_from(metadata)] +
examples.map { |e| Metadata.location_tuple_from(e.metadata) } +
FlatMap.flat_map(children, &:declaration_locations)
end

# @return [String] the unique id of this example group. Pass
Expand Down
5 changes: 5 additions & 0 deletions lib/rspec/core/metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ def self.id_from(metadata)
"#{metadata[:rerun_file_path]}[#{metadata[:scoped_id]}]"
end

# @private
def self.location_tuple_from(metadata)
[metadata[:absolute_file_path], metadata[:line_number]]
end

# @private
# Used internally to populate metadata hashes with computed keys
# managed by RSpec.
Expand Down
29 changes: 10 additions & 19 deletions lib/rspec/core/metadata_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ def apply?(predicate, filters, metadata)
# @private
def filter_applies?(key, value, metadata)
silence_metadata_example_group_deprecations do
return location_filter_applies?(value, metadata) if key == :locations
return id_filter_applies?(value, metadata) if key == :ids
return filters_apply?(key, value, metadata) if Hash === value
return location_filter_applies?(value, metadata) if key == :locations
return id_filter_applies?(value, metadata) if key == :ids
return filters_apply?(key, value, metadata) if Hash === value

return false unless metadata.key?(key)
return true if TrueClass === value && !!metadata[key]
Expand Down Expand Up @@ -49,13 +49,14 @@ def id_filter_applies?(rerun_paths_to_scoped_ids, metadata)
end

def location_filter_applies?(locations, metadata)
line_numbers = example_group_declaration_lines(locations, metadata)
line_number_filter_applies?(line_numbers, metadata)
end
Metadata.ascend(metadata).any? do |meta|
file_path = meta[:absolute_file_path]
line_num = meta[:line_number]

def line_number_filter_applies?(line_numbers, metadata)
preceding_declaration_lines = line_numbers.map { |n| RSpec.world.preceding_declaration_line(n) }
!(relevant_line_numbers(metadata) & preceding_declaration_lines).empty?
locations[file_path].any? do |filter_line_num|
line_num == RSpec.world.preceding_declaration_line(file_path, filter_line_num)
end
end
end

def proc_filter_applies?(key, proc, metadata)
Expand All @@ -66,16 +67,6 @@ def proc_filter_applies?(key, proc, metadata)
end
end

def relevant_line_numbers(metadata)
Metadata.ascend(metadata).map { |meta| meta[:line_number] }
end

def example_group_declaration_lines(locations, metadata)
FlatMap.flat_map(Metadata.ascend(metadata)) do |meta|
locations[meta[:absolute_file_path]]
end.uniq
end

def filters_apply?(key, value, metadata)
subhash = metadata[key]
return false unless Hash === subhash || HashImitatable === subhash
Expand Down
26 changes: 21 additions & 5 deletions lib/rspec/core/world.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,12 @@ def all_examples
# @api private
#
# Find line number of previous declaration.
def preceding_declaration_line(filter_line)
declaration_line_numbers.sort.inject(nil) do |highest_prior_declaration_line, line|
line <= filter_line ? line : highest_prior_declaration_line
def preceding_declaration_line(absolute_file_name, filter_line)
line_numbers = descending_declaration_line_numbers_by_file.fetch(absolute_file_name) do
return nil
end

line_numbers.find { |num| num <= filter_line }
end

# @private
Expand Down Expand Up @@ -179,8 +181,22 @@ def announce_exclusion_filter(announcements)

private

def declaration_line_numbers
@declaration_line_numbers ||= FlatMap.flat_map(example_groups, &:declaration_line_numbers)
def descending_declaration_line_numbers_by_file
@descending_declaration_line_numbers_by_file ||= begin
declaration_locations = FlatMap.flat_map(example_groups, &:declaration_locations)
hash_of_arrays = Hash.new { |h, k| h[k] = [] }

# TODO: change `inject` to `each_with_object` when we drop 1.8.7 support.
line_nums_by_file = declaration_locations.inject(hash_of_arrays) do |hash, (file_name, line_number)|
hash[file_name] << line_number
hash
end

line_nums_by_file.each_value do |list|
list.sort!
list.reverse!
end
end
end

def fail_if_config_and_cli_options_invalid
Expand Down
27 changes: 27 additions & 0 deletions spec/integration/filtering_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,33 @@ def run_rerun_command_for_failing_spec
run_command "spec/a_spec.rb:13 -fd" # selecting :if => false example
expect(last_cmd_stdout).to include("0 examples, 0 failures").and exclude("ex 1", "ex 2", "ex 3", "ex 4", "ex 5")
end

it 'works correctly when line numbers align with a shared example group line number from another file' do
write_file_formatted 'spec/support/shared_examples_with_matching_line.rb', "
# line 1
# line 2
# line 3
RSpec.shared_examples_for 'shared examples' do # line 4
it 'fails' do # line 5
fail 'shared example'
end
end
"

write_file_formatted 'spec/some_spec.rb', "
require File.expand_path('../support/shared_examples_with_matching_line', __FILE__) # line 1
RSpec.describe 'A group' do # line 2
it_behaves_like 'shared examples' # line 3
# line 4
it 'passes' do # line 5
expect(1).to eq(1)
end
end
"

run_command "spec/some_spec.rb:5"
expect(last_cmd_stdout).to include("1 example, 0 failures")
end
end

context "passing a line-number-filtered file and a non-filtered file" do
Expand Down
2 changes: 1 addition & 1 deletion spec/rspec/core/example_group_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1878,7 +1878,7 @@ def foo; end
def group_ids group
ids = []
['descendant_filtered_examples', 'descendants',
'parent_groups', 'declaration_line_numbers', 'before_context_ivars'].each do |method|
'parent_groups', 'declaration_locations', 'before_context_ivars'].each do |method|
ids << group.send(method).object_id
end
ids
Expand Down
4 changes: 2 additions & 2 deletions spec/rspec/core/metadata_filter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def filter_applies?(key, value, metadata)
}}

before do
expect(world).to receive(:preceding_declaration_line).at_least(:once) do |v|
preceeding_declaration_lines[v]
expect(world).to receive(:preceding_declaration_line).at_least(:once) do |_file_name, line_num|
preceeding_declaration_lines[line_num]
end
end

Expand Down
52 changes: 45 additions & 7 deletions spec/rspec/core/world_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,31 +91,35 @@ module RSpec::Core
let(:group_declaration_line) { group.metadata[:line_number] }
let(:example_declaration_line) { group_declaration_line + 2 }

def preceding_declaration_line(line_num)
world.preceding_declaration_line(__FILE__, line_num)
end

context "with one example" do
before { world.register(group) }

it "returns nil if no example or group precedes the line" do
expect(world.preceding_declaration_line(group_declaration_line - 1)).to be_nil
expect(preceding_declaration_line(group_declaration_line - 1)).to be_nil
end

it "returns the argument line number if a group starts on that line" do
expect(world.preceding_declaration_line(group_declaration_line)).to eq(group_declaration_line)
expect(preceding_declaration_line(group_declaration_line)).to eq(group_declaration_line)
end

it "returns the argument line number if an example starts on that line" do
expect(world.preceding_declaration_line(example_declaration_line)).to eq(example_declaration_line)
expect(preceding_declaration_line(example_declaration_line)).to eq(example_declaration_line)
end

it "returns line number of a group that immediately precedes the argument line" do
expect(world.preceding_declaration_line(group_declaration_line + 1)).to eq(group_declaration_line)
expect(preceding_declaration_line(group_declaration_line + 1)).to eq(group_declaration_line)
end

it "returns line number of an example that immediately precedes the argument line" do
expect(world.preceding_declaration_line(example_declaration_line + 1)).to eq(example_declaration_line)
expect(preceding_declaration_line(example_declaration_line + 1)).to eq(example_declaration_line)
end
end

context "with two exaples and the second example is registre first" do
context "with two groups and the second example is registered first" do
let(:second_group_declaration_line) { second_group.metadata[:line_number] }

before do
Expand All @@ -124,7 +128,41 @@ module RSpec::Core
end

it 'return line number of group if a group start on that line' do
expect(world.preceding_declaration_line(second_group_declaration_line)).to eq(second_group_declaration_line)
expect(preceding_declaration_line(second_group_declaration_line)).to eq(second_group_declaration_line)
end
end

context "with groups from multiple files registered" do
another_file = File.join(__FILE__, "another_spec_file.rb")

let(:group_from_another_file) do
instance_eval <<-EOS, another_file, 1
RSpec.describe("third group") do
example("inside of a gropu")
end
EOS
end

before do
world.register(group)
world.register(group_from_another_file)
end

it "returns nil if given a file name with no declarations" do
expect(world.preceding_declaration_line("/some/other/file.rb", 100_000)).to eq(nil)
end

it "considers only declaration lines from the provided files", :aggregate_failures do
expect(world.preceding_declaration_line(another_file, 1)).to eq(1)
expect(world.preceding_declaration_line(another_file, 2)).to eq(1)
expect(world.preceding_declaration_line(another_file, 3)).to eq(3)
expect(world.preceding_declaration_line(another_file, 4)).to eq(3)
expect(world.preceding_declaration_line(another_file, 5)).to eq(3)
expect(world.preceding_declaration_line(another_file, 100_000)).to eq(3)

expect(world.preceding_declaration_line(__FILE__, group_declaration_line + 1)).to eq(group_declaration_line)
end
end
end
Expand Down