rspec-expectations 3.0.4 → 3.12.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +530 -5
- data/{License.txt → LICENSE.md} +5 -4
- data/README.md +73 -31
- data/lib/rspec/expectations/block_snippet_extractor.rb +253 -0
- data/lib/rspec/expectations/configuration.rb +96 -1
- data/lib/rspec/expectations/expectation_target.rb +82 -38
- data/lib/rspec/expectations/fail_with.rb +11 -6
- data/lib/rspec/expectations/failure_aggregator.rb +229 -0
- data/lib/rspec/expectations/handler.rb +36 -15
- data/lib/rspec/expectations/minitest_integration.rb +43 -2
- data/lib/rspec/expectations/syntax.rb +5 -5
- data/lib/rspec/expectations/version.rb +1 -1
- data/lib/rspec/expectations.rb +15 -1
- data/lib/rspec/matchers/aliased_matcher.rb +79 -4
- data/lib/rspec/matchers/built_in/all.rb +11 -0
- data/lib/rspec/matchers/built_in/base_matcher.rb +111 -28
- data/lib/rspec/matchers/built_in/be.rb +28 -114
- data/lib/rspec/matchers/built_in/be_between.rb +1 -1
- data/lib/rspec/matchers/built_in/be_instance_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_kind_of.rb +5 -1
- data/lib/rspec/matchers/built_in/be_within.rb +5 -12
- data/lib/rspec/matchers/built_in/change.rb +171 -63
- data/lib/rspec/matchers/built_in/compound.rb +201 -30
- data/lib/rspec/matchers/built_in/contain_exactly.rb +73 -12
- data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
- data/lib/rspec/matchers/built_in/eq.rb +3 -38
- data/lib/rspec/matchers/built_in/eql.rb +2 -2
- data/lib/rspec/matchers/built_in/equal.rb +3 -3
- data/lib/rspec/matchers/built_in/exist.rb +7 -3
- data/lib/rspec/matchers/built_in/has.rb +93 -30
- data/lib/rspec/matchers/built_in/have_attributes.rb +114 -0
- data/lib/rspec/matchers/built_in/include.rb +133 -25
- data/lib/rspec/matchers/built_in/match.rb +79 -2
- data/lib/rspec/matchers/built_in/operators.rb +14 -5
- data/lib/rspec/matchers/built_in/output.rb +59 -2
- data/lib/rspec/matchers/built_in/raise_error.rb +130 -27
- data/lib/rspec/matchers/built_in/respond_to.rb +117 -15
- data/lib/rspec/matchers/built_in/satisfy.rb +28 -14
- data/lib/rspec/matchers/built_in/{start_and_end_with.rb → start_or_end_with.rb} +20 -8
- data/lib/rspec/matchers/built_in/throw_symbol.rb +15 -5
- data/lib/rspec/matchers/built_in/yield.rb +129 -156
- data/lib/rspec/matchers/built_in.rb +5 -3
- data/lib/rspec/matchers/composable.rb +24 -36
- data/lib/rspec/matchers/dsl.rb +203 -37
- data/lib/rspec/matchers/english_phrasing.rb +58 -0
- data/lib/rspec/matchers/expecteds_for_multiple_diffs.rb +82 -0
- data/lib/rspec/matchers/fail_matchers.rb +42 -0
- data/lib/rspec/matchers/generated_descriptions.rb +1 -2
- data/lib/rspec/matchers/matcher_delegator.rb +3 -4
- data/lib/rspec/matchers/matcher_protocol.rb +105 -0
- data/lib/rspec/matchers.rb +267 -144
- data.tar.gz.sig +0 -0
- metadata +71 -49
- metadata.gz.sig +0 -0
- data/lib/rspec/matchers/pretty.rb +0 -77
data/lib/rspec/matchers.rb
CHANGED
@@ -1,31 +1,24 @@
|
|
1
1
|
require 'rspec/support'
|
2
|
+
RSpec::Support.require_rspec_support 'matcher_definition'
|
2
3
|
RSpec::Support.define_optimized_require_for_rspec(:matchers) { |f| require_relative(f) }
|
3
4
|
|
4
5
|
%w[
|
5
|
-
|
6
|
+
english_phrasing
|
6
7
|
composable
|
7
8
|
built_in
|
8
9
|
generated_descriptions
|
9
10
|
dsl
|
10
11
|
matcher_delegator
|
11
12
|
aliased_matcher
|
13
|
+
expecteds_for_multiple_diffs
|
12
14
|
].each { |file| RSpec::Support.require_rspec_matchers(file) }
|
13
15
|
|
14
16
|
# RSpec's top level namespace. All of rspec-expectations is contained
|
15
17
|
# in the `RSpec::Expectations` and `RSpec::Matchers` namespaces.
|
16
18
|
module RSpec
|
17
19
|
# RSpec::Matchers provides a number of useful matchers we use to define
|
18
|
-
# expectations.
|
19
|
-
#
|
20
|
-
# matches?(actual)
|
21
|
-
# failure_message
|
22
|
-
#
|
23
|
-
# These methods are also part of the matcher protocol, but are optional:
|
24
|
-
#
|
25
|
-
# does_not_match?(actual)
|
26
|
-
# failure_message_when_negated
|
27
|
-
# description
|
28
|
-
# supports_block_expectations?
|
20
|
+
# expectations. Any object that implements the [matcher protocol](Matchers/MatcherProtocol)
|
21
|
+
# can be used as a matcher.
|
29
22
|
#
|
30
23
|
# ## Predicates
|
31
24
|
#
|
@@ -43,14 +36,14 @@ module RSpec
|
|
43
36
|
# expect([]).to be_empty # => [].empty?() | passes
|
44
37
|
# expect([]).not_to be_empty # => [].empty?() | fails
|
45
38
|
#
|
46
|
-
# In
|
39
|
+
# In addition to prefixing the predicate matchers with "be_", you can also use "be_a_"
|
47
40
|
# and "be_an_", making your specs read much more naturally:
|
48
41
|
#
|
49
42
|
# expect("a string").to be_an_instance_of(String) # =>"a string".instance_of?(String) # passes
|
50
43
|
#
|
51
|
-
# expect(3).to be_a_kind_of(
|
52
|
-
# expect(3).to be_a_kind_of(Numeric)
|
53
|
-
# expect(3).to be_an_instance_of(
|
44
|
+
# expect(3).to be_a_kind_of(Integer) # => 3.kind_of?(Numeric) | passes
|
45
|
+
# expect(3).to be_a_kind_of(Numeric) # => 3.kind_of?(Numeric) | passes
|
46
|
+
# expect(3).to be_an_instance_of(Integer) # => 3.instance_of?(Integer) | passes
|
54
47
|
# expect(3).not_to be_an_instance_of(Numeric) # => 3.instance_of?(Numeric) | fails
|
55
48
|
#
|
56
49
|
# RSpec will also create custom matchers for predicates like `has_key?`. To
|
@@ -69,6 +62,26 @@ module RSpec
|
|
69
62
|
# RSpec::Matchers.alias_matcher :a_user_who_is_an_admin, :be_an_admin
|
70
63
|
# expect(user_list).to include(a_user_who_is_an_admin)
|
71
64
|
#
|
65
|
+
# ## Alias Matchers
|
66
|
+
#
|
67
|
+
# With {RSpec::Matchers.alias_matcher}, you can easily create an
|
68
|
+
# alternate name for a given matcher.
|
69
|
+
#
|
70
|
+
# The description will also change according to the new name:
|
71
|
+
#
|
72
|
+
# RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
|
73
|
+
# sum_to(3).description # => "sum to 3"
|
74
|
+
# a_list_that_sums_to(3).description # => "a list that sums to 3"
|
75
|
+
#
|
76
|
+
# or you can specify a custom description like this:
|
77
|
+
#
|
78
|
+
# RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
|
79
|
+
# description.sub("be sorted by", "a list sorted by")
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# be_sorted_by(:age).description # => "be sorted by age"
|
83
|
+
# a_list_sorted_by(:age).description # => "a list sorted by age"
|
84
|
+
#
|
72
85
|
# ## Custom Matchers
|
73
86
|
#
|
74
87
|
# When you find that none of the stock matchers provide a natural feeling
|
@@ -209,55 +222,88 @@ module RSpec
|
|
209
222
|
# expressions, and also uses the noun-phrase wording in the matcher's `description`,
|
210
223
|
# for readable failure messages. You can alias your custom matchers in similar fashion
|
211
224
|
# using {RSpec::Matchers.alias_matcher}.
|
212
|
-
|
225
|
+
#
|
226
|
+
# ## Negated Matchers
|
227
|
+
#
|
228
|
+
# Sometimes if you want to test for the opposite using a more descriptive name
|
229
|
+
# instead of using `not_to`, you can use {RSpec::Matchers.define_negated_matcher}:
|
230
|
+
#
|
231
|
+
# RSpec::Matchers.define_negated_matcher :exclude, :include
|
232
|
+
# include(1, 2).description # => "include 1 and 2"
|
233
|
+
# exclude(1, 2).description # => "exclude 1 and 2"
|
234
|
+
#
|
235
|
+
# While the most obvious negated form may be to add a `not_` prefix,
|
236
|
+
# the failure messages you get with that form can be confusing (e.g.
|
237
|
+
# "expected [actual] to not [verb], but did not"). We've found it works
|
238
|
+
# best to find a more positive name for the negated form, such as
|
239
|
+
# `avoid_changing` rather than `not_change`.
|
240
|
+
#
|
241
|
+
module Matchers # rubocop:disable Metrics/ModuleLength
|
242
|
+
extend ::RSpec::Matchers::DSL
|
243
|
+
|
244
|
+
# @!macro [attach] alias_matcher
|
245
|
+
# @!parse
|
246
|
+
# alias $1 $2
|
247
|
+
# @!visibility private
|
248
|
+
# We define this override here so we can attach a YARD macro to it.
|
249
|
+
# It ensures that our docs list all the matcher aliases.
|
250
|
+
def self.alias_matcher(*args, &block)
|
251
|
+
super(*args, &block)
|
252
|
+
end
|
253
|
+
|
254
|
+
# @!method self.alias_matcher(new_name, old_name, options={}, &description_override)
|
255
|
+
# Extended from {RSpec::Matchers::DSL#alias_matcher}.
|
256
|
+
|
257
|
+
# @!method self.define(name, &declarations)
|
258
|
+
# Extended from {RSpec::Matchers::DSL#define}.
|
259
|
+
|
260
|
+
# @!method self.define_negated_matcher(negated_name, base_name, &description_override)
|
261
|
+
# Extended from {RSpec::Matchers::DSL#define_negated_matcher}.
|
262
|
+
|
213
263
|
# @method expect
|
214
264
|
# Supports `expect(actual).to matcher` syntax by wrapping `actual` in an
|
215
265
|
# `ExpectationTarget`.
|
216
266
|
# @example
|
217
267
|
# expect(actual).to eq(expected)
|
218
268
|
# expect(actual).not_to eq(expected)
|
219
|
-
# @return [ExpectationTarget]
|
220
|
-
# @see ExpectationTarget#to
|
221
|
-
# @see ExpectationTarget#not_to
|
269
|
+
# @return [Expectations::ExpectationTarget]
|
270
|
+
# @see Expectations::ExpectationTarget#to
|
271
|
+
# @see Expectations::ExpectationTarget#not_to
|
222
272
|
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
235
|
-
#
|
236
|
-
#
|
237
|
-
#
|
238
|
-
#
|
273
|
+
# Allows multiple expectations in the provided block to fail, and then
|
274
|
+
# aggregates them into a single exception, rather than aborting on the
|
275
|
+
# first expectation failure like normal. This allows you to see all
|
276
|
+
# failures from an entire set of expectations without splitting each
|
277
|
+
# off into its own example (which may slow things down if the example
|
278
|
+
# setup is expensive).
|
279
|
+
#
|
280
|
+
# @param label [String] label for this aggregation block, which will be
|
281
|
+
# included in the aggregated exception message.
|
282
|
+
# @param metadata [Hash] additional metadata about this failure aggregation
|
283
|
+
# block. If multiple expectations fail, it will be exposed from the
|
284
|
+
# {Expectations::MultipleExpectationsNotMetError} exception. Mostly
|
285
|
+
# intended for internal RSpec use but you can use it as well.
|
286
|
+
# @yield Block containing as many expectation as you want. The block is
|
287
|
+
# simply yielded to, so you can trust that anything that works outside
|
288
|
+
# the block should work within it.
|
289
|
+
# @raise [Expectations::MultipleExpectationsNotMetError] raised when
|
290
|
+
# multiple expectations fail.
|
291
|
+
# @raise [Expectations::ExpectationNotMetError] raised when a single
|
292
|
+
# expectation fails.
|
293
|
+
# @raise [Exception] other sorts of exceptions will be raised as normal.
|
239
294
|
#
|
240
295
|
# @example
|
241
|
-
#
|
242
|
-
#
|
243
|
-
#
|
296
|
+
# aggregate_failures("verifying response") do
|
297
|
+
# expect(response.status).to eq(200)
|
298
|
+
# expect(response.headers).to include("Content-Type" => "text/plain")
|
299
|
+
# expect(response.body).to include("Success")
|
244
300
|
# end
|
245
301
|
#
|
246
|
-
#
|
247
|
-
#
|
248
|
-
#
|
249
|
-
|
250
|
-
|
251
|
-
# alias $1 $2
|
252
|
-
def self.alias_matcher(new_name, old_name, &description_override)
|
253
|
-
description_override ||= lambda do |old_desc|
|
254
|
-
old_desc.gsub(Pretty.split_words(old_name), Pretty.split_words(new_name))
|
255
|
-
end
|
256
|
-
|
257
|
-
define_method(new_name) do |*args, &block|
|
258
|
-
matcher = __send__(old_name, *args, &block)
|
259
|
-
AliasedMatcher.new(matcher, description_override)
|
260
|
-
end
|
302
|
+
# @note The implementation of this feature uses a thread-local variable,
|
303
|
+
# which means that if you have an expectation failure in another thread,
|
304
|
+
# it'll abort like normal.
|
305
|
+
def aggregate_failures(label=nil, metadata={}, &block)
|
306
|
+
Expectations::FailureAggregator.new(label, metadata).aggregate(&block)
|
261
307
|
end
|
262
308
|
|
263
309
|
# Passes if actual is truthy (anything but false or nil)
|
@@ -270,9 +316,9 @@ module RSpec
|
|
270
316
|
def be_falsey
|
271
317
|
BuiltIn::BeFalsey.new
|
272
318
|
end
|
273
|
-
alias_matcher :be_falsy,
|
319
|
+
alias_matcher :be_falsy, :be_falsey
|
274
320
|
alias_matcher :a_falsey_value, :be_falsey
|
275
|
-
alias_matcher :a_falsy_value,
|
321
|
+
alias_matcher :a_falsy_value, :be_falsey
|
276
322
|
|
277
323
|
# Passes if actual is nil
|
278
324
|
def be_nil
|
@@ -303,7 +349,7 @@ module RSpec
|
|
303
349
|
def be(*args)
|
304
350
|
args.empty? ? Matchers::BuiltIn::Be.new : equal(*args)
|
305
351
|
end
|
306
|
-
alias_matcher :a_value, :be
|
352
|
+
alias_matcher :a_value, :be, :klass => AliasedMatcherWithOperatorSupport
|
307
353
|
|
308
354
|
# passes if target.kind_of?(klass)
|
309
355
|
def be_a(klass)
|
@@ -314,8 +360,7 @@ module RSpec
|
|
314
360
|
# Passes if actual.instance_of?(expected)
|
315
361
|
#
|
316
362
|
# @example
|
317
|
-
#
|
318
|
-
# expect(5).to be_an_instance_of(Fixnum)
|
363
|
+
# expect(5).to be_an_instance_of(Integer)
|
319
364
|
# expect(5).not_to be_an_instance_of(Numeric)
|
320
365
|
# expect(5).not_to be_an_instance_of(Float)
|
321
366
|
def be_an_instance_of(expected)
|
@@ -327,15 +372,14 @@ module RSpec
|
|
327
372
|
# Passes if actual.kind_of?(expected)
|
328
373
|
#
|
329
374
|
# @example
|
330
|
-
#
|
331
|
-
# expect(5).to be_a_kind_of(Fixnum)
|
375
|
+
# expect(5).to be_a_kind_of(Integer)
|
332
376
|
# expect(5).to be_a_kind_of(Numeric)
|
333
377
|
# expect(5).not_to be_a_kind_of(Float)
|
334
378
|
def be_a_kind_of(expected)
|
335
379
|
BuiltIn::BeAKindOf.new(expected)
|
336
380
|
end
|
337
381
|
alias_method :be_kind_of, :be_a_kind_of
|
338
|
-
alias_matcher :a_kind_of,
|
382
|
+
alias_matcher :a_kind_of, :be_a_kind_of
|
339
383
|
|
340
384
|
# Passes if actual.between?(min, max). Works with any Comparable object,
|
341
385
|
# including String, Symbol, Time, or Numeric (Fixnum, Bignum, Integer,
|
@@ -345,7 +389,6 @@ module RSpec
|
|
345
389
|
# but you can make it `exclusive` by chaining that off the matcher.
|
346
390
|
#
|
347
391
|
# @example
|
348
|
-
#
|
349
392
|
# expect(5).to be_between(1, 10)
|
350
393
|
# expect(11).not_to be_between(1, 10)
|
351
394
|
# expect(10).not_to be_between(1, 10).exclusive
|
@@ -357,14 +400,13 @@ module RSpec
|
|
357
400
|
# Passes if actual == expected +/- delta
|
358
401
|
#
|
359
402
|
# @example
|
360
|
-
#
|
361
403
|
# expect(result).to be_within(0.5).of(3.0)
|
362
404
|
# expect(result).not_to be_within(0.5).of(3.0)
|
363
405
|
def be_within(delta)
|
364
406
|
BuiltIn::BeWithin.new(delta)
|
365
407
|
end
|
366
408
|
alias_matcher :a_value_within, :be_within
|
367
|
-
alias_matcher :within,
|
409
|
+
alias_matcher :within, :be_within
|
368
410
|
|
369
411
|
# Applied to a proc, specifies that its execution will cause some value to
|
370
412
|
# change.
|
@@ -382,14 +424,16 @@ module RSpec
|
|
382
424
|
# You can chain any of the following off of the end to specify details
|
383
425
|
# about the change:
|
384
426
|
#
|
427
|
+
# * `from`
|
428
|
+
# * `to`
|
429
|
+
#
|
430
|
+
# or any one of:
|
431
|
+
#
|
385
432
|
# * `by`
|
386
433
|
# * `by_at_least`
|
387
434
|
# * `by_at_most`
|
388
|
-
# * `from`
|
389
|
-
# * `to`
|
390
435
|
#
|
391
436
|
# @example
|
392
|
-
#
|
393
437
|
# expect {
|
394
438
|
# team.add_player(player)
|
395
439
|
# }.to change(roster, :count)
|
@@ -436,7 +480,10 @@ module RSpec
|
|
436
480
|
# == Notes
|
437
481
|
#
|
438
482
|
# Evaluates `receiver.message` or `block` before and after it
|
439
|
-
# evaluates the block passed to `expect`.
|
483
|
+
# evaluates the block passed to `expect`. If the value is the same
|
484
|
+
# object, its before/after `hash` value is used to see if it has changed.
|
485
|
+
# Therefore, your object needs to properly implement `hash` to work correctly
|
486
|
+
# with this matcher.
|
440
487
|
#
|
441
488
|
# `expect( ... ).not_to change` supports the form that specifies `from`
|
442
489
|
# (which specifies what you expect the starting, unchanged value to be)
|
@@ -445,8 +492,8 @@ module RSpec
|
|
445
492
|
def change(receiver=nil, message=nil, &block)
|
446
493
|
BuiltIn::Change.new(receiver, message, &block)
|
447
494
|
end
|
448
|
-
alias_matcher :a_block_changing,
|
449
|
-
alias_matcher :changing,
|
495
|
+
alias_matcher :a_block_changing, :change
|
496
|
+
alias_matcher :changing, :change
|
450
497
|
|
451
498
|
# Passes if actual contains all of the expected regardless of order.
|
452
499
|
# This works for collections. Pass in multiple args and it will only
|
@@ -455,11 +502,7 @@ module RSpec
|
|
455
502
|
# @note This is also available using the `=~` operator with `should`,
|
456
503
|
# but `=~` is not supported with `expect`.
|
457
504
|
#
|
458
|
-
# @note This matcher only supports positive expectations.
|
459
|
-
# `expect(...).not_to contain_exactly(other_array)` is not supported.
|
460
|
-
#
|
461
505
|
# @example
|
462
|
-
#
|
463
506
|
# expect([1, 2, 3]).to contain_exactly(1, 2, 3)
|
464
507
|
# expect([1, 2, 3]).to contain_exactly(1, 3, 2)
|
465
508
|
#
|
@@ -468,7 +511,7 @@ module RSpec
|
|
468
511
|
BuiltIn::ContainExactly.new(items)
|
469
512
|
end
|
470
513
|
alias_matcher :a_collection_containing_exactly, :contain_exactly
|
471
|
-
alias_matcher :containing_exactly,
|
514
|
+
alias_matcher :containing_exactly, :contain_exactly
|
472
515
|
|
473
516
|
# Passes if actual covers expected. This works for
|
474
517
|
# Ranges. You can also pass in multiple args
|
@@ -486,7 +529,7 @@ module RSpec
|
|
486
529
|
BuiltIn::Cover.new(*values)
|
487
530
|
end
|
488
531
|
alias_matcher :a_range_covering, :cover
|
489
|
-
alias_matcher :covering,
|
532
|
+
alias_matcher :covering, :cover
|
490
533
|
|
491
534
|
# Matches if the actual value ends with the expected value(s). In the case
|
492
535
|
# of a string, matches against the last `expected.length` characters of the
|
@@ -494,7 +537,6 @@ module RSpec
|
|
494
537
|
# `expected.length` elements of the actual array.
|
495
538
|
#
|
496
539
|
# @example
|
497
|
-
#
|
498
540
|
# expect("this string").to end_with "string"
|
499
541
|
# expect([0, 1, 2, 3, 4]).to end_with 4
|
500
542
|
# expect([0, 2, 3, 4, 4]).to end_with 3, 4
|
@@ -502,8 +544,8 @@ module RSpec
|
|
502
544
|
BuiltIn::EndWith.new(*expected)
|
503
545
|
end
|
504
546
|
alias_matcher :a_collection_ending_with, :end_with
|
505
|
-
alias_matcher :a_string_ending_with,
|
506
|
-
alias_matcher :ending_with,
|
547
|
+
alias_matcher :a_string_ending_with, :end_with
|
548
|
+
alias_matcher :ending_with, :end_with
|
507
549
|
|
508
550
|
# Passes if <tt>actual == expected</tt>.
|
509
551
|
#
|
@@ -511,14 +553,13 @@ module RSpec
|
|
511
553
|
# information about equality in Ruby.
|
512
554
|
#
|
513
555
|
# @example
|
514
|
-
#
|
515
556
|
# expect(5).to eq(5)
|
516
557
|
# expect(5).not_to eq(3)
|
517
558
|
def eq(expected)
|
518
559
|
BuiltIn::Eq.new(expected)
|
519
560
|
end
|
520
561
|
alias_matcher :an_object_eq_to, :eq
|
521
|
-
alias_matcher :eq_to,
|
562
|
+
alias_matcher :eq_to, :eq
|
522
563
|
|
523
564
|
# Passes if `actual.eql?(expected)`
|
524
565
|
#
|
@@ -526,14 +567,13 @@ module RSpec
|
|
526
567
|
# information about equality in Ruby.
|
527
568
|
#
|
528
569
|
# @example
|
529
|
-
#
|
530
570
|
# expect(5).to eql(5)
|
531
571
|
# expect(5).not_to eql(3)
|
532
572
|
def eql(expected)
|
533
573
|
BuiltIn::Eql.new(expected)
|
534
574
|
end
|
535
575
|
alias_matcher :an_object_eql_to, :eql
|
536
|
-
alias_matcher :eql_to,
|
576
|
+
alias_matcher :eql_to, :eql
|
537
577
|
|
538
578
|
# Passes if <tt>actual.equal?(expected)</tt> (object identity).
|
539
579
|
#
|
@@ -541,14 +581,13 @@ module RSpec
|
|
541
581
|
# information about equality in Ruby.
|
542
582
|
#
|
543
583
|
# @example
|
544
|
-
#
|
545
|
-
# expect(5).to equal(5) # Fixnums are equal
|
584
|
+
# expect(5).to equal(5) # Integers are equal
|
546
585
|
# expect("5").not_to equal("5") # Strings that look the same are not the same object
|
547
586
|
def equal(expected)
|
548
587
|
BuiltIn::Equal.new(expected)
|
549
588
|
end
|
550
589
|
alias_matcher :an_object_equal_to, :equal
|
551
|
-
alias_matcher :equal_to,
|
590
|
+
alias_matcher :equal_to, :equal
|
552
591
|
|
553
592
|
# Passes if `actual.exist?` or `actual.exists?`
|
554
593
|
#
|
@@ -558,33 +597,57 @@ module RSpec
|
|
558
597
|
BuiltIn::Exist.new(*args)
|
559
598
|
end
|
560
599
|
alias_matcher :an_object_existing, :exist
|
561
|
-
alias_matcher :existing,
|
600
|
+
alias_matcher :existing, :exist
|
601
|
+
|
602
|
+
# Passes if actual's attribute values match the expected attributes hash.
|
603
|
+
# This works no matter how you define your attribute readers.
|
604
|
+
#
|
605
|
+
# @example
|
606
|
+
# Person = Struct.new(:name, :age)
|
607
|
+
# person = Person.new("Bob", 32)
|
608
|
+
#
|
609
|
+
# expect(person).to have_attributes(:name => "Bob", :age => 32)
|
610
|
+
# expect(person).to have_attributes(:name => a_string_starting_with("B"), :age => (a_value > 30) )
|
611
|
+
#
|
612
|
+
# @note It will fail if actual doesn't respond to any of the expected attributes.
|
613
|
+
#
|
614
|
+
# @example
|
615
|
+
# expect(person).to have_attributes(:color => "red")
|
616
|
+
def have_attributes(expected)
|
617
|
+
BuiltIn::HaveAttributes.new(expected)
|
618
|
+
end
|
619
|
+
alias_matcher :an_object_having_attributes, :have_attributes
|
620
|
+
alias_matcher :having_attributes, :have_attributes
|
562
621
|
|
563
622
|
# Passes if actual includes expected. This works for
|
564
623
|
# collections and Strings. You can also pass in multiple args
|
565
624
|
# and it will only pass if all args are found in collection.
|
566
625
|
#
|
567
626
|
# @example
|
568
|
-
#
|
569
627
|
# expect([1,2,3]).to include(3)
|
570
628
|
# expect([1,2,3]).to include(2,3)
|
571
629
|
# expect([1,2,3]).to include(2,3,4) # fails
|
572
630
|
# expect([1,2,3]).not_to include(4)
|
573
631
|
# expect("spread").to include("read")
|
574
632
|
# expect("spread").not_to include("red")
|
633
|
+
# expect(:a => 1, :b => 2).to include(:a)
|
634
|
+
# expect(:a => 1, :b => 2).to include(:a, :b)
|
635
|
+
# expect(:a => 1, :b => 2).to include(:a => 1)
|
636
|
+
# expect(:a => 1, :b => 2).to include(:b => 2, :a => 1)
|
637
|
+
# expect(:a => 1, :b => 2).to include(:c) # fails
|
638
|
+
# expect(:a => 1, :b => 2).not_to include(:a => 2)
|
575
639
|
def include(*expected)
|
576
640
|
BuiltIn::Include.new(*expected)
|
577
641
|
end
|
578
642
|
alias_matcher :a_collection_including, :include
|
579
|
-
alias_matcher :a_string_including,
|
580
|
-
alias_matcher :a_hash_including,
|
581
|
-
alias_matcher :including,
|
643
|
+
alias_matcher :a_string_including, :include
|
644
|
+
alias_matcher :a_hash_including, :include
|
645
|
+
alias_matcher :including, :include
|
582
646
|
|
583
|
-
# Passes if
|
584
|
-
#
|
647
|
+
# Passes if the provided matcher passes when checked against all
|
648
|
+
# elements of the collection.
|
585
649
|
#
|
586
650
|
# @example
|
587
|
-
#
|
588
651
|
# expect([1, 3, 5]).to all be_odd
|
589
652
|
# expect([1, 3, 6]).to all be_odd # fails
|
590
653
|
#
|
@@ -606,12 +669,10 @@ module RSpec
|
|
606
669
|
# pair of elements.
|
607
670
|
#
|
608
671
|
# @example
|
609
|
-
#
|
610
672
|
# expect(email).to match(/^([^\s]+)((?:[-a-z0-9]+\.)+[a-z]{2,})$/i)
|
611
673
|
# expect(email).to match("@example.com")
|
612
674
|
#
|
613
675
|
# @example
|
614
|
-
#
|
615
676
|
# hash = {
|
616
677
|
# :a => {
|
617
678
|
# :b => ["foo", 5],
|
@@ -623,7 +684,7 @@ module RSpec
|
|
623
684
|
# :a => {
|
624
685
|
# :b => a_collection_containing_exactly(
|
625
686
|
# a_string_starting_with("f"),
|
626
|
-
# an_instance_of(
|
687
|
+
# an_instance_of(Integer)
|
627
688
|
# ),
|
628
689
|
# :c => { :d => (a_value < 3) }
|
629
690
|
# }
|
@@ -636,17 +697,16 @@ module RSpec
|
|
636
697
|
def match(expected)
|
637
698
|
BuiltIn::Match.new(expected)
|
638
699
|
end
|
639
|
-
alias_matcher :match_regex,
|
700
|
+
alias_matcher :match_regex, :match
|
640
701
|
alias_matcher :an_object_matching, :match
|
641
|
-
alias_matcher :a_string_matching,
|
642
|
-
alias_matcher :matching,
|
702
|
+
alias_matcher :a_string_matching, :match
|
703
|
+
alias_matcher :matching, :match
|
643
704
|
|
644
705
|
# An alternate form of `contain_exactly` that accepts
|
645
706
|
# the expected contents as a single array arg rather
|
646
|
-
#
|
707
|
+
# than splatted out as individual items.
|
647
708
|
#
|
648
709
|
# @example
|
649
|
-
#
|
650
710
|
# expect(results).to contain_exactly(1, 2)
|
651
711
|
# # is identical to:
|
652
712
|
# expect(results).to match_array([1, 2])
|
@@ -655,13 +715,19 @@ module RSpec
|
|
655
715
|
def match_array(items)
|
656
716
|
contain_exactly(*items)
|
657
717
|
end
|
718
|
+
alias_matcher :an_array_matching, :match_array do |desc|
|
719
|
+
desc.sub("contain exactly", "an array containing exactly")
|
720
|
+
end
|
658
721
|
|
659
722
|
# With no arg, passes if the block outputs `to_stdout` or `to_stderr`.
|
660
|
-
# With a string, passes if the
|
661
|
-
# With a regexp or matcher, passes if the
|
723
|
+
# With a string, passes if the block outputs that specific string `to_stdout` or `to_stderr`.
|
724
|
+
# With a regexp or matcher, passes if the block outputs a string `to_stdout` or `to_stderr` that matches.
|
662
725
|
#
|
663
|
-
#
|
726
|
+
# To capture output from any spawned subprocess as well, use `to_stdout_from_any_process` or
|
727
|
+
# `to_stderr_from_any_process`. Output from any process that inherits the main process's corresponding
|
728
|
+
# standard stream will be captured.
|
664
729
|
#
|
730
|
+
# @example
|
665
731
|
# expect { print 'foo' }.to output.to_stdout
|
666
732
|
# expect { print 'foo' }.to output('foo').to_stdout
|
667
733
|
# expect { print 'foo' }.to output(/foo/).to_stdout
|
@@ -674,10 +740,15 @@ module RSpec
|
|
674
740
|
#
|
675
741
|
# expect { do_something }.to_not output.to_stderr
|
676
742
|
#
|
677
|
-
#
|
678
|
-
#
|
743
|
+
# expect { system('echo foo') }.to output("foo\n").to_stdout_from_any_process
|
744
|
+
# expect { system('echo foo', out: :err) }.to output("foo\n").to_stderr_from_any_process
|
745
|
+
#
|
746
|
+
# @note `to_stdout` and `to_stderr` work by temporarily replacing `$stdout` or `$stderr`,
|
747
|
+
# so they're not able to intercept stream output that explicitly uses `STDOUT`/`STDERR`
|
679
748
|
# or that uses a reference to `$stdout`/`$stderr` that was stored before the
|
680
|
-
# matcher
|
749
|
+
# matcher was used.
|
750
|
+
# @note `to_stdout_from_any_process` and `to_stderr_from_any_process` use Tempfiles, and
|
751
|
+
# are thus significantly (~30x) slower than `to_stdout` and `to_stderr`.
|
681
752
|
def output(expected=nil)
|
682
753
|
BuiltIn::Output.new(expected)
|
683
754
|
end
|
@@ -685,29 +756,30 @@ module RSpec
|
|
685
756
|
|
686
757
|
# With no args, matches if any error is raised.
|
687
758
|
# With a named error, matches only if that specific error is raised.
|
688
|
-
# With a named error and
|
689
|
-
# With a named error and
|
759
|
+
# With a named error and message specified as a String, matches only if both match.
|
760
|
+
# With a named error and message specified as a Regexp, matches only if both match.
|
690
761
|
# Pass an optional block to perform extra verifications on the exception matched
|
691
762
|
#
|
692
763
|
# @example
|
693
|
-
#
|
694
764
|
# expect { do_something_risky }.to raise_error
|
695
765
|
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError)
|
696
766
|
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError) { |error| expect(error.data).to eq 42 }
|
767
|
+
# expect { do_something_risky }.to raise_error { |error| expect(error.data).to eq 42 }
|
697
768
|
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError, "that was too risky")
|
698
769
|
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError, /oo ri/)
|
770
|
+
# expect { do_something_risky }.to raise_error("that was too risky")
|
699
771
|
#
|
700
772
|
# expect { do_something_risky }.not_to raise_error
|
701
|
-
def raise_error(error=
|
773
|
+
def raise_error(error=BuiltIn::RaiseError::UndefinedValue, message=nil, &block)
|
702
774
|
BuiltIn::RaiseError.new(error, message, &block)
|
703
775
|
end
|
704
|
-
alias_method :raise_exception,
|
776
|
+
alias_method :raise_exception, :raise_error
|
705
777
|
|
706
|
-
alias_matcher :a_block_raising,
|
778
|
+
alias_matcher :a_block_raising, :raise_error do |desc|
|
707
779
|
desc.sub("raise", "a block raising")
|
708
780
|
end
|
709
781
|
|
710
|
-
alias_matcher :raising,
|
782
|
+
alias_matcher :raising, :raise_error do |desc|
|
711
783
|
desc.sub("raise", "raising")
|
712
784
|
end
|
713
785
|
|
@@ -715,14 +787,13 @@ module RSpec
|
|
715
787
|
# provided. Names can be Strings or Symbols.
|
716
788
|
#
|
717
789
|
# @example
|
718
|
-
#
|
719
790
|
# expect("string").to respond_to(:length)
|
720
791
|
#
|
721
792
|
def respond_to(*names)
|
722
793
|
BuiltIn::RespondTo.new(*names)
|
723
794
|
end
|
724
795
|
alias_matcher :an_object_responding_to, :respond_to
|
725
|
-
alias_matcher :responding_to,
|
796
|
+
alias_matcher :responding_to, :respond_to
|
726
797
|
|
727
798
|
# Passes if the submitted block returns true. Yields target to the
|
728
799
|
# block.
|
@@ -734,14 +805,16 @@ module RSpec
|
|
734
805
|
# If you do find yourself in such a situation, you could always write
|
735
806
|
# a custom matcher, which would likely make your specs more expressive.
|
736
807
|
#
|
737
|
-
# @
|
808
|
+
# @param description [String] optional description to be used for this matcher.
|
738
809
|
#
|
810
|
+
# @example
|
739
811
|
# expect(5).to satisfy { |n| n > 3 }
|
740
|
-
|
741
|
-
|
812
|
+
# expect(5).to satisfy("be greater than 3") { |n| n > 3 }
|
813
|
+
def satisfy(description=nil, &block)
|
814
|
+
BuiltIn::Satisfy.new(description, &block)
|
742
815
|
end
|
743
816
|
alias_matcher :an_object_satisfying, :satisfy
|
744
|
-
alias_matcher :satisfying,
|
817
|
+
alias_matcher :satisfying, :satisfy
|
745
818
|
|
746
819
|
# Matches if the actual value starts with the expected value(s). In the
|
747
820
|
# case of a string, matches against the first `expected.length` characters
|
@@ -749,7 +822,6 @@ module RSpec
|
|
749
822
|
# `expected.length` elements of the actual array.
|
750
823
|
#
|
751
824
|
# @example
|
752
|
-
#
|
753
825
|
# expect("this string").to start_with "this s"
|
754
826
|
# expect([0, 1, 2, 3, 4]).to start_with 0
|
755
827
|
# expect([0, 2, 3, 4, 4]).to start_with 0, 1
|
@@ -757,8 +829,8 @@ module RSpec
|
|
757
829
|
BuiltIn::StartWith.new(*expected)
|
758
830
|
end
|
759
831
|
alias_matcher :a_collection_starting_with, :start_with
|
760
|
-
alias_matcher :a_string_starting_with,
|
761
|
-
alias_matcher :starting_with,
|
832
|
+
alias_matcher :a_string_starting_with, :start_with
|
833
|
+
alias_matcher :starting_with, :start_with
|
762
834
|
|
763
835
|
# Given no argument, matches if a proc throws any Symbol.
|
764
836
|
#
|
@@ -768,7 +840,6 @@ module RSpec
|
|
768
840
|
# specified Symbol with the specified arg.
|
769
841
|
#
|
770
842
|
# @example
|
771
|
-
#
|
772
843
|
# expect { do_something_risky }.to throw_symbol
|
773
844
|
# expect { do_something_risky }.to throw_symbol(:that_was_risky)
|
774
845
|
# expect { do_something_risky }.to throw_symbol(:that_was_risky, 'culprit')
|
@@ -784,7 +855,7 @@ module RSpec
|
|
784
855
|
desc.sub("throw", "a block throwing")
|
785
856
|
end
|
786
857
|
|
787
|
-
alias_matcher :throwing,
|
858
|
+
alias_matcher :throwing, :throw_symbol do |desc|
|
788
859
|
desc.sub("throw", "throwing")
|
789
860
|
end
|
790
861
|
|
@@ -792,25 +863,21 @@ module RSpec
|
|
792
863
|
# of whether or not arguments are yielded.
|
793
864
|
#
|
794
865
|
# @example
|
795
|
-
#
|
796
866
|
# expect { |b| 5.tap(&b) }.to yield_control
|
797
867
|
# expect { |b| "a".to_sym(&b) }.not_to yield_control
|
798
868
|
#
|
799
869
|
# @note Your expect block must accept a parameter and pass it on to
|
800
870
|
# the method-under-test as a block.
|
801
|
-
# @note This matcher is not designed for use with methods that yield
|
802
|
-
# multiple times.
|
803
871
|
def yield_control
|
804
872
|
BuiltIn::YieldControl.new
|
805
873
|
end
|
806
|
-
alias_matcher :a_block_yielding_control,
|
807
|
-
alias_matcher :yielding_control,
|
874
|
+
alias_matcher :a_block_yielding_control, :yield_control
|
875
|
+
alias_matcher :yielding_control, :yield_control
|
808
876
|
|
809
877
|
# Passes if the method called in the expect block yields with
|
810
878
|
# no arguments. Fails if it does not yield, or yields with arguments.
|
811
879
|
#
|
812
880
|
# @example
|
813
|
-
#
|
814
881
|
# expect { |b| User.transaction(&b) }.to yield_with_no_args
|
815
882
|
# expect { |b| 5.tap(&b) }.not_to yield_with_no_args # because it yields with `5`
|
816
883
|
# expect { |b| "a".to_sym(&b) }.not_to yield_with_no_args # because it does not yield
|
@@ -822,8 +889,8 @@ module RSpec
|
|
822
889
|
def yield_with_no_args
|
823
890
|
BuiltIn::YieldWithNoArgs.new
|
824
891
|
end
|
825
|
-
alias_matcher :a_block_yielding_with_no_args,
|
826
|
-
alias_matcher :yielding_with_no_args,
|
892
|
+
alias_matcher :a_block_yielding_with_no_args, :yield_with_no_args
|
893
|
+
alias_matcher :yielding_with_no_args, :yield_with_no_args
|
827
894
|
|
828
895
|
# Given no arguments, matches if the method called in the expect
|
829
896
|
# block yields with arguments (regardless of what they are or how
|
@@ -837,10 +904,9 @@ module RSpec
|
|
837
904
|
# operator, the matcher will pass.
|
838
905
|
#
|
839
906
|
# @example
|
840
|
-
#
|
841
907
|
# expect { |b| 5.tap(&b) }.to yield_with_args # because #tap yields an arg
|
842
908
|
# expect { |b| 5.tap(&b) }.to yield_with_args(5) # because 5 == 5
|
843
|
-
# expect { |b| 5.tap(&b) }.to yield_with_args(
|
909
|
+
# expect { |b| 5.tap(&b) }.to yield_with_args(Integer) # because Integer === 5
|
844
910
|
# expect { |b| File.open("f.txt", &b) }.to yield_with_args(/txt/) # because /txt/ === "f.txt"
|
845
911
|
#
|
846
912
|
# expect { |b| User.transaction(&b) }.not_to yield_with_args # because it yields no args
|
@@ -853,8 +919,8 @@ module RSpec
|
|
853
919
|
def yield_with_args(*args)
|
854
920
|
BuiltIn::YieldWithArgs.new(*args)
|
855
921
|
end
|
856
|
-
alias_matcher :a_block_yielding_with_args,
|
857
|
-
alias_matcher :yielding_with_args,
|
922
|
+
alias_matcher :a_block_yielding_with_args, :yield_with_args
|
923
|
+
alias_matcher :yielding_with_args, :yield_with_args
|
858
924
|
|
859
925
|
# Designed for use with methods that repeatedly yield (such as
|
860
926
|
# iterators). Passes if the method called in the expect block yields
|
@@ -865,7 +931,6 @@ module RSpec
|
|
865
931
|
# operator, the matcher will pass.
|
866
932
|
#
|
867
933
|
# @example
|
868
|
-
#
|
869
934
|
# expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
|
870
935
|
# expect { |b| { :a => 1, :b => 2 }.each(&b) }.to yield_successive_args([:a, 1], [:b, 2])
|
871
936
|
# expect { |b| [1, 2, 3].each(&b) }.not_to yield_successive_args(1, 2)
|
@@ -875,8 +940,8 @@ module RSpec
|
|
875
940
|
def yield_successive_args(*args)
|
876
941
|
BuiltIn::YieldSuccessiveArgs.new(*args)
|
877
942
|
end
|
878
|
-
alias_matcher :a_block_yielding_successive_args,
|
879
|
-
alias_matcher :yielding_successive_args,
|
943
|
+
alias_matcher :a_block_yielding_successive_args, :yield_successive_args
|
944
|
+
alias_matcher :yielding_successive_args, :yield_successive_args
|
880
945
|
|
881
946
|
# Delegates to {RSpec::Expectations.configuration}.
|
882
947
|
# This is here because rspec-core's `expect_with` option
|
@@ -889,8 +954,9 @@ module RSpec
|
|
889
954
|
|
890
955
|
private
|
891
956
|
|
892
|
-
BE_PREDICATE_REGEX = /^(be_(?:an?_)?)(.*)/
|
957
|
+
BE_PREDICATE_REGEX = /^(?:be_(?:an?_)?)(.*)/
|
893
958
|
HAS_REGEX = /^(?:have_)(.*)/
|
959
|
+
DYNAMIC_MATCHER_REGEX = Regexp.union(BE_PREDICATE_REGEX, HAS_REGEX)
|
894
960
|
|
895
961
|
def method_missing(method, *args, &block)
|
896
962
|
case method.to_s
|
@@ -902,20 +968,77 @@ module RSpec
|
|
902
968
|
super
|
903
969
|
end
|
904
970
|
end
|
971
|
+
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
|
972
|
+
|
973
|
+
if RUBY_VERSION.to_f >= 1.9
|
974
|
+
def respond_to_missing?(method, *)
|
975
|
+
method =~ DYNAMIC_MATCHER_REGEX || super
|
976
|
+
end
|
977
|
+
else # for 1.8.7
|
978
|
+
# :nocov:
|
979
|
+
def respond_to?(method, *)
|
980
|
+
method = method.to_s
|
981
|
+
method =~ DYNAMIC_MATCHER_REGEX || super
|
982
|
+
end
|
983
|
+
public :respond_to?
|
984
|
+
# :nocov:
|
985
|
+
end
|
905
986
|
|
906
987
|
# @api private
|
907
988
|
def self.is_a_matcher?(obj)
|
908
989
|
return true if ::RSpec::Matchers::BuiltIn::BaseMatcher === obj
|
909
|
-
|
990
|
+
begin
|
991
|
+
return false if obj.respond_to?(:i_respond_to_everything_so_im_not_really_a_matcher)
|
992
|
+
rescue NoMethodError
|
993
|
+
# Some objects, like BasicObject, don't implemented standard
|
994
|
+
# reflection methods.
|
995
|
+
return false
|
996
|
+
end
|
910
997
|
return false unless obj.respond_to?(:matches?)
|
911
998
|
|
912
999
|
obj.respond_to?(:failure_message) ||
|
913
1000
|
obj.respond_to?(:failure_message_for_should) # support legacy matchers
|
914
1001
|
end
|
915
1002
|
|
1003
|
+
::RSpec::Support.register_matcher_definition do |obj|
|
1004
|
+
is_a_matcher?(obj)
|
1005
|
+
end
|
1006
|
+
|
916
1007
|
# @api private
|
917
1008
|
def self.is_a_describable_matcher?(obj)
|
918
1009
|
is_a_matcher?(obj) && obj.respond_to?(:description)
|
919
1010
|
end
|
1011
|
+
|
1012
|
+
class << self
|
1013
|
+
private
|
1014
|
+
|
1015
|
+
if RSpec::Support::Ruby.mri? && RUBY_VERSION[0, 3] == '1.9'
|
1016
|
+
# Note that `included` doesn't work for this because it is triggered
|
1017
|
+
# _after_ `RSpec::Matchers` is an ancestor of the inclusion host, rather
|
1018
|
+
# than _before_, like `append_features`. It's important we check this before
|
1019
|
+
# in order to find the cases where it was already previously included.
|
1020
|
+
# @api private
|
1021
|
+
def append_features(mod)
|
1022
|
+
return super if mod < self # `mod < self` indicates a re-inclusion.
|
1023
|
+
|
1024
|
+
subclasses = ObjectSpace.each_object(Class).select { |c| c < mod && c < self }
|
1025
|
+
return super unless subclasses.any?
|
1026
|
+
|
1027
|
+
subclasses.reject! { |s| subclasses.any? { |s2| s < s2 } } # Filter to the root ancestor.
|
1028
|
+
subclasses = subclasses.map { |s| "`#{s}`" }.join(", ")
|
1029
|
+
|
1030
|
+
RSpec.warning "`#{self}` has been included in a superclass (`#{mod}`) " \
|
1031
|
+
"after previously being included in subclasses (#{subclasses}), " \
|
1032
|
+
"which can trigger infinite recursion from `super` due to an MRI 1.9 bug " \
|
1033
|
+
"(https://redmine.ruby-lang.org/issues/3351). To work around this, " \
|
1034
|
+
"either upgrade to MRI 2.0+, include a dup of the module (e.g. " \
|
1035
|
+
"`include #{self}.dup`), or find a way to include `#{self}` in `#{mod}` " \
|
1036
|
+
"before it is included in subclasses (#{subclasses}). See " \
|
1037
|
+
"https://github.com/rspec/rspec-expectations/issues/814 for more info"
|
1038
|
+
|
1039
|
+
super
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
end
|
920
1043
|
end
|
921
1044
|
end
|