Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.dakusui.thincrest.metamor.MetamorphicTestCaseFactory Maven / Gradle / Ivy
package com.github.dakusui.thincrest.metamor;
import com.github.dakusui.thincrest.metamor.internals.InternalUtils;
import com.github.dakusui.thincrest_pcond.forms.Printables;
import java.text.MessageFormat;
import java.util.function.*;
import static com.github.dakusui.thincrest_pcond.forms.Predicates.transform;
import static java.util.Objects.requireNonNull;
/**
* An interface of a factory for a metamorphic test case.
*
* @param Type of "source value".
* @param Input type of the function under test.
* @param Output type of function under test.
* @param Input type of metamorphic relation.
*/
public interface MetamorphicTestCaseFactory {
/**
* Returns a function under test.
*
* @return a function under test.
*/
Function fut();
InputResolver.Sequence.Factory inputResolverSequenceFactory();
Function>, R> metamorphicTransformer();
Predicate metamorphicChecker();
default Predicate>> metamorphicRelation() {
return transform(metamorphicTransformer()).check(metamorphicChecker());
}
/**
* Returns a function that executes the FUT for each element in `Dataset>`.
*
* @return A function that executes the FUT for each element in `Dataset>`.
*/
default Function>, Dataset>> metamorphicExecutor() {
return InternalUtils.createObservableProcessingPipeline("fut", this.metamorphicMapper(), this.inputResolverSequenceFactory().count(), inputVariableNameFormatter(), ioVariableName());
}
/**
* A name of input variable.
* This will be printed in the test report.
*
* @return A name of input variable.
*/
String inputVariableName();
/**
* In metamorphic testing context, the function under test is executed multiple times with different input values.
* The returned function renders input variable names so that they can be identified each other when an index is given.
* By default, it returns a function that appends the given index.
*
* @return A function to render an input variable name corresponding to a given index.
*/
default IntFunction inputVariableNameFormatter() {
return i -> this.inputVariableName() + "[" + i + "]";
}
default Function, IoPair>, Function, IoPair>> metamorphicMapper() {
return Printables.function(
() -> " " + fut(),
ioContext -> Printables.function(
() -> "input:" + ioContext.output(),
inputResolver -> {
I in = inputResolver.apply(ioContext.output());
return IoPair.create(in, fut().apply(in));
}));
}
/**
* A builder method that returns a printable predicate that examines the function under test.
*
* @return A printable predicate that examines FUT with a given metamorphic relation.
*/
default Predicate toMetamorphicTestPredicate() {
return transform(this.inputResolverSequenceFactory().andThen(this.metamorphicExecutor())).check(this.metamorphicRelation());
}
String ioVariableName();
static Builder forFunctionUnderTest(String name, Function fut) {
return forFunctionUnderTest(Printables.function(name, fut));
}
static Builder forFunctionUnderTest(Function fut) {
return new Builder().fut(fut);
}
class Impl implements MetamorphicTestCaseFactory {
private final Function fut;
private final InputResolver.Sequence.Factory inputResolverSequenceFactory;
private final Function>, R> metamorphicTransformer;
private final Predicate metamorphicChecker;
private final String inputVariableName;
private final String ioVariableName;
public Impl(Function fut, InputResolver.Sequence.Factory inputResolverSequenceFactory, Function>, R> metamorphicTransformer, Predicate metamorphicChecker, String inputVariableName, String ioVariableName) {
this.fut = fut;
this.inputResolverSequenceFactory = inputResolverSequenceFactory;
this.metamorphicTransformer = metamorphicTransformer;
this.metamorphicChecker = metamorphicChecker;
this.inputVariableName = inputVariableName;
this.ioVariableName = ioVariableName;
}
@Override
public Function fut() {
return this.fut;
}
@Override
public InputResolver.Sequence.Factory inputResolverSequenceFactory() {
return this.inputResolverSequenceFactory;
}
@Override
public Function>, R> metamorphicTransformer() {
return this.metamorphicTransformer;
}
@Override
public Predicate metamorphicChecker() {
return this.metamorphicChecker;
}
@Override
public String inputVariableName() {
return this.inputVariableName;
}
@Override
public String ioVariableName() {
return this.ioVariableName;
}
}
abstract class BuilderBase, X, I, O, R> {
abstract static class InputResolverSequenceFactoryProvider implements Supplier> {
final BuilderBase, X, I, O, ?> parent;
protected InputResolverSequenceFactoryProvider(BuilderBase, X, I, O, ?> parent) {
this.parent = parent;
}
abstract void add(Function formatter, Function function);
abstract int count();
}
protected Function fut;
protected InputResolverSequenceFactoryProvider inputResolverSequenceFactoryProvider;
protected Predicate checker;
protected String sourceVariableName;
protected String inputVariableName;
protected String ioVariableName;
protected String outputVariableName;
protected BuilderBase() {
this.sourceVariableName("x")
.inputVariableName("input")
.ioVariableName("io")
.outputVariableName("out");
}
protected , XX, RR> BB newBuilder(Supplier constructor) {
return constructor.get()
.fut(this.fut)
.sourceVariableName(this.sourceVariableName)
.inputVariableName(this.inputVariableName)
.ioVariableName(this.ioVariableName)
.outputVariableName(this.outputVariableName);
}
protected , RR> BB newBuilderWithSpecifiedRelationType(Supplier constructor) {
return newBuilder(constructor)
.inputResolverSequenceFactoryProvider(this.inputResolverSequenceFactoryProvider);
}
protected , XX> BB newBuilderWithSpecifiedSourceType(Supplier constructor) {
return this.newBuilder(constructor);
}
@SuppressWarnings("unchecked")
public B sourceVariableName(String sourceVariableName) {
this.sourceVariableName = sourceVariableName;
return (B) this;
}
@SuppressWarnings("unchecked")
public B inputVariableName(String inputVariableName) {
this.inputVariableName = inputVariableName;
return (B) this;
}
@SuppressWarnings("unchecked")
public B outputVariableName(String outputVariableName) {
this.outputVariableName = requireNonNull(outputVariableName);
return (B) this;
}
@SuppressWarnings("unchecked")
public B ioVariableName(String ioVariableName) {
this.ioVariableName = requireNonNull(ioVariableName);
return (B) this;
}
@SuppressWarnings("unchecked")
B inputResolverSequenceFactoryProvider(InputResolverSequenceFactoryProvider inputResolverSequenceFactory) {
this.inputResolverSequenceFactoryProvider = requireNonNull(inputResolverSequenceFactory);
return (B) this;
}
public B inputResolverSequenceFactory(InputResolver.Sequence.Factory inputResolverSequenceFactory) {
Utils.requireState(this.inputResolverSequenceFactoryProvider == null, "Input Resolver Sequence Factory is already set.");
return this.inputResolverSequenceFactoryProvider(new InputResolverSequenceFactoryProvider(this) {
@Override
public InputResolver.Sequence.Factory get() {
return inputResolverSequenceFactory;
}
@Override
void add(Function formatter, Function function) {
throw new IllegalStateException();
}
@Override
int count() {
return inputResolverSequenceFactory.count();
}
});
}
public B addInputResolvers(Function, InputResolver.Sequence.Factory> b) {
return this.addInputResolvers(this.sourceVariableName, b);
}
public B addInputResolvers(String variableName, Function, InputResolver.Sequence.Factory> b) {
InputResolver.Sequence.Factory.Builder ib = new InputResolver.Sequence.Factory.Builder<>(this.inputVariableName, variableName);
return this.inputResolverSequenceFactory(b.apply(ib));
}
@SuppressWarnings("unchecked")
public B addInputResolver(Function formatter, Function f) {
requireNonNull(formatter);
requireNonNull(f);
if (this.inputResolverSequenceFactoryProvider == null) {
this.inputResolverSequenceFactoryProvider = new InputResolverSequenceFactoryProvider(this) {
int count = 0;
Consumer> inputResolverAdder = b -> {
};
@Override
void add(Function formatter, Function f) {
inputResolverAdder = inputResolverAdder.andThen(b -> b.function(formatter, f));
count++;
}
@Override
int count() {
return count;
}
@Override
public InputResolver.Sequence.Factory get() {
InputResolver.Sequence.Factory.Builder b = new InputResolver.Sequence.Factory.Builder<>(BuilderBase.this.inputVariableName, BuilderBase.this.sourceVariableName);
this.inputResolverAdder.accept(b);
return b.build();
}
};
}
this.inputResolverSequenceFactoryProvider.add(formatter, f);
return (B) this;
}
public abstract , XX> BB sourceValueType(XX sourceType);
/**
* Let this object know the source type.
*
* @param sourceType The type of the source value.x
* @param The type of this object.
* @param The type of the input value.
* @return This object
*/
@SuppressWarnings("unused")
public , XX> BB sourceValueType(Class sourceType) {
return this.sourceValueType((XX) null);
}
/**
* Let this factory know that the source value and the input values are the same type.
*
* @param The type of this builder.
* @return This object
*/
@SuppressWarnings("unchecked")
public > BB makeInputResolversEndomorphic() {
return (BB) this.sourceValueType((I) null)
.addInputResolver(x -> String.format("%s", x), Function.identity());
}
/**
* Specifies a function under test.
*
* @param fut A function under test
* @return This builder object
*/
@SuppressWarnings("unchecked")
public B fut(Function fut) {
this.fut = requireNonNull(fut);
return (B) this;
}
public MetamorphicTestCaseFactoryWithPreformer.Builder withPreformer() {
return this.newBuilderWithSpecifiedRelationType(MetamorphicTestCaseFactoryWithPreformer.Builder::new);
}
public Builder skipPreformer() {
return this.newBuilderWithSpecifiedRelationType(Builder::new);
}
public abstract MetamorphicTestCaseFactoryWithPreformer.Builder preformer(Function, P> preformer);
public MetamorphicTestCaseFactoryWithPreformer.Builder preformer(String preformerName, Function, P> preformer) {
return this.preformer(Printables.function(preformerName, preformer));
}
public MetamorphicTestCaseFactoryWithPreformer.Builder outputOnly() {
return this.preformer("outputOnly", IoPair::output);
}
@SuppressWarnings("unchecked")
public B checker(Predicate checker) {
this.checker = requireNonNull(checker);
return (B) this;
}
public MetamorphicTestCaseFactory check(String name, Predicate checker) {
return this.check(Printables.predicate(name, checker));
}
public MetamorphicTestCaseFactory check(Predicate checker) {
return checker(checker).build();
}
public abstract MetamorphicTestCaseFactory build();
}
class Builder extends BuilderBase, X, I, O, R> {
private Function>, R> transformer;
public Builder() {
}
@SuppressWarnings("unchecked")
@Override
public , XX> BB sourceValueType(XX sourceType) {
return (BB) this., XX>newBuilderWithSpecifiedSourceType(Builder::new);
}
public Builder propositionFactory(Function>, Proposition> pf) {
return this., Proposition>newBuilderWithSpecifiedRelationType(Builder::new)
.transformer(pf)
.checker(PropositionPredicate.INSTANCE);
}
public MetamorphicTestCaseFactory proposition(Function formatter, Predicate>> p) {
return this.propositionFactory(
Proposition.Factory.create(
p,
formatter,
i -> ioVariableName + "[" + i + "]",
inputResolverSequenceFactoryProvider.count()))
.build();
}
public MetamorphicTestCaseFactory proposition(String propositionName, Predicate>> p) {
return this.proposition(args -> MessageFormat.format(propositionName, args), p);
}
public MetamorphicTestCaseFactoryWithPreformer.Builder preformer(Function, P> preformer) {
return this.withPreformer().preformer(preformer);
}
public Builder transformer(Function>, R> transformer) {
this.transformer = requireNonNull(transformer);
return this;
}
@Override
public MetamorphicTestCaseFactory build() {
return new Impl<>(this.fut, this.inputResolverSequenceFactoryProvider.get(), this.transformer, this.checker, this.inputVariableName, this.ioVariableName);
}
}
}