rspec-core 2.6.4 → 2.7.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/{bin → exe}/autospec +0 -0
- data/{bin → exe}/rspec +0 -0
- data/features/Upgrade.md +11 -0
- data/features/command_line/exit_status.feature +20 -3
- data/features/command_line/format_option.feature +8 -0
- data/features/command_line/line_number_appended_to_path.feature +35 -1
- data/features/command_line/line_number_option.feature +16 -3
- data/features/command_line/pattern_option.feature +31 -0
- data/features/command_line/rake_task.feature +1 -1
- data/features/command_line/ruby.feature +22 -0
- data/features/configuration/default_path.feature +38 -0
- data/features/example_groups/{shared_example_group.feature → shared_examples.feature} +49 -26
- data/features/expectation_framework_integration/configure_expectation_framework.feature +1 -1
- data/features/filtering/inclusion_filters.feature +4 -5
- data/features/formatters/text_formatter.feature +16 -13
- data/features/helper_methods/let.feature +4 -4
- data/features/hooks/around_hooks.feature +1 -1
- data/features/hooks/before_and_after_hooks.feature +3 -3
- data/features/hooks/filtering.feature +13 -6
- data/features/metadata/user_defined.feature +12 -10
- data/features/pending/pending_examples.feature +21 -8
- data/features/step_definitions/additional_cli_steps.rb +1 -1
- data/features/subject/attribute_of_subject.feature +2 -2
- data/features/support/env.rb +1 -2
- data/lib/rspec/core.rb +1 -23
- data/lib/rspec/core/configuration.rb +64 -16
- data/lib/rspec/core/configuration_options.rb +11 -4
- data/lib/rspec/core/example.rb +10 -7
- data/lib/rspec/core/example_group.rb +34 -18
- data/lib/rspec/core/formatters/base_text_formatter.rb +10 -2
- data/lib/rspec/core/formatters/html_formatter.rb +3 -1
- data/lib/rspec/core/formatters/snippet_extractor.rb +9 -3
- data/lib/rspec/core/formatters/text_mate_formatter.rb +20 -6
- data/lib/rspec/core/hooks.rb +2 -2
- data/lib/rspec/core/let.rb +5 -5
- data/lib/rspec/core/metadata.rb +136 -94
- data/lib/rspec/core/option_parser.rb +10 -5
- data/lib/rspec/core/pending.rb +2 -1
- data/lib/rspec/core/rake_task.rb +26 -15
- data/lib/rspec/core/reporter.rb +2 -3
- data/lib/rspec/core/runner.rb +1 -1
- data/lib/rspec/core/shared_example_group.rb +4 -4
- data/lib/rspec/core/subject.rb +7 -7
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/world.rb +4 -8
- data/spec/autotest/discover_spec.rb +2 -2
- data/spec/autotest/failed_results_re_spec.rb +29 -21
- data/spec/autotest/rspec_spec.rb +3 -3
- data/spec/rspec/core/command_line_spec.rb +1 -6
- data/spec/rspec/core/configuration_options_spec.rb +65 -13
- data/spec/rspec/core/configuration_spec.rb +148 -37
- data/spec/rspec/core/deprecations_spec.rb +2 -2
- data/spec/rspec/core/drb_command_line_spec.rb +6 -6
- data/spec/rspec/core/example_group_spec.rb +197 -61
- data/spec/rspec/core/example_spec.rb +33 -16
- data/spec/rspec/core/formatters/base_formatter_spec.rb +3 -3
- data/spec/rspec/core/formatters/base_text_formatter_spec.rb +149 -1
- data/spec/rspec/core/formatters/helpers_spec.rb +8 -8
- data/spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html +85 -17
- data/spec/rspec/core/formatters/html_formatted-1.8.7.html +12 -11
- data/spec/rspec/core/formatters/html_formatted-1.9.2.html +12 -11
- data/spec/rspec/core/formatters/{html_formatted-1.9.1.html → html_formatted-1.9.3.html} +12 -11
- data/spec/rspec/core/formatters/html_formatter_spec.rb +5 -5
- data/spec/rspec/core/formatters/progress_formatter_spec.rb +2 -2
- data/spec/rspec/core/formatters/snippet_extractor_spec.rb +2 -2
- data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html +86 -18
- data/spec/rspec/core/formatters/text_mate_formatted-1.8.7.html +13 -12
- data/spec/rspec/core/formatters/text_mate_formatted-1.9.2.html +29 -28
- data/spec/rspec/core/formatters/{text_mate_formatted-1.9.1.html → text_mate_formatted-1.9.3.html} +29 -28
- data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +2 -2
- data/spec/rspec/core/hooks_filtering_spec.rb +18 -18
- data/spec/rspec/core/let_spec.rb +19 -6
- data/spec/rspec/core/metadata_spec.rb +146 -61
- data/spec/rspec/core/pending_example_spec.rb +4 -4
- data/spec/rspec/core/rake_task_spec.rb +71 -50
- data/spec/rspec/core/reporter_spec.rb +2 -2
- data/spec/rspec/core/ruby_project_spec.rb +2 -2
- data/spec/rspec/core/runner_spec.rb +4 -1
- data/spec/rspec/core/shared_example_group_spec.rb +15 -119
- data/spec/rspec/core/subject_spec.rb +13 -13
- data/spec/rspec/core/world_spec.rb +31 -22
- data/spec/rspec/core_spec.rb +1 -29
- data/spec/spec_helper.rb +51 -49
- data/spec/support/shared_example_groups.rb +3 -3
- data/spec/support/spec_files.rb +8 -8
- metadata +79 -93
- data/.document +0 -5
- data/.gitignore +0 -12
- data/.rspec +0 -0
- data/.travis.yml +0 -9
- data/Changelog.md +0 -305
- data/Gemfile +0 -49
- data/Guardfile +0 -5
- data/License.txt +0 -23
- data/Rakefile +0 -93
- data/cucumber.yml +0 -2
- data/features/.nav +0 -57
- data/rspec-core.gemspec +0 -24
- data/script/FullBuildRakeFile +0 -63
- data/script/console +0 -8
- data/script/cucumber +0 -1
- data/script/full_build +0 -1
- data/script/spec +0 -1
- data/spec.txt +0 -1126
- data/spec/rspec/core/formatters/html_formatted-1.8.6.html +0 -398
- data/spec/rspec/core/formatters/text_mate_formatted-1.8.6.html +0 -398
@@ -9,9 +9,7 @@ module RSpec
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def configure(config)
|
12
|
-
keys = options.keys
|
13
|
-
keys.unshift(:requires) if keys.delete(:requires)
|
14
|
-
keys.unshift(:libs) if keys.delete(:libs)
|
12
|
+
keys = order(options.keys, :libs, :requires, :default_path, :pattern)
|
15
13
|
|
16
14
|
formatters = options[:formatters] if keys.delete(:formatters)
|
17
15
|
|
@@ -31,7 +29,6 @@ module RSpec
|
|
31
29
|
argv << "--backtrace" if options[:full_backtrace]
|
32
30
|
argv << "--tty" if options[:tty]
|
33
31
|
argv << "--fail-fast" if options[:fail_fast]
|
34
|
-
argv << "--line_number" << options[:line_number] if options[:line_number]
|
35
32
|
argv << "--options" << options[:custom_options_file] if options[:custom_options_file]
|
36
33
|
if options[:full_description]
|
37
34
|
# The argument to --example is regexp-escaped before being stuffed
|
@@ -40,6 +37,9 @@ module RSpec
|
|
40
37
|
# backslashes, so we must remove them.
|
41
38
|
argv << "--example" << options[:full_description].source.delete('\\')
|
42
39
|
end
|
40
|
+
if options[:line_numbers]
|
41
|
+
argv += options[:line_numbers].inject([]){|a,l| a << "--line_number" << l}
|
42
|
+
end
|
43
43
|
if options[:filter]
|
44
44
|
options[:filter].each_pair do |k, v|
|
45
45
|
argv << "--tag" << k.to_s
|
@@ -71,6 +71,13 @@ module RSpec
|
|
71
71
|
|
72
72
|
private
|
73
73
|
|
74
|
+
def order(keys, *ordered)
|
75
|
+
ordered.reverse.each do |key|
|
76
|
+
keys.unshift(key) if keys.delete(key)
|
77
|
+
end
|
78
|
+
keys
|
79
|
+
end
|
80
|
+
|
74
81
|
def file_options
|
75
82
|
custom_options_file ? custom_options : global_options.merge(local_options)
|
76
83
|
end
|
data/lib/rspec/core/example.rb
CHANGED
@@ -4,6 +4,10 @@ module RSpec
|
|
4
4
|
|
5
5
|
attr_reader :metadata, :options, :example_group_instance
|
6
6
|
|
7
|
+
# Returns the first exception raised, if any, in the context of running
|
8
|
+
# this example.
|
9
|
+
attr_reader :exception
|
10
|
+
|
7
11
|
def self.delegate_to_metadata(*keys)
|
8
12
|
keys.each do |key|
|
9
13
|
define_method(key) {@metadata[key]}
|
@@ -23,13 +27,12 @@ module RSpec
|
|
23
27
|
@example_group_class
|
24
28
|
end
|
25
29
|
|
26
|
-
def around_hooks
|
30
|
+
def around_hooks # :nodoc:
|
27
31
|
@around_hooks ||= example_group.around_hooks_for(self)
|
28
32
|
end
|
29
33
|
|
30
|
-
def
|
31
|
-
@metadata.
|
32
|
-
@example_group_class.apply?(predicate, filters)
|
34
|
+
def all_apply?(filters)
|
35
|
+
@metadata.all_apply?(filters) || @example_group_class.all_apply?(filters)
|
33
36
|
end
|
34
37
|
|
35
38
|
alias_method :pending?, :pending
|
@@ -106,7 +109,7 @@ module RSpec
|
|
106
109
|
if around_hooks.empty?
|
107
110
|
yield
|
108
111
|
else
|
109
|
-
@example_group_class.
|
112
|
+
@example_group_class.run_around_each_hooks(self, Example.procsy(metadata, &block)).call
|
110
113
|
end
|
111
114
|
end
|
112
115
|
|
@@ -142,11 +145,11 @@ module RSpec
|
|
142
145
|
|
143
146
|
def run_before_each
|
144
147
|
@example_group_instance.setup_mocks_for_rspec if @example_group_instance.respond_to?(:setup_mocks_for_rspec)
|
145
|
-
@example_group_class.
|
148
|
+
@example_group_class.run_before_each_hooks(self)
|
146
149
|
end
|
147
150
|
|
148
151
|
def run_after_each
|
149
|
-
@example_group_class.
|
152
|
+
@example_group_class.run_after_each_hooks(self)
|
150
153
|
@example_group_instance.verify_mocks_for_rspec if @example_group_instance.respond_to?(:verify_mocks_for_rspec)
|
151
154
|
ensure
|
152
155
|
@example_group_instance.teardown_mocks_for_rspec if @example_group_instance.respond_to?(:teardown_mocks_for_rspec)
|
@@ -60,16 +60,20 @@ module RSpec
|
|
60
60
|
|
61
61
|
alias_example_to :it
|
62
62
|
alias_example_to :specify
|
63
|
-
|
64
|
-
alias_example_to :
|
65
|
-
alias_example_to :
|
66
|
-
alias_example_to :xit,
|
63
|
+
|
64
|
+
alias_example_to :pending, :pending => true
|
65
|
+
alias_example_to :xexample, :pending => true
|
66
|
+
alias_example_to :xit, :pending => true
|
67
|
+
alias_example_to :xspecify, :pending => true
|
68
|
+
|
69
|
+
alias_example_to :focused, :focused => true, :focus => true
|
70
|
+
alias_example_to :focus, :focused => true, :focus => true
|
67
71
|
|
68
72
|
def self.define_nested_shared_group_method(new_name, report_label=nil)
|
69
73
|
module_eval(<<-END_RUBY, __FILE__, __LINE__)
|
70
74
|
def self.#{new_name}(name, *args, &customization_block)
|
71
|
-
shared_block =
|
72
|
-
raise "Could not find shared
|
75
|
+
shared_block = find_shared("examples", name)
|
76
|
+
raise "Could not find shared examples \#{name.inspect}" unless shared_block
|
73
77
|
|
74
78
|
group = describe("#{report_label || "it should behave like"} \#{name}") do
|
75
79
|
module_eval_with_args(*args, &shared_block)
|
@@ -90,11 +94,19 @@ module RSpec
|
|
90
94
|
alias_it_should_behave_like_to :it_behaves_like, "behaves like"
|
91
95
|
|
92
96
|
def self.include_context(name)
|
93
|
-
module_eval(&
|
97
|
+
module_eval(&find_shared("context", name))
|
94
98
|
end
|
95
99
|
|
96
|
-
|
97
|
-
|
100
|
+
def self.include_examples(name)
|
101
|
+
module_eval(&find_shared("examples", name))
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.find_shared(label, name)
|
105
|
+
if world.shared_example_groups.has_key?(name)
|
106
|
+
world.shared_example_groups[name]
|
107
|
+
else
|
108
|
+
raise ArgumentError, "Could not find shared #{label} #{name.inspect}"
|
109
|
+
end
|
98
110
|
end
|
99
111
|
|
100
112
|
def self.examples
|
@@ -200,7 +212,7 @@ module RSpec
|
|
200
212
|
ivars.each { |ivar, val| example_group_instance.instance_variable_set(ivar, val) }
|
201
213
|
end
|
202
214
|
|
203
|
-
def self.
|
215
|
+
def self.run_before_all_hooks(example_group_instance)
|
204
216
|
return if descendant_filtered_examples.empty?
|
205
217
|
assign_before_all_ivars(superclass.before_all_ivars, example_group_instance)
|
206
218
|
world.run_hook_filtered(:before, :all, self, example_group_instance)
|
@@ -208,7 +220,7 @@ module RSpec
|
|
208
220
|
store_before_all_ivars(example_group_instance)
|
209
221
|
end
|
210
222
|
|
211
|
-
def self.
|
223
|
+
def self.run_around_each_hooks(example, initial_procsy)
|
212
224
|
example.around_hooks.reverse.inject(initial_procsy) do |procsy, around_hook|
|
213
225
|
Example.procsy(procsy.metadata) do
|
214
226
|
example.example_group_instance.instance_eval_with_args(procsy, &around_hook)
|
@@ -216,17 +228,17 @@ module RSpec
|
|
216
228
|
end
|
217
229
|
end
|
218
230
|
|
219
|
-
def self.
|
231
|
+
def self.run_before_each_hooks(example)
|
220
232
|
world.run_hook_filtered(:before, :each, self, example.example_group_instance, example)
|
221
233
|
ancestors.reverse.each { |ancestor| ancestor.run_hook(:before, :each, example.example_group_instance) }
|
222
234
|
end
|
223
235
|
|
224
|
-
def self.
|
236
|
+
def self.run_after_each_hooks(example)
|
225
237
|
ancestors.each { |ancestor| ancestor.run_hook(:after, :each, example.example_group_instance) }
|
226
238
|
world.run_hook_filtered(:after, :each, self, example.example_group_instance, example)
|
227
239
|
end
|
228
240
|
|
229
|
-
def self.
|
241
|
+
def self.run_after_all_hooks(example_group_instance)
|
230
242
|
return if descendant_filtered_examples.empty?
|
231
243
|
assign_before_all_ivars(before_all_ivars, example_group_instance)
|
232
244
|
|
@@ -258,14 +270,14 @@ An error occurred in an after(:all) hook.
|
|
258
270
|
reporter.example_group_started(self)
|
259
271
|
|
260
272
|
begin
|
261
|
-
|
273
|
+
run_before_all_hooks(new)
|
262
274
|
result_for_this_group = run_examples(reporter)
|
263
275
|
results_for_descendants = children.map {|child| child.run(reporter)}.all?
|
264
276
|
result_for_this_group && results_for_descendants
|
265
277
|
rescue Exception => ex
|
266
278
|
fail_filtered_examples(ex, reporter)
|
267
279
|
ensure
|
268
|
-
|
280
|
+
run_after_all_hooks(new)
|
269
281
|
before_all_ivars.clear
|
270
282
|
reporter.example_group_finished(self)
|
271
283
|
end
|
@@ -297,8 +309,12 @@ An error occurred in an after(:all) hook.
|
|
297
309
|
end.all?
|
298
310
|
end
|
299
311
|
|
300
|
-
def self.
|
301
|
-
metadata.
|
312
|
+
def self.any_apply?(filters)
|
313
|
+
metadata.any_apply?(filters)
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.all_apply?(filters)
|
317
|
+
metadata.all_apply?(filters)
|
302
318
|
end
|
303
319
|
|
304
320
|
def self.declaration_line_numbers
|
@@ -77,6 +77,11 @@ module RSpec
|
|
77
77
|
output.puts yellow(" #{pending_example.full_description}")
|
78
78
|
output.puts cyan(" # #{pending_example.execution_result[:pending_message]}")
|
79
79
|
output.puts cyan(" # #{format_caller(pending_example.location)}")
|
80
|
+
if pending_example.execution_result[:exception] \
|
81
|
+
&& RSpec.configuration.show_failures_in_pending_blocks?
|
82
|
+
dump_failure_info(pending_example)
|
83
|
+
dump_backtrace(pending_example)
|
84
|
+
end
|
80
85
|
end
|
81
86
|
end
|
82
87
|
end
|
@@ -156,8 +161,12 @@ module RSpec
|
|
156
161
|
end
|
157
162
|
|
158
163
|
def dump_failure(example, index)
|
159
|
-
exception = example.execution_result[:exception]
|
160
164
|
output.puts "#{short_padding}#{index.next}) #{example.full_description}"
|
165
|
+
dump_failure_info(example)
|
166
|
+
end
|
167
|
+
|
168
|
+
def dump_failure_info(example)
|
169
|
+
exception = example.execution_result[:exception]
|
161
170
|
output.puts "#{long_padding}#{red("Failure/Error:")} #{red(read_failed_line(exception, example).strip)}"
|
162
171
|
output.puts "#{long_padding}#{red(exception.class.name << ":")}" unless exception.class.name =~ /RSpec/
|
163
172
|
exception.message.split("\n").each { |line| output.puts "#{long_padding} #{red(line)}" } if exception.message
|
@@ -170,7 +179,6 @@ module RSpec
|
|
170
179
|
end
|
171
180
|
end
|
172
181
|
end
|
173
|
-
|
174
182
|
end
|
175
183
|
end
|
176
184
|
end
|
@@ -108,8 +108,10 @@ module RSpec
|
|
108
108
|
#
|
109
109
|
def extra_failure_content(exception)
|
110
110
|
require 'rspec/core/formatters/snippet_extractor'
|
111
|
+
backtrace = exception.backtrace.map {|line| backtrace_line(line)}
|
112
|
+
backtrace.compact!
|
111
113
|
@snippet_extractor ||= SnippetExtractor.new
|
112
|
-
" <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(
|
114
|
+
" <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
|
113
115
|
end
|
114
116
|
|
115
117
|
def move_progress
|
@@ -4,10 +4,16 @@ module RSpec
|
|
4
4
|
# This class extracts code snippets by looking at the backtrace of the passed error
|
5
5
|
class SnippetExtractor #:nodoc:
|
6
6
|
class NullConverter; def convert(code, pre); code; end; end #:nodoc:
|
7
|
-
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'syntax/convertors/html'
|
10
|
+
@@converter = Syntax::Convertors::HTML.for_syntax "ruby"
|
11
|
+
rescue LoadError => e
|
12
|
+
@@converter = NullConverter.new
|
13
|
+
end
|
8
14
|
|
9
|
-
def snippet(
|
10
|
-
raw_code, line = snippet_for(
|
15
|
+
def snippet(backtrace)
|
16
|
+
raw_code, line = snippet_for(backtrace[0])
|
11
17
|
highlighted = @@converter.convert(raw_code, false)
|
12
18
|
highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
|
13
19
|
post_process(highlighted, line)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'cgi'
|
1
2
|
require 'rspec/core/formatters/html_formatter'
|
2
3
|
|
3
4
|
module RSpec
|
@@ -5,15 +6,28 @@ module RSpec
|
|
5
6
|
module Formatters
|
6
7
|
# Formats backtraces so they're clickable by TextMate
|
7
8
|
class TextMateFormatter < HtmlFormatter
|
8
|
-
def backtrace_line(line)
|
9
|
-
if
|
10
|
-
line
|
11
|
-
|
12
|
-
|
9
|
+
def backtrace_line(line, skip_textmate_conversion=false)
|
10
|
+
if skip_textmate_conversion
|
11
|
+
super(line)
|
12
|
+
else
|
13
|
+
format_backtrace_line_for_textmate(super(line))
|
14
|
+
end
|
15
|
+
end
|
13
16
|
|
14
|
-
|
17
|
+
def format_backtrace_line_for_textmate(line)
|
18
|
+
return nil unless line
|
19
|
+
CGI.escapeHTML(line).sub(/([^:]*\.e?rb):(\d*)/) do
|
20
|
+
"<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
|
15
21
|
end
|
16
22
|
end
|
23
|
+
|
24
|
+
def extra_failure_content(exception)
|
25
|
+
require 'rspec/core/formatters/snippet_extractor'
|
26
|
+
backtrace = exception.backtrace.map {|line| backtrace_line(line, :skip_textmate_conversion)}
|
27
|
+
backtrace.compact!
|
28
|
+
@snippet_extractor ||= SnippetExtractor.new
|
29
|
+
" <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
|
30
|
+
end
|
17
31
|
end
|
18
32
|
end
|
19
33
|
end
|
data/lib/rspec/core/hooks.rb
CHANGED
@@ -13,7 +13,7 @@ module RSpec
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def options_apply?(example_or_group)
|
16
|
-
|
16
|
+
example_or_group.all_apply?(options)
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_proc
|
@@ -77,7 +77,7 @@ module RSpec
|
|
77
77
|
|
78
78
|
class AfterHooks < HookCollection
|
79
79
|
def run_all(example_group_instance)
|
80
|
-
reverse.each {|h| h.run_in(example_group_instance) }
|
80
|
+
reverse.each {|h| h.run_in(example_group_instance) }
|
81
81
|
end
|
82
82
|
|
83
83
|
def run_all!(example_group_instance)
|
data/lib/rspec/core/let.rb
CHANGED
@@ -21,7 +21,7 @@ module RSpec
|
|
21
21
|
# end
|
22
22
|
def let(name, &block)
|
23
23
|
define_method(name) do
|
24
|
-
__memoized[
|
24
|
+
__memoized.fetch(name) {|k| __memoized[k] = instance_eval(&block) }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -57,12 +57,12 @@ module RSpec
|
|
57
57
|
# let(:thing) { Thing.new }
|
58
58
|
#
|
59
59
|
# it "is not invoked implicitly" do
|
60
|
-
# Thing.count.should
|
60
|
+
# Thing.count.should eq(0)
|
61
61
|
# end
|
62
62
|
#
|
63
63
|
# it "can be invoked explicitly" do
|
64
64
|
# thing
|
65
|
-
# Thing.count.should
|
65
|
+
# Thing.count.should eq(1)
|
66
66
|
# end
|
67
67
|
# end
|
68
68
|
#
|
@@ -70,12 +70,12 @@ module RSpec
|
|
70
70
|
# let!(:thing) { Thing.new }
|
71
71
|
#
|
72
72
|
# it "is invoked implicitly" do
|
73
|
-
# Thing.count.should
|
73
|
+
# Thing.count.should eq(1)
|
74
74
|
# end
|
75
75
|
#
|
76
76
|
# it "returns memoized version on first invocation" do
|
77
77
|
# thing
|
78
|
-
# Thing.count.should
|
78
|
+
# Thing.count.should eq(1)
|
79
79
|
# end
|
80
80
|
# end
|
81
81
|
# end
|
data/lib/rspec/core/metadata.rb
CHANGED
@@ -2,7 +2,9 @@ module RSpec
|
|
2
2
|
module Core
|
3
3
|
class Metadata < Hash
|
4
4
|
|
5
|
-
|
5
|
+
# Used to extend metadata Hashes to support lazy evaluation of locations
|
6
|
+
# and descriptions.
|
7
|
+
module MetadataHash
|
6
8
|
def [](key)
|
7
9
|
return super if has_key?(key)
|
8
10
|
case key
|
@@ -12,12 +14,22 @@ module RSpec
|
|
12
14
|
file_path, line_number = file_and_line_number
|
13
15
|
store(:file_path, file_path)
|
14
16
|
store(:line_number, line_number)
|
15
|
-
|
17
|
+
super
|
18
|
+
when :execution_result
|
19
|
+
store(:execution_result, {})
|
20
|
+
when :describes
|
21
|
+
store(:describes, described_class_for(self))
|
22
|
+
when :full_description
|
23
|
+
store(:full_description, full_description_for(self))
|
24
|
+
when :description
|
25
|
+
store(:description, build_description_from(*self[:description_args]))
|
16
26
|
else
|
17
27
|
super
|
18
28
|
end
|
19
29
|
end
|
20
30
|
|
31
|
+
private
|
32
|
+
|
21
33
|
def location
|
22
34
|
"#{self[:file_path]}:#{self[:line_number]}"
|
23
35
|
end
|
@@ -30,97 +42,105 @@ module RSpec
|
|
30
42
|
def first_caller_from_outside_rspec
|
31
43
|
self[:caller].detect {|l| l !~ /\/lib\/rspec\/core/}
|
32
44
|
end
|
45
|
+
|
46
|
+
def described_class_for(m)
|
47
|
+
m[:example_group][:describes]
|
48
|
+
end
|
49
|
+
|
50
|
+
def full_description_for(m)
|
51
|
+
build_description_from(m[:example_group][:full_description], *m[:description_args])
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_description_from(*parts)
|
55
|
+
parts.map {|p| p.to_s}.reduce do |desc, p|
|
56
|
+
p =~ /^(#|::|\.)/ ? "#{desc}#{p}" : "#{desc} #{p}"
|
57
|
+
end || ""
|
58
|
+
end
|
33
59
|
end
|
34
60
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
61
|
+
module GroupMetadataHash
|
62
|
+
include MetadataHash
|
63
|
+
|
64
|
+
def described_class_for(*)
|
65
|
+
ancestors.each do |g|
|
66
|
+
return g[:describes] if g.has_key?(:describes)
|
67
|
+
end
|
68
|
+
|
69
|
+
ancestors.reverse.each do |g|
|
70
|
+
candidate = g[:description_args].first
|
71
|
+
return candidate unless String === candidate || Symbol === candidate
|
72
|
+
end
|
73
|
+
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def full_description_for(*)
|
78
|
+
build_description_from(*ancestors.reverse.map do |a|
|
79
|
+
a.has_key?(:full_description) ? a[:full_description] : a[:description_args]
|
80
|
+
end.flatten)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def ancestors
|
86
|
+
@ancestors ||= begin
|
87
|
+
groups = [group = self]
|
88
|
+
while group.has_key?(:example_group)
|
89
|
+
groups << group[:example_group]
|
90
|
+
group = group[:example_group]
|
91
|
+
end
|
92
|
+
groups
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize(parent_group_metadata=nil)
|
98
|
+
if parent_group_metadata
|
99
|
+
update(parent_group_metadata)
|
100
|
+
store(:example_group, {:example_group => parent_group_metadata[:example_group]}.extend(GroupMetadataHash))
|
40
101
|
else
|
41
|
-
example_group
|
102
|
+
store(:example_group, {}.extend(GroupMetadataHash))
|
42
103
|
end
|
43
104
|
|
44
|
-
store(:example_group, example_group.extend(LocationKeys))
|
45
105
|
yield self if block_given?
|
46
106
|
end
|
47
107
|
|
48
|
-
RESERVED_KEYS = [
|
49
|
-
:description,
|
50
|
-
:example_group,
|
51
|
-
:execution_result,
|
52
|
-
:file_path,
|
53
|
-
:full_description,
|
54
|
-
:line_number,
|
55
|
-
:location
|
56
|
-
]
|
57
|
-
|
58
108
|
def process(*args)
|
59
109
|
user_metadata = args.last.is_a?(Hash) ? args.pop : {}
|
60
110
|
ensure_valid_keys(user_metadata)
|
61
111
|
|
112
|
+
self[:example_group].store(:description_args, args)
|
62
113
|
self[:example_group].store(:caller, user_metadata.delete(:caller) || caller)
|
63
|
-
self[:example_group].store(:describes, described_class_from(*args))
|
64
|
-
self[:example_group].store(:description, description_from(*args))
|
65
|
-
self[:example_group].store(:full_description, full_description_from(*args))
|
66
|
-
self[:example_group].store(:block, user_metadata.delete(:example_group_block))
|
67
114
|
|
68
115
|
update(user_metadata)
|
69
116
|
end
|
70
117
|
|
71
|
-
def ensure_valid_keys(user_metadata)
|
72
|
-
RESERVED_KEYS.each do |key|
|
73
|
-
if user_metadata.keys.include?(key)
|
74
|
-
raise <<-EOM
|
75
|
-
#{"*"*50}
|
76
|
-
:#{key} is not allowed
|
77
|
-
|
78
|
-
RSpec reserves some hash keys for its own internal use,
|
79
|
-
including :#{key}, which is used on:
|
80
|
-
|
81
|
-
#{caller(0)[4]}.
|
82
|
-
|
83
|
-
Here are all of RSpec's reserved hash keys:
|
84
|
-
|
85
|
-
#{RESERVED_KEYS.join("\n ")}
|
86
|
-
#{"*"*50}
|
87
|
-
EOM
|
88
|
-
raise ":#{key} is not allowed"
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
118
|
def for_example(description, user_metadata)
|
94
|
-
dup.extend(
|
119
|
+
dup.extend(MetadataHash).configure_for_example(description, user_metadata)
|
95
120
|
end
|
96
121
|
|
97
|
-
def
|
98
|
-
|
99
|
-
store(:full_description, "#{self[:example_group][:full_description]} #{self[:description]}")
|
100
|
-
store(:execution_result, {})
|
101
|
-
store(:caller, user_metadata.delete(:caller) || caller)
|
102
|
-
update(user_metadata)
|
103
|
-
end
|
104
|
-
|
105
|
-
def apply?(predicate, filters)
|
106
|
-
filters.send(predicate) do |key, value|
|
107
|
-
apply_condition(key, value)
|
108
|
-
end
|
122
|
+
def any_apply?(filters)
|
123
|
+
filters.any? {|k,v| filter_applies?(k,v)}
|
109
124
|
end
|
110
125
|
|
111
|
-
def
|
112
|
-
|
113
|
-
if metadata[:example_group]
|
114
|
-
line_numbers + relevant_line_numbers(metadata[:example_group])
|
115
|
-
else
|
116
|
-
line_numbers
|
117
|
-
end
|
126
|
+
def all_apply?(filters)
|
127
|
+
filters.all? {|k,v| filter_applies?(k,v)}
|
118
128
|
end
|
119
129
|
|
120
|
-
def
|
130
|
+
def filter_applies?(key, value, metadata=self)
|
121
131
|
case value
|
122
132
|
when Hash
|
123
|
-
|
133
|
+
if key == :locations
|
134
|
+
file_path = (self[:example_group] || {})[:file_path]
|
135
|
+
expanded_path = file_path && File.expand_path( file_path )
|
136
|
+
if expanded_path && line_numbers = value[expanded_path]
|
137
|
+
filter_applies?(:line_numbers, line_numbers)
|
138
|
+
else
|
139
|
+
true
|
140
|
+
end
|
141
|
+
else
|
142
|
+
value.all? { |k, v| filter_applies?(k, v, metadata[key]) }
|
143
|
+
end
|
124
144
|
when Regexp
|
125
145
|
metadata[key] =~ value
|
126
146
|
when Proc
|
@@ -135,9 +155,12 @@ EOM
|
|
135
155
|
else
|
136
156
|
value.call(metadata[key]) rescue false
|
137
157
|
end
|
138
|
-
when
|
139
|
-
|
140
|
-
|
158
|
+
when String
|
159
|
+
metadata[key].to_s == value.to_s
|
160
|
+
when Enumerable
|
161
|
+
if key == :line_numbers
|
162
|
+
preceding_declaration_lines = value.map{|v| world.preceding_declaration_line(v)}
|
163
|
+
!(relevant_line_numbers(metadata) & preceding_declaration_lines).empty?
|
141
164
|
else
|
142
165
|
metadata[key] == value
|
143
166
|
end
|
@@ -146,42 +169,61 @@ EOM
|
|
146
169
|
end
|
147
170
|
end
|
148
171
|
|
149
|
-
|
172
|
+
protected
|
150
173
|
|
151
|
-
def
|
152
|
-
|
174
|
+
def configure_for_example(description, user_metadata)
|
175
|
+
store(:description_args, [description])
|
176
|
+
store(:caller, user_metadata.delete(:caller) || caller)
|
177
|
+
update(user_metadata)
|
153
178
|
end
|
154
179
|
|
155
|
-
|
156
|
-
@superclass_metadata ||= { :example_group => {} }
|
157
|
-
end
|
180
|
+
private
|
158
181
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
182
|
+
RESERVED_KEYS = [
|
183
|
+
:description,
|
184
|
+
:example_group,
|
185
|
+
:execution_result,
|
186
|
+
:file_path,
|
187
|
+
:full_description,
|
188
|
+
:line_number,
|
189
|
+
:location
|
190
|
+
]
|
191
|
+
|
192
|
+
def ensure_valid_keys(user_metadata)
|
193
|
+
RESERVED_KEYS.each do |key|
|
194
|
+
if user_metadata.keys.include?(key)
|
195
|
+
raise <<-EOM
|
196
|
+
#{"*"*50}
|
197
|
+
:#{key} is not allowed
|
198
|
+
|
199
|
+
RSpec reserves some hash keys for its own internal use,
|
200
|
+
including :#{key}, which is used on:
|
201
|
+
|
202
|
+
#{caller(0)[4]}.
|
203
|
+
|
204
|
+
Here are all of RSpec's reserved hash keys:
|
205
|
+
|
206
|
+
#{RESERVED_KEYS.join("\n ")}
|
207
|
+
#{"*"*50}
|
208
|
+
EOM
|
209
|
+
raise ":#{key} is not allowed"
|
168
210
|
end
|
169
211
|
end
|
170
212
|
end
|
171
213
|
|
172
|
-
def
|
173
|
-
|
174
|
-
description_from(superclass_metadata[:example_group][:full_description], *args)
|
175
|
-
else
|
176
|
-
description_from(*args)
|
177
|
-
end
|
214
|
+
def world
|
215
|
+
RSpec.world
|
178
216
|
end
|
179
217
|
|
180
|
-
def
|
181
|
-
|
182
|
-
|
218
|
+
def relevant_line_numbers(metadata)
|
219
|
+
line_numbers = [metadata[:line_number]]
|
220
|
+
if metadata[:example_group]
|
221
|
+
line_numbers + relevant_line_numbers(metadata[:example_group])
|
222
|
+
else
|
223
|
+
line_numbers
|
183
224
|
end
|
184
225
|
end
|
226
|
+
|
185
227
|
end
|
186
228
|
end
|
187
229
|
end
|