Skip to content

Commit

Permalink
Merge pull request #1283 from Grryum/zio2-withrun-instance-fix-with-test
Browse files Browse the repository at this point in the history
Fix bug with zioTofuBiInstance, add tests for zio2-core module, add logging mid test with bug related function for zio2 logging.
  • Loading branch information
dos65 authored May 16, 2024
2 parents 5b3290c + ef35537 commit 832f14f
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 40 deletions.
7 changes: 4 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ lazy val zio2Core = projectMatrix
.settings(
defaultSettings,
scala3MigratedModuleOptions,
libraryDependencies ++= List(zio2, zio2Cats),
libraryDependencies ++= List(zio2, zio2Cats, zio2Test, zio2TestSbt),
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"),
name := "tofu-zio2-core"
)
.jvmPlatform(scalaVersions = scala2And3Versions)
Expand All @@ -357,7 +358,7 @@ lazy val zio1Logging = projectMatrix
name := "tofu-zio-logging"
)
.jvmPlatform(scala2Versions)
.dependsOn(loggingStr, loggingDer % "test", zio1Core % Test)
.dependsOn(loggingStr, loggingDer % Test, zio1Core % Test)

lazy val zio2Logging = projectMatrix
.in(modules / "interop" / "zio2" / "logging")
Expand All @@ -369,7 +370,7 @@ lazy val zio2Logging = projectMatrix
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
)
.jvmPlatform(scalaVersions = scala2And3Versions)
.dependsOn(loggingStr, loggingDer % "test")
.dependsOn(loggingStr, loggingDer % Test, zio2Core % Test)

val interop = modules / "interop"

Expand Down
12 changes: 5 additions & 7 deletions modules/interop/zio2/core/src/main/scala/tofu/zioFunctions.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package tofu

import izumi.reflect.Tag
import tofu.higherKind.bi.{EmbedBK, FunctorBK}
import tofu.syntax.functorbk._
import tofu.zioInstances.implicits._
import zio.{IO, ZIO, Tag => ZTag}

import scala.annotation.nowarn
import zio.{IO, Tag, ZIO}

object zioFunctions {
@nowarn
def expose[U[_[_, _]]: EmbedBK: FunctorBK: Tag.auto.T]: U[ZIO[U[IO], +_, +_]] =
EmbedBK.of[ZIO[U[IO], +_, +_], U](ZIO.environmentWith(_.get[U[IO]](ZTag[U[IO]]).widenb))

def expose[U[bf[_, _]]: EmbedBK: FunctorBK](implicit ev: Tag[U[IO]]): U[ZIO[U[IO], +_, +_]] =
EmbedBK.of[ZIO[U[IO], +_, +_], U](ZIO.environmentWith(_.get[U[IO]].widenb))

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ private[zioInstances] class ZioInstances {
private[this] val zioTofuInstanceAny: ZioTofuInstance[Any, Any] = new ZioTofuInstance
final def zioTofuInstance[R, E]: ZioTofuInstance[R, E] = zioTofuInstanceAny.asInstanceOf[ZioTofuInstance[R, E]]

private[this] val zioTofuWithRunInstanceAny = new ZioTofuWithRunInstance[Any, Any]
final def zioTofuWithRunInstance[R, E]: ZioTofuWithRunInstance[R, E] =
zioTofuWithRunInstanceAny.asInstanceOf[ZioTofuWithRunInstance[R, E]]
final def zioTofuWithRunInstance[R: Tag, E]: ZioTofuWithRunInstance[R, E] = new ZioTofuWithRunInstance

final def zioTofuUnliftManyInstance[R, E, R1: Tag]: ZioTofuUnliftManyInstance[R, E, R1] =
new ZioTofuUnliftManyInstance
Expand All @@ -63,6 +61,5 @@ private[zioInstances] class ZioInstances {
final def rioTofuBlockingInstance[R]: RioTofuBlockingInstance[R] =
rioTofuBlockingInstanceAny.asInstanceOf[RioTofuBlockingInstance[R]]

private[this] val zioTofuBiInstanceAny = new ZioTofuBiInstance[Any]
final def zioTofuBiInstance[R]: ZioTofuBiInstance[R] = zioTofuBiInstanceAny.asInstanceOf[ZioTofuBiInstance[R]]
final def zioTofuBiInstance[R: Tag]: ZioTofuBiInstance[R] = new ZioTofuBiInstance
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ private[zioInstances] class ZioTofuImplicits1 extends ZioTofuImplicits2 {
@inline final implicit def rioTofuBlockingImplicit[R]: RioTofuBlockingInstance[R] =
rioTofuBlockingInstance[R]

@inline final implicit def zioTofuBiImplicit[R]: ZioTofuBiInstance[R] = zioTofuBiInstance[R]
@inline final implicit def zioTofuBiImplicit[R: Tag]: ZioTofuBiInstance[R] = zioTofuBiInstance[R]
}
private[zioInstances] trait ZioTofuImplicits2 extends ZioTofuImplicits3 {
@inline final implicit def zioTofuErrorsToImplicit[R, E]: ZioTofuErrorsToInstance[R, E, Nothing] =
zioTofuErrorsToInstance
@inline final implicit def zioTofuImplicit[R, E]: ZioTofuInstance[R, E] = zioTofuInstance
@inline final implicit def zioTofuWithRunImplicit[R, E]: ZioTofuWithRunInstance[R, E] = zioTofuWithRunInstance
@inline final implicit def zioTofuWithRunImplicit[R: Tag, E]: ZioTofuWithRunInstance[R, E] = zioTofuWithRunInstance
@inline final implicit def zioTofuBlockingImplicit[R, E]: ZioTofuBlockingInstance[R, E] =
zioTofuBlockingInstance[R, E]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package tofu.zioInstances

import tofu.WithRun
import tofu.zioInstances.implicits._
import zio._
import zio.test._
import tofu.bi.BiRun

object RunZioSpec extends ZIOSpecDefault {
case class Context(x: Int)
private val app: RIO[Context, Int] = ZIO.serviceWith[Context](_.x)
private val someResult = 111

override def spec =
suite("RunZioSpec")(
test("should summon and run WithRun instance without errors") {
for {
result <- WithRun[RIO[Context, _], Task, Context].runContext(app)(Context(someResult))
} yield assertTrue(result == someResult)
},
test("should summon and run BiRun instance without errors") {
for {
result <- BiRun[ZIO[Context, +_, +_], IO[+_, +_], Context, Context].runLeft(app)(Context(someResult))
} yield assertTrue(result == someResult)
}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package tofu.logging
package zlogs

import derevo.derive
import tofu.logging.derivation.{loggable, loggingBiMid}
import zio._
import zio.URLayer
import tofu.logging.bi.LoggingBiCompanion
import tofu.higherKind.bi.BiTemplate
import tofu.zioInstances.implicits._
import tofu.zioFunctions
import zio.test.ZIOSpecDefault
import zio.test._
import tofu.higherKind.bi.RepresentableB
import tofu.higherKind
import tofu.higherKind.bi.FunBK

object ZLogBiMidSpec extends ZIOSpecDefault {
import tofu.logging.zlogs.Lurker.Point
val cls = s"<${classOf[Lurker[Any]].getName()}>"

def go[E, A](z: ZIO[Lurker.Dep, E, A]): ZIO[Any, Nothing, (Either[E, A], Vector[String])] =
(for {
ref <- Ref.make(Vector[String]())
// TODO: remake to true ZIO style
e <- z.provide(ZTestLog.layer, Lurker.attachLog, ZLayer.succeed(ref)).either
logs <- ref.get
} yield (e, logs))

override def spec =
suite("ZLogBiMidSpec")(
test("simple walk") {
for {
(res, logs) <- go(Lurker.DO.move(Point(20, 3)))
} yield assertTrue(
logs == Vector(
s"[Debug] $cls entering move (target = Point{x=20,y=3})",
s"[Debug] $cls leaving move (target = Point{x=20,y=3}) result is Point{x=5,y=3}",
),
res == Right(Point(5, 3))
)
},
test("attack") {
for {
(res, logs) <- go(Lurker.DO.attack(Point(20, 3)))
} yield assertTrue(
res == Left(Lurker.Normal),
logs == Vector(
s"[Debug] $cls entering attack (target = Point{x=20,y=3})",
s"[Error] $cls error during attack (target = Point{x=20,y=3}) error is cant do this operation in Normal state",
)
)
},
test("burrow and attack") {
for {
(res, logs) <- go(Lurker.DO.burrowChange *> Lurker.DO.attack(Point(2, 3)))
} yield assertTrue(
res == Right(true),
logs == Vector(
s"[Debug] $cls entering burrowChange ()",
s"[Debug] $cls leaving burrowChange () result is ()",
s"[Debug] $cls entering attack (target = Point{x=2,y=3})",
s"[Debug] $cls leaving attack (target = Point{x=2,y=3}) result is true",
)
)
}
)
}

@derive(loggingBiMid)
trait Lurker[F[_, _]] extends BiTemplate[F] {
import Lurker._
def move(target: Point): F[State, Point]
def burrowChange: FS[Unit]
def attack(target: Point): F[State, Boolean]
}

object Lurker extends LoggingBiCompanion[Lurker] {
// TODO: use `@derive(representableB)` from derivation module when it is ready
implicit def reprBInstance: RepresentableB[Lurker] = new RepresentableB[Lurker] {
override def bitabulate[F[_, _]](repr: FunBK[higherKind.bi.RepBK[Lurker, _, _], F]): Lurker[F] =
new Lurker[F] {
def move(target: Point): F[State, Point] =
repr(higherKind.bi.RepBK[Lurker](_.move(target)))
def burrowChange: FS[Unit] =
repr(higherKind.bi.RepBK[Lurker](_.burrowChange))
def attack(target: Point): F[State, Boolean] =
repr(higherKind.bi.RepBK[Lurker](_.attack(target)))
}
}

type Dep = Lurker[IO]

sealed trait State
case object Normal extends State
case object Buried extends State
implicit val wrongStateLoggable: Loggable[State] =
Loggable[String].contramap(st => s"cant do this operation in $st state")

lazy val DO: Lurker[ZIO[Dep, +_, +_]] = zioFunctions.expose[Lurker]

@derive(loggable)
final case class Point(x: Long, y: Long)

class Test(ref: Ref.Synchronized[(State, Point)], maxDistance: Long = 5) extends Lurker[IO] {

private def moveOne(cur: Long, target: Long) = {
val shift = target - cur
val dist = shift.abs.min(maxDistance)
if (shift >= 0) cur + dist else cur - dist
}
def move(target: Point): IO[State, Point] =
ref.modifyZIO {
case (Normal, pt) =>
val res = Point(moveOne(pt.x, target.x), moveOne(pt.y, target.y))
ZIO.succeed((res, (Normal, res)))
case (Buried, _) => ZIO.fail(Buried)
}

def burrowChange: IO[Nothing, Unit] = ref.update {
case (Normal, pt) => (Buried, pt)
case (Buried, pt) => (Normal, pt)
}

def attack(target: Point): IO[State, Boolean] = ref.get.flatMap {
case (Normal, _) =>
ZIO.fail(Normal)
case (Buried, pt) =>
ZIO.succeed((pt.x - target.x).abs < maxDistance && (pt.y - target.x).abs < maxDistance)
}
}

val make: UIO[Lurker[IO]] = Ref.Synchronized.make((Normal: State, Point(0, 0))).map(new Test(_))

val attachLog: URLayer[ZLogging.Make, Dep] =
ZLayer.fromZIO(ZIO.serviceWithZIO[ZLogging.Make](implicit logs => make.map(_.attachLogs)))
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ package tofu.logging.zlogs

import tofu.logging.Loggable
import tofu.logging.derivation.loggable
import tofu.logging.derivation._

object DerivedData {

final case class User(name: String)

object User {
implicit val userLoggable: Loggable[User] = loggable.instance
}
}
object DerivedData:
final case class User(name: String) derives Loggable
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package tofu.logging
package zlogs

import zio._
import org.slf4j.helpers.MessageFormatter

class ZTestLog(svc: String, val ref: Ref[Vector[String]]) extends Logging[UIO] {
override def write(level: Logging.Level, message: String, values: LoggedValue*): UIO[Unit] = {
val formatted = MessageFormatter.arrayFormat(message, values.toArray).getMessage()
ref.update(_ :+ s"[$level] <$svc> $formatted")
}
}

object ZTestLog {
def make(): URIO[Ref[Vector[String]], Logging.Make[UIO]] =
ZIO.serviceWith[Ref[Vector[String]]](ref => (svc: String) => new ZTestLog(svc, ref))

val layer: URLayer[Ref[Vector[String]], ZLogging.Make] =
ZLayer.fromZIO(make())
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package tofu.logging
package tofu.logging.derivation

import magnolia1.TypeInfo
import scala.collection.compat._
import scala.deriving.Mirror
import tofu.logging.Loggable

package object derivation {
extension (x: Loggable.type) inline def derived[A](using Mirror.Of[A]): Loggable[A] = loggable.derived[A]

extension (x: Loggable.type) inline def derived[A](using Mirror.Of[A]): Loggable[A] = loggable.derived[A]
private[derivation] def strJoin(typeName: String, strings: IterableOnce[String]): String =
if (strings.iterator.isEmpty) typeName else strings.iterator.mkString(s"$typeName{", ",", "}")

private[derivation] def strJoin(typeName: String, strings: IterableOnce[String]): String =
if (strings.iterator.isEmpty) typeName else strings.iterator.mkString(s"$typeName{", ",", "}")
private[derivation] def calcTypeName(typeName: TypeInfo, seen: Set[TypeInfo] = Set()): String =
if (seen(typeName)) "#"
else {
val args = typeName.typeParams
val name = typeName.full

private[derivation] def calcTypeName(typeName: TypeInfo, seen: Set[TypeInfo] = Set()): String =
if (seen(typeName)) "#"
else {
val args = typeName.typeParams
val name = typeName.full

if (args.isEmpty) name
else args.iterator.map(calcTypeName(_, seen + typeName)).mkString(name + "[", ",", "]")
}
}
if (args.isEmpty) name
else args.iterator.map(calcTypeName(_, seen + typeName)).mkString(name + "[", ",", "]")
}

0 comments on commit 832f14f

Please sign in to comment.