rspec-core 2.14.8 → 2.99.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +8 -8
  2. data/Changelog.md +54 -8
  3. data/features/command_line/order.feature +5 -8
  4. data/features/configuration/custom_settings.feature +10 -10
  5. data/features/configuration/deprecation_stream.feature +3 -3
  6. data/features/configuration/read_options_from_file.feature +1 -1
  7. data/features/example_groups/shared_examples.feature +2 -2
  8. data/features/hooks/around_hooks.feature +1 -1
  9. data/features/metadata/current_example.feature +43 -4
  10. data/features/metadata/user_defined.feature +12 -12
  11. data/lib/autotest/rspec2.rb +60 -56
  12. data/lib/rspec/core.rb +40 -2
  13. data/lib/rspec/core/caller_filter.rb +55 -0
  14. data/lib/rspec/core/command_line.rb +2 -2
  15. data/lib/rspec/core/configuration.rb +201 -13
  16. data/lib/rspec/core/deprecation.rb +2 -7
  17. data/lib/rspec/core/example.rb +5 -8
  18. data/lib/rspec/core/example_group.rb +101 -17
  19. data/lib/rspec/core/filter_manager.rb +2 -2
  20. data/lib/rspec/core/formatters/deprecation_formatter.rb +173 -15
  21. data/lib/rspec/core/formatters/text_mate_formatter.rb +0 -12
  22. data/lib/rspec/core/hooks.rb +1 -1
  23. data/lib/rspec/core/memoized_helpers.rb +49 -17
  24. data/lib/rspec/core/metadata.rb +1 -1
  25. data/lib/rspec/core/option_parser.rb +8 -3
  26. data/lib/rspec/core/pending.rb +14 -10
  27. data/lib/rspec/core/rake_task.rb +30 -6
  28. data/lib/rspec/core/runner.rb +9 -0
  29. data/lib/rspec/core/shared_example_group.rb +11 -9
  30. data/lib/rspec/core/shared_example_group/collection.rb +3 -1
  31. data/lib/rspec/core/version.rb +1 -2
  32. data/spec/command_line/order_spec.rb +4 -4
  33. data/spec/rspec/core/backtrace_cleaner_spec.rb +10 -10
  34. data/spec/rspec/core/caller_filter_spec.rb +58 -0
  35. data/spec/rspec/core/command_line_spec.rb +1 -0
  36. data/spec/rspec/core/configuration_options_spec.rb +6 -6
  37. data/spec/rspec/core/configuration_spec.rb +285 -52
  38. data/spec/rspec/core/deprecation_spec.rb +10 -29
  39. data/spec/rspec/core/deprecations_spec.rb +0 -14
  40. data/spec/rspec/core/example_group_spec.rb +74 -56
  41. data/spec/rspec/core/example_spec.rb +54 -17
  42. data/spec/rspec/core/filter_manager_spec.rb +2 -2
  43. data/spec/rspec/core/formatters/deprecation_formatter_spec.rb +156 -52
  44. data/spec/rspec/core/formatters/html_formatter_spec.rb +1 -1
  45. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +1 -2
  46. data/spec/rspec/core/hooks_spec.rb +2 -0
  47. data/spec/rspec/core/memoized_helpers_spec.rb +154 -113
  48. data/spec/rspec/core/metadata_spec.rb +25 -25
  49. data/spec/rspec/core/option_parser_spec.rb +19 -1
  50. data/spec/rspec/core/project_initializer_spec.rb +4 -4
  51. data/spec/rspec/core/rake_task_spec.rb +25 -4
  52. data/spec/rspec/core/shared_context_spec.rb +4 -4
  53. data/spec/rspec/core/shared_example_group_spec.rb +1 -1
  54. data/spec/rspec/core_spec.rb +36 -2
  55. data/spec/spec_helper.rb +3 -0
  56. data/spec/support/helper_methods.rb +16 -1
  57. data/spec/support/shared_example_groups.rb +1 -0
  58. data/spec/support/silence_dsl_deprecations.rb +32 -0
  59. metadata +10 -7
  60. data/spec/rspec/core/formatters/text_mate_formatted-2.1.0.html +0 -425
@@ -4,16 +4,11 @@ module RSpec
4
4
  # @private
5
5
  #
6
6
  # Used internally to print deprecation warnings
7
- def deprecate(deprecated, replacement_or_hash={}, ignore_version=nil)
8
- # Temporarily support old and new APIs while we transition the other
9
- # rspec libs to use a hash for the 2nd arg and no version arg
10
- data = Hash === replacement_or_hash ? replacement_or_hash : { :replacement => replacement_or_hash }
11
- call_site = caller.find { |line| line !~ %r{/lib/rspec/(core|mocks|expectations|matchers|rails)/} }
12
-
7
+ def deprecate(deprecated, data = {})
13
8
  RSpec.configuration.reporter.deprecation(
14
9
  {
15
10
  :deprecated => deprecated,
16
- :call_site => call_site
11
+ :call_site => CallerFilter.first_non_rspec_line
17
12
  }.merge(data)
18
13
  )
19
14
  end
@@ -102,7 +102,7 @@ module RSpec
102
102
  # @param example_group_instance the instance of an ExampleGroup subclass
103
103
  def run(example_group_instance, reporter)
104
104
  @example_group_instance = example_group_instance
105
- @example_group_instance.example = self
105
+ RSpec.current_example = self
106
106
 
107
107
  start(reporter)
108
108
 
@@ -111,7 +111,7 @@ module RSpec
111
111
  with_around_each_hooks do
112
112
  begin
113
113
  run_before_each
114
- @example_group_instance.instance_eval(&@example_block)
114
+ @example_group_instance.instance_eval_with_args(self, &@example_block)
115
115
  rescue Pending::PendingDeclaredInExample => e
116
116
  @pending_declared_in_example = e.message
117
117
  rescue Exception => e
@@ -137,6 +137,8 @@ module RSpec
137
137
  end
138
138
 
139
139
  finish(reporter)
140
+ ensure
141
+ RSpec.current_example = nil
140
142
  end
141
143
 
142
144
  # @api private
@@ -232,14 +234,9 @@ An error occurred #{context}
232
234
  finish(reporter)
233
235
  end
234
236
 
235
- # @private
236
- def instance_eval(*args, &block)
237
- @example_group_instance.instance_eval(*args, &block)
238
- end
239
-
240
237
  # @private
241
238
  def instance_eval_with_rescue(context = nil, &block)
242
- @example_group_instance.instance_eval_with_rescue(context, &block)
239
+ @example_group_instance.instance_eval_with_rescue(self, context, &block)
243
240
  end
244
241
 
245
242
  # @private
@@ -58,6 +58,7 @@ module RSpec
58
58
  # @param [String] name
59
59
  # @param [Hash] extra_options
60
60
  # @param [Block] implementation
61
+ # @yield [Example] the example object
61
62
  def self.define_example_method(name, extra_options={})
62
63
  module_eval(<<-END_RUBY, __FILE__, __LINE__)
63
64
  def #{name}(desc=nil, *args, &block)
@@ -71,10 +72,22 @@ module RSpec
71
72
  end
72
73
 
73
74
  # Defines an example within a group.
75
+ # @example
76
+ # example do
77
+ # end
78
+ #
79
+ # example "does something" do
80
+ # end
81
+ #
82
+ # example "does something", :with => 'additional metadata' do
83
+ # end
84
+ #
85
+ # example "does something" do |ex|
86
+ # # ex is the Example object that evals this block
87
+ # end
74
88
  define_example_method :example
75
89
  # Defines an example within a group.
76
- #
77
- # @see example
90
+ # @example
78
91
  define_example_method :it
79
92
  # Defines an example within a group.
80
93
  # This is here primarily for backward compatibility with early versions
@@ -83,20 +96,26 @@ module RSpec
83
96
  define_example_method :specify
84
97
 
85
98
  # Shortcut to define an example with `:focus` => true
99
+ # @see example
86
100
  define_example_method :focus, :focused => true, :focus => true
87
101
  # Shortcut to define an example with `:focus` => true
102
+ # @see example
88
103
  define_example_method :focused, :focused => true, :focus => true
89
104
  # Shortcut to define an example with `:focus` => true
90
105
  # @see example
91
106
  define_example_method :fit, :focused => true, :focus => true
92
107
 
93
108
  # Shortcut to define an example with :pending => true
109
+ # @see example
94
110
  define_example_method :pending, :pending => true
95
111
  # Shortcut to define an example with :pending => 'Temporarily disabled with xexample'
112
+ # @see example
96
113
  define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
97
114
  # Shortcut to define an example with :pending => 'Temporarily disabled with xit'
115
+ # @see example
98
116
  define_example_method :xit, :pending => 'Temporarily disabled with xit'
99
117
  # Shortcut to define an example with :pending => 'Temporarily disabled with xspecify'
118
+ # @see example
100
119
  define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
101
120
 
102
121
  # Works like `alias_method :name, :example` with the added benefit of
@@ -163,10 +182,44 @@ module RSpec
163
182
  find_and_eval_shared("examples", name, *args, &block)
164
183
  end
165
184
 
185
+ if Proc.method_defined?(:parameters) # for >= 1.9
186
+ # Warn when submitting the name of more than one example group to
187
+ # include_examples, it_behaves_like, etc.
188
+ #
189
+ # Helpful when upgrading from rspec-1 (which supported multiple shared
190
+ # groups in one call) to rspec-2 (which does not).
191
+ #
192
+ # See https://github.com/rspec/rspec-core/issues/1066 for background.
193
+ def self.warn_unexpected_args(label, name, args, shared_block)
194
+ if !args.empty? && shared_block.parameters.count == 0
195
+ if shared_example_groups[args.first]
196
+ warn <<-WARNING
197
+ shared #{label} support#{'s' if /context/ =~ label.to_s} the name of only one example group, received #{[name, *args].inspect}
198
+ called from #{CallerFilter.first_non_rspec_line}"
199
+ WARNING
200
+ else
201
+ warn <<-WARNING
202
+ shared #{label} #{name.inspect} expected #{shared_block.arity} args, got #{args.inspect}
203
+ called from #{CallerFilter.first_non_rspec_line}"
204
+ WARNING
205
+ end
206
+ end
207
+ end
208
+ else
209
+ # no-op for Ruby < 1.9
210
+ #
211
+ # Ruby 1.8 reports lambda {}.arity == -1, so can't support this warning
212
+ # reliably
213
+ def self.warn_unexpected_args(*)
214
+ end
215
+ end
216
+
166
217
  # @private
167
218
  def self.find_and_eval_shared(label, name, *args, &customization_block)
168
219
  raise ArgumentError, "Could not find shared #{label} #{name.inspect}" unless
169
- shared_block = shared_example_groups[name]
220
+ shared_block = shared_example_groups[name]
221
+
222
+ warn_unexpected_args(label, name, args, shared_block)
170
223
 
171
224
  module_eval_with_args(*args, &shared_block)
172
225
  module_eval(&customization_block) if customization_block
@@ -327,7 +380,7 @@ module RSpec
327
380
  begin
328
381
  assign_before_all_ivars(superclass.before_all_ivars, example_group_instance)
329
382
 
330
- BeforeAllMemoizedHash.isolate_for_before_all(example_group_instance) do
383
+ AllHookMemoizedHash::Before.isolate_for_all_hook(example_group_instance) do
331
384
  run_hook(:before, :all, example_group_instance)
332
385
  end
333
386
  ensure
@@ -355,7 +408,9 @@ module RSpec
355
408
  return if descendant_filtered_examples.empty?
356
409
  assign_before_all_ivars(before_all_ivars, example_group_instance)
357
410
 
358
- run_hook(:after, :all, example_group_instance)
411
+ AllHookMemoizedHash::After.isolate_for_all_hook(example_group_instance) do
412
+ run_hook(:after, :all, example_group_instance)
413
+ end
359
414
  end
360
415
 
361
416
  # Runs all the examples in this group
@@ -437,16 +492,42 @@ module RSpec
437
492
  ivars.each {|name, value| instance.instance_variable_set(name, value)}
438
493
  end
439
494
 
440
- # @attr_reader
441
- # Returns the {Example} object that wraps this instance of
442
- # `ExampleGroup`
443
- attr_accessor :example
495
+ def example=(current_example)
496
+ RSpec.current_example = current_example
497
+ end
498
+
499
+ # @deprecated use a block argument
500
+ def example
501
+ warn_deprecation_of_example_accessor :example
502
+ RSpec.current_example
503
+ end
444
504
 
445
- # @deprecated use {ExampleGroup#example}
505
+ # @deprecated use a block argument
446
506
  def running_example
447
- RSpec.deprecate("running_example",
448
- :replacement => "example")
449
- example
507
+ warn_deprecation_of_example_accessor :running_example
508
+ RSpec.current_example
509
+ end
510
+
511
+ def warn_deprecation_of_example_accessor(name)
512
+ RSpec.warn_deprecation(<<-EOS.gsub(/^\s*\|/, ''))
513
+ |RSpec::Core::ExampleGroup##{name} is deprecated and will be removed
514
+ |in RSpec 3. There are a few options for what you can use instead:
515
+ |
516
+ | - rspec-core's DSL methods (`it`, `before`, `after`, `let`, `subject`, etc)
517
+ | now yield the example as a block argument, and that is the recommended
518
+ | way to access the current example from those contexts.
519
+ | - The current example is now exposed via `RSpec.current_example`,
520
+ | which is accessible from any context.
521
+ | - If you can't update the code at this call site (e.g. because it is in
522
+ | an extension gem), you can use this snippet to continue making this
523
+ | method available in RSpec 2.99 and RSpec 3:
524
+ |
525
+ | RSpec.configure do |c|
526
+ | c.expose_current_running_example_as :#{name}
527
+ | end
528
+ |
529
+ |(Called from #{CallerFilter.first_non_rspec_line})
530
+ EOS
450
531
  end
451
532
 
452
533
  # Returns the class or module passed to the `describe` method (or alias).
@@ -466,12 +547,15 @@ module RSpec
466
547
  # @private
467
548
  # instance_evals the block, capturing and reporting an exception if
468
549
  # raised
469
- def instance_eval_with_rescue(context = nil, &hook)
550
+ def instance_eval_with_rescue(example, context = nil, &hook)
470
551
  begin
471
- instance_eval(&hook)
552
+ instance_eval_with_args(example, &hook)
472
553
  rescue Exception => e
473
- raise unless example
474
- example.set_exception(e, context)
554
+ if RSpec.current_example
555
+ RSpec.current_example.set_exception(e, context)
556
+ else
557
+ raise
558
+ end
475
559
  end
476
560
  end
477
561
  end
@@ -74,11 +74,11 @@ module RSpec
74
74
  STANDALONE_FILTERS = [:locations, :line_numbers, :full_description]
75
75
 
76
76
  module Describable
77
- PROC_HEX_NUMBER = /0x[0-9a-f]+@/
77
+ PROC_HEX_NUMBER = /0x[0-9a-f]+\s?@/
78
78
  PROJECT_DIR = File.expand_path('.')
79
79
 
80
80
  def description
81
- reject { |k, v| RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS[k] == v }.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','')
81
+ reject { |k, v| RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS[k] == v }.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','').gsub('__is_lambda__=true','')
82
82
  end
83
83
 
84
84
  def empty_without_conditional_filters?
@@ -1,39 +1,197 @@
1
+ require 'rspec/core/formatters/helpers'
2
+ require 'set'
3
+
1
4
  module RSpec
2
5
  module Core
3
6
  module Formatters
4
7
  class DeprecationFormatter
5
- def initialize(deprecation_stream=$stderr, summary_stream=$stdout)
8
+ attr_reader :count, :deprecation_stream, :summary_stream
9
+
10
+ def initialize(deprecation_stream, summary_stream)
6
11
  @deprecation_stream = deprecation_stream
7
12
  @summary_stream = summary_stream
13
+ @seen_deprecations = Set.new
8
14
  @count = 0
9
15
  end
10
16
 
11
- def start(example_count=nil)
12
- #no-op to fix #966
17
+ def printer
18
+ @printer ||= case deprecation_stream
19
+ when File, RaiseErrorStream
20
+ ImmediatePrinter.new(deprecation_stream, summary_stream, self)
21
+ else
22
+ DelayedPrinter.new(deprecation_stream, summary_stream, self)
23
+ end
13
24
  end
14
25
 
15
26
  def deprecation(data)
27
+ return if @seen_deprecations.include?(data)
28
+
16
29
  @count += 1
30
+ printer.print_deprecation_message data
31
+ @seen_deprecations << data
32
+ end
33
+
34
+ def deprecation_summary
35
+ printer.deprecation_summary
36
+ end
37
+
38
+ def start(example_count=nil)
39
+ #no-op to fix #966
40
+ end
41
+
42
+ def deprecation_message_for(data)
17
43
  if data[:message]
18
- @deprecation_stream.print data[:message]
44
+ SpecifiedDeprecationMessage.new(data)
19
45
  else
20
- @deprecation_stream.print "DEPRECATION: " unless File === @deprecation_stream
21
- @deprecation_stream.print "#{data[:deprecated]} is deprecated."
22
- @deprecation_stream.print " Use #{data[:replacement]} instead." if data[:replacement]
23
- @deprecation_stream.print " Called from #{data[:call_site]}." if data[:call_site]
24
- @deprecation_stream.puts
46
+ GeneratedDeprecationMessage.new(data)
25
47
  end
26
48
  end
27
49
 
28
- def deprecation_summary
29
- if @count > 0 && File === @deprecation_stream
30
- @summary_stream.print "\n#{@count} deprecation"
31
- @summary_stream.print "s" if @count > 1
32
- @summary_stream.print " logged to "
33
- @summary_stream.puts @deprecation_stream.path
50
+ RAISE_ERROR_CONFIG_NOTICE = <<-EOS.gsub(/^\s+\|/, '')
51
+ |
52
+ |If you need more of the backtrace for any of these deprecations to
53
+ |identify where to make the necessary changes, you can configure
54
+ |`config.raise_errors_for_deprecations!`, and it will turn the
55
+ |deprecation warnings into errors, giving you the full backtrace.
56
+ EOS
57
+
58
+ SpecifiedDeprecationMessage = Struct.new(:type) do
59
+ def initialize(data)
60
+ @message = data[:message]
61
+ super deprecation_type_for(data)
62
+ end
63
+
64
+ def to_s
65
+ @message
66
+ end
67
+
68
+ def too_many_warnings_message
69
+ msg = "Too many similar deprecation messages reported, disregarding further reports."
70
+ msg << " Set config.deprecation_stream to a File for full output."
71
+ msg
72
+ end
73
+
74
+ private
75
+
76
+ def deprecation_type_for(data)
77
+ data[:message].gsub(/(\w+\/)+\w+\.rb:\d+/, '')
78
+ end
79
+ end
80
+
81
+ GeneratedDeprecationMessage = Struct.new(:type) do
82
+ def initialize(data)
83
+ @data = data
84
+ super data[:deprecated]
85
+ end
86
+
87
+ def to_s
88
+ msg = "#{@data[:deprecated]} is deprecated."
89
+ msg << " Use #{@data[:replacement]} instead." if @data[:replacement]
90
+ msg << " Called from #{@data[:call_site]}." if @data[:call_site]
91
+ msg
92
+ end
93
+
94
+ def too_many_warnings_message
95
+ msg = "Too many uses of deprecated '#{type}'."
96
+ msg << " Set config.deprecation_stream to a File for full output."
97
+ msg
34
98
  end
35
99
  end
100
+
101
+ class ImmediatePrinter
102
+ include ::RSpec::Core::Formatters::Helpers
103
+
104
+ attr_reader :deprecation_stream, :summary_stream, :deprecation_formatter
105
+
106
+ def initialize(deprecation_stream, summary_stream, deprecation_formatter)
107
+ @deprecation_stream = deprecation_stream
108
+
109
+ # In one of my test suites, I got lots of duplicate output in the
110
+ # deprecation file (e.g. 200 of the same deprecation, even though
111
+ # the `puts` below was only called 6 times). Setting `sync = true`
112
+ # fixes this (but we really have no idea why!).
113
+ @deprecation_stream.sync = true
114
+
115
+ @summary_stream = summary_stream
116
+ @deprecation_formatter = deprecation_formatter
117
+ end
118
+
119
+ def print_deprecation_message(data)
120
+ deprecation_message = deprecation_formatter.deprecation_message_for(data)
121
+ deprecation_stream.puts deprecation_message.to_s
122
+ end
123
+
124
+ def deprecation_summary
125
+ if deprecation_formatter.count > 0
126
+ summary_stream.puts "\n#{pluralize(deprecation_formatter.count, 'deprecation')} logged to #{deprecation_stream.path}"
127
+ deprecation_stream.puts RAISE_ERROR_CONFIG_NOTICE
128
+ end
129
+ end
130
+ end
131
+
132
+ class DelayedPrinter
133
+ TOO_MANY_USES_LIMIT = 4
134
+
135
+ include ::RSpec::Core::Formatters::Helpers
136
+
137
+ attr_reader :deprecation_stream, :summary_stream, :deprecation_formatter
138
+
139
+ def initialize(deprecation_stream, summary_stream, deprecation_formatter)
140
+ @deprecation_stream = deprecation_stream
141
+ @summary_stream = summary_stream
142
+ @deprecation_formatter = deprecation_formatter
143
+ @seen_deprecations = Hash.new { 0 }
144
+ @deprecation_messages = Hash.new { |h, k| h[k] = [] }
145
+ end
146
+
147
+ def print_deprecation_message(data)
148
+ deprecation_message = deprecation_formatter.deprecation_message_for(data)
149
+ @seen_deprecations[deprecation_message] += 1
150
+
151
+ stash_deprecation_message(deprecation_message)
152
+ end
153
+
154
+ def stash_deprecation_message(deprecation_message)
155
+ if @seen_deprecations[deprecation_message] < TOO_MANY_USES_LIMIT
156
+ @deprecation_messages[deprecation_message] << deprecation_message.to_s
157
+ elsif @seen_deprecations[deprecation_message] == TOO_MANY_USES_LIMIT
158
+ @deprecation_messages[deprecation_message] << deprecation_message.too_many_warnings_message
159
+ end
160
+ end
161
+
162
+ def deprecation_summary
163
+ return unless @deprecation_messages.any?
164
+
165
+ print_deferred_deprecation_warnings
166
+ deprecation_stream.puts RAISE_ERROR_CONFIG_NOTICE
167
+
168
+ summary_stream.puts "\n#{pluralize(deprecation_formatter.count, 'deprecation warning')} total"
169
+ end
170
+
171
+ def print_deferred_deprecation_warnings
172
+ deprecation_stream.puts "\nDeprecation Warnings:\n\n"
173
+ @deprecation_messages.keys.sort_by(&:type).each do |deprecation|
174
+ messages = @deprecation_messages[deprecation]
175
+ messages.each { |msg| deprecation_stream.puts msg }
176
+ deprecation_stream.puts
177
+ end
178
+ end
179
+ end
180
+
181
+ # Not really a stream, but is usable in place of one.
182
+ class RaiseErrorStream
183
+ def puts(message)
184
+ raise DeprecationError, message
185
+ end
186
+
187
+ def sync=(value)
188
+ # no-op
189
+ end
190
+ end
191
+
36
192
  end
37
193
  end
194
+
195
+ DeprecationError = Class.new(StandardError)
38
196
  end
39
197
  end