Skip to content

Commit

Permalink
Implement tuple arbitrary
Browse files Browse the repository at this point in the history
  • Loading branch information
ohbarye committed Mar 30, 2024
1 parent 7d07950 commit a43b7c6
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 7 deletions.
8 changes: 7 additions & 1 deletion lib/pbt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ class PropertyFailure < StandardError; end
# @param arbs [Array<Pbt::Arbitrary>]
# @return [Property]
def self.property(*arbs, &predicate)
Check::Property.new(*arbs, &predicate)
arb = if arbs.size == 1
arbs.first
else
# wrap by tuple arbitrary so that property class doesn't have to take care of an array
tuple(*arbs)
end
Check::Property.new(arb, &predicate)
end
end
6 changes: 6 additions & 0 deletions lib/pbt/arbitrary/arbitrary_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require "pbt/arbitrary/array_arbitrary"
require "pbt/arbitrary/integer_arbitrary"
require "pbt/arbitrary/tuple_arbitrary"

module Pbt
module Arbitrary
Expand All @@ -26,6 +27,11 @@ def array(arbitrary, min: 0, max: 10, empty: true)
min = 1 if min.zero? && !empty
ArrayArbitrary.new(arbitrary, min, max)
end

# @param arbs [Array<Pbt::Arbitrary>
def tuple(*arbs)
TupleArbitrary.new(*arbs)
end
end
end
end
31 changes: 31 additions & 0 deletions lib/pbt/arbitrary/tuple_arbitrary.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module Pbt
module Arbitrary
class TupleArbitrary
# @param arbs [Array<Pbt::Arbitrary>]
def initialize(*arbs)
@arbs = arbs
end

# @return [Array]
def generate(rng)
@arbs.map { |arb| arb.generate(rng) }
end

# @return [Enumerator]
def shrink(current)
# This is not the most comprehensive but allows a reasonable number of entries in the shrink
Enumerator.new do |y|
@arbs.each_with_index do |arb, idx|
arb.shrink(current[idx]).each do |v|
next_values = current.dup
next_values[idx] = v
y << next_values
end
end
end
end
end
end
end
7 changes: 3 additions & 4 deletions lib/pbt/check/property.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
module Pbt
module Check
class Property
# @param arbs [Array<Pbt::Arbitrary>]
# @param arb [Array<Pbt::Arbitrary>]
# @param predicate [Proc]
def initialize(*arbs, &predicate)
p arbs
@arb = arbs[0] # TODO: Support multiple arbs after implementing TupleArbitrary
def initialize(arb, &predicate)
@arb = arb
@predicate = predicate
end

Expand Down
2 changes: 1 addition & 1 deletion spec/e2e/e2e_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
describe "property failure" do
it "describes a property" do
Pbt.assert do
Pbt.property(Pbt.integer, Pbt.integer) do |n1, n2 = 1| # TODO: remove default value
Pbt.property(Pbt.integer, Pbt.integer) do |n1, n2|
raise if PbtTestTarget.biggest([n1, n2]) != [n1, n2].max
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/pbt/arbitrary/array_arbitrary_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

it "generates an array of given arbitrary" do
val = Pbt::Arbitrary::ArrayArbitrary.new(Pbt.integer).generate(Random.new)
val.all? { |e| expect(e).to be_a(Integer) }
val.each { |e| expect(e).to be_a(Integer) }
end

it "allows to specify size with min and max" do
Expand Down
60 changes: 60 additions & 0 deletions spec/pbt/arbitrary/tuple_arbitrary_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

RSpec.describe Pbt::Arbitrary::TupleArbitrary do
describe "#generate" do
it "generates an array" do
val = Pbt::Arbitrary::TupleArbitrary.new(Pbt.integer, Pbt.integer).generate(Random.new)
expect(val).to be_a(Array)
end

it "generates an array of given arbitrary" do
val = Pbt::Arbitrary::TupleArbitrary.new(Pbt.integer, Pbt.integer).generate(Random.new)
val.each { |e| expect(e).to be_a(Integer) }
end
end

describe "#shrink" do
it "returns an Enumerator" do
arb = Pbt::Arbitrary::TupleArbitrary.new(Pbt.integer)
val = arb.generate(Random.new)
expect(arb.shrink(val)).to be_a(Enumerator)
end

it "returns an Enumerator that returns shrunken values" do
arb = Pbt::Arbitrary::TupleArbitrary.new(Pbt.integer)
expect(arb.shrink([50]).to_a).to eq [
[25],
[13],
[7],
[4],
[2],
[1],
[0]
]
end

it "returns an Enumerator that returns shrunken values for each arbitraries" do
arb = Pbt::Arbitrary::TupleArbitrary.new(Pbt.integer, Pbt.integer)
expect(arb.shrink([10, 20]).to_a).to eq [
[5, 20],
[3, 20],
[2, 20],
[1, 20],
[0, 20],
[10, 10],
[10, 5],
[10, 3],
[10, 2],
[10, 1],
[10, 0]
]
end

context "when current value and target is same" do
it "returns an empty Enumerator" do
arb = Pbt::Arbitrary::TupleArbitrary.new(Pbt.integer, Pbt.integer)
expect(arb.shrink([0, 0]).to_a).to eq []
end
end
end
end

0 comments on commit a43b7c6

Please sign in to comment.