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

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

The newest version!
package net.jqwik.api;

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

import org.apiguardian.api.*;
import org.jspecify.annotations.*;

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

@API(status = STABLE, since = "1.0")
public interface Shrinkable extends Comparable> {

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

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

		public abstract  Shrinkable unshrinkable(Supplier valueSupplier, ShrinkingDistance distance);

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

		public abstract  Shrinkable filter(Shrinkable self, Predicate filter);

		public abstract  Shrinkable flatMap(Shrinkable self, Function> flatMapper, int tries, long randomSeed);
	}

	static  Shrinkable unshrinkable(T value) {
		return unshrinkable(value, ShrinkingDistance.MIN);
	}

	static  Shrinkable unshrinkable(T value, ShrinkingDistance distance) {
		return ShrinkableFacade.implementation.unshrinkable(() -> value, distance);
	}

	@API(status = INTERNAL)
	static  Shrinkable supplyUnshrinkable(Supplier supplier) {
		return ShrinkableFacade.implementation.unshrinkable(supplier, ShrinkingDistance.MIN);
	}

	/**
	 * Create value freshly, so that in case of mutable objects shrinking (and reporting)
	 * can rely on untouched values.
	 *
	 * @return An un-changed instance of the value represented by this shrinkable
	 */
	T value();

	/**
	 * Create a new and finite stream of smaller or same size shrinkables; size is measured by {@linkplain #distance()}.
	 * 

* Same size shrinkables are allowed but they have to iterate towards a single value to prevent endless shrinking. * This also means that a shrinkable must never be in its own shrink stream! * * @return a finite stream of shrinking options */ @API(status = INTERNAL, since = "1.3.3") Stream> shrink(); /** * To be able to "move" values towards the end of collections while keeping some constraint constant * it's necessary to grow a shrinkable by what another has been shrunk. * One example is keeping a sum of values and still shrinking to the same resulting list. * * @param before The other shrinkable before shrinking * @param after The other shrinkable after shrinking * @return this shrinkable grown by the difference of before and after */ @API(status = INTERNAL, since = "1.3.3") default Optional> grow(Shrinkable before, Shrinkable after) { return Optional.empty(); } /** * Grow a shrinkable to allow broader searching in flat mapped shrinkables * * @return a finite stream of grown values */ @API(status = INTERNAL, since = "1.3.3") default Stream> grow() { return Stream.empty(); } ShrinkingDistance distance(); /** * Sometimes simplifies test writing * * @return generic version of a shrinkable */ @SuppressWarnings("unchecked") @API(status = INTERNAL, since = "1.2.4") default Shrinkable asGeneric() { return (Shrinkable) this; } default Shrinkable map(Function mapper) { return ShrinkableFacade.implementation.map(this, mapper); } default Shrinkable filter(Predicate filter) { return ShrinkableFacade.implementation.filter(this, filter); } default Shrinkable flatMap(Function> flatMapper, int tries, long randomSeed) { return ShrinkableFacade.implementation.flatMap(this, flatMapper, tries, randomSeed); } @SuppressWarnings("unchecked") @Override @API(status = INTERNAL) default int compareTo(Shrinkable other) { int comparison = this.distance().compareTo(other.distance()); if (comparison == 0) { T value = value(); if (value instanceof Comparable && this.getClass().equals(other.getClass())) { return ((Comparable) value).compareTo(other.value()); } } return comparison; } @API(status = INTERNAL) default Shrinkable makeUnshrinkable() { return ShrinkableFacade.implementation.unshrinkable(this::value, this.distance()); } }