rspec-core 3.7.1 → 3.9.3

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