Alpha Chen 2 years ago
parent fb7c012f2c
commit 7bc04b20e2
Signed by: alpha
SSH Key Fingerprint: SHA256:3fOT8fiYQG/aK9ntivV3Bqtg8AYQ7q4nV6ZgihOA20g

@ -193,9 +193,9 @@ module Minitest
class Possibility
attr_reader :produce, :name
def initialize(produce, name: "TODO")
@produce = produce
def initialize(name = "TODO", &produce)
@name = name
@produce = produce
end
def inspect = name
@ -203,41 +203,34 @@ module Minitest
# "Returns a `Possibility` where values come from applying `f` to some possible value for `self`."
def map(&f)
self.class.new(
->(test_case) { f.(test_case.any(self)) },
name: "#{name}.map(TODO)",
)
self.class.new("#{name}.map(TODO)") {|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)
produce = ->(test_case) { test_case.any(f.(test_case.any(self))) }
self.class.new(produce, name: "#{name}.bind(TODO)")
self.class.new("#{name}.bind(TODO)") {|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)
produce = ->(test_case) {
3.times do
self.class.new("#{name}.select(TODO)") {|test_case|
3.times.first {
candidate = test_case.any(self)
return candidate if f.(candidate)
end
test_case.reject
candidate if f.(candidate)
} || test_case.reject
}
self.class.new(produce, name: "#{name}.select(TODO)")
end
end
# Any integer in the range [m, n] is possible
def integers(m, n) = Possibility.new(->(tc) { m + tc.choice(n - m) }, name: "integers(#{m}, #{n})")
def integers(m, n) = Possibility.new("integers(#{m}, #{n})") {|tc| m + tc.choice(n - m) }
# Any lists whose elements are possible values from `elements` are possible.
def lists(elements, min_size: 0, max_size: Float::INFINITY)
produce = ->(test_case) {
Possibility.new("lists(#{elements.name})") {|test_case|
result = []
loop do
if result.length < min_size
@ -252,33 +245,29 @@ module Minitest
end
result
}
Possibility.new(produce, name: "lists(#{elements.name})")
end
# Only `value` is possible.
def just(value) = Possibility.new(->(_) { value }, name: "just(#{value})")
def just(value) = Possibility.new("just(#{value})") { value }
# No possible values. i.e. Any call to `any` will reject the test case.
def nothing = Possibility.new(->(tc) { tc.reject })
def nothing = Possibility.new {|tc| tc.reject }
# Possible values can be any value possible for one of `possibilities`.
def mix_of(*possibilities)
return nothing if possibilities.empty?
Possibility.new(
->(tc) { tc.any(possibilities[tc.choice(possibilities.length - 1)]) },
name: "mix_of(#{possibilities.map(&:name).join(", ")})",
)
Possibility.new("mix_of(#{possibilities.map(&:name).join(", ")})") {|tc|
tc.any(possibilities[tc.choice(possibilities.length - 1)])
}
end
# Any tuple t of of length len(possibilities) such that t[i] is possible
# for possibilities[i] is possible.
def tuples(*possibilities)
Possibility.new(
->(tc) { possibilities.map {|p| tc.any(p) } },
name: "tuples(#{possibilities.map(&:name).join(", ")})",
)
Possibility.new( "tuples(#{possibilities.map(&:name).join(", ")})") {|tc|
possibilities.map {|p| tc.any(p) }
}
end
# We cap the maximum amount of entropy a test case can use.

@ -31,10 +31,10 @@ class Minitest::ThesisTest < Minitest::Thesis
# 1001]``, and then lowering the length by two, turning it into ``[1001]`` as
# desired.
def test_finds_small_list_even_with_bad_lists
bad_list = Possibility.new(
->(tc) { n = tc.choice(10); Array.new(n) { tc.choice(10_000) }},
name: "bad_list",
)
bad_list = Possibility.new("bad_list") {|tc|
n = tc.choice(10)
Array.new(n) { tc.choice(10_000) }
}
(0...10).each do |seed|
out, _ = capture_io do
@ -321,7 +321,10 @@ class Minitest::ThesisTest < Minitest::Thesis
def test_selected_possibility
run_test("selected_possibility", database: {}) do |tc|
n = tc.any(integers(0, 5).satisfying(&:even?))
n = tc.any(
integers(0, 5)
.satisfying(&:even?)
)
assert n.even?
end
end

Loading…
Cancel
Save