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; @SuppressWarnings("unused") public final class Generators { private Generators() { } public static Generator sized(Function> makeGenerator) { return (random, size) -> makeGenerator.apply(size).generate(random, size); } public static Generator suchThat(Generator gen, Predicate predicate) { return (random, size) -> { ShrinkTree result = gen.generate(random, size); if (predicate.test(result.getValue())) { return result.filter(predicate); } else { return suchThat(gen, predicate).generate(random, size); } }; } public static Generator noShrink(Generator gen) { return (random, size) -> new ShrinkTree<>( gen.generate(random, size).getValue(), Collections.emptyList()); } @SafeVarargs public static Generator oneOf(Generator... gens) { return integer(0, gens.length).flatMap(index -> gens[index]); } public static Generator elements(T[] elements) { return elements(Arrays.asList(elements)); } public static Generator elements(List elements) { return integer(0, elements.size()).map(elements::get); } public static Generator bool() { return (random, size) -> random.nextBoolean() ? new ShrinkTree<>(true, Collections.singletonList(new ShrinkTree<>(false, Collections.emptyList()))) : new ShrinkTree<>(false, Collections.emptyList()); } public static Generator 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 ShrinkTree<>(value, intShrinkingIterable(value, bound)); }; } public static Generator integer() { return sized(size -> integer(-size, size)); } public static Generator natural() { return sized(size -> integer(0, size)); } private static Iterable> intShrinkingIterable(final int value, final int bound) { return () -> new Iterator>() { int curr = value - bound; @Override public boolean hasNext() { return curr != 0; } @Override public ShrinkTree next() { int prevCurr = curr; curr = curr / 2; return new ShrinkTree<>(value - prevCurr, intShrinkingIterable(value - prevCurr, bound)); } }; } public static Generator> listOf(Generator gen, int minElements, int maxElements) { return (random, size) -> { Generator countGen = sized(s -> integer(minElements, maxElements)); int count = countGen.generate(random, size).getValue(); return Generator.list(count, gen) .generate(random, size) .filter(list -> minElements <= list.size() && list.size() < maxElements) .map(Collections::unmodifiableList); }; } public static Generator> listOf(Generator gen) { return (random, size) -> { Generator countGen = sized(s -> integer(0, s)); int count = countGen.generate(random, size).getValue(); return Generator.list(count, gen) .generate(random, size) .map(Collections::unmodifiableList); }; } public static Generator character() { return integer(0, 256).map(i -> (char) i.intValue()); } public static Generator asciiCharacter() { return integer(32, 127).map(i -> (char) i.intValue()); } public static Generator alphaNumericCharacter() { return oneOf( integer(48, 58), integer(65, 91), integer(97, 123)).map(i -> (char) i.intValue()); } public static Generator alphaCharacter() { return oneOf( integer(65, 91), integer(97, 123)).map(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() { return stringOf(character()); } public static Generator stringOf(Generator charGen) { return listOf(charGen).map(list -> { char[] chars = new char[list.size()]; int i = 0; for (Character c : list) { chars[i++] = c; } return String.valueOf(chars); // return new String(chars); }); } }