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.
- 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
|