net.jqwik.engine.properties.shrinking.FlatMappedShrinkable Maven / Gradle / Ivy
package net.jqwik.engine.properties.shrinking;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import net.jqwik.api.*;
import net.jqwik.api.support.*;
import net.jqwik.engine.*;
import net.jqwik.engine.support.*;
import org.jspecify.annotations.*;
public class FlatMappedShrinkable implements Shrinkable {
private final Shrinkable toMap;
private final Function super T, ? extends Shrinkable> mapper;
public FlatMappedShrinkable(
Shrinkable toMap,
Function super T, ? extends Arbitrary> toArbitraryMapper,
int genSize,
long randomSeed,
boolean withEmbeddedEdgeCases
) {
this(toMap, t -> {
Arbitrary arbitrary = toArbitraryMapper.apply(t);
return arbitrary.generator(genSize, withEmbeddedEdgeCases);
}, randomSeed);
}
public FlatMappedShrinkable(Shrinkable toMap, Function super T, ? extends RandomGenerator> toGeneratorMapper, long randomSeed) {
this(toMap, t -> toGeneratorMapper.apply(t).next(SourceOfRandomness.newRandom(randomSeed)));
}
protected FlatMappedShrinkable(Shrinkable toMap, Function super T, ? extends Shrinkable> mapper) {
this.toMap = toMap;
this.mapper = mapper;
}
private Shrinkable generateShrinkable(T value) {
return mapper.apply(value);
}
@Override
public Stream> shrink() {
return JqwikStreamSupport.concat(
shrinkRightSide(),
shrinkLeftSide(),
shrinkLeftGrowRightSide()
);
}
private Stream> shrinkRightSide() {
final ShrinkingDistance rightDistance = shrinkable().distance();
return shrinkable().shrink()
.filter(s -> s.distance().size() <= rightDistance.size())
.map(rightSide -> new FixedValueFlatMappedShrinkable<>(toMap, mapper, () -> rightSide));
}
private Stream> shrinkLeftSide() {
// final ShrinkingDistance leftDistance = toMap.distance();
return toMap.shrink()
// Seems to make shrinking less effective in some cases:
// .filter(s -> s.distance().size() <= leftDistance.size())
.map(shrunkLeftSide -> new FlatMappedShrinkable<>(shrunkLeftSide, mapper));
}
private Stream> growRightSide() {
return shrinkable().grow()
.map(rightSide -> new FixedValueFlatMappedShrinkable<>(toMap, mapper, () -> rightSide));
}
private Stream> shrinkLeftGrowRightSide() {
return toMap.shrink()
.map(shrunkLeftSide -> new FlatMappedShrinkable<>(shrunkLeftSide, mapper))
.flatMap(FlatMappedShrinkable::growRightSide);
}
@Override
public U value() {
return shrinkable().value();
}
protected Shrinkable shrinkable() {
return generateShrinkable(toMap.value());
}
@Override
public ShrinkingDistance distance() {
return toMap.distance().append(shrinkable().distance());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FlatMappedShrinkable)) return false;
FlatMappedShrinkable, ?> that = (FlatMappedShrinkable, ?>) o;
return Objects.equals(value(), that.value());
}
@Override
public int hashCode() {
return HashCodeSupport.hash(value());
}
@Override
public String toString() {
return String.format(
"%s<%s>(%s:%s)|%s",
getClass().getSimpleName(),
value().getClass().getSimpleName(),
value(),
distance(),
toMap
);
}
}