module RSpec::Matchers::BuiltIn::CountExpectation

def at_least(number)

Other tags:
    Api: - public
def at_least(number)
  set_expected_count(:>=, number)
  self
end

def at_most(number)

Other tags:
    Api: - public
def at_most(number)
  set_expected_count(:<=, number)
  self
end

def count_constraint_to_number(n)

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 count_expectation_description

def count_expectation_description
  "#{human_readable_expectation_type}#{human_readable_count(expected_count)}"
end

def count_failure_reason(action)

def count_failure_reason(action)
  "#{count_expectation_description}" \
  " but #{action}#{human_readable_count(@actual_count)}"
end

def cover?(count, number)

def cover?(count, number)
  count.cover?(number)
end

def cover?(count, number)

:nocov:
def cover?(count, number)
  number >= count.first && number <= count.last
end

def exactly(number)

Other tags:
    Api: - public
def exactly(number)
  set_expected_count(:==, number)
  self
end

def expected_count_matches?(actual_count)

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?

def has_expected_count?
  !!count_expectation_type
end

def human_readable_count(count)

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

def human_readable_expectation_type

def human_readable_expectation_type
  case count_expectation_type
  when :<= then ' at most'
  when :>= then ' at least'
  when :<=> then ' between'
  else ''
  end
end

def once

Other tags:
    Api: - public
def once
  exactly(1)
end

def raise_impossible_count_expectation(count)

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

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 set_expected_count(relativity, n)

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 thrice

Other tags:
    Api: - public
def thrice
  exactly(3)
end

def times

Other tags:
    Api: - public
def times
  self
end

def twice

Other tags:
    Api: - public
def twice
  exactly(2)
end

def unsupported_count_expectation?(relativity)

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