rspec-core 2.7.1 → 2.8.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +1 -1
- data/features/command_line/order.feature +29 -0
- data/features/command_line/tag.feature +10 -9
- data/features/configuration/default_path.feature +2 -2
- data/features/filtering/exclusion_filters.feature +1 -1
- data/features/filtering/run_all_when_everything_filtered.feature +1 -1
- data/features/subject/attribute_of_subject.feature +1 -1
- data/lib/rspec/core.rb +148 -12
- data/lib/rspec/core/command_line.rb +2 -2
- data/lib/rspec/core/configuration.rb +300 -155
- data/lib/rspec/core/configuration_options.rb +34 -53
- data/lib/rspec/core/deprecation.rb +4 -0
- data/lib/rspec/core/drb_options.rb +72 -0
- data/lib/rspec/core/example.rb +58 -24
- data/lib/rspec/core/example_group.rb +10 -5
- data/lib/rspec/core/extensions.rb +1 -0
- data/lib/rspec/core/extensions/ordered.rb +16 -0
- data/lib/rspec/core/filter_manager.rb +170 -0
- data/lib/rspec/core/formatters/base_formatter.rb +3 -1
- data/lib/rspec/core/formatters/base_text_formatter.rb +6 -0
- data/lib/rspec/core/formatters/snippet_extractor.rb +1 -1
- data/lib/rspec/core/hooks.rb +197 -1
- data/lib/rspec/core/let.rb +3 -2
- data/lib/rspec/core/metadata.rb +25 -4
- data/lib/rspec/core/option_parser.rb +89 -54
- data/lib/rspec/core/pending.rb +41 -0
- data/lib/rspec/core/rake_task.rb +9 -25
- data/lib/rspec/core/reporter.rb +43 -19
- data/lib/rspec/core/shared_context.rb +35 -0
- data/lib/rspec/core/shared_example_group.rb +0 -1
- data/lib/rspec/core/subject.rb +4 -4
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/world.rb +34 -52
- data/spec/autotest/failed_results_re_spec.rb +2 -2
- data/spec/command_line/order_spec.rb +131 -0
- data/spec/rspec/core/command_line_spec.rb +2 -1
- data/spec/rspec/core/configuration_options_spec.rb +83 -163
- data/spec/rspec/core/configuration_spec.rb +311 -139
- data/spec/rspec/core/drb_options_spec.rb +131 -0
- data/spec/rspec/core/example_group_spec.rb +22 -11
- data/spec/rspec/core/example_spec.rb +1 -2
- data/spec/rspec/core/filter_manager_spec.rb +175 -0
- data/spec/rspec/core/formatters/helpers_spec.rb +1 -1
- data/spec/rspec/core/formatters/html_formatter_spec.rb +3 -2
- data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +1 -1
- data/spec/rspec/core/metadata_spec.rb +21 -6
- data/spec/rspec/core/option_parser_spec.rb +74 -0
- data/spec/rspec/core/reporter_spec.rb +18 -1
- data/spec/rspec/core/shared_context_spec.rb +54 -17
- data/spec/rspec/core/subject_spec.rb +1 -1
- data/spec/rspec/core/world_spec.rb +7 -188
- data/spec/spec_helper.rb +47 -43
- data/spec/support/config_options_helper.rb +27 -0
- metadata +28 -12
- data/lib/rspec/core/expecting/with_rspec.rb +0 -9
- data/lib/rspec/core/expecting/with_stdlib.rb +0 -9
@@ -1,6 +1,7 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Core
|
3
|
-
|
4
5
|
class ConfigurationOptions
|
5
6
|
attr_reader :options
|
6
7
|
|
@@ -9,68 +10,39 @@ module RSpec
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def configure(config)
|
12
|
-
|
13
|
-
|
14
|
-
formatters = options[:formatters] if keys.delete(:formatters)
|
13
|
+
formatters = options.delete(:formatters)
|
15
14
|
|
16
|
-
config.
|
15
|
+
config.filter_manager = filter_manager
|
17
16
|
|
18
|
-
keys.each do |key|
|
19
|
-
config.
|
17
|
+
order(options.keys, :libs, :requires, :default_path, :pattern).each do |key|
|
18
|
+
force?(key) ? config.force(key => options[key]) : config.send("#{key}=", options[key])
|
20
19
|
end
|
21
20
|
|
22
21
|
formatters.each {|pair| config.add_formatter(*pair) } if formatters
|
23
22
|
end
|
24
23
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
argv << "--profile" if options[:profile_examples]
|
29
|
-
argv << "--backtrace" if options[:full_backtrace]
|
30
|
-
argv << "--tty" if options[:tty]
|
31
|
-
argv << "--fail-fast" if options[:fail_fast]
|
32
|
-
argv << "--options" << options[:custom_options_file] if options[:custom_options_file]
|
33
|
-
if options[:full_description]
|
34
|
-
# The argument to --example is regexp-escaped before being stuffed
|
35
|
-
# into a regexp when received for the first time (see OptionParser).
|
36
|
-
# Hence, merely grabbing the source of this regexp will retain the
|
37
|
-
# backslashes, so we must remove them.
|
38
|
-
argv << "--example" << options[:full_description].source.delete('\\')
|
39
|
-
end
|
40
|
-
if options[:line_numbers]
|
41
|
-
argv += options[:line_numbers].inject([]){|a,l| a << "--line_number" << l}
|
42
|
-
end
|
43
|
-
if options[:filter]
|
44
|
-
options[:filter].each_pair do |k, v|
|
45
|
-
argv << "--tag" << k.to_s
|
46
|
-
end
|
47
|
-
end
|
48
|
-
if options[:exclusion_filter]
|
49
|
-
options[:exclusion_filter].each_pair do |k, v|
|
50
|
-
argv << "--tag" << "~#{k.to_s}"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
if options[:formatters]
|
54
|
-
options[:formatters].each do |pair|
|
55
|
-
argv << "--format" << pair[0]
|
56
|
-
argv << "--out" << pair[1] if pair[1]
|
57
|
-
end
|
58
|
-
end
|
59
|
-
(options[:libs] || []).each do |path|
|
60
|
-
argv << "-I" << path
|
61
|
-
end
|
62
|
-
(options[:requires] || []).each do |path|
|
63
|
-
argv << "--require" << path
|
24
|
+
def parse_options
|
25
|
+
@options ||= extract_filters_from(*all_configs).inject do |merged, pending|
|
26
|
+
merged.merge(pending)
|
64
27
|
end
|
65
|
-
argv + options[:files_or_directories_to_run]
|
66
28
|
end
|
67
29
|
|
68
|
-
def
|
69
|
-
|
30
|
+
def drb_argv
|
31
|
+
DrbOptions.new(options, filter_manager).options
|
32
|
+
end
|
33
|
+
|
34
|
+
def filter_manager
|
35
|
+
@filter_manager ||= FilterManager.new
|
70
36
|
end
|
71
37
|
|
72
38
|
private
|
73
39
|
|
40
|
+
NON_FORCED_OPTIONS = [:debug, :order, :seed, :requires, :libs, :files_or_directories_to_run, :line_numbers, :full_description]
|
41
|
+
|
42
|
+
def force?(key)
|
43
|
+
!NON_FORCED_OPTIONS.include?(key)
|
44
|
+
end
|
45
|
+
|
74
46
|
def order(keys, *ordered)
|
75
47
|
ordered.reverse.each do |key|
|
76
48
|
keys.unshift(key) if keys.delete(key)
|
@@ -78,8 +50,19 @@ module RSpec
|
|
78
50
|
keys
|
79
51
|
end
|
80
52
|
|
53
|
+
def extract_filters_from(*configs)
|
54
|
+
configs.compact.each do |config|
|
55
|
+
filter_manager.include config.delete(:inclusion_filter) if config.has_key?(:inclusion_filter)
|
56
|
+
filter_manager.exclude config.delete(:exclusion_filter) if config.has_key?(:exclusion_filter)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def all_configs
|
61
|
+
@all_configs ||= file_options << command_line_options << env_options
|
62
|
+
end
|
63
|
+
|
81
64
|
def file_options
|
82
|
-
custom_options_file ? custom_options : global_options
|
65
|
+
custom_options_file ? [custom_options] : [global_options, local_options]
|
83
66
|
end
|
84
67
|
|
85
68
|
def env_options
|
@@ -113,8 +96,7 @@ module RSpec
|
|
113
96
|
end
|
114
97
|
|
115
98
|
def options_file_as_erb_string(path)
|
116
|
-
|
117
|
-
ERB.new(IO.read(path)).result(binding)
|
99
|
+
ERB.new(File.read(path)).result(binding)
|
118
100
|
end
|
119
101
|
|
120
102
|
def custom_options_file
|
@@ -133,7 +115,6 @@ module RSpec
|
|
133
115
|
nil
|
134
116
|
end
|
135
117
|
end
|
136
|
-
|
137
118
|
end
|
138
119
|
end
|
139
120
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module RSpec
|
2
2
|
|
3
3
|
class << self
|
4
|
+
# @api private
|
5
|
+
#
|
4
6
|
# Used internally to print deprecation warnings
|
5
7
|
def deprecate(method, alternate_method=nil, version=nil)
|
6
8
|
version_string = version ? "rspec-#{version}" : "a future version of RSpec"
|
@@ -25,6 +27,8 @@ ADDITIONAL
|
|
25
27
|
warn_deprecation(message)
|
26
28
|
end
|
27
29
|
|
30
|
+
# @api private
|
31
|
+
#
|
28
32
|
# Used internally to print deprecation warnings
|
29
33
|
def warn_deprecation(message)
|
30
34
|
send :warn, message
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Builds command line arguments to pass to the rspec command over DRb
|
2
|
+
class RSpec::Core::DrbOptions
|
3
|
+
def initialize(submitted_options, filter_manager)
|
4
|
+
@submitted_options = submitted_options
|
5
|
+
@filter_manager = filter_manager
|
6
|
+
end
|
7
|
+
|
8
|
+
def options
|
9
|
+
argv = []
|
10
|
+
argv << "--color" if @submitted_options[:color]
|
11
|
+
argv << "--profile" if @submitted_options[:profile_examples]
|
12
|
+
argv << "--backtrace" if @submitted_options[:full_backtrace]
|
13
|
+
argv << "--tty" if @submitted_options[:tty]
|
14
|
+
argv << "--fail-fast" if @submitted_options[:fail_fast]
|
15
|
+
argv << "--options" << @submitted_options[:custom_options_file] if @submitted_options[:custom_options_file]
|
16
|
+
argv << "--order" << @submitted_options[:order] if @submitted_options[:order]
|
17
|
+
|
18
|
+
add_full_description(argv)
|
19
|
+
add_line_numbers(argv)
|
20
|
+
add_filter(argv, :inclusion, @filter_manager.inclusions)
|
21
|
+
add_filter(argv, :exclusion, @filter_manager.exclusions)
|
22
|
+
add_formatters(argv)
|
23
|
+
add_libs(argv)
|
24
|
+
add_requires(argv)
|
25
|
+
|
26
|
+
argv + @submitted_options[:files_or_directories_to_run]
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_full_description(argv)
|
30
|
+
if @submitted_options[:full_description]
|
31
|
+
# The argument to --example is regexp-escaped before being stuffed
|
32
|
+
# into a regexp when received for the first time (see OptionParser).
|
33
|
+
# Hence, merely grabbing the source of this regexp will retain the
|
34
|
+
# backslashes, so we must remove them.
|
35
|
+
argv << "--example" << @submitted_options[:full_description].source.delete('\\')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_line_numbers(argv)
|
40
|
+
if @submitted_options[:line_numbers]
|
41
|
+
argv.push(*@submitted_options[:line_numbers].inject([]){|a,l| a << "--line_number" << l})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_filter(argv, name, hash)
|
46
|
+
hash.each_pair do |k, v|
|
47
|
+
next if [:if,:unless].include?(k)
|
48
|
+
tag = name == :inclusion ? k.to_s : "~#{k.to_s}"
|
49
|
+
tag << ":#{v.to_s}" if v.is_a?(String)
|
50
|
+
argv << "--tag" << tag
|
51
|
+
end unless hash.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_formatters(argv)
|
55
|
+
@submitted_options[:formatters].each do |pair|
|
56
|
+
argv << "--format" << pair[0]
|
57
|
+
argv << "--out" << pair[1] if pair[1]
|
58
|
+
end if @submitted_options[:formatters]
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_libs(argv)
|
62
|
+
@submitted_options[:libs].each do |path|
|
63
|
+
argv << "-I" << path
|
64
|
+
end if @submitted_options[:libs]
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_requires(argv)
|
68
|
+
@submitted_options[:requires].each do |path|
|
69
|
+
argv << "--require" << path
|
70
|
+
end if @submitted_options[:requires]
|
71
|
+
end
|
72
|
+
end
|
data/lib/rspec/core/example.rb
CHANGED
@@ -2,12 +2,6 @@ module RSpec
|
|
2
2
|
module Core
|
3
3
|
class Example
|
4
4
|
|
5
|
-
attr_reader :metadata, :options, :example_group_instance
|
6
|
-
|
7
|
-
# Returns the first exception raised, if any, in the context of running
|
8
|
-
# this example.
|
9
|
-
attr_reader :exception
|
10
|
-
|
11
5
|
def self.delegate_to_metadata(*keys)
|
12
6
|
keys.each do |key|
|
13
7
|
define_method(key) {@metadata[key]}
|
@@ -16,6 +10,24 @@ module RSpec
|
|
16
10
|
|
17
11
|
delegate_to_metadata :description, :full_description, :execution_result, :file_path, :pending, :location
|
18
12
|
|
13
|
+
# @attr_reader
|
14
|
+
#
|
15
|
+
# Returns the first exception raised in the context of running this
|
16
|
+
# example (nil if no exception is raised)
|
17
|
+
attr_reader :exception
|
18
|
+
|
19
|
+
# @attr_reader
|
20
|
+
#
|
21
|
+
# Returns the metadata object associated with this example.
|
22
|
+
attr_reader :metadata
|
23
|
+
|
24
|
+
# @attr_reader
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
# Returns the example_group_instance that provides the context for
|
28
|
+
# running this example.
|
29
|
+
attr_reader :example_group_instance
|
30
|
+
|
19
31
|
def initialize(example_group_class, desc, options, example_block=nil)
|
20
32
|
@example_group_class, @options, @example_block = example_group_class, options, example_block
|
21
33
|
@metadata = @example_group_class.metadata.for_example(desc, options)
|
@@ -23,16 +35,15 @@ module RSpec
|
|
23
35
|
@pending_declared_in_example = false
|
24
36
|
end
|
25
37
|
|
26
|
-
|
27
|
-
|
38
|
+
# @deprecated access options via metadata instead
|
39
|
+
def options
|
40
|
+
@options
|
28
41
|
end
|
29
42
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def all_apply?(filters)
|
35
|
-
@metadata.all_apply?(filters) || @example_group_class.all_apply?(filters)
|
43
|
+
# Returns the example group class that provides the context for running
|
44
|
+
# this example.
|
45
|
+
def example_group
|
46
|
+
@example_group_class
|
36
47
|
end
|
37
48
|
|
38
49
|
alias_method :pending?, :pending
|
@@ -76,16 +87,6 @@ module RSpec
|
|
76
87
|
finish(reporter)
|
77
88
|
end
|
78
89
|
|
79
|
-
def set_exception(exception)
|
80
|
-
@exception ||= exception
|
81
|
-
end
|
82
|
-
|
83
|
-
def fail_fast(reporter, exception)
|
84
|
-
start(reporter)
|
85
|
-
set_exception(exception)
|
86
|
-
finish(reporter)
|
87
|
-
end
|
88
|
-
|
89
90
|
def self.procsy(metadata, &block)
|
90
91
|
Proc.new(&block).extend(Procsy).with(metadata)
|
91
92
|
end
|
@@ -103,6 +104,39 @@ module RSpec
|
|
103
104
|
end
|
104
105
|
end
|
105
106
|
|
107
|
+
# @api private
|
108
|
+
def all_apply?(filters)
|
109
|
+
@metadata.all_apply?(filters) || @example_group_class.all_apply?(filters)
|
110
|
+
end
|
111
|
+
|
112
|
+
# @api private
|
113
|
+
def around_hooks
|
114
|
+
@around_hooks ||= example_group.around_hooks_for(self)
|
115
|
+
end
|
116
|
+
|
117
|
+
# @api private
|
118
|
+
#
|
119
|
+
# Used internally to set an exception in an after hook, which
|
120
|
+
# captures the exception but doesn't raise it.
|
121
|
+
def set_exception(exception)
|
122
|
+
@exception ||= exception
|
123
|
+
end
|
124
|
+
|
125
|
+
# @api private
|
126
|
+
#
|
127
|
+
# Used internally to set an exception and fail without actually executing
|
128
|
+
# the example when an exception is raised in before(:all).
|
129
|
+
def fail_with_exception(reporter, exception)
|
130
|
+
start(reporter)
|
131
|
+
set_exception(exception)
|
132
|
+
finish(reporter)
|
133
|
+
end
|
134
|
+
|
135
|
+
# @api private
|
136
|
+
def any_apply?(filters)
|
137
|
+
metadata.any_apply?(filters)
|
138
|
+
end
|
139
|
+
|
106
140
|
private
|
107
141
|
|
108
142
|
def with_around_hooks(&block)
|
@@ -112,7 +112,7 @@ module RSpec
|
|
112
112
|
def self.examples
|
113
113
|
@examples ||= []
|
114
114
|
end
|
115
|
-
|
115
|
+
|
116
116
|
def self.filtered_examples
|
117
117
|
world.filtered_examples[self]
|
118
118
|
end
|
@@ -121,10 +121,13 @@ module RSpec
|
|
121
121
|
@descendant_filtered_examples ||= filtered_examples + children.inject([]){|l,c| l + c.descendant_filtered_examples}
|
122
122
|
end
|
123
123
|
|
124
|
+
# @see Metadata
|
124
125
|
def self.metadata
|
125
126
|
@metadata if defined?(@metadata)
|
126
127
|
end
|
127
128
|
|
129
|
+
# @api private
|
130
|
+
# @return [Metadata] belonging to the parent of a nested [ExampleGroup](ExampleGroup)
|
128
131
|
def self.superclass_metadata
|
129
132
|
@superclass_metadata ||= self.superclass.respond_to?(:metadata) ? self.superclass.metadata : nil
|
130
133
|
end
|
@@ -156,7 +159,7 @@ module RSpec
|
|
156
159
|
end
|
157
160
|
|
158
161
|
def self.children
|
159
|
-
@children ||= []
|
162
|
+
@children ||= [].extend(Extensions::Ordered)
|
160
163
|
end
|
161
164
|
|
162
165
|
def self.descendants
|
@@ -272,7 +275,7 @@ An error occurred in an after(:all) hook.
|
|
272
275
|
begin
|
273
276
|
run_before_all_hooks(new)
|
274
277
|
result_for_this_group = run_examples(reporter)
|
275
|
-
results_for_descendants = children.map {|child| child.run(reporter)}.all?
|
278
|
+
results_for_descendants = children.ordered.map {|child| child.run(reporter)}.all?
|
276
279
|
result_for_this_group && results_for_descendants
|
277
280
|
rescue Exception => ex
|
278
281
|
fail_filtered_examples(ex, reporter)
|
@@ -284,7 +287,7 @@ An error occurred in an after(:all) hook.
|
|
284
287
|
end
|
285
288
|
|
286
289
|
def self.fail_filtered_examples(exception, reporter)
|
287
|
-
filtered_examples.each { |example| example.
|
290
|
+
filtered_examples.each { |example| example.fail_with_exception(reporter, exception) }
|
288
291
|
|
289
292
|
children.each do |child|
|
290
293
|
reporter.example_group_started(child)
|
@@ -299,7 +302,7 @@ An error occurred in an after(:all) hook.
|
|
299
302
|
end
|
300
303
|
|
301
304
|
def self.run_examples(reporter)
|
302
|
-
filtered_examples.map do |example|
|
305
|
+
filtered_examples.ordered.map do |example|
|
303
306
|
next if RSpec.wants_to_quit
|
304
307
|
instance = new
|
305
308
|
set_ivars(instance, before_all_ivars)
|
@@ -309,10 +312,12 @@ An error occurred in an after(:all) hook.
|
|
309
312
|
end.all?
|
310
313
|
end
|
311
314
|
|
315
|
+
# @api private
|
312
316
|
def self.any_apply?(filters)
|
313
317
|
metadata.any_apply?(filters)
|
314
318
|
end
|
315
319
|
|
320
|
+
# @api private
|
316
321
|
def self.all_apply?(filters)
|
317
322
|
metadata.all_apply?(filters)
|
318
323
|
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Core
|
3
|
+
# Manages the filtering of examples and groups by matching tags declared on
|
4
|
+
# the command line or options files, or filters declared via
|
5
|
+
# `RSpec.configure`, with hash key/values submitted within example group
|
6
|
+
# and/or example declarations. For example, given this declaration:
|
7
|
+
#
|
8
|
+
# describe Thing, :awesome => true do
|
9
|
+
# it "does something" do
|
10
|
+
# # ...
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# That group (or any other with `:awesome => true`) would be filtered in
|
15
|
+
# with any of the following commands:
|
16
|
+
#
|
17
|
+
# rspec --tag awesome:true
|
18
|
+
# rspec --tag awesome
|
19
|
+
# rspec -t awesome:true
|
20
|
+
# rspec -t awesome
|
21
|
+
#
|
22
|
+
# Prefixing the tag names with `~` negates the tags, thus excluding this group with
|
23
|
+
# any of:
|
24
|
+
#
|
25
|
+
# rspec --tag ~awesome:true
|
26
|
+
# rspec --tag ~awesome
|
27
|
+
# rspec -t ~awesome:true
|
28
|
+
# rspec -t ~awesome
|
29
|
+
#
|
30
|
+
# ## Options files and command line overrides
|
31
|
+
#
|
32
|
+
# Tag declarations can be stored in `.rspec`, `~/.rspec`, or a custom
|
33
|
+
# options file. This is useful for storing defaults. For example, let's
|
34
|
+
# say you've got some slow specs that you want to suppress most of the
|
35
|
+
# time. You can tag them like this:
|
36
|
+
#
|
37
|
+
# describe Something, :slow => true do
|
38
|
+
#
|
39
|
+
# And then store this in `.rspec`:
|
40
|
+
#
|
41
|
+
# --tag ~slow:true
|
42
|
+
#
|
43
|
+
# Now when you run `rspec`, that group will be excluded.
|
44
|
+
#
|
45
|
+
# ## Overriding
|
46
|
+
#
|
47
|
+
# Of course, you probably want to run them sometimes, so you can override
|
48
|
+
# this tag on the command line like this:
|
49
|
+
#
|
50
|
+
# rspec --tag slow:true
|
51
|
+
#
|
52
|
+
# ## RSpec.configure
|
53
|
+
#
|
54
|
+
# You can also store default tags with `RSpec.configure`. We use `tag` on
|
55
|
+
# the command line (and in options files like `.rspec`), but for historical
|
56
|
+
# reasons we use the term `filter` in `RSpec.configure:
|
57
|
+
#
|
58
|
+
# RSpec.configure do |c|
|
59
|
+
# c.filter_run_including :foo => :bar
|
60
|
+
# c.filter_run_excluding :foo => :bar
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# These declarations can also be overridden from the command line.
|
64
|
+
class FilterManager
|
65
|
+
DEFAULT_EXCLUSIONS = {
|
66
|
+
:if => lambda { |value, metadata| metadata.has_key?(:if) && !value },
|
67
|
+
:unless => lambda { |value| value }
|
68
|
+
}
|
69
|
+
|
70
|
+
STANDALONE_FILTERS = [:locations, :line_numbers, :full_description]
|
71
|
+
|
72
|
+
module Describable
|
73
|
+
PROC_HEX_NUMBER = /0x[0-9a-f]+@/
|
74
|
+
PROJECT_DIR = File.expand_path('.')
|
75
|
+
|
76
|
+
def description
|
77
|
+
reject { |k, v| RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS[k] == v }.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','')
|
78
|
+
end
|
79
|
+
|
80
|
+
def empty_without_conditional_filters?
|
81
|
+
reject { |k, v| RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS[k] == v }.empty?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
module BackwardCompatibility
|
86
|
+
# This is to support a use case that probably doesn't exist: overriding
|
87
|
+
# the if/unless procs.
|
88
|
+
def update(orig, opposite, *updates)
|
89
|
+
if updates.last.has_key?(:unless)
|
90
|
+
RSpec.warn_deprecation("\nDEPRECATION NOTICE: FilterManager#exclude(:unless => #{updates.last[:unless].inspect}) is deprecated with no replacement, and will be removed from rspec-3.0.")
|
91
|
+
@exclusions[:unless] = updates.last.delete(:unless)
|
92
|
+
end
|
93
|
+
if updates.last.has_key?(:if)
|
94
|
+
RSpec.warn_deprecation("\nDEPRECATION NOTICE: FilterManager#exclude(:if => #{updates.last[:if].inspect}) is deprecated with no replacement, and will be removed from rspec-3.0.")
|
95
|
+
@exclusions[:if] = updates.last.delete(:if)
|
96
|
+
end
|
97
|
+
|
98
|
+
super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_reader :exclusions, :inclusions
|
103
|
+
|
104
|
+
def initialize
|
105
|
+
@exclusions = DEFAULT_EXCLUSIONS.dup.extend(Describable)
|
106
|
+
@inclusions = {}.extend(Describable)
|
107
|
+
extend(BackwardCompatibility)
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_location(file_path, line_numbers)
|
111
|
+
# filter_locations is a hash of expanded paths to arrays of line
|
112
|
+
# numbers to match against. e.g.
|
113
|
+
# { "path/to/file.rb" => [37, 42] }
|
114
|
+
filter_locations = @inclusions[:locations] ||= Hash.new {|h,k| h[k] = []}
|
115
|
+
@exclusions.clear
|
116
|
+
@inclusions.clear
|
117
|
+
filter_locations[File.expand_path(file_path)].push(*line_numbers)
|
118
|
+
include :locations => filter_locations
|
119
|
+
end
|
120
|
+
|
121
|
+
def empty?
|
122
|
+
inclusions.empty? && exclusions.empty_without_conditional_filters?
|
123
|
+
end
|
124
|
+
|
125
|
+
def prune(examples)
|
126
|
+
examples.select {|e| !exclude?(e) && include?(e)}
|
127
|
+
end
|
128
|
+
|
129
|
+
def exclude?(example)
|
130
|
+
@exclusions.empty? ? false : example.any_apply?(@exclusions)
|
131
|
+
end
|
132
|
+
|
133
|
+
def include?(example)
|
134
|
+
@inclusions.empty? ? true : example.any_apply?(@inclusions)
|
135
|
+
end
|
136
|
+
|
137
|
+
def exclude(*args)
|
138
|
+
update(@exclusions, @inclusions, *args)
|
139
|
+
end
|
140
|
+
|
141
|
+
def include(*args)
|
142
|
+
return if already_set_standalone_filter?
|
143
|
+
|
144
|
+
is_standalone_filter?(args.last) ? @inclusions.replace(args.last) : update(@inclusions, @exclusions, *args)
|
145
|
+
end
|
146
|
+
|
147
|
+
def update(orig, opposite, *updates)
|
148
|
+
if updates.length == 2
|
149
|
+
if updates[0] == :replace
|
150
|
+
updated = updates.last
|
151
|
+
else
|
152
|
+
updated = updates.last.merge(orig)
|
153
|
+
opposite.each_key {|k| updated.delete(k)}
|
154
|
+
end
|
155
|
+
orig.replace(updated)
|
156
|
+
else
|
157
|
+
orig.merge!(updates.last).each_key {|k| opposite.delete(k)}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def already_set_standalone_filter?
|
162
|
+
is_standalone_filter?(inclusions)
|
163
|
+
end
|
164
|
+
|
165
|
+
def is_standalone_filter?(filter)
|
166
|
+
STANDALONE_FILTERS.any? {|key| filter.has_key?(key)}
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|