rspec-core 3.7.1 → 3.9.3

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 (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +116 -0
  5. data/README.md +18 -18
  6. data/lib/rspec/core.rb +1 -0
  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 +138 -0
  10. data/lib/rspec/core/bisect/server.rb +5 -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 +236 -79
  15. data/lib/rspec/core/configuration_options.rb +41 -4
  16. data/lib/rspec/core/did_you_mean.rb +46 -0
  17. data/lib/rspec/core/example.rb +18 -8
  18. data/lib/rspec/core/example_group.rb +33 -16
  19. data/lib/rspec/core/filter_manager.rb +1 -1
  20. data/lib/rspec/core/formatters.rb +14 -6
  21. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  22. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  23. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +29 -16
  24. data/lib/rspec/core/formatters/deprecation_formatter.rb +3 -1
  25. data/lib/rspec/core/formatters/documentation_formatter.rb +35 -3
  26. data/lib/rspec/core/formatters/exception_presenter.rb +29 -6
  27. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  28. data/lib/rspec/core/formatters/html_printer.rb +0 -2
  29. data/lib/rspec/core/formatters/protocol.rb +17 -17
  30. data/lib/rspec/core/formatters/syntax_highlighter.rb +19 -19
  31. data/lib/rspec/core/hooks.rb +44 -24
  32. data/lib/rspec/core/invocations.rb +9 -7
  33. data/lib/rspec/core/memoized_helpers.rb +33 -14
  34. data/lib/rspec/core/metadata.rb +2 -3
  35. data/lib/rspec/core/option_parser.rb +10 -3
  36. data/lib/rspec/core/profiler.rb +3 -1
  37. data/lib/rspec/core/rake_task.rb +22 -2
  38. data/lib/rspec/core/reporter.rb +11 -6
  39. data/lib/rspec/core/runner.rb +25 -14
  40. data/lib/rspec/core/shared_example_group.rb +5 -5
  41. data/lib/rspec/core/shell_escape.rb +2 -2
  42. data/lib/rspec/core/version.rb +1 -1
  43. data/lib/rspec/core/world.rb +14 -1
  44. metadata +25 -15
  45. metadata.gz.sig +0 -0
  46. data/lib/rspec/core/formatters/bisect_formatter.rb +0 -69
@@ -4,8 +4,9 @@ require 'shellwords'
4
4
  module RSpec
5
5
  module Core
6
6
  # Responsible for utilizing externally provided configuration options,
7
- # whether via the command line, `.rspec`, `~/.rspec`, `.rspec-local`
8
- # or a custom options file.
7
+ # whether via the command line, `.rspec`, `~/.rspec`,
8
+ # `$XDG_CONFIG_HOME/rspec/options`, `.rspec-local` or a custom options
9
+ # file.
9
10
  class ConfigurationOptions
10
11
  # @param args [Array<String>] command line arguments
11
12
  def initialize(args)
@@ -118,7 +119,11 @@ module RSpec
118
119
  end
119
120
 
120
121
  def file_options
121
- custom_options_file ? [custom_options] : [global_options, project_options, local_options]
122
+ if custom_options_file
123
+ [custom_options]
124
+ else
125
+ [global_options, project_options, local_options]
126
+ end
122
127
  end
123
128
 
124
129
  def env_options
@@ -168,7 +173,11 @@ module RSpec
168
173
  end
169
174
 
170
175
  def options_file_as_erb_string(path)
171
- ERB.new(File.read(path), nil, '-').result(binding)
176
+ if RUBY_VERSION >= '2.6'
177
+ ERB.new(File.read(path), :trim_mode => '-').result(binding)
178
+ else
179
+ ERB.new(File.read(path), nil, '-').result(binding)
180
+ end
172
181
  end
173
182
 
174
183
  def custom_options_file
@@ -184,6 +193,17 @@ module RSpec
184
193
  end
185
194
 
186
195
  def global_options_file
196
+ xdg_options_file_if_exists || home_options_file_path
197
+ end
198
+
199
+ def xdg_options_file_if_exists
200
+ path = xdg_options_file_path
201
+ if path && File.exist?(path)
202
+ path
203
+ end
204
+ end
205
+
206
+ def home_options_file_path
187
207
  File.join(File.expand_path("~"), ".rspec")
188
208
  rescue ArgumentError
189
209
  # :nocov:
@@ -191,6 +211,23 @@ module RSpec
191
211
  nil
192
212
  # :nocov:
193
213
  end
214
+
215
+ def xdg_options_file_path
216
+ xdg_config_home = resolve_xdg_config_home
217
+ if xdg_config_home
218
+ File.join(xdg_config_home, "rspec", "options")
219
+ end
220
+ end
221
+
222
+ def resolve_xdg_config_home
223
+ File.expand_path(ENV.fetch("XDG_CONFIG_HOME", "~/.config"))
224
+ rescue ArgumentError
225
+ # :nocov:
226
+ # On Ruby 2.4, `File.expand("~")` works even if `ENV['HOME']` is not set.
227
+ # But on earlier versions, it fails.
228
+ nil
229
+ # :nocov:
230
+ end
194
231
  end
195
232
  end
196
233
  end
@@ -0,0 +1,46 @@
1
+ module RSpec
2
+ module Core
3
+ # @private
4
+ # Wrapper around Ruby's `DidYouMean::SpellChecker` when available to provide file name suggestions.
5
+ class DidYouMean
6
+ attr_reader :relative_file_name
7
+
8
+ def initialize(relative_file_name)
9
+ @relative_file_name = relative_file_name
10
+ end
11
+
12
+ if defined?(::DidYouMean::SpellChecker)
13
+ # provide probable suggestions
14
+ def call
15
+ checker = ::DidYouMean::SpellChecker.new(:dictionary => Dir["spec/**/*.rb"])
16
+ probables = checker.correct(relative_file_name.sub('./', ''))[0..2]
17
+ return '' unless probables.any?
18
+
19
+ formats probables
20
+ end
21
+ else
22
+ # return a hint if API for ::DidYouMean::SpellChecker not supported
23
+ def call
24
+ "\nHint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files."
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def formats(probables)
31
+ rspec_format = probables.map { |s, _| "rspec ./#{s}" }
32
+ red_font(top_and_tail rspec_format)
33
+ end
34
+
35
+ def top_and_tail(rspec_format)
36
+ spaces = ' ' * 20
37
+ rspec_format.insert(0, ' - Did you mean?').join("\n#{spaces}") + "\n"
38
+ end
39
+
40
+ def red_font(mytext)
41
+ colorizer = ::RSpec::Core::Formatters::ConsoleCodes
42
+ colorizer.wrap mytext, :failure
43
+ end
44
+ end
45
+ end
46
+ end
@@ -203,10 +203,13 @@ module RSpec
203
203
  description, example_block
204
204
  )
205
205
 
206
+ config = RSpec.configuration
207
+ config.apply_derived_metadata_to(@metadata)
208
+
206
209
  # This should perhaps be done in `Metadata::ExampleHash.create`,
207
210
  # but the logic there has no knowledge of `RSpec.world` and we
208
211
  # want to keep it that way. It's easier to just assign it here.
209
- @metadata[:last_run_status] = RSpec.configuration.last_run_statuses[id]
212
+ @metadata[:last_run_status] = config.last_run_statuses[id]
210
213
 
211
214
  @example_group_instance = @exception = nil
212
215
  @clock = RSpec::Core::Time
@@ -228,8 +231,13 @@ module RSpec
228
231
  @example_group_class
229
232
  end
230
233
 
231
- alias_method :pending?, :pending
232
- alias_method :skipped?, :skip
234
+ def pending?
235
+ !!pending
236
+ end
237
+
238
+ def skipped?
239
+ !!skip
240
+ end
233
241
 
234
242
  # @api private
235
243
  # instance_execs the block passed to the constructor in the context of
@@ -393,7 +401,7 @@ module RSpec
393
401
  end
394
402
  end
395
403
 
396
- # rubocop:disable Style/AccessorMethodName
404
+ # rubocop:disable Naming/AccessorMethodName
397
405
 
398
406
  # @private
399
407
  #
@@ -420,7 +428,7 @@ module RSpec
420
428
  self.display_exception = exception
421
429
  end
422
430
 
423
- # rubocop:enable Style/AccessorMethodName
431
+ # rubocop:enable Naming/AccessorMethodName
424
432
 
425
433
  # @private
426
434
  #
@@ -574,7 +582,9 @@ module RSpec
574
582
  # this indicates whether or not it now passes.
575
583
  attr_accessor :pending_fixed
576
584
 
577
- alias pending_fixed? pending_fixed
585
+ def pending_fixed?
586
+ !!pending_fixed
587
+ end
578
588
 
579
589
  # @return [Boolean] Indicates if the example was completely skipped
580
590
  # (typically done via `:skip` metadata or the `skip` method). Skipped examples
@@ -642,12 +652,12 @@ module RSpec
642
652
  @reporter = reporter
643
653
  end
644
654
 
645
- # rubocop:disable Style/AccessorMethodName
655
+ # rubocop:disable Naming/AccessorMethodName
646
656
  def set_exception(exception)
647
657
  reporter.notify_non_example_exception(exception, "An error occurred in #{description}.")
648
658
  RSpec.world.wants_to_quit = true
649
659
  end
650
- # rubocop:enable Style/AccessorMethodName
660
+ # rubocop:enable Naming/AccessorMethodName
651
661
  end
652
662
  end
653
663
  end
@@ -7,7 +7,7 @@ module RSpec
7
7
  # ExampleGroup and {Example} are the main structural elements of
8
8
  # rspec-core. Consider this example:
9
9
  #
10
- # describe Thing do
10
+ # RSpec.describe Thing do
11
11
  # it "does something" do
12
12
  # end
13
13
  # end
@@ -90,7 +90,7 @@ module RSpec
90
90
  # Returns the class or module passed to the `describe` method (or alias).
91
91
  # Returns nil if the subject is not a class or module.
92
92
  # @example
93
- # describe Thing do
93
+ # RSpec.describe Thing do
94
94
  # it "does something" do
95
95
  # described_class == Thing
96
96
  # end
@@ -107,19 +107,18 @@ module RSpec
107
107
  # @private
108
108
  # @macro [attach] define_example_method
109
109
  # @!scope class
110
+ # @method $1
110
111
  # @overload $1
111
112
  # @overload $1(&example_implementation)
112
113
  # @param example_implementation [Block] The implementation of the example.
113
- # @overload $1(doc_string, *metadata_keys, metadata={})
114
+ # @overload $1(doc_string, *metadata)
114
115
  # @param doc_string [String] The example's doc string.
115
- # @param metadata [Hash] Metadata for the example.
116
- # @param metadata_keys [Array<Symbol>] Metadata tags for the example.
117
- # Will be transformed into hash entries with `true` values.
118
- # @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
116
+ # @param metadata [Array<Symbol>, Hash] Metadata for the example.
117
+ # Symbols will be transformed into hash entries with `true` values.
118
+ # @overload $1(doc_string, *metadata, &example_implementation)
119
119
  # @param doc_string [String] The example's doc string.
120
- # @param metadata [Hash] Metadata for the example.
121
- # @param metadata_keys [Array<Symbol>] Metadata tags for the example.
122
- # Will be transformed into hash entries with `true` values.
120
+ # @param metadata [Array<Symbol>, Hash] Metadata for the example.
121
+ # Symbols will be transformed into hash entries with `true` values.
123
122
  # @param example_implementation [Block] The implementation of the example.
124
123
  # @yield [Example] the example object
125
124
  # @example
@@ -138,6 +137,11 @@ module RSpec
138
137
  # $1 "does something" do |ex|
139
138
  # # ex is the Example object that contains metadata about the example
140
139
  # end
140
+ #
141
+ # @example
142
+ # $1 "does something", :slow, :load_factor => 100 do
143
+ # end
144
+ #
141
145
  def self.define_example_method(name, extra_options={})
142
146
  idempotently_define_singleton_method(name) do |*all_args, &block|
143
147
  desc, *args = *all_args
@@ -203,11 +207,10 @@ module RSpec
203
207
  # @overload $1
204
208
  # @overload $1(&example_group_definition)
205
209
  # @param example_group_definition [Block] The definition of the example group.
206
- # @overload $1(doc_string, *metadata_keys, metadata={}, &example_implementation)
210
+ # @overload $1(doc_string, *metadata, &example_implementation)
207
211
  # @param doc_string [String] The group's doc string.
208
- # @param metadata [Hash] Metadata for the group.
209
- # @param metadata_keys [Array<Symbol>] Metadata tags for the group.
210
- # Will be transformed into hash entries with `true` values.
212
+ # @param metadata [Array<Symbol>, Hash] Metadata for the group.
213
+ # Symbols will be transformed into hash entries with `true` values.
211
214
  # @param example_group_definition [Block] The definition of the example group.
212
215
  #
213
216
  # Generates a subclass of this example group which inherits
@@ -222,12 +225,21 @@ module RSpec
222
225
  # do_something_before
223
226
  # end
224
227
  #
228
+ # before(:example, :clean_env) do
229
+ # env.clear!
230
+ # end
231
+ #
225
232
  # let(:thing) { Thing.new }
226
233
  #
227
234
  # $1 "attribute (of something)" do
228
235
  # # examples in the group get the before hook
229
236
  # # declared above, and can access `thing`
230
237
  # end
238
+ #
239
+ # $1 "needs additional setup", :clean_env, :implementation => JSON do
240
+ # # specifies that hooks with matching metadata
241
+ # # should be be run additionally
242
+ # end
231
243
  # end
232
244
  #
233
245
  # @see DSL#describe
@@ -423,11 +435,15 @@ module RSpec
423
435
  superclass.method(:next_runnable_index_for),
424
436
  description, *args, &example_group_block
425
437
  )
438
+
439
+ config = RSpec.configuration
440
+ config.apply_derived_metadata_to(@metadata)
441
+
426
442
  ExampleGroups.assign_const(self)
427
443
 
428
444
  @currently_executing_a_context_hook = false
429
445
 
430
- RSpec.configuration.configure_group(self)
446
+ config.configure_group(self)
431
447
  end
432
448
 
433
449
  # @private
@@ -745,8 +761,9 @@ module RSpec
745
761
  "on an example group (e.g. a `describe` or `context` block)."
746
762
  end
747
763
 
748
- super
764
+ super(name, *args)
749
765
  end
766
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
750
767
  end
751
768
  # rubocop:enable Metrics/ClassLength
752
769
 
@@ -113,7 +113,7 @@ module RSpec
113
113
 
114
114
  # @private
115
115
  class FilterRules
116
- PROC_HEX_NUMBER = /0x[0-9a-f]+@/
116
+ PROC_HEX_NUMBER = /0x[0-9a-f]+@?/
117
117
  PROJECT_DIR = File.expand_path('.')
118
118
 
119
119
  attr_accessor :opposite
@@ -1,4 +1,5 @@
1
1
  RSpec::Support.require_rspec_support "directory_maker"
2
+
2
3
  # ## Built-in Formatters
3
4
  #
4
5
  # * progress (default) - Prints dots for passing examples, `F` for failures, `*`
@@ -72,8 +73,9 @@ module RSpec::Core::Formatters
72
73
  autoload :ProgressFormatter, 'rspec/core/formatters/progress_formatter'
73
74
  autoload :ProfileFormatter, 'rspec/core/formatters/profile_formatter'
74
75
  autoload :JsonFormatter, 'rspec/core/formatters/json_formatter'
75
- autoload :BisectFormatter, 'rspec/core/formatters/bisect_formatter'
76
+ autoload :BisectDRbFormatter, 'rspec/core/formatters/bisect_drb_formatter'
76
77
  autoload :ExceptionPresenter, 'rspec/core/formatters/exception_presenter'
78
+ autoload :FailureListFormatter, 'rspec/core/formatters/failure_list_formatter'
77
79
 
78
80
  # Register the formatter class
79
81
  # @param formatter_class [Class] formatter class to register
@@ -133,9 +135,6 @@ module RSpec::Core::Formatters
133
135
  end
134
136
 
135
137
  return unless RSpec.configuration.profile_examples?
136
-
137
- @reporter.setup_profiler
138
-
139
138
  return if existing_formatter_implements?(:dump_profile)
140
139
 
141
140
  add RSpec::Core::Formatters::ProfileFormatter, output_stream
@@ -143,6 +142,13 @@ module RSpec::Core::Formatters
143
142
 
144
143
  # @private
145
144
  def add(formatter_to_use, *paths)
145
+ # If a formatter instance was passed, we can register it directly,
146
+ # with no need for any of the further processing that happens below.
147
+ if Loader.formatters.key?(formatter_to_use.class)
148
+ register formatter_to_use, notifications_for(formatter_to_use.class)
149
+ return
150
+ end
151
+
146
152
  formatter_class = find_formatter(formatter_to_use)
147
153
 
148
154
  args = paths.map { |p| p.respond_to?(:puts) ? p : open_stream(p) }
@@ -206,8 +212,10 @@ module RSpec::Core::Formatters
206
212
  ProgressFormatter
207
213
  when 'j', 'json'
208
214
  JsonFormatter
209
- when 'bisect'
210
- BisectFormatter
215
+ when 'bisect-drb'
216
+ BisectDRbFormatter
217
+ when 'f', 'failures'
218
+ FailureListFormatter
211
219
  end
212
220
  end
213
221
 
@@ -0,0 +1,45 @@
1
+ RSpec::Support.require_rspec_core "bisect/utilities"
2
+
3
+ module RSpec
4
+ module Core
5
+ module Formatters
6
+ # Contains common logic for formatters used by `--bisect` to communicate results
7
+ # back to the bisect runner.
8
+ #
9
+ # Subclasses must define a `notify_results(all_example_ids, failed_example_ids)`
10
+ # method.
11
+ # @private
12
+ class BaseBisectFormatter
13
+ def self.inherited(formatter)
14
+ Formatters.register formatter, :start_dump, :example_failed, :example_finished
15
+ end
16
+
17
+ def initialize(expected_failures)
18
+ @all_example_ids = []
19
+ @failed_example_ids = []
20
+ @remaining_failures = expected_failures
21
+ end
22
+
23
+ def example_failed(notification)
24
+ @failed_example_ids << notification.example.id
25
+ end
26
+
27
+ def example_finished(notification)
28
+ @all_example_ids << notification.example.id
29
+ return unless @remaining_failures.include?(notification.example.id)
30
+ @remaining_failures.delete(notification.example.id)
31
+
32
+ status = notification.example.execution_result.status
33
+ return if status == :failed && !@remaining_failures.empty?
34
+ RSpec.world.wants_to_quit = true
35
+ end
36
+
37
+ def start_dump(_notification)
38
+ # `notify_results` is defined in the subclass
39
+ notify_results(Bisect::ExampleSetDescriptor.new(
40
+ @all_example_ids, @failed_example_ids))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ require 'drb/drb'
2
+ RSpec::Support.require_rspec_core "formatters/base_bisect_formatter"
3
+
4
+ module RSpec
5
+ module Core
6
+ module Formatters
7
+ # Used by `--bisect`. When it shells out and runs a portion of the suite, it uses
8
+ # this formatter as a means to have the status reported back to it, via DRb.
9
+ #
10
+ # Note that since DRb calls carry considerable overhead compared to normal
11
+ # method calls, we try to minimize the number of DRb calls for perf reasons,
12
+ # opting to communicate only at the start and the end of the run, rather than
13
+ # after each example.
14
+ # @private
15
+ class BisectDRbFormatter < BaseBisectFormatter
16
+ def initialize(_output)
17
+ drb_uri = "druby://localhost:#{RSpec.configuration.drb_port}"
18
+ @bisect_server = DRbObject.new_with_uri(drb_uri)
19
+ RSpec.configuration.files_or_directories_to_run = @bisect_server.files_or_directories_to_run
20
+ super(Set.new(@bisect_server.expected_failures))
21
+ end
22
+
23
+ def notify_results(results)
24
+ @bisect_server.latest_run_results = results
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end