rspec-core 2.11.1 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (222) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +3 -1
  5. data/Changelog.md +1814 -29
  6. data/{License.txt → LICENSE.md} +6 -4
  7. data/README.md +197 -48
  8. data/exe/rspec +2 -23
  9. data/lib/rspec/autorun.rb +1 -0
  10. data/lib/rspec/core/backtrace_formatter.rb +65 -0
  11. data/lib/rspec/core/bisect/coordinator.rb +62 -0
  12. data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
  13. data/lib/rspec/core/bisect/fork_runner.rb +138 -0
  14. data/lib/rspec/core/bisect/server.rb +61 -0
  15. data/lib/rspec/core/bisect/shell_command.rb +126 -0
  16. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  17. data/lib/rspec/core/bisect/utilities.rb +69 -0
  18. data/lib/rspec/core/configuration.rb +1846 -407
  19. data/lib/rspec/core/configuration_options.rb +154 -50
  20. data/lib/rspec/core/did_you_mean.rb +46 -0
  21. data/lib/rspec/core/drb.rb +120 -0
  22. data/lib/rspec/core/dsl.rb +90 -18
  23. data/lib/rspec/core/example.rb +488 -152
  24. data/lib/rspec/core/example_group.rb +733 -294
  25. data/lib/rspec/core/example_status_persister.rb +235 -0
  26. data/lib/rspec/core/filter_manager.rb +175 -147
  27. data/lib/rspec/core/flat_map.rb +20 -0
  28. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  29. data/lib/rspec/core/formatters/base_formatter.rb +32 -130
  30. data/lib/rspec/core/formatters/base_text_formatter.rb +62 -190
  31. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  32. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
  33. data/lib/rspec/core/formatters/console_codes.rb +76 -0
  34. data/lib/rspec/core/formatters/deprecation_formatter.rb +223 -0
  35. data/lib/rspec/core/formatters/documentation_formatter.rb +62 -27
  36. data/lib/rspec/core/formatters/exception_presenter.rb +521 -0
  37. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  38. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  39. data/lib/rspec/core/formatters/helpers.rb +93 -14
  40. data/lib/rspec/core/formatters/html_formatter.rb +104 -415
  41. data/lib/rspec/core/formatters/html_printer.rb +414 -0
  42. data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
  43. data/lib/rspec/core/formatters/json_formatter.rb +102 -0
  44. data/lib/rspec/core/formatters/profile_formatter.rb +68 -0
  45. data/lib/rspec/core/formatters/progress_formatter.rb +12 -15
  46. data/lib/rspec/core/formatters/protocol.rb +182 -0
  47. data/lib/rspec/core/formatters/snippet_extractor.rb +115 -39
  48. data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
  49. data/lib/rspec/core/formatters.rb +279 -0
  50. data/lib/rspec/core/hooks.rb +451 -300
  51. data/lib/rspec/core/invocations.rb +87 -0
  52. data/lib/rspec/core/memoized_helpers.rb +580 -0
  53. data/lib/rspec/core/metadata.rb +395 -173
  54. data/lib/rspec/core/metadata_filter.rb +255 -0
  55. data/lib/rspec/core/minitest_assertions_adapter.rb +31 -0
  56. data/lib/rspec/core/mocking_adapters/flexmock.rb +31 -0
  57. data/lib/rspec/core/mocking_adapters/mocha.rb +57 -0
  58. data/lib/rspec/core/mocking_adapters/null.rb +14 -0
  59. data/lib/rspec/core/mocking_adapters/rr.rb +31 -0
  60. data/lib/rspec/core/mocking_adapters/rspec.rb +32 -0
  61. data/lib/rspec/core/notifications.rb +521 -0
  62. data/lib/rspec/core/option_parser.rb +208 -64
  63. data/lib/rspec/core/ordering.rb +169 -0
  64. data/lib/rspec/core/output_wrapper.rb +29 -0
  65. data/lib/rspec/core/pending.rb +115 -59
  66. data/lib/rspec/core/profiler.rb +34 -0
  67. data/lib/rspec/core/project_initializer/.rspec +1 -0
  68. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +98 -0
  69. data/lib/rspec/core/project_initializer.rb +26 -65
  70. data/lib/rspec/core/rake_task.rb +140 -131
  71. data/lib/rspec/core/reporter.rb +207 -44
  72. data/lib/rspec/core/ruby_project.rb +15 -6
  73. data/lib/rspec/core/runner.rb +180 -44
  74. data/lib/rspec/core/sandbox.rb +37 -0
  75. data/lib/rspec/core/set.rb +54 -0
  76. data/lib/rspec/core/shared_context.rb +25 -19
  77. data/lib/rspec/core/shared_example_group.rb +229 -54
  78. data/lib/rspec/core/shell_escape.rb +49 -0
  79. data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
  80. data/lib/rspec/core/version.rb +3 -1
  81. data/lib/rspec/core/warnings.rb +40 -0
  82. data/lib/rspec/core/world.rb +208 -49
  83. data/lib/rspec/core.rb +166 -80
  84. data.tar.gz.sig +0 -0
  85. metadata +230 -445
  86. metadata.gz.sig +0 -0
  87. data/exe/autospec +0 -13
  88. data/features/Autotest.md +0 -38
  89. data/features/README.md +0 -17
  90. data/features/Upgrade.md +0 -364
  91. data/features/command_line/README.md +0 -28
  92. data/features/command_line/example_name_option.feature +0 -101
  93. data/features/command_line/exit_status.feature +0 -83
  94. data/features/command_line/format_option.feature +0 -81
  95. data/features/command_line/init.feature +0 -18
  96. data/features/command_line/line_number_appended_to_path.feature +0 -140
  97. data/features/command_line/line_number_option.feature +0 -58
  98. data/features/command_line/order.feature +0 -29
  99. data/features/command_line/pattern_option.feature +0 -31
  100. data/features/command_line/rake_task.feature +0 -68
  101. data/features/command_line/ruby.feature +0 -22
  102. data/features/command_line/tag.feature +0 -91
  103. data/features/configuration/alias_example_to.feature +0 -48
  104. data/features/configuration/custom_settings.feature +0 -84
  105. data/features/configuration/default_path.feature +0 -38
  106. data/features/configuration/fail_fast.feature +0 -77
  107. data/features/configuration/read_options_from_file.feature +0 -87
  108. data/features/example_groups/basic_structure.feature +0 -55
  109. data/features/example_groups/shared_context.feature +0 -74
  110. data/features/example_groups/shared_examples.feature +0 -204
  111. data/features/expectation_framework_integration/configure_expectation_framework.feature +0 -102
  112. data/features/filtering/exclusion_filters.feature +0 -139
  113. data/features/filtering/if_and_unless.feature +0 -168
  114. data/features/filtering/inclusion_filters.feature +0 -105
  115. data/features/filtering/run_all_when_everything_filtered.feature +0 -46
  116. data/features/formatters/custom_formatter.feature +0 -36
  117. data/features/formatters/text_formatter.feature +0 -46
  118. data/features/helper_methods/arbitrary_methods.feature +0 -40
  119. data/features/helper_methods/let.feature +0 -50
  120. data/features/helper_methods/modules.feature +0 -149
  121. data/features/hooks/around_hooks.feature +0 -343
  122. data/features/hooks/before_and_after_hooks.feature +0 -423
  123. data/features/hooks/filtering.feature +0 -234
  124. data/features/metadata/current_example.feature +0 -17
  125. data/features/metadata/described_class.feature +0 -17
  126. data/features/metadata/user_defined.feature +0 -113
  127. data/features/mock_framework_integration/use_any_framework.feature +0 -106
  128. data/features/mock_framework_integration/use_flexmock.feature +0 -96
  129. data/features/mock_framework_integration/use_mocha.feature +0 -97
  130. data/features/mock_framework_integration/use_rr.feature +0 -98
  131. data/features/mock_framework_integration/use_rspec.feature +0 -97
  132. data/features/pending/pending_examples.feature +0 -229
  133. data/features/spec_files/arbitrary_file_suffix.feature +0 -13
  134. data/features/step_definitions/additional_cli_steps.rb +0 -30
  135. data/features/subject/attribute_of_subject.feature +0 -124
  136. data/features/subject/explicit_subject.feature +0 -82
  137. data/features/subject/implicit_receiver.feature +0 -29
  138. data/features/subject/implicit_subject.feature +0 -63
  139. data/features/support/env.rb +0 -12
  140. data/lib/autotest/discover.rb +0 -1
  141. data/lib/autotest/rspec2.rb +0 -73
  142. data/lib/rspec/core/backward_compatibility.rb +0 -65
  143. data/lib/rspec/core/command_line.rb +0 -36
  144. data/lib/rspec/core/deprecation.rb +0 -36
  145. data/lib/rspec/core/drb_command_line.rb +0 -26
  146. data/lib/rspec/core/drb_options.rb +0 -87
  147. data/lib/rspec/core/extensions/instance_eval_with_args.rb +0 -44
  148. data/lib/rspec/core/extensions/kernel.rb +0 -9
  149. data/lib/rspec/core/extensions/module_eval_with_args.rb +0 -38
  150. data/lib/rspec/core/extensions/ordered.rb +0 -21
  151. data/lib/rspec/core/extensions.rb +0 -4
  152. data/lib/rspec/core/formatters/text_mate_formatter.rb +0 -34
  153. data/lib/rspec/core/let.rb +0 -110
  154. data/lib/rspec/core/load_path.rb +0 -3
  155. data/lib/rspec/core/metadata_hash_builder.rb +0 -97
  156. data/lib/rspec/core/mocking/with_absolutely_nothing.rb +0 -11
  157. data/lib/rspec/core/mocking/with_flexmock.rb +0 -27
  158. data/lib/rspec/core/mocking/with_mocha.rb +0 -29
  159. data/lib/rspec/core/mocking/with_rr.rb +0 -27
  160. data/lib/rspec/core/mocking/with_rspec.rb +0 -23
  161. data/lib/rspec/core/subject.rb +0 -219
  162. data/spec/autotest/discover_spec.rb +0 -19
  163. data/spec/autotest/failed_results_re_spec.rb +0 -45
  164. data/spec/autotest/rspec_spec.rb +0 -123
  165. data/spec/command_line/order_spec.rb +0 -137
  166. data/spec/rspec/core/command_line_spec.rb +0 -108
  167. data/spec/rspec/core/command_line_spec_output.txt +0 -0
  168. data/spec/rspec/core/configuration_options_spec.rb +0 -377
  169. data/spec/rspec/core/configuration_spec.rb +0 -1196
  170. data/spec/rspec/core/deprecations_spec.rb +0 -66
  171. data/spec/rspec/core/drb_command_line_spec.rb +0 -108
  172. data/spec/rspec/core/drb_options_spec.rb +0 -180
  173. data/spec/rspec/core/dsl_spec.rb +0 -17
  174. data/spec/rspec/core/example_group_spec.rb +0 -1098
  175. data/spec/rspec/core/example_spec.rb +0 -370
  176. data/spec/rspec/core/filter_manager_spec.rb +0 -256
  177. data/spec/rspec/core/formatters/base_formatter_spec.rb +0 -80
  178. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +0 -363
  179. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +0 -88
  180. data/spec/rspec/core/formatters/helpers_spec.rb +0 -66
  181. data/spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html +0 -410
  182. data/spec/rspec/core/formatters/html_formatted-1.8.7.html +0 -409
  183. data/spec/rspec/core/formatters/html_formatted-1.9.2.html +0 -416
  184. data/spec/rspec/core/formatters/html_formatted-1.9.3.html +0 -416
  185. data/spec/rspec/core/formatters/html_formatter_spec.rb +0 -82
  186. data/spec/rspec/core/formatters/progress_formatter_spec.rb +0 -30
  187. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +0 -18
  188. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html +0 -410
  189. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7.html +0 -409
  190. data/spec/rspec/core/formatters/text_mate_formatted-1.9.2.html +0 -416
  191. data/spec/rspec/core/formatters/text_mate_formatted-1.9.3.html +0 -416
  192. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +0 -83
  193. data/spec/rspec/core/hooks_filtering_spec.rb +0 -227
  194. data/spec/rspec/core/hooks_spec.rb +0 -250
  195. data/spec/rspec/core/kernel_extensions_spec.rb +0 -9
  196. data/spec/rspec/core/let_spec.rb +0 -55
  197. data/spec/rspec/core/metadata_spec.rb +0 -447
  198. data/spec/rspec/core/option_parser_spec.rb +0 -166
  199. data/spec/rspec/core/pending_example_spec.rb +0 -220
  200. data/spec/rspec/core/project_initializer_spec.rb +0 -130
  201. data/spec/rspec/core/rake_task_spec.rb +0 -138
  202. data/spec/rspec/core/reporter_spec.rb +0 -103
  203. data/spec/rspec/core/resources/a_bar.rb +0 -0
  204. data/spec/rspec/core/resources/a_foo.rb +0 -0
  205. data/spec/rspec/core/resources/a_spec.rb +0 -1
  206. data/spec/rspec/core/resources/custom_example_group_runner.rb +0 -14
  207. data/spec/rspec/core/resources/formatter_specs.rb +0 -60
  208. data/spec/rspec/core/resources/utf8_encoded.rb +0 -8
  209. data/spec/rspec/core/rspec_matchers_spec.rb +0 -45
  210. data/spec/rspec/core/ruby_project_spec.rb +0 -24
  211. data/spec/rspec/core/runner_spec.rb +0 -81
  212. data/spec/rspec/core/shared_context_spec.rb +0 -67
  213. data/spec/rspec/core/shared_example_group_spec.rb +0 -84
  214. data/spec/rspec/core/subject_spec.rb +0 -244
  215. data/spec/rspec/core/world_spec.rb +0 -144
  216. data/spec/rspec/core_spec.rb +0 -35
  217. data/spec/spec_helper.rb +0 -98
  218. data/spec/support/config_options_helper.rb +0 -24
  219. data/spec/support/helper_methods.rb +0 -5
  220. data/spec/support/matchers.rb +0 -65
  221. data/spec/support/shared_example_groups.rb +0 -41
  222. data/spec/support/spec_files.rb +0 -44
@@ -1,9 +1,13 @@
1
+ RSpec::Support.require_rspec_support 'recursive_const_methods'
2
+
1
3
  module RSpec
2
4
  module Core
5
+ # rubocop:disable Metrics/ClassLength
6
+
3
7
  # ExampleGroup and {Example} are the main structural elements of
4
- # rspec-core. Consider this example:
8
+ # rspec-core. Consider this example:
5
9
  #
6
- # describe Thing do
10
+ # RSpec.describe Thing do
7
11
  # it "does something" do
8
12
  # end
9
13
  # end
@@ -12,251 +16,502 @@ module RSpec
12
16
  # The object returned by `it "does something"` is an instance of Example,
13
17
  # which serves as a wrapper for an instance of the ExampleGroup in which it
14
18
  # is declared.
19
+ #
20
+ # Example group bodies (e.g. `describe` or `context` blocks) are evaluated
21
+ # in the context of a new subclass of ExampleGroup. Individual examples are
22
+ # evaluated in the context of an instance of the specific ExampleGroup
23
+ # subclass to which they belong.
24
+ #
25
+ # Besides the class methods defined here, there are other interesting macros
26
+ # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and
27
+ # {SharedExampleGroup}. There are additional instance methods available to
28
+ # your examples defined in {MemoizedHelpers} and {Pending}.
15
29
  class ExampleGroup
16
- extend MetadataHashBuilder::WithDeprecationWarning
17
- extend Extensions::ModuleEvalWithArgs
18
- extend Subject::ExampleGroupMethods
19
- extend Hooks
30
+ extend Hooks
20
31
 
21
- include Extensions::InstanceEvalWithArgs
22
- include Subject::ExampleMethods
32
+ include MemoizedHelpers
33
+ extend MemoizedHelpers::ClassMethods
23
34
  include Pending
24
- include Let
35
+ extend SharedExampleGroup
25
36
 
37
+ # Define a singleton method for the singleton class (remove the method if
38
+ # it's already been defined).
26
39
  # @private
27
- def self.world
28
- RSpec.world
40
+ def self.idempotently_define_singleton_method(name, &definition)
41
+ (class << self; self; end).module_exec do
42
+ remove_method(name) if method_defined?(name) && instance_method(name).owner == self
43
+ define_method(name, &definition)
44
+ end
45
+ end
46
+
47
+ # @!group Metadata
48
+
49
+ # The [Metadata](Metadata) object associated with this group.
50
+ # @see Metadata
51
+ def self.metadata
52
+ @metadata ||= nil
29
53
  end
30
54
 
55
+ # Temporarily replace the provided metadata.
56
+ # Intended primarily to allow an example group's singleton class
57
+ # to return the metadata of the example that it exists for. This
58
+ # is necessary for shared example group inclusion to work properly
59
+ # with singleton example groups.
31
60
  # @private
32
- def self.register
33
- world.register(self)
61
+ def self.with_replaced_metadata(meta)
62
+ orig_metadata = metadata
63
+ @metadata = meta
64
+ yield
65
+ ensure
66
+ @metadata = orig_metadata
34
67
  end
35
68
 
36
- class << self
37
- # @private
38
- def self.delegate_to_metadata(*names)
39
- names.each do |name|
40
- define_method name do
41
- metadata[:example_group][name]
42
- end
43
- end
69
+ # @private
70
+ # @return [Metadata] belonging to the parent of a nested {ExampleGroup}
71
+ def self.superclass_metadata
72
+ @superclass_metadata ||= superclass.respond_to?(:metadata) ? superclass.metadata : nil
73
+ end
74
+
75
+ # @private
76
+ def self.delegate_to_metadata(*names)
77
+ names.each do |name|
78
+ idempotently_define_singleton_method(name) { metadata.fetch(name) }
44
79
  end
80
+ end
45
81
 
46
- delegate_to_metadata :description, :described_class, :file_path
47
- alias_method :display_name, :description
48
- # @private
49
- alias_method :describes, :described_class
82
+ delegate_to_metadata :described_class, :file_path, :location
50
83
 
51
- # @private
52
- # @macro [attach] define_example_method
53
- # @param [String] name
54
- # @param [Hash] extra_options
55
- # @param [Block] implementation
56
- def self.define_example_method(name, extra_options={})
57
- module_eval(<<-END_RUBY, __FILE__, __LINE__)
58
- def #{name}(desc=nil, *args, &block)
59
- options = build_metadata_hash_from(args)
60
- options.update(:pending => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
61
- options.update(#{extra_options.inspect})
62
- examples << RSpec::Core::Example.new(self, desc, options, block)
63
- examples.last
64
- end
65
- END_RUBY
66
- end
84
+ # @return [String] the current example group description
85
+ def self.description
86
+ description = metadata[:description]
87
+ RSpec.configuration.format_docstrings_block.call(description)
88
+ end
67
89
 
68
- # Defines an example within a group.
69
- define_example_method :example
70
- # Defines an example within a group.
71
- #
72
- # @see example
73
- define_example_method :it
74
- # Defines an example within a group.
75
- # This is here primarily for backward compatibility with early versions
76
- # of RSpec which used `context` and `specify` instead of `describe` and
77
- # `it`.
78
- define_example_method :specify
79
-
80
- # Shortcut to define an example with `:focus` => true
81
- define_example_method :focus, :focused => true, :focus => true
82
- # Shortcut to define an example with `:focus` => true
83
- define_example_method :focused, :focused => true, :focus => true
84
-
85
- # Shortcut to define an example with :pending => true
86
- define_example_method :pending, :pending => true
87
- # Shortcut to define an example with :pending => 'Temporarily disabled with xexample'
88
- define_example_method :xexample, :pending => 'Temporarily disabled with xexample'
89
- # Shortcut to define an example with :pending => 'Temporarily disabled with xit'
90
- define_example_method :xit, :pending => 'Temporarily disabled with xit'
91
- # Shortcut to define an example with :pending => 'Temporarily disabled with xspecify'
92
- define_example_method :xspecify, :pending => 'Temporarily disabled with xspecify'
93
-
94
- # Works like `alias_method :name, :example` with the added benefit of
95
- # assigning default metadata to the generated example.
96
- #
97
- # @note Use with caution. This extends the language used in your
98
- # specs, but does not add any additional documentation. We use this
99
- # in rspec to define methods like `focus` and `xit`, but we also add
100
- # docs for those methods.
101
- def alias_example_to name, extra={}
102
- (class << self; self; end).define_example_method name, extra
90
+ # Returns the class or module passed to the `describe` method (or alias).
91
+ # Returns nil if the subject is not a class or module.
92
+ # @example
93
+ # RSpec.describe Thing do
94
+ # it "does something" do
95
+ # described_class == Thing
96
+ # end
97
+ # end
98
+ #
99
+ def described_class
100
+ self.class.described_class
101
+ end
102
+
103
+ # @!endgroup
104
+
105
+ # @!group Defining Examples
106
+
107
+ # @private
108
+ # @macro [attach] define_example_method
109
+ # @!scope class
110
+ # @method $1
111
+ # @overload $1
112
+ # @overload $1(&example_implementation)
113
+ # @param example_implementation [Block] The implementation of the example.
114
+ # @overload $1(doc_string, *metadata)
115
+ # @param doc_string [String] The example's doc string.
116
+ # @param metadata [Array<Symbol>, Hash] Metadata for the example.
117
+ # Symbols will be transformed into hash entries with `true` values.
118
+ # @overload $1(doc_string, *metadata, &example_implementation)
119
+ # @param doc_string [String] The example's doc string.
120
+ # @param metadata [Array<Symbol>, Hash] Metadata for the example.
121
+ # Symbols will be transformed into hash entries with `true` values.
122
+ # @param example_implementation [Block] The implementation of the example.
123
+ # @yield [Example] the example object
124
+ # @example
125
+ # $1 do
126
+ # end
127
+ #
128
+ # $1 "does something" do
129
+ # end
130
+ #
131
+ # $1 "does something", :slow, :uses_js do
132
+ # end
133
+ #
134
+ # $1 "does something", :with => 'additional metadata' do
135
+ # end
136
+ #
137
+ # $1 "does something" do |ex|
138
+ # # ex is the Example object that contains metadata about the example
139
+ # end
140
+ #
141
+ # @example
142
+ # $1 "does something", :slow, :load_factor => 100 do
143
+ # end
144
+ #
145
+ def self.define_example_method(name, extra_options={})
146
+ idempotently_define_singleton_method(name) do |*all_args, &block|
147
+ desc, *args = *all_args
148
+
149
+ options = Metadata.build_hash_from(args)
150
+ options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
151
+ options.update(extra_options)
152
+
153
+ RSpec::Core::Example.new(self, desc, options, block)
103
154
  end
155
+ end
104
156
 
105
- # @private
106
- # @macro [attach] define_nested_shared_group_method
107
- #
108
- # @see SharedExampleGroup
109
- def self.define_nested_shared_group_method(new_name, report_label=nil)
110
- module_eval(<<-END_RUBY, __FILE__, __LINE__)
111
- def #{new_name}(name, *args, &customization_block)
112
- group = describe("#{report_label || "it should behave like"} \#{name}") do
113
- find_and_eval_shared("examples", name, *args, &customization_block)
157
+ # Defines an example within a group.
158
+ define_example_method :example
159
+ # Defines an example within a group.
160
+ # This is the primary API to define a code example.
161
+ define_example_method :it
162
+ # Defines an example within a group.
163
+ # Useful for when your docstring does not read well off of `it`.
164
+ # @example
165
+ # RSpec.describe MyClass do
166
+ # specify "#do_something is deprecated" do
167
+ # # ...
168
+ # end
169
+ # end
170
+ define_example_method :specify
171
+
172
+ # Shortcut to define an example with `:focus => true`.
173
+ # @see example
174
+ define_example_method :focus, :focus => true
175
+ # Shortcut to define an example with `:focus => true`.
176
+ # @see example
177
+ define_example_method :fexample, :focus => true
178
+ # Shortcut to define an example with `:focus => true`.
179
+ # @see example
180
+ define_example_method :fit, :focus => true
181
+ # Shortcut to define an example with `:focus => true`.
182
+ # @see example
183
+ define_example_method :fspecify, :focus => true
184
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`.
185
+ # @see example
186
+ define_example_method :xexample, :skip => 'Temporarily skipped with xexample'
187
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`.
188
+ # @see example
189
+ define_example_method :xit, :skip => 'Temporarily skipped with xit'
190
+ # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`.
191
+ # @see example
192
+ define_example_method :xspecify, :skip => 'Temporarily skipped with xspecify'
193
+ # Shortcut to define an example with `:skip => true`
194
+ # @see example
195
+ define_example_method :skip, :skip => true
196
+ # Shortcut to define an example with `:pending => true`
197
+ # @see example
198
+ define_example_method :pending, :pending => true
199
+
200
+ # @!endgroup
201
+
202
+ # @!group Defining Example Groups
203
+
204
+ # @private
205
+ # @macro [attach] define_example_group_method
206
+ # @!scope class
207
+ # @overload $1
208
+ # @overload $1(&example_group_definition)
209
+ # @param example_group_definition [Block] The definition of the example group.
210
+ # @overload $1(doc_string, *metadata, &example_implementation)
211
+ # @param doc_string [String] The group's doc string.
212
+ # @param metadata [Array<Symbol>, Hash] Metadata for the group.
213
+ # Symbols will be transformed into hash entries with `true` values.
214
+ # @param example_group_definition [Block] The definition of the example group.
215
+ #
216
+ # Generates a subclass of this example group which inherits
217
+ # everything except the examples themselves.
218
+ #
219
+ # @example
220
+ #
221
+ # RSpec.describe "something" do # << This describe method is defined in
222
+ # # << RSpec::Core::DSL, included in the
223
+ # # << global namespace (optional)
224
+ # before do
225
+ # do_something_before
226
+ # end
227
+ #
228
+ # before(:example, :clean_env) do
229
+ # env.clear!
230
+ # end
231
+ #
232
+ # let(:thing) { Thing.new }
233
+ #
234
+ # $1 "attribute (of something)" do
235
+ # # examples in the group get the before hook
236
+ # # declared above, and can access `thing`
237
+ # end
238
+ #
239
+ # $1 "needs additional setup", :clean_env, :implementation => JSON do
240
+ # # specifies that hooks with matching metadata
241
+ # # should be be run additionally
242
+ # end
243
+ # end
244
+ #
245
+ # @see DSL#describe
246
+ def self.define_example_group_method(name, metadata={})
247
+ idempotently_define_singleton_method(name) do |*args, &example_group_block|
248
+ thread_data = RSpec::Support.thread_local_data
249
+ top_level = self == ExampleGroup
250
+
251
+ registration_collection =
252
+ if top_level
253
+ if thread_data[:in_example_group]
254
+ raise "Creating an isolated context from within a context is " \
255
+ "not allowed. Change `RSpec.#{name}` to `#{name}` or " \
256
+ "move this to a top-level scope."
114
257
  end
115
- group.metadata[:shared_group_name] = name
116
- group
258
+
259
+ thread_data[:in_example_group] = true
260
+ RSpec.world.example_groups
261
+ else
262
+ children
117
263
  end
118
- END_RUBY
264
+
265
+ begin
266
+ description = args.shift
267
+ combined_metadata = metadata.dup
268
+ combined_metadata.merge!(args.pop) if args.last.is_a? Hash
269
+ args << combined_metadata
270
+
271
+ subclass(self, description, args, registration_collection, &example_group_block)
272
+ ensure
273
+ thread_data.delete(:in_example_group) if top_level
274
+ end
119
275
  end
120
276
 
121
- # Generates a nested example group and includes the shared content
122
- # mapped to `name` in the nested group.
123
- define_nested_shared_group_method :it_behaves_like, "behaves like"
124
- # Generates a nested example group and includes the shared content
125
- # mapped to `name` in the nested group.
126
- define_nested_shared_group_method :it_should_behave_like
127
-
128
- # Works like `alias_method :name, :it_behaves_like` with the added
129
- # benefit of assigning default metadata to the generated example.
130
- #
131
- # @note Use with caution. This extends the language used in your
132
- # specs, but does not add any additional documentation. We use this
133
- # in rspec to define `it_should_behave_like` (for backward
134
- # compatibility), but we also add docs for that method.
135
- def alias_it_behaves_like_to name, *args, &block
136
- (class << self; self; end).define_nested_shared_group_method name, *args, &block
277
+ RSpec::Core::DSL.expose_example_group_alias(name)
278
+ end
279
+
280
+ define_example_group_method :example_group
281
+
282
+ # An alias of `example_group`. Generally used when grouping examples by a
283
+ # thing you are describing (e.g. an object, class or method).
284
+ # @see example_group
285
+ define_example_group_method :describe
286
+
287
+ # An alias of `example_group`. Generally used when grouping examples
288
+ # contextually (e.g. "with xyz", "when xyz" or "if xyz").
289
+ # @see example_group
290
+ define_example_group_method :context
291
+
292
+ # Shortcut to temporarily make an example group skipped.
293
+ # @see example_group
294
+ define_example_group_method :xdescribe, :skip => "Temporarily skipped with xdescribe"
295
+
296
+ # Shortcut to temporarily make an example group skipped.
297
+ # @see example_group
298
+ define_example_group_method :xcontext, :skip => "Temporarily skipped with xcontext"
299
+
300
+ # Shortcut to define an example group with `:focus => true`.
301
+ # @see example_group
302
+ define_example_group_method :fdescribe, :focus => true
303
+
304
+ # Shortcut to define an example group with `:focus => true`.
305
+ # @see example_group
306
+ define_example_group_method :fcontext, :focus => true
307
+
308
+ # @!endgroup
309
+
310
+ # @!group Including Shared Example Groups
311
+
312
+ # @private
313
+ # @macro [attach] define_nested_shared_group_method
314
+ # @!scope class
315
+ #
316
+ # @see SharedExampleGroup
317
+ def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
318
+ idempotently_define_singleton_method(new_name) do |name, *args, &customization_block|
319
+ # Pass :caller so the :location metadata is set properly.
320
+ # Otherwise, it'll be set to the next line because that's
321
+ # the block's source_location.
322
+ group = example_group("#{report_label} #{name}", :caller => (the_caller = caller)) do
323
+ find_and_eval_shared("examples", name, the_caller.first, *args, &customization_block)
324
+ end
325
+ group.metadata[:shared_group_name] = name
326
+ group
137
327
  end
138
328
  end
139
329
 
330
+ # Generates a nested example group and includes the shared content
331
+ # mapped to `name` in the nested group.
332
+ define_nested_shared_group_method :it_behaves_like, "behaves like"
333
+ # Generates a nested example group and includes the shared content
334
+ # mapped to `name` in the nested group.
335
+ define_nested_shared_group_method :it_should_behave_like
336
+
140
337
  # Includes shared content mapped to `name` directly in the group in which
141
338
  # it is declared, as opposed to `it_behaves_like`, which creates a nested
142
- # group. If given a block, that block is also eval'd in the current context.
339
+ # group. If given a block, that block is also eval'd in the current
340
+ # context.
143
341
  #
144
342
  # @see SharedExampleGroup
145
343
  def self.include_context(name, *args, &block)
146
- find_and_eval_shared("context", name, *args, &block)
344
+ find_and_eval_shared("context", name, caller.first, *args, &block)
147
345
  end
148
346
 
149
347
  # Includes shared content mapped to `name` directly in the group in which
150
348
  # it is declared, as opposed to `it_behaves_like`, which creates a nested
151
- # group. If given a block, that block is also eval'd in the current context.
349
+ # group. If given a block, that block is also eval'd in the current
350
+ # context.
152
351
  #
153
352
  # @see SharedExampleGroup
154
353
  def self.include_examples(name, *args, &block)
155
- find_and_eval_shared("examples", name, *args, &block)
354
+ find_and_eval_shared("examples", name, caller.first, *args, &block)
156
355
  end
157
356
 
357
+ # Clear memoized values when adding/removing examples
158
358
  # @private
159
- def self.find_and_eval_shared(label, name, *args, &customization_block)
160
- raise ArgumentError, "Could not find shared #{label} #{name.inspect}" unless
161
- shared_block = world.shared_example_groups[name]
359
+ def self.reset_memoized
360
+ @descendant_filtered_examples = nil
361
+ @_descendants = nil
362
+ @parent_groups = nil
363
+ @declaration_locations = nil
364
+ end
365
+
366
+ # Adds an example to the example group
367
+ def self.add_example(example)
368
+ reset_memoized
369
+ examples << example
370
+ end
162
371
 
163
- module_eval_with_args(*args, &shared_block)
164
- module_eval(&customization_block) if customization_block
372
+ # Removes an example from the example group
373
+ def self.remove_example(example)
374
+ reset_memoized
375
+ examples.delete example
165
376
  end
166
377
 
167
378
  # @private
168
- def self.examples
169
- @examples ||= []
379
+ def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block)
380
+ shared_module = RSpec.world.shared_example_group_registry.find(parent_groups, name)
381
+
382
+ unless shared_module
383
+ raise ArgumentError, "Could not find shared #{label} #{name.inspect}"
384
+ end
385
+
386
+ shared_module.include_in(
387
+ self, Metadata.relative_path(inclusion_location),
388
+ args, customization_block
389
+ )
170
390
  end
171
391
 
392
+ # @!endgroup
393
+
172
394
  # @private
173
- def self.filtered_examples
174
- world.filtered_examples[self]
395
+ def self.subclass(parent, description, args, registration_collection, &example_group_block)
396
+ subclass = Class.new(parent)
397
+ subclass.set_it_up(description, args, registration_collection, &example_group_block)
398
+ subclass.module_exec(&example_group_block) if example_group_block
399
+
400
+ # The LetDefinitions module must be included _after_ other modules
401
+ # to ensure that it takes precedence when there are name collisions.
402
+ # Thus, we delay including it until after the example group block
403
+ # has been eval'd.
404
+ MemoizedHelpers.define_helpers_on(subclass)
405
+
406
+ subclass
175
407
  end
176
408
 
177
409
  # @private
178
- def self.descendant_filtered_examples
179
- @descendant_filtered_examples ||= filtered_examples + children.inject([]){|l,c| l + c.descendant_filtered_examples}
410
+ def self.set_it_up(description, args, registration_collection, &example_group_block)
411
+ # Ruby 1.9 has a bug that can lead to infinite recursion and a
412
+ # SystemStackError if you include a module in a superclass after
413
+ # including it in a subclass: https://gist.github.com/845896
414
+ # To prevent this, we must include any modules in
415
+ # RSpec::Core::ExampleGroup before users create example groups and have
416
+ # a chance to include the same module in a subclass of
417
+ # RSpec::Core::ExampleGroup. So we need to configure example groups
418
+ # here.
419
+ ensure_example_groups_are_configured
420
+
421
+ # Register the example with the group before creating the metadata hash.
422
+ # This is necessary since creating the metadata hash triggers
423
+ # `when_first_matching_example_defined` callbacks, in which users can
424
+ # load RSpec support code which defines hooks. For that to work, the
425
+ # examples and example groups must be registered at the time the
426
+ # support code is called or be defined afterwards.
427
+ # Begin defined beforehand but registered afterwards causes hooks to
428
+ # not be applied where they should.
429
+ registration_collection << self
430
+
431
+ @user_metadata = Metadata.build_hash_from(args)
432
+
433
+ @metadata = Metadata::ExampleGroupHash.create(
434
+ superclass_metadata, @user_metadata,
435
+ superclass.method(:next_runnable_index_for),
436
+ description, *args, &example_group_block
437
+ )
438
+
439
+ config = RSpec.configuration
440
+ config.apply_derived_metadata_to(@metadata)
441
+
442
+ ExampleGroups.assign_const(self)
443
+
444
+ @currently_executing_a_context_hook = false
445
+
446
+ config.configure_group(self)
180
447
  end
181
448
 
182
- # The [Metadata](Metadata) object associated with this group.
183
- # @see Metadata
184
- def self.metadata
185
- @metadata if defined?(@metadata)
449
+ # @private
450
+ def self.examples
451
+ @examples ||= []
186
452
  end
187
453
 
188
454
  # @private
189
- # @return [Metadata] belonging to the parent of a nested {ExampleGroup}
190
- def self.superclass_metadata
191
- @superclass_metadata ||= self.superclass.respond_to?(:metadata) ? self.superclass.metadata : nil
455
+ def self.filtered_examples
456
+ RSpec.world.filtered_examples[self]
192
457
  end
193
458
 
194
- # Generates a subclass of this example group which inherits
195
- # everything except the examples themselves.
196
- #
197
- # ## Examples
198
- #
199
- # describe "something" do # << This describe method is defined in
200
- # # << RSpec::Core::DSL, included in the
201
- # # << global namespace
202
- # before do
203
- # do_something_before
204
- # end
205
- #
206
- # let(:thing) { Thing.new }
207
- #
208
- # describe "attribute (of something)" do
209
- # # examples in the group get the before hook
210
- # # declared above, and can access `thing`
211
- # end
212
- # end
213
- #
214
- # @see DSL#describe
215
- def self.describe(*args, &example_group_block)
216
- @_subclass_count ||= 0
217
- @_subclass_count += 1
218
- args << {} unless args.last.is_a?(Hash)
219
- args.last.update(:example_group_block => example_group_block)
220
-
221
- # TODO 2010-05-05: Because we don't know if const_set is thread-safe
222
- child = const_set(
223
- "Nested_#{@_subclass_count}",
224
- subclass(self, args, &example_group_block)
225
- )
226
- children << child
227
- child
459
+ # @private
460
+ def self.descendant_filtered_examples
461
+ @descendant_filtered_examples ||= filtered_examples +
462
+ FlatMap.flat_map(children, &:descendant_filtered_examples)
228
463
  end
229
464
 
230
- class << self
231
- alias_method :context, :describe
465
+ # @private
466
+ def self.children
467
+ @children ||= []
232
468
  end
233
469
 
234
470
  # @private
235
- def self.subclass(parent, args, &example_group_block)
236
- subclass = Class.new(parent)
237
- subclass.set_it_up(*args)
238
- subclass.module_eval(&example_group_block) if example_group_block
239
- subclass
471
+ # Traverses the tree of groups, starting with `self`, then the children, recursively.
472
+ # Halts the traversal of a branch of the tree as soon as the passed block returns true.
473
+ # Note that siblings groups and their sub-trees will continue to be explored.
474
+ # This is intended to make it easy to find the top-most group that satisfies some
475
+ # condition.
476
+ def self.traverse_tree_until(&block)
477
+ return if yield self
478
+
479
+ children.each do |child|
480
+ child.traverse_tree_until(&block)
481
+ end
240
482
  end
241
483
 
242
484
  # @private
243
- def self.children
244
- @children ||= [].extend(Extensions::Ordered)
485
+ def self.next_runnable_index_for(file)
486
+ if self == ExampleGroup
487
+ # We add 1 so the ids start at 1 instead of 0. This is
488
+ # necessary for this branch (but not for the other one)
489
+ # because we register examples and groups with the
490
+ # `children` and `examples` collection BEFORE this
491
+ # method is called as part of metadata hash creation,
492
+ # but the example group is recorded with
493
+ # `RSpec.world.example_group_counts_by_spec_file` AFTER
494
+ # the metadata hash is created and the group is returned
495
+ # to the caller.
496
+ RSpec.world.num_example_groups_defined_in(file) + 1
497
+ else
498
+ children.count + examples.count
499
+ end
245
500
  end
246
501
 
247
502
  # @private
248
503
  def self.descendants
249
- @_descendants ||= [self] + children.inject([]) {|list, c| list + c.descendants}
504
+ @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants)
250
505
  end
251
506
 
252
- # @private
253
- def self.ancestors
254
- @_ancestors ||= super().select {|a| a < RSpec::Core::ExampleGroup}
507
+ ## @private
508
+ def self.parent_groups
509
+ @parent_groups ||= ancestors.select { |a| a < RSpec::Core::ExampleGroup }
255
510
  end
256
511
 
257
512
  # @private
258
513
  def self.top_level?
259
- @top_level ||= superclass == ExampleGroup
514
+ superclass == ExampleGroup
260
515
  end
261
516
 
262
517
  # @private
@@ -264,203 +519,387 @@ module RSpec
264
519
  unless defined?(@@example_groups_configured)
265
520
  RSpec.configuration.configure_mock_framework
266
521
  RSpec.configuration.configure_expectation_framework
522
+ # rubocop:disable Style/ClassVars
267
523
  @@example_groups_configured = true
524
+ # rubocop:enable Style/ClassVars
268
525
  end
269
526
  end
270
527
 
271
528
  # @private
272
- def self.set_it_up(*args)
273
- # Ruby 1.9 has a bug that can lead to infinite recursion and a
274
- # SystemStackError if you include a module in a superclass after
275
- # including it in a subclass: https://gist.github.com/845896
276
- # To prevent this, we must include any modules in RSpec::Core::ExampleGroup
277
- # before users create example groups and have a chance to include
278
- # the same module in a subclass of RSpec::Core::ExampleGroup.
279
- # So we need to configure example groups here.
280
- ensure_example_groups_are_configured
281
-
282
- symbol_description = args.shift if args.first.is_a?(Symbol)
283
- args << build_metadata_hash_from(args)
284
- args.unshift(symbol_description) if symbol_description
285
- @metadata = RSpec::Core::Metadata.new(superclass_metadata).process(*args)
286
- world.configure_group(self)
287
- hooks.register_globals(self, RSpec.configuration.hooks)
529
+ def self.before_context_ivars
530
+ @before_context_ivars ||= {}
288
531
  end
289
532
 
290
533
  # @private
291
- def self.before_all_ivars
292
- @before_all_ivars ||= {}
293
- end
294
-
295
- # @private
296
- def self.store_before_all_ivars(example_group_instance)
297
- return if example_group_instance.instance_variables.empty?
298
- example_group_instance.instance_variables.each { |ivar|
299
- before_all_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
300
- }
534
+ def self.store_before_context_ivars(example_group_instance)
535
+ each_instance_variable_for_example(example_group_instance) do |ivar|
536
+ before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
537
+ end
301
538
  end
302
539
 
303
- # @private
304
- def self.assign_before_all_ivars(ivars, example_group_instance)
305
- ivars.each { |ivar, val| example_group_instance.instance_variable_set(ivar, val) }
540
+ # Returns true if a `before(:context)` or `after(:context)`
541
+ # hook is currently executing.
542
+ def self.currently_executing_a_context_hook?
543
+ @currently_executing_a_context_hook
306
544
  end
307
545
 
308
546
  # @private
309
- def self.run_before_all_hooks(example_group_instance)
310
- return if descendant_filtered_examples.empty?
311
- assign_before_all_ivars(superclass.before_all_ivars, example_group_instance)
312
- run_hook(:before, :all, example_group_instance)
313
- store_before_all_ivars(example_group_instance)
314
- end
547
+ def self.run_before_context_hooks(example_group_instance)
548
+ set_ivars(example_group_instance, superclass_before_context_ivars)
315
549
 
316
- # @private
317
- def self.run_around_each_hooks(example, initial_procsy)
318
- run_hook(:around, :each, example, initial_procsy)
319
- end
550
+ @currently_executing_a_context_hook = true
320
551
 
321
- # @private
322
- def self.run_before_each_hooks(example)
323
- run_hook(:before, :each, example)
552
+ ContextHookMemoized::Before.isolate_for_context_hook(example_group_instance) do
553
+ hooks.run(:before, :context, example_group_instance)
554
+ end
555
+ ensure
556
+ store_before_context_ivars(example_group_instance)
557
+ @currently_executing_a_context_hook = false
324
558
  end
325
559
 
326
- # @private
327
- def self.run_after_each_hooks(example)
328
- run_hook(:after, :each, example)
560
+ if RUBY_VERSION.to_f >= 1.9
561
+ # @private
562
+ def self.superclass_before_context_ivars
563
+ superclass.before_context_ivars
564
+ end
565
+ else # 1.8.7
566
+ # :nocov:
567
+ # @private
568
+ def self.superclass_before_context_ivars
569
+ if superclass.respond_to?(:before_context_ivars)
570
+ superclass.before_context_ivars
571
+ else
572
+ # `self` must be the singleton class of an ExampleGroup instance.
573
+ # On 1.8.7, the superclass of a singleton class of an instance of A
574
+ # is A's singleton class. On 1.9+, it's A. On 1.8.7, the first ancestor
575
+ # is A, so we can mirror 1.8.7's behavior here. Note that we have to
576
+ # search for the first that responds to `before_context_ivars`
577
+ # in case a module has been included in the singleton class.
578
+ ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars
579
+ end
580
+ end
581
+ # :nocov:
329
582
  end
330
583
 
331
584
  # @private
332
- def self.run_after_all_hooks(example_group_instance)
333
- return if descendant_filtered_examples.empty?
334
- assign_before_all_ivars(before_all_ivars, example_group_instance)
335
-
336
- begin
337
- run_hook(:after, :all, example_group_instance)
338
- rescue => e
339
- # TODO: come up with a better solution for this.
340
- RSpec.configuration.reporter.message <<-EOS
585
+ def self.run_after_context_hooks(example_group_instance)
586
+ set_ivars(example_group_instance, before_context_ivars)
341
587
 
342
- An error occurred in an after(:all) hook.
343
- #{e.class}: #{e.message}
344
- occurred at #{e.backtrace.first}
588
+ @currently_executing_a_context_hook = true
345
589
 
346
- EOS
590
+ ContextHookMemoized::After.isolate_for_context_hook(example_group_instance) do
591
+ hooks.run(:after, :context, example_group_instance)
347
592
  end
593
+ ensure
594
+ before_context_ivars.clear
595
+ @currently_executing_a_context_hook = false
348
596
  end
349
597
 
350
- # Runs all the examples in this group
351
- def self.run(reporter)
352
- if RSpec.wants_to_quit
353
- RSpec.clear_remaining_example_groups if top_level?
354
- return
355
- end
598
+ # Runs all the examples in this group.
599
+ def self.run(reporter=RSpec::Core::NullReporter)
600
+ return if RSpec.world.wants_to_quit
356
601
  reporter.example_group_started(self)
357
602
 
603
+ should_run_context_hooks = descendant_filtered_examples.any?
358
604
  begin
359
- run_before_all_hooks(new)
605
+ RSpec.current_scope = :before_context_hook
606
+ run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks
360
607
  result_for_this_group = run_examples(reporter)
361
- results_for_descendants = children.ordered.map {|child| child.run(reporter)}.all?
608
+ results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
362
609
  result_for_this_group && results_for_descendants
363
- rescue Exception => ex
364
- fail_filtered_examples(ex, reporter)
610
+ rescue Pending::SkipDeclaredInExample => ex
611
+ for_filtered_examples(reporter) { |example| example.skip_with_exception(reporter, ex) }
612
+ true
613
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
614
+ for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) }
615
+ RSpec.world.wants_to_quit = true if reporter.fail_fast_limit_met?
616
+ false
365
617
  ensure
366
- run_after_all_hooks(new)
367
- before_all_ivars.clear
618
+ RSpec.current_scope = :after_context_hook
619
+ run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
368
620
  reporter.example_group_finished(self)
369
621
  end
370
622
  end
371
623
 
624
+ # @private
625
+ def self.ordering_strategy
626
+ order = metadata.fetch(:order, :global)
627
+ registry = RSpec.configuration.ordering_registry
628
+
629
+ registry.fetch(order) do
630
+ warn <<-WARNING.gsub(/^ +\|/, '')
631
+ |WARNING: Ignoring unknown ordering specified using `:order => #{order.inspect}` metadata.
632
+ | Falling back to configured global ordering.
633
+ | Unrecognized ordering specified at: #{location}
634
+ WARNING
635
+
636
+ registry.fetch(:global)
637
+ end
638
+ end
639
+
372
640
  # @private
373
641
  def self.run_examples(reporter)
374
- filtered_examples.ordered.map do |example|
375
- next if RSpec.wants_to_quit
376
- instance = new
377
- set_ivars(instance, before_all_ivars)
642
+ ordering_strategy.order(filtered_examples).map do |example|
643
+ next if RSpec.world.wants_to_quit
644
+ instance = new(example.inspect_output)
645
+ set_ivars(instance, before_context_ivars)
378
646
  succeeded = example.run(instance, reporter)
379
- RSpec.wants_to_quit = true if fail_fast? && !succeeded
647
+ if !succeeded && reporter.fail_fast_limit_met?
648
+ RSpec.world.wants_to_quit = true
649
+ end
380
650
  succeeded
381
651
  end.all?
382
652
  end
383
653
 
384
654
  # @private
385
- def self.fail_filtered_examples(exception, reporter)
386
- filtered_examples.each { |example| example.fail_with_exception(reporter, exception) }
655
+ def self.for_filtered_examples(reporter, &block)
656
+ filtered_examples.each(&block)
387
657
 
388
658
  children.each do |child|
389
659
  reporter.example_group_started(child)
390
- child.fail_filtered_examples(exception, reporter)
660
+ child.for_filtered_examples(reporter, &block)
391
661
  reporter.example_group_finished(child)
392
662
  end
393
663
  false
394
664
  end
395
665
 
396
666
  # @private
397
- def self.fail_fast?
398
- RSpec.configuration.fail_fast?
667
+ def self.declaration_locations
668
+ @declaration_locations ||= [Metadata.location_tuple_from(metadata)] +
669
+ examples.map { |e| Metadata.location_tuple_from(e.metadata) } +
670
+ FlatMap.flat_map(children, &:declaration_locations)
671
+ end
672
+
673
+ # @return [String] the unique id of this example group. Pass
674
+ # this at the command line to re-run this exact example group.
675
+ def self.id
676
+ Metadata.id_from(metadata)
399
677
  end
400
678
 
401
679
  # @private
402
- def self.any_apply?(filters)
403
- metadata.any_apply?(filters)
680
+ def self.top_level_description
681
+ parent_groups.last.description
404
682
  end
405
683
 
406
684
  # @private
407
- def self.all_apply?(filters)
408
- metadata.all_apply?(filters)
685
+ def self.set_ivars(instance, ivars)
686
+ ivars.each { |name, value| instance.instance_variable_set(name, value) }
687
+ end
688
+
689
+ if RUBY_VERSION.to_f < 1.9
690
+ # :nocov:
691
+ # @private
692
+ INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze
693
+ # :nocov:
694
+ else
695
+ # @private
696
+ INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output
409
697
  end
410
698
 
411
699
  # @private
412
- def self.declaration_line_numbers
413
- @declaration_line_numbers ||= [metadata[:example_group][:line_number]] +
414
- examples.collect {|e| e.metadata[:line_number]} +
415
- children.inject([]) {|l,c| l + c.declaration_line_numbers}
700
+ def self.each_instance_variable_for_example(group)
701
+ group.instance_variables.each do |ivar|
702
+ yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE
703
+ end
416
704
  end
417
705
 
418
706
  # @private
419
- def self.top_level_description
420
- ancestors.last.description
707
+ def initialize(inspect_output=nil)
708
+ @__inspect_output = inspect_output || '(no description provided)'
709
+ super() # no args get passed
421
710
  end
422
711
 
423
712
  # @private
424
- def self.set_ivars(instance, ivars)
425
- ivars.each {|name, value| instance.instance_variable_set(name, value)}
713
+ def inspect
714
+ "#<#{self.class} #{@__inspect_output}>"
715
+ end
716
+
717
+ unless method_defined?(:singleton_class) # for 1.8.7
718
+ # :nocov:
719
+ # @private
720
+ def singleton_class
721
+ class << self; self; end
722
+ end
723
+ # :nocov:
426
724
  end
427
725
 
428
- # @attr_reader
429
- # Returns the {Example} object that wraps this instance of
430
- # `ExampleGroup`
431
- attr_accessor :example
726
+ # @private
727
+ def self.update_inherited_metadata(updates)
728
+ metadata.update(updates) do |key, existing_group_value, new_inherited_value|
729
+ @user_metadata.key?(key) ? existing_group_value : new_inherited_value
730
+ end
432
731
 
433
- # @deprecated use {ExampleGroup#example}
434
- def running_example
435
- RSpec.deprecate("running_example", "example")
436
- example
732
+ RSpec.configuration.configure_group(self)
733
+ examples.each { |ex| ex.update_inherited_metadata(updates) }
734
+ children.each { |group| group.update_inherited_metadata(updates) }
437
735
  end
438
736
 
439
- # Returns the class or module passed to the `describe` method (or alias).
440
- # Returns nil if the subject is not a class or module.
441
- # @example
442
- # describe Thing do
443
- # it "does something" do
444
- # described_class == Thing
445
- # end
446
- # end
447
- #
448
- #
449
- def described_class
450
- self.class.described_class
737
+ # Raised when an RSpec API is called in the wrong scope, such as `before`
738
+ # being called from within an example rather than from within an example
739
+ # group block.
740
+ WrongScopeError = Class.new(NoMethodError)
741
+
742
+ def self.method_missing(name, *args)
743
+ if method_defined?(name)
744
+ raise WrongScopeError,
745
+ "`#{name}` is not available on an example group (e.g. a " \
746
+ "`describe` or `context` block). It is only available from " \
747
+ "within individual examples (e.g. `it` blocks) or from " \
748
+ "constructs that run in the scope of an example (e.g. " \
749
+ "`before`, `let`, etc)."
750
+ end
751
+
752
+ super
451
753
  end
754
+ private_class_method :method_missing
755
+
756
+ private
757
+
758
+ def method_missing(name, *args)
759
+ if self.class.respond_to?(name)
760
+ raise WrongScopeError,
761
+ "`#{name}` is not available from within an example (e.g. an " \
762
+ "`it` block) or from constructs that run in the scope of an " \
763
+ "example (e.g. `before`, `let`, etc). It is only available " \
764
+ "on an example group (e.g. a `describe` or `context` block)."
765
+ end
766
+
767
+ super(name, *args)
768
+ end
769
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
770
+ end
771
+ # rubocop:enable Metrics/ClassLength
772
+
773
+ # @private
774
+ # Unnamed example group used by `SuiteHookContext`.
775
+ class AnonymousExampleGroup < ExampleGroup
776
+ def self.metadata
777
+ {}
778
+ end
779
+ end
780
+
781
+ # Contains information about the inclusion site of a shared example group.
782
+ class SharedExampleGroupInclusionStackFrame
783
+ # @return [String] the name of the shared example group
784
+ attr_reader :shared_group_name
785
+ # @return [String] the location where the shared example was included
786
+ attr_reader :inclusion_location
452
787
 
453
788
  # @private
454
- # instance_evals the block, capturing and reporting an exception if
455
- # raised
456
- def instance_eval_with_rescue(context = nil, &hook)
457
- begin
458
- instance_eval(&hook)
459
- rescue Exception => e
460
- raise unless example
461
- example.set_exception(e, context)
789
+ def initialize(shared_group_name, inclusion_location)
790
+ @shared_group_name = shared_group_name
791
+ @inclusion_location = inclusion_location
792
+ end
793
+
794
+ # @return [String] The {#inclusion_location}, formatted for display by a formatter.
795
+ def formatted_inclusion_location
796
+ @formatted_inclusion_location ||= begin
797
+ RSpec.configuration.backtrace_formatter.backtrace_line(
798
+ inclusion_location.sub(/(:\d+):in .+$/, '\1')
799
+ )
800
+ end
801
+ end
802
+
803
+ # @return [String] Description of this stack frame, in the form used by
804
+ # RSpec's built-in formatters.
805
+ def description
806
+ @description ||= "Shared Example Group: #{shared_group_name.inspect} " \
807
+ "called from #{formatted_inclusion_location}"
808
+ end
809
+
810
+ # @private
811
+ def self.current_backtrace
812
+ shared_example_group_inclusions.reverse
813
+ end
814
+
815
+ # @private
816
+ def self.with_frame(name, location)
817
+ current_stack = shared_example_group_inclusions
818
+ if current_stack.any? { |frame| frame.shared_group_name == name }
819
+ raise ArgumentError, "can't include shared examples recursively"
820
+ else
821
+ current_stack << new(name, location)
822
+ yield
823
+ end
824
+ ensure
825
+ current_stack.pop
826
+ end
827
+
828
+ # @private
829
+ def self.shared_example_group_inclusions
830
+ RSpec::Support.thread_local_data[:shared_example_group_inclusions] ||= []
831
+ end
832
+ end
833
+ end
834
+
835
+ # @private
836
+ #
837
+ # Namespace for the example group subclasses generated by top-level
838
+ # `describe`.
839
+ module ExampleGroups
840
+ extend Support::RecursiveConstMethods
841
+
842
+ def self.assign_const(group)
843
+ base_name = base_name_for(group)
844
+ const_scope = constant_scope_for(group)
845
+ name = disambiguate(base_name, const_scope)
846
+
847
+ const_scope.const_set(name, group)
848
+ end
849
+
850
+ def self.constant_scope_for(group)
851
+ const_scope = group.superclass
852
+ const_scope = self if const_scope == ::RSpec::Core::ExampleGroup
853
+ const_scope
854
+ end
855
+
856
+ def self.remove_all_constants
857
+ constants.each do |constant|
858
+ __send__(:remove_const, constant)
859
+ end
860
+ end
861
+
862
+ def self.base_name_for(group)
863
+ return "Anonymous".dup if group.description.empty?
864
+
865
+ # Convert to CamelCase.
866
+ name = ' ' + group.description
867
+ name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
868
+ match = ::Regexp.last_match[1]
869
+ match.upcase!
870
+ match
871
+ end
872
+
873
+ name.lstrip! # Remove leading whitespace
874
+ name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names
875
+
876
+ # Ruby requires first const letter to be A-Z. Use `Nested`
877
+ # as necessary to enforce that.
878
+ name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1'.freeze)
879
+
880
+ name
881
+ end
882
+
883
+ if RUBY_VERSION == '1.9.2'
884
+ # :nocov:
885
+ class << self
886
+ alias _base_name_for base_name_for
887
+ def base_name_for(group)
888
+ _base_name_for(group) + '_'
462
889
  end
463
890
  end
891
+ private_class_method :_base_name_for
892
+ # :nocov:
893
+ end
894
+
895
+ def self.disambiguate(name, const_scope)
896
+ return name unless const_defined_on?(const_scope, name)
897
+
898
+ # Add a trailing number if needed to disambiguate from an existing
899
+ # constant.
900
+ name << "_2"
901
+ name.next! while const_defined_on?(const_scope, name)
902
+ name
464
903
  end
465
904
  end
466
905
  end