rspec-core 3.0.0.beta2 → 3.0.0.rc1

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 (201) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.yardopts +1 -0
  5. data/Changelog.md +297 -57
  6. data/README.md +16 -13
  7. data/lib/rspec/core.rb +55 -84
  8. data/lib/rspec/core/backport_random.rb +35 -3
  9. data/lib/rspec/core/backtrace_formatter.rb +4 -13
  10. data/lib/rspec/core/configuration.rb +330 -114
  11. data/lib/rspec/core/configuration_options.rb +38 -22
  12. data/lib/rspec/core/drb.rb +111 -0
  13. data/lib/rspec/core/dsl.rb +8 -2
  14. data/lib/rspec/core/example.rb +203 -94
  15. data/lib/rspec/core/example_group.rb +344 -316
  16. data/lib/rspec/core/filter_manager.rb +135 -90
  17. data/lib/rspec/core/flat_map.rb +1 -0
  18. data/lib/rspec/core/formatters.rb +50 -14
  19. data/lib/rspec/core/formatters/base_formatter.rb +32 -138
  20. data/lib/rspec/core/formatters/base_text_formatter.rb +32 -253
  21. data/lib/rspec/core/formatters/console_codes.rb +65 -0
  22. data/lib/rspec/core/formatters/deprecation_formatter.rb +24 -15
  23. data/lib/rspec/core/formatters/documentation_formatter.rb +7 -10
  24. data/lib/rspec/core/formatters/helpers.rb +15 -9
  25. data/lib/rspec/core/formatters/html_formatter.rb +17 -16
  26. data/lib/rspec/core/formatters/html_printer.rb +1 -0
  27. data/lib/rspec/core/formatters/json_formatter.rb +18 -20
  28. data/lib/rspec/core/formatters/profile_formatter.rb +67 -0
  29. data/lib/rspec/core/formatters/progress_formatter.rb +6 -7
  30. data/lib/rspec/core/formatters/snippet_extractor.rb +8 -6
  31. data/lib/rspec/core/hooks.rb +131 -125
  32. data/lib/rspec/core/memoized_helpers.rb +31 -26
  33. data/lib/rspec/core/metadata.rb +277 -184
  34. data/lib/rspec/core/metadata_filter.rb +86 -0
  35. data/lib/rspec/core/minitest_assertions_adapter.rb +28 -0
  36. data/lib/rspec/core/mocking_adapters/flexmock.rb +1 -1
  37. data/lib/rspec/core/mocking_adapters/mocha.rb +1 -1
  38. data/lib/rspec/core/mocking_adapters/null.rb +1 -1
  39. data/lib/rspec/core/mocking_adapters/rr.rb +2 -1
  40. data/lib/rspec/core/mocking_adapters/rspec.rb +1 -1
  41. data/lib/rspec/core/notifications.rb +435 -24
  42. data/lib/rspec/core/option_parser.rb +16 -25
  43. data/lib/rspec/core/ordering.rb +3 -1
  44. data/lib/rspec/core/pending.rb +57 -33
  45. data/lib/rspec/core/project_initializer.rb +2 -0
  46. data/lib/rspec/core/project_initializer/spec_helper.rb +5 -4
  47. data/lib/rspec/core/rake_task.rb +45 -20
  48. data/lib/rspec/core/reporter.rb +50 -22
  49. data/lib/rspec/core/ruby_project.rb +1 -0
  50. data/lib/rspec/core/runner.rb +93 -39
  51. data/lib/rspec/core/shared_context.rb +7 -5
  52. data/lib/rspec/core/shared_example_group.rb +85 -77
  53. data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
  54. data/lib/rspec/core/version.rb +3 -1
  55. data/lib/rspec/core/warnings.rb +35 -17
  56. data/lib/rspec/core/world.rb +57 -5
  57. metadata +56 -369
  58. metadata.gz.sig +3 -3
  59. data/features/README.md +0 -13
  60. data/features/Upgrade.md +0 -352
  61. data/features/command_line/README.md +0 -25
  62. data/features/command_line/dry_run.feature +0 -29
  63. data/features/command_line/example_name_option.feature +0 -97
  64. data/features/command_line/exit_status.feature +0 -82
  65. data/features/command_line/fail_fast.feature +0 -26
  66. data/features/command_line/format_option.feature +0 -75
  67. data/features/command_line/init.feature +0 -57
  68. data/features/command_line/line_number_appended_to_path.feature +0 -140
  69. data/features/command_line/line_number_option.feature +0 -58
  70. data/features/command_line/order.feature +0 -25
  71. data/features/command_line/pattern_option.feature +0 -49
  72. data/features/command_line/rake_task.feature +0 -122
  73. data/features/command_line/randomization.feature +0 -63
  74. data/features/command_line/require_option.feature +0 -43
  75. data/features/command_line/ruby.feature +0 -23
  76. data/features/command_line/tag.feature +0 -98
  77. data/features/command_line/warnings_option.feature +0 -29
  78. data/features/configuration/alias_example_to.feature +0 -39
  79. data/features/configuration/backtrace_exclusion_patterns.feature +0 -105
  80. data/features/configuration/custom_settings.feature +0 -84
  81. data/features/configuration/default_path.feature +0 -38
  82. data/features/configuration/deprecation_stream.feature +0 -58
  83. data/features/configuration/enable_global_dsl.feature +0 -54
  84. data/features/configuration/fail_fast.feature +0 -77
  85. data/features/configuration/failure_exit_code.feature +0 -36
  86. data/features/configuration/order_and_seed.feature +0 -3
  87. data/features/configuration/output_stream.feature +0 -24
  88. data/features/configuration/overriding_global_ordering.feature +0 -93
  89. data/features/configuration/pattern.feature +0 -38
  90. data/features/configuration/profile.feature +0 -220
  91. data/features/configuration/read_options_from_file.feature +0 -90
  92. data/features/configuration/run_all_when_everything_filtered.feature +0 -76
  93. data/features/example_groups/aliasing.feature +0 -48
  94. data/features/example_groups/basic_structure.feature +0 -55
  95. data/features/example_groups/shared_context.feature +0 -74
  96. data/features/example_groups/shared_examples.feature +0 -286
  97. data/features/expectation_framework_integration/configure_expectation_framework.feature +0 -102
  98. data/features/filtering/exclusion_filters.feature +0 -135
  99. data/features/filtering/if_and_unless.feature +0 -138
  100. data/features/filtering/inclusion_filters.feature +0 -101
  101. data/features/formatters/configurable_colors.feature +0 -31
  102. data/features/formatters/custom_formatter.feature +0 -68
  103. data/features/formatters/json_formatter.feature +0 -30
  104. data/features/formatters/regression_tests.feature +0 -95
  105. data/features/formatters/text_formatter.feature +0 -46
  106. data/features/helper_methods/arbitrary_methods.feature +0 -40
  107. data/features/helper_methods/let.feature +0 -50
  108. data/features/helper_methods/modules.feature +0 -146
  109. data/features/hooks/around_hooks.feature +0 -344
  110. data/features/hooks/before_and_after_hooks.feature +0 -427
  111. data/features/hooks/filtering.feature +0 -232
  112. data/features/metadata/current_example.feature +0 -56
  113. data/features/metadata/described_class.feature +0 -17
  114. data/features/metadata/user_defined.feature +0 -100
  115. data/features/mock_framework_integration/use_any_framework.feature +0 -106
  116. data/features/mock_framework_integration/use_flexmock.feature +0 -94
  117. data/features/mock_framework_integration/use_mocha.feature +0 -95
  118. data/features/mock_framework_integration/use_rr.feature +0 -96
  119. data/features/mock_framework_integration/use_rspec.feature +0 -95
  120. data/features/pending_and_skipped_examples/README.md +0 -3
  121. data/features/pending_and_skipped_examples/pending_examples.feature +0 -118
  122. data/features/pending_and_skipped_examples/skipped_examples.feature +0 -106
  123. data/features/spec_files/arbitrary_file_suffix.feature +0 -13
  124. data/features/step_definitions/additional_cli_steps.rb +0 -83
  125. data/features/subject/explicit_subject.feature +0 -101
  126. data/features/subject/implicit_subject.feature +0 -63
  127. data/features/subject/one_liner_syntax.feature +0 -71
  128. data/features/support/env.rb +0 -21
  129. data/features/support/require_expect_syntax_in_aruba_specs.rb +0 -16
  130. data/features/support/rubinius.rb +0 -6
  131. data/lib/rspec/core/command_line.rb +0 -35
  132. data/lib/rspec/core/drb_command_line.rb +0 -26
  133. data/lib/rspec/core/drb_options.rb +0 -87
  134. data/lib/rspec/core/formatters/legacy_formatter.rb +0 -227
  135. data/lib/rspec/core/shared_example_group/collection.rb +0 -27
  136. data/spec/command_line/order_spec.rb +0 -211
  137. data/spec/rspec/core/backtrace_formatter_spec.rb +0 -230
  138. data/spec/rspec/core/command_line_spec.rb +0 -112
  139. data/spec/rspec/core/command_line_spec_output.txt +0 -0
  140. data/spec/rspec/core/configuration_options_spec.rb +0 -409
  141. data/spec/rspec/core/configuration_spec.rb +0 -1479
  142. data/spec/rspec/core/drb_command_line_spec.rb +0 -102
  143. data/spec/rspec/core/drb_options_spec.rb +0 -193
  144. data/spec/rspec/core/dsl_spec.rb +0 -88
  145. data/spec/rspec/core/example_group_spec.rb +0 -1533
  146. data/spec/rspec/core/example_spec.rb +0 -642
  147. data/spec/rspec/core/filter_manager_spec.rb +0 -229
  148. data/spec/rspec/core/formatters/base_formatter_spec.rb +0 -64
  149. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +0 -303
  150. data/spec/rspec/core/formatters/deprecation_formatter_spec.rb +0 -208
  151. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +0 -75
  152. data/spec/rspec/core/formatters/helpers_spec.rb +0 -104
  153. data/spec/rspec/core/formatters/html_formatted-2.1.0.html +0 -392
  154. data/spec/rspec/core/formatters/html_formatted.html +0 -397
  155. data/spec/rspec/core/formatters/html_formatter_spec.rb +0 -122
  156. data/spec/rspec/core/formatters/json_formatter_spec.rb +0 -206
  157. data/spec/rspec/core/formatters/legacy_formatter_spec.rb +0 -137
  158. data/spec/rspec/core/formatters/progress_formatter_spec.rb +0 -43
  159. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +0 -26
  160. data/spec/rspec/core/formatters_spec.rb +0 -120
  161. data/spec/rspec/core/hooks_filtering_spec.rb +0 -227
  162. data/spec/rspec/core/hooks_spec.rb +0 -294
  163. data/spec/rspec/core/memoized_helpers_spec.rb +0 -495
  164. data/spec/rspec/core/metadata_spec.rb +0 -491
  165. data/spec/rspec/core/option_parser_spec.rb +0 -262
  166. data/spec/rspec/core/ordering_spec.rb +0 -102
  167. data/spec/rspec/core/pending_example_spec.rb +0 -117
  168. data/spec/rspec/core/pending_spec.rb +0 -8
  169. data/spec/rspec/core/project_initializer_spec.rb +0 -73
  170. data/spec/rspec/core/rake_task_spec.rb +0 -146
  171. data/spec/rspec/core/random_spec.rb +0 -47
  172. data/spec/rspec/core/reporter_spec.rb +0 -155
  173. data/spec/rspec/core/resources/a_bar.rb +0 -0
  174. data/spec/rspec/core/resources/a_foo.rb +0 -0
  175. data/spec/rspec/core/resources/a_spec.rb +0 -1
  176. data/spec/rspec/core/resources/custom_example_group_runner.rb +0 -14
  177. data/spec/rspec/core/resources/formatter_specs.rb +0 -58
  178. data/spec/rspec/core/resources/utf8_encoded.rb +0 -8
  179. data/spec/rspec/core/rspec_matchers_spec.rb +0 -45
  180. data/spec/rspec/core/ruby_project_spec.rb +0 -26
  181. data/spec/rspec/core/runner_spec.rb +0 -151
  182. data/spec/rspec/core/shared_context_spec.rb +0 -102
  183. data/spec/rspec/core/shared_example_group/collection_spec.rb +0 -57
  184. data/spec/rspec/core/shared_example_group_spec.rb +0 -114
  185. data/spec/rspec/core/warnings_spec.rb +0 -29
  186. data/spec/rspec/core/world_spec.rb +0 -142
  187. data/spec/rspec/core_spec.rb +0 -91
  188. data/spec/spec_helper.rb +0 -160
  189. data/spec/support/config_options_helper.rb +0 -13
  190. data/spec/support/formatter_support.rb +0 -83
  191. data/spec/support/helper_methods.rb +0 -26
  192. data/spec/support/isolate_load_path_mutation.rb +0 -5
  193. data/spec/support/isolated_directory.rb +0 -10
  194. data/spec/support/isolated_home_directory.rb +0 -16
  195. data/spec/support/legacy_formatter_using_sub_classing_example.rb +0 -87
  196. data/spec/support/matchers.rb +0 -85
  197. data/spec/support/mathn_integration_support.rb +0 -12
  198. data/spec/support/old_style_formatter_example.rb +0 -69
  199. data/spec/support/shared_example_groups.rb +0 -13
  200. data/spec/support/spec_files.rb +0 -44
  201. data/spec/support/stderr_splitter.rb +0 -36
@@ -1,47 +1,59 @@
1
1
  require 'erb'
2
2
  require 'shellwords'
3
+ require 'set'
3
4
 
4
5
  module RSpec
5
6
  module Core
6
- # @private
7
+ # Responsible for utilizing externally provided configuration options,
8
+ # whether via the command line, `.rspec`, `~/.rspec`, `.rspec-local`
9
+ # or a custom options file.
7
10
  class ConfigurationOptions
11
+ # @param args [Array<String>] command line arguments
8
12
  def initialize(args)
9
13
  @args = args.dup
14
+ organize_options
10
15
  end
11
16
 
17
+ # Updates the provided {Configuration} instance based on the provided
18
+ # external configuration options.
19
+ #
20
+ # @param config [Configuration] the configuration instance to update
12
21
  def configure(config)
13
- config.filter_manager = filter_manager
14
-
15
22
  process_options_into config
23
+ configure_filter_manager config.filter_manager
16
24
  load_formatters_into config
17
25
  end
18
26
 
19
- def options
20
- @options ||= (file_options << command_line_options << env_options).
21
- each {|opts|
22
- filter_manager.include opts.delete(:inclusion_filter) if opts.has_key?(:inclusion_filter)
23
- filter_manager.exclude opts.delete(:exclusion_filter) if opts.has_key?(:exclusion_filter)
24
- }.
25
- inject(:libs => [], :requires => []) {|h, opts|
26
- h.merge(opts) {|k, oldval, newval|
27
- [:libs, :requires].include?(k) ? oldval + newval : newval
28
- }
29
- }
27
+ # @api private
28
+ # Updates the provided {FilterManager} based on the filter options.
29
+ # @param filter_manager [FilterManager] instance to update
30
+ def configure_filter_manager(filter_manager)
31
+ @filter_manager_inclusions.each { |val| filter_manager.include(val) }
32
+ @filter_manager_exclusions.each { |val| filter_manager.exclude(val) }
30
33
  end
31
34
 
32
- def drb_argv
33
- DrbOptions.new(options, filter_manager).options
34
- end
35
-
36
- def filter_manager
37
- @filter_manager ||= RSpec::configuration.filter_manager
38
- end
35
+ # @return [Hash] the final merged options, drawn from all external sources
36
+ attr_reader :options
39
37
 
40
38
  private
41
39
 
40
+ def organize_options
41
+ @filter_manager_inclusions = []
42
+ @filter_manager_exclusions = []
43
+
44
+ @options = (file_options << command_line_options << env_options).each { |opts|
45
+ @filter_manager_inclusions << opts.delete(:inclusion_filter) if opts.key?(:inclusion_filter)
46
+ @filter_manager_exclusions << opts.delete(:exclusion_filter) if opts.key?(:exclusion_filter)
47
+ }.inject(:libs => [], :requires => []) { |hash, opts|
48
+ hash.merge(opts) { |key, oldval, newval|
49
+ [:libs, :requires].include?(key) ? oldval + newval : newval
50
+ }
51
+ }
52
+ end
53
+
42
54
  UNFORCED_OPTIONS = [
43
55
  :requires, :profile, :drb, :libs, :files_or_directories_to_run,
44
- :line_numbers, :full_description, :full_backtrace, :tty
56
+ :full_description, :full_backtrace, :tty
45
57
  ].to_set
46
58
 
47
59
  UNPROCESSABLE_OPTIONS = [:formatters].to_set
@@ -58,6 +70,10 @@ module RSpec
58
70
  end
59
71
 
60
72
  OPTIONS_ORDER = [
73
+ # It's important to set this before anything that might issue a
74
+ # deprecation (or otherwise access the reporter).
75
+ :deprecation_stream,
76
+
61
77
  # load paths depend on nothing, but must be set before `requires`
62
78
  # to support load-path-relative requires.
63
79
  :libs,
@@ -0,0 +1,111 @@
1
+ require 'drb/drb'
2
+
3
+ module RSpec
4
+ module Core
5
+ # @private
6
+ class DRbRunner
7
+ def initialize(options, configuration=RSpec.configuration)
8
+ @options = options
9
+ @configuration = configuration
10
+ end
11
+
12
+ def drb_port
13
+ @options.options[:drb_port] || ENV['RSPEC_DRB'] || 8989
14
+ end
15
+
16
+ def run(err, out)
17
+ begin
18
+ DRb.start_service("druby://localhost:0")
19
+ rescue SocketError, Errno::EADDRNOTAVAIL
20
+ DRb.start_service("druby://:0")
21
+ end
22
+ spec_server = DRbObject.new_with_uri("druby://127.0.0.1:#{drb_port}")
23
+ spec_server.run(drb_argv, err, out)
24
+ end
25
+
26
+ def drb_argv
27
+ @drb_argv ||= begin
28
+ @options.configure_filter_manager(@configuration.filter_manager)
29
+ DRbOptions.new(@options.options, @configuration.filter_manager).options
30
+ end
31
+ end
32
+ end
33
+
34
+ # @private
35
+ class DRbOptions
36
+ def initialize(submitted_options, filter_manager)
37
+ @submitted_options = submitted_options
38
+ @filter_manager = filter_manager
39
+ end
40
+
41
+ def options
42
+ argv = []
43
+ argv << "--color" if @submitted_options[:color]
44
+ argv << "--profile" if @submitted_options[:profile_examples]
45
+ argv << "--backtrace" if @submitted_options[:full_backtrace]
46
+ argv << "--tty" if @submitted_options[:tty]
47
+ argv << "--fail-fast" if @submitted_options[:fail_fast]
48
+ argv << "--options" << @submitted_options[:custom_options_file] if @submitted_options[:custom_options_file]
49
+ argv << "--order" << @submitted_options[:order] if @submitted_options[:order]
50
+
51
+ add_failure_exit_code(argv)
52
+ add_full_description(argv)
53
+ add_filter(argv, :inclusion, @filter_manager.inclusions)
54
+ add_filter(argv, :exclusion, @filter_manager.exclusions)
55
+ add_formatters(argv)
56
+ add_libs(argv)
57
+ add_requires(argv)
58
+
59
+ argv + @submitted_options[:files_or_directories_to_run]
60
+ end
61
+
62
+ def add_failure_exit_code(argv)
63
+ if @submitted_options[:failure_exit_code]
64
+ argv << "--failure-exit-code" << @submitted_options[:failure_exit_code].to_s
65
+ end
66
+ end
67
+
68
+ def add_full_description(argv)
69
+ if @submitted_options[:full_description]
70
+ # The argument to --example is regexp-escaped before being stuffed
71
+ # into a regexp when received for the first time (see OptionParser).
72
+ # Hence, merely grabbing the source of this regexp will retain the
73
+ # backslashes, so we must remove them.
74
+ @submitted_options[:full_description].each do |description|
75
+ argv << "--example" << description.source.delete('\\')
76
+ end
77
+ end
78
+ end
79
+
80
+ CONDITIONAL_FILTERS = [:if, :unless]
81
+
82
+ def add_filter(argv, name, hash)
83
+ hash.each_pair do |k, v|
84
+ next if CONDITIONAL_FILTERS.include?(k)
85
+ tag = name == :inclusion ? k.to_s : "~#{k}"
86
+ tag << ":#{v}" if v.is_a?(String)
87
+ argv << "--tag" << tag
88
+ end unless hash.empty?
89
+ end
90
+
91
+ def add_formatters(argv)
92
+ @submitted_options[:formatters].each do |pair|
93
+ argv << "--format" << pair[0]
94
+ argv << "--out" << pair[1] if pair[1]
95
+ end if @submitted_options[:formatters]
96
+ end
97
+
98
+ def add_libs(argv)
99
+ @submitted_options[:libs].each do |path|
100
+ argv << "-I" << path
101
+ end if @submitted_options[:libs]
102
+ end
103
+
104
+ def add_requires(argv)
105
+ @submitted_options[:requires].each do |path|
106
+ argv << "--require" << path
107
+ end if @submitted_options[:requires]
108
+ end
109
+ end
110
+ end
111
+ end
@@ -33,11 +33,12 @@ module RSpec
33
33
  @exposed_globally ||= false
34
34
  end
35
35
 
36
+ # @private
36
37
  def self.expose_example_group_alias(name)
37
38
  example_group_aliases << name
38
39
 
39
40
  (class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block|
40
- RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block).register
41
+ RSpec.world.register RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block)
41
42
  end
42
43
 
43
44
  expose_example_group_alias_globally(name) if exposed_globally?
@@ -48,7 +49,8 @@ module RSpec
48
49
  attr_accessor :top_level
49
50
  end
50
51
 
51
- # Add's the describe method to Module and the top level binding
52
+ # Adds the describe method to Module and the top level binding
53
+ # @api private
52
54
  def self.expose_globally!
53
55
  return if exposed_globally?
54
56
 
@@ -59,6 +61,8 @@ module RSpec
59
61
  @exposed_globally = true
60
62
  end
61
63
 
64
+ # Removes the describe method from Module and the top level binding
65
+ # @api private
62
66
  def self.remove_globally!
63
67
  return unless exposed_globally?
64
68
 
@@ -69,12 +73,14 @@ module RSpec
69
73
  @exposed_globally = false
70
74
  end
71
75
 
76
+ # @private
72
77
  def self.expose_example_group_alias_globally(method_name)
73
78
  change_global_dsl do
74
79
  define_method(method_name) { |*a, &b| ::RSpec.__send__(method_name, *a, &b) }
75
80
  end
76
81
  end
77
82
 
83
+ # @private
78
84
  def self.change_global_dsl(&changes)
79
85
  (class << top_level; self; end).class_exec(&changes)
80
86
  Module.class_exec(&changes)
@@ -1,9 +1,15 @@
1
1
  module RSpec
2
2
  module Core
3
3
  # Wrapper for an instance of a subclass of {ExampleGroup}. An instance of
4
- # `Example` is returned by the {ExampleGroup#example example} method
5
- # exposed to examples, {Hooks#before before} and {Hooks#after after} hooks,
6
- # and yielded to {Hooks#around around} hooks.
4
+ # `RSpec::Core::Example` is returned by example definition methods
5
+ # such as {ExampleGroup.it it} and is yielded to the {ExampleGroup.it it},
6
+ # {Hooks#before before}, {Hooks#after after}, {Hooks#around around},
7
+ # {MemoizedHelpers::ClassMethods#let let} and
8
+ # {MemoizedHelpers::ClassMethods#subject subject} blocks.
9
+ #
10
+ # This allows us to provide rich metadata about each individual
11
+ # example without adding tons of methods directly to the ExampleGroup
12
+ # that users may inadvertantly redefine.
7
13
  #
8
14
  # Useful for configuring logging and/or taking some action based
9
15
  # on the state of an example's metadata.
@@ -11,17 +17,17 @@ module RSpec
11
17
  # @example
12
18
  #
13
19
  # RSpec.configure do |config|
14
- # config.before do
20
+ # config.before do |example|
15
21
  # log example.description
16
22
  # end
17
23
  #
18
- # config.after do
24
+ # config.after do |example|
19
25
  # log example.description
20
26
  # end
21
27
  #
22
- # config.around do |ex|
28
+ # config.around do |example|
23
29
  # log example.description
24
- # ex.run
30
+ # example.run
25
31
  # end
26
32
  # end
27
33
  #
@@ -33,16 +39,33 @@ module RSpec
33
39
  # end
34
40
  #
35
41
  # @see ExampleGroup
42
+ # @note Example blocks are evaluated in the context of an instance
43
+ # of an `ExampleGroup`, not in the context of an instance of `Example`.
36
44
  class Example
37
45
  # @private
38
46
  #
39
47
  # Used to define methods that delegate to this example's metadata
40
- def self.delegate_to_metadata(*keys)
41
- keys.each { |key| define_method(key) { @metadata[key] } }
48
+ def self.delegate_to_metadata(key)
49
+ define_method(key) { @metadata[key] }
42
50
  end
43
51
 
44
- delegate_to_metadata :execution_result, :file_path, :full_description,
45
- :location, :pending, :skip
52
+ # @return [ExecutionResult] represents the result of running this example.
53
+ delegate_to_metadata :execution_result
54
+ # @return [String] the relative path to the file where this example was defined.
55
+ delegate_to_metadata :file_path
56
+ # @return [String] the full description (including the docstrings of
57
+ # all parent example groups).
58
+ delegate_to_metadata :full_description
59
+ # @return [String] the exact source location of this example in a form
60
+ # like `./path/to/spec.rb:17`
61
+ delegate_to_metadata :location
62
+ # @return [Boolean] flag that indicates that the example is not expected to pass.
63
+ # It will be run and will either have a pending result (if a failure occurs)
64
+ # or a failed result (if no failure occurs).
65
+ delegate_to_metadata :pending
66
+ # @return [Boolean] flag that will cause the example to not run.
67
+ # The {ExecutionResult} status will be `:pending`.
68
+ delegate_to_metadata :skip
46
69
 
47
70
  # Returns the string submitted to `example` or its aliases (e.g.
48
71
  # `specify`, `it`, etc). If no string is submitted (e.g. `it { is_expected.to
@@ -74,27 +97,28 @@ module RSpec
74
97
  # running this example.
75
98
  attr_reader :example_group_instance
76
99
 
77
- # @attr_accessor
100
+ # @attr
78
101
  # @private
79
102
  attr_accessor :clock
80
103
 
81
104
  # Creates a new instance of Example.
82
- # @param example_group_class the subclass of ExampleGroup in which this Example is declared
83
- # @param description the String passed to the `it` method (or alias)
84
- # @param metadata additional args passed to `it` to be used as metadata
85
- # @param example_block the block of code that represents the example
86
- def initialize(example_group_class, description, metadata, example_block=nil)
87
- @example_group_class, @options, @example_block = example_group_class, metadata, example_block
88
- @metadata = @example_group_class.metadata.for_example(description, metadata)
105
+ # @param example_group_class [Class] the subclass of ExampleGroup in which this Example is declared
106
+ # @param description [String] the String passed to the `it` method (or alias)
107
+ # @param user_metadata [Hash] additional args passed to `it` to be used as metadata
108
+ # @param example_block [Proc] the block of code that represents the example
109
+ # @api private
110
+ def initialize(example_group_class, description, user_metadata, example_block=nil)
111
+ @example_group_class = example_group_class
112
+ @example_block = example_block
113
+
114
+ @metadata = Metadata::ExampleHash.create(
115
+ @example_group_class.metadata, user_metadata, description, example_block
116
+ )
117
+
89
118
  @example_group_instance = @exception = nil
90
119
  @clock = RSpec::Core::Time
91
120
  end
92
121
 
93
- # @deprecated access options via metadata instead
94
- def options
95
- @options
96
- end
97
-
98
122
  # Returns the example group class that provides the context for running
99
123
  # this example.
100
124
  def example_group
@@ -105,7 +129,7 @@ module RSpec
105
129
  alias_method :skipped?, :skip
106
130
 
107
131
  # @api private
108
- # instance_evals the block passed to the constructor in the context of
132
+ # instance_execs the block passed to the constructor in the context of
109
133
  # the instance of {ExampleGroup}.
110
134
  # @param example_group_instance the instance of an ExampleGroup subclass
111
135
  def run(example_group_instance, reporter)
@@ -118,9 +142,9 @@ module RSpec
118
142
  if skipped?
119
143
  Pending.mark_pending! self, skip
120
144
  elsif !RSpec.configuration.dry_run?
121
- with_around_each_hooks do
145
+ with_around_example_hooks do
122
146
  begin
123
- run_before_each
147
+ run_before_example
124
148
  @example_group_instance.instance_exec(self, &@example_block)
125
149
 
126
150
  if pending?
@@ -128,7 +152,7 @@ module RSpec
128
152
 
129
153
  raise Pending::PendingExampleFixedError,
130
154
  'Expected example to fail since it is pending, but it passed.',
131
- metadata[:caller]
155
+ [location]
132
156
  end
133
157
  rescue Pending::SkipDeclaredInExample
134
158
  # no-op, required metadata has already been set by the `skip`
@@ -136,7 +160,7 @@ module RSpec
136
160
  rescue Exception => e
137
161
  set_exception(e)
138
162
  ensure
139
- run_after_each
163
+ run_after_example
140
164
  end
141
165
  end
142
166
  end
@@ -147,12 +171,6 @@ module RSpec
147
171
  @example_group_instance.instance_variable_set(ivar, nil)
148
172
  end
149
173
  @example_group_instance = nil
150
-
151
- begin
152
- assign_generated_description
153
- rescue Exception => e
154
- set_exception(e, "while assigning the example description")
155
- end
156
174
  end
157
175
 
158
176
  finish(reporter)
@@ -160,12 +178,10 @@ module RSpec
160
178
  RSpec.current_example = nil
161
179
  end
162
180
 
163
- # Wraps a `Proc` and exposes a `run` method for use in {Hooks#around
164
- # around} hooks.
165
- #
166
- # @note Procsy, itself, is not a public API, but we're documenting it
167
- # here to document how to interact with the object yielded to an
168
- # `around` hook.
181
+ # Wraps both a `Proc` and an {Example} for use in {Hooks#around
182
+ # around} hooks. In around hooks we need to yield this special
183
+ # kind of object (rather than the raw {Example}) because when
184
+ # there are multiple `around` hooks we have to wrap them recursively.
169
185
  #
170
186
  # @example
171
187
  #
@@ -177,39 +193,46 @@ module RSpec
177
193
  # ex.run # run delegates to ex.call
178
194
  # end
179
195
  # end
196
+ #
197
+ # @note This class also exposes the instance methods of {Example},
198
+ # proxying them through to the wrapped {Example} instance.
180
199
  class Procsy
181
- # The `metadata` of the {Example} instance.
182
- attr_reader :metadata
200
+ # The {Example} instance.
201
+ attr_reader :example
202
+
203
+ Example.public_instance_methods(false).each do |name|
204
+ define_method(name) { |*a, &b| @example.__send__(name, *a, &b) }
205
+ end
183
206
 
184
207
  Proc.public_instance_methods(false).each do |name|
185
208
  define_method(name) { |*a, &b| @proc.__send__(name, *a, &b) }
186
209
  end
187
210
  alias run call
188
211
 
189
- def initialize(metadata, &block)
190
- @metadata = metadata
191
- @proc = block
212
+ def initialize(example, &block)
213
+ @example = example
214
+ @proc = block
192
215
  end
193
216
 
194
- # @api private
217
+ # @private
195
218
  def wrap(&block)
196
- self.class.new(metadata, &block)
219
+ self.class.new(example, &block)
197
220
  end
198
221
  end
199
222
 
200
223
  # @private
201
224
  def any_apply?(filters)
202
- metadata.any_apply?(filters)
225
+ MetadataFilter.any_apply?(filters, metadata)
203
226
  end
204
227
 
205
228
  # @private
206
229
  def all_apply?(filters)
207
- @metadata.all_apply?(filters) || @example_group_class.all_apply?(filters)
230
+ MetadataFilter.all_apply?(filters, metadata) || @example_group_class.all_apply?(filters)
208
231
  end
209
232
 
210
233
  # @private
211
- def around_each_hooks
212
- @around_each_hooks ||= example_group.hooks.around_each_hooks_for(self)
234
+ def around_example_hooks
235
+ @around_example_hooks ||= example_group.hooks.around_example_hooks_for(self)
213
236
  end
214
237
 
215
238
  # @private
@@ -217,10 +240,10 @@ module RSpec
217
240
  # Used internally to set an exception in an after hook, which
218
241
  # captures the exception but doesn't raise it.
219
242
  def set_exception(exception, context=nil)
220
- if pending?
221
- metadata[:execution_result][:pending_exception] = exception
243
+ if pending? && !(Pending::PendingExampleFixedError === exception)
244
+ execution_result.pending_exception = exception
222
245
  else
223
- if @exception && context != :dont_print
246
+ if @exception
224
247
  # An error has already been set; we don't want to override it,
225
248
  # but we also don't want silence the error, so let's print it.
226
249
  msg = <<-EOS
@@ -240,7 +263,7 @@ module RSpec
240
263
  # @private
241
264
  #
242
265
  # Used internally to set an exception and fail without actually executing
243
- # the example when an exception is raised in before(:all).
266
+ # the example when an exception is raised in before(:context).
244
267
  def fail_with_exception(reporter, exception)
245
268
  start(reporter)
246
269
  set_exception(exception)
@@ -250,7 +273,7 @@ module RSpec
250
273
  # @private
251
274
  #
252
275
  # Used internally to skip without actually executing the example when
253
- # skip is used in before(:all)
276
+ # skip is used in before(:context)
254
277
  def skip_with_exception(reporter, exception)
255
278
  start(reporter)
256
279
  Pending.mark_skipped! self, exception.argument
@@ -258,8 +281,10 @@ module RSpec
258
281
  end
259
282
 
260
283
  # @private
261
- def instance_exec_with_rescue(context = nil, &block)
262
- @example_group_instance.instance_exec_with_rescue(self, context, &block)
284
+ def instance_exec_with_rescue(context, &block)
285
+ @example_group_instance.instance_exec(self, &block)
286
+ rescue Exception => e
287
+ set_exception(e, context)
263
288
  end
264
289
 
265
290
  # @private
@@ -269,88 +294,86 @@ module RSpec
269
294
 
270
295
  private
271
296
 
272
- def with_around_each_hooks(&block)
273
- if around_each_hooks.empty?
297
+ def with_around_example_hooks(&block)
298
+ if around_example_hooks.empty?
274
299
  yield
275
300
  else
276
- @example_group_class.hooks.run(:around, :each, self, Procsy.new(metadata, &block))
301
+ @example_group_class.hooks.run(:around, :example, self, Procsy.new(self, &block))
277
302
  end
278
303
  rescue Exception => e
279
- set_exception(e, "in an around(:each) hook")
304
+ set_exception(e, "in an `around(:example)` hook")
280
305
  end
281
306
 
282
307
  def start(reporter)
283
308
  reporter.example_started(self)
284
- record :started_at => clock.now
309
+ execution_result.started_at = clock.now
285
310
  end
286
311
 
287
312
  def finish(reporter)
288
- pending_message = metadata[:execution_result][:pending_message]
313
+ pending_message = execution_result.pending_message
289
314
 
290
315
  if @exception
291
- record_finished 'failed', :exception => @exception
316
+ record_finished :failed
317
+ execution_result.exception = @exception
292
318
  reporter.example_failed self
293
319
  false
294
320
  elsif pending_message
295
- record_finished 'pending', :pending_message => pending_message
321
+ record_finished :pending
322
+ execution_result.pending_message = pending_message
296
323
  reporter.example_pending self
297
324
  true
298
325
  else
299
- record_finished 'passed'
326
+ record_finished :passed
300
327
  reporter.example_passed self
301
328
  true
302
329
  end
303
330
  end
304
331
 
305
- def record_finished(status, results={})
306
- finished_at = clock.now
307
- record results.merge(
308
- :status => status,
309
- :finished_at => finished_at,
310
- :run_time => (finished_at - execution_result[:started_at]).to_f
311
- )
332
+ def record_finished(status)
333
+ execution_result.record_finished(status, clock.now)
312
334
  end
313
335
 
314
- def run_before_each
336
+ def run_before_example
315
337
  @example_group_instance.setup_mocks_for_rspec
316
- @example_group_class.hooks.run(:before, :each, self)
338
+ @example_group_class.hooks.run(:before, :example, self)
317
339
  end
318
340
 
319
- def run_after_each
320
- @example_group_class.hooks.run(:after, :each, self)
341
+ def run_after_example
342
+ @example_group_class.hooks.run(:after, :example, self)
321
343
  verify_mocks
344
+ assign_generated_description if RSpec.configuration.expecting_with_rspec?
322
345
  rescue Exception => e
323
- set_exception(e, "in an after(:each) hook")
346
+ set_exception(e, "in an `after(:example)` hook")
324
347
  ensure
325
348
  @example_group_instance.teardown_mocks_for_rspec
326
349
  end
327
350
 
328
351
  def verify_mocks
329
- @example_group_instance.verify_mocks_for_rspec
352
+ @example_group_instance.verify_mocks_for_rspec if mocks_need_verification?
330
353
  rescue Exception => e
331
- if metadata[:execution_result][:pending_message]
332
- metadata[:execution_result][:pending_fixed] = false
333
- metadata[:pending] = true
354
+ if pending?
355
+ execution_result.pending_fixed = false
334
356
  @exception = nil
335
357
  else
336
- set_exception(e, :dont_print)
358
+ set_exception(e)
337
359
  end
338
360
  end
339
361
 
340
- def assign_generated_description
341
- return unless RSpec.configuration.expecting_with_rspec?
362
+ def mocks_need_verification?
363
+ exception.nil? || execution_result.pending_fixed?
364
+ end
342
365
 
343
- if metadata[:description_args].empty?
344
- metadata[:description_args] << RSpec::Matchers.generated_description
366
+ def assign_generated_description
367
+ if metadata[:description].empty? && (description = RSpec::Matchers.generated_description)
368
+ metadata[:description] = description
369
+ metadata[:full_description] << description
345
370
  end
346
-
371
+ rescue Exception => e
372
+ set_exception(e, "while assigning the example description")
373
+ ensure
347
374
  RSpec::Matchers.clear_generated_description
348
375
  end
349
376
 
350
- def record(results={})
351
- execution_result.update(results)
352
- end
353
-
354
377
  def skip_message
355
378
  if String === skip
356
379
  skip
@@ -358,6 +381,92 @@ module RSpec
358
381
  Pending::NO_REASON_GIVEN
359
382
  end
360
383
  end
384
+
385
+ # Represents the result of executing an example.
386
+ # Behaves like a hash for backwards compatibility.
387
+ class ExecutionResult
388
+ include HashImitatable
389
+
390
+ # @return [Symbol] `:passed`, `:failed` or `:pending`.
391
+ attr_accessor :status
392
+
393
+ # @return [Exception, nil] The failure, if there was one.
394
+ attr_accessor :exception
395
+
396
+ # @return [Time] When the example started.
397
+ attr_accessor :started_at
398
+
399
+ # @return [Time] When the example finished.
400
+ attr_accessor :finished_at
401
+
402
+ # @return [Float] How long the example took in seconds.
403
+ attr_accessor :run_time
404
+
405
+ # @return [String, nil] The reason the example was pending,
406
+ # or nil if the example was not pending.
407
+ attr_accessor :pending_message
408
+
409
+ # @return [Exception, nil] The exception triggered while
410
+ # executing the pending example. If no exception was triggered
411
+ # it would no longer get a status of `:pending` unless it was
412
+ # tagged with `:skip`.
413
+ attr_accessor :pending_exception
414
+
415
+ # @return [Boolean] For examples tagged with `:pending`,
416
+ # this indicates whether or not it now passes.
417
+ attr_accessor :pending_fixed
418
+
419
+ alias pending_fixed? pending_fixed
420
+
421
+ # @api private
422
+ # Records the finished status of the example.
423
+ def record_finished(status, finished_at)
424
+ self.status = status
425
+ self.finished_at = finished_at
426
+ self.run_time = (finished_at - started_at).to_f
427
+ end
428
+
429
+ private
430
+
431
+ # For backwards compatibility we present `status` as a string
432
+ # when presenting the legacy hash interface.
433
+ def hash_for_delegation
434
+ super.tap do |hash|
435
+ hash[:status] &&= status.to_s
436
+ end
437
+ end
438
+
439
+ def set_value(name, value)
440
+ value &&= value.to_sym if name == :status
441
+ super(name, value)
442
+ end
443
+
444
+ def get_value(name)
445
+ if name == :status
446
+ status.to_s if status
447
+ else
448
+ super
449
+ end
450
+ end
451
+
452
+ def issue_deprecation(method_name, *args)
453
+ RSpec.deprecate("Treating `metadata[:execution_result]` as a hash",
454
+ :replacement => "the attributes methods to access the data")
455
+ end
456
+ end
457
+ end
458
+
459
+ # @private
460
+ # Provides an execution context for before/after :suite hooks.
461
+ class SuiteHookContext < Example
462
+ def initialize
463
+ super(AnonymousExampleGroup, "", {})
464
+ end
465
+
466
+ # To ensure we don't silence errors...
467
+ def set_exception(exception, context=nil)
468
+ raise exception
469
+ end
361
470
  end
362
471
  end
363
472
  end