rspec-core 3.0.4 → 3.12.2

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 (85) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +2 -1
  5. data/Changelog.md +888 -2
  6. data/{License.txt → LICENSE.md} +6 -5
  7. data/README.md +165 -24
  8. data/lib/rspec/autorun.rb +1 -0
  9. data/lib/rspec/core/backtrace_formatter.rb +19 -20
  10. data/lib/rspec/core/bisect/coordinator.rb +62 -0
  11. data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
  12. data/lib/rspec/core/bisect/fork_runner.rb +138 -0
  13. data/lib/rspec/core/bisect/server.rb +61 -0
  14. data/lib/rspec/core/bisect/shell_command.rb +126 -0
  15. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  16. data/lib/rspec/core/bisect/utilities.rb +69 -0
  17. data/lib/rspec/core/configuration.rb +1287 -246
  18. data/lib/rspec/core/configuration_options.rb +95 -35
  19. data/lib/rspec/core/did_you_mean.rb +46 -0
  20. data/lib/rspec/core/drb.rb +21 -12
  21. data/lib/rspec/core/dsl.rb +10 -6
  22. data/lib/rspec/core/example.rb +305 -113
  23. data/lib/rspec/core/example_group.rb +431 -223
  24. data/lib/rspec/core/example_status_persister.rb +235 -0
  25. data/lib/rspec/core/filter_manager.rb +86 -115
  26. data/lib/rspec/core/flat_map.rb +6 -4
  27. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  28. data/lib/rspec/core/formatters/base_formatter.rb +14 -116
  29. data/lib/rspec/core/formatters/base_text_formatter.rb +18 -21
  30. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  31. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
  32. data/lib/rspec/core/formatters/console_codes.rb +29 -18
  33. data/lib/rspec/core/formatters/deprecation_formatter.rb +16 -16
  34. data/lib/rspec/core/formatters/documentation_formatter.rb +49 -16
  35. data/lib/rspec/core/formatters/exception_presenter.rb +525 -0
  36. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  37. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  38. data/lib/rspec/core/formatters/helpers.rb +45 -15
  39. data/lib/rspec/core/formatters/html_formatter.rb +33 -28
  40. data/lib/rspec/core/formatters/html_printer.rb +30 -20
  41. data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
  42. data/lib/rspec/core/formatters/json_formatter.rb +18 -9
  43. data/lib/rspec/core/formatters/profile_formatter.rb +10 -9
  44. data/lib/rspec/core/formatters/progress_formatter.rb +5 -4
  45. data/lib/rspec/core/formatters/protocol.rb +182 -0
  46. data/lib/rspec/core/formatters/snippet_extractor.rb +113 -82
  47. data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
  48. data/lib/rspec/core/formatters.rb +81 -41
  49. data/lib/rspec/core/hooks.rb +314 -244
  50. data/lib/rspec/core/invocations.rb +87 -0
  51. data/lib/rspec/core/memoized_helpers.rb +161 -51
  52. data/lib/rspec/core/metadata.rb +132 -61
  53. data/lib/rspec/core/metadata_filter.rb +224 -64
  54. data/lib/rspec/core/minitest_assertions_adapter.rb +6 -3
  55. data/lib/rspec/core/mocking_adapters/flexmock.rb +4 -2
  56. data/lib/rspec/core/mocking_adapters/mocha.rb +11 -9
  57. data/lib/rspec/core/mocking_adapters/null.rb +2 -0
  58. data/lib/rspec/core/mocking_adapters/rr.rb +3 -1
  59. data/lib/rspec/core/mocking_adapters/rspec.rb +3 -1
  60. data/lib/rspec/core/notifications.rb +192 -206
  61. data/lib/rspec/core/option_parser.rb +174 -69
  62. data/lib/rspec/core/ordering.rb +48 -35
  63. data/lib/rspec/core/output_wrapper.rb +29 -0
  64. data/lib/rspec/core/pending.rb +25 -33
  65. data/lib/rspec/core/profiler.rb +34 -0
  66. data/lib/rspec/core/project_initializer/.rspec +0 -2
  67. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +59 -39
  68. data/lib/rspec/core/project_initializer.rb +5 -3
  69. data/lib/rspec/core/rake_task.rb +99 -55
  70. data/lib/rspec/core/reporter.rb +128 -15
  71. data/lib/rspec/core/ruby_project.rb +14 -6
  72. data/lib/rspec/core/runner.rb +96 -45
  73. data/lib/rspec/core/sandbox.rb +37 -0
  74. data/lib/rspec/core/set.rb +54 -0
  75. data/lib/rspec/core/shared_example_group.rb +133 -43
  76. data/lib/rspec/core/shell_escape.rb +49 -0
  77. data/lib/rspec/core/test_unit_assertions_adapter.rb +4 -4
  78. data/lib/rspec/core/version.rb +1 -1
  79. data/lib/rspec/core/warnings.rb +6 -6
  80. data/lib/rspec/core/world.rb +172 -68
  81. data/lib/rspec/core.rb +66 -21
  82. data.tar.gz.sig +0 -0
  83. metadata +93 -69
  84. metadata.gz.sig +0 -0
  85. data/lib/rspec/core/backport_random.rb +0 -336
@@ -0,0 +1,87 @@
1
+ module RSpec
2
+ module Core
3
+ # @private
4
+ module Invocations
5
+ # @private
6
+ class InitializeProject
7
+ def call(*_args)
8
+ RSpec::Support.require_rspec_core "project_initializer"
9
+ ProjectInitializer.new.run
10
+ 0
11
+ end
12
+ end
13
+
14
+ # @private
15
+ class DRbWithFallback
16
+ def call(options, err, out)
17
+ require 'rspec/core/drb'
18
+ begin
19
+ return DRbRunner.new(options).run(err, out)
20
+ rescue DRb::DRbConnError
21
+ err.puts "No DRb server is running. Running in local process instead ..."
22
+ end
23
+ RSpec::Core::Runner.new(options).run(err, out)
24
+ end
25
+ end
26
+
27
+ # @private
28
+ class Bisect
29
+ def call(options, err, out)
30
+ RSpec::Support.require_rspec_core "bisect/coordinator"
31
+ runner = Runner.new(options).tap { |r| r.configure(err, out) }
32
+ formatter = bisect_formatter_klass_for(options.options[:bisect]).new(
33
+ out, runner.configuration.bisect_runner
34
+ )
35
+
36
+ success = RSpec::Core::Bisect::Coordinator.bisect_with(
37
+ runner, options.args, formatter
38
+ )
39
+
40
+ runner.exit_code(success)
41
+ end
42
+
43
+ private
44
+
45
+ def bisect_formatter_klass_for(argument)
46
+ return Formatters::BisectDebugFormatter if argument == "verbose"
47
+ Formatters::BisectProgressFormatter
48
+ end
49
+ end
50
+
51
+ # @private
52
+ class PrintVersion
53
+ def call(_options, _err, out)
54
+ overall_version = RSpec::Core::Version::STRING
55
+ unless overall_version =~ /[a-zA-Z]+/
56
+ overall_version = overall_version.split('.').first(2).join('.')
57
+ end
58
+
59
+ out.puts "RSpec #{overall_version}"
60
+
61
+ [:Core, :Expectations, :Mocks, :Rails, :Support].each do |const_name|
62
+ lib_name = const_name.to_s.downcase
63
+ begin
64
+ require "rspec/#{lib_name}/version"
65
+ rescue LoadError
66
+ # Not worth mentioning libs that are not installed
67
+ nil
68
+ else
69
+ out.puts " - rspec-#{lib_name} #{RSpec.const_get(const_name)::Version::STRING}"
70
+ end
71
+ end
72
+
73
+ 0
74
+ end
75
+ end
76
+
77
+ # @private
78
+ PrintHelp = Struct.new(:parser, :hidden_options) do
79
+ def call(_options, _err, out)
80
+ # Removing the hidden options from the output.
81
+ out.puts parser.to_s.gsub(/^\s+(#{hidden_options.join('|')})\b.*$\n/, '')
82
+ 0
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,3 +1,5 @@
1
+ RSpec::Support.require_rspec_support 'reentrant_mutex'
2
+
1
3
  module RSpec
2
4
  module Core
3
5
  # This module is included in {ExampleGroup}, making the methods
@@ -8,7 +10,7 @@ module RSpec
8
10
  # @note `subject` was contributed by Joe Ferris to support the one-liner
9
11
  # syntax embraced by shoulda matchers:
10
12
  #
11
- # describe Widget do
13
+ # RSpec.describe Widget do
12
14
  # it { is_expected.to validate_presence_of(:name) }
13
15
  # # or
14
16
  # it { should validate_presence_of(:name) }
@@ -20,8 +22,8 @@ module RSpec
20
22
  #
21
23
  # @example
22
24
  #
23
- # # explicit declaration of subject
24
- # describe Person do
25
+ # # Explicit declaration of subject.
26
+ # RSpec.describe Person do
25
27
  # subject { Person.new(:birthdate => 19.years.ago) }
26
28
  # it "should be eligible to vote" do
27
29
  # subject.should be_eligible_to_vote
@@ -29,35 +31,33 @@ module RSpec
29
31
  # end
30
32
  # end
31
33
  #
32
- # # implicit subject => { Person.new }
33
- # describe Person do
34
+ # # Implicit subject => { Person.new }.
35
+ # RSpec.describe Person do
34
36
  # it "should be eligible to vote" do
35
37
  # subject.should be_eligible_to_vote
36
38
  # # ^ ^ explicit reference to subject not recommended
37
39
  # end
38
40
  # end
39
41
  #
40
- # # one-liner syntax - expectation is set on the subject
41
- # describe Person do
42
+ # # One-liner syntax - expectation is set on the subject.
43
+ # RSpec.describe Person do
42
44
  # it { is_expected.to be_eligible_to_vote }
43
45
  # # or
44
46
  # it { should be_eligible_to_vote }
45
47
  # end
46
48
  #
47
- # @note Because `subject` is designed to create state that is reset between
48
- # each example, and `before(:context)` is designed to setup state that is
49
- # shared across _all_ examples in an example group, `subject` is _not_
50
- # intended to be used in a `before(:context)` hook.
49
+ # @note Because `subject` is designed to create state that is reset
50
+ # between each example, and `before(:context)` is designed to setup
51
+ # state that is shared across _all_ examples in an example group,
52
+ # `subject` is _not_ intended to be used in a `before(:context)` hook.
51
53
  #
52
54
  # @see #should
53
55
  # @see #should_not
54
56
  # @see #is_expected
55
57
  def subject
56
- __memoized.fetch(:subject) do
57
- __memoized[:subject] = begin
58
- described = described_class || self.class.description
59
- Class === described ? described.new : described
60
- end
58
+ __memoized.fetch_or_store(:subject) do
59
+ described = described_class || self.class.metadata.fetch(:description_args).first
60
+ Class === described ? described.new : described
61
61
  end
62
62
  end
63
63
 
@@ -67,7 +67,7 @@ module RSpec
67
67
  #
68
68
  # @example
69
69
  #
70
- # describe Person do
70
+ # RSpec.describe Person do
71
71
  # it { should be_eligible_to_vote }
72
72
  # end
73
73
  #
@@ -78,6 +78,7 @@ module RSpec
78
78
  # @note If you are using RSpec's newer expect-based syntax you may
79
79
  # want to use `is_expected.to` instead of `should`.
80
80
  def should(matcher=nil, message=nil)
81
+ enforce_value_expectation(matcher, 'should')
81
82
  RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message)
82
83
  end
83
84
 
@@ -86,7 +87,7 @@ module RSpec
86
87
  #
87
88
  # @example
88
89
  #
89
- # describe Person do
90
+ # RSpec.describe Person do
90
91
  # it { should_not be_eligible_to_vote }
91
92
  # end
92
93
  #
@@ -97,6 +98,7 @@ module RSpec
97
98
  # @note If you are using RSpec's newer expect-based syntax you may
98
99
  # want to use `is_expected.to_not` instead of `should_not`.
99
100
  def should_not(matcher=nil, message=nil)
101
+ enforce_value_expectation(matcher, 'should_not')
100
102
  RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message)
101
103
  end
102
104
 
@@ -119,38 +121,110 @@ module RSpec
119
121
  expect(subject)
120
122
  end
121
123
 
124
+ # @private
125
+ # should just be placed in private section,
126
+ # but Ruby issues warnings on private attributes.
127
+ # and expanding it to the equivalent method upsets Rubocop,
128
+ # b/c it should obviously be a reader
129
+ attr_reader :__memoized
130
+ private :__memoized
131
+
122
132
  private
123
133
 
124
134
  # @private
125
- def __memoized
126
- @__memoized ||= {}
135
+ def initialize(*)
136
+ __init_memoized
137
+ super
138
+ end
139
+
140
+ # @private
141
+ def __init_memoized
142
+ @__memoized = if RSpec.configuration.threadsafe?
143
+ ThreadsafeMemoized.new
144
+ else
145
+ NonThreadSafeMemoized.new
146
+ end
147
+ end
148
+
149
+ # @private
150
+ def enforce_value_expectation(matcher, method_name)
151
+ return if matcher_supports_value_expectations?(matcher)
152
+
153
+ RSpec.deprecate(
154
+ "#{method_name} #{RSpec::Support::ObjectFormatter.format(matcher)}",
155
+ :message =>
156
+ "The implicit block expectation syntax is deprecated, you should pass " \
157
+ "a block to `expect` to use the provided block expectation matcher " \
158
+ "(#{RSpec::Support::ObjectFormatter.format(matcher)}), " \
159
+ "or the matcher must implement `supports_value_expectations?`."
160
+ )
161
+ end
162
+
163
+ def matcher_supports_value_expectations?(matcher)
164
+ matcher.supports_value_expectations?
165
+ rescue
166
+ true
167
+ end
168
+
169
+ # @private
170
+ class ThreadsafeMemoized
171
+ def initialize
172
+ @memoized = {}
173
+ @mutex = Support::ReentrantMutex.new
174
+ end
175
+
176
+ def fetch_or_store(key)
177
+ @memoized.fetch(key) do # only first access pays for synchronization
178
+ @mutex.synchronize do
179
+ @memoized.fetch(key) { @memoized[key] = yield }
180
+ end
181
+ end
182
+ end
183
+ end
184
+
185
+ # @private
186
+ class NonThreadSafeMemoized
187
+ def initialize
188
+ @memoized = {}
189
+ end
190
+
191
+ def fetch_or_store(key)
192
+ @memoized.fetch(key) { @memoized[key] = yield }
193
+ end
127
194
  end
128
195
 
129
196
  # Used internally to customize the behavior of the
130
197
  # memoized hash when used in a `before(:context)` hook.
131
198
  #
132
199
  # @private
133
- class ContextHookMemoizedHash
200
+ class ContextHookMemoized
134
201
  def self.isolate_for_context_hook(example_group_instance)
135
- hash = self
202
+ exploding_memoized = self
136
203
 
137
204
  example_group_instance.instance_exec do
138
- @__memoized = hash
205
+ @__memoized = exploding_memoized
139
206
 
140
207
  begin
141
208
  yield
142
209
  ensure
143
- @__memoized = nil
210
+ # This is doing a reset instead of just isolating for context hook.
211
+ # Really, this should set the old @__memoized back into place.
212
+ #
213
+ # Caller is the before and after context hooks
214
+ # which are both called from self.run
215
+ # I didn't look at why it made tests fail, maybe an object was getting reused in RSpec tests,
216
+ # if so, then that probably already works, and its the tests that are wrong.
217
+ __init_memoized
144
218
  end
145
219
  end
146
220
  end
147
221
 
148
- def self.fetch(key, &block)
222
+ def self.fetch_or_store(key, &_block)
149
223
  description = if key == :subject
150
- "subject"
151
- else
152
- "let declaration `#{key}`"
153
- end
224
+ "subject"
225
+ else
226
+ "let declaration `#{key}`"
227
+ end
154
228
 
155
229
  raise <<-EOS
156
230
  #{description} accessed in #{article} #{hook_expression} hook at:
@@ -206,25 +280,26 @@ EOS
206
280
  # maybe 3 declarations) in any given example group, but that can
207
281
  # quickly degrade with overuse. YMMV.
208
282
  #
209
- # @note `let` uses an `||=` conditional that has the potential to
210
- # behave in surprising ways in examples that spawn separate threads,
211
- # though we have yet to see this in practice. You've been warned.
283
+ # @note `let` can be configured to be threadsafe or not.
284
+ # If it is threadsafe, it will take longer to access the value.
285
+ # If it is not threadsafe, it may behave in surprising ways in examples
286
+ # that spawn separate threads. Specify this on `RSpec.configure`
212
287
  #
213
288
  # @note Because `let` is designed to create state that is reset between
214
- # each example, and `before(:context)` is designed to setup state that is
215
- # shared across _all_ examples in an example group, `let` is _not_
289
+ # each example, and `before(:context)` is designed to setup state that
290
+ # is shared across _all_ examples in an example group, `let` is _not_
216
291
  # intended to be used in a `before(:context)` hook.
217
292
  #
218
293
  # @example
219
294
  #
220
- # describe Thing do
295
+ # RSpec.describe Thing do
221
296
  # let(:thing) { Thing.new }
222
297
  #
223
298
  # it "does something" do
224
- # # first invocation, executes block, memoizes and returns result
299
+ # # First invocation, executes block, memoizes and returns result.
225
300
  # thing.do_something
226
301
  #
227
- # # second invocation, returns the memoized value
302
+ # # Second invocation, returns the memoized value.
228
303
  # thing.should be_something
229
304
  # end
230
305
  # end
@@ -232,14 +307,40 @@ EOS
232
307
  # We have to pass the block directly to `define_method` to
233
308
  # allow it to use method constructs like `super` and `return`.
234
309
  raise "#let or #subject called without a block" if block.nil?
235
- MemoizedHelpers.module_for(self).__send__(:define_method, name, &block)
310
+
311
+ # A list of reserved words that can't be used as a name for a memoized helper
312
+ # Matches for both symbols and passed strings
313
+ if [:initialize, :to_s].include?(name.to_sym)
314
+ raise ArgumentError, "#let or #subject called with reserved name `#{name}`"
315
+ end
316
+
317
+ our_module = MemoizedHelpers.module_for(self)
318
+
319
+ # If we have a module clash in our helper module
320
+ # then we need to remove it to prevent a warning.
321
+ #
322
+ # Note we do not check ancestor modules (see: `instance_methods(false)`)
323
+ # as we can override them.
324
+ if our_module.instance_methods(false).include?(name)
325
+ our_module.__send__(:remove_method, name)
326
+ end
327
+ our_module.__send__(:define_method, name, &block)
328
+
329
+ # If we have a module clash in the example module
330
+ # then we need to remove it to prevent a warning.
331
+ #
332
+ # Note we do not check ancestor modules (see: `instance_methods(false)`)
333
+ # as we can override them.
334
+ if instance_methods(false).include?(name)
335
+ remove_method(name)
336
+ end
236
337
 
237
338
  # Apply the memoization. The method has been defined in an ancestor
238
339
  # module so we can use `super` here to get the value.
239
340
  if block.arity == 1
240
- define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = super(RSpec.current_example, &nil) } }
341
+ define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } }
241
342
  else
242
- define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = super(&nil) } }
343
+ define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } }
243
344
  end
244
345
  end
245
346
 
@@ -267,7 +368,7 @@ EOS
267
368
  # end
268
369
  # end
269
370
  #
270
- # describe Thing do
371
+ # RSpec.describe Thing do
271
372
  # after(:example) { Thing.reset_count }
272
373
  #
273
374
  # context "using let" do
@@ -302,27 +403,34 @@ EOS
302
403
  end
303
404
 
304
405
  # Declares a `subject` for an example group which can then be wrapped
305
- # with `expect` using `is_expected` to make it the target of an expectation
306
- # in a concise, one-line example.
406
+ # with `expect` using `is_expected` to make it the target of an
407
+ # expectation in a concise, one-line example.
307
408
  #
308
409
  # Given a `name`, defines a method with that name which returns the
309
410
  # `subject`. This lets you declare the subject once and access it
310
411
  # implicitly in one-liners and explicitly using an intention revealing
311
412
  # name.
312
413
  #
414
+ # When given a `name`, calling `super` in the block is not supported.
415
+ #
416
+ # @note `subject` can be configured to be threadsafe or not.
417
+ # If it is threadsafe, it will take longer to access the value.
418
+ # If it is not threadsafe, it may behave in surprising ways in examples
419
+ # that spawn separate threads. Specify this on `RSpec.configure`
420
+ #
313
421
  # @param name [String,Symbol] used to define an accessor with an
314
422
  # intention revealing name
315
423
  # @param block defines the value to be returned by `subject` in examples
316
424
  #
317
425
  # @example
318
426
  #
319
- # describe CheckingAccount, "with $50" do
427
+ # RSpec.describe CheckingAccount, "with $50" do
320
428
  # subject { CheckingAccount.new(Money.new(50, :USD)) }
321
429
  # it { is_expected.to have_a_balance_of(Money.new(50, :USD)) }
322
430
  # it { is_expected.not_to be_overdrawn }
323
431
  # end
324
432
  #
325
- # describe CheckingAccount, "with a non-zero starting balance" do
433
+ # RSpec.describe CheckingAccount, "with a non-zero starting balance" do
326
434
  # subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
327
435
  # it { is_expected.not_to be_overdrawn }
328
436
  # it "has a balance equal to the starting balance" do
@@ -346,9 +454,9 @@ EOS
346
454
  end
347
455
  end
348
456
 
349
- # Just like `subject`, except the block is invoked by an implicit `before`
350
- # hook. This serves a dual purpose of setting up state and providing a
351
- # memoized reference to that state.
457
+ # Just like `subject`, except the block is invoked by an implicit
458
+ # `before` hook. This serves a dual purpose of setting up state and
459
+ # providing a memoized reference to that state.
352
460
  #
353
461
  # @example
354
462
  #
@@ -370,7 +478,7 @@ EOS
370
478
  # end
371
479
  # end
372
480
  #
373
- # describe Thing do
481
+ # RSpec.describe Thing do
374
482
  # after(:example) { Thing.reset_count }
375
483
  #
376
484
  # context "using subject" do
@@ -420,9 +528,9 @@ EOS
420
528
  def self.module_for(example_group)
421
529
  get_constant_or_yield(example_group, :LetDefinitions) do
422
530
  mod = Module.new do
423
- include Module.new {
531
+ include(Module.new {
424
532
  example_group.const_set(:NamedSubjectPreventSuper, self)
425
- }
533
+ })
426
534
  end
427
535
 
428
536
  example_group.const_set(:LetDefinitions, mod)
@@ -441,6 +549,7 @@ EOS
441
549
  # Gets the named constant or yields.
442
550
  # On 1.8, const_defined? / const_get do not take into
443
551
  # account the inheritance hierarchy.
552
+ # :nocov:
444
553
  def self.get_constant_or_yield(example_group, name)
445
554
  if example_group.const_defined?(name)
446
555
  example_group.const_get(name)
@@ -448,6 +557,7 @@ EOS
448
557
  yield
449
558
  end
450
559
  end
560
+ # :nocov:
451
561
  else
452
562
  # @private
453
563
  #