net.jqwik.engine.properties.arbitraries.randomized.FunctionGenerator Maven / Gradle / Ivy
package net.jqwik.engine.properties.arbitraries.randomized;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.stream.*;
import net.jqwik.api.*;
import net.jqwik.api.Tuple.*;
import net.jqwik.engine.*;
import net.jqwik.engine.support.*;
public class FunctionGenerator extends AbstractFunctionGenerator {
private final AtomicReference> lastResult = new AtomicReference<>();
public FunctionGenerator(
Class functionalType,
RandomGenerator resultGenerator,
List>, Function super List>, ? extends R>>> conditions
) {
super(functionalType, resultGenerator, conditions);
}
@Override
public Shrinkable next(Random random) {
return new ShrinkableFunction(createFunction(random));
}
private F createFunction(Random random) {
long baseSeed = random.nextLong();
InvocationHandler handler = (proxy, method, args) -> {
if (JqwikReflectionSupport.isEqualsMethod(method)) {
return handleEqualsMethod(proxy, args);
}
if (JqwikReflectionSupport.isToStringMethod(method)) {
return handleToString(baseSeed);
}
if (JqwikReflectionSupport.isHashCodeMethod(method)) {
return handleHashCode((int) baseSeed);
}
if (method.isDefault()) {
return handleDefaultMethod(proxy, method, args);
}
return conditionalResult(args).orElseGet(() -> {
Random randomForArgs = SourceOfRandomness.newRandom(seedForArgs(baseSeed, args));
Shrinkable shrinkableResult = resultGenerator.next(randomForArgs);
storeLastResult(shrinkableResult);
return new Object[]{shrinkableResult.value()};
})[0];
};
return createFunctionProxy(handler);
}
private int handleHashCode(final int baseSeed) {
return baseSeed;
}
private Object handleToString(final long baseSeed) {
return String.format(
"Function<%s>(baseSeed: %s)",
functionalType.getSimpleName(),
baseSeed
);
}
private void storeLastResult(Shrinkable result) {
lastResult.set(result);
}
private long seedForArgs(long baseSeed, Object[] args) {
long seed = baseSeed;
if (args != null) {
for (Object arg : args) {
seed = Long.rotateRight(seed, 16);
if (arg != null) {
seed ^= arg.hashCode();
}
}
}
return seed;
}
private class ShrinkableFunction implements Shrinkable {
private final F value;
private ShrinkableFunction(F function) {
value = function;
}
@Override
public F value() {
return value;
}
@Override
public Stream> shrink() {
if (lastResult.get() == null) {
return Stream.empty();
}
Shrinkable constantFunction = createConstantFunction(lastResult.get());
return Stream.of(constantFunction);
}
@Override
public ShrinkingDistance distance() {
return ShrinkingDistance.MAX;
}
}
}