Skip to content

Commit

Permalink
filter using Filter instead of calling the inclusion/exclusion filter…
Browse files Browse the repository at this point in the history
…s separately
  • Loading branch information
dchelimsky committed Nov 5, 2011
1 parent 9eb45d3 commit 2595ccf
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 277 deletions.
35 changes: 15 additions & 20 deletions lib/rspec/core/configuration.rb
Expand Up @@ -76,11 +76,6 @@ def #{name}
add_setting :show_failures_in_pending_blocks
add_setting :order

DEFAULT_EXCLUSION_FILTERS = {
:if => lambda { |value, metadata| metadata.has_key?(:if) && !value },
:unless => lambda { |value| value }
}

DEFAULT_BACKTRACE_PATTERNS = [
/\/lib\d*\/ruby\//,
/org\/jruby\//,
Expand All @@ -102,7 +97,6 @@ def initialize
@backtrace_clean_patterns = DEFAULT_BACKTRACE_PATTERNS.dup
@default_path = 'spec'
@filter = Filter.new
@filter.exclude DEFAULT_EXCLUSION_FILTERS.dup
@preferred_options = {}
@seed = srand % 0xFFFF
end
Expand All @@ -111,6 +105,14 @@ def force(hash)
@preferred_options.merge!(hash)
end

def force_include(hash)
@filter.include hash
end

def force_exclude(hash)
@filter.exclude hash
end

def reset
@reporter = nil
@formatters.clear
Expand Down Expand Up @@ -474,7 +476,7 @@ def filter_run_including(*args)
elsif contains_standalone_filter?(filter)
@filter.inclusions.replace(filter)
else
@filter.include filter
@filter.include :late, filter
end
end

Expand All @@ -484,15 +486,15 @@ def filter_run_including(*args)
# want any inclusion filter at all.
def inclusion_filter=(filter)
filter = build_metadata_hash_from([filter])
filter.empty? ? @filter.inclusions.clear : @filter.inclusions.replace(filter)
filter.empty? ? inclusion_filter.clear : inclusion_filter.replace(filter)
end

alias_method :filter=, :inclusion_filter=

# Returns the `inclusion_filter`. If none has been set, returns an empty
# hash.
def inclusion_filter
value_for(:inclusion_filter, @filter.inclusions) || {}
@filter.inclusions
end

alias_method :filter, :inclusion_filter
Expand All @@ -508,20 +510,20 @@ def inclusion_filter
# # with treat_symbols_as_metadata_keys_with_true_values = true
# filter_run_excluding :foo # results in {:foo => true}
def filter_run_excluding(*args)
@filter.exclude build_metadata_hash_from(args)
@filter.exclude :late, build_metadata_hash_from(args)
end

# Clears and reassigns the `exclusion_filter`. Set to `nil` if you don't
# want any exclusion filter at all.
def exclusion_filter=(filter)
filter = build_metadata_hash_from([filter])
filter.empty? ? @filter.exclusions.clear : @filter.exclusions.replace(filter)
filter.empty? ? exclusion_filter.clear : exclusion_filter.replace(filter)
end

# Returns the `exclusion_filter`. If none has been set, returns an empty
# hash.
def exclusion_filter
value_for(:exclusion_filter, @filter.exclusions) || {}
@filter.exclusions
end

STANDALONE_FILTERS = [:line_numbers, :full_description]
Expand Down Expand Up @@ -598,14 +600,7 @@ def order=(type)
private

def value_for(key, default=nil)
if [:inclusion_filter, :exclusion_filter].any? {|k| key == k}
local = { :inclusion_filter => @filter.inclusions, :exclusion_filter => @filter.exclusions }
Configuration.reconcile_opposing_filters(local, @preferred_options, :inclusion_filter, :exclusion_filter)
Configuration.reconcile_opposing_filters(local, @preferred_options, :exclusion_filter, :inclusion_filter)
@preferred_options.has_key?(key) ? default.merge(@preferred_options[key]) : default
else
@preferred_options.has_key?(key) ? @preferred_options[key] : default
end
@preferred_options.has_key?(key) ? @preferred_options[key] : default
end

def add_location(file_path, line_numbers)
Expand Down
3 changes: 3 additions & 0 deletions lib/rspec/core/configuration_options.rb
Expand Up @@ -12,6 +12,9 @@ def initialize(args)
def configure(config)
formatters = options.delete(:formatters)

config.force_exclude options.delete(:exclusion_filter) || {}
config.force_include options.delete(:inclusion_filter) || {}

order(options.keys, :libs, :requires, :default_path, :pattern).each do |key|
force?(key) ? config.force(key => options[key]) : config.send("#{key}=", options[key])
end
Expand Down
4 changes: 4 additions & 0 deletions lib/rspec/core/example.rb
Expand Up @@ -132,6 +132,10 @@ def fail_with_exception(reporter, exception)
finish(reporter)
end

def any_apply?(filters)
metadata.any_apply?(filters)
end

private

def with_around_hooks(&block)
Expand Down
66 changes: 59 additions & 7 deletions lib/rspec/core/filter.rb
@@ -1,17 +1,63 @@
module RSpec
module Core
class Filter
DEFAULT_EXCLUSIONS = {
:if => lambda { |value, metadata| metadata.has_key?(:if) && !value },
:unless => lambda { |value| value }
}

module Describable
PROC_HEX_NUMBER = /0x[0-9a-f]+@/
PROJECT_DIR = File.expand_path('.')

def description
reject { |k, v| RSpec::Core::Filter::DEFAULT_EXCLUSIONS[k] == v }.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','')
end

def empty_without_conditional_filters?
reject { |k, v| RSpec::Core::Filter::DEFAULT_EXCLUSIONS[k] == v }.empty?
end

def reject
super rescue {}
end

def empty?
super rescue false
end
end

module BackwardCompatibility
# This is to support a use case that probably doesn't exist: overriding
# the if/unless procs.
#
# TODO - add deprecation warning on :if/:unless
def update(orig, opposite, *updates)
if updates.last.has_key?(:unless)
@exclusions[:unless] = updates.last.delete(:unless)
end
if updates.last.has_key?(:if)
@exclusions[:if] = updates.last.delete(:if)
end

super
end
end

attr_reader :exclusions, :inclusions

def initialize
@exclusions = {}
@inclusions = {}
@exclusions = DEFAULT_EXCLUSIONS.dup.extend(Describable)
@inclusions = {}.extend(Describable)
extend(BackwardCompatibility)
end

def filter(examples)
def prune(examples)
examples.select {|e| !exclude?(e) && include?(e)}
end

alias_method :filter, :prune

def exclude?(example)
@exclusions.empty? ? false : example.any_apply?(@exclusions)
end
Expand All @@ -21,15 +67,21 @@ def include?(example)
end

def exclude(*args)
@exclusions = update(@exclusions, *args)
@exclusions = update(@exclusions, @inclusions, *args)
end

def include(*args)
@inclusions = update(@inclusions, *args)
@inclusions = update(@inclusions, @exclusions, *args)
end

def update(orig, *updates)
updates.length == 2 ? updates.last.merge(orig) : orig.merge(updates.last)
def update(orig, opposite, *updates)
if updates.length == 2
updated = updates.last.merge(orig)
opposite.each_key {|k| updated.delete(k)}
orig.replace(updated)
else
orig.merge!(updates.last).each_key {|k| opposite.delete(k)}
end
end
end
end
Expand Down
47 changes: 9 additions & 38 deletions lib/rspec/core/world.rb
Expand Up @@ -2,27 +2,6 @@ module RSpec
module Core
class World

module Describable
PROC_HEX_NUMBER = /0x[0-9a-f]+@/
PROJECT_DIR = File.expand_path('.')

def description
reject { |k, v| RSpec::Core::Configuration::DEFAULT_EXCLUSION_FILTERS[k] == v }.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','')
end

def empty_without_conditional_filters?
reject { |k, v| RSpec::Core::Configuration::DEFAULT_EXCLUSION_FILTERS[k] == v }.empty?
end

def reject
super rescue {}
end

def empty?
super rescue false
end
end

include RSpec::Core::Hooks

attr_reader :example_groups, :filtered_examples, :wants_to_quit
Expand All @@ -31,13 +10,10 @@ def empty?
def initialize(configuration=RSpec.configuration)
@configuration = configuration
@example_groups = [].extend(Extensions::Ordered)
@configuration.inclusion_filter.extend(Describable)
@configuration.exclusion_filter.extend(Describable)
@filtered_examples = Hash.new { |hash,group|
hash[group] = begin
examples = group.examples.dup
examples = apply_exclusion_filters(examples, exclusion_filter)
examples = apply_inclusion_filters(examples, inclusion_filter)
examples = filter.filter(examples)
examples.uniq
examples.extend(Extensions::Ordered)
end
Expand All @@ -48,17 +24,22 @@ def reset
example_groups.clear
end

# TODO - fix me
def filter
@configuration.instance_variable_get("@filter")
end

def register(example_group)
example_groups << example_group
example_group
end

def inclusion_filter
@configuration.inclusion_filter.extend(Describable)
@configuration.inclusion_filter
end

def exclusion_filter
@configuration.exclusion_filter.extend(Describable)
@configuration.exclusion_filter
end

def configure_group(group)
Expand All @@ -73,16 +54,6 @@ def example_count
example_groups.collect {|g| g.descendants}.flatten.inject(0) { |sum, g| sum += g.filtered_examples.size }
end

def apply_inclusion_filters(examples, filters)
filters.empty? ? examples : examples.select {|e| e.metadata.any_apply?(filters)}
end

alias_method :find, :apply_inclusion_filters

def apply_exclusion_filters(examples, filters)
filters.empty? ? examples : examples.reject {|e| e.metadata.any_apply?(filters)}
end

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
Expand All @@ -99,7 +70,7 @@ def announce_filters
if @configuration.run_all_when_everything_filtered? && example_count.zero?
reporter.message( "No examples matched #{inclusion_filter.description}. Running all.")
filtered_examples.clear
@configuration.inclusion_filter.clear
inclusion_filter.clear
end

announce_inclusion_filter filter_announcements
Expand Down
1 change: 0 additions & 1 deletion spec/rspec/core/command_line_spec.rb
Expand Up @@ -96,7 +96,6 @@ def config_options(argv=[])
config.should_receive(:error_stream=).ordered
config.should_receive(:output_stream=).ordered
config.should_receive(:force).with(:default_path => anything).ordered
config.should_receive(:force).with(:exclusion_filter => anything).ordered
config.should_receive(:force).with(:color => true).ordered
command_line.run(err, out) rescue nil
end
Expand Down
4 changes: 2 additions & 2 deletions spec/rspec/core/configuration_options_spec.rb
Expand Up @@ -65,14 +65,14 @@
it "forces inclusion_filter" do
opts = config_options_object(*%w[--tag foo:bar])
config = RSpec::Core::Configuration.new
config.should_receive(:force).with(:inclusion_filter => {:foo => 'bar'})
config.should_receive(:force_include).with(:foo => 'bar')
opts.configure(config)
end

it "forces exclusion_filter" do
opts = config_options_object(*%w[--tag ~foo:bar])
config = RSpec::Core::Configuration.new
config.should_receive(:force).with(:exclusion_filter => {:foo => 'bar'})
config.should_receive(:force_exclude).with(:foo => 'bar')
opts.configure(config)
end

Expand Down
4 changes: 2 additions & 2 deletions spec/rspec/core/configuration_spec.rb
Expand Up @@ -651,7 +651,7 @@ def metadata_hash(*args)
end

it "gets overrided by forced options" do
config.force :exclusion_filter => { :foo => true }
config.force_exclude :foo => true
config.filter_run_including :foo => true
config.inclusion_filter.should eq({})
end
Expand Down Expand Up @@ -704,7 +704,7 @@ def metadata_hash(*args)

it "gets overrided by forced options" do
config.exclusion_filter.clear
config.force :inclusion_filter => { :foo => true }
config.force_include :foo => true
config.filter_run_excluding :foo => true
config.exclusion_filter.should eq({})
end
Expand Down
17 changes: 14 additions & 3 deletions spec/rspec/core/example_group_spec.rb
Expand Up @@ -121,7 +121,11 @@ def metadata_hash(*args)

shared_examples "matching filters" do
context "inclusion" do
before { world.stub(:inclusion_filter).and_return(filter_metadata) }
before do
filter = Filter.new
filter.include filter_metadata
world.stub(:filter => filter)
end

it "includes examples in groups matching filter" do
group = ExampleGroup.describe("does something", spec_metadata)
Expand All @@ -145,7 +149,12 @@ def metadata_hash(*args)
end

context "exclusion" do
before { world.stub(:exclusion_filter).and_return(filter_metadata) }
before do
filter = Filter.new
filter.exclude filter_metadata
world.stub(:filter => filter)
end

it "excludes examples in groups matching filter" do
group = ExampleGroup.describe("does something", spec_metadata)
group.stub(:world) { world }
Expand Down Expand Up @@ -245,7 +254,9 @@ def metadata_hash(*args)

context "with no examples or groups that match filters" do
it "returns none" do
world.stub(:inclusion_filter).and_return({ :awesome => false })
filter = Filter.new
filter.include :awesome => false
world.stub(:filter => filter)
group = ExampleGroup.describe
group.stub(:world) { world }
group.example("does something")
Expand Down

0 comments on commit 2595ccf

Please sign in to comment.