rspec-core 3.0.4 → 3.12.2

Sign up to get free protection for your applications and to get access to all the features.
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