diff options
Diffstat (limited to 'src/main/java/au/id/zancanaro/javacheck/Generators.java')
-rw-r--r-- | src/main/java/au/id/zancanaro/javacheck/Generators.java | 135 |
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(); + }); + } +} |