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

teststate.run.Test.scala Maven / Gradle / Ivy

The newest version!
package teststate.run

import japgolly.microlibs.name_fn._
import teststate.core.CoreExports._
import teststate.core.CoreExports2._
import teststate.core._
import teststate.data._
import teststate.typeclass._

sealed abstract class PlanLike[F[_], R, O, S, E, This] { self: This =>
  type Self[FF[_], RR, OO, SS, EE]

  // This causes scalac to produce stupid, spurious errors on the F type.
  // final type This = Self[F, R, O, S, E]

  protected def plan: Plan[F, R, O, S, E]
  protected def setPlan(plan: Plan[F, R, O, S, E]): This

  def name      : Option[Name]           = plan.name
  def actions   : Actions[F, R, O, S, E] = plan.actions
  def invariants: Invariants[O, S, E]    = plan.invariants
  implicit val executionModel: ExecutionModel[F] = plan.executionModel

//  def trans[G[_]: ExecutionModel](t: F ~~> G): Self[G, R, O, S, E]
//  def mapR[R2](f: R2 => R): Self[F, R2, O, S, E]
//  def pmapR[R2](f: R2 => E Or R): Self[F, R2, O, S, E]
//  def pmapO[OO](g: OO => E Or O): Self[F, R, OO, S, E]
//  def mapS[SS](g: SS => S)(s: (SS, S) => SS): Self[F, R, O, SS, E]
//  def mapE[EE](f: E => EE): Self[F, R, O, S, EE]
//  def lift[F2[_], R2, O2, S2, E2](implicit t: Transformer[F, R, O, S, E, F2, R2, O2, S2, E2]): Self[F2, R2, O2, S2, E2]

  final protected def modPlan(f: Plan[F, R, O, S, E] => Plan[F, R, O, S, E]): This =
    setPlan(f(plan))

  final def setActions(actions: Actions[F, R, O, S, E]): This =
    modPlan(p => new Plan(p.name, actions, p.invariants))

  final def setInvariants(invariants: Invariants[O, S, E]): This =
    modPlan(p => new Plan(p.name, p.actions, invariants))

  final def clearName: This =
    modPlan(p => new Plan(None, p.actions, p.invariants))

  final def named(name: Name): This =
    modPlan(p => new Plan(Some(name), p.actions, p.invariants))

  final def modActions(f: Actions[F, R, O, S, E] => Actions[F, R, O, S, E]): This =
    setActions(f(actions))

  final def modInvariants(f: Invariants[O, S, E] => Invariants[O, S, E]): This =
    setInvariants(f(invariants))

  def addInvariants(i: Invariants[O, S, E]): This =
    modInvariants(_ & i)

  // TODO asAction that uses this.name
  final def asAction(name: NameFn[ROS[R, O, S]]): Actions[F, R, O, S, E] =
    Action.liftInner(Action.SubTest(actions, invariants))(name)
}

// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████

object Plan {
  def apply[F[_], R, O, S, E](a: Actions[F, R, O, S, E], i: Invariants[O, S, E])(implicit em: ExecutionModel[F]): Plan[F, R, O, S, E] =
    new Plan(None, a, i)(em)

  def empty[F[_], R, O, S, E](implicit em: ExecutionModel[F]): Plan[F, R, O, S, E] =
    apply[F, R, O, S, E](Empty.instance, Empty.instance)

  implicit def emptyInstance[F[_], R, O, S, E](implicit em: ExecutionModel[F]): Empty[Plan[F, R, O, S, E]] =
    Empty(empty)

  def action[F[_], R, O, S, E](a: Actions[F, R, O, S, E])(implicit em: ExecutionModel[F]): Plan[F, R, O, S, E] =
    apply(a, Empty.instance[Invariants[O, S, E]])(em)

  def invariants[F[_], R, O, S, E](i: Invariants[O, S, E])(implicit em: ExecutionModel[F]): Plan[F, R, O, S, E] =
    apply[F, R, O, S, E](Empty.instance, i)(em)

  implicit def planInstanceDisplay[F[_], R, O, S, E](implicit sa: Display[Actions[F, R, O, S, E]],
                                                  si: Display[Invariants[O, S, E]]): Display[Plan[F, R, O, S, E]] =
    Display(p =>
      s"""
         |Invariants:
         |${si.indent(p.invariants)}
         |Actions:
         |${sa.indent(p.actions)}
       """.stripMargin.trim
    )
}

final class Plan[F[_], R, O, S, E](override val name: Option[Name],
                                   override val actions: Actions[F, R, O, S, E],
                                   override val invariants: Invariants[O, S, E])
                                  (implicit override val executionModel: ExecutionModel[F])
    extends PlanLike[F, R, O, S, E, Plan[F, R, O, S, E]] {

  override def toString: String =
    s"Plan($name, $actions, $invariants)"

  override type Self[FF[_], RR, OO, SS, EE] = Plan[FF, RR, OO, SS, EE]

  override protected def plan                            = this
  override protected def setPlan(p: Plan[F, R, O, S, E]) = p

  def trans[G[_]: ExecutionModel](t: F ~~> G): Self[G, R, O, S, E] =
    new Plan(name, actions trans t, invariants)

  def mapR[R2](f: R2 => R): Self[F, R2, O, S, E] =
    new Plan(name, actions mapR f, invariants)

  def pmapR[R2](f: R2 => E Or R): Self[F, R2, O, S, E] =
    new Plan(name, actions pmapR f, invariants)

  def pmapO[OO](g: OO => E Or O): Self[F, R, OO, S, E] =
    new Plan(name, actions pmapO g, invariants pmapO g)

  def mapS[SS](g: SS => S)(s: (SS, S) => SS): Self[F, R, O, SS, E] =
    new Plan(name, actions.mapS(g)(s), invariants.mapS(g))

  def mapE[EE](f: E => EE): Self[F, R, O, S, EE] =
    new Plan(name, actions mapE f, invariants mapE f)

  def lift[F2[_], R2, O2, S2, E2](implicit t: Transformer[F, R, O, S, E, F2, R2, O2, S2, E2]): Self[F2, R2, O2, S2, E2] =
    new Plan(name, t actions actions, t invariants invariants)(t.f2)

  def withInitialState(s: S) =
    PlanWithInitialState(this, s)

  def stateless(implicit ev: Unit =:= S) =
    withInitialState(())

  def test(observer: Observer[R, O, E])(implicit a: ErrorHandler[E]) =
    Test(this, observer, Retry.Policy.never)(a)

  def testU(implicit ev: Observer[R, Unit, E] =:= Observer[R, O, E], a: ErrorHandler[E]) =
    test(ev(Observer.unit))(a)
}

// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████

final case class PlanWithInitialState[F[_], R, O, S, E](override val plan: Plan[F, R, O, S, E], initialState: S)
    extends PlanLike[F, R, O, S, E, PlanWithInitialState[F, R, O, S, E]] {

  override type Self[FF[_], RR, OO, SS, EE] = PlanWithInitialState[FF, RR, OO, SS, EE]

  override protected def setPlan(p: Plan[F, R, O, S, E]) = copy(plan = p)

  def trans[G[_]: ExecutionModel](t: F ~~> G): Self[G, R, O, S, E] =
    plan.trans(t).withInitialState(initialState)

  def mapR[R2](f: R2 => R): Self[F, R2, O, S, E] =
    plan.mapR(f).withInitialState(initialState)

  def pmapR[R2](f: R2 => E Or R): Self[F, R2, O, S, E] =
    plan.pmapR(f).withInitialState(initialState)

  def pmapO[OO](g: OO => E Or O): Self[F, R, OO, S, E] =
    plan.pmapO(g).withInitialState(initialState)

//  def mapS[SS](g: SS => S)(s: (SS, S) => SS): Self[F, R, O, SS, E] =
//  def mapS[SS](f: S => SS)(g: SS => S): Self[F, R, O, SS, E] =
//    plan.mapS(g)((_, s) => f(s)).withInitialState(f(initialState))

  def mapE[EE](f: E => EE): Self[F, R, O, S, EE] =
    plan.mapE(f).withInitialState(initialState)

//  def lift[F2[_], R2, O2, S2, E2](implicit t: Transformer[F, R, O, S, E, F2, R2, O2, S2, E2]): Self[F2, R2, O2, S2, E2] =
//    plan.lift(t).withInitialState(initialState)

  def test(observer: Observer[R, O, E])(implicit a: ErrorHandler[E]) =
    TestWithInitialState(plan.test(observer)(a), initialState)

  def testU(implicit ev: Observer[R, Unit, E] =:= Observer[R, O, E], a: ErrorHandler[E]) =
    test(ev(Observer.unit))(a)
}

// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████

final case class Test[F[_], R, O, S, E](override val plan: Plan[F, R, O, S, E],
                                        observer: Observer[R, O, E],
                                        retryPolicy: Retry.Policy)
                                       (implicit val attempt: ErrorHandler[E])
    extends PlanLike[F, R, O, S, E, Test[F, R, O, S, E]] {

  override type Self[FF[_], RR, OO, SS, EE] = Test[FF, RR, OO, SS, EE]

  override def setPlan(p: Plan[F, R, O, S, E]): Self[F, R, O, S, E] =
    copy(plan = p)

  def withRetryPolicy(p: Retry.Policy): Test[F, R, O, S, E] =
    copy(retryPolicy = p)

  def trans[G[_]: ExecutionModel](t: F ~~> G): Self[G, R, O, S, E] =
    copy(plan = plan trans t)

  def mapR[R2](f: R2 => R): Self[F, R2, O, S, E] =
    Test(plan mapR f, observer cmapR f, retryPolicy)

  def pmapR[R2](f: R2 => E Or R): Self[F, R2, O, S, E] =
    Test(plan pmapR f, observer pmapR f, retryPolicy)

//  def pmapO[OO](g: OO => E Or O): Self[F, R, OO, S, E] =
//    Test(plan pmapO g, observer pmapO g)

  def mapS[SS](g: SS => S)(s: (SS, S) => SS): Self[F, R, O, SS, E] =
    copy(plan = plan.mapS(g)(s))

  def mapE[EE](f: E => EE): Self[F, R, O, S, EE] =
    Test(plan mapE f, observer mapE f, retryPolicy)(attempt map f)

//  def lift[F2[_], R2, O2, S2, E2](implicit t: Transformer[F, R, O, S, E, F2, R2, O2, S2, E2]): Self[F2, R2, O2, S2, E2] =
//    Test(plan.lift(t), observer)(recover)

  def withInitialState(s: S) =
    TestWithInitialState(this, s)

  def stateless(implicit ev: Unit =:= S) =
    withInitialState(())

  @deprecated("Use withInitialState(s).withRefByName(ref).run()", "2.2.0")
  def run(initialState: S, ref: => R): F[Report[E]] =
    withInitialState(initialState).withRefByName(ref).run()

  def runU(initialState: S)(implicit ev: Unit =:= R): F[Report[E]] =
    withInitialState(initialState).withoutRef.run()
}

// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████

final case class TestWithInitialState[F[_], R, O, S, E](test: Test[F, R, O, S, E], initialState: S)
  extends PlanLike[F, R, O, S, E, TestWithInitialState[F, R, O, S, E]] {

  override type Self[FF[_], RR, OO, SS, EE] = TestWithInitialState[FF, RR, OO, SS, EE]

  override def plan                            = test.plan
  override def setPlan(p: Plan[F, R, O, S, E]) = test.setPlan(p).withInitialState(initialState)

  def recover     = test.attempt
  def observer    = test.observer
  def retryPolicy = test.retryPolicy

  def withRetryPolicy(p: Retry.Policy): TestWithInitialState[F, R, O, S, E] =
    copy(test = test.withRetryPolicy(p))

  def trans[G[_]: ExecutionModel](t: F ~~> G): Self[G, R, O, S, E] =
    test.trans(t).withInitialState(initialState)

  def mapR[R2](f: R2 => R): Self[F, R2, O, S, E] =
    test.mapR(f).withInitialState(initialState)

  def pmapR[R2](f: R2 => E Or R): Self[F, R2, O, S, E] =
    test.pmapR(f).withInitialState(initialState)

//  def pmapO[OO](g: OO => E Or O): Self[F, R, OO, S, E] =
//    test.pmapO(g).withInitialState(initialState)

//  def mapS[SS](g: SS => S)(s: (SS, S) => SS): Self[F, R, O, SS, E] =
//    test.mapS(g)((_, s) => f(s)).withInitialState(f(initialState))

  def mapE[EE](f: E => EE): Self[F, R, O, S, EE] =
    test.mapE(f).withInitialState(initialState)

//  def lift[F2[_], R2, O2, S2, E2](implicit t: Transformer[F, R, O, S, E, F2, R2, O2, S2, E2]): Self[F2, R2, O2, S2, E2] =
//    test.lift(t).withInitialState(initialState)

  def planWithInitialState =
    plan.withInitialState(initialState)

  @deprecated("Use withRefByName(ref).run()", "2.2.0")
  def run(ref: => R): F[Report[E]] =
    withRefByName(ref).run()

  def runU()(implicit ev: Unit =:= R): F[Report[E]] =
    withoutRef.run()

  def withRef(ref: R): RunnableTest[F, R, O, S, E] =
    RunnableTest(test, initialState, () => () => ref)

  /** ref is evaluated once per test run, and reused after that */
  def withLazyRef(ref: => R): RunnableTest[F, R, O, S, E] =
    RunnableTest(test, initialState, () => {
      lazy val r: R = ref
      val f: () => R = () => r
      f
    })

  /** ref is evaluated each time it's used */
  def withRefByName(ref: => R): RunnableTest[F, R, O, S, E] =
    RunnableTest(test, initialState, () => () => ref)

  def withoutRef(implicit ev: Unit =:= R): RunnableTest[F, R, O, S, E] =
    RunnableTest(test, initialState, () => () => ())
}

// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████

final case class RunnableTest[F[_], R, O, S, E](test: Test[F, R, O, S, E], initialState: S, refFnFn: () => () => R) {
  def plan = test.plan
  def recover = test.attempt
  def observer = test.observer
  def retryPolicy = test.retryPolicy

  def withRetryPolicy(p: Retry.Policy): RunnableTest[F, R, O, S, E] =
    copy(test = test.withRetryPolicy(p))

  def trans[G[_]: ExecutionModel](t: F ~~> G): RunnableTest[G, R, O, S, E] =
    copy(test = test.trans(t))

  def run(): F[Report[E]] =
    Runner.run(test)(initialState, refFnFn())
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy