package fj.data;
import fj.F;
import fj.P2;
import fj.Unit;
import fj.test.Cogen;
import fj.test.Gen;
import fj.test.Property;
import fj.test.reflect.CheckParams;
import fj.test.runner.PropertyTestRunner;
import org.junit.runner.RunWith;
import static fj.P.p;
import static fj.data.List.range;
import static fj.test.Arbitrary.arbF;
import static fj.test.Arbitrary.arbInteger;
import static fj.test.Arbitrary.arbList;
import static fj.test.Arbitrary.arbP2;
import static fj.test.Cogen.cogenInteger;
import static fj.test.Cogen.cogenP2;
import static fj.test.Property.prop;
import static fj.test.Property.property;
@RunWith(PropertyTestRunner.class)
public final class StateProperties {
private static final Gen<Unit> arbUnit = Gen.value(Unit.unit());
private static final int HUGE_SIZE = 10000;
private static <S, A> boolean testUnit(F<S, P2<S, A>> runF, S initS) {
State<S, A> instance = State.unit(runF);
P2<S, A> actual = instance.run(initS);
P2<S, A> expected = runF.f(initS);
return actual.equals(expected);
}
private static <S> boolean testInit(S initS) {
State<S, S> instance = State.init();
P2<S, S> actual = instance.run(initS);
P2<S, S> expected = p(initS, initS);
return actual.equals(expected);
}
private static <S> boolean testUnits(F<S, S> f, S initS) {
State<S, S> instance = State.units(f);
P2<S, S> actual = instance.run(initS);
S expectedS = f.f(initS);
P2<S, S> expected = p(expectedS, expectedS);
return actual.equals(expected);
}
private static <S, A> boolean testConstant(A a, S initS) {
State<S, A> instance = State.constant(a);
P2<S, A> actual = instance.run(initS);
P2<S, A> expected = p(initS, a);
return actual.equals(expected);
}
private static <S, A> boolean testStaticGets(F<S, A> f, S initS) {
State<S, A> instance = State.gets(f);
P2<S, A> actual = instance.run(initS);
P2<S, A> expected = p(initS, f.f(initS));
return actual.equals(expected);
}
private static <S> boolean testPut(S newS, S initS) {
State<S, Unit> instance = State.put(newS);
P2<S, Unit> actual = instance.run(initS);
P2<S, Unit> expected = p(newS, Unit.unit());
return actual.equals(expected);
}
private static <S> boolean testModify(F<S, S> f, S initS) {
State<S, Unit> instance = State.modify(f);
P2<S, Unit> actual = instance.run(initS);
P2<S, Unit> expected = p(f.f(initS), Unit.unit());
return actual.equals(expected);
}
private static <S, A, B> boolean testStaticFlatMap(F<S, P2<S, A>> runF, F<A, State<S, B>> f, S initS) {
State<S, B> instance = State.flatMap(State.unit(runF), f);
P2<S, B> actual = instance.run(initS);
P2<S, A> intermediateExpected = runF.f(initS);
P2<S, B> expected = f.f(intermediateExpected._2()).run(intermediateExpected._1());
return actual.equals(expected);
}
private static <S, A> boolean testSequence(List<State<S, A>> states, S initS) {
State<S, List<A>> instance = State.sequence(states);
P2<S, List<A>> actual = instance.run(initS);
S expectedFinalS = initS;
List<A> expectedAs = List.nil();
List<State<S, A>> remainingStates = states;
while (remainingStates.isNotEmpty()) {
P2<S, A> nextResult = remainingStates.head().run(expectedFinalS);
expectedFinalS = nextResult._1();
expectedAs = List.cons(nextResult._2(), expectedAs);
remainingStates = remainingStates.tail();
}
expectedAs = expectedAs.reverse();
P2<S, List<A>> expected = p(expectedFinalS, expectedAs);
return actual.equals(expected);
}
private static <S, A, B> boolean testTraverse(List<A> as, F<A, State<S, B>> f, S initS) {
State<S, List<B>> instance = State.traverse(as, f);
P2<S, List<B>> actual = instance.run(initS);
S expectedFinalS = initS;
List<B> expectedFinalBs = List.nil();
List<A> currAs = as;
while (currAs.isNotEmpty()) {
P2<S, B> nextStateAndB = f.f(currAs.head()).run(expectedFinalS);
expectedFinalS = nextStateAndB._1();
expectedFinalBs = List.cons(nextStateAndB._2(), expectedFinalBs);
currAs = currAs.tail();
}
expectedFinalBs = expectedFinalBs.reverse();
P2<S, List<B>> expected = p(expectedFinalS, expectedFinalBs);
return actual.equals(expected);
}
private static <S, A> boolean testRun(F<S, P2<S, A>> runF, S initS) {
State<S, A> instance = State.unit(runF);
P2<S, A> actual = instance.run(initS);
P2<S, A> expected = runF.f(initS);
return actual.equals(expected);
}
private static <S, A> boolean testEval(F<S, P2<S, A>> runF, S initS) {
State<S, A> instance = State.unit(runF);
A actual = instance.eval(initS);
A expected = runF.f(initS)._2();
return actual.equals(expected);
}
private static <S> boolean testExec(F<S, P2<S, Unit>> runF, S initS) {
State<S, Unit> instance = State.unit(runF);
S actual = instance.exec(initS);
S expected = runF.f(initS)._1();
return actual.equals(expected);
}
private static <S, A> boolean testGets(State<S, A> state, S initS) {
State<S, S> instance = state.gets();
P2<S, S> actual = instance.run(initS);
P2<S, S> expected = p(state.run(initS)._1(), state.run(initS)._1());
return actual.equals(expected);
}
private static <S, A, B> boolean testMap(State<S, A> state, F<A, B> f, S initS) {
State<S, B> instance = state.map(f);
P2<S, B> actual = instance.run(initS);
P2<S, B> expected = state.run(initS).map2(f);
return actual.equals(expected);
}
private static <S, A, B> boolean testMapState(State<S, A> state, F<P2<S, A>, P2<S, B>> f, S initS) {
State<S, B> instance = state.mapState(f);
P2<S, B> actual = instance.run(initS);
P2<S, B> expected = f.f(state.run(initS));
return actual.equals(expected);
}
private static <S, A> boolean testWiths(State<S, A> state, F<S, S> f, S initS) {
State<S, A> instance = state.withs(f);
P2<S, A> actual = instance.run(initS);
P2<S, A> expected = state.run(f.f(initS));
return actual.equals(expected);
}
private static <S, A, B> boolean testFlatMap(State<S, A> state, F<A, State<S, B>> f, S initS) {
State<S, B> instance = state.flatMap(f);
P2<S, B> actual = instance.run(initS);
P2<S, B> expected = f.f(state.run(initS)._2()).run(state.run(initS)._1());
return actual.equals(expected);
}
private static <S, A> boolean testNoStackOverflow(State<S, A> instance, S initS) {
instance.run(initS);
return true;
}
private static <S, A> Gen<F<S, P2<S, A>>> arbRunF(
Cogen<S> cogenInitS,
Gen<S> arbNextS,
Gen<A> arbValue) {
return arbF(cogenInitS, arbP2(arbNextS, arbValue));
}
private static <S, A> Gen<F<P2<S, A>, P2<S, A>>> arbMapStateF(
Cogen<S> cogenInitS,
Cogen<A> cogenInitValue,
Gen<S> arbNextS,
Gen<A> arbNextValue) {
return arbF(cogenP2(cogenInitS, cogenInitValue), arbP2(arbNextS, arbNextValue));
}
private static <S, A> Gen<State<S, A>> arbState(
Cogen<S> cogenInitS,
Gen<S> arbNextS,
Gen<A> arbValue) {
Gen<F<S, P2<S, A>>> arbRunF = arbRunF(cogenInitS, arbNextS, arbValue);
return Gen.gen(s -> r -> State.unit(arbRunF.gen(s, r)));
}
private static <S, A> Gen<State<S, A>> arbHugeState(
Gen<State<S, A>> arbInitState,
F<State<S, A>, Gen<State<S, A>>> nextArbStateF) {
return Gen.gen(s -> r -> range(0, HUGE_SIZE).foldLeft(
(acc, x) -> nextArbStateF.f(acc).gen(s, r),
arbInitState.gen(s, r)));
}
public Property unit() {
return property(
arbRunF(cogenInteger, arbInteger, arbInteger),
arbInteger,
(runF, initS) -> prop(testUnit(runF, initS)));
}
public Property init() {
return property(
arbInteger,
initS -> prop(testInit(initS)));
}
public Property units() {
return property(
arbF(cogenInteger, arbInteger),
arbInteger,
(f, initS) -> prop(testUnits(f, initS)));
}
public Property constant() {
return property(
arbInteger,
arbInteger,
(a, initS) -> prop(testConstant(a, initS)));
}
public Property staticGets() {
return property(
arbF(cogenInteger, arbInteger),
arbInteger,
(f, initS) -> prop(testStaticGets(f, initS)));
}
public Property put() {
return property(
arbInteger,
arbInteger,
(newS, initS) -> prop(testPut(newS, initS)));
}
public Property modify() {
return property(
arbF(cogenInteger, arbInteger),
arbInteger,
(f, initS) -> prop(testModify(f, initS)));
}
public Property staticFlatMap() {
return property(
arbRunF(cogenInteger, arbInteger, arbInteger),
arbF(cogenInteger, arbState(cogenInteger, arbInteger, arbInteger)),
arbInteger,
(runF, f, initS) -> prop(testStaticFlatMap(runF, f, initS)));
}
public Property sequence() {
return property(
arbList(arbState(cogenInteger, arbInteger, arbInteger)),
arbInteger,
(states, initS) -> prop(testSequence(states, initS)));
}
public Property traverse() {
return property(
arbList(arbInteger),
arbF(cogenInteger, arbState(cogenInteger, arbInteger, arbInteger)),
arbInteger,
(as, f, initS) -> prop(testTraverse(as, f, initS)));
}
public Property run() {
return property(
arbRunF(cogenInteger, arbInteger, arbInteger),
arbInteger,
(runF, initS) -> prop(testRun(runF, initS)));
}
public Property eval() {
return property(
arbRunF(cogenInteger, arbInteger, arbInteger),
arbInteger,
(runF, initS) -> prop(testEval(runF, initS)));
}
public Property exec() {
return property(
arbRunF(cogenInteger, arbInteger, arbUnit),
arbInteger,
(runF, initS) -> prop(testExec(runF, initS)));
}
public Property getsProperty() {
return property(
arbState(cogenInteger, arbInteger, arbInteger),
arbInteger,
(state, initS) -> prop(testGets(state, initS)));
}
public Property map() {
return property(
arbState(cogenInteger, arbInteger, arbInteger),
arbF(cogenInteger, arbInteger),
arbInteger,
(state, f, initS) -> prop(testMap(state, f, initS)));
}
public Property mapState() {
return property(
arbState(cogenInteger, arbInteger, arbInteger),
arbMapStateF(cogenInteger, cogenInteger, arbInteger, arbInteger),
arbInteger,
(state, f, initS) -> prop(testMapState(state, f, initS)));
}
public Property withs() {
return property(
arbState(cogenInteger, arbInteger, arbInteger),
arbF(cogenInteger, arbInteger),
arbInteger,
(state, f, initS) -> prop(testWiths(state, f, initS)));
}
public Property flatMap() {
return property(
arbState(cogenInteger, arbInteger, arbInteger),
arbF(cogenInteger, arbState(cogenInteger, arbInteger, arbInteger)),
arbInteger,
(state, f, initS) -> prop(testFlatMap(state, f, initS)));
}
@CheckParams(minSuccessful = 1)
public Property getsStackSafety() {
return property(
arbHugeState(
arbState(cogenInteger, arbInteger, arbInteger),
currState -> Gen.value(currState.gets())),
arbInteger,
(instance, initS) -> prop(testNoStackOverflow(instance, initS)));
}
@CheckParams(minSuccessful = 1)
public Property mapStackSafety() {
return property(
arbHugeState(
arbState(cogenInteger, arbInteger, arbInteger),
currState -> Gen.gen(s -> r -> currState.map(arbF(cogenInteger, arbInteger).gen(s, r)))),
arbInteger,
(instance, initS) -> prop(testNoStackOverflow(instance, initS)));
}
@CheckParams(minSuccessful = 1)
public Property mapStateStackSafety() {
return property(
arbHugeState(
arbState(cogenInteger, arbInteger, arbInteger),
currState -> Gen.gen(s -> r ->
currState.mapState(arbMapStateF(cogenInteger, cogenInteger, arbInteger, arbInteger).gen(s, r)))),
arbInteger,
(instance, initS) -> prop(testNoStackOverflow(instance, initS)));
}
@CheckParams(minSuccessful = 1)
public Property withsStackSafety() {
return property(
arbHugeState(
arbState(cogenInteger, arbInteger, arbInteger),
currState -> Gen.gen(s -> r -> currState.withs(arbF(cogenInteger, arbInteger).gen(s, r)))),
arbInteger,
(instance, initS) -> prop(testNoStackOverflow(instance, initS)));
}
@CheckParams(minSuccessful = 1)
public Property flatMapStackSafety() {
return property(
arbHugeState(
arbState(cogenInteger, arbInteger, arbInteger),
currState -> Gen.gen(s -> r ->
currState.flatMap(arbF(cogenInteger, arbState(cogenInteger, arbInteger, arbInteger)).gen(s, r)))),
arbInteger,
(instance, initS) -> prop(testNoStackOverflow(instance, initS)));
}
}