Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a Functional Random Number Generator and a "Gen" class for doing …
…property based tests
- Loading branch information
1 parent
f4c0bc7
commit 7ba7c20
Showing
2 changed files
with
84 additions
and
0 deletions.
There are no files selected for viewing
34 changes: 34 additions & 0 deletions
34
src/test/java/ca/genovese/coffeecats/gen/FunctionalRandom.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package ca.genovese.coffeecats.gen; | ||
|
||
import ca.genovese.coffeecats.data.tuple.Tuple2; | ||
|
||
import java.security.SecureRandom; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
|
||
public class FunctionalRandom { | ||
private static final int DEFAULT_SEED_LENGTH = 20; | ||
private final int seedLength; | ||
private final SecureRandom rnd; | ||
private AtomicReference<Tuple2<Integer, FunctionalRandom>> value = new AtomicReference<>(null); | ||
|
||
public FunctionalRandom() { | ||
this(DEFAULT_SEED_LENGTH, new SecureRandom()); | ||
} | ||
|
||
public FunctionalRandom(int seedLength, SecureRandom rnd) { | ||
this.seedLength = seedLength; | ||
this.rnd = rnd; | ||
} | ||
|
||
public Tuple2<Integer, FunctionalRandom> nextInt() { | ||
return value.updateAndGet((v) -> { | ||
if (v != null) return v; | ||
else { | ||
byte[] seed = new byte[seedLength]; | ||
rnd.nextBytes(seed); | ||
return new Tuple2<>(rnd.nextInt(), new FunctionalRandom(seedLength, new SecureRandom(seed))); | ||
} | ||
}); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package ca.genovese.coffeecats.gen; | ||
|
||
import ca.genovese.coffeecats.data.List; | ||
import ca.genovese.coffeecats.data.Option; | ||
import ca.genovese.coffeecats.data.tuple.Tuple2; | ||
import ca.genovese.coffeecats.kind.Kind; | ||
|
||
import java.util.function.BinaryOperator; | ||
import java.util.function.Consumer; | ||
import java.util.function.Function; | ||
|
||
@FunctionalInterface | ||
public interface Gen<A> extends Kind<Gen, A> { | ||
Gen<Integer> intGen = FunctionalRandom::nextInt; | ||
|
||
Tuple2<A, FunctionalRandom> run(FunctionalRandom rnd); | ||
|
||
static <A> Gen<A> unit(A a) { | ||
return (r) -> new Tuple2<>(a, r); | ||
} | ||
|
||
default <B> Gen<B> map(Function<A, B> f) { | ||
return (r) -> run(r).applyTo((a, rnd) -> new Tuple2<>(f.apply(a), rnd)); | ||
} | ||
|
||
default <B> Gen<B> flatMap(Function<A, Gen<B>> f) { | ||
return (r) -> run(r).applyTo((a, rnd) -> f.apply(a).run(rnd)); | ||
} | ||
|
||
default <B> Gen<B> nTimes(int n, Function<A, B> f, BinaryOperator<B> c) { | ||
Gen<B> result = map(f); | ||
|
||
for (int i = 1; i < n; i++) { | ||
result = result.flatMap((b) -> map(a -> c.apply(b, f.apply(a)))); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
default Gen<List<Option<Exception>>> createCheck(Consumer<A> check) { | ||
return map((a) -> { | ||
try { | ||
check.accept(a); | ||
} catch (Exception e) { | ||
return List.of(Option.of(e)); | ||
} | ||
return List.of(Option.<Exception>none()); | ||
}).nTimes(10, l -> l, (a, b) -> b.append(a)); | ||
} | ||
} |