rspec-expectations 3.9.1 → 3.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - checksums.yaml.gz.sig +0 -0
 - data.tar.gz.sig +0 -0
 - data/Changelog.md +62 -0
 - data/README.md +4 -4
 - data/lib/rspec/expectations/configuration.rb +15 -0
 - data/lib/rspec/expectations/failure_aggregator.rb +23 -5
 - data/lib/rspec/expectations/handler.rb +18 -6
 - data/lib/rspec/expectations/version.rb +1 -1
 - data/lib/rspec/matchers.rb +43 -40
 - data/lib/rspec/matchers/built_in.rb +2 -1
 - data/lib/rspec/matchers/built_in/be.rb +10 -107
 - data/lib/rspec/matchers/built_in/be_within.rb +2 -2
 - data/lib/rspec/matchers/built_in/count_expectation.rb +169 -0
 - data/lib/rspec/matchers/built_in/has.rb +88 -24
 - data/lib/rspec/matchers/built_in/include.rb +72 -15
 - data/lib/rspec/matchers/built_in/raise_error.rb +42 -13
 - data/lib/rspec/matchers/built_in/respond_to.rb +46 -45
 - data/lib/rspec/matchers/built_in/yield.rb +6 -83
 - data/lib/rspec/matchers/dsl.rb +8 -2
 - data/lib/rspec/matchers/english_phrasing.rb +1 -1
 - metadata +13 -12
 - metadata.gz.sig +0 -0
 
| 
         @@ -1,13 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rspec/matchers/built_in/count_expectation'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module RSpec
         
     | 
| 
       2 
4 
     | 
    
         
             
              module Matchers
         
     | 
| 
       3 
5 
     | 
    
         
             
                module BuiltIn
         
     | 
| 
       4 
6 
     | 
    
         
             
                  # @api private
         
     | 
| 
       5 
7 
     | 
    
         
             
                  # Provides the implementation for `include`.
         
     | 
| 
       6 
8 
     | 
    
         
             
                  # Not intended to be instantiated directly.
         
     | 
| 
       7 
     | 
    
         
            -
                  class Include < BaseMatcher
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class Include < BaseMatcher # rubocop:disable Metrics/ClassLength
         
     | 
| 
      
 10 
     | 
    
         
            +
                    include CountExpectation
         
     | 
| 
       8 
11 
     | 
    
         
             
                    # @private
         
     | 
| 
       9 
12 
     | 
    
         
             
                    attr_reader :expecteds
         
     | 
| 
       10 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                    # @api private
         
     | 
| 
       11 
15 
     | 
    
         
             
                    def initialize(*expecteds)
         
     | 
| 
       12 
16 
     | 
    
         
             
                      @expecteds = expecteds
         
     | 
| 
       13 
17 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -15,21 +19,29 @@ module RSpec 
     | 
|
| 
       15 
19 
     | 
    
         
             
                    # @api private
         
     | 
| 
       16 
20 
     | 
    
         
             
                    # @return [Boolean]
         
     | 
| 
       17 
21 
     | 
    
         
             
                    def matches?(actual)
         
     | 
| 
       18 
     | 
    
         
            -
                       
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 22 
     | 
    
         
            +
                      check_actual?(actual) &&
         
     | 
| 
      
 23 
     | 
    
         
            +
                        if check_expected_count?
         
     | 
| 
      
 24 
     | 
    
         
            +
                          expected_count_matches?(count_inclusions)
         
     | 
| 
      
 25 
     | 
    
         
            +
                        else
         
     | 
| 
      
 26 
     | 
    
         
            +
                          perform_match { |v| v }
         
     | 
| 
      
 27 
     | 
    
         
            +
                        end
         
     | 
| 
       20 
28 
     | 
    
         
             
                    end
         
     | 
| 
       21 
29 
     | 
    
         | 
| 
       22 
30 
     | 
    
         
             
                    # @api private
         
     | 
| 
       23 
31 
     | 
    
         
             
                    # @return [Boolean]
         
     | 
| 
       24 
32 
     | 
    
         
             
                    def does_not_match?(actual)
         
     | 
| 
       25 
     | 
    
         
            -
                       
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 33 
     | 
    
         
            +
                      check_actual?(actual) &&
         
     | 
| 
      
 34 
     | 
    
         
            +
                        if check_expected_count?
         
     | 
| 
      
 35 
     | 
    
         
            +
                          !expected_count_matches?(count_inclusions)
         
     | 
| 
      
 36 
     | 
    
         
            +
                        else
         
     | 
| 
      
 37 
     | 
    
         
            +
                          perform_match { |v| !v }
         
     | 
| 
      
 38 
     | 
    
         
            +
                        end
         
     | 
| 
       27 
39 
     | 
    
         
             
                    end
         
     | 
| 
       28 
40 
     | 
    
         | 
| 
       29 
41 
     | 
    
         
             
                    # @api private
         
     | 
| 
       30 
42 
     | 
    
         
             
                    # @return [String]
         
     | 
| 
       31 
43 
     | 
    
         
             
                    def description
         
     | 
| 
       32 
     | 
    
         
            -
                      improve_hash_formatting("include#{readable_list_of(expecteds)}")
         
     | 
| 
      
 44 
     | 
    
         
            +
                      improve_hash_formatting("include#{readable_list_of(expecteds)}#{count_expectation_description}")
         
     | 
| 
       33 
45 
     | 
    
         
             
                    end
         
     | 
| 
       34 
46 
     | 
    
         | 
| 
       35 
47 
     | 
    
         
             
                    # @api private
         
     | 
| 
         @@ -62,12 +74,33 @@ module RSpec 
     | 
|
| 
       62 
74 
     | 
    
         | 
| 
       63 
75 
     | 
    
         
             
                  private
         
     | 
| 
       64 
76 
     | 
    
         | 
| 
       65 
     | 
    
         
            -
                    def  
     | 
| 
       66 
     | 
    
         
            -
                       
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                       
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
      
 77 
     | 
    
         
            +
                    def check_actual?(actual)
         
     | 
| 
      
 78 
     | 
    
         
            +
                      actual = actual.to_hash if convert_to_hash?(actual)
         
     | 
| 
      
 79 
     | 
    
         
            +
                      @actual = actual
         
     | 
| 
      
 80 
     | 
    
         
            +
                      @actual.respond_to?(:include?)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    def check_expected_count?
         
     | 
| 
      
 84 
     | 
    
         
            +
                      case
         
     | 
| 
      
 85 
     | 
    
         
            +
                      when !has_expected_count?
         
     | 
| 
      
 86 
     | 
    
         
            +
                        return false
         
     | 
| 
      
 87 
     | 
    
         
            +
                      when expecteds.size != 1
         
     | 
| 
      
 88 
     | 
    
         
            +
                        raise NotImplementedError, 'Count constraint supported only when testing for a single value being included'
         
     | 
| 
      
 89 
     | 
    
         
            +
                      when actual.is_a?(Hash)
         
     | 
| 
      
 90 
     | 
    
         
            +
                        raise NotImplementedError, 'Count constraint on hash keys not implemented'
         
     | 
| 
       70 
91 
     | 
    
         
             
                      end
         
     | 
| 
      
 92 
     | 
    
         
            +
                      true
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    def format_failure_message(preposition)
         
     | 
| 
      
 96 
     | 
    
         
            +
                      msg = if actual.respond_to?(:include?)
         
     | 
| 
      
 97 
     | 
    
         
            +
                              "expected #{description_of @actual} #{preposition}" \
         
     | 
| 
      
 98 
     | 
    
         
            +
                              " include#{readable_list_of @divergent_items}" \
         
     | 
| 
      
 99 
     | 
    
         
            +
                              "#{count_failure_reason('it is included') if has_expected_count?}"
         
     | 
| 
      
 100 
     | 
    
         
            +
                            else
         
     | 
| 
      
 101 
     | 
    
         
            +
                              "#{yield}, but it does not respond to `include?`"
         
     | 
| 
      
 102 
     | 
    
         
            +
                            end
         
     | 
| 
      
 103 
     | 
    
         
            +
                      improve_hash_formatting(msg)
         
     | 
| 
       71 
104 
     | 
    
         
             
                    end
         
     | 
| 
       72 
105 
     | 
    
         | 
| 
       73 
106 
     | 
    
         
             
                    def readable_list_of(items)
         
     | 
| 
         @@ -79,10 +112,9 @@ module RSpec 
     | 
|
| 
       79 
112 
     | 
    
         
             
                      end
         
     | 
| 
       80 
113 
     | 
    
         
             
                    end
         
     | 
| 
       81 
114 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
                    def perform_match( 
     | 
| 
       83 
     | 
    
         
            -
                      @actual = actual
         
     | 
| 
      
 115 
     | 
    
         
            +
                    def perform_match(&block)
         
     | 
| 
       84 
116 
     | 
    
         
             
                      @divergent_items = excluded_from_actual(&block)
         
     | 
| 
       85 
     | 
    
         
            -
                       
     | 
| 
      
 117 
     | 
    
         
            +
                      @divergent_items.empty?
         
     | 
| 
       86 
118 
     | 
    
         
             
                    end
         
     | 
| 
       87 
119 
     | 
    
         | 
| 
       88 
120 
     | 
    
         
             
                    def excluded_from_actual
         
     | 
| 
         @@ -107,7 +139,10 @@ module RSpec 
     | 
|
| 
       107 
139 
     | 
    
         
             
                    end
         
     | 
| 
       108 
140 
     | 
    
         | 
| 
       109 
141 
     | 
    
         
             
                    def actual_hash_includes?(expected_key, expected_value)
         
     | 
| 
       110 
     | 
    
         
            -
                      actual_value = 
     | 
| 
      
 142 
     | 
    
         
            +
                      actual_value =
         
     | 
| 
      
 143 
     | 
    
         
            +
                        actual.fetch(expected_key) do
         
     | 
| 
      
 144 
     | 
    
         
            +
                          actual.find(Proc.new { return false }) { |actual_key, _| values_match?(expected_key, actual_key) }[1]
         
     | 
| 
      
 145 
     | 
    
         
            +
                        end
         
     | 
| 
       111 
146 
     | 
    
         
             
                      values_match?(expected_value, actual_value)
         
     | 
| 
       112 
147 
     | 
    
         
             
                    end
         
     | 
| 
       113 
148 
     | 
    
         | 
| 
         @@ -131,6 +166,28 @@ module RSpec 
     | 
|
| 
       131 
166 
     | 
    
         
             
                      actual.any? { |value| values_match?(expected_item, value) }
         
     | 
| 
       132 
167 
     | 
    
         
             
                    end
         
     | 
| 
       133 
168 
     | 
    
         | 
| 
      
 169 
     | 
    
         
            +
                    if RUBY_VERSION < '1.9'
         
     | 
| 
      
 170 
     | 
    
         
            +
                      def count_enumerable(expected_item)
         
     | 
| 
      
 171 
     | 
    
         
            +
                        actual.select { |value| values_match?(expected_item, value) }.size
         
     | 
| 
      
 172 
     | 
    
         
            +
                      end
         
     | 
| 
      
 173 
     | 
    
         
            +
                    else
         
     | 
| 
      
 174 
     | 
    
         
            +
                      def count_enumerable(expected_item)
         
     | 
| 
      
 175 
     | 
    
         
            +
                        actual.count { |value| values_match?(expected_item, value) }
         
     | 
| 
      
 176 
     | 
    
         
            +
                      end
         
     | 
| 
      
 177 
     | 
    
         
            +
                    end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                    def count_inclusions
         
     | 
| 
      
 180 
     | 
    
         
            +
                      @divergent_items = expected
         
     | 
| 
      
 181 
     | 
    
         
            +
                      case actual
         
     | 
| 
      
 182 
     | 
    
         
            +
                      when String
         
     | 
| 
      
 183 
     | 
    
         
            +
                        actual.scan(expected.first).length
         
     | 
| 
      
 184 
     | 
    
         
            +
                      when Enumerable
         
     | 
| 
      
 185 
     | 
    
         
            +
                        count_enumerable(expected.first)
         
     | 
| 
      
 186 
     | 
    
         
            +
                      else
         
     | 
| 
      
 187 
     | 
    
         
            +
                        raise NotImplementedError, 'Count constraints are implemented for Enumerable and String values only'
         
     | 
| 
      
 188 
     | 
    
         
            +
                      end
         
     | 
| 
      
 189 
     | 
    
         
            +
                    end
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
       134 
191 
     | 
    
         
             
                    def diff_would_wrongly_highlight_matched_item?
         
     | 
| 
       135 
192 
     | 
    
         
             
                      return false unless actual.is_a?(String) && expected.is_a?(Array)
         
     | 
| 
       136 
193 
     | 
    
         | 
| 
         @@ -9,13 +9,20 @@ module RSpec 
     | 
|
| 
       9 
9 
     | 
    
         
             
                  class RaiseError
         
     | 
| 
       10 
10 
     | 
    
         
             
                    include Composable
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                     
     | 
| 
      
 12 
     | 
    
         
            +
                    # Used as a sentinel value to be able to tell when the user did not pass an
         
     | 
| 
      
 13 
     | 
    
         
            +
                    # argument. We can't use `nil` for that because we need to warn when `nil` is
         
     | 
| 
      
 14 
     | 
    
         
            +
                    # passed in a different way. It's an Object, not a Module, since Module's `===`
         
     | 
| 
      
 15 
     | 
    
         
            +
                    # does not evaluate to true when compared to itself.
         
     | 
| 
      
 16 
     | 
    
         
            +
                    UndefinedValue = Object.new.freeze
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    def initialize(expected_error_or_message, expected_message, &block)
         
     | 
| 
       13 
19 
     | 
    
         
             
                      @block = block
         
     | 
| 
       14 
20 
     | 
    
         
             
                      @actual_error = nil
         
     | 
| 
       15 
     | 
    
         
            -
                      @warn_about_bare_error = expected_error_or_message 
     | 
| 
      
 21 
     | 
    
         
            +
                      @warn_about_bare_error = UndefinedValue === expected_error_or_message
         
     | 
| 
      
 22 
     | 
    
         
            +
                      @warn_about_nil_error = expected_error_or_message.nil?
         
     | 
| 
       16 
23 
     | 
    
         | 
| 
       17 
24 
     | 
    
         
             
                      case expected_error_or_message
         
     | 
| 
       18 
     | 
    
         
            -
                      when nil
         
     | 
| 
      
 25 
     | 
    
         
            +
                      when nil, UndefinedValue
         
     | 
| 
       19 
26 
     | 
    
         
             
                        @expected_error = Exception
         
     | 
| 
       20 
27 
     | 
    
         
             
                        @expected_message = expected_message
         
     | 
| 
       21 
28 
     | 
    
         
             
                      when String
         
     | 
| 
         @@ -58,8 +65,11 @@ module RSpec 
     | 
|
| 
       58 
65 
     | 
    
         
             
                        end
         
     | 
| 
       59 
66 
     | 
    
         
             
                      end
         
     | 
| 
       60 
67 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
                       
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
      
 68 
     | 
    
         
            +
                      unless negative_expectation
         
     | 
| 
      
 69 
     | 
    
         
            +
                        warn_about_bare_error! if warn_about_bare_error?
         
     | 
| 
      
 70 
     | 
    
         
            +
                        warn_about_nil_error! if warn_about_nil_error?
         
     | 
| 
      
 71 
     | 
    
         
            +
                        eval_block if ready_to_eval_block?
         
     | 
| 
      
 72 
     | 
    
         
            +
                      end
         
     | 
| 
       63 
73 
     | 
    
         | 
| 
       64 
74 
     | 
    
         
             
                      expectation_matched?
         
     | 
| 
       65 
75 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -67,7 +77,7 @@ module RSpec 
     | 
|
| 
       67 
77 
     | 
    
         | 
| 
       68 
78 
     | 
    
         
             
                    # @private
         
     | 
| 
       69 
79 
     | 
    
         
             
                    def does_not_match?(given_proc)
         
     | 
| 
       70 
     | 
    
         
            -
                       
     | 
| 
      
 80 
     | 
    
         
            +
                      warn_for_negative_false_positives!
         
     | 
| 
       71 
81 
     | 
    
         
             
                      !matches?(given_proc, :negative_expectation) && Proc === given_proc
         
     | 
| 
       72 
82 
     | 
    
         
             
                    end
         
     | 
| 
       73 
83 
     | 
    
         | 
| 
         @@ -131,29 +141,35 @@ module RSpec 
     | 
|
| 
       131 
141 
     | 
    
         
             
                      values_match?(@expected_message, @actual_error.message.to_s)
         
     | 
| 
       132 
142 
     | 
    
         
             
                    end
         
     | 
| 
       133 
143 
     | 
    
         | 
| 
       134 
     | 
    
         
            -
                    def  
     | 
| 
      
 144 
     | 
    
         
            +
                    def warn_for_negative_false_positives!
         
     | 
| 
       135 
145 
     | 
    
         
             
                      expression = if expecting_specific_exception? && @expected_message
         
     | 
| 
       136 
146 
     | 
    
         
             
                                     "`expect { }.not_to raise_error(SpecificErrorClass, message)`"
         
     | 
| 
       137 
147 
     | 
    
         
             
                                   elsif expecting_specific_exception?
         
     | 
| 
       138 
148 
     | 
    
         
             
                                     "`expect { }.not_to raise_error(SpecificErrorClass)`"
         
     | 
| 
       139 
149 
     | 
    
         
             
                                   elsif @expected_message
         
     | 
| 
       140 
150 
     | 
    
         
             
                                     "`expect { }.not_to raise_error(message)`"
         
     | 
| 
      
 151 
     | 
    
         
            +
                                   elsif @warn_about_nil_error
         
     | 
| 
      
 152 
     | 
    
         
            +
                                     "`expect { }.not_to raise_error(nil)`"
         
     | 
| 
       141 
153 
     | 
    
         
             
                                   end
         
     | 
| 
       142 
154 
     | 
    
         | 
| 
       143 
155 
     | 
    
         
             
                      return unless expression
         
     | 
| 
       144 
156 
     | 
    
         | 
| 
       145 
     | 
    
         
            -
                      warn_about_negative_false_positive expression
         
     | 
| 
      
 157 
     | 
    
         
            +
                      warn_about_negative_false_positive! expression
         
     | 
| 
       146 
158 
     | 
    
         
             
                    end
         
     | 
| 
       147 
159 
     | 
    
         | 
| 
       148 
160 
     | 
    
         
             
                    def handle_warning(message)
         
     | 
| 
       149 
161 
     | 
    
         
             
                      RSpec::Expectations.configuration.false_positives_handler.call(message)
         
     | 
| 
       150 
162 
     | 
    
         
             
                    end
         
     | 
| 
       151 
163 
     | 
    
         | 
| 
       152 
     | 
    
         
            -
                    def  
     | 
| 
      
 164 
     | 
    
         
            +
                    def warn_about_bare_error?
         
     | 
| 
       153 
165 
     | 
    
         
             
                      @warn_about_bare_error && @block.nil?
         
     | 
| 
       154 
166 
     | 
    
         
             
                    end
         
     | 
| 
       155 
167 
     | 
    
         | 
| 
       156 
     | 
    
         
            -
                    def  
     | 
| 
      
 168 
     | 
    
         
            +
                    def warn_about_nil_error?
         
     | 
| 
      
 169 
     | 
    
         
            +
                      @warn_about_nil_error
         
     | 
| 
      
 170 
     | 
    
         
            +
                    end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                    def warn_about_bare_error!
         
     | 
| 
       157 
173 
     | 
    
         
             
                      handle_warning("Using the `raise_error` matcher without providing a specific " \
         
     | 
| 
       158 
174 
     | 
    
         
             
                                     "error or message risks false positives, since `raise_error` " \
         
     | 
| 
       159 
175 
     | 
    
         
             
                                     "will match when Ruby raises a `NoMethodError`, `NameError` or " \
         
     | 
| 
         @@ -166,11 +182,24 @@ module RSpec 
     | 
|
| 
       166 
182 
     | 
    
         
             
                                     "_positives = :nothing`")
         
     | 
| 
       167 
183 
     | 
    
         
             
                    end
         
     | 
| 
       168 
184 
     | 
    
         | 
| 
       169 
     | 
    
         
            -
                    def  
     | 
| 
      
 185 
     | 
    
         
            +
                    def warn_about_nil_error!
         
     | 
| 
      
 186 
     | 
    
         
            +
                      handle_warning("Using the `raise_error` matcher with a `nil` error is probably " \
         
     | 
| 
      
 187 
     | 
    
         
            +
                                     "unintentional, it risks false positives, since `raise_error` " \
         
     | 
| 
      
 188 
     | 
    
         
            +
                                     "will match when Ruby raises a `NoMethodError`, `NameError` or " \
         
     | 
| 
      
 189 
     | 
    
         
            +
                                     "`ArgumentError`, potentially allowing the expectation to pass " \
         
     | 
| 
      
 190 
     | 
    
         
            +
                                     "without even executing the method you are intending to call. " \
         
     | 
| 
      
 191 
     | 
    
         
            +
                                     "#{warning}"\
         
     | 
| 
      
 192 
     | 
    
         
            +
                                     "Instead consider providing a specific error class or message. " \
         
     | 
| 
      
 193 
     | 
    
         
            +
                                     "This message can be suppressed by setting: " \
         
     | 
| 
      
 194 
     | 
    
         
            +
                                     "`RSpec::Expectations.configuration.on_potential_false" \
         
     | 
| 
      
 195 
     | 
    
         
            +
                                     "_positives = :nothing`")
         
     | 
| 
      
 196 
     | 
    
         
            +
                    end
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                    def warn_about_negative_false_positive!(expression)
         
     | 
| 
       170 
199 
     | 
    
         
             
                      handle_warning("Using #{expression} risks false positives, since literally " \
         
     | 
| 
       171 
200 
     | 
    
         
             
                                     "any other error would cause the expectation to pass, " \
         
     | 
| 
       172 
     | 
    
         
            -
                                     "including those raised by Ruby (e.g. NoMethodError 
     | 
| 
       173 
     | 
    
         
            -
                                     "and ArgumentError), meaning the code you are intending to test " \
         
     | 
| 
      
 201 
     | 
    
         
            +
                                     "including those raised by Ruby (e.g. `NoMethodError`, `NameError` " \
         
     | 
| 
      
 202 
     | 
    
         
            +
                                     "and `ArgumentError`), meaning the code you are intending to test " \
         
     | 
| 
       174 
203 
     | 
    
         
             
                                     "may not even get reached. Instead consider using " \
         
     | 
| 
       175 
204 
     | 
    
         
             
                                     "`expect { }.not_to raise_error` or `expect { }.to raise_error" \
         
     | 
| 
       176 
205 
     | 
    
         
             
                                     "(DifferentSpecificErrorClass)`. This message can be suppressed by " \
         
     | 
| 
         @@ -1,7 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            RSpec::Support.require_rspec_support "method_signature_verifier"
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            # TODO: Refactor this file to be under our class length
         
     | 
| 
       4 
     | 
    
         
            -
            # rubocop:disable ClassLength
         
     | 
| 
       5 
3 
     | 
    
         
             
            module RSpec
         
     | 
| 
       6 
4 
     | 
    
         
             
              module Matchers
         
     | 
| 
       7 
5 
     | 
    
         
             
                module BuiltIn
         
     | 
| 
         @@ -118,49 +116,15 @@ module RSpec 
     | 
|
| 
       118 
116 
     | 
    
         
             
                      end
         
     | 
| 
       119 
117 
     | 
    
         
             
                    end
         
     | 
| 
       120 
118 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
                    def setup_method_signature_expectation
         
     | 
| 
       122 
     | 
    
         
            -
                      expectation = Support::MethodSignatureExpectation.new
         
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
       124 
     | 
    
         
            -
                      if @expected_arity.is_a?(Range)
         
     | 
| 
       125 
     | 
    
         
            -
                        expectation.min_count = @expected_arity.min
         
     | 
| 
       126 
     | 
    
         
            -
                        expectation.max_count = @expected_arity.max
         
     | 
| 
       127 
     | 
    
         
            -
                      else
         
     | 
| 
       128 
     | 
    
         
            -
                        expectation.min_count = @expected_arity
         
     | 
| 
       129 
     | 
    
         
            -
                      end
         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
                      expectation.keywords = @expected_keywords
         
     | 
| 
       132 
     | 
    
         
            -
                      expectation.expect_unlimited_arguments = @unlimited_arguments
         
     | 
| 
       133 
     | 
    
         
            -
                      expectation.expect_arbitrary_keywords  = @arbitrary_keywords
         
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
                      expectation
         
     | 
| 
       136 
     | 
    
         
            -
                    end
         
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
119 
     | 
    
         
             
                    def matches_arity?(actual, name)
         
     | 
| 
       139 
     | 
    
         
            -
                       
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
                      return true if  
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                        return true if @ignoring_method_signature_failure
         
     | 
| 
       148 
     | 
    
         
            -
                        raise ArgumentError, "The #{matcher_name} matcher requires that " \
         
     | 
| 
       149 
     | 
    
         
            -
                                             "the actual object define the method(s) in " \
         
     | 
| 
       150 
     | 
    
         
            -
                                             "order to check arity, but the method " \
         
     | 
| 
       151 
     | 
    
         
            -
                                             "`#{name}` is not defined. Remove the arity " \
         
     | 
| 
       152 
     | 
    
         
            -
                                             "check or define the method to continue."
         
     | 
| 
       153 
     | 
    
         
            -
                      end
         
     | 
| 
       154 
     | 
    
         
            -
                    end
         
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
                    def method_signature_for(actual, name)
         
     | 
| 
       157 
     | 
    
         
            -
                      method_handle = Support.method_handle_for(actual, name)
         
     | 
| 
       158 
     | 
    
         
            -
             
     | 
| 
       159 
     | 
    
         
            -
                      if name == :new && method_handle.owner === ::Class && ::Class === actual
         
     | 
| 
       160 
     | 
    
         
            -
                        Support::MethodSignature.new(actual.instance_method(:initialize))
         
     | 
| 
       161 
     | 
    
         
            -
                      else
         
     | 
| 
       162 
     | 
    
         
            -
                        Support::MethodSignature.new(method_handle)
         
     | 
| 
       163 
     | 
    
         
            -
                      end
         
     | 
| 
      
 120 
     | 
    
         
            +
                      ArityCheck.new(@expected_arity, @expected_keywords, @arbitrary_keywords, @unlimited_arguments).matches?(actual, name)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    rescue NameError
         
     | 
| 
      
 122 
     | 
    
         
            +
                      return true if @ignoring_method_signature_failure
         
     | 
| 
      
 123 
     | 
    
         
            +
                      raise ArgumentError, "The #{matcher_name} matcher requires that " \
         
     | 
| 
      
 124 
     | 
    
         
            +
                                           "the actual object define the method(s) in " \
         
     | 
| 
      
 125 
     | 
    
         
            +
                                           "order to check arity, but the method " \
         
     | 
| 
      
 126 
     | 
    
         
            +
                                           "`#{name}` is not defined. Remove the arity " \
         
     | 
| 
      
 127 
     | 
    
         
            +
                                           "check or define the method to continue."
         
     | 
| 
       164 
128 
     | 
    
         
             
                    end
         
     | 
| 
       165 
129 
     | 
    
         | 
| 
       166 
130 
     | 
    
         
             
                    def with_arity
         
     | 
| 
         @@ -192,8 +156,45 @@ module RSpec 
     | 
|
| 
       192 
156 
     | 
    
         
             
                    def pp_names
         
     | 
| 
       193 
157 
     | 
    
         
             
                      @names.length == 1 ? "##{@names.first}" : description_of(@names)
         
     | 
| 
       194 
158 
     | 
    
         
             
                    end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 161 
     | 
    
         
            +
                    class ArityCheck
         
     | 
| 
      
 162 
     | 
    
         
            +
                      def initialize(expected_arity, expected_keywords, arbitrary_keywords, unlimited_arguments)
         
     | 
| 
      
 163 
     | 
    
         
            +
                        expectation = Support::MethodSignatureExpectation.new
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                        if expected_arity.is_a?(Range)
         
     | 
| 
      
 166 
     | 
    
         
            +
                          expectation.min_count = expected_arity.min
         
     | 
| 
      
 167 
     | 
    
         
            +
                          expectation.max_count = expected_arity.max
         
     | 
| 
      
 168 
     | 
    
         
            +
                        else
         
     | 
| 
      
 169 
     | 
    
         
            +
                          expectation.min_count = expected_arity
         
     | 
| 
      
 170 
     | 
    
         
            +
                        end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                        expectation.keywords = expected_keywords
         
     | 
| 
      
 173 
     | 
    
         
            +
                        expectation.expect_unlimited_arguments = unlimited_arguments
         
     | 
| 
      
 174 
     | 
    
         
            +
                        expectation.expect_arbitrary_keywords  = arbitrary_keywords
         
     | 
| 
      
 175 
     | 
    
         
            +
                        @expectation = expectation
         
     | 
| 
      
 176 
     | 
    
         
            +
                      end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                      def matches?(actual, name)
         
     | 
| 
      
 179 
     | 
    
         
            +
                        return true if @expectation.empty?
         
     | 
| 
      
 180 
     | 
    
         
            +
                        verifier_for(actual, name).with_expectation(@expectation).valid?
         
     | 
| 
      
 181 
     | 
    
         
            +
                      end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                      def verifier_for(actual, name)
         
     | 
| 
      
 184 
     | 
    
         
            +
                        Support::StrictSignatureVerifier.new(method_signature_for(actual, name))
         
     | 
| 
      
 185 
     | 
    
         
            +
                      end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                      def method_signature_for(actual, name)
         
     | 
| 
      
 188 
     | 
    
         
            +
                        method_handle = Support.method_handle_for(actual, name)
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                        if name == :new && method_handle.owner === ::Class && ::Class === actual
         
     | 
| 
      
 191 
     | 
    
         
            +
                          Support::MethodSignature.new(actual.instance_method(:initialize))
         
     | 
| 
      
 192 
     | 
    
         
            +
                        else
         
     | 
| 
      
 193 
     | 
    
         
            +
                          Support::MethodSignature.new(method_handle)
         
     | 
| 
      
 194 
     | 
    
         
            +
                        end
         
     | 
| 
      
 195 
     | 
    
         
            +
                      end
         
     | 
| 
      
 196 
     | 
    
         
            +
                    end
         
     | 
| 
       195 
197 
     | 
    
         
             
                  end
         
     | 
| 
       196 
198 
     | 
    
         
             
                end
         
     | 
| 
       197 
199 
     | 
    
         
             
              end
         
     | 
| 
       198 
200 
     | 
    
         
             
            end
         
     | 
| 
       199 
     | 
    
         
            -
            # rubocop:enable ClassLength
         
     | 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rspec/matchers/built_in/count_expectation'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            RSpec::Support.require_rspec_support 'method_signature_verifier'
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
            module RSpec
         
     | 
| 
         @@ -97,64 +99,12 @@ module RSpec 
     | 
|
| 
       97 
99 
     | 
    
         
             
                  # Provides the implementation for `yield_control`.
         
     | 
| 
       98 
100 
     | 
    
         
             
                  # Not intended to be instantiated directly.
         
     | 
| 
       99 
101 
     | 
    
         
             
                  class YieldControl < BaseMatcher
         
     | 
| 
       100 
     | 
    
         
            -
                     
     | 
| 
       101 
     | 
    
         
            -
                      at_least(:once)
         
     | 
| 
       102 
     | 
    
         
            -
                    end
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                    # @api public
         
     | 
| 
       105 
     | 
    
         
            -
                    # Specifies that the method is expected to yield once.
         
     | 
| 
       106 
     | 
    
         
            -
                    def once
         
     | 
| 
       107 
     | 
    
         
            -
                      exactly(1)
         
     | 
| 
       108 
     | 
    
         
            -
                      self
         
     | 
| 
       109 
     | 
    
         
            -
                    end
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                    # @api public
         
     | 
| 
       112 
     | 
    
         
            -
                    # Specifies that the method is expected to yield twice.
         
     | 
| 
       113 
     | 
    
         
            -
                    def twice
         
     | 
| 
       114 
     | 
    
         
            -
                      exactly(2)
         
     | 
| 
       115 
     | 
    
         
            -
                      self
         
     | 
| 
       116 
     | 
    
         
            -
                    end
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
                    # @api public
         
     | 
| 
       119 
     | 
    
         
            -
                    # Specifies that the method is expected to yield thrice.
         
     | 
| 
       120 
     | 
    
         
            -
                    def thrice
         
     | 
| 
       121 
     | 
    
         
            -
                      exactly(3)
         
     | 
| 
       122 
     | 
    
         
            -
                      self
         
     | 
| 
       123 
     | 
    
         
            -
                    end
         
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
                    # @api public
         
     | 
| 
       126 
     | 
    
         
            -
                    # Specifies that the method is expected to yield the given number of times.
         
     | 
| 
       127 
     | 
    
         
            -
                    def exactly(number)
         
     | 
| 
       128 
     | 
    
         
            -
                      set_expected_yields_count(:==, number)
         
     | 
| 
       129 
     | 
    
         
            -
                      self
         
     | 
| 
       130 
     | 
    
         
            -
                    end
         
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
                    # @api public
         
     | 
| 
       133 
     | 
    
         
            -
                    # Specifies the maximum number of times the method is expected to yield
         
     | 
| 
       134 
     | 
    
         
            -
                    def at_most(number)
         
     | 
| 
       135 
     | 
    
         
            -
                      set_expected_yields_count(:<=, number)
         
     | 
| 
       136 
     | 
    
         
            -
                      self
         
     | 
| 
       137 
     | 
    
         
            -
                    end
         
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                    # @api public
         
     | 
| 
       140 
     | 
    
         
            -
                    # Specifies the minimum number of times the method is expected to yield
         
     | 
| 
       141 
     | 
    
         
            -
                    def at_least(number)
         
     | 
| 
       142 
     | 
    
         
            -
                      set_expected_yields_count(:>=, number)
         
     | 
| 
       143 
     | 
    
         
            -
                      self
         
     | 
| 
       144 
     | 
    
         
            -
                    end
         
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
                    # @api public
         
     | 
| 
       147 
     | 
    
         
            -
                    # No-op. Provides syntactic sugar.
         
     | 
| 
       148 
     | 
    
         
            -
                    def times
         
     | 
| 
       149 
     | 
    
         
            -
                      self
         
     | 
| 
       150 
     | 
    
         
            -
                    end
         
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
      
 102 
     | 
    
         
            +
                    include CountExpectation
         
     | 
| 
       152 
103 
     | 
    
         
             
                    # @private
         
     | 
| 
       153 
104 
     | 
    
         
             
                    def matches?(block)
         
     | 
| 
       154 
105 
     | 
    
         
             
                      @probe = YieldProbe.probe(block)
         
     | 
| 
       155 
106 
     | 
    
         
             
                      return false unless @probe.has_block?
         
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
       157 
     | 
    
         
            -
                      @probe.num_yields.__send__(@expectation_type, @expected_yields_count)
         
     | 
| 
      
 107 
     | 
    
         
            +
                      expected_count_matches?(@probe.num_yields)
         
     | 
| 
       158 
108 
     | 
    
         
             
                    end
         
     | 
| 
       159 
109 
     | 
    
         | 
| 
       160 
110 
     | 
    
         
             
                    # @private
         
     | 
| 
         @@ -181,37 +131,10 @@ module RSpec 
     | 
|
| 
       181 
131 
     | 
    
         | 
| 
       182 
132 
     | 
    
         
             
                  private
         
     | 
| 
       183 
133 
     | 
    
         | 
| 
       184 
     | 
    
         
            -
                    def set_expected_yields_count(relativity, n)
         
     | 
| 
       185 
     | 
    
         
            -
                      @expectation_type = relativity
         
     | 
| 
       186 
     | 
    
         
            -
                      @expected_yields_count = case n
         
     | 
| 
       187 
     | 
    
         
            -
                                               when Numeric then n
         
     | 
| 
       188 
     | 
    
         
            -
                                               when :once then 1
         
     | 
| 
       189 
     | 
    
         
            -
                                               when :twice then 2
         
     | 
| 
       190 
     | 
    
         
            -
                                               when :thrice then 3
         
     | 
| 
       191 
     | 
    
         
            -
                                               end
         
     | 
| 
       192 
     | 
    
         
            -
                    end
         
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
134 
     | 
    
         
             
                    def failure_reason
         
     | 
| 
       195 
135 
     | 
    
         
             
                      return ' but was not a block' unless @probe.has_block?
         
     | 
| 
       196 
     | 
    
         
            -
                      return  
     | 
| 
       197 
     | 
    
         
            -
                       
     | 
| 
       198 
     | 
    
         
            -
                      " but yielded #{human_readable_count(@probe.num_yields)}"
         
     | 
| 
       199 
     | 
    
         
            -
                    end
         
     | 
| 
       200 
     | 
    
         
            -
             
     | 
| 
       201 
     | 
    
         
            -
                    def human_readable_expectation_type
         
     | 
| 
       202 
     | 
    
         
            -
                      case @expectation_type
         
     | 
| 
       203 
     | 
    
         
            -
                      when :<= then 'at most '
         
     | 
| 
       204 
     | 
    
         
            -
                      when :>= then 'at least '
         
     | 
| 
       205 
     | 
    
         
            -
                      else ''
         
     | 
| 
       206 
     | 
    
         
            -
                      end
         
     | 
| 
       207 
     | 
    
         
            -
                    end
         
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
                    def human_readable_count(count)
         
     | 
| 
       210 
     | 
    
         
            -
                      case count
         
     | 
| 
       211 
     | 
    
         
            -
                      when 1 then 'once'
         
     | 
| 
       212 
     | 
    
         
            -
                      when 2 then 'twice'
         
     | 
| 
       213 
     | 
    
         
            -
                      else "#{count} times"
         
     | 
| 
       214 
     | 
    
         
            -
                      end
         
     | 
| 
      
 136 
     | 
    
         
            +
                      return "#{count_expectation_description} but did not yield" if @probe.num_yields == 0
         
     | 
| 
      
 137 
     | 
    
         
            +
                      count_failure_reason('yielded')
         
     | 
| 
       215 
138 
     | 
    
         
             
                    end
         
     | 
| 
       216 
139 
     | 
    
         
             
                  end
         
     | 
| 
       217 
140 
     | 
    
         |