rspec-core 3.0.4 → 3.12.2

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