rspec-core 3.5.4 → 3.9.0

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 (64) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +165 -2
  5. data/README.md +16 -16
  6. data/lib/rspec/core.rb +2 -1
  7. data/lib/rspec/core/bisect/coordinator.rb +26 -30
  8. data/lib/rspec/core/bisect/example_minimizer.rb +12 -8
  9. data/lib/rspec/core/bisect/fork_runner.rb +134 -0
  10. data/lib/rspec/core/bisect/server.rb +10 -14
  11. data/lib/rspec/core/bisect/{runner.rb → shell_command.rb} +27 -70
  12. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  13. data/lib/rspec/core/bisect/utilities.rb +58 -0
  14. data/lib/rspec/core/configuration.rb +315 -79
  15. data/lib/rspec/core/configuration_options.rb +43 -4
  16. data/lib/rspec/core/did_you_mean.rb +46 -0
  17. data/lib/rspec/core/drb.rb +3 -1
  18. data/lib/rspec/core/example.rb +19 -12
  19. data/lib/rspec/core/example_group.rb +17 -7
  20. data/lib/rspec/core/formatters.rb +28 -11
  21. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  22. data/lib/rspec/core/formatters/base_formatter.rb +1 -1
  23. data/lib/rspec/core/formatters/base_text_formatter.rb +3 -5
  24. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  25. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +29 -16
  26. data/lib/rspec/core/formatters/console_codes.rb +7 -4
  27. data/lib/rspec/core/formatters/deprecation_formatter.rb +9 -9
  28. data/lib/rspec/core/formatters/documentation_formatter.rb +37 -4
  29. data/lib/rspec/core/formatters/exception_presenter.rb +21 -4
  30. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  31. data/lib/rspec/core/formatters/html_formatter.rb +4 -2
  32. data/lib/rspec/core/formatters/html_printer.rb +3 -3
  33. data/lib/rspec/core/formatters/html_snippet_extractor.rb +4 -0
  34. data/lib/rspec/core/formatters/json_formatter.rb +9 -3
  35. data/lib/rspec/core/formatters/progress_formatter.rb +1 -0
  36. data/lib/rspec/core/formatters/protocol.rb +43 -42
  37. data/lib/rspec/core/formatters/snippet_extractor.rb +1 -3
  38. data/lib/rspec/core/{source → formatters}/syntax_highlighter.rb +21 -1
  39. data/lib/rspec/core/hooks.rb +18 -10
  40. data/lib/rspec/core/invocations.rb +30 -10
  41. data/lib/rspec/core/memoized_helpers.rb +36 -14
  42. data/lib/rspec/core/metadata.rb +2 -3
  43. data/lib/rspec/core/metadata_filter.rb +29 -17
  44. data/lib/rspec/core/notifications.rb +34 -11
  45. data/lib/rspec/core/option_parser.rb +32 -4
  46. data/lib/rspec/core/output_wrapper.rb +29 -0
  47. data/lib/rspec/core/profiler.rb +3 -1
  48. data/lib/rspec/core/project_initializer/.rspec +0 -1
  49. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +1 -4
  50. data/lib/rspec/core/rake_task.rb +21 -1
  51. data/lib/rspec/core/reporter.rb +33 -16
  52. data/lib/rspec/core/runner.rb +31 -15
  53. data/lib/rspec/core/set.rb +5 -0
  54. data/lib/rspec/core/shared_example_group.rb +41 -19
  55. data/lib/rspec/core/shell_escape.rb +2 -2
  56. data/lib/rspec/core/version.rb +1 -1
  57. data/lib/rspec/core/world.rb +24 -5
  58. metadata +26 -20
  59. metadata.gz.sig +0 -0
  60. data/lib/rspec/core/formatters/bisect_formatter.rb +0 -69
  61. data/lib/rspec/core/source.rb +0 -86
  62. data/lib/rspec/core/source/location.rb +0 -13
  63. data/lib/rspec/core/source/node.rb +0 -93
  64. data/lib/rspec/core/source/token.rb +0 -87
@@ -20,7 +20,9 @@ module RSpec
20
20
  def example_group_finished(notification)
21
21
  return unless notification.group.top_level?
22
22
 
23
- @example_groups[notification.group][:total_time] = Time.now - @example_groups[notification.group][:start]
23
+ group = @example_groups[notification.group]
24
+ return unless group.key?(:start)
25
+ group[:total_time] = Time.now - group[:start]
24
26
  end
25
27
 
26
28
  def example_started(notification)
@@ -1,2 +1 @@
1
- --color
2
1
  --require spec_helper
@@ -12,9 +12,6 @@
12
12
  # the additional setup, and require it from the spec files that actually need
13
13
  # it.
14
14
  #
15
- # The `.rspec` file also contains a few flags that are not defaults but that
16
- # users commonly want.
17
- #
18
15
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
16
  RSpec.configure do |config|
20
17
  # rspec-expectations config goes here. You can use an alternate
@@ -80,7 +77,7 @@ RSpec.configure do |config|
80
77
  # Use the documentation formatter for detailed output,
81
78
  # unless a formatter has already been configured
82
79
  # (e.g. via a command-line flag).
83
- config.default_formatter = 'doc'
80
+ config.default_formatter = "doc"
84
81
  end
85
82
 
86
83
  # Print the 10 slowest examples and example groups at the
@@ -45,6 +45,21 @@ module RSpec
45
45
  # A message to print to stderr when there are failures.
46
46
  attr_accessor :failure_message
47
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
+
48
63
  # Use verbose output. If this is set to true, the task will print the
49
64
  # executed spec command to stdout. Defaults to `true`.
50
65
  attr_accessor :verbose
@@ -76,7 +91,12 @@ module RSpec
76
91
  command = spec_command
77
92
  puts command if verbose
78
93
 
79
- return if system(command)
94
+ if with_clean_environment
95
+ return if system({}, command, :unsetenv_others => true)
96
+ else
97
+ return if system(command)
98
+ end
99
+
80
100
  puts failure_message if failure_message
81
101
 
82
102
  return unless fail_on_error
@@ -18,25 +18,15 @@ module RSpec::Core
18
18
  @failed_examples = []
19
19
  @pending_examples = []
20
20
  @duration = @start = @load_time = nil
21
+ @non_example_exception_count = 0
22
+ @setup_default = lambda {}
23
+ @setup = false
24
+ @profiler = nil
21
25
  end
22
26
 
23
27
  # @private
24
28
  attr_reader :examples, :failed_examples, :pending_examples
25
29
 
26
- # @private
27
- def reset
28
- @examples = []
29
- @failed_examples = []
30
- @pending_examples = []
31
- @profiler = Profiler.new if defined?(@profiler)
32
- end
33
-
34
- # @private
35
- def setup_profiler
36
- @profiler = Profiler.new
37
- register_listener @profiler, *Profiler::NOTIFICATIONS
38
- end
39
-
40
30
  # Registers a listener to a list of notifications. The reporter will send
41
31
  # notification of events to all registered listeners.
42
32
  #
@@ -51,6 +41,13 @@ module RSpec::Core
51
41
  true
52
42
  end
53
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
+
54
51
  # @private
55
52
  def registered_listeners(notification)
56
53
  @listeners[notification].to_a
@@ -80,6 +77,14 @@ module RSpec::Core
80
77
  end
81
78
  end
82
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
+
83
88
  # @private
84
89
  def start(expected_example_count, time=RSpec::Core::Time.now)
85
90
  @start = time
@@ -153,10 +158,11 @@ module RSpec::Core
153
158
 
154
159
  # @private
155
160
  # Provides a way to notify of an exception that is not tied to any
156
- # particular exception (such as an exception encountered in a :suite hook).
161
+ # particular example (such as an exception encountered in a :suite hook).
157
162
  # Exceptions will be formatted the same way they normally are.
158
163
  def notify_non_example_exception(exception, context_description)
159
164
  @configuration.world.non_example_failure = true
165
+ @non_example_exception_count += 1
160
166
 
161
167
  example = Example.new(AnonymousExampleGroup, context_description, {})
162
168
  presenter = Formatters::ExceptionPresenter.new(exception, example, :indentation => 0)
@@ -177,7 +183,8 @@ module RSpec::Core
177
183
  @profiler.example_groups)
178
184
  end
179
185
  notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples,
180
- @pending_examples, @load_time)
186
+ @pending_examples, @load_time,
187
+ @non_example_exception_count)
181
188
  notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?)
182
189
  end
183
190
  end
@@ -197,6 +204,7 @@ module RSpec::Core
197
204
 
198
205
  # @private
199
206
  def notify(event, notification)
207
+ ensure_listeners_ready
200
208
  registered_listeners(event).each do |formatter|
201
209
  formatter.__send__(event, notification)
202
210
  end
@@ -222,6 +230,15 @@ module RSpec::Core
222
230
 
223
231
  private
224
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
+
225
242
  def close
226
243
  notify :close, Notifications::NullNotification
227
244
  end
@@ -84,6 +84,8 @@ module RSpec
84
84
  # @param out [IO] output stream
85
85
  def run(err, out)
86
86
  setup(err, out)
87
+ return @configuration.reporter.exit_early(@configuration.failure_exit_code) if RSpec.world.wants_to_quit
88
+
87
89
  run_specs(@world.ordered_example_groups).tap do
88
90
  persist_example_statuses
89
91
  end
@@ -94,10 +96,11 @@ module RSpec
94
96
  # @param err [IO] error stream
95
97
  # @param out [IO] output stream
96
98
  def setup(err, out)
97
- @configuration.error_stream = err
98
- @configuration.output_stream = out if @configuration.output_stream == $stdout
99
- @options.configure(@configuration)
99
+ configure(err, out)
100
+ return if RSpec.world.wants_to_quit
101
+
100
102
  @configuration.load_spec_files
103
+ ensure
101
104
  @world.announce_filters
102
105
  end
103
106
 
@@ -108,8 +111,13 @@ module RSpec
108
111
  # or the configured failure exit code (1 by default) if specs
109
112
  # failed.
110
113
  def run_specs(example_groups)
111
- success = @configuration.reporter.report(@world.example_count(example_groups)) do |reporter|
114
+ examples_count = @world.example_count(example_groups)
115
+ success = @configuration.reporter.report(examples_count) do |reporter|
112
116
  @configuration.with_suite_hooks do
117
+ if examples_count == 0 && @configuration.fail_if_no_examples
118
+ return @configuration.failure_exit_code
119
+ end
120
+
113
121
  example_groups.map { |g| g.run(reporter) }.all?
114
122
  end
115
123
  end && !@world.non_example_failure
@@ -117,17 +125,11 @@ module RSpec
117
125
  success ? 0 : @configuration.failure_exit_code
118
126
  end
119
127
 
120
- private
121
-
122
- def persist_example_statuses
123
- return unless (path = @configuration.example_status_persistence_file_path)
124
-
125
- ExampleStatusPersister.persist(@world.all_examples, path)
126
- rescue SystemCallError => e
127
- RSpec.warning "Could not write example statuses to #{path} (configured as " \
128
- "`config.example_status_persistence_file_path`) due to a " \
129
- "system error: #{e.inspect}. Please check that the config " \
130
- "option is set to an accessible, valid file path", :call_site => nil
128
+ # @private
129
+ def configure(err, out)
130
+ @configuration.error_stream = err
131
+ @configuration.output_stream = out if @configuration.output_stream == $stdout
132
+ @options.configure(@configuration)
131
133
  end
132
134
 
133
135
  # @private
@@ -183,6 +185,20 @@ module RSpec
183
185
  $stderr.puts "\nRSpec is shutting down and will print the summary report... Interrupt again to force quit."
184
186
  end
185
187
  end
188
+
189
+ private
190
+
191
+ def persist_example_statuses
192
+ return if @configuration.dry_run
193
+ return unless (path = @configuration.example_status_persistence_file_path)
194
+
195
+ ExampleStatusPersister.persist(@world.all_examples, path)
196
+ rescue SystemCallError => e
197
+ RSpec.warning "Could not write example statuses to #{path} (configured as " \
198
+ "`config.example_status_persistence_file_path`) due to a " \
199
+ "system error: #{e.inspect}. Please check that the config " \
200
+ "option is set to an accessible, valid file path", :call_site => nil
201
+ end
186
202
  end
187
203
  end
188
204
  end
@@ -44,6 +44,11 @@ module RSpec
44
44
  end
45
45
  self
46
46
  end
47
+
48
+ def clear
49
+ @values.clear
50
+ self
51
+ end
47
52
  end
48
53
  end
49
54
  end
@@ -64,11 +64,6 @@ module RSpec
64
64
  # group; any example group or example with matching metadata will
65
65
  # automatically include this shared example group.
66
66
  # @param block The block to be eval'd
67
- # @overload shared_examples(metadata, &block)
68
- # @param metadata [Array<Symbol>, Hash] metadata to attach to this
69
- # group; any example group or example with matching metadata will
70
- # automatically include this shared example group.
71
- # @param block The block to be eval'd
72
67
  #
73
68
  # Stores the block for later use. The block will be evaluated
74
69
  # in the context of an example group via `include_examples`,
@@ -81,7 +76,7 @@ module RSpec
81
76
  # end
82
77
  # end
83
78
  #
84
- # describe Account do
79
+ # RSpec.describe Account do
85
80
  # it_behaves_like "auditable" do
86
81
  # let(:auditable) { Account.new }
87
82
  # end
@@ -108,7 +103,6 @@ module RSpec
108
103
  # Shared examples top level DSL.
109
104
  module TopLevelDSL
110
105
  # @private
111
- # rubocop:disable Lint/NestedMethodDefinition
112
106
  def self.definitions
113
107
  proc do
114
108
  def shared_examples(name, *args, &block)
@@ -118,7 +112,6 @@ module RSpec
118
112
  alias shared_examples_for shared_examples
119
113
  end
120
114
  end
121
- # rubocop:enable Lint/NestedMethodDefinition
122
115
 
123
116
  # @private
124
117
  def self.exposed_globally?
@@ -153,6 +146,12 @@ module RSpec
153
146
  # @private
154
147
  class Registry
155
148
  def add(context, name, *metadata_args, &block)
149
+ unless block
150
+ RSpec.warning "Shared example group #{name} was defined without a "\
151
+ "block and will have no effect. Please define a "\
152
+ "block or remove the definition."
153
+ end
154
+
156
155
  if RSpec.configuration.shared_context_metadata_behavior == :trigger_inclusion
157
156
  return legacy_add(context, name, *metadata_args, &block)
158
157
  end
@@ -213,20 +212,43 @@ module RSpec
213
212
 
214
213
  def warn_if_key_taken(context, key, new_block)
215
214
  existing_module = shared_example_groups[context][key]
216
-
217
215
  return unless existing_module
218
216
 
219
- RSpec.warn_with <<-WARNING.gsub(/^ +\|/, ''), :call_site => nil
220
- |WARNING: Shared example group '#{key}' has been previously defined at:
221
- | #{formatted_location existing_module.definition}
222
- |...and you are now defining it at:
223
- | #{formatted_location new_block}
224
- |The new definition will overwrite the original one.
225
- WARNING
217
+ old_definition_location = formatted_location existing_module.definition
218
+ new_definition_location = formatted_location new_block
219
+ loaded_spec_files = RSpec.configuration.loaded_spec_files
220
+
221
+ if loaded_spec_files.include?(new_definition_location) && old_definition_location == new_definition_location
222
+ RSpec.warn_with <<-WARNING.gsub(/^ +\|/, ''), :call_site => nil
223
+ |WARNING: Your shared example group, '#{key}', defined at:
224
+ | #{old_definition_location}
225
+ |was automatically loaded by RSpec because the file name
226
+ |matches the configured autoloading pattern (#{RSpec.configuration.pattern}),
227
+ |and is also being required from somewhere else. To fix this
228
+ |warning, either rename the file to not match the pattern, or
229
+ |do not explicitly require the file.
230
+ WARNING
231
+ else
232
+ RSpec.warn_with <<-WARNING.gsub(/^ +\|/, ''), :call_site => nil
233
+ |WARNING: Shared example group '#{key}' has been previously defined at:
234
+ | #{old_definition_location}
235
+ |...and you are now defining it at:
236
+ | #{new_definition_location}
237
+ |The new definition will overwrite the original one.
238
+ WARNING
239
+ end
226
240
  end
227
241
 
228
- def formatted_location(block)
229
- block.source_location.join ":"
242
+ if RUBY_VERSION.to_f >= 1.9
243
+ def formatted_location(block)
244
+ block.source_location.join(":")
245
+ end
246
+ else # 1.8.7
247
+ # :nocov:
248
+ def formatted_location(block)
249
+ block.source_location.join(":").gsub(/:in.*$/, '')
250
+ end
251
+ # :nocov:
230
252
  end
231
253
 
232
254
  if Proc.method_defined?(:source_location)
@@ -235,7 +257,7 @@ module RSpec
235
257
  # :nocov:
236
258
  def ensure_block_has_source_location(block)
237
259
  source_location = yield.split(':')
238
- block.extend Module.new { define_method(:source_location) { source_location } }
260
+ block.extend(Module.new { define_method(:source_location) { source_location } })
239
261
  end
240
262
  # :nocov:
241
263
  end
@@ -6,7 +6,7 @@ module RSpec
6
6
  module_function
7
7
 
8
8
  def quote(argument)
9
- "'#{argument.gsub("'", "\\\\'")}'"
9
+ "'#{argument.to_s.gsub("'", "\\\\'")}'"
10
10
  end
11
11
 
12
12
  if RSpec::Support::OS.windows?
@@ -17,7 +17,7 @@ module RSpec
17
17
  require 'shellwords'
18
18
 
19
19
  def escape(shell_command)
20
- shell_command.shellescape
20
+ Shellwords.escape(shell_command.to_s)
21
21
  end
22
22
  end
23
23
 
@@ -3,7 +3,7 @@ module RSpec
3
3
  # Version information for RSpec Core.
4
4
  module Version
5
5
  # Current version of RSpec Core, in semantic versioning format.
6
- STRING = '3.5.4'
6
+ STRING = '3.9.0'
7
7
  end
8
8
  end
9
9
  end
@@ -21,6 +21,17 @@ module RSpec
21
21
  configuration.world = self
22
22
  @example_groups = []
23
23
  @example_group_counts_by_spec_file = Hash.new(0)
24
+ prepare_example_filtering
25
+ end
26
+
27
+ # @api public
28
+ #
29
+ # Prepares filters so that they apply to example groups when they run.
30
+ #
31
+ # This is a separate method so that filters can be modified/replaced and
32
+ # examples refiltered during a process's lifetime, which can be useful for
33
+ # a custom runner.
34
+ def prepare_example_filtering
24
35
  @filtered_examples = Hash.new do |hash, group|
25
36
  hash[group] = filter_manager.prune(group.examples)
26
37
  end
@@ -40,7 +51,8 @@ module RSpec
40
51
  def reset
41
52
  RSpec::ExampleGroups.remove_all_constants
42
53
  example_groups.clear
43
- @shared_example_group_registry = nil
54
+ @sources_by_path.clear if defined?(@sources_by_path)
55
+ @syntax_highlighter = nil
44
56
  end
45
57
 
46
58
  # @private
@@ -129,11 +141,18 @@ module RSpec
129
141
  end
130
142
 
131
143
  # @private
132
- def source_cache
133
- @source_cache ||= begin
134
- RSpec::Support.require_rspec_core "source"
135
- Source::Cache.new(@configuration)
144
+ def source_from_file(path)
145
+ unless defined?(@sources_by_path)
146
+ RSpec::Support.require_rspec_support 'source'
147
+ @sources_by_path = {}
136
148
  end
149
+
150
+ @sources_by_path[path] ||= Support::Source.from_file(path)
151
+ end
152
+
153
+ # @private
154
+ def syntax_highlighter
155
+ @syntax_highlighter ||= Formatters::SyntaxHighlighter.new(@configuration)
137
156
  end
138
157
 
139
158
  # @api private