All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jqwik.engine.facades.ArbitrariesFacadeImpl Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show 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.engine.execution.lifecycle.*;
import net.jqwik.engine.properties.*;
import net.jqwik.engine.properties.arbitraries.*;
import net.jqwik.engine.properties.arbitraries.exhaustive.*;
import net.jqwik.engine.properties.arbitraries.randomized.*;
import net.jqwik.engine.properties.stateful.*;

import static net.jqwik.engine.properties.arbitraries.ArbitrariesSupport.*;

/**
 * Is loaded through reflection in api module
 */
public class ArbitrariesFacadeImpl extends Arbitraries.ArbitrariesFacade {
	@Override
	public  RandomGenerator randomChoose(List values) {
		return RandomGenerators.choose(values);
	}

	@Override
	public  EdgeCases edgeCasesChoose(List values, int maxEdgeCases) {
		return EdgeCasesSupport.choose(values, maxEdgeCases);
	}

	@Override
	public EdgeCases edgeCasesChoose(char[] characters, int maxEdgeCases) {
		List validCharacters = new ArrayList<>(characters.length);
		for (char character : characters) {
			validCharacters.add(character);
		}
		return edgeCasesChoose(validCharacters, maxEdgeCases);
	}

	@Override
	public  Optional> exhaustiveChoose(List values, long maxNumberOfSamples) {
		return ExhaustiveGenerators.choose(values, maxNumberOfSamples);
	}

	@Override
	public RandomGenerator randomChoose(char[] values) {
		return RandomGenerators.choose(values);
	}

	@Override
	public Optional> exhaustiveChoose(char[] values, long maxNumberOfSamples) {
		return ExhaustiveGenerators.choose(values, maxNumberOfSamples);
	}

	@Override
	public  Optional> exhaustiveCreate(Supplier supplier, long maxNumberOfSamples) {
		return ExhaustiveGenerators.create(supplier, maxNumberOfSamples);
	}

	@Override
	public  Arbitrary just(T value) {
		return new Arbitrary() {

			// Optimization: just(value).flatMap(mapper) -> mapper(value)
			@Override
			public  Arbitrary flatMap(Function> mapper) {
				return mapper.apply(value);
			}

			// Optimization: just(value).map(mapper) -> just(mapper(value))
			@Override
			public  Arbitrary map(Function mapper) {
				return just(mapper.apply(value));
			}

			@Override
			public RandomGenerator generator(int tries) {
				return random -> Shrinkable.unshrinkable(value);
			}

			@Override
			public Optional> exhaustive(long maxNumberOfSamples) {
				return exhaustiveChoose(Arrays.asList(value), maxNumberOfSamples);
			}

			@Override
			public EdgeCases edgeCases(int maxEdgeCases1) {
				return maxEdgeCases1 <= 0
						   ? EdgeCases.none()
						   : EdgeCases.fromSupplier(() -> Shrinkable.unshrinkable(value));
			}
		};
	}

	@Override
	public  Arbitrary oneOf(Collection> choices) {
		return new OneOfArbitrary<>(choices);
	}

	@Override
	public  RandomGenerator randomFrequency(List> frequencies) {
		return RandomGenerators.frequency(frequencies);
	}

	@Override
	public  RandomGenerator> randomShuffle(List values) {
		return RandomGenerators.shuffle(values);
	}

	@Override
	public  Optional>> exhaustiveShuffle(List values, long maxNumberOfSamples) {
		return ExhaustiveGenerators.shuffle(values, maxNumberOfSamples);
	}

	@Override
	public  ActionSequenceArbitrary sequences(Arbitrary> actionArbitrary) {
		return new DefaultActionSequenceArbitrary<>(actionArbitrary);
	}

	@Override
	public  Arbitrary frequencyOf(List>> frequencies) {
		List>> aboveZeroFrequencies =
			frequencies.stream().filter(f -> f.get1() > 0).collect(Collectors.toList());
		if (aboveZeroFrequencies.size() == 1) {
			return aboveZeroFrequencies.get(0).get2();
		}
		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> arbitrarySupplier) {
		return new LazyArbitrary<>(arbitrarySupplier);
	}

	@Override
	public  Arbitrary lazyOf(List>> 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);
	}

	/**
	 * The calculated cash 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 -> Objects.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> noDefaultResolver) {
		Set> arbitraries = allDefaultsFor(typeUsage, noDefaultResolver);
		if (arbitraries.isEmpty()) {
			return (Arbitrary) noDefaultResolver.apply(typeUsage);
		}

		List> arbitrariesList = new ArrayList<>();
		arbitraries.forEach(arbitrary -> arbitrariesList.add((Arbitrary) 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> 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(Collectors.toSet());
	}

	private static Set> createDefaultArbitraries(
		TypeUsage typeUsage,
		Function> 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> base,
		Function, 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);
		}

		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> base,
		Function, Arbitrary> recur,
		int depth
	) {
		return new RecursiveArbitrary<>(base, recur, depth);
	}
}