net.jqwik.engine.properties.shrinking.ShrinkableContainer 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 static net.jqwik.engine.properties.UniquenessChecker.*;
abstract class ShrinkableContainer implements Shrinkable {
protected final List> elements;
protected final int minSize;
protected final int maxSize;
protected final Collection> uniquenessExtractors;
ShrinkableContainer(List> elements, int minSize, int maxSize, Collection> uniquenessExtractors) {
this.elements = elements;
this.minSize = minSize;
this.maxSize = maxSize;
this.uniquenessExtractors = uniquenessExtractors;
}
private C createValue(List> shrinkables) {
return shrinkables
.stream()
.map(Shrinkable::value)
.collect(containerCollector());
}
@Override
public C value() {
return createValue(elements);
}
@Override
public Stream> shrink() {
return JqwikStreamSupport.concat(
shrinkSizeOfList(),
shrinkElementsOneAfterTheOther(0),
shrinkPairsOfElements()
);
}
@Override
public Optional> grow(Shrinkable> before, Shrinkable> after) {
if (before instanceof ShrinkableContainer && after instanceof ShrinkableContainer) {
List> removedShrinkables = new ArrayList<>(((ShrinkableContainer, ?>) before).elements);
removedShrinkables.removeAll(((ShrinkableContainer, ?>) after).elements);
return growBy(removedShrinkables);
}
return Optional.empty();
}
@Override
public Stream> grow() {
return growOneElementAfterTheOther();
}
private Stream> growOneElementAfterTheOther() {
List>> growPerElementStreams = new ArrayList<>();
for (int i = 0; i < elements.size(); i++) {
int index = i;
Shrinkable element = elements.get(i);
Stream> shrinkElement = element.grow().flatMap(shrunkElement -> {
List> elementsCopy = new ArrayList<>(elements);
elementsCopy.set(index, shrunkElement);
return Stream.of(createShrinkable(elementsCopy));
});
growPerElementStreams.add(shrinkElement);
}
return JqwikStreamSupport.concat(growPerElementStreams);
}
private Optional> growBy(List> shrinkables) {
if (elements.size() + shrinkables.size() <= maxSize) {
List> grownElements = new ArrayList<>(elements);
for (Shrinkable> shrinkable : shrinkables) {
try {
@SuppressWarnings("unchecked")
Shrinkable castShrinkable = (Shrinkable) shrinkable;
grownElements.add(0, castShrinkable);
} catch (Throwable classCastException) {
return Optional.empty();
}
}
Shrinkable grownShrinkable = createShrinkable(grownElements);
if (hasReallyGrown(grownShrinkable)) {
return Optional.of(grownShrinkable);
}
}
return Optional.empty();
}
protected boolean hasReallyGrown(Shrinkable grownShrinkable) {
return true;
}
protected Stream> shrinkSizeAggressively() {
return new AggressiveSizeOfListShrinker>(minSize)
.shrink(elements)
.map(this::createShrinkable)
.sorted(Comparator.comparing(Shrinkable::distance));
}
protected Stream> shrinkSizeOfList() {
return new SizeOfListShrinker>(minSize)
.shrink(elements)
.map(this::createShrinkable)
.sorted(Comparator.comparing(Shrinkable::distance));
}
protected Stream> shrinkElementsOneAfterTheOther(int maxToShrink) {
List>> shrinkPerElementStreams = new ArrayList<>();
for (int i = 0; i < elements.size(); i++) {
if (maxToShrink > 0 && i >= maxToShrink) {
break;
}
int index = i;
Shrinkable element = elements.get(i);
Stream> shrinkElement = element.shrink().flatMap(shrunkElement -> {
List> elementsCopy = new ArrayList<>(elements);
elementsCopy.remove(index);
if (!checkShrinkableUniqueIn(uniquenessExtractors, shrunkElement, elementsCopy)) {
return Stream.empty();
}
elementsCopy.add(index, shrunkElement);
return Stream.of(createShrinkable(elementsCopy));
});
shrinkPerElementStreams.add(shrinkElement);
}
return JqwikStreamSupport.concat(shrinkPerElementStreams);
}
protected Stream> shrinkPairsOfElements() {
return Combinatorics
.distinctPairs(elements.size())
.flatMap(pair -> JqwikStreamSupport.zip(
elements.get(pair.get1()).shrink(),
elements.get(pair.get2()).shrink(),
(Shrinkable s1, Shrinkable s2) -> {
List> newElements = new ArrayList<>(elements);
newElements.set(pair.get1(), s1);
newElements.set(pair.get2(), s2);
if (checkUniquenessOfShrinkables(uniquenessExtractors, newElements)) {
return createShrinkable(newElements);
} else {
// null value will skip the entry in zipped stream
return null;
}
}
));
}
protected Stream> sortElements() {
return ShrinkingCommons.sortElements(elements, this::createShrinkable);
}
@Override
public ShrinkingDistance distance() {
return ShrinkingDistance.forCollection(elements);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShrinkableContainer, ?> that = (ShrinkableContainer, ?>) o;
return value().equals(that.value());
}
@Override
public int hashCode() {
return Objects.hash(elements);
}
@Override
public String toString() {
return String.format(
"%s<%s>(%s:%s)",
getClass().getSimpleName(),
value().getClass().getSimpleName(),
value(), distance()
);
}
abstract Shrinkable createShrinkable(List> shrunkElements);
abstract Collector containerCollector();
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy