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
module Minitest::Thesis
  # Represents some range of values that might be used in a test, that can be
  # requested from a `TestCase`. Pass one of these to TestCase.any to get a
  # concrete value.
  class Possibility
    attr_reader :produce, :name

    def initialize(name, &produce)
      @name = name
      @produce = produce
    end

    def inspect = name
    def to_s = name

    # "Returns a `Possibility` where values come from applying `f` to some possible value for `self`."
    def map(&f)
      self.class.new("#{name}.map(#{f.class})") {|tc| f.call(tc.any(self)) }
    end

    # Returns a `Possibility` where values come from applying `f` (which
    # should return a new `Possibility` to some possible value for `self`
    # then returning a possible value from that.
    def bind(&f)
      self.class.new("#{name}.bind(#{f.class})") {|tc| tc.any(f.(tc.any(self))) }
    end

    # Returns a `Possibility` whose values are any possible value of `self`
    # for which `f` returns True.
    def satisfying(&f)
      self.class.new("#{name}.select(#{f.class})") {|test_case|
        3.times.first {
          candidate = test_case.any(self)
          candidate if f.(candidate)
        } || test_case.reject
      }
    end
  end
end