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

net.jqwik.engine.properties.state.DefaultActionChainArbitrary Maven / Gradle / Ivy

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

import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;

import org.jspecify.annotations.*;
import org.junit.platform.commons.support.*;

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

public class DefaultActionChainArbitrary extends ArbitraryDecorator> implements ActionChainArbitrary {

	private ChainArbitrary chainArbitrary;

	public DefaultActionChainArbitrary(Supplier initialSupplier) {
		chainArbitrary = new DefaultChainArbitrary<>(initialSupplier);
	}

	private Transformation createTransformation(Action action) {
		checkActionIsConsistent(action);
		Optional> optionalPrecondition = precondition(action);
		return optionalPrecondition.map(precondition -> Transformation.when(precondition)
																	  .provide(state -> toTransformerArbitrary(action, () -> state)))
								   .orElseGet(() -> supplier -> toTransformerArbitrary(action, supplier));
	}

	@SuppressWarnings("ConstantConditions")
	private Optional> precondition(Action action) {
		try {
			Method precondition = preconditionMethod(action.getClass());
			if (!precondition.equals(preconditionMethod(Action.class))) {
				return Optional.of(
					state -> {
						try {
							return (boolean) ReflectionSupport.invokeMethod(precondition, action, state);
						} catch (Exception exception) {
							return JqwikExceptionSupport.throwAsUncheckedException(exception);
						}
					});
			}
		} catch (NoSuchMethodException ignore) {}
		return Optional.empty();
	}

	private void checkActionIsConsistent(Action action) {
		if (!(action instanceof Action.Dependent) && !(action instanceof Action.Independent)) {
			String message = String.format(
				"Action <%s> must implement exactly one of type Action.Dependent or Action.Independent," +
					"but implements neither.",
				action
			);
			throw new IllegalArgumentException(message);
		}
		if ((action instanceof Action.Dependent) && (action instanceof Action.Independent)) {
			String message = String.format(
				"Action <%s> must implement exactly one of type Action.Dependent or Action.Independent," +
					"but implements both.",
				action
			);
			throw new IllegalArgumentException(message);
		}
	}

	private Arbitrary> toTransformerArbitrary(Action action, Supplier stateSupplier) {
		if (action instanceof Action.Independent) {
			Action.Independent independentAction = (Action.Independent) action;
			return independentAction.transformer();
		}
		if (action instanceof Action.Dependent) {
			Action.Dependent dependentAction = (Action.Dependent) action;
			return dependentAction.transformer(stateSupplier.get());
		}
		throw new RuntimeException("Should never get here. Should be caught before by checkActionIsConsistent()");
	}

	private Method preconditionMethod(Class aClass) throws NoSuchMethodException {
		return aClass.getMethod("precondition", Object.class);
	}

	@Override
	public ActionChainArbitrary withAction(int weight, Action action) {
		DefaultActionChainArbitrary clone = typedClone();
		clone.chainArbitrary = clone.chainArbitrary.withTransformation(weight, createTransformation(action));
		return clone;
	}

	@Override
	public ActionChainArbitrary withMaxTransformations(int maxSize) {
		DefaultActionChainArbitrary clone = typedClone();
		clone.chainArbitrary = clone.chainArbitrary.withMaxTransformations(maxSize);
		return clone;
	}

	@Override
	public ActionChainArbitrary improveShrinkingWith(Supplier> changeDetectorSupplier) {
		DefaultActionChainArbitrary clone = typedClone();
		clone.chainArbitrary = clone.chainArbitrary.improveShrinkingWith(changeDetectorSupplier);
		return clone;
	}

	@Override
	protected Arbitrary> arbitrary() {
		return chainArbitrary.map(SequentialActionChain::new);
	}

	@Override
	public boolean isGeneratorMemoizable() {
		// Not memoizable, because any non-memoizable arbitrary could be used in actions
		return false;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy