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

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

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

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

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

import org.jspecify.annotations.*;

import static net.jqwik.engine.support.JqwikExceptionSupport.*;

public class EdgeCasesSupport {

	public static  EdgeCases fromSuppliers(final List>> suppliers) {
		return new EdgeCases() {
			@Override
			public List>> suppliers() {
				return suppliers;
			}

			@Override
			public String toString() {
				String edgeCases =
					suppliers
						.stream()
						.map(Supplier::get)
						.map(Shrinkable::value)
						.map(JqwikStringSupport::displayString)
						.collect(Collectors.joining(", "));
				return String.format("EdgeCases[%s]", edgeCases);
			}
		};
	}

	public static  EdgeCases choose(final List values, int maxEdgeCases) {
		List> shrinkables = new ArrayList<>();
		if (values.size() > 0) {
			shrinkables.add(new ChooseValueShrinkable<>(values.get(0), values));
		}
		if (values.size() > 1 && (shrinkables.size() < maxEdgeCases)) {
			int lastIndex = values.size() - 1;
			shrinkables.add(new ChooseValueShrinkable<>(values.get(lastIndex), values));
		}
		//noinspection CatchMayIgnoreException
		try {
			if (values.contains(null) && (shrinkables.size() < maxEdgeCases)) {
				shrinkables.add(Shrinkable.unshrinkable(null));
			}
		} catch (NullPointerException someListsDoNotAllowNullValues) { }
		return EdgeCasesSupport.fromShrinkables(shrinkables);
	}

	public static  EdgeCases concatFrom(final List> arbitraries, int maxEdgeCases) {
		List>> shrinkables = new ArrayList<>();
		for (Arbitrary arbitrary : arbitraries) {
			shrinkables.add(new ChooseValueShrinkable<>(arbitrary, arbitraries));
		}
		return flatMapArbitrary(fromShrinkables(shrinkables), Function.identity(), maxEdgeCases);
	}

	public static  EdgeCases concat(List> edgeCases, int maxEdgeCases) {
		if (edgeCases.isEmpty() || maxEdgeCases <= 0) {
			return EdgeCases.none();
		}
		if (edgeCases.size() == 1) {
			return edgeCases.get(0);
		}
		List>> concatenatedSuppliers = new ArrayList<>();
		int remainingMaxEdgeCases = maxEdgeCases;
		for (EdgeCases edgeCase : edgeCases) {
			if (edgeCase.isEmpty()) {
				continue;
			}
			List>> suppliers =
				edgeCase.suppliers()
						.stream()
						.limit(Math.max(0, remainingMaxEdgeCases))
						.collect(Collectors.toList());
			concatenatedSuppliers.addAll(suppliers);
			remainingMaxEdgeCases = remainingMaxEdgeCases - suppliers.size();
		}
		return EdgeCasesSupport.fromSuppliers(concatenatedSuppliers);
	}

	public static  EdgeCases fromShrinkables(List> shrinkables) {
		return () -> shrinkables
						 .stream()
						 .map(shrinkable -> (Supplier>) () -> shrinkable)
						 .collect(Collectors.toList());
	}

	public static  EdgeCases map(EdgeCases self, Function mapper) {
		return mapShrinkable(self, tShrinkable -> tShrinkable.map(mapper));
	}

	public static  EdgeCases mapShrinkable(EdgeCases self, Function, ? extends Shrinkable> mapper) {
		List>> mappedSuppliers =
			self.suppliers().stream()
				.map(tSupplier -> mapper.apply(tSupplier.get()))
				.filter(Objects::nonNull)
				.map(uShrinkable -> (Supplier>) () -> uShrinkable)
				.collect(Collectors.toList());
		return EdgeCases.fromSuppliers(mappedSuppliers);
	}

	public static  EdgeCases filter(EdgeCases self, Predicate filterPredicate) {
		List>> filteredSuppliers =
			self.suppliers().stream()
				.filter(supplier -> filterPredicate.test(supplier.get().value()))
				.map(supplier -> (Supplier>) () -> new FilteredShrinkable<>(supplier.get(), filterPredicate))
				.collect(Collectors.toList());
		return EdgeCases.fromSuppliers(filteredSuppliers);
	}

	public static  EdgeCases ignoreExceptions(final EdgeCases self, final Class[] exceptionTypes) {
		List>> filteredSuppliers =
			self.suppliers().stream()
				.filter(supplier -> {
					try {
						supplier.get().value();
						return true;
					} catch (Throwable throwable) {
						if (isInstanceOfAny(throwable, exceptionTypes)) {
							return false;
						}
						throw throwable;
					}
				})
				.map(shrinkableSupplier -> (Supplier>) () -> {
					Shrinkable tShrinkable = shrinkableSupplier.get();
					return new IgnoreExceptionShrinkable(tShrinkable, exceptionTypes);
				})
				.collect(Collectors.toList());
		return EdgeCases.fromSuppliers(filteredSuppliers);
	}

	public static  EdgeCases dontShrink(EdgeCases self) {
		return () -> self.suppliers()
						 .stream()
						 .map(supplier -> (Supplier>) () -> supplier.get().makeUnshrinkable())
						 .collect(Collectors.toList());
	}

	public static  EdgeCases flatMapArbitrary(
		EdgeCases self,
		Function> mapper,
		int maxEdgeCases
	) {
		List>> flatMappedSuppliers =
			self.suppliers().stream()
				.flatMap(tSupplier -> {
					T t = tSupplier.get().value();
					return mapper.apply(t).edgeCases(maxEdgeCases).suppliers()
								 .stream()
								 .map(uSupplier -> {
									 Function> shrinkableMapper =
										 newT -> mapper.apply(newT).generator(1000)
													   .next(SourceOfRandomness.newRandom(42L));
									 return (Supplier>) () -> new FixedValueFlatMappedShrinkable<>(
										 tSupplier.get(),
										 shrinkableMapper,
										 uSupplier
									 );
								 });
				})
				.limit(Math.max(0, maxEdgeCases))
				.collect(Collectors.toList());
		return EdgeCases.fromSuppliers(flatMappedSuppliers);
	}

	public static  EdgeCases combine(
		final List> arbitraries,
		final Function, ? extends T> combineFunction,
		int maxEdgeCases
	) {
		if (arbitraries.isEmpty() || maxEdgeCases <= 0) {
			return EdgeCases.none();
		}
		List>>> listOfSuppliers = new ArrayList<>();
		int remainingEdgeCases = maxEdgeCases;
		for (Arbitrary a : arbitraries) {
			EdgeCases edgeCases = a.edgeCases(remainingEdgeCases);
			if (edgeCases.isEmpty()) {
				return EdgeCases.none();
			}
			List>> supplierList = edgeCases.suppliers();
			listOfSuppliers.add(supplierList);
			remainingEdgeCases = (int) Math.max(1, Math.ceil(remainingEdgeCases / (double) supplierList.size()));
		}

		Iterator>>> iterator = Combinatorics.combine(listOfSuppliers);

		List>> suppliers = new ArrayList<>();
		int count = 0;
		while (iterator.hasNext() && count < maxEdgeCases) {
			List>> next = iterator.next();
			List> shrinkables = next.stream().map(Supplier::get).collect(Collectors.toList());
			suppliers.add(() -> new CombinedShrinkable<>(shrinkables, combineFunction));
			count++;
		}

		return EdgeCases.fromSuppliers(suppliers);
	}

}