1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
module RSpec
module Matchers
module BuiltIn
# @api private
# Abstract class to implement `once`, `at_least` and other
# count constraints.
module CountExpectation
# @api public
# Specifies that the method is expected to match once.
def once
exactly(1)
end
# @api public
# Specifies that the method is expected to match twice.
def twice
exactly(2)
end
# @api public
# Specifies that the method is expected to match thrice.
def thrice
exactly(3)
end
# @api public
# Specifies that the method is expected to match the given number of times.
def exactly(number)
set_expected_count(:==, number)
self
end
# @api public
# Specifies the maximum number of times the method is expected to match
def at_most(number)
set_expected_count(:<=, number)
self
end
# @api public
# Specifies the minimum number of times the method is expected to match
def at_least(number)
set_expected_count(:>=, number)
self
end
# @api public
# No-op. Provides syntactic sugar.
def times
self
end
protected
# @api private
attr_reader :count_expectation_type, :expected_count
private
if RUBY_VERSION.to_f > 1.8
def cover?(count, number)
count.cover?(number)
end
else
def cover?(count, number)
number >= count.first && number <= count.last
end
end
def expected_count_matches?(actual_count)
@actual_count = actual_count
return @actual_count > 0 unless count_expectation_type
return cover?(expected_count, actual_count) if count_expectation_type == :<=>
@actual_count.__send__(count_expectation_type, expected_count)
end
def has_expected_count?
!!count_expectation_type
end
def set_expected_count(relativity, n)
raise_unsupported_count_expectation if unsupported_count_expectation?(relativity)
count = count_constraint_to_number(n)
if count_expectation_type == :<= && relativity == :>=
raise_impossible_count_expectation(count) if count > expected_count
@count_expectation_type = :<=>
@expected_count = count..expected_count
elsif count_expectation_type == :>= && relativity == :<=
raise_impossible_count_expectation(count) if count < expected_count
@count_expectation_type = :<=>
@expected_count = expected_count..count
else
@count_expectation_type = relativity
@expected_count = count
end
end
def raise_impossible_count_expectation(count)
text =
case count_expectation_type
when :<= then "at_least(#{count}).at_most(#{expected_count})"
when :>= then "at_least(#{expected_count}).at_most(#{count})"
end
raise ArgumentError, "The constraint #{text} is not possible"
end
def raise_unsupported_count_expectation
text =
case count_expectation_type
when :<= then "at_least"
when :>= then "at_most"
when :<=> then "at_least/at_most combination"
else "count"
end
raise ArgumentError, "Multiple #{text} constraints are not supported"
end
def count_constraint_to_number(n)
case n
when Numeric then n
when :once then 1
when :twice then 2
when :thrice then 3
else
raise ArgumentError, "Expected a number, :once, :twice or :thrice," \
" but got #{n}"
end
end
def unsupported_count_expectation?(relativity)
return true if count_expectation_type == :==
return true if count_expectation_type == :<=>
(count_expectation_type == :<= && relativity == :<=) ||
(count_expectation_type == :>= && relativity == :>=)
end
def count_expectation_description
"#{human_readable_expectation_type}#{human_readable_count(expected_count)}"
end
def count_failure_reason(action)
"#{count_expectation_description}" \
" but #{action}#{human_readable_count(@actual_count)}"
end
def human_readable_expectation_type
case count_expectation_type
when :<= then ' at most'
when :>= then ' at least'
when :<=> then ' between'
else ''
end
end
def human_readable_count(count)
case count
when Range then " #{count.first} and #{count.last} times"
when nil then ''
when 1 then ' once'
when 2 then ' twice'
else " #{count} times"
end
end
end
end
end
end
|