rspec-core 2.7.1 → 2.8.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|