net.jqwik.engine.facades.ArbitrariesFacadeImpl Maven / Gradle / Ivy
The newest version!
package net.jqwik.engine.facades;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import net.jqwik.api.*;
import net.jqwik.api.arbitraries.*;
import net.jqwik.api.domains.*;
import net.jqwik.api.providers.*;
import net.jqwik.api.stateful.*;
import net.jqwik.api.support.*;
import net.jqwik.engine.execution.lifecycle.*;
import net.jqwik.engine.properties.*;
import net.jqwik.engine.properties.arbitraries.*;
import net.jqwik.engine.properties.arbitraries.randomized.*;
import net.jqwik.engine.properties.stateful.*;
import org.jspecify.annotations.*;
import static net.jqwik.engine.properties.arbitraries.ArbitrariesSupport.*;
/**
* Is loaded through reflection in api module
*/
public class ArbitrariesFacadeImpl extends Arbitraries.ArbitrariesFacade {
@Override
public Arbitrary just(T value) {
return new JustArbitrary<>(value);
}
@Override
public Arbitrary oneOf(Collection extends Arbitrary extends T>> choices) {
return new OneOfArbitrary<>(choices);
}
@Override
public ActionSequenceArbitrary sequences(Arbitrary extends Action> actionArbitrary) {
return new DefaultActionSequenceArbitrary<>(actionArbitrary);
}
@Override
public Arbitrary frequencyOf(List extends Tuple.Tuple2>> frequencies) {
List>> aboveZeroFrequencies =
frequencies.stream().filter(f -> f.get1() > 0).collect(Collectors.toList());
if (aboveZeroFrequencies.size() == 1) {
return aboveZeroFrequencies.get(0).get2();
}
if (aboveZeroFrequencies.isEmpty()) {
String message = "frequencyOf() must be called with at least one choice with a frequency > 0";
throw new JqwikException(message);
}
return new FrequencyOfArbitrary<>(aboveZeroFrequencies);
}
@Override
public IntegerArbitrary integers() {
return new DefaultIntegerArbitrary();
}
@Override
public LongArbitrary longs() {
return new DefaultLongArbitrary();
}
@Override
public BigIntegerArbitrary bigIntegers() {
return new DefaultBigIntegerArbitrary();
}
@Override
public FloatArbitrary floats() {
return new DefaultFloatArbitrary();
}
@Override
public BigDecimalArbitrary bigDecimals() {
return new DefaultBigDecimalArbitrary();
}
@Override
public DoubleArbitrary doubles() {
return new DefaultDoubleArbitrary();
}
@Override
public ByteArbitrary bytes() {
return new DefaultByteArbitrary();
}
@Override
public ShortArbitrary shorts() {
return new DefaultShortArbitrary();
}
@Override
public StringArbitrary strings() {
return new DefaultStringArbitrary();
}
@Override
public CharacterArbitrary chars() {
return new DefaultCharacterArbitrary();
}
@Override
public Arbitrary lazy(Supplier extends Arbitrary> arbitrarySupplier) {
return new LazyArbitrary<>(arbitrarySupplier);
}
@Override
public Arbitrary lazyOf(List extends Supplier extends Arbitrary>> suppliers) {
int hashIdentifier = calculateIdentifier(suppliers.size());
return LazyOfArbitrary.of(hashIdentifier, suppliers);
}
@Override
public TraverseArbitrary traverse(Class targetType, TraverseArbitrary.Traverser traverser) {
return new DefaultTraverseArbitrary<>(targetType, traverser);
}
@Override
public Arbitrary of(char[] chars) {
return new ChooseCharacterArbitrary(chars);
}
@Override
public Arbitrary of(Collection extends T> values) {
List valueList = values instanceof List ? (List) values : new ArrayList<>(values);
return new ChooseValueArbitrary<>(valueList);
}
@Override
public Arbitrary create(Supplier supplier) {
return new CreateArbitrary<>(supplier);
}
@Override
public Arbitrary> shuffle(List values) {
return new ShuffleArbitrary<>(values);
}
@Override
public Arbitrary fromGenerator(IntFunction extends RandomGenerator> generatorSupplier) {
return new FromGeneratorWithSizeArbitrary<>(generatorSupplier);
}
@Override
public Arbitrary frequency(List extends Tuple.Tuple2> frequencies) {
List> frequenciesAbove0 = frequencies.stream()
.filter(f -> f.get1() > 0)
.collect(Collectors.toList());
if (frequenciesAbove0.isEmpty()) {
String message = "frequency() must be called with at least one value with a frequency > 0";
throw new JqwikException(message);
}
if (frequenciesAbove0.size() == 1) {
return new JustArbitrary<>(frequenciesAbove0.get(0).get2());
}
return new FrequencyArbitrary<>(frequenciesAbove0);
}
/**
* The calculated hash is supposed to be the same for the same callers of Arbitraries.lazyOf()
* This is important to have a single instance of LazyOfArbitrary for the same code.
*/
private static int calculateIdentifier(int numberOfSuppliers) {
try {
throw new RuntimeException();
} catch (RuntimeException rte) {
Optional optionalHash =
Arrays.stream(rte.getStackTrace())
.filter(stackTraceElement -> !stackTraceElement.getClassName().equals(ArbitrariesFacadeImpl.class.getName()))
.filter(stackTraceElement -> !stackTraceElement.getClassName().equals(Arbitraries.class.getName()))
.findFirst()
.map(stackTraceElement -> HashCodeSupport.hash(
stackTraceElement.getClassName(),
stackTraceElement.getMethodName(),
stackTraceElement.getLineNumber(),
numberOfSuppliers
));
return optionalHash.orElse(0);
}
}
@Override
public Arbitrary defaultFor(Class type, Class>[] typeParameters) {
TypeUsage[] genericTypeParameters =
Arrays.stream(typeParameters)
.map(TypeUsage::of)
.toArray(TypeUsage[]::new);
return defaultFor(TypeUsage.of(type, genericTypeParameters), typeUsage -> {throw new CannotFindArbitraryException(typeUsage);});
}
@SuppressWarnings("unchecked")
@Override
public Arbitrary defaultFor(TypeUsage typeUsage, Function super TypeUsage, ? extends Arbitrary> noDefaultResolver) {
Set> arbitraries = allDefaultsFor(typeUsage, noDefaultResolver);
if (arbitraries.isEmpty()) {
return noDefaultResolver.apply(typeUsage);
}
List> arbitrariesList = new ArrayList<>();
arbitraries.forEach(arbitrary -> arbitrariesList.add((Arbitrary extends T>) arbitrary));
return Arbitraries.oneOf(arbitrariesList);
}
@Override
public TypeArbitrary forType(Class targetType) {
return new DefaultTypeArbitrary<>(targetType);
}
@Override
public MapArbitrary maps(Arbitrary keysArbitrary, Arbitrary valuesArbitrary) {
// The map cannot be larger than the max number of possible keys
return new DefaultMapArbitrary<>(keysArbitrary, valuesArbitrary)
.ofMaxSize(maxNumberOfElements(keysArbitrary, RandomGenerators.DEFAULT_COLLECTION_SIZE));
}
@Override
public Arbitrary> entries(Arbitrary keysArbitrary, Arbitrary valuesArbitrary) {
return Combinators.combine(keysArbitrary, valuesArbitrary).as(AbstractMap.SimpleEntry::new);
}
private static Set> allDefaultsFor(TypeUsage typeUsage, Function super TypeUsage, ? extends Arbitrary>> noDefaultResolver) {
DomainContext domainContext = CurrentDomainContext.get();
Set> unconfiguredArbitraries = createDefaultArbitraries(typeUsage, noDefaultResolver, domainContext);
return configureDefaultArbitraries(typeUsage, domainContext, unconfiguredArbitraries);
}
private static Set> configureDefaultArbitraries(
TypeUsage typeUsage,
DomainContext domainContext,
Set> unconfiguredArbitraries
) {
RegisteredArbitraryConfigurer defaultArbitraryConfigurer = new RegisteredArbitraryConfigurer(domainContext.getArbitraryConfigurators());
return unconfiguredArbitraries.stream()
.map(arbitrary -> defaultArbitraryConfigurer.configure(arbitrary, typeUsage))
.collect(CollectorsSupport.toLinkedHashSet());
}
private static Set> createDefaultArbitraries(
TypeUsage typeUsage,
Function super TypeUsage, ? extends Arbitrary>> noDefaultResolver,
DomainContext domainContext
) {
RegisteredArbitraryResolver defaultArbitraryResolver = new RegisteredArbitraryResolver(domainContext.getArbitraryProviders());
ArbitraryProvider.SubtypeProvider subtypeProvider = subtypeUsage -> {
Set> subtypeArbitraries = allDefaultsFor(subtypeUsage, noDefaultResolver);
if (subtypeArbitraries.isEmpty()) {
return Collections.singleton(noDefaultResolver.apply(subtypeUsage));
}
return subtypeArbitraries;
};
return defaultArbitraryResolver.resolve(typeUsage, subtypeProvider);
}
@Override
public Arbitrary recursive(
Supplier extends Arbitrary> base,
Function super Arbitrary, ? extends Arbitrary> recur,
int minDepth,
int maxDepth
) {
if (minDepth < 0) {
String message = String.format("minDepth <%s> must be >= 0.", minDepth);
throw new IllegalArgumentException(message);
}
if (minDepth > maxDepth) {
String message = String.format("minDepth <%s> must not be > maxDepth <%s>", minDepth, maxDepth);
throw new IllegalArgumentException(message);
}
if (minDepth == maxDepth) {
return recursive(base, recur, minDepth);
} else {
Arbitrary depths = Arbitraries.integers().between(minDepth, maxDepth)
.withDistribution(RandomDistribution.uniform())
.edgeCases(c -> c.includeOnly(minDepth, maxDepth));
return depths.flatMap(depth -> recursive(base, recur, depth));
}
}
private Arbitrary recursive(
Supplier extends Arbitrary> base,
Function super Arbitrary, ? extends Arbitrary> recur,
int depth
) {
return new RecursiveArbitrary<>(base, recur, depth);
}
}