net.jqwik.api.state.Action Maven / Gradle / Ivy
package net.jqwik.api.state;
import java.util.function.*;
import org.apiguardian.api.*;
import net.jqwik.api.*;
import org.jspecify.annotations.*;
import static org.apiguardian.api.API.Status.*;
/**
* An action class represents a state transformation that can be performed
* on an object of type {@code S}.
* Those objects can be mutable, which means that the state changing method will return the same object,
* or immutable, which requires the method to return another object that represents the transformed state.
*
*
* Do not implement this interface directly, but either {@linkplain Action.Dependent} or {@linkplain Action.Independent}.
* Only those can be used to create an arbitrary for a {@linkplain ActionChain}.
*
*
*
* Mind that there is a another interface {@link net.jqwik.api.stateful.Action} which looks similar
* but refers to jqwik's old and deprecated style of state-based property testing.
* The two interfaces CANNOT be used interchangeably.
*
*
* @param Type of the object to transform through an action
* @see ActionChain
* @see ActionChainArbitrary
*/
@API(status = EXPERIMENTAL, since = "1.7.0")
public interface Action {
/**
* Create an unconditioned {@linkplain ActionBuilder}.
*/
static ActionBuilder builder() {
return new ActionBuilder();
}
/**
* Create an {@linkplain ActionBuilder} with a precondition.
*/
static ActionBuilder when(Predicate super T> precondition) {
return new ActionBuilder().when(precondition);
}
/**
* Convenience method to create an independent {@linkplain Action} with a constant transformer
*/
static Action.Independent just(Transformer transformer) {
return Action.builder().just(transformer);
}
/**
* Convenience method to create an independent {@linkplain Action} with a description and a constant transformer
*/
static Action.Independent just(String description, Transformer transformer) {
return Action.builder().describeAs(description).just(transformer);
}
/**
* If this method returns false, the action will not be performed.
*
*
* Implementing this method will make the chain of actions harder to shrink.
*
*
* @param state the current state
* @return true if the precondition holds
*/
default boolean precondition(S state) {
return true;
}
/**
* Implement this interface if you want to have the action's transforming behaviour depend on the previous state.
*
*
* Implementing this interface instead of {@linkplain Independent} will make the chain of actions harder to shrink.
*
*/
@FunctionalInterface
interface Dependent extends Action {
/**
* Return an arbitrary for transformers that depends on the previous state.
*
*
* In addition to performing a state transformation the transformin function
* can also check or assert post-conditions and invariants that should hold when doing the transformation.
*
*
* @param state the current state
* @return an arbitrary of type {@linkplain Transformer Transformer}.
*/
Arbitrary> transformer(S state);
}
/**
* Implement this interface if you want to have the action's transforming behaviour not to depend on previous state.
*
*
* In addition to performing a state transformation the mutator function
* can also check or assert post-conditions and invariants that should hold when doing the transformation.
*
*
* @see JustMutate
* @see JustTransform
*/
@FunctionalInterface
interface Independent extends Action {
/**
* Return an arbitrary for transformers that does not depend on the previous state.
*
*
* In addition to performing a state transformation the transforming function
* can also check or assert post-conditions and invariants that should hold when doing the transformation.
*
*
* @return an arbitrary of type {@linkplain Transformer Transformer}.
*/
Arbitrary> transformer();
}
/**
* Subclass if you want to implement an {@linkplain Action.Independent independent action} that simply transforms the given state.
*
*
* If you rather want to declaratively specify an action start with {@linkplain Action#just(Transformer)},
* {@linkplain Action#when(Predicate)} or {@linkplain Action#builder()}.
*
*
*
* For actions whose state transformation depends on the state,
* you have to implement either {@linkplain Action.Dependent} or {@linkplain Action.Independent}.
*
*/
abstract class JustTransform implements Action.Independent {
@Override
public Arbitrary> transformer() {
return Arbitraries.just(Transformer.transform(
description(),
this::transform
));
}
public abstract S transform(S state);
String description() {
return getClass().getSimpleName();
}
}
/**
* Subclass if you want to implement an {@linkplain Action.Independent independent action} that simply mutates the given state.
*
*
* If you rather want to declaratively specify an action start with {@linkplain Action#just(Transformer)},
* {@linkplain Action#when(Predicate)} or {@linkplain Action#builder()}.
*
*
*
* For actions whose state transformation depends on the state,
* you have to implement either {@linkplain Action.Dependent} or {@linkplain Action.Independent}.
*
*/
abstract class JustMutate implements Action.Independent {
@Override
public Arbitrary> transformer() {
return Arbitraries.just(Transformer.mutate(
description(),
this::mutate
));
}
abstract public void mutate(S state);
public String description() {
return getClass().getSimpleName();
}
}
}