net.jqwik.engine.properties.shrinking.ShrinkableList Maven / Gradle / Ivy
package net.jqwik.engine.properties.shrinking;
import java.util.*;
import java.util.stream.*;
import net.jqwik.api.*;
import net.jqwik.engine.properties.*;
import net.jqwik.engine.support.*;
import org.jspecify.annotations.*;
import static net.jqwik.engine.properties.UniquenessChecker.*;
public class ShrinkableList extends ShrinkableContainer, E> {
// Only used in tests
ShrinkableList(List> elements, int minSize, int maxSize) {
this(elements, minSize, maxSize, Collections.emptySet(), null);
}
public ShrinkableList(
List extends Shrinkable> elements,
int minSize, int maxSize,
Collection extends FeatureExtractor> uniquenessExtractors,
Arbitrary elementArbitrary
) {
super(elements, minSize, maxSize, uniquenessExtractors, elementArbitrary);
}
@Override
List createValue(List extends Shrinkable> shrinkables) {
// Using loop instead of stream to make stack traces more readable
List values = new ArrayList<>(shrinkables.size());
for (Shrinkable shrinkable : shrinkables) {
values.add(shrinkable.value());
}
return values;
}
@Override
Shrinkable> createShrinkable(List extends Shrinkable> shrunkElements) {
return new ShrinkableList<>(shrunkElements, minSize, maxSize, uniquenessExtractors, elementArbitrary);
}
@Override
public Stream>> shrink() {
return JqwikStreamSupport.concat(
super.shrink(),
sortElements(),
moveIndividualValuesTowardsEnd()
);
}
// TODO: Simplify and clean up
@SuppressWarnings("OverlyLongMethod")
private Stream>> moveIndividualValuesTowardsEnd() {
ShrinkingDistance distance = distance();
return Combinatorics
.distinctPairs(elements.size())
.map(pair -> {
int firstIndex = Math.min(pair.get1(), pair.get2());
int secondIndex = Math.max(pair.get1(), pair.get2());
Shrinkable first = elements.get(firstIndex);
Shrinkable second = elements.get(secondIndex);
return Tuple.of(firstIndex, first, secondIndex, second);
})
.filter(quadruple -> quadruple.get2().compareTo(quadruple.get4()) <= 0)
.flatMap(quadruple -> {
int firstIndex = quadruple.get1();
Shrinkable first = quadruple.get2();
int secondIndex = quadruple.get3();
Shrinkable second = quadruple.get4();
return first.shrink()
.map(after -> {
Optional> grow = second.grow(first, after);
return Tuple.of(after, grow);
})
.filter(tuple -> tuple.get2().isPresent())
.map(tuple -> {
List> pairMove = new ArrayList<>(elements);
pairMove.set(firstIndex, tuple.get1());
pairMove.set(secondIndex, tuple.get2().get());
return pairMove;
})
.filter(shrinkables -> checkUniquenessOfShrinkables(uniquenessExtractors, shrinkables))
.map(this::createShrinkable);
})
// In rare cases of nested lists shrinkGrow can increase the distance
.filter(s -> s.distance().compareTo(distance) <= 0);
}
}