rspec-core 3.5.4 → 3.9.0

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