summaryrefslogtreecommitdiff
path: root/src/main/java/au/id/zancanaro/javacheck/Generators.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/au/id/zancanaro/javacheck/Generators.java')
-rw-r--r--src/main/java/au/id/zancanaro/javacheck/Generators.java135
1 files changed, 135 insertions, 0 deletions
diff --git a/src/main/java/au/id/zancanaro/javacheck/Generators.java b/src/main/java/au/id/zancanaro/javacheck/Generators.java
new file mode 100644
index 0000000..ec6a329
--- /dev/null
+++ b/src/main/java/au/id/zancanaro/javacheck/Generators.java
@@ -0,0 +1,135 @@
+package au.id.zancanaro.javacheck;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public final class Generators {
+ private Generators() {
+ }
+
+ public static <T> Generator<T> sized(Function<Integer, Generator<T>> makeGenerator) {
+ return (random, size) -> makeGenerator.apply(size).generate(random, size);
+ }
+
+ public static <T> Generator<T> suchThat(Generator<T> gen, Predicate<T> pred) {
+ return (random, size) -> {
+ RoseTree<T> result = gen.generate(random, size);
+ if (pred.test(result.getValue())) {
+ return result.filter(pred);
+ } else {
+ return suchThat(gen, pred).generate(random, size);
+ }
+ };
+ }
+
+ @SafeVarargs
+ public static <T> Generator<T> oneOf(Generator<T>... gens) {
+ return integer(0, gens.length).flatMap(index -> gens[index]);
+ }
+
+ public static <T> Generator<T> elements(T[] elements) {
+ return elements(Arrays.asList(elements));
+ }
+
+ public static <T> Generator<T> elements(List<T> elements) {
+ return integer(0, elements.size()).fmap(elements::get);
+ }
+
+ public static Generator<Boolean> bool() {
+ return (random, size) ->
+ random.nextBoolean() ?
+ new RoseTree<>(true, Collections.singletonList(new RoseTree<>(false, Collections.emptyList()))) :
+ new RoseTree<>(false, Collections.emptyList());
+ }
+
+ public static Generator<Integer> integer(int lower, int upper) {
+ return (random, size) -> {
+ int value = lower + random.nextInt(upper - lower);
+ int bound = lower > 0 ? lower : (upper < 0 ? upper : 0);
+ return new RoseTree<>(value, intShrinkingIterable(value, bound));
+ };
+ }
+
+ public static Generator<Integer> integer() {
+ return sized(size -> integer(-size, size));
+ }
+
+ public static Generator<Integer> natural() {
+ return sized(size -> integer(0, size));
+ }
+
+ private static Iterable<RoseTree<Integer>> intShrinkingIterable(final int value, final int bound) {
+ return () -> new Iterator<RoseTree<Integer>>() {
+ int curr = value - bound;
+
+ @Override
+ public boolean hasNext() {
+ return curr != 0;
+ }
+
+ @Override
+ public RoseTree<Integer> next() {
+ int prevCurr = curr;
+ curr = curr / 2;
+ return new RoseTree<>(value - prevCurr, intShrinkingIterable(value - prevCurr, bound));
+ }
+ };
+ }
+
+ public static <T> Generator<List<T>> listOf(Generator<T> gen) {
+ return sized(size ->
+ integer(0, size).flatMap(count -> {
+ @SuppressWarnings("unchecked")
+ Generator<T>[] gens = (Generator<T>[]) new Generator[count];
+ Arrays.fill(gens, gen);
+ return Generator.tuple(gens);
+ })).fmap(Collections::unmodifiableList);
+ }
+
+ public static Generator<Character> character() {
+ return integer(0, 256).fmap(i -> (char) i.intValue());
+ }
+
+ public static Generator<Character> asciiCharacter() {
+ return integer(32, 127).fmap(i -> (char) i.intValue());
+ }
+
+ public static Generator<Character> alphaNumericCharacter() {
+ return oneOf(
+ integer(48, 58),
+ integer(65, 91),
+ integer(97, 123)).fmap(i -> (char) i.intValue());
+ }
+
+ public static Generator<Character> alphaCharacter() {
+ return oneOf(
+ integer(65, 91),
+ integer(97, 123)).fmap(i -> (char) i.intValue());
+ }
+
+ private static String makeString(Character[] arr) {
+ StringBuilder builder = new StringBuilder(arr.length);
+ for (Character c : arr) {
+ builder.append(c);
+ }
+ return builder.toString();
+ }
+
+ public static Generator<String> string() {
+ return stringOf(character());
+ }
+
+ public static Generator<String> stringOf(Generator<Character> charGen) {
+ return Generators.listOf(charGen).fmap(list -> {
+ StringBuilder builder = new StringBuilder(list.size());
+ for (Object c : list) {
+ builder.append(c);
+ }
+ return builder.toString();
+ });
+ }
+}