Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor with MapArbitrary #11

Merged
merged 2 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions lib/pbt/arbitrary/arbitrary.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module Pbt
module Arbitrary
# @abstract
class Arbitrary
# @abstract
# @param rng [Random]
# @return [Object]
def generate(rng)
raise NotImplementedError
end

# @abstract
# @param current [Object]
# @return [Enumerator]
def shrink(current)
raise NotImplementedError
end

# @param mapper [Proc] a function to map the generated value. it's mainly used for #generate.
# @param unmapper [Proc] a function to unmap the generated value. it's used for #shrink.
def map(mapper, unmapper)
MapArbitrary.new(self, mapper, unmapper)
end

# @param refinement [Proc] a function to filter the generated value and shrunken values.
def filter(&refinement)
FilterArbitrary.new(self, &refinement)
end
end
end
end
15 changes: 8 additions & 7 deletions lib/pbt/arbitrary/arbitrary_methods.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# frozen_string_literal: true

require "pbt/arbitrary/arbitrary"
require "pbt/arbitrary/constant"
require "pbt/arbitrary/array_arbitrary"
require "pbt/arbitrary/char_arbitrary"
require "pbt/arbitrary/integer_arbitrary"
require "pbt/arbitrary/tuple_arbitrary"
require "pbt/arbitrary/fixed_hash_arbitrary"
require "pbt/arbitrary/choose_arbitrary"
require "pbt/arbitrary/one_of_arbitrary"
require "pbt/arbitrary/string_arbitrary"
require "pbt/arbitrary/map_arbitrary"
require "pbt/arbitrary/filter_arbitrary"

module Pbt
module Arbitrary
Expand Down Expand Up @@ -56,39 +57,39 @@ def one_of(*choices)

# Generates a single unicode character (including printable and non-printable).
def char
CharArbitrary.new
choose(0..0x10FFFF).map(CHAR_MAPPER, CHAR_UNMAPPER)
end

def alphanumeric_char
one_of(*ALPHANUMERIC_CHARS)
end

def alphanumeric_string(**kwargs)
StringArbitrary.new(array(alphanumeric_char, **kwargs))
array(alphanumeric_char, **kwargs).map(STRING_MAPPER, STRING_UNMAPPER)
end

def ascii_char
one_of(*ASCII_CHARS)
end

def ascii_string(**kwargs)
StringArbitrary.new(array(ascii_char, **kwargs))
array(ascii_char, **kwargs).map(STRING_MAPPER, STRING_UNMAPPER)
end

def printable_ascii_char
one_of(*PRINTABLE_ASCII_CHARS)
end

def printable_ascii_string(**kwargs)
StringArbitrary.new(array(printable_ascii_char, **kwargs))
array(printable_ascii_char, **kwargs).map(STRING_MAPPER, STRING_UNMAPPER)
end

def printable_char
one_of(*PRINTABLE_CHARS)
end

def printable_string(**kwargs)
StringArbitrary.new(array(printable_char, **kwargs))
array(printable_char, **kwargs).map(STRING_MAPPER, STRING_UNMAPPER)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/pbt/arbitrary/array_arbitrary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Pbt
module Arbitrary
class ArrayArbitrary
class ArrayArbitrary < Arbitrary
DEFAULT_MAX_SIZE = 10

# @param min_length [Integer]
Expand Down
36 changes: 0 additions & 36 deletions lib/pbt/arbitrary/char_arbitrary.rb

This file was deleted.

2 changes: 1 addition & 1 deletion lib/pbt/arbitrary/choose_arbitrary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Pbt
module Arbitrary
class ChooseArbitrary
class ChooseArbitrary < Arbitrary
# @param range [Range<Integer>]
def initialize(range)
@range = range
Expand Down
5 changes: 5 additions & 0 deletions lib/pbt/arbitrary/constant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@ module Arbitrary
*("\u{E000}".."\u{FFFD}"),
*("\u{10000}".."\u{10FFFF}")
].freeze

CHAR_MAPPER = ->(v) { [v].pack("U") }
CHAR_UNMAPPER = ->(v) { v.unpack1("U") }
STRING_MAPPER = ->(v) { v.join }
STRING_UNMAPPER = ->(v) { v.chars }
end
end
36 changes: 36 additions & 0 deletions lib/pbt/arbitrary/filter_arbitrary.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module Pbt
module Arbitrary
class FilterArbitrary < Arbitrary
# @param arb [ArrayArbitrary]
# @param refinement [Proc] a function to filter the generated value and shrunken values.
#
def initialize(arb, &refinement)
@arb = arb
@refinement = refinement
end

# @return [Array]
def generate(rng)
loop do
val = @arb.generate(rng)
return val if @refinement.call(val)
end
end

# @return [Enumerator]
def shrink(current)
Enumerator.new do |y|
@arb.shrink(current).each do |v|
if @refinement.call(v)
y.yield v
else
next
end
end
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/pbt/arbitrary/fixed_hash_arbitrary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Pbt
module Arbitrary
class FixedHashArbitrary
class FixedHashArbitrary < Arbitrary
# @param hash [Hash<Symbol->Pbt::Arbitrary>]
def initialize(hash)
@keys = hash.keys
Expand Down
2 changes: 1 addition & 1 deletion lib/pbt/arbitrary/integer_arbitrary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Pbt
module Arbitrary
class IntegerArbitrary
class IntegerArbitrary < Arbitrary
DEFAULT_TARGET = 0
DEFAULT_SIZE = 1000000

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,27 @@

module Pbt
module Arbitrary
class StringArbitrary
class MapArbitrary < Arbitrary
# @param arb [ArrayArbitrary]
def initialize(arb)
def initialize(arb, mapper, unmapper)
@arb = arb
@mapper = mapper
@unmapper = unmapper
end

# @return [Array]
def generate(rng)
map(@arb.generate(rng))
@mapper.call(@arb.generate(rng))
end

# @return [Enumerator]
def shrink(current)
Enumerator.new do |y|
@arb.shrink(unmap(current)).each do |v|
y.yield map(v)
@arb.shrink(@unmapper.call(current)).each do |v|
y.yield @mapper.call(v)
end
end
end

private

def map(v)
v.join
end

def unmap(v)
v.chars
end
end
end
end
2 changes: 1 addition & 1 deletion lib/pbt/arbitrary/one_of_arbitrary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Pbt
module Arbitrary
class OneOfArbitrary
class OneOfArbitrary < Arbitrary
# @param choices [Array]
def initialize(choices)
@choices = choices
Expand Down
2 changes: 1 addition & 1 deletion lib/pbt/arbitrary/tuple_arbitrary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Pbt
module Arbitrary
class TupleArbitrary
class TupleArbitrary < Arbitrary
# @param arbs [Array<Pbt::Arbitrary>]
def initialize(*arbs)
@arbs = arbs
Expand Down
21 changes: 21 additions & 0 deletions spec/pbt/arbitrary/arbitrary_methods_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
# frozen_string_literal: true

RSpec.describe Pbt::Arbitrary::ArbitraryMethods do
describe ".char" do
it "generates a character" do
val = Pbt.char.generate(Random.new)
expect(val).to be_a(String)
expect(val.size).to eq(1)
end

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

it "returns an Enumerator that iterates characters shrinking towards lower codepoint" do
arb = Pbt.char
expect(arb.shrink("z").to_a).to eq ["=", "\u001F", "\u0010", "\b", "\u0004", "\u0002", "\u0001", "\u0000"]
end
end
end

describe ".alphanumeric_char" do
it "generates a character" do
val = Pbt.alphanumeric_char.generate(Random.new)
Expand Down
24 changes: 0 additions & 24 deletions spec/pbt/arbitrary/char_arbitrary_spec.rb

This file was deleted.

37 changes: 37 additions & 0 deletions spec/pbt/arbitrary/filter_arbitrary_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

RSpec.describe Pbt::Arbitrary::FilterArbitrary do
describe "#initialize" do
it do
arb = Pbt.integer.filter { |n| n % 2 == 0 }
expect(arb).to be_a(Pbt::Arbitrary::FilterArbitrary)
end
end

describe "#generate" do
it "generates filtered values of given arbitrary" do
val = Pbt.integer.filter { |n| n % 2 == 0 }.generate(Random.new)
expect(val).to be_even
end
end

describe "#shrink" do
it "returns an Enumerator" do
arb = Pbt.integer.filter { |n| n % 2 == 0 }
val = arb.generate(Random.new)
expect(arb.shrink(val)).to be_a(Enumerator)
end

it "returns an Enumerator that iterates filtered values" do
arb = Pbt.integer.filter { |n| n % 2 == 0 }
expect(arb.shrink(50).to_a).to eq [4, 2, 0]
end

context "when current value and target is same" do
it "returns an empty Enumerator" do
arb = Pbt.integer.filter { |n| n % 2 == 0 }
expect(arb.shrink(0).to_a).to eq []
end
end
end
end
Loading