rspec-core 3.0.4 → 3.12.2

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 (85) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +2 -1
  5. data/Changelog.md +888 -2
  6. data/{License.txt → LICENSE.md} +6 -5
  7. data/README.md +165 -24
  8. data/lib/rspec/autorun.rb +1 -0
  9. data/lib/rspec/core/backtrace_formatter.rb +19 -20
  10. data/lib/rspec/core/bisect/coordinator.rb +62 -0
  11. data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
  12. data/lib/rspec/core/bisect/fork_runner.rb +138 -0
  13. data/lib/rspec/core/bisect/server.rb +61 -0
  14. data/lib/rspec/core/bisect/shell_command.rb +126 -0
  15. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  16. data/lib/rspec/core/bisect/utilities.rb +69 -0
  17. data/lib/rspec/core/configuration.rb +1287 -246
  18. data/lib/rspec/core/configuration_options.rb +95 -35
  19. data/lib/rspec/core/did_you_mean.rb +46 -0
  20. data/lib/rspec/core/drb.rb +21 -12
  21. data/lib/rspec/core/dsl.rb +10 -6
  22. data/lib/rspec/core/example.rb +305 -113
  23. data/lib/rspec/core/example_group.rb +431 -223
  24. data/lib/rspec/core/example_status_persister.rb +235 -0
  25. data/lib/rspec/core/filter_manager.rb +86 -115
  26. data/lib/rspec/core/flat_map.rb +6 -4
  27. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  28. data/lib/rspec/core/formatters/base_formatter.rb +14 -116
  29. data/lib/rspec/core/formatters/base_text_formatter.rb +18 -21
  30. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  31. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
  32. data/lib/rspec/core/formatters/console_codes.rb +29 -18
  33. data/lib/rspec/core/formatters/deprecation_formatter.rb +16 -16
  34. data/lib/rspec/core/formatters/documentation_formatter.rb +49 -16
  35. data/lib/rspec/core/formatters/exception_presenter.rb +525 -0
  36. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  37. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  38. data/lib/rspec/core/formatters/helpers.rb +45 -15
  39. data/lib/rspec/core/formatters/html_formatter.rb +33 -28
  40. data/lib/rspec/core/formatters/html_printer.rb +30 -20
  41. data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
  42. data/lib/rspec/core/formatters/json_formatter.rb +18 -9
  43. data/lib/rspec/core/formatters/profile_formatter.rb +10 -9
  44. data/lib/rspec/core/formatters/progress_formatter.rb +5 -4
  45. data/lib/rspec/core/formatters/protocol.rb +182 -0
  46. data/lib/rspec/core/formatters/snippet_extractor.rb +113 -82
  47. data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
  48. data/lib/rspec/core/formatters.rb +81 -41
  49. data/lib/rspec/core/hooks.rb +314 -244
  50. data/lib/rspec/core/invocations.rb +87 -0
  51. data/lib/rspec/core/memoized_helpers.rb +161 -51
  52. data/lib/rspec/core/metadata.rb +132 -61
  53. data/lib/rspec/core/metadata_filter.rb +224 -64
  54. data/lib/rspec/core/minitest_assertions_adapter.rb +6 -3
  55. data/lib/rspec/core/mocking_adapters/flexmock.rb +4 -2
  56. data/lib/rspec/core/mocking_adapters/mocha.rb +11 -9
  57. data/lib/rspec/core/mocking_adapters/null.rb +2 -0
  58. data/lib/rspec/core/mocking_adapters/rr.rb +3 -1
  59. data/lib/rspec/core/mocking_adapters/rspec.rb +3 -1
  60. data/lib/rspec/core/notifications.rb +192 -206
  61. data/lib/rspec/core/option_parser.rb +174 -69
  62. data/lib/rspec/core/ordering.rb +48 -35
  63. data/lib/rspec/core/output_wrapper.rb +29 -0
  64. data/lib/rspec/core/pending.rb +25 -33
  65. data/lib/rspec/core/profiler.rb +34 -0
  66. data/lib/rspec/core/project_initializer/.rspec +0 -2
  67. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +59 -39
  68. data/lib/rspec/core/project_initializer.rb +5 -3
  69. data/lib/rspec/core/rake_task.rb +99 -55
  70. data/lib/rspec/core/reporter.rb +128 -15
  71. data/lib/rspec/core/ruby_project.rb +14 -6
  72. data/lib/rspec/core/runner.rb +96 -45
  73. data/lib/rspec/core/sandbox.rb +37 -0
  74. data/lib/rspec/core/set.rb +54 -0
  75. data/lib/rspec/core/shared_example_group.rb +133 -43
  76. data/lib/rspec/core/shell_escape.rb +49 -0
  77. data/lib/rspec/core/test_unit_assertions_adapter.rb +4 -4
  78. data/lib/rspec/core/version.rb +1 -1
  79. data/lib/rspec/core/warnings.rb +6 -6
  80. data/lib/rspec/core/world.rb +172 -68
  81. data/lib/rspec/core.rb +66 -21
  82. data.tar.gz.sig +0 -0
  83. metadata +93 -69
  84. metadata.gz.sig +0 -0
  85. data/lib/rspec/core/backport_random.rb +0 -336
@@ -0,0 +1,34 @@
1
+ module RSpec
2
+ module Core
3
+ # @private
4
+ class Profiler
5
+ NOTIFICATIONS = [:example_group_started, :example_group_finished, :example_started]
6
+
7
+ def initialize
8
+ @example_groups = Hash.new { |h, k| h[k] = { :count => 0 } }
9
+ end
10
+
11
+ attr_reader :example_groups
12
+
13
+ def example_group_started(notification)
14
+ return unless notification.group.top_level?
15
+
16
+ @example_groups[notification.group][:start] = Time.now
17
+ @example_groups[notification.group][:description] = notification.group.top_level_description
18
+ end
19
+
20
+ def example_group_finished(notification)
21
+ return unless notification.group.top_level?
22
+
23
+ group = @example_groups[notification.group]
24
+ return unless group.key?(:start)
25
+ group[:total_time] = Time.now - group[:start]
26
+ end
27
+
28
+ def example_started(notification)
29
+ group = notification.example.example_group.parent_groups.last
30
+ @example_groups[group][:count] += 1
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,3 +1 @@
1
- --color
2
- --warnings
3
1
  --require spec_helper
@@ -1,29 +1,72 @@
1
1
  # This file was generated by the `rspec --init` command. Conventionally, all
2
2
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
- # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
- # file to always be loaded, without a need to explicitly require it in any files.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
5
6
  #
6
7
  # Given that it is always loaded, you are encouraged to keep this file as
7
8
  # light-weight as possible. Requiring heavyweight dependencies from this file
8
9
  # will add to the boot time of your test suite on EVERY test run, even for an
9
- # individual file that may not need all of that loaded. Instead, make a
10
- # separate helper file that requires this one and then use it only in the specs
11
- # that actually need it.
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
12
14
  #
13
- # The `.rspec` file also contains a few flags that are not defaults but that
14
- # users commonly want.
15
- #
16
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
15
+ # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
16
  RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
18
47
  # The settings below are suggested to provide a good initial experience
19
48
  # with RSpec, but feel free to customize to your heart's content.
20
49
  =begin
21
- # These two settings work together to allow you to limit a spec run
22
- # to individual examples or groups you care about by tagging them with
23
- # `:focus` metadata. When nothing is tagged with `:focus`, all examples
24
- # get run.
25
- config.filter_run :focus
26
- config.run_all_when_everything_filtered = true
50
+ # This allows you to limit a spec run to individual examples or groups
51
+ # you care about by tagging them with `:focus` metadata. When nothing
52
+ # is tagged with `:focus`, all examples get run. RSpec also provides
53
+ # aliases for `it`, `describe`, and `context` that include `:focus`
54
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55
+ config.filter_run_when_matching :focus
56
+
57
+ # Allows RSpec to persist some state between runs in order to support
58
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
59
+ # you configure your source control system to ignore this file.
60
+ config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is
63
+ # recommended. For more details, see:
64
+ # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/
65
+ config.disable_monkey_patching!
66
+
67
+ # This setting enables warnings. It's recommended, but in some cases may
68
+ # be too noisy due to issues in dependencies.
69
+ config.warnings = true
27
70
 
28
71
  # Many RSpec users commonly either run the entire suite or an individual
29
72
  # file, and it's useful to allow more verbose output when running an
@@ -32,7 +75,7 @@ RSpec.configure do |config|
32
75
  # Use the documentation formatter for detailed output,
33
76
  # unless a formatter has already been configured
34
77
  # (e.g. via a command-line flag).
35
- config.default_formatter = 'doc'
78
+ config.default_formatter = "doc"
36
79
  end
37
80
 
38
81
  # Print the 10 slowest examples and example groups at the
@@ -51,28 +94,5 @@ RSpec.configure do |config|
51
94
  # test failures related to randomization by passing the same `--seed` value
52
95
  # as the one that triggered the failure.
53
96
  Kernel.srand config.seed
54
-
55
- # rspec-expectations config goes here. You can use an alternate
56
- # assertion/expectation library such as wrong or the stdlib/minitest
57
- # assertions if you prefer.
58
- config.expect_with :rspec do |expectations|
59
- # Enable only the newer, non-monkey-patching expect syntax.
60
- # For more details, see:
61
- # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
62
- expectations.syntax = :expect
63
- end
64
-
65
- # rspec-mocks config goes here. You can use an alternate test double
66
- # library (such as bogus or mocha) by changing the `mock_with` option here.
67
- config.mock_with :rspec do |mocks|
68
- # Enable only the newer, non-monkey-patching expect syntax.
69
- # For more details, see:
70
- # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
71
- mocks.syntax = :expect
72
-
73
- # Prevents you from mocking or stubbing a method that does not exist on
74
- # a real object. This is generally recommended.
75
- mocks.verify_partial_doubles = true
76
- end
77
97
  =end
78
98
  end
@@ -1,14 +1,16 @@
1
+ RSpec::Support.require_rspec_support "directory_maker"
2
+
1
3
  module RSpec
2
4
  module Core
3
5
  # @private
4
- # Generates conventional files for an rspec project
6
+ # Generates conventional files for an RSpec project.
5
7
  class ProjectInitializer
6
8
  attr_reader :destination, :stream, :template_path
7
9
 
8
10
  DOT_RSPEC_FILE = '.rspec'
9
11
  SPEC_HELPER_FILE = 'spec/spec_helper.rb'
10
12
 
11
- def initialize(opts = {})
13
+ def initialize(opts={})
12
14
  @destination = opts.fetch(:destination, Dir.getwd)
13
15
  @stream = opts.fetch(:report_stream, $stdout)
14
16
  @template_path = opts.fetch(:template_path) do
@@ -28,7 +30,7 @@ module RSpec
28
30
  return report_exists(file) if File.exist?(destination_file)
29
31
 
30
32
  report_creating(file)
31
- FileUtils.mkdir_p(File.dirname(destination_file))
33
+ RSpec::Support::DirectoryMaker.mkdir_p(File.dirname(destination_file))
32
34
  File.open(destination_file, 'w') do |f|
33
35
  f.write File.read(File.join(template_path, file))
34
36
  end
@@ -1,69 +1,77 @@
1
- require 'rspec/support'
2
- require 'rspec/core/version'
3
- RSpec::Support.require_rspec_support "warnings"
4
-
5
1
  require 'rake'
6
2
  require 'rake/tasklib'
7
- require 'shellwords'
3
+ require 'rspec/support'
4
+
5
+ RSpec::Support.require_rspec_support "ruby_features"
6
+
7
+ # :nocov:
8
+ unless RSpec::Support.respond_to?(:require_rspec_core)
9
+ RSpec::Support.define_optimized_require_for_rspec(:core) { |f| require_relative "../#{f}" }
10
+ end
11
+ # :nocov:
12
+
13
+ RSpec::Support.require_rspec_core "shell_escape"
8
14
 
9
15
  module RSpec
10
16
  module Core
11
- # Rspec rake task
17
+ # RSpec rake task
12
18
  #
13
19
  # @see Rakefile
14
20
  class RakeTask < ::Rake::TaskLib
15
21
  include ::Rake::DSL if defined?(::Rake::DSL)
22
+ include RSpec::Core::ShellEscape
16
23
 
17
- # Default path to the rspec executable
24
+ # Default path to the RSpec executable.
18
25
  DEFAULT_RSPEC_PATH = File.expand_path('../../../../exe/rspec', __FILE__)
19
26
 
20
27
  # Default pattern for spec files.
21
- DEFAULT_PATTERN = './spec{,/*/**}/*_spec.rb'
28
+ DEFAULT_PATTERN = 'spec/**{,/*/**}/*_spec.rb'
22
29
 
23
- # Name of task.
24
- #
25
- # default:
26
- # :spec
30
+ # Name of task. Defaults to `:spec`.
27
31
  attr_accessor :name
28
32
 
29
- # Glob pattern to match files.
30
- #
31
- # default:
32
- # 'spec/**/*_spec.rb'
33
+ # Files matching this pattern will be loaded.
34
+ # Defaults to `'spec/**{,/*/**}/*_spec.rb'`.
33
35
  attr_accessor :pattern
34
36
 
35
- # Whether or not to fail Rake when an error occurs (typically when examples fail).
36
- #
37
- # default:
38
- # true
37
+ # Files matching this pattern will be excluded.
38
+ # Defaults to `nil`.
39
+ attr_accessor :exclude_pattern
40
+
41
+ # Whether or not to fail Rake when an error occurs (typically when
42
+ # examples fail). Defaults to `true`.
39
43
  attr_accessor :fail_on_error
40
44
 
41
45
  # A message to print to stderr when there are failures.
42
46
  attr_accessor :failure_message
43
47
 
48
+ if RUBY_VERSION < "1.9.0" || Support::Ruby.jruby?
49
+ # Run RSpec with a clean (empty) environment is not supported
50
+ def with_clean_environment=(_value)
51
+ raise ArgumentError, "Running in a clean environment is not supported on Ruby versions before 1.9.0"
52
+ end
53
+
54
+ # Run RSpec with a clean (empty) environment is not supported
55
+ def with_clean_environment
56
+ false
57
+ end
58
+ else
59
+ # Run RSpec with a clean (empty) environment.
60
+ attr_accessor :with_clean_environment
61
+ end
62
+
44
63
  # Use verbose output. If this is set to true, the task will print the
45
- # executed spec command to stdout.
46
- #
47
- # default:
48
- # true
64
+ # executed spec command to stdout. Defaults to `true`.
49
65
  attr_accessor :verbose
50
66
 
51
- # Command line options to pass to ruby.
52
- #
53
- # default:
54
- # nil
67
+ # Command line options to pass to ruby. Defaults to `nil`.
55
68
  attr_accessor :ruby_opts
56
69
 
57
- # Path to rspec
58
- #
59
- # default:
60
- # 'rspec'
70
+ # Path to RSpec. Defaults to the absolute path to the
71
+ # rspec binary from the loaded rspec-core gem.
61
72
  attr_accessor :rspec_path
62
73
 
63
- # Command line options to pass to rspec.
64
- #
65
- # default:
66
- # nil
74
+ # Command line options to pass to RSpec. Defaults to `nil`.
67
75
  attr_accessor :rspec_opts
68
76
 
69
77
  def initialize(*args, &task_block)
@@ -81,24 +89,26 @@ module RSpec
81
89
  # @private
82
90
  def run_task(verbose)
83
91
  command = spec_command
92
+ puts command if verbose
84
93
 
85
- begin
86
- puts command if verbose
87
- success = system(command)
88
- rescue
89
- puts failure_message if failure_message
90
- end
91
- if fail_on_error && !success
92
- $stderr.puts "#{command} failed"
93
- exit $?.exitstatus
94
+ if with_clean_environment
95
+ return if system({}, command, :unsetenv_others => true)
96
+ else
97
+ return if system(command)
94
98
  end
99
+
100
+ puts failure_message if failure_message
101
+
102
+ return unless fail_on_error
103
+ $stderr.puts "#{command} failed" if verbose
104
+ exit $?.exitstatus || 1
95
105
  end
96
106
 
97
107
  private
98
108
 
99
109
  # @private
100
110
  def define(args, &task_block)
101
- desc "Run RSpec code examples" unless ::Rake.application.last_comment
111
+ desc "Run RSpec code examples" unless ::Rake.application.last_description
102
112
 
103
113
  task name, *args do |_, task_args|
104
114
  RakeFileUtils.__send__(:verbose, verbose) do
@@ -108,35 +118,69 @@ module RSpec
108
118
  end
109
119
  end
110
120
 
111
- def files_to_run
121
+ def file_inclusion_specification
112
122
  if ENV['SPEC']
113
- FileList[ ENV['SPEC'] ].sort
123
+ FileList[ENV['SPEC']].sort
124
+ elsif String === pattern && !File.exist?(pattern)
125
+ return if [*rspec_opts].any? { |opt| opt =~ /--pattern/ }
126
+ "--pattern #{escape pattern}"
114
127
  else
115
- FileList[ pattern ].sort.map(&:shellescape)
128
+ # Before RSpec 3.1, we used `FileList` to get the list of matched
129
+ # files, and then pass that along to the `rspec` command. Starting
130
+ # with 3.1, we prefer to pass along the pattern as-is to the `rspec`
131
+ # command, for 3 reasons:
132
+ #
133
+ # * It's *much* less verbose to pass one `--pattern` option than a
134
+ # long list of files.
135
+ # * It ensures `task.pattern` and `--pattern` have the same
136
+ # behavior.
137
+ # * It fixes a bug, where
138
+ # `task.pattern = pattern_that_matches_no_files` would run *all*
139
+ # files because it would cause no pattern or file args to get
140
+ # passed to `rspec`, which causes all files to get run.
141
+ #
142
+ # However, `FileList` is *far* more flexible than the `--pattern`
143
+ # option. Specifically, it supports individual files and directories,
144
+ # as well as arrays of files, directories and globs, as well as other
145
+ # `FileList` objects.
146
+ #
147
+ # For backwards compatibility, we have to fall back to using FileList
148
+ # if the user has passed a `pattern` option that will not work with
149
+ # `--pattern`.
150
+ #
151
+ # TODO: consider deprecating support for this and removing it in
152
+ # RSpec 4.
153
+ FileList[pattern].sort.map { |file| escape file }
116
154
  end
117
155
  end
118
156
 
157
+ def file_exclusion_specification
158
+ " --exclude-pattern #{escape exclude_pattern}" if exclude_pattern
159
+ end
160
+
119
161
  def spec_command
120
162
  cmd_parts = []
121
163
  cmd_parts << RUBY
122
164
  cmd_parts << ruby_opts
123
165
  cmd_parts << rspec_load_path
124
- cmd_parts << "-S" << rspec_path
125
- cmd_parts << files_to_run
166
+ cmd_parts << escape(rspec_path)
167
+ cmd_parts << file_inclusion_specification
168
+ cmd_parts << file_exclusion_specification
126
169
  cmd_parts << rspec_opts
127
170
  cmd_parts.flatten.reject(&blank).join(" ")
128
171
  end
129
172
 
130
173
  def blank
131
- lambda {|s| s.nil? || s == ""}
174
+ lambda { |s| s.nil? || s == "" }
132
175
  end
133
176
 
134
177
  def rspec_load_path
135
178
  @rspec_load_path ||= begin
136
- core_and_support = $LOAD_PATH.grep \
137
- %r{#{File::SEPARATOR}rspec-(core|support)[^#{File::SEPARATOR}]*#{File::SEPARATOR}lib}
179
+ core_and_support = $LOAD_PATH.grep(
180
+ /#{File::SEPARATOR}rspec-(core|support)[^#{File::SEPARATOR}]*#{File::SEPARATOR}lib/
181
+ ).uniq
138
182
 
139
- "-I#{core_and_support.map(&:shellescape).join(File::PATH_SEPARATOR)}"
183
+ "-I#{core_and_support.map { |file| escape file }.join(File::PATH_SEPARATOR)}"
140
184
  end
141
185
  end
142
186
  end
@@ -2,24 +2,38 @@ module RSpec::Core
2
2
  # A reporter will send notifications to listeners, usually formatters for the
3
3
  # spec suite run.
4
4
  class Reporter
5
+ # @private
6
+ RSPEC_NOTIFICATIONS = Set.new(
7
+ [
8
+ :close, :deprecation, :deprecation_summary, :dump_failures, :dump_pending,
9
+ :dump_profile, :dump_summary, :example_failed, :example_group_finished,
10
+ :example_group_started, :example_passed, :example_pending, :example_started,
11
+ :message, :seed, :start, :start_dump, :stop, :example_finished
12
+ ])
5
13
 
6
14
  def initialize(configuration)
7
15
  @configuration = configuration
8
- @listeners = Hash.new { |h,k| h[k] = Set.new }
16
+ @listeners = Hash.new { |h, k| h[k] = Set.new }
9
17
  @examples = []
10
18
  @failed_examples = []
11
19
  @pending_examples = []
12
20
  @duration = @start = @load_time = nil
21
+ @non_example_exception_count = 0
22
+ @setup_default = lambda {}
23
+ @setup = false
24
+ @profiler = nil
13
25
  end
14
26
 
15
27
  # @private
16
28
  attr_reader :examples, :failed_examples, :pending_examples
17
29
 
18
- # Registers a listener to a list of notifications. The reporter will send notification of
19
- # events to all registered listeners
30
+ # Registers a listener to a list of notifications. The reporter will send
31
+ # notification of events to all registered listeners.
20
32
  #
21
- # @param listener [Object] An obect that wishes to be notified of reporter events
22
- # @param notifications [Array] Array of symbols represents the events a listener wishes to subscribe too
33
+ # @param listener [Object] An object that wishes to be notified of reporter
34
+ # events
35
+ # @param notifications [Array] Array of symbols represents the events a
36
+ # listener wishes to subscribe too
23
37
  def register_listener(listener, *notifications)
24
38
  notifications.each do |notification|
25
39
  @listeners[notification.to_sym] << listener
@@ -27,12 +41,18 @@ module RSpec::Core
27
41
  true
28
42
  end
29
43
 
44
+ # @private
45
+ def prepare_default(loader, output_stream, deprecation_stream)
46
+ @setup_default = lambda do
47
+ loader.setup_default output_stream, deprecation_stream
48
+ end
49
+ end
50
+
30
51
  # @private
31
52
  def registered_listeners(notification)
32
53
  @listeners[notification].to_a
33
54
  end
34
55
 
35
- # @api
36
56
  # @overload report(count, &block)
37
57
  # @overload report(count, &block)
38
58
  # @param expected_example_count [Integer] the number of examples being run
@@ -57,18 +77,42 @@ module RSpec::Core
57
77
  end
58
78
  end
59
79
 
80
+ # @param exit_code [Integer] the exit_code to be return by the reporter
81
+ #
82
+ # Reports a run that exited early without having run any examples.
83
+ #
84
+ def exit_early(exit_code)
85
+ report(0) { exit_code }
86
+ end
87
+
60
88
  # @private
61
- def start(expected_example_count, time = RSpec::Core::Time.now)
89
+ def start(expected_example_count, time=RSpec::Core::Time.now)
62
90
  @start = time
63
91
  @load_time = (@start - @configuration.start_time).to_f
92
+ notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?)
64
93
  notify :start, Notifications::StartNotification.new(expected_example_count, @load_time)
65
94
  end
66
95
 
67
- # @private
96
+ # @param message [#to_s] A message object to send to formatters
97
+ #
98
+ # Send a custom message to supporting formatters.
68
99
  def message(message)
69
100
  notify :message, Notifications::MessageNotification.new(message)
70
101
  end
71
102
 
103
+ # @param event [Symbol] Name of the custom event to trigger on formatters
104
+ # @param options [Hash] Hash of arguments to provide via `CustomNotification`
105
+ #
106
+ # Publish a custom event to supporting registered formatters.
107
+ # @see RSpec::Core::Notifications::CustomNotification
108
+ def publish(event, options={})
109
+ if RSPEC_NOTIFICATIONS.include? event
110
+ raise "RSpec::Core::Reporter#publish is intended for sending custom " \
111
+ "events not internal RSpec ones, please rename your custom event."
112
+ end
113
+ notify event, Notifications::CustomNotification.for(options)
114
+ end
115
+
72
116
  # @private
73
117
  def example_group_started(group)
74
118
  notify :example_group_started, Notifications::GroupNotification.new(group) unless group.descendant_filtered_examples.empty?
@@ -85,6 +129,11 @@ module RSpec::Core
85
129
  notify :example_started, Notifications::ExampleNotification.for(example)
86
130
  end
87
131
 
132
+ # @private
133
+ def example_finished(example)
134
+ notify :example_finished, Notifications::ExampleNotification.for(example)
135
+ end
136
+
88
137
  # @private
89
138
  def example_passed(example)
90
139
  notify :example_passed, Notifications::ExampleNotification.for(example)
@@ -107,24 +156,46 @@ module RSpec::Core
107
156
  notify :deprecation, Notifications::DeprecationNotification.from_hash(hash)
108
157
  end
109
158
 
159
+ # @private
160
+ # Provides a way to notify of an exception that is not tied to any
161
+ # particular example (such as an exception encountered in a :suite hook).
162
+ # Exceptions will be formatted the same way they normally are.
163
+ def notify_non_example_exception(exception, context_description)
164
+ @configuration.world.non_example_failure = true
165
+ @non_example_exception_count += 1
166
+
167
+ example = Example.new(AnonymousExampleGroup, context_description, {})
168
+ presenter = Formatters::ExceptionPresenter.new(exception, example, :indentation => 0)
169
+ message presenter.fully_formatted(nil)
170
+ end
171
+
110
172
  # @private
111
173
  def finish
112
- begin
174
+ close_after do
113
175
  stop
114
176
  notify :start_dump, Notifications::NullNotification
115
177
  notify :dump_pending, Notifications::ExamplesNotification.new(self)
116
178
  notify :dump_failures, Notifications::ExamplesNotification.new(self)
117
179
  notify :deprecation_summary, Notifications::NullNotification
118
- notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples, @pending_examples, @load_time)
119
180
  unless mute_profile_output?
120
- notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples, @configuration.profile_examples)
181
+ notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples,
182
+ @configuration.profile_examples,
183
+ @profiler.example_groups)
121
184
  end
185
+ notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples,
186
+ @pending_examples, @load_time,
187
+ @non_example_exception_count)
122
188
  notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?)
123
- ensure
124
- notify :close, Notifications::NullNotification
125
189
  end
126
190
  end
127
191
 
192
+ # @private
193
+ def close_after
194
+ yield
195
+ ensure
196
+ close
197
+ end
198
+
128
199
  # @private
129
200
  def stop
130
201
  @duration = (RSpec::Core::Time.now - @start).to_f if @start
@@ -133,20 +204,62 @@ module RSpec::Core
133
204
 
134
205
  # @private
135
206
  def notify(event, notification)
207
+ ensure_listeners_ready
136
208
  registered_listeners(event).each do |formatter|
137
209
  formatter.__send__(event, notification)
138
210
  end
139
211
  end
140
212
 
213
+ # @private
214
+ def abort_with(msg, exit_status)
215
+ message(msg)
216
+ close
217
+ exit!(exit_status)
218
+ end
219
+
220
+ # @private
221
+ def fail_fast_limit_met?
222
+ return false unless (fail_fast = @configuration.fail_fast)
223
+
224
+ if fail_fast == true
225
+ @failed_examples.any?
226
+ else
227
+ fail_fast <= @failed_examples.size
228
+ end
229
+ end
230
+
141
231
  private
142
232
 
233
+ def ensure_listeners_ready
234
+ return if @setup
235
+
236
+ @setup_default.call
237
+ @profiler = Profiler.new
238
+ register_listener @profiler, *Profiler::NOTIFICATIONS
239
+ @setup = true
240
+ end
241
+
242
+ def close
243
+ notify :close, Notifications::NullNotification
244
+ end
245
+
143
246
  def mute_profile_output?
144
- # Don't print out profiled info if there are failures and `--fail-fast` is used, it just clutters the output
145
- !@configuration.profile_examples? || (@configuration.fail_fast? && @failed_examples.size > 0)
247
+ # Don't print out profiled info if there are failures and `--fail-fast` is
248
+ # used, it just clutters the output.
249
+ !@configuration.profile_examples? || fail_fast_limit_met?
146
250
  end
147
251
 
148
252
  def seed_used?
149
253
  @configuration.seed && @configuration.seed_used?
150
254
  end
151
255
  end
256
+
257
+ # @private
258
+ # # Used in place of a {Reporter} for situations where we don't want reporting output.
259
+ class NullReporter
260
+ def self.method_missing(*)
261
+ # ignore
262
+ end
263
+ private_class_method :method_missing
264
+ end
152
265
  end