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

net.jqwik.engine.properties.ExhaustiveShrinkablesGenerator Maven / Gradle / Ivy

The newest version!
package net.jqwik.engine.properties;

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

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

public class ExhaustiveShrinkablesGenerator implements ForAllParametersGenerator {

	public static ExhaustiveShrinkablesGenerator forParameters(
		List parameters,
		ArbitraryResolver arbitraryResolver,
		long maxNumberOfSamples
	) {
		List>> exhaustiveGenerators =
			parameters.stream()
					  .map(parameter -> resolveParameter(arbitraryResolver, parameter, maxNumberOfSamples))
					  .collect(Collectors.toList());

		return new ExhaustiveShrinkablesGenerator(exhaustiveGenerators);
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	private static List> resolveParameter(
		ArbitraryResolver arbitraryResolver,
		MethodParameter parameter,
		long maxNumberOfSamples
	) {
		Set> arbitraries = arbitraryResolver.forParameter(parameter);
		if (arbitraries.isEmpty()) {
			throw new CannotFindArbitraryException(TypeUsageImpl.forParameter(parameter), parameter.getAnnotation(ForAll.class));
		}

		List> exhaustiveGenerators = new ArrayList<>();
		for (Arbitrary arbitrary : arbitraries) {
			Optional> optionalGenerator = arbitrary.exhaustive(maxNumberOfSamples);
			if (!optionalGenerator.isPresent()) {
				String message = String.format("Arbitrary %s does not provide exhaustive generator", arbitrary);
				throw new JqwikException(message);
			}
			exhaustiveGenerators.add(optionalGenerator.get());
		}
		return exhaustiveGenerators;

	}

	private final List>> generators;
	private final long maxCount;
	private Iterator>> combinatorialIterator;

	private ExhaustiveShrinkablesGenerator(List>> generators) {
		this.maxCount = generators
							.stream()
							.mapToLong(set -> set.stream().mapToLong(ExhaustiveGenerator::maxCount).sum())
							.reduce((product, count) -> product * count)
							.orElse(1L);
		this.generators = generators;
		this.reset();
	}

	private Iterator>> combine(List>> generators) {
		List> iterables = generators
											   .stream()
											   .map(this::concat)
											   .collect(Collectors.toList());

		return new Iterator>>() {
			final Iterator> iterator = Combinatorics.combine(iterables);

			@Override
			public boolean hasNext() {
				return iterator.hasNext();
			}

			@Override
			public List> next() {
				List> values = new ArrayList<>();
				for (Object o : iterator.next()) {
					values.add(Shrinkable.unshrinkable(o));
				}
				return values;
			}
		};
	}

	private Iterable concat(List> generatorList) {
		List> iterables = generatorList
											   .stream()
											   .map(g -> (Iterable) g)
											   .collect(Collectors.toList());
		return () -> Combinatorics.concat(iterables);
	}

	@Override
	public boolean hasNext() {
		return combinatorialIterator.hasNext();
	}

	@Override
	public List> next() {
		return combinatorialIterator.next();
	}

	@Override
	public void reset() {
		this.combinatorialIterator = combine(generators);
	}

	public long maxCount() {
		return maxCount;
	}

}