rspec-core 2.14.8 → 2.99.0.beta1

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.
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