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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Changelog.md +116 -0
- data/README.md +18 -18
- data/lib/rspec/core.rb +1 -0
- data/lib/rspec/core/bisect/coordinator.rb +26 -30
- data/lib/rspec/core/bisect/example_minimizer.rb +12 -8
- data/lib/rspec/core/bisect/fork_runner.rb +138 -0
- data/lib/rspec/core/bisect/server.rb +5 -14
- data/lib/rspec/core/bisect/{runner.rb → shell_command.rb} +27 -70
- data/lib/rspec/core/bisect/shell_runner.rb +73 -0
- data/lib/rspec/core/bisect/utilities.rb +58 -0
- data/lib/rspec/core/configuration.rb +236 -79
- data/lib/rspec/core/configuration_options.rb +41 -4
- data/lib/rspec/core/did_you_mean.rb +46 -0
- data/lib/rspec/core/example.rb +18 -8
- data/lib/rspec/core/example_group.rb +33 -16
- data/lib/rspec/core/filter_manager.rb +1 -1
- data/lib/rspec/core/formatters.rb +14 -6
- data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
- data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
- data/lib/rspec/core/formatters/bisect_progress_formatter.rb +29 -16
- data/lib/rspec/core/formatters/deprecation_formatter.rb +3 -1
- data/lib/rspec/core/formatters/documentation_formatter.rb +35 -3
- data/lib/rspec/core/formatters/exception_presenter.rb +29 -6
- data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
- data/lib/rspec/core/formatters/html_printer.rb +0 -2
- data/lib/rspec/core/formatters/protocol.rb +17 -17
- data/lib/rspec/core/formatters/syntax_highlighter.rb +19 -19
- data/lib/rspec/core/hooks.rb +44 -24
- data/lib/rspec/core/invocations.rb +9 -7
- data/lib/rspec/core/memoized_helpers.rb +33 -14
- data/lib/rspec/core/metadata.rb +2 -3
- data/lib/rspec/core/option_parser.rb +10 -3
- data/lib/rspec/core/profiler.rb +3 -1
- data/lib/rspec/core/rake_task.rb +22 -2
- data/lib/rspec/core/reporter.rb +11 -6
- data/lib/rspec/core/runner.rb +25 -14
- data/lib/rspec/core/shared_example_group.rb +5 -5
- data/lib/rspec/core/shell_escape.rb +2 -2
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/world.rb +14 -1
- metadata +25 -15
- metadata.gz.sig +0 -0
- 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`,
|
8
|
-
# or a custom options
|
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
|
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
|
-
|
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
|
data/lib/rspec/core/example.rb
CHANGED
@@ -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] =
|
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
|
-
|
232
|
-
|
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
|
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
|
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
|
-
|
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
|
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
|
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, *
|
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
|
-
#
|
117
|
-
#
|
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
|
-
#
|
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, *
|
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
|
-
#
|
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
|
-
|
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
|
|
@@ -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 :
|
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
|
-
|
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
|