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

net.jqwik.engine.properties.shrinking.FlatMappedShrinkable Maven / Gradle / Ivy

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

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

import net.jqwik.api.*;
import net.jqwik.api.support.*;
import net.jqwik.engine.*;
import net.jqwik.engine.support.*;

import org.jspecify.annotations.*;

public class FlatMappedShrinkable implements Shrinkable {

	private final Shrinkable toMap;
	private final Function> mapper;

	public FlatMappedShrinkable(
		Shrinkable toMap,
		Function> toArbitraryMapper,
		int genSize,
		long randomSeed,
		boolean withEmbeddedEdgeCases
	) {
		this(toMap, t -> {
			Arbitrary arbitrary = toArbitraryMapper.apply(t);
			return arbitrary.generator(genSize, withEmbeddedEdgeCases);
		}, randomSeed);
	}

	public FlatMappedShrinkable(Shrinkable toMap, Function> toGeneratorMapper, long randomSeed) {
		this(toMap, t -> toGeneratorMapper.apply(t).next(SourceOfRandomness.newRandom(randomSeed)));
	}

	protected FlatMappedShrinkable(Shrinkable toMap, Function> mapper) {
		this.toMap = toMap;
		this.mapper = mapper;
	}

	private Shrinkable generateShrinkable(T value) {
		return mapper.apply(value);
	}

	@Override
	public Stream> shrink() {
		return JqwikStreamSupport.concat(
			shrinkRightSide(),
			shrinkLeftSide(),
			shrinkLeftGrowRightSide()
		);
	}

	private Stream> shrinkRightSide() {
		final ShrinkingDistance rightDistance = shrinkable().distance();
		return shrinkable().shrink()
						   .filter(s -> s.distance().size() <= rightDistance.size())
						   .map(rightSide -> new FixedValueFlatMappedShrinkable<>(toMap, mapper, () -> rightSide));
	}

	private Stream> shrinkLeftSide() {
		// final ShrinkingDistance leftDistance = toMap.distance();
		return toMap.shrink()
					// Seems to make shrinking less effective in some cases:
					// .filter(s -> s.distance().size() <= leftDistance.size())
					.map(shrunkLeftSide -> new FlatMappedShrinkable<>(shrunkLeftSide, mapper));
	}

	private Stream> growRightSide() {
		return shrinkable().grow()
						   .map(rightSide -> new FixedValueFlatMappedShrinkable<>(toMap, mapper, () -> rightSide));
	}

	private Stream> shrinkLeftGrowRightSide() {
		return toMap.shrink()
					.map(shrunkLeftSide -> new FlatMappedShrinkable<>(shrunkLeftSide, mapper))
					.flatMap(FlatMappedShrinkable::growRightSide);
	}

	@Override
	public U value() {
		return shrinkable().value();
	}

	protected Shrinkable shrinkable() {
		return generateShrinkable(toMap.value());
	}

	@Override
	public ShrinkingDistance distance() {
		return toMap.distance().append(shrinkable().distance());
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (!(o instanceof FlatMappedShrinkable)) return false;
		FlatMappedShrinkable that = (FlatMappedShrinkable) o;
		return Objects.equals(value(), that.value());
	}

	@Override
	public int hashCode() {
		return HashCodeSupport.hash(value());
	}

	@Override
	public String toString() {
		return String.format(
			"%s<%s>(%s:%s)|%s",
			getClass().getSimpleName(),
			value().getClass().getSimpleName(),
			value(),
			distance(),
			toMap
		);
	}

}