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

net.jqwik.engine.properties.arbitraries.MultivalueArbitraryBase Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
package net.jqwik.engine.properties.arbitraries;

import java.util.ArrayList;
import java.util.*;
import java.util.function.*;

import net.jqwik.api.*;
import net.jqwik.api.arbitraries.*;
import net.jqwik.api.support.*;
import net.jqwik.engine.properties.*;
import net.jqwik.engine.properties.arbitraries.randomized.*;

import org.jspecify.annotations.*;

import static java.util.Arrays.*;

import static net.jqwik.engine.properties.UniquenessChecker.*;

abstract class MultivalueArbitraryBase extends TypedCloneable implements StreamableArbitrary {

	protected Arbitrary elementArbitrary;

	protected int minSize = 0;
	private Integer maxSize = null;
	protected Set> uniquenessExtractors = new LinkedHashSet<>();
	protected RandomDistribution sizeDistribution = null;

	protected MultivalueArbitraryBase(Arbitrary elementArbitrary) {
		this.elementArbitrary = elementArbitrary;
	}

	@Override
	public boolean isGeneratorMemoizable() {
		return elementArbitrary.isGeneratorMemoizable();
	}

	@Override
	public StreamableArbitrary ofMinSize(int minSize) {
		if (minSize < 0) {
			String message = String.format("minSize (%s) must be between 0 and 2147483647", minSize);
			throw new IllegalArgumentException(message);
		}

		MultivalueArbitraryBase clone = typedClone();
		clone.minSize = minSize;
		return clone;
	}

	@Override
	public StreamableArbitrary ofMaxSize(int maxSize) {
		if (maxSize < 0) {
			String message = String.format("maxSize (%s) must be between 0 and 2147483647", maxSize);
			throw new IllegalArgumentException(message);
		}
		if (maxSize < minSize) {
			String message = String.format("minSize (%s) must not be larger than maxSize (%s)", minSize, maxSize);
			throw new IllegalArgumentException(message);
		}

		MultivalueArbitraryBase clone = typedClone();
		clone.maxSize = maxSize;
		return clone;
	}

	@Override
	public StreamableArbitrary withSizeDistribution(RandomDistribution distribution) {
		MultivalueArbitraryBase clone = typedClone();
		clone.sizeDistribution = distribution;
		return clone;
	}

	@Override
	public  Arbitrary reduce(R initial, BiFunction accumulator) {
		return this.map(streamable -> {
			// Couldn't find a way to use Stream.reduce since it requires a combinator
			@SuppressWarnings("unchecked")
			R[] result = (R[]) new Object[]{initial};
			Iterable iterable = toIterable(streamable);
			for (T each : iterable) {
				result[0] = accumulator.apply(result[0], each);
			}
			return result[0];
		});
	}

	@Override
	public StreamableArbitrary uniqueElements() {
		return uniqueElements(FeatureExtractor.identity());
	}

	protected abstract Iterable toIterable(U streamable);

	protected StreamableArbitrary uniqueElements(FeatureExtractor by) {
		MultivalueArbitraryBase clone = typedClone();
		clone.uniquenessExtractors = new LinkedHashSet<>(uniquenessExtractors);
		clone.uniquenessExtractors.add(by);
		return clone;
	}

	protected RandomGenerator> createListGenerator(int genSize, boolean withEmbeddedEdgeCases) {
		RandomGenerator elementGenerator = elementGenerator(elementArbitrary, genSize, withEmbeddedEdgeCases);
		long maxUniqueElements = elementArbitrary.exhaustive(maxSize()).map(ExhaustiveGenerator::maxCount).orElse((long) maxSize());
		return RandomGenerators.list(elementGenerator, minSize, maxSize(), maxUniqueElements, genSize, sizeDistribution, uniquenessExtractors, elementArbitrary);
	}

	protected RandomGenerator elementGenerator(Arbitrary elementArbitrary, int genSize, boolean withEdgeCases) {
		return elementArbitrary.generator(genSize, withEdgeCases);
	}

	protected > EdgeCases edgeCases(
		BiFunction>, Integer, Shrinkable> shrinkableCreator,
		int maxEdgeCases
	) {
		// Optimization. Already handled by EdgeCases.concat(..)
		if (maxEdgeCases <= 0) {
			return EdgeCases.none();
		}

		EdgeCases emptyListEdgeCase = (minSize == 0) ? emptyListEdgeCase(shrinkableCreator) : EdgeCases.none();

		int effectiveMaxEdgeCases = maxEdgeCases - emptyListEdgeCase.size();
		EdgeCases singleElementEdgeCases = (minSize <= 1 && maxSize() >= 1)
												  ? fixedSizeEdgeCases(1, shrinkableCreator, effectiveMaxEdgeCases)
												  : EdgeCases.none();

		effectiveMaxEdgeCases = maxEdgeCases - singleElementEdgeCases.size();
		EdgeCases fixedSizeEdgeCases = generateFixedSizeEdgeCases()
											  ? fixedSizeEdgeCases(minSize, shrinkableCreator, effectiveMaxEdgeCases)
											  : EdgeCases.none();

		return EdgeCasesSupport.concat(asList(emptyListEdgeCase, singleElementEdgeCases, fixedSizeEdgeCases), maxEdgeCases);
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;

		MultivalueArbitraryBase that = (MultivalueArbitraryBase) o;
		if (minSize != that.minSize) return false;
		if (!Objects.equals(maxSize, that.maxSize)) return false;
		if (!elementArbitrary.equals(that.elementArbitrary)) return false;
		if (!uniquenessExtractors.equals(that.uniquenessExtractors)) return false;
		return Objects.equals(sizeDistribution, that.sizeDistribution);
	}

	@Override
	public int hashCode() {
		return HashCodeSupport.hash(elementArbitrary, minSize, maxSize, uniquenessExtractors);
	}

	private boolean generateFixedSizeEdgeCases() {
		return minSize == maxSize() && minSize > 1;
	}

	private > EdgeCases fixedSizeEdgeCases(
		final int fixedSize,
		final BiFunction>, Integer, Shrinkable> shrinkableCreator,
		int maxEdgeCases
	) {
		return EdgeCasesSupport.mapShrinkable(
			elementArbitrary.edgeCases(maxEdgeCases),
			shrinkableT -> {
				List> elements = new ArrayList<>(Collections.nCopies(fixedSize, shrinkableT));
				if (!checkUniquenessOfShrinkables(uniquenessExtractors, elements)) {
					return null;
				}
				return shrinkableCreator.apply(elements, minSize);
			}
		);
	}

	private > EdgeCases emptyListEdgeCase(BiFunction>, Integer, Shrinkable> shrinkableCreator) {
		return EdgeCases.fromSupplier(
			() -> shrinkableCreator.apply(Collections.emptyList(), minSize)
		);
	}

	protected int maxSize() {
		return RandomGenerators.collectionMaxSize(minSize, maxSize);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy