Class: RushCheck::Gen

Inherits:
Object
  • Object
show all
Defined in:
lib/rushcheck/gen.rb

Overview

Gen provides functions for generating test instances.

Constant Summary collapse

@@max_create =
10000

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(&f) ⇒ Gen

to initialize Gen object, it requires a block which takes two variables. The first argument of block is assumed as an integer, and the second one is assumed as a random generator of RandomGen.



167
168
169
# File 'lib/rushcheck/gen.rb', line 167

def initialize(&f)
  @proc = f    
end

Class Method Details

.choose(lo = nil, hi = nil) ⇒ Object

choose is one of primitive generators to create a random Gen object. choose returns a Gen object which generates a random value in the bound. It may useful to implement arbitrary method into your class.



19
20
21
22
23
# File 'lib/rushcheck/gen.rb', line 19

def self.choose(lo=nil, hi=nil)
  rand.fmap do |x| 
    lo.class.random(x, lo, hi).first
  end
end

.create(*cs, &f) ⇒ Object

create is one of primitive generators to create a random Gen object. create takes at least a class, and any block to generate object. Then create returns a Gen object. It may useful to implement arbitrary method into your class.



59
60
61
# File 'lib/rushcheck/gen.rb', line 59

def self.create(*cs, &f)
  self.create_by_gen(cs.map {|c| c.arbitrary}, &f) 
end

.create_by_gen(xs, &f) ⇒ Object

create_gen is one of primitive generators to create a random Gen object. create_gen takes an array of Gen objects, and any block to generate object. Then create_gen returns a Gen object. It may useful to implement arbitrary method into your class.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/rushcheck/gen.rb', line 29

def self.create_by_gen(xs, &f)
  self.new do |n, r|
    r2 = r

    try = 0
    begin
      if try > @@max_create 
        raise(RuntimeError, "Failed to guards too many in the assertion.") 
      end
      args = xs.map do |gen|
        r1, r2 = r2.split
        gen.value(n, r1)
      end
      f.call(*args)
    rescue Exception => ex
      case ex
      when RushCheck::GuardException
        try += 1
        retry
      else
        raise(ex, ex.to_s)
      end
    end
  end
end

.elements(xs) ⇒ Object

elements is one of primitive generators to create a random Gen object. elements requires an array and returns a Gen object which generates an object in the array randomly. It may useful to implement arbitrary method into your class.

Raises:

  • (RuntimeError)


67
68
69
70
71
# File 'lib/rushcheck/gen.rb', line 67

def self.elements(xs)
  raise(RuntimeError, "given argument is empty") if xs.empty?

  choose(0, xs.length - 1).fmap {|i| xs[i] }
end

.frequency(xs) ⇒ Object

frequency is one of primitive generators to create a random Gen object. frequency requires an array of pairs and returns a Gen object. The first component of pair should be a positive Integer and the second one should be a Gen object. The integer acts as a weight for choosing random Gen object in the array. For example, frequency([[1, Gen.rand], [2, Integer.arbitrary]]) returns the random generator Gen.rand in 33%, while another random generator of Integer (Integer.arbitrary) in 67%.

Raises:

  • (RuntimeError)


81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rushcheck/gen.rb', line 81

def self.frequency(xs)
  tot = xs.inject(0) {|r, pair| r + pair.first}
  raise(RuntimeError, "Illegal frequency:#{xs.inspect}") if tot == 0
  choose(0, tot - 1).bind do |n|
    m = n
    xs.each do |pair|
      if m <= pair[0]
      then break pair[1]
      else m -= pair[0]
      end
    end
  end 
end

.lift_array(xs) ⇒ Object

lift_array is one of primitive generators to create a randam Gen object. lift_array takes an array and a block which has a variable. The block should return a Gen object. lift_array returns a Gen object which generates an array of the result of given block for applying each member of given array.



100
101
102
103
104
105
106
107
108
# File 'lib/rushcheck/gen.rb', line 100

def self.lift_array(xs)
  self.new do |n, r|
    r2 = r
    xs.map do |c|
      r1, r2 = r2.split
      yield.value(n, r1)
    end
  end
end

.oneof(gens) ⇒ Object

oneof is /one of/ primitive generators to create a random Gen object. oneof requires an array of Gen objects, and returns a Gen object which choose a Gen object in the array randomly. It may useful to implement arbitrary method into your class.



114
115
116
# File 'lib/rushcheck/gen.rb', line 114

def self.oneof(gens)
  elements(gens).bind {|x| x}
end

.promoteObject

promote is the function to create a Gen object which generates a procedure (Proc). promote requires a block which takes one variable and the block should be return a Gen object. promote returns a Gen object which generate a new procedure with the given block. It may useful to implement coarbitrary method into your class.



124
125
126
# File 'lib/rushcheck/gen.rb', line 124

def self.promote
  new {|n, r| Proc.new {|a| yield(a).value(n, r) } }
end

.randObject

rand returns a Gen object which generates a random number generator.



130
131
132
# File 'lib/rushcheck/gen.rb', line 130

def self.rand
  new {|n, r| r}
end

.sizedObject

sized is a combinator which the programmer can use to access the size bound. It requires a block which takes a variable as an integer for size. The block should be a function which changes the size of random instances.



138
139
140
# File 'lib/rushcheck/gen.rb', line 138

def self.sized
  new {|n, r| yield(n).value(n, r) }
end

.unit(x) ⇒ Object

unit is a monadic function which equals the return function in the Haskell’s monad. It requires one variable and returns a Gen object which generates the given object.



145
146
147
# File 'lib/rushcheck/gen.rb', line 145

def self.unit(x)
  new {|n, r| x}
end

.vector(c, len) ⇒ Object

vector is one of primitive generators to create a Gen object. vector takes two variables, while the first one should be class, and the second one should be length. vector returns a Gen object which generates an array whose components belongs the given class and given length.



154
155
156
157
158
159
160
161
162
# File 'lib/rushcheck/gen.rb', line 154

def self.vector(c, len)
  new do |n, r|
    r2 = r
    (1..len).map do 
      r1, r2 = r2.split
      c.arbitrary.value(n, r1) 
    end
  end
end

Instance Method Details

#bindObject

bind is a monadic function such as Haskel’s (>>=). bind takes a block which has a variable where is the return value of the Gen object. The block should return a Gen object.



174
175
176
177
178
179
# File 'lib/rushcheck/gen.rb', line 174

def bind
  self.class.new do |n, r| 
    r1, r2 = r.split
    yield(value(n, r1)).value(n, r2)
  end
end

#fmapObject

fmap is a categorical function as same in Haskell. fmap requires a block which takes one variable.



191
192
193
# File 'lib/rushcheck/gen.rb', line 191

def fmap
  bind {|x| self.class.unit(yield(x)) }
end

#forallObject

forall is a function to create a Gen object. forall requires a block which takes any variables and returns a Property object. Then forall returns a generator of the property.



199
200
201
202
203
204
205
206
# File 'lib/rushcheck/gen.rb', line 199

def forall
  bind do |*a|
    yield(*a).property.gen.bind do |res|
      res.arguments.push(a.to_s)
      self.class.unit(res)
    end
  end
end

#generate(n, rnd) ⇒ Object

generate returns the random instance. generates takes two variables, where the first one should be an integer and the second should be the random number generator such as StdGen.



211
212
213
214
# File 'lib/rushcheck/gen.rb', line 211

def generate(n, rnd)
  s, r = Integer.random(rnd, 0, n)
  value(s, r)
end

#resize(n) ⇒ Object

resize returns another Gen object which resized by the given paramater. resize takes one variable in Integer.



218
219
220
# File 'lib/rushcheck/gen.rb', line 218

def resize(n)
  self.class.new {|x, r| value(n, r) }
end

#value(n, g) ⇒ Object

value is a method to get the value of the internal procedure. value takes two variables where the first argument is assumed as an integer and the second one is assumed as a random generator of RandomGen.



185
186
187
# File 'lib/rushcheck/gen.rb', line 185

def value(n, g)
  @proc.call(n, g)
end

#variant(v) ⇒ Object

variant constructs a generator which transforms the random number seed. variant takes one variable which should be an Integer. variant is needed to generate random functions.



225
226
227
228
229
230
231
# File 'lib/rushcheck/gen.rb', line 225

def variant(v)
  self.class.new do |n, r| 
    gen = r
    v.times { gen, dummy = gen.split }
    value(n, gen)
  end
end