Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
initial PoC without objects
Browse files Browse the repository at this point in the history
  • Loading branch information
senia-psm committed Mar 17, 2022
1 parent ab75569 commit 9a02f85
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 16 deletions.
82 changes: 82 additions & 0 deletions mock-tests/shared/src/test/scala/zio/mock/Scala3PoCSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package zio.mock

import zio._
import zio.mock.Expectation._
import zio.test._

object Scala3PoCSpec extends ZIOBaseSpec {
trait Srv1 {
val action: UIO[Int]
}

object Srv1Mock1 extends Mock[Srv1] {
object Action extends Mock.Effect3[Srv1, Unit, Nothing, Int](Srv1Mock1, "action")

val compose: URLayer[Proxy, Srv1] = {
for {
proxy <- ZIO.service[Proxy]
_ <- withRuntime[Any]
} yield new Srv1 {
override val action: UIO[Int] = proxy(Action)
}
}.toLayer
}

object Srv1Mock2 extends Mock[Srv1] {
object Action extends Mock.Effect3[Srv1, Unit, Nothing, Int](Srv1Mock2, "action")

val compose: URLayer[Proxy, Srv1] = {
for {
proxy <- ZIO.service[Proxy]
_ <- withRuntime[Any]
} yield new Srv1 {
override val action: UIO[Int] = proxy(Action)
}
}.toLayer
}

class GenericMock[R: Tag: EnvironmentTag](makeCompose: (Proxy, Runtime[Any], () => Mock[R]) => R) extends Mock[R] {
override protected[mock] val compose: URLayer[Proxy, R] = {
for {
proxy <- ZIO.service[Proxy]
rts <- Mock.withRuntime[Any]
} yield makeCompose(proxy, rts, () => this)
}.toLayer
}

def mockSrv[Srv: Tag: EnvironmentTag, E: EnvironmentTag, A: EnvironmentTag](
call: Srv => ZIO[_, E, A]
)(methodName: String, compose: (Proxy, Runtime[Any], () => Mock[Srv]) => Srv): Capability[Srv, Unit, E, A] =
new Mock.Effect3(new GenericMock[Srv](compose), methodName)

def spec = suite("Scala3PoCSpec")(
test("mock duplication") {
check(Gen.int, Gen.int) { (i1, i2) =>
val c1 = mockSrv[Srv1, Nothing, Int](_.action)(
"action",
(proxy, _, mock) =>
new Srv1 {
override val action: UIO[Int] = proxy(new Mock.Effect3[Srv1, Unit, Nothing, Int](mock(), "action"))
}
).apply(value(i1))

val c2 = mockSrv[Srv1, Nothing, Int](_.action)(
"action",
(proxy, _, mock) =>
new Srv1 {
override val action: UIO[Int] = proxy(new Mock.Effect3[Srv1, Unit, Nothing, Int](mock(), "action"))
}
).apply(value(i2))

val mockEnv = c1 && c2

val action = for {
srv <- ZIO.service[Srv1]
pair <- srv.action.zipPar(srv.action)
} yield assertTrue(pair == (i1, i2) || pair == (i2, i1))

action.provideLayer(mockEnv)
}
}
)
}
26 changes: 19 additions & 7 deletions mock/shared/src/main/scala/zio/mock/Capability.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ import java.util.UUID
* `Method`, `Sink` or `Stream` type members.
*/
protected[mock] abstract class Capability[R: EnvironmentTag, I: EnvironmentTag, E: EnvironmentTag, A: EnvironmentTag](
val mock: Mock[R]
) extends Capability.Base[R] { self =>
_mock: => Mock[R],
method: Option[String] = None
) extends Capability.Base[R](method, taggedTagType(implicitly[EnvironmentTag[I]])) { self =>

// TODO: separate Capability and Capability.ID. Use Capability.ID in Proxy
override def mock: Mock[R] = _mock

val inputTag: LightTypeTag = taggedTagType(implicitly[EnvironmentTag[I]])
val errorTag: LightTypeTag = taggedTagType(implicitly[EnvironmentTag[E]])
Expand Down Expand Up @@ -64,10 +68,17 @@ protected[mock] abstract class Capability[R: EnvironmentTag, I: EnvironmentTag,

object Capability {

protected abstract class Base[R] {
sealed trait Id

case class ObjectId(uuid: UUID) extends Id
case class MethodDescriptionId(servType: LightTypeTag, methodName: String, input: LightTypeTag) extends Id

val id: UUID = UUID.randomUUID
val mock: Mock[R]
protected abstract class Base[R: EnvironmentTag](method: Option[String], inputTag: LightTypeTag) {

val id: Id = method.fold[Id](ObjectId(UUID.randomUUID))(m =>
MethodDescriptionId(servType = taggedTagType(implicitly[EnvironmentTag[R]]), methodName = m, input = inputTag)
)
def mock: Mock[R]

/** Render method fully qualified name.
*/
Expand All @@ -84,7 +95,8 @@ object Capability {

sealed abstract class Unknown

protected[mock] abstract class Poly[R: EnvironmentTag, I, E, A] extends Base[R]
protected[mock] abstract class Poly[R: EnvironmentTag, I: EnvironmentTag, E, A]
extends Base[R](None, taggedTagType(implicitly[EnvironmentTag[I]]))

object Poly {

Expand Down Expand Up @@ -280,7 +292,7 @@ object Capability {
private def toMethod[R: EnvironmentTag, I: EnvironmentTag, E: EnvironmentTag, A: EnvironmentTag](
poly: Poly[R, _, _, _]
): Capability[R, I, E, A] = new Capability[R, I, E, A](poly.mock) {
override val id: UUID = poly.id
override val id: Id = poly.id
override val toString: String = poly.toString
}
}
Expand Down
25 changes: 16 additions & 9 deletions mock/shared/src/main/scala/zio/mock/Mock.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,7 @@ abstract class Mock[R: EnvironmentTag] { self =>

/** Replaces Runtime on JS platform to one with unyielding executor.
*/
protected def withRuntime[R](implicit trace: ZTraceElement): URIO[R, Runtime[R]] =
ZIO.runtime[R].map { runtime =>
if (!TestPlatform.isJS) runtime
else
runtime.withExecutor {
val ec = runtime.runtimeConfig.executor.asExecutionContext
Executor.fromExecutionContext(Int.MaxValue)(ec)
}
}
protected def withRuntime[R](implicit trace: ZTraceElement): URIO[R, Runtime[R]] = Mock.withRuntime[R]

abstract class Effect[I: EnvironmentTag, E: EnvironmentTag, A: EnvironmentTag] extends Capability[R, I, E, A](self)
abstract class Method[I: EnvironmentTag, E <: Throwable: EnvironmentTag, A: EnvironmentTag]
Expand Down Expand Up @@ -78,4 +70,19 @@ abstract class Mock[R: EnvironmentTag] { self =>
object Mock {

private[mock] case class Composed[R: EnvironmentTag](compose: URLayer[Proxy, R]) extends Mock[R]

class Effect3[R: EnvironmentTag, I: EnvironmentTag, E: EnvironmentTag, A: EnvironmentTag](
mock: => Mock[R],
method: String
) extends Capability[R, I, E, A](mock, Some(method))

def withRuntime[R](implicit trace: ZTraceElement): URIO[R, Runtime[R]] =
ZIO.runtime[R].map { runtime =>
if (!TestPlatform.isJS) runtime
else
runtime.withExecutor {
val ec = runtime.runtimeConfig.executor.asExecutionContext
Executor.fromExecutionContext(Int.MaxValue)(ec)
}
}
}

0 comments on commit 9a02f85

Please sign in to comment.