rspec-core 2.99.2 → 3.0.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 (165) hide show
  1. checksums.yaml +14 -6
  2. checksums.yaml.gz.sig +2 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +103 -191
  5. data/License.txt +1 -0
  6. data/README.md +4 -25
  7. data/features/Upgrade.md +2 -14
  8. data/features/command_line/dry_run.feature +29 -0
  9. data/features/command_line/example_name_option.feature +1 -1
  10. data/features/command_line/fail_fast.feature +26 -0
  11. data/features/command_line/format_option.feature +3 -3
  12. data/features/command_line/line_number_option.feature +16 -11
  13. data/features/command_line/order.feature +2 -3
  14. data/features/command_line/pattern_option.feature +3 -3
  15. data/features/command_line/randomization.feature +63 -0
  16. data/features/command_line/require_option.feature +2 -2
  17. data/features/command_line/ruby.feature +1 -1
  18. data/features/configuration/alias_example_to.feature +13 -22
  19. data/features/configuration/{backtrace_clean_patterns.feature → backtrace_exclusion_patterns.feature} +17 -14
  20. data/features/configuration/custom_settings.feature +11 -11
  21. data/features/configuration/overriding_global_ordering.feature +93 -0
  22. data/features/configuration/profile.feature +13 -13
  23. data/features/configuration/read_options_from_file.feature +7 -7
  24. data/features/example_groups/basic_structure.feature +1 -1
  25. data/features/example_groups/shared_context.feature +8 -8
  26. data/features/example_groups/shared_examples.feature +6 -14
  27. data/features/expectation_framework_integration/configure_expectation_framework.feature +27 -122
  28. data/features/filtering/exclusion_filters.feature +2 -5
  29. data/features/filtering/inclusion_filters.feature +1 -5
  30. data/features/formatters/json_formatter.feature +2 -2
  31. data/features/formatters/text_formatter.feature +4 -4
  32. data/features/helper_methods/arbitrary_methods.feature +2 -2
  33. data/features/helper_methods/let.feature +5 -5
  34. data/features/helper_methods/modules.feature +5 -8
  35. data/features/hooks/around_hooks.feature +2 -2
  36. data/features/hooks/before_and_after_hooks.feature +14 -14
  37. data/features/hooks/filtering.feature +12 -14
  38. data/features/metadata/described_class.feature +1 -1
  39. data/features/metadata/user_defined.feature +16 -29
  40. data/features/mock_framework_integration/use_flexmock.feature +1 -1
  41. data/features/mock_framework_integration/use_mocha.feature +1 -1
  42. data/features/mock_framework_integration/use_rr.feature +1 -1
  43. data/features/mock_framework_integration/use_rspec.feature +5 -5
  44. data/features/pending/pending_examples.feature +5 -5
  45. data/features/spec_files/arbitrary_file_suffix.feature +1 -1
  46. data/features/step_definitions/additional_cli_steps.rb +3 -3
  47. data/features/subject/explicit_subject.feature +8 -8
  48. data/features/subject/implicit_receiver.feature +29 -0
  49. data/features/subject/implicit_subject.feature +4 -4
  50. data/features/support/env.rb +10 -3
  51. data/features/support/require_expect_syntax_in_aruba_specs.rb +16 -0
  52. data/lib/rspec/core.rb +11 -48
  53. data/lib/rspec/core/backport_random.rb +302 -0
  54. data/lib/rspec/core/backtrace_formatter.rb +65 -0
  55. data/lib/rspec/core/command_line.rb +7 -18
  56. data/lib/rspec/core/configuration.rb +202 -507
  57. data/lib/rspec/core/configuration_options.rb +17 -30
  58. data/lib/rspec/core/example.rb +29 -39
  59. data/lib/rspec/core/example_group.rb +166 -259
  60. data/lib/rspec/core/filter_manager.rb +30 -47
  61. data/lib/rspec/core/flat_map.rb +17 -0
  62. data/lib/rspec/core/formatters.rb +0 -138
  63. data/lib/rspec/core/formatters/base_formatter.rb +46 -1
  64. data/lib/rspec/core/formatters/base_text_formatter.rb +38 -61
  65. data/lib/rspec/core/formatters/deprecation_formatter.rb +21 -52
  66. data/lib/rspec/core/formatters/helpers.rb +0 -28
  67. data/lib/rspec/core/formatters/html_formatter.rb +1 -1
  68. data/lib/rspec/core/formatters/json_formatter.rb +38 -9
  69. data/lib/rspec/core/formatters/snippet_extractor.rb +14 -5
  70. data/lib/rspec/core/hooks.rb +55 -39
  71. data/lib/rspec/core/memoized_helpers.rb +17 -167
  72. data/lib/rspec/core/metadata.rb +16 -64
  73. data/lib/rspec/core/option_parser.rb +30 -39
  74. data/lib/rspec/core/ordering.rb +154 -0
  75. data/lib/rspec/core/pending.rb +12 -69
  76. data/lib/rspec/core/project_initializer.rb +12 -10
  77. data/lib/rspec/core/rake_task.rb +5 -108
  78. data/lib/rspec/core/reporter.rb +15 -18
  79. data/lib/rspec/core/runner.rb +16 -30
  80. data/lib/rspec/core/shared_context.rb +3 -5
  81. data/lib/rspec/core/shared_example_group.rb +3 -51
  82. data/lib/rspec/core/shared_example_group/collection.rb +1 -19
  83. data/lib/rspec/core/version.rb +1 -1
  84. data/lib/rspec/core/warnings.rb +22 -0
  85. data/lib/rspec/core/world.rb +12 -8
  86. data/spec/command_line/order_spec.rb +20 -23
  87. data/spec/rspec/core/backtrace_formatter_spec.rb +216 -0
  88. data/spec/rspec/core/command_line_spec.rb +32 -48
  89. data/spec/rspec/core/configuration_options_spec.rb +19 -50
  90. data/spec/rspec/core/configuration_spec.rb +142 -713
  91. data/spec/rspec/core/drb_command_line_spec.rb +2 -0
  92. data/spec/rspec/core/dsl_spec.rb +0 -1
  93. data/spec/rspec/core/example_group_spec.rb +192 -223
  94. data/spec/rspec/core/example_spec.rb +40 -16
  95. data/spec/rspec/core/filter_manager_spec.rb +2 -2
  96. data/spec/rspec/core/formatters/base_formatter_spec.rb +0 -41
  97. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +5 -123
  98. data/spec/rspec/core/formatters/deprecation_formatter_spec.rb +2 -87
  99. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +2 -3
  100. data/spec/rspec/core/formatters/{text_mate_formatted.html → html_formatted-1.8.7-jruby.html} +44 -25
  101. data/spec/rspec/core/formatters/html_formatted-1.8.7-rbx.html +477 -0
  102. data/spec/rspec/core/formatters/{html_formatted.html → html_formatted-1.8.7.html} +42 -25
  103. data/spec/rspec/core/formatters/html_formatted-1.9.2.html +425 -0
  104. data/spec/rspec/core/formatters/html_formatted-1.9.3-jruby.html +416 -0
  105. data/spec/rspec/core/formatters/html_formatted-1.9.3-rbx.html +477 -0
  106. data/spec/rspec/core/formatters/html_formatted-1.9.3.html +419 -0
  107. data/spec/rspec/core/formatters/html_formatted-2.0.0.html +425 -0
  108. data/spec/rspec/core/formatters/html_formatter_spec.rb +21 -46
  109. data/spec/rspec/core/formatters/json_formatter_spec.rb +97 -8
  110. data/spec/rspec/core/hooks_filtering_spec.rb +5 -5
  111. data/spec/rspec/core/hooks_spec.rb +61 -47
  112. data/spec/rspec/core/memoized_helpers_spec.rb +20 -322
  113. data/spec/rspec/core/metadata_spec.rb +1 -24
  114. data/spec/rspec/core/option_parser_spec.rb +20 -62
  115. data/spec/rspec/core/ordering_spec.rb +102 -0
  116. data/spec/rspec/core/pending_example_spec.rb +0 -40
  117. data/spec/rspec/core/project_initializer_spec.rb +1 -25
  118. data/spec/rspec/core/rake_task_spec.rb +5 -72
  119. data/spec/rspec/core/random_spec.rb +47 -0
  120. data/spec/rspec/core/reporter_spec.rb +23 -48
  121. data/spec/rspec/core/runner_spec.rb +31 -39
  122. data/spec/rspec/core/shared_context_spec.rb +3 -15
  123. data/spec/rspec/core/shared_example_group/collection_spec.rb +4 -17
  124. data/spec/rspec/core/shared_example_group_spec.rb +12 -45
  125. data/spec/rspec/core/{deprecation_spec.rb → warnings_spec.rb} +3 -1
  126. data/spec/rspec/core_spec.rb +4 -21
  127. data/spec/spec_helper.rb +41 -5
  128. data/spec/support/helper_methods.rb +0 -29
  129. data/spec/support/sandboxed_mock_space.rb +0 -16
  130. data/spec/support/shared_example_groups.rb +7 -36
  131. data/spec/support/stderr_splitter.rb +36 -0
  132. metadata +163 -157
  133. metadata.gz.sig +1 -0
  134. data/exe/autospec +0 -13
  135. data/features/Autotest.md +0 -38
  136. data/features/configuration/treat_symbols_as_metadata_keys_with_true_values.feature +0 -52
  137. data/features/subject/attribute_of_subject.feature +0 -124
  138. data/features/subject/one_liner_syntax.feature +0 -71
  139. data/lib/autotest/discover.rb +0 -10
  140. data/lib/autotest/rspec2.rb +0 -77
  141. data/lib/rspec/core/backtrace_cleaner.rb +0 -46
  142. data/lib/rspec/core/backward_compatibility.rb +0 -55
  143. data/lib/rspec/core/caller_filter.rb +0 -60
  144. data/lib/rspec/core/deprecated_mutable_array_proxy.rb +0 -32
  145. data/lib/rspec/core/deprecation.rb +0 -26
  146. data/lib/rspec/core/extensions/instance_eval_with_args.rb +0 -44
  147. data/lib/rspec/core/extensions/kernel.rb +0 -9
  148. data/lib/rspec/core/extensions/module_eval_with_args.rb +0 -38
  149. data/lib/rspec/core/extensions/ordered.rb +0 -27
  150. data/lib/rspec/core/formatters/console_codes.rb +0 -42
  151. data/lib/rspec/core/formatters/text_mate_formatter.rb +0 -34
  152. data/lib/rspec/core/metadata_hash_builder.rb +0 -97
  153. data/lib/rspec/core/minitest_assertions_adapter.rb +0 -28
  154. data/lib/rspec/core/test_unit_assertions_adapter.rb +0 -30
  155. data/spec/autotest/discover_spec.rb +0 -49
  156. data/spec/autotest/failed_results_re_spec.rb +0 -45
  157. data/spec/autotest/rspec_spec.rb +0 -133
  158. data/spec/rspec/core/backtrace_cleaner_spec.rb +0 -68
  159. data/spec/rspec/core/caller_filter_spec.rb +0 -58
  160. data/spec/rspec/core/deprecations_spec.rb +0 -59
  161. data/spec/rspec/core/formatters/console_codes_spec.rb +0 -50
  162. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +0 -107
  163. data/spec/rspec/core/kernel_extensions_spec.rb +0 -9
  164. data/spec/rspec/core/pending_spec.rb +0 -27
  165. data/spec/support/silence_dsl_deprecations.rb +0 -32
@@ -8,14 +8,9 @@ module RSpec
8
8
  attr_reader :options
9
9
 
10
10
  def initialize(args)
11
- @args = args.dup
12
- if @args.include?("--default_path")
13
- @args[@args.index("--default_path")] = "--default-path"
14
- end
15
-
16
- if @args.include?("--line_number")
17
- @args[@args.index("--line_number")] = "--line-number"
18
- end
11
+ @args = args.map {|a|
12
+ a.sub("default_path", "default-path").sub("line_number", "line-number")
13
+ }
19
14
  end
20
15
 
21
16
  def configure(config)
@@ -29,11 +24,16 @@ module RSpec
29
24
  end
30
25
 
31
26
  def parse_options
32
- @options ||= extract_filters_from(*all_configs).inject do |merged, pending|
33
- merged.merge(pending) { |key, oldval, newval|
34
- MERGED_OPTIONS.include?(key) ? oldval + newval : newval
27
+ @options = (file_options << command_line_options << env_options).
28
+ each {|opts|
29
+ filter_manager.include opts.delete(:inclusion_filter) if opts.has_key?(:inclusion_filter)
30
+ filter_manager.exclude opts.delete(:exclusion_filter) if opts.has_key?(:exclusion_filter)
31
+ }.
32
+ inject {|h, opts|
33
+ h.merge(opts) {|k, oldval, newval|
34
+ [:libs, :requires].include?(k) ? oldval + newval : newval
35
+ }
35
36
  }
36
- end
37
37
  end
38
38
 
39
39
  def drb_argv
@@ -46,17 +46,15 @@ module RSpec
46
46
 
47
47
  private
48
48
 
49
- NON_FORCED_OPTIONS = [
50
- :debug, :requires, :profile, :drb, :libs, :files_or_directories_to_run,
49
+ UNFORCED_OPTIONS = [
50
+ :requires, :profile, :drb, :libs, :files_or_directories_to_run,
51
51
  :line_numbers, :full_description, :full_backtrace, :tty
52
52
  ].to_set
53
53
 
54
- MERGED_OPTIONS = [:requires, :libs].to_set
55
-
56
54
  UNPROCESSABLE_OPTIONS = [:libs, :formatters, :requires].to_set
57
55
 
58
56
  def force?(key)
59
- !NON_FORCED_OPTIONS.include?(key)
57
+ !UNFORCED_OPTIONS.include?(key)
60
58
  end
61
59
 
62
60
  def order(keys, *ordered)
@@ -78,17 +76,6 @@ module RSpec
78
76
  options[:formatters].each { |pair| config.add_formatter(*pair) } if options[:formatters]
79
77
  end
80
78
 
81
- def extract_filters_from(*configs)
82
- configs.compact.each do |config|
83
- filter_manager.include config.delete(:inclusion_filter) if config.has_key?(:inclusion_filter)
84
- filter_manager.exclude config.delete(:exclusion_filter) if config.has_key?(:exclusion_filter)
85
- end
86
- end
87
-
88
- def all_configs
89
- @all_configs ||= file_options << command_line_options << env_options
90
- end
91
-
92
79
  def file_options
93
80
  custom_options_file ? [custom_options] : [global_options, project_options, local_options]
94
81
  end
@@ -124,7 +111,7 @@ module RSpec
124
111
  def args_from_options_file(path)
125
112
  return [] unless path && File.exist?(path)
126
113
  config_string = options_file_as_erb_string(path)
127
- config_string.split(/\n+/).map {|l| Shellwords.shellwords(l) }.flatten
114
+ FlatMap.flat_map(config_string.split(/\n+/), &:shellsplit)
128
115
  end
129
116
 
130
117
  def options_file_as_erb_string(path)
@@ -147,7 +134,7 @@ module RSpec
147
134
  begin
148
135
  File.join(File.expand_path("~"), ".rspec")
149
136
  rescue ArgumentError
150
- warn "Unable to find ~/.rspec because the HOME environment variable is not set"
137
+ RSpec.warning "Unable to find ~/.rspec because the HOME environment variable is not set"
151
138
  nil
152
139
  end
153
140
  end
@@ -44,7 +44,7 @@ module RSpec
44
44
  delegate_to_metadata :full_description, :execution_result, :file_path, :pending, :location
45
45
 
46
46
  # Returns the string submitted to `example` or its aliases (e.g.
47
- # `specify`, `it`, etc). If no string is submitted (e.g. `it { is_expected.to
47
+ # `specify`, `it`, etc). If no string is submitted (e.g. `it { should
48
48
  # do_something }`) it returns the message generated by the matcher if
49
49
  # there is one, otherwise returns a message including the location of the
50
50
  # example.
@@ -85,8 +85,6 @@ module RSpec
85
85
 
86
86
  # @deprecated access options via metadata instead
87
87
  def options
88
- RSpec.deprecate("`RSpec::Core::Example#options`",
89
- :replacement => "`RSpec::Core::Example#metadata`")
90
88
  @options
91
89
  end
92
90
 
@@ -109,17 +107,16 @@ module RSpec
109
107
  start(reporter)
110
108
 
111
109
  begin
112
- unless pending
110
+ unless pending || RSpec.configuration.dry_run?
113
111
  with_around_each_hooks do
114
112
  begin
115
113
  run_before_each
116
- @example_group_instance.instance_eval_with_args(self, &@example_block)
117
- rescue Pending::SkipDeclaredInExample => e
114
+ @example_group_instance.instance_exec(self, &@example_block)
115
+ rescue Pending::PendingDeclaredInExample => e
118
116
  @pending_declared_in_example = e.message
119
117
  rescue Exception => e
120
118
  set_exception(e)
121
119
  ensure
122
- assign_generated_description
123
120
  run_after_each
124
121
  end
125
122
  end
@@ -131,6 +128,12 @@ module RSpec
131
128
  @example_group_instance.instance_variable_set(ivar, nil)
132
129
  end
133
130
  @example_group_instance = nil
131
+
132
+ begin
133
+ assign_generated_description
134
+ rescue Exception => e
135
+ set_exception(e, "while assigning the example description")
136
+ end
134
137
  end
135
138
 
136
139
  finish(reporter)
@@ -138,16 +141,8 @@ module RSpec
138
141
  RSpec.current_example = nil
139
142
  end
140
143
 
141
- # @api private
142
- #
143
- # Wraps the example block in a Proc so it can invoked using `run` or
144
- # `call` in [around](../Hooks#around-instance_method) hooks.
145
- def self.procsy(metadata, &proc)
146
- proc.extend(Procsy).with(metadata)
147
- end
148
-
149
- # Used to extend a `Proc` with behavior that makes it look something like
150
- # an {Example} in an {Hooks#around around} hook.
144
+ # Wraps a `Proc` and exposes a `run` method for use in {Hooks#around
145
+ # around} hooks.
151
146
  #
152
147
  # @note Procsy, itself, is not a public API, but we're documenting it
153
148
  # here to document how to interact with the object yielded to an
@@ -156,32 +151,30 @@ module RSpec
156
151
  # @example
157
152
  #
158
153
  # RSpec.configure do |c|
159
- # c.around do |ex| # ex is a Proc extended with Procsy
154
+ # c.around do |ex| # Procsy which wraps the example
160
155
  # if ex.metadata[:key] == :some_value && some_global_condition
161
156
  # raise "some message"
162
157
  # end
163
158
  # ex.run # run delegates to ex.call
164
159
  # end
165
160
  # end
166
- module Procsy
161
+ class Procsy
167
162
  # The `metadata` of the {Example} instance.
168
163
  attr_reader :metadata
169
164
 
170
- # @api private
171
- # @param [Proc]
172
- # Adds a `run` method to the extended Proc, allowing it to be invoked
173
- # in an [around](../Hooks#around-instance_method) hook using either
174
- # `run` or `call`.
175
- def self.extended(proc)
176
- # @api public
177
- # Foo bar
178
- def proc.run; call; end
165
+ Proc.public_instance_methods(false).each do |name|
166
+ define_method(name) { |*a, &b| @proc.__send__(name, *a, &b) }
179
167
  end
168
+ alias run call
180
169
 
181
- # @api private
182
- def with(metadata)
170
+ def initialize(metadata, &block)
183
171
  @metadata = metadata
184
- self
172
+ @proc = block
173
+ end
174
+
175
+ # @api private
176
+ def wrap(&block)
177
+ self.class.new(metadata, &block)
185
178
  end
186
179
  end
187
180
 
@@ -232,13 +225,13 @@ An error occurred #{context}
232
225
  end
233
226
 
234
227
  # @private
235
- def instance_eval_with_rescue(context = nil, &block)
236
- @example_group_instance.instance_eval_with_rescue(self, context, &block)
228
+ def instance_exec_with_rescue(context = nil, &block)
229
+ @example_group_instance.instance_exec_with_rescue(self, context, &block)
237
230
  end
238
231
 
239
232
  # @private
240
- def instance_eval_with_args(*args, &block)
241
- @example_group_instance.instance_eval_with_args(*args, &block)
233
+ def instance_exec(*args, &block)
234
+ @example_group_instance.instance_exec(*args, &block)
242
235
  end
243
236
 
244
237
  private
@@ -247,7 +240,7 @@ An error occurred #{context}
247
240
  if around_each_hooks.empty?
248
241
  yield
249
242
  else
250
- @example_group_class.run_around_each_hooks(self, Example.procsy(metadata, &block))
243
+ @example_group_class.run_around_each_hooks(self, Procsy.new(metadata, &block))
251
244
  end
252
245
  rescue Exception => e
253
246
  set_exception(e, "in an around(:each) hook")
@@ -308,9 +301,6 @@ An error occurred #{context}
308
301
  if metadata[:description_args].empty? and !pending?
309
302
  metadata[:description_args] << RSpec::Matchers.generated_description
310
303
  end
311
- rescue Exception => e
312
- set_exception(e, "while assigning the example description")
313
- ensure
314
304
  RSpec::Matchers.clear_generated_description
315
305
  end
316
306
 
@@ -13,14 +13,10 @@ module RSpec
13
13
  # which serves as a wrapper for an instance of the ExampleGroup in which it
14
14
  # is declared.
15
15
  class ExampleGroup
16
- extend MetadataHashBuilder::WithDeprecationWarning
17
- extend Extensions::ModuleEvalWithArgs
18
16
  extend Hooks
19
17
 
20
18
  include MemoizedHelpers
21
- include Extensions::InstanceEvalWithArgs
22
19
  include Pending
23
- include SharedExampleGroup
24
20
  extend SharedExampleGroup
25
21
 
26
22
  # @private
@@ -49,166 +45,116 @@ module RSpec
49
45
  end
50
46
 
51
47
  delegate_to_metadata :described_class, :file_path
52
-
48
+ alias_method :display_name, :description
53
49
  # @private
54
- def display_name
55
- RSpec.deprecate('`RSpec::Core::ExampleGroup.display_name`',
56
- :replacement => "`RSpec::Core::ExampleGroup.description`")
57
- description
58
- end
50
+ alias_method :describes, :described_class
59
51
 
60
52
  # @private
61
- def describes
62
- RSpec.deprecate('`RSpec::Core::ExampleGroup.describes`',
63
- :replacement => "`RSpec::Core::ExampleGroup.described_class`")
64
- described_class
65
- end
66
- end
67
-
68
- # @private
69
- # @macro [attach] define_example_method
70
- # @param [String] name
71
- # @param [Hash] extra_options
72
- # @param [Block] implementation
73
- # @yield [Example] the example object
74
- def self.define_example_method(name, extra_options={})
75
- module_eval(<<-END_RUBY, __FILE__, __LINE__)
76
- def self.#{name}(desc=nil, *args, &block)
77
- if #{name.inspect} == :pending
78
- RSpec.warn_deprecation(<<-EOS.gsub(/^\s+\\|/, ''))
79
- |The semantics of `RSpec::Core::ExampleGroup.pending` are changing in RSpec 3.
80
- |In RSpec 2.x, it caused the example to be skipped. In RSpec 3, the example will
81
- |still be run but is expected to fail, and will be marked as a failure (rather
82
- |than as pending) if the example passes, just like how `pending` with a block
83
- |from within an example already works.
84
- |
85
- |To keep the same skip semantics, change `pending` to `skip`. Otherwise, if you
86
- |want the new RSpec 3 behavior, you can safely ignore this warning and continue
87
- |to upgrade to RSpec 3 without addressing it.
88
- |
89
- |Called from \#{CallerFilter.first_non_rspec_line}.
90
- |
91
- EOS
92
- end
93
- options = build_metadata_hash_from(args)
53
+ # @macro [attach] define_example_method
54
+ # @param [String] name
55
+ # @param [Hash] extra_options
56
+ # @param [Block] implementation
57
+ # @yield [Example] the example object
58
+ def self.define_example_method(name, extra_options={})
59
+ define_method(name) do |*all_args, &block|
60
+ desc, *args = *all_args
61
+ options = Metadata.build_hash_from(args)
94
62
  options.update(:pending => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
95
- # Backport from RSpec 3 to assist with upgrading
96
- options.update(:pending => options[:skip]) if options[:skip]
97
- options.update(#{extra_options.inspect})
63
+ options.update(extra_options)
98
64
  examples << RSpec::Core::Example.new(self, desc, options, block)
99
65
  examples.last
100
66
  end
101
- END_RUBY
102
- end
67
+ end
103
68
 
104
- # Defines an example within a group.
105
- # @example
106
- # example do
107
- # end
108
- #
109
- # example "does something" do
110
- # end
111
- #
112
- # example "does something", :with => 'additional metadata' do
113
- # end
114
- #
115
- # example "does something" do |ex|
116
- # # ex is the Example object that evals this block
117
- # end
118
- define_example_method :example
119
- # Defines an example within a group.
120
- # @example
121
- define_example_method :it
122
- # Defines an example within a group.
123
- # This is here primarily for backward compatibility with early versions
124
- # of RSpec which used `context` and `specify` instead of `describe` and
125
- # `it`.
126
- define_example_method :specify
127
-
128
- # Shortcut to define an example with `:focus` => true
129
- # @see example
130
- define_example_method :focus, :focused => true, :focus => true
131
- # Shortcut to define an example with `:focus` => true
132
- # @see example
133
- define_example_method :fit, :focused => true, :focus => true
134
-
135
- # Shortcut to define an example with :pending => true
136
- # @see example
137
- define_example_method :pending, :pending => true
138
- # Shortcut to define an example with :pending => true
139
- # Backported from RSpec 3 to aid migration.
140
- # @see example
141
- define_example_method :skip, :pending => true
142
- # Shortcut to define an example with :pending => 'Temporarily disabled with xexample'
143
- # @see example
144
- define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
145
- # Shortcut to define an example with :pending => 'Temporarily disabled with xit'
146
- # @see example
147
- define_example_method :xit, :pending => 'Temporarily disabled with xit'
148
- # Shortcut to define an example with :pending => 'Temporarily disabled with xspecify'
149
- # @see example
150
- define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
151
-
152
- # Shortcut to define an example with `:focus` => true
153
- # @see example
154
- def self.focused(desc=nil, *args, &block)
155
- RSpec.deprecate("`RSpec::Core::ExampleGroup.focused`",
156
- :replacement => "`RSpec::Core::ExampleGroup.focus`")
157
-
158
- metadata = Hash === args.last ? args.pop : {}
159
- metadata.merge!(:focus => true, :focused => true)
160
- args << metadata
161
-
162
- example(desc, *args, &block)
163
- end
164
-
165
- # Works like `alias_method :name, :example` with the added benefit of
166
- # assigning default metadata to the generated example.
167
- #
168
- # @note Use with caution. This extends the language used in your
169
- # specs, but does not add any additional documentation. We use this
170
- # in rspec to define methods like `focus` and `xit`, but we also add
171
- # docs for those methods.
172
- def self.alias_example_to name, extra={}
173
- RSpec.deprecate("`RSpec::Core::ExampleGroup.alias_example_to`",
174
- :replacement => "`RSpec::Core::Configuration#alias_example_to`")
175
- define_example_method name, extra
176
- end
69
+ # Defines an example within a group.
70
+ # @example
71
+ # example do
72
+ # end
73
+ #
74
+ # example "does something" do
75
+ # end
76
+ #
77
+ # example "does something", :with => 'additional metadata' do
78
+ # end
79
+ #
80
+ # example "does something" do |ex|
81
+ # # ex is the Example object that evals this block
82
+ # end
83
+ define_example_method :example
84
+ # Defines an example within a group.
85
+ # @example
86
+ define_example_method :it
87
+ # Defines an example within a group.
88
+ # This is here primarily for backward compatibility with early versions
89
+ # of RSpec which used `context` and `specify` instead of `describe` and
90
+ # `it`.
91
+ define_example_method :specify
92
+
93
+ # Shortcut to define an example with `:focus` => true
94
+ # @see example
95
+ define_example_method :focus, :focused => true, :focus => true
96
+ # Shortcut to define an example with `:focus` => true
97
+ # @see example
98
+ define_example_method :focused, :focused => true, :focus => true
99
+ # Shortcut to define an example with `:focus` => true
100
+ # @see example
101
+ define_example_method :fit, :focused => true, :focus => true
102
+
103
+ # Shortcut to define an example with :pending => true
104
+ # @see example
105
+ define_example_method :pending, :pending => true
106
+ # Shortcut to define an example with :pending => 'Temporarily disabled with xexample'
107
+ # @see example
108
+ define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
109
+ # Shortcut to define an example with :pending => 'Temporarily disabled with xit'
110
+ # @see example
111
+ define_example_method :xit, :pending => 'Temporarily disabled with xit'
112
+ # Shortcut to define an example with :pending => 'Temporarily disabled with xspecify'
113
+ # @see example
114
+ define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
115
+
116
+ # Works like `alias_method :name, :example` with the added benefit of
117
+ # assigning default metadata to the generated example.
118
+ #
119
+ # @note Use with caution. This extends the language used in your
120
+ # specs, but does not add any additional documentation. We use this
121
+ # in rspec to define methods like `focus` and `xit`, but we also add
122
+ # docs for those methods.
123
+ def alias_example_to name, extra={}
124
+ (class << self; self; end).define_example_method name, extra
125
+ end
177
126
 
178
- # @private
179
- # @macro [attach] define_nested_shared_group_method
180
- #
181
- # @see SharedExampleGroup
182
- def self.define_nested_shared_group_method(new_name, report_label=nil)
183
- module_eval(<<-END_RUBY, __FILE__, __LINE__)
184
- def self.#{new_name}(name, *args, &customization_block)
185
- group = describe("#{report_label || "it should behave like"} \#{name}") do
127
+ # @private
128
+ # @macro [attach] define_nested_shared_group_method
129
+ #
130
+ # @see SharedExampleGroup
131
+ def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
132
+ define_method(new_name) do |name, *args, &customization_block|
133
+ group = describe("#{report_label} #{name}") do
186
134
  find_and_eval_shared("examples", name, *args, &customization_block)
187
135
  end
188
136
  group.metadata[:shared_group_name] = name
189
137
  group
190
138
  end
191
- END_RUBY
192
- end
139
+ end
193
140
 
194
- # Generates a nested example group and includes the shared content
195
- # mapped to `name` in the nested group.
196
- define_nested_shared_group_method :it_behaves_like, "behaves like"
197
- # Generates a nested example group and includes the shared content
198
- # mapped to `name` in the nested group.
199
- define_nested_shared_group_method :it_should_behave_like
141
+ # Generates a nested example group and includes the shared content
142
+ # mapped to `name` in the nested group.
143
+ define_nested_shared_group_method :it_behaves_like, "behaves like"
144
+ # Generates a nested example group and includes the shared content
145
+ # mapped to `name` in the nested group.
146
+ define_nested_shared_group_method :it_should_behave_like
200
147
 
201
- # Works like `alias_method :name, :it_behaves_like` with the added
202
- # benefit of assigning default metadata to the generated example.
203
- #
204
- # @note Use with caution. This extends the language used in your
205
- # specs, but does not add any additional documentation. We use this
206
- # in rspec to define `it_should_behave_like` (for backward
207
- # compatibility), but we also add docs for that method.
208
- def self.alias_it_behaves_like_to name, *args, &block
209
- RSpec.deprecate("`RSpec::Core::ExampleGroup.alias_it_behaves_like_to`",
210
- :replacement => "`RSpec::Core::Configuration#alias_it_behaves_like_to`")
211
- define_nested_shared_group_method name, *args, &block
148
+ # Works like `alias_method :name, :it_behaves_like` with the added
149
+ # benefit of assigning default metadata to the generated example.
150
+ #
151
+ # @note Use with caution. This extends the language used in your
152
+ # specs, but does not add any additional documentation. We use this
153
+ # in rspec to define `it_should_behave_like` (for backward
154
+ # compatibility), but we also add docs for that method.
155
+ def alias_it_behaves_like_to name, *args, &block
156
+ (class << self; self; end).define_nested_shared_group_method name, *args, &block
157
+ end
212
158
  end
213
159
 
214
160
  # Includes shared content mapped to `name` directly in the group in which
@@ -229,46 +175,12 @@ module RSpec
229
175
  find_and_eval_shared("examples", name, *args, &block)
230
176
  end
231
177
 
232
- if Proc.method_defined?(:parameters) # for >= 1.9
233
- # Warn when submitting the name of more than one example group to
234
- # include_examples, it_behaves_like, etc.
235
- #
236
- # Helpful when upgrading from rspec-1 (which supported multiple shared
237
- # groups in one call) to rspec-2 (which does not).
238
- #
239
- # See https://github.com/rspec/rspec-core/issues/1066 for background.
240
- def self.warn_unexpected_args(label, name, args, shared_block)
241
- if !args.empty? && shared_block.parameters.count == 0
242
- if shared_example_groups[args.first]
243
- warn <<-WARNING
244
- shared #{label} support#{'s' if /context/ =~ label.to_s} the name of only one example group, received #{[name, *args].inspect}
245
- called from #{CallerFilter.first_non_rspec_line}"
246
- WARNING
247
- else
248
- warn <<-WARNING
249
- shared #{label} #{name.inspect} expected #{shared_block.arity} args, got #{args.inspect}
250
- called from #{CallerFilter.first_non_rspec_line}"
251
- WARNING
252
- end
253
- end
254
- end
255
- else
256
- # no-op for Ruby < 1.9
257
- #
258
- # Ruby 1.8 reports lambda {}.arity == -1, so can't support this warning
259
- # reliably
260
- def self.warn_unexpected_args(*)
261
- end
262
- end
263
-
264
178
  # @private
265
179
  def self.find_and_eval_shared(label, name, *args, &customization_block)
266
180
  raise ArgumentError, "Could not find shared #{label} #{name.inspect}" unless
267
181
  shared_block = shared_example_groups[name]
268
182
 
269
- warn_unexpected_args(label, name, args, shared_block)
270
-
271
- module_eval_with_args(*args, &shared_block)
183
+ module_exec(*args, &shared_block)
272
184
  module_eval(&customization_block) if customization_block
273
185
  end
274
186
 
@@ -321,43 +233,14 @@ WARNING
321
233
  #
322
234
  # @see DSL#describe
323
235
  def self.describe(*args, &example_group_block)
324
- @_subclass_count ||= 0
325
- @_subclass_count += 1
326
-
327
- if Symbol === args.first || Hash === args.first
328
- description_arg_behavior_changing_in_rspec_3 = DescriptionBehaviorChange.new(
329
- args.first, CallerFilter.first_non_rspec_line
330
- )
331
- end
332
-
333
236
  args << {} unless args.last.is_a?(Hash)
334
- args.last.update(
335
- :example_group_block => example_group_block,
336
- :description_arg_behavior_changing_in_rspec_3 => description_arg_behavior_changing_in_rspec_3
337
- )
338
-
339
- # TODO 2010-05-05: Because we don't know if const_set is thread-safe
340
- child = const_set(
341
- "Nested_#{@_subclass_count}",
342
- subclass(self, args, &example_group_block)
343
- )
237
+ args.last.update(:example_group_block => example_group_block)
238
+
239
+ child = subclass(self, args, &example_group_block)
344
240
  children << child
345
241
  child
346
242
  end
347
243
 
348
- DescriptionBehaviorChange = Struct.new(:arg, :call_site) do
349
- def warning
350
- <<-EOS.gsub(/^\s+\|/, '')
351
- |The semantics of `describe <a #{arg.class.name}>` are changing in RSpec 3. In RSpec 2,
352
- |this would be treated as metadata, but as the first `describe` argument,
353
- |this will be treated as the described object (affecting the value of
354
- |`described_class`) in RSpec 3. If you want this to be treated as metadata,
355
- |pass a description as the first argument.
356
- |(Example group defined at #{call_site})
357
- EOS
358
- end
359
- end
360
-
361
244
  class << self
362
245
  alias_method :context, :describe
363
246
  end
@@ -366,6 +249,7 @@ WARNING
366
249
  def self.subclass(parent, args, &example_group_block)
367
250
  subclass = Class.new(parent)
368
251
  subclass.set_it_up(*args)
252
+ ExampleGroups.assign_const(subclass)
369
253
  subclass.module_eval(&example_group_block) if example_group_block
370
254
 
371
255
  # The LetDefinitions module must be included _after_ other modules
@@ -379,7 +263,7 @@ WARNING
379
263
 
380
264
  # @private
381
265
  def self.children
382
- @children ||= [].extend(Extensions::Ordered::ExampleGroups)
266
+ @children ||= []
383
267
  end
384
268
 
385
269
  # @private
@@ -418,9 +302,10 @@ WARNING
418
302
  ensure_example_groups_are_configured
419
303
 
420
304
  symbol_description = args.shift if args.first.is_a?(Symbol)
421
- args << build_metadata_hash_from(args)
305
+ args << Metadata.build_hash_from(args)
422
306
  args.unshift(symbol_description) if symbol_description
423
307
  @metadata = RSpec::Core::Metadata.new(superclass_metadata).process(*args)
308
+ @order = nil
424
309
  hooks.register_globals(self, RSpec.configuration.hooks)
425
310
  world.configure_group(self)
426
311
  end
@@ -494,7 +379,7 @@ WARNING
494
379
  begin
495
380
  run_before_all_hooks(new)
496
381
  result_for_this_group = run_examples(reporter)
497
- results_for_descendants = children.ordered.map {|child| child.run(reporter)}.all?
382
+ results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
498
383
  result_for_this_group && results_for_descendants
499
384
  rescue Exception => ex
500
385
  RSpec.wants_to_quit = true if fail_fast?
@@ -506,9 +391,25 @@ WARNING
506
391
  end
507
392
  end
508
393
 
394
+ # @private
395
+ def self.ordering_strategy
396
+ order = metadata.fetch(:order, :global)
397
+ registry = RSpec.configuration.ordering_registry
398
+
399
+ registry.fetch(order) do
400
+ warn <<-WARNING.gsub(/^ +\|/, '')
401
+ |WARNING: Ignoring unknown ordering specified using `:order => #{order.inspect}` metadata.
402
+ | Falling back to configured global ordering.
403
+ | Unrecognized ordering specified at: #{metadata[:example_group][:location]}
404
+ WARNING
405
+
406
+ registry.fetch(:global)
407
+ end
408
+ end
409
+
509
410
  # @private
510
411
  def self.run_examples(reporter)
511
- filtered_examples.ordered.map do |example|
412
+ ordering_strategy.order(filtered_examples).map do |example|
512
413
  next if RSpec.wants_to_quit
513
414
  instance = new
514
415
  set_ivars(instance, before_all_ivars)
@@ -562,44 +463,6 @@ WARNING
562
463
  ivars.each {|name, value| instance.instance_variable_set(name, value)}
563
464
  end
564
465
 
565
- def example=(current_example)
566
- RSpec.current_example = current_example
567
- end
568
-
569
- # @deprecated use a block argument
570
- def example
571
- warn_deprecation_of_example_accessor :example
572
- RSpec.current_example
573
- end
574
-
575
- # @deprecated use a block argument
576
- def running_example
577
- warn_deprecation_of_example_accessor :running_example
578
- RSpec.current_example
579
- end
580
-
581
- def warn_deprecation_of_example_accessor(name)
582
- RSpec.warn_deprecation(<<-EOS.gsub(/^\s*\|/, ''))
583
- |RSpec::Core::ExampleGroup##{name} is deprecated and will be removed
584
- |in RSpec 3. There are a few options for what you can use instead:
585
- |
586
- | - rspec-core's DSL methods (`it`, `before`, `after`, `let`, `subject`, etc)
587
- | now yield the example as a block argument, and that is the recommended
588
- | way to access the current example from those contexts.
589
- | - The current example is now exposed via `RSpec.current_example`,
590
- | which is accessible from any context.
591
- | - If you can't update the code at this call site (e.g. because it is in
592
- | an extension gem), you can use this snippet to continue making this
593
- | method available in RSpec 2.99 and RSpec 3:
594
- |
595
- | RSpec.configure do |c|
596
- | c.expose_current_running_example_as :#{name}
597
- | end
598
- |
599
- |(Called from #{CallerFilter.first_non_rspec_line})
600
- EOS
601
- end
602
-
603
466
  # Returns the class or module passed to the `describe` method (or alias).
604
467
  # Returns nil if the subject is not a class or module.
605
468
  # @example
@@ -617,9 +480,9 @@ WARNING
617
480
  # @private
618
481
  # instance_evals the block, capturing and reporting an exception if
619
482
  # raised
620
- def instance_eval_with_rescue(example, context = nil, &hook)
483
+ def instance_exec_with_rescue(example, context = nil, &hook)
621
484
  begin
622
- instance_eval_with_args(example, &hook)
485
+ instance_exec(example, &hook)
623
486
  rescue Exception => e
624
487
  if RSpec.current_example
625
488
  RSpec.current_example.set_exception(e, context)
@@ -630,4 +493,48 @@ WARNING
630
493
  end
631
494
  end
632
495
  end
496
+
497
+ # Namespace for the example group subclasses generated by top-level `describe`.
498
+ module ExampleGroups
499
+ def self.assign_const(group)
500
+ base_name = base_name_for(group)
501
+ const_scope = constant_scope_for(group)
502
+ name = disambiguate(base_name, const_scope)
503
+
504
+ const_scope.const_set(name, group)
505
+ end
506
+
507
+ def self.constant_scope_for(group)
508
+ const_scope = group.superclass
509
+ const_scope = self if const_scope == Core::ExampleGroup
510
+ const_scope
511
+ end
512
+
513
+ def self.base_name_for(group)
514
+ return "Anonymous" if group.description.empty?
515
+
516
+ # convert to CamelCase
517
+ name = ' ' + group.description
518
+ name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) { $1.upcase }
519
+
520
+ name.lstrip! # Remove leading whitespace
521
+ name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
522
+
523
+ # Ruby requires first const letter to be A-Z. Use `Nested`
524
+ # as necessary to enforce that.
525
+ name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
526
+
527
+ name
528
+ end
529
+
530
+ def self.disambiguate(name, const_scope)
531
+ return name unless const_scope.const_defined?(name)
532
+
533
+ # Add a trailing number if needed to disambiguate from an existing constant.
534
+ name << "_2"
535
+ name.next! while const_scope.const_defined?(name)
536
+ name
537
+ end
538
+ end
633
539
  end
540
+