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

net.jqwik.api.Arbitrary Maven / Gradle / Ivy

The newest version!
package net.jqwik.api;

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

import com.google.errorprone.annotations.*;
import org.apiguardian.api.*;
import org.jspecify.annotations.*;

import net.jqwik.api.arbitraries.*;

import static org.apiguardian.api.API.Status.*;

/**
 * The main interface for representing objects that can be generated and shrunk.
 *
 * @param  The type of generated objects. Primitive objects (e.g. int, boolean etc.) are represented by their boxed
 *            type (e.g. Integer, Boolean).
 */
@API(status = STABLE, since = "1.0")
@CheckReturnValue
public interface Arbitrary {

	@API(status = INTERNAL)
	abstract class ArbitraryFacade {
		private static final ArbitraryFacade implementation;

		static {
			implementation = FacadeLoader.load(ArbitraryFacade.class);
		}

		public abstract  ListArbitrary list(Arbitrary elementArbitrary);

		public abstract  SetArbitrary set(Arbitrary elementArbitrary);

		public abstract  StreamArbitrary stream(Arbitrary elementArbitrary);

		public abstract  IteratorArbitrary iterator(Arbitrary elementArbitrary);

		public abstract  ArrayArbitrary array(Arbitrary elementArbitrary, Class arrayClass);

		public abstract  Stream sampleStream(Arbitrary arbitrary);

		public abstract  Arbitrary<@Nullable T> injectNull(Arbitrary self, double nullProbability);

		public abstract  Arbitrary filter(Arbitrary self, Predicate filterPredicate, int maxMisses);

		public abstract  Arbitrary map(Arbitrary self, Function mapper);

		public abstract  Arbitrary flatMap(Arbitrary self, Function> mapper);

		public abstract  Arbitrary ignoreExceptions(Arbitrary self, int maxThrows, Class[] exceptionTypes);

		public abstract  Arbitrary dontShrink(Arbitrary self);

		public abstract  Arbitrary configureEdgeCases(Arbitrary self, Consumer> configurator);

		public abstract  Arbitrary withoutEdgeCases(Arbitrary self);

		public abstract  RandomGenerator memoizedGenerator(Arbitrary self, int genSize, boolean withEdgeCases);

		public abstract  Arbitrary fixGenSize(Arbitrary self, int genSize);

		public abstract  Arbitrary> collect(Arbitrary self, Predicate> until);
	}

	/**
	 * Create the random generator for an arbitrary.
	 *
	 * 

* Starting with version 1.4.0 the returned generator should no longer * include edge cases explicitly since those will be injected in {@linkplain #generator(int, boolean)} *

* * @param genSize a very unspecific configuration parameter that can be used * to influence the configuration and behaviour of a random generator * if and only if the generator wants to be influenced. * Many generators are independent of genSize. *

* The default value of {@code genSize} is the number of tries configured * for a property. Use {@linkplain Arbitrary#fixGenSize(int)} to fix * the parameter for a given arbitrary. * @return a new random generator instance */ RandomGenerator generator(int genSize); /** * Create the random generator for an arbitrary with or without edge cases. * *

Never override this method. Override {@linkplain #generator(int)} instead.

* * @param genSize See {@linkplain #generator(int)} about meaning of this parameter * @param withEdgeCases True if edge cases should be injected into the stream of generated values * @return a new random generator instance */ @API(status = INTERNAL, since = "1.4.0") default RandomGenerator generator(int genSize, boolean withEdgeCases) { return ArbitraryFacade.implementation.memoizedGenerator(this, genSize, withEdgeCases); } /** * Create the random generator for an arbitrary where the embedded generators, * if there are any, also generate edge cases. * *

* Override only if there are any embedded arbitraries / generators, * e.g. a container using an element generator *

* * @param genSize See {@linkplain #generator(int)} about meaning of this parameter * @return a new random generator instance */ @API(status = INTERNAL, since = "1.4.0") default RandomGenerator generatorWithEmbeddedEdgeCases(int genSize) { return generator(genSize); } /** * @return The same instance but with type Arbitrary<Object> */ @SuppressWarnings("unchecked") @API(status = INTERNAL) default Arbitrary asGeneric() { return (Arbitrary) this; } /** * Create the exhaustive generator for an arbitrary using the maximum allowed * number of generated samples. Just trying to find out if such a generator * exists might take a long time. This method should never be overridden. * * @return a new exhaustive generator or Optional.empty() if it cannot be created. */ @API(status = INTERNAL) default Optional> exhaustive() { return exhaustive(ExhaustiveGenerator.MAXIMUM_SAMPLES_TO_GENERATE); } /** * Create the exhaustive generator for an arbitrary. Depending on * {@code maxNumberOfSamples} this can take a long time. * This method must be overridden in all arbitraries that support exhaustive * generation. * * @param maxNumberOfSamples The maximum number of samples considered. * If during generation it becomes clear that this * number will be exceeded generation stops. * @return a new exhaustive generator or Optional.empty() if it cannot be created. */ @API(status = INTERNAL) default Optional> exhaustive(long maxNumberOfSamples) { return Optional.empty(); } @API(status = INTERNAL) default boolean isGeneratorMemoizable() { return true; } EdgeCases edgeCases(int maxEdgeCases); /** * Return an arbitrary's edge cases up to a limit of 1000. * *

* Never override. Override {@linkplain #edgeCases(int)} instead. *

* * @return an instance of type {@linkplain EdgeCases} */ @API(status = EXPERIMENTAL, since = "1.3.0") default EdgeCases edgeCases() { return edgeCases(1000); } /** * Create optional stream of all possible values this arbitrary could generate. * This is only possible if the arbitrary is available for exhaustive generation. * * @return optional stream of all possible values */ default Optional> allValues() { return exhaustive().map(generator -> StreamSupport.stream(generator.spliterator(), false)); } /** * Iterate through each value this arbitrary can generate if - and only if - * exhaustive generation is possible. This method can be used for example * to make assertions about a set of values described by an arbitrary. * * @param action the consumer function to be invoked for each value * @throws AssertionError if exhaustive generation is not possible */ @API(status = MAINTAINED, since = "1.1.2") default void forEachValue(Consumer action) { if (!allValues().isPresent()) throw new AssertionError("Cannot generate all values of " + this); allValues().ifPresent( stream -> stream.forEach(action::accept)); } /** * Create a new arbitrary of the same type {@code T} that creates and shrinks the original arbitrary but only allows * values that are accepted by the {@code filterPredicate}. * * @param filterPredicate The predicate used for filtering * @return a new arbitrary instance * @throws TooManyFilterMissesException if filtering will fail to come up with a value after 10000 tries */ default Arbitrary filter(Predicate filterPredicate) { return filter(10000, filterPredicate); } /** * Create a new arbitrary of the same type {@code T} that creates and shrinks the original arbitrary but only allows * values that are accepted by the {@code filterPredicate}. * * @param maxMisses The max number of misses allowed for filtering * @param filterPredicate The predicate used for filtering * @return a new arbitrary instance * @throws TooManyFilterMissesException if filtering will fail to come up with a value after {@code maxMisses} tries */ @API(status = EXPERIMENTAL, since = "1.7.0") default Arbitrary filter(int maxMisses, Predicate filterPredicate) { return ArbitraryFacade.implementation.filter(this, filterPredicate, maxMisses); } /** * Create a new arbitrary of type {@code U} that maps the values of the original arbitrary using the {@code mapper} * function. * * @param type of resulting object * @param mapper the function used to map * @return a new arbitrary instance */ default Arbitrary map(Function mapper) { return ArbitraryFacade.implementation.map(this, mapper); } /** * Create a new arbitrary of type {@code U} that uses the values of the existing arbitrary to create a new arbitrary * using the {@code mapper} function. * * @param type of resulting object * @param mapper the function used to map to arbitrary * @return a new arbitrary instance */ default Arbitrary flatMap(Function> mapper) { return ArbitraryFacade.implementation.flatMap(this, mapper); } /** * Create a new arbitrary of the same type but inject null values with a probability of {@code nullProbability}. * * @param nullProbability the probability. ≥ 0 and ≤ 1. * @return a new arbitrary instance */ default Arbitrary<@Nullable T> injectNull(double nullProbability) { return ArbitraryFacade.implementation.injectNull(Arbitrary.this, nullProbability); } /** * Fix the genSize of an arbitrary so that it can no longer be influenced from outside * * @param genSize The size used in arbitrary instead of the dynamic one * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.2.0") default Arbitrary fixGenSize(int genSize) { return ArbitraryFacade.implementation.fixGenSize(this, genSize); } /** * Create a new arbitrary of type {@code List} using the existing arbitrary for generating the elements of the list. * * @return a new arbitrary instance */ default ListArbitrary list() { return ArbitraryFacade.implementation.list(this); } /** * Create a new arbitrary of type {@code Set} using the existing arbitrary for generating the elements of the set. * * @return a new arbitrary instance */ default SetArbitrary set() { return ArbitraryFacade.implementation.set(this); } /** * Create a new arbitrary of type {@code Stream} using the existing arbitrary * for generating the elements of the stream. * * @return a new arbitrary instance */ default StreamArbitrary stream() { return ArbitraryFacade.implementation.stream(this); } /** * Create a new arbitrary of type {@code Iterable} using the existing arbitrary for generating the elements of the * stream. * * @return a new arbitrary instance */ default IteratorArbitrary iterator() { return ArbitraryFacade.implementation.iterator(this); } /** * Create a new arbitrary of type {@code T[]} using the existing arbitrary for generating the elements of the array. * * @param Type of resulting array class * @param arrayClass The arrays class to create, e.g. {@code String[].class}. This is required due to limitations in Java's * reflection capabilities. * @return a new arbitrary instance */ default ArrayArbitrary array(Class arrayClass) { return ArbitraryFacade.implementation.array(this, arrayClass); } /** * Create a new arbitrary of type {@code Optional} using the existing arbitrary for generating the elements of the * stream. * *

* The new arbitrary generates {@code Optional.empty()} values with a probability of {@code 0.05} (i.e. 1 in 20). *

* * @return a new arbitrary instance */ default Arbitrary> optional() { return optional(0.95); } /** * Create a new arbitrary of type {@code Optional} using the existing arbitrary for generating the elements of the * stream. * *

* The new arbitrary generates {@code Optional.empty()} values with a probability of {@code 1 - presenceProbability}. *

* * @param presenceProbability The probability with which a value is present, i.e. not empty * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.5.4") default Arbitrary> optional(double presenceProbability) { double emptyProbability = 1.0 - presenceProbability; return this.injectNull(emptyProbability).map(Optional::ofNullable); } /** * Create a new arbitrary of type {@code List} by adding elements of type T until condition {@code until} is fulfilled. * * @param until predicate to check if final condition has been reached * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.3.0") default Arbitrary> collect(Predicate> until) { return ArbitraryFacade.implementation.collect(this, until); } /** * Generate a stream of sample values using this arbitrary. * This can be useful for *
    *
  • Testing arbitraries
  • *
  • Playing around with arbitraries in jshell
  • *
  • Using arbitraries independently from jqwik, e.g. to feed test data builders
  • *
* *

* The underlying generator is created with size 1000. * Outside a property a new instance of {@linkplain Random} will be created * to feed the generator. *

* *

* Using this method within a property does not break reproducibility of results, * i.e. rerunning it with same seed will also generate the same values. *

* * @return a stream of newly generated values */ @API(status = MAINTAINED, since = "1.3.0") default Stream sampleStream() { return ArbitraryFacade.implementation.sampleStream(this); } /** * Generate a single sample value using this arbitrary. * This can be useful for *
    *
  • Testing arbitraries
  • *
  • Playing around with arbitraries in jshell
  • *
  • Using arbitraries independently from jqwik, e.g. to feed test data builders
  • *
*

* Some additional things to be aware of: *

    *
  • * If you feel the need to use this method for real generation, e.g. in a provider method * you are most probably doing it wrong. You might want to use {@linkplain Arbitrary#flatMap(Function)}. *
  • *
  • * The underlying generator is created with size 1000. * Outside a property a new instance of {@linkplain Random} will be created * to feed the generator.
  • *
  • * Using this method within a property does not break reproducibility of results, * i.e. rerunning it with same seed will also generate the same value. *
  • *
* * @return a newly generated value */ @API(status = MAINTAINED, since = "1.3.0") default T sample() { return this.sampleStream() .map(Optional::ofNullable) .findFirst() .orElseThrow(() -> new JqwikException("Cannot generate a value")) .orElse(null); } /** * Create a new arbitrary of type {@code Iterable} that will * inject duplicates of previously generated values with a probability of {@code duplicateProbability}. * *

* Shrinking behavior for duplicate values * -- if duplication is required for falsification -- is poor, * i.e. those duplicate values cannot be shrunk to "smaller" duplicate values. *

* * @param duplicateProbability The probability with which a previous value will be generated * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.3.0") default Arbitrary injectDuplicates(double duplicateProbability) { return new Arbitrary() { @Override public RandomGenerator generator(int genSize) { return Arbitrary.this.generator(genSize).injectDuplicates(duplicateProbability); } @Override public RandomGenerator generatorWithEmbeddedEdgeCases(int genSize) { return Arbitrary.this.generatorWithEmbeddedEdgeCases(genSize).injectDuplicates(duplicateProbability); } @Override public Optional> exhaustive(long maxNumberOfSamples) { return Arbitrary.this.exhaustive(maxNumberOfSamples); } @Override public EdgeCases edgeCases(int maxEdgeCases) { if (duplicateProbability >= 1.0) { // This is a pathological case anyway return EdgeCases.none(); } return Arbitrary.this.edgeCases(maxEdgeCases); } @Override public boolean isGeneratorMemoizable() { return false; } }; } /** * Create a new arbitrary of type {@code Tuple.Tuple1} that will use the underlying * arbitrary to create the tuple value; * * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.3.0") default Arbitrary> tuple1() { return Arbitrary.this.map(Tuple::of); } /** * Create a new arbitrary of type {@code Tuple.Tuple2} that will use the underlying * arbitrary to create the tuple values; * * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.3.0") default Arbitrary> tuple2() { return Arbitrary.this.list().ofSize(2).map(l -> Tuple.of(l.get(0), l.get(1))); } /** * Create a new arbitrary of type {@code Tuple.Tuple3} that will use the underlying * arbitrary to create the tuple values; * * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.3.0") default Arbitrary> tuple3() { return Arbitrary.this.list().ofSize(3).map(l -> Tuple.of(l.get(0), l.get(1), l.get(2))); } /** * Create a new arbitrary of type {@code Tuple.Tuple4} that will use the underlying * arbitrary to create the tuple values; * * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.3.0") default Arbitrary> tuple4() { return Arbitrary.this.list().ofSize(4).map(l -> Tuple.of(l.get(0), l.get(1), l.get(2), l.get(3))); } /** * Create a new arbitrary of type {@code Tuple.Tuple5} that will use the underlying * arbitrary to create the tuple values; * * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.3.3") default Arbitrary> tuple5() { return Arbitrary.this.list().ofSize(5).map(l -> Tuple.of(l.get(0), l.get(1), l.get(2), l.get(3), l.get(4))); } /** * Create a new arbitrary of type {@code T} that will use the underlying * arbitrary to create the tuple values but will ignore any raised exception of * type {@code exceptionType} during generation. * * @param exceptionType The exception type to ignore * @return a new arbitrary instance * @throws TooManyFilterMissesException if more than 10000 exceptions are thrown in a row */ @SuppressWarnings("unchecked") @API(status = MAINTAINED, since = "1.3.1") default Arbitrary ignoreException(Class exceptionType) { return ignoreExceptions(exceptionType); } /** * Create a new arbitrary of type {@code T} that will use the underlying * arbitrary to create the tuple values but will ignore any raised exception of * type {@code exceptionType} during generation. * * @param maxThrows The maximum number of subsequent exception throws before generation * is stopped. * @param exceptionType The exception type to ignore * @return a new arbitrary instance * @throws TooManyFilterMissesException if more than {@code maxThrows} exceptions are thrown in a row */ @SuppressWarnings("unchecked") @API(status = EXPERIMENTAL, since = "1.7.3") default Arbitrary ignoreException(int maxThrows, Class exceptionType) { return ignoreExceptions(maxThrows, exceptionType); } /** * Create a new arbitrary of type {@code T} that will use the underlying * arbitrary to create the tuple values but will ignore any raised exception in * {@code exceptionTypes} during generation. * *

* If {@code exceptionTypes} is empty, the original arbitrary is returned. *

* * @param exceptionTypes The exception types to ignore * @return a new arbitrary instance * @throws TooManyFilterMissesException if more than 10000 exceptions are thrown in a row */ @SuppressWarnings("unchecked") @API(status = MAINTAINED, since = "1.7.2") default Arbitrary ignoreExceptions(Class... exceptionTypes) { return this.ignoreExceptions(10000, exceptionTypes); } /** * Create a new arbitrary of type {@code T} that will use the underlying * arbitrary to create the tuple values but will ignore any raised exception in * {@code exceptionTypes} during generation. * *

* If {@code exceptionTypes} is empty, the original arbitrary is returned. *

* * @param maxThrows The maximum number of subsequent exception throws before generation * is stopped. * @param exceptionTypes The exception types to ignore * @return a new arbitrary instance * @throws TooManyFilterMissesException if more than {@code maxThrows} exceptions are thrown in a row */ @SuppressWarnings("unchecked") @API(status = EXPERIMENTAL, since = "1.7.3") default Arbitrary ignoreExceptions(int maxThrows, Class... exceptionTypes) { return ArbitraryFacade.implementation.ignoreExceptions(Arbitrary.this, maxThrows, exceptionTypes); } /** * Create a new arbitrary of type {@code T} that will use the underlying * arbitrary to create the tuple values but will return unshrinkable values. * This might be necessary if values are being mutated during a property run * and the mutated state would make a shrunk value invalid. * *

* This is a hack to get around a weakness in jqwik's shrinking mechanism *

* * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.4.0") default Arbitrary dontShrink() { return ArbitraryFacade.implementation.dontShrink(Arbitrary.this); } /** * Experimental interface to change generated edge cases of a specific arbitrary. * * @param configurator A consumer that configures deviating edge cases behaviour * @return a new arbitrary instance * @see EdgeCases.Config */ @API(status = MAINTAINED, since = "1.8.0") default Arbitrary edgeCases(Consumer> configurator) { return ArbitraryFacade.implementation.configureEdgeCases(Arbitrary.this, configurator); } /** * Create a new arbitrary of type {@code T} that will not explicitly generate * any edge cases, neither directly or in embedded arbitraries. * This is useful if you want to prune selected branches of edge case generation * because they are to costly or generate too many cases. * * @return a new arbitrary instance */ @API(status = MAINTAINED, since = "1.8.0") default Arbitrary withoutEdgeCases() { return ArbitraryFacade.implementation.withoutEdgeCases(Arbitrary.this); } }