From ac1b23148ad8143a40bd3b8555c148fd718238c4 Mon Sep 17 00:00:00 2001 From: Jonas Spenger Date: Sat, 4 Apr 2026 11:23:21 +0200 Subject: [PATCH] Change JSON serialization format - Update tests to work with new serialization format - Bump uPickle version: "3.3.0" -> "4.4.3" - Replace the `Packed` spore representation with AST spore representation: now contains the value to be serialized whereas previously it was the serialized value - Remove Spore[ReadWriter[EmptyTuple]] - Add tests for serialization format --- .github/workflows/build-test.yml | 10 +- build.mill | 4 +- spores3/src-jvm/spores/jvm/Spore.scala | 1 - spores3/src/spores/Env.scala | 7 +- spores3/src/spores/ReadWriters.scala | 90 +- spores3/src/spores/Spore.scala | 47 +- spores3/src/spores/SporeBuilder.scala | 6 +- spores3/src/spores/SporeClassBuilder.scala | 6 +- spores3/src/spores/SporeLambdaBuilder.scala | 6 +- .../src-jvm/spores/jvm/AutoCaptureTests.scala | 8 +- .../src-jvm/spores/jvm/SporeLambdaTests.scala | 10 +- .../jvm/SporeSerializationJvmTests.scala | 141 +++ .../src/spores/SporeClassBuilderTests.scala | 6 +- .../src/spores/SporeObjectBuilderTests.scala | 6 +- .../src/spores/SporeSerializationTests.scala | 894 ++++++++++++++++++ .../src/spores/upickle/test/PickleTests.scala | 8 +- 16 files changed, 1155 insertions(+), 95 deletions(-) create mode 100644 spores3/test/src-jvm/spores/jvm/SporeSerializationJvmTests.scala create mode 100644 spores3/test/src/spores/SporeSerializationTests.scala diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index c667f89..6520977 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -49,9 +49,15 @@ jobs: curl.exe -L https://repo1.maven.org/maven2/com/lihaoyi/mill-dist/1.1.5/mill-dist-1.1.5-mill.bat -o mill.bat ./mill version - name: Build - run: ./mill __.compile + run: | + ./mill __.jvm.compile + ./mill __.js.compile + ./mill __.native.compile - name: Test - run: ./mill __.test + run: | + ./mill __.jvm.test + ./mill __.js.test + ./mill __.native.test - name: Sample run: > ./mill --no-daemon diff --git a/build.mill b/build.mill index 086e0ad..1546fe8 100644 --- a/build.mill +++ b/build.mill @@ -13,8 +13,8 @@ import com.github.lolgab.mill.mima._ val scala3 = "3.3.7" val scalaJS = "1.20.2" -val scalaNative = "0.5.10" -val upickle = "3.3.0" +val scalaNative = "0.5.9" +val upickle = "4.4.3" val utest = "0.9.5" trait CommonTestModule extends TestModule.Utest { diff --git a/spores3/src-jvm/spores/jvm/Spore.scala b/spores3/src-jvm/spores/jvm/Spore.scala index a90f8a8..1d31f87 100644 --- a/spores3/src-jvm/spores/jvm/Spore.scala +++ b/spores3/src-jvm/spores/jvm/Spore.scala @@ -3,7 +3,6 @@ package spores.jvm import upickle.default.* import spores.* -import spores.Packed.* /** Internal API. Used by the [[spores.SporeObjectCompanionJVM]] factories. diff --git a/spores3/src/spores/Env.scala b/spores3/src/spores/Env.scala index a93683f..9770a3f 100644 --- a/spores3/src/spores/Env.scala +++ b/spores3/src/spores/Env.scala @@ -3,7 +3,6 @@ package spores import upickle.default.* import spores.* -import spores.Packed.* /** A factory for packing environment values of type `T` into `Spore[T]` by @@ -15,14 +14,14 @@ object Env { * * @param env * The value to pack. - * @param rw + * @param ev * The implicit `Spore[ReadWriter[T]]` used for packing the `env`. * @tparam T * The type of the value to pack. * @return * A new `Spore[T]` with the packed `env`. */ - def apply[T](env: T)(using rw: Spore[ReadWriter[T]]): Spore[T] = { - PackedEnv(write(env)(using rw.unwrap()), rw) + def apply[T](env: T)(using ev: Spore[ReadWriter[T]]): Spore[T] = { + AST.Val(ev, env) } } diff --git a/spores3/src/spores/ReadWriters.scala b/spores3/src/spores/ReadWriters.scala index 54c040e..ec9bb58 100644 --- a/spores3/src/spores/ReadWriters.scala +++ b/spores3/src/spores/ReadWriters.scala @@ -3,7 +3,6 @@ package spores import upickle.default.* import spores.* -import spores.Packed.* /** A collection of ReadWriters. Contains both `ReadWriter[Spore[T]]` and @@ -23,13 +22,65 @@ object ReadWriters { // ReadWriter[Spore[T]] ////////////////////////////////////////////////////////////////////////////// - given [T]: ReadWriter[Spore[T]] = macroRW - given [T]: ReadWriter[PackedObject[T]] = macroRW - given [T]: ReadWriter[PackedClass[T]] = macroRW - given [T]: ReadWriter[PackedLambda[T]] = macroRW - given [E]: ReadWriter[PackedEnv[E]] = macroRW - given [E, T]: ReadWriter[PackedWithEnv[E, T]] = macroRW - given [E, T]: ReadWriter[PackedWithCtx[E, T]] = macroRW + given [T]: ReadWriter[Spore[T]] = + summon[ReadWriter[ujson.Value]].bimap[Spore[T]]( + sp => { + sp match { + case AST.Body(kind, className, _) => + ujson.Obj( + "tag" -> ujson.Str("Body"), + "kind" -> ujson.Num(kind), + "className" -> ujson.Str(className), + ) + case AST.Val(ev, value) => + ujson.Obj( + "tag" -> ujson.Str("Val"), + "ev" -> writeJs(ev), + "value" -> writeJs(value)(using ev.unwrap()), + ) + case AST.WithEnv(fun, env) => + ujson.Obj( + "tag" -> ujson.Str("WithEnv"), + "fun" -> writeJs(fun), + "env" -> writeJs(env), + ) + case AST.WithCtx(fun, env) => + ujson.Obj( + "tag" -> ujson.Str("WithCtx"), + "fun" -> writeJs(fun), + "env" -> writeJs(env), + ) + } + }, + js => { + js("tag").str match { + case "Body" => + val kind = js("kind").num.toInt + val className = js("className").str + val body = kind match { + case 0 => + Reflection.loadModuleFieldValue[SporeBuilder[T]](className).body + case 1 => + Reflection.loadClassInstance[SporeClassBuilder[T]](className).body + case 2 => + Reflection.loadClassInstance[SporeLambdaBuilder[T]](className).body + } + AST.Body(kind, className, body) + case "Val" => + val ev = read[Spore[ReadWriter[T]]](js("ev")) + val value = read[T](js("value"))(using ev.unwrap()) + AST.Val(ev, value) + case "WithEnv" => + val fun = read[Spore[Any => T]](js("fun")) + val env = read[Spore[Any]](js("env")) + AST.WithEnv(fun, env) + case "WithCtx" => + val fun = read[Spore[Any ?=> T]](js("fun")) + val env = read[Spore[Any]](js("env")) + AST.WithCtx(fun, env) + } + }, + ) ////////////////////////////////////////////////////////////////////////////// // Spore[ReadWriter[T]] for primitive T @@ -69,27 +120,9 @@ object ReadWriters { // Spore[ReadWriter[Spore[?]]] ////////////////////////////////////////////////////////////////////////////// - private[spores] object SporeRW extends SporeBuilder[ReadWriter[Spore[?]]](macroRW) + private[spores] object SporeRW extends SporeBuilder[ReadWriter[Spore[?]]](summon[ReadWriter[Spore[?]]]) given packedSporeRW[T]: Spore[ReadWriter[Spore[T]]] = SporeRW.build().asInstanceOf[Spore[ReadWriter[Spore[T]]]] - private[spores] object PackedObjectRW extends SporeBuilder[ReadWriter[PackedObject[?]]](macroRW) - given packedObjectRW[T]: Spore[ReadWriter[PackedObject[T]]] = PackedObjectRW.build().asInstanceOf[Spore[ReadWriter[PackedObject[T]]]] - - private[spores] object PackedClassRW extends SporeBuilder[ReadWriter[PackedClass[?]]](macroRW) - given packedClassRW[T]: Spore[ReadWriter[PackedClass[T]]] = PackedClassRW.build().asInstanceOf[Spore[ReadWriter[PackedClass[T]]]] - - private[spores] object PackedLambdaRW extends SporeBuilder[ReadWriter[PackedLambda[?]]](macroRW) - given packedLambdaRW[T]: Spore[ReadWriter[PackedLambda[T]]] = PackedLambdaRW.build().asInstanceOf[Spore[ReadWriter[PackedLambda[T]]]] - - private[spores] object PackedEnvRW extends SporeBuilder[ReadWriter[PackedEnv[?]]](macroRW) - given packedEnvRW[E]: Spore[ReadWriter[PackedEnv[E]]] = PackedEnvRW.build().asInstanceOf[Spore[ReadWriter[PackedEnv[E]]]] - - private[spores] object PackedWithEnvRW extends SporeBuilder[ReadWriter[PackedWithEnv[?, ?]]](macroRW) - given packedWithEnvRW[E, T]: Spore[ReadWriter[PackedWithEnv[E, T]]] = PackedWithEnvRW.build().asInstanceOf[Spore[ReadWriter[PackedWithEnv[E, T]]]] - - private[spores] object PackedWithCtxRW extends SporeBuilder[ReadWriter[PackedWithCtx[?, ?]]](macroRW) - given packedWithCtxRW[E, T]: Spore[ReadWriter[PackedWithCtx[E, T]]] = PackedWithCtxRW.build().asInstanceOf[Spore[ReadWriter[PackedWithCtx[E, T]]]] - ////////////////////////////////////////////////////////////////////////////// // Spore[ReadWriter[F[T]]] for Option[T], List[T], etc. ////////////////////////////////////////////////////////////////////////////// @@ -106,9 +139,6 @@ object ReadWriters { private[spores] class ListRW[T] extends SporeClassBuilder[ReadWriter[T] ?=> ReadWriter[List[T]]]({ summon }) given listRW[T](using tRW: Spore[ReadWriter[T]]): Spore[ReadWriter[List[T]]] = new ListRW[T].build().withCtx2(tRW) - private[spores] object Tuple0RW extends SporeBuilder[ReadWriter[EmptyTuple]](macroRW[EmptyTuple]) - given tuple0RW: Spore[ReadWriter[EmptyTuple]] = Tuple0RW.build() - private[spores] class Tuple1RW[T1] extends SporeClassBuilder[ReadWriter[T1] ?=> ReadWriter[Tuple1[T1]]]({ summon }) given tuple1RW[T1](using t1RW: Spore[ReadWriter[T1]]): Spore[ReadWriter[Tuple1[T1]]] = new Tuple1RW[T1].build().withCtx2(t1RW) diff --git a/spores3/src/spores/Spore.scala b/spores3/src/spores/Spore.scala index fdbd3fc..7664484 100644 --- a/spores3/src/spores/Spore.scala +++ b/spores3/src/spores/Spore.scala @@ -49,7 +49,6 @@ import upickle.default.* * The type of the packed closure. */ sealed trait Spore[+T] { - import spores.Packed.* /** Applies the packed closure to a value of type `T1`. Only available if the * wrapped closure of type `T` is a subtype of `T1 => R`. @@ -59,7 +58,7 @@ sealed trait Spore[+T] { * * @param env * The value applied to the packed closure. - * @param prw + * @param ev * The implicit `Spore[ReadWriter[T1]]` used for packing the `env`. * @tparam T1 * The type of the value applied to the packed closure. @@ -68,8 +67,8 @@ sealed trait Spore[+T] { * @return * A new `Spore[R]` with the result of the application. */ - def withEnv[T1, R](env: T1)(using prw: Spore[ReadWriter[T1]])(using @implicitNotFound(CanWithEnv.MSG) ev: CanWithEnv[T, T1, R]): Spore[R] = { - PackedWithEnv(this, PackedEnv(write(env)(using prw.unwrap()), prw)) + def withEnv[T1, R](env: T1)(using ev: Spore[ReadWriter[T1]])(using @implicitNotFound(CanWithEnv.MSG) ev2: CanWithEnv[T, T1, R]): Spore[R] = { + AST.WithEnv(this, AST.Val(ev, env)) } /** Optimization for applying this `Spore[T1 => R]` directly to a `Spore[T1]`. @@ -79,8 +78,8 @@ sealed trait Spore[+T] { * Only available if the wrapped closure of type `T` is a subtype of `T1 => * R`. */ - def withEnv2[T1, R](env: Spore[T1])(using @implicitNotFound(CanWithEnv.MSG) ev: CanWithEnv[T, T1, R]): Spore[R] = { - PackedWithEnv(this, env) + def withEnv2[T1, R](env: Spore[T1])(using @implicitNotFound(CanWithEnv.MSG) ev2: CanWithEnv[T, T1, R]): Spore[R] = { + AST.WithEnv(this, env) } /** Applies the packed closure to a context value of type `T1`. Only available @@ -91,7 +90,7 @@ sealed trait Spore[+T] { * * @param env * The context value applied to the packed closure. - * @param prw + * @param ev * The implicit `Spore[ReadWriter[T1]]` used for packing the `env`. * @tparam T1 * The type of the context value applied to the packed closure. @@ -100,8 +99,8 @@ sealed trait Spore[+T] { * @return * A new `Spore[R]` with the result of the application. */ - def withCtx[T1, R](env: T1)(using prw: Spore[ReadWriter[T1]])(using @implicitNotFound(CanWithCtx.MSG) ev: CanWithCtx[T, T1, R]): Spore[R] = { - PackedWithCtx(this, PackedEnv(write(env)(using prw.unwrap()), prw)) + def withCtx[T1, R](env: T1)(using ev: Spore[ReadWriter[T1]])(using @implicitNotFound(CanWithCtx.MSG) ev2: CanWithCtx[T, T1, R]): Spore[R] = { + AST.WithCtx(this, AST.Val(ev, env)) } /** Optimization for applying this `Spore[T1 ?=> R]` directly to a @@ -112,8 +111,8 @@ sealed trait Spore[+T] { * Only available if the wrapped closure of type `T` is a subtype of `T1 ?=> * R`. */ - def withCtx2[T1, R](env: Spore[T1])(using @implicitNotFound(CanWithCtx.MSG) ev: CanWithCtx[T, T1, R]): Spore[R] = { - PackedWithCtx(this, env) + def withCtx2[T1, R](env: Spore[T1])(using @implicitNotFound(CanWithCtx.MSG) ev2: CanWithCtx[T, T1, R]): Spore[R] = { + AST.WithCtx(this, env) } def map[U](fun: Spore[T => U]): Spore[U] = { @@ -131,12 +130,10 @@ sealed trait Spore[+T] { */ def unwrap(): T = { this match - case PackedObject(className) => Reflection.loadModuleFieldValue[SporeBuilder[T]](className).fun - case PackedClass(className) => Reflection.loadClassInstance[SporeClassBuilder[T]](className).fun - case PackedLambda(className) => Reflection.loadClassInstance[SporeLambdaBuilder[T]](className).fun - case PackedEnv(env, rw) => read(env)(using rw.unwrap()) - case PackedWithEnv(packed, packedEnv) => packed.unwrap()(packedEnv.unwrap()) - case PackedWithCtx(packed, packedEnv) => packed.unwrap()(using packedEnv.unwrap()) + case AST.Body(_, _, body) => body + case AST.Val(_, value) => value + case AST.WithEnv(fun, env) => fun.unwrap()(env.unwrap()) + case AST.WithCtx(fun, env) => fun.unwrap()(using env.unwrap()) } } @@ -151,17 +148,11 @@ sealed trait Spore[+T] { object Spore extends SporeObjectCompanionJVM -private object Packed { - - // Static: - final case class PackedObject[+T](className: String) extends Spore[T] - final case class PackedClass[+T] (className: String) extends Spore[T] - final case class PackedLambda[+T](className: String) extends Spore[T] - // Dynamic: - final case class PackedEnv[E] (env: String, rw: Spore[ReadWriter[E]]) extends Spore[E] - final case class PackedWithEnv[E, +T](packed: Spore[E => T], packedEnv: Spore[E]) extends Spore[T] - final case class PackedWithCtx[E, +T](packed: Spore[E ?=> T], packedEnv: Spore[E]) extends Spore[T] - +private object AST { + case class Body[+T](kind: Int, className: String, body: T) extends Spore[T] + case class Val[T](ev: Spore[ReadWriter[T]], value: T) extends Spore[T] + case class WithEnv[E, +R](fun: Spore[E => R], env: Spore[E]) extends Spore[R] + case class WithCtx[E, +R](fun: Spore[E ?=> R], env: Spore[E]) extends Spore[R] } diff --git a/spores3/src/spores/SporeBuilder.scala b/spores3/src/spores/SporeBuilder.scala index 053a0ab..d2ac093 100644 --- a/spores3/src/spores/SporeBuilder.scala +++ b/spores3/src/spores/SporeBuilder.scala @@ -3,7 +3,7 @@ package spores import upickle.default.* import spores.Reflection -import spores.Packed.* +import spores.AST /** A builder trait that packs a [[Spore]] with a closure of type `T`. Extend @@ -29,7 +29,7 @@ import spores.Packed.* * The wrapped closure. */ @Reflection.EnableReflectiveInstantiation -trait SporeBuilder[+T](private[spores] val fun: T) { +trait SporeBuilder[+T](private[spores] val body: T) { /** Packs the wrapped closure into a [[Spore]] of type `T`. * @@ -47,6 +47,6 @@ private object SporeBuilder { def buildMacro[T](expr: Expr[SporeBuilder[T]])(using Type[T], Quotes): Expr[Spore[T]] = { Macros.isTopLevelObject(expr) - '{ PackedObject($expr.getClass().getName()) } + '{ AST.Body(0, $expr.getClass().getName(), $expr.body) } } } diff --git a/spores3/src/spores/SporeClassBuilder.scala b/spores3/src/spores/SporeClassBuilder.scala index 4887c0d..6392b38 100644 --- a/spores3/src/spores/SporeClassBuilder.scala +++ b/spores3/src/spores/SporeClassBuilder.scala @@ -3,7 +3,7 @@ package spores import upickle.default.* import spores.Reflection -import spores.Packed.* +import spores.AST /** A class-based builder trait that packs a [[Spore]] with a closure of type @@ -30,7 +30,7 @@ import spores.Packed.* * The wrapped closure. */ @Reflection.EnableReflectiveInstantiation -trait SporeClassBuilder[+T](private[spores] val fun: T) { +trait SporeClassBuilder[+T](private[spores] val body: T) { /** Packs the wrapped closure into a [[Spore]] of type `T`. * @@ -48,6 +48,6 @@ private object SporeClassBuilder { def buildMacro[T](expr: Expr[SporeClassBuilder[T]])(using Type[T], Quotes): Expr[Spore[T]] = { Macros.isTopLevelClass(expr) - '{ PackedClass($expr.getClass().getName()) } + '{ AST.Body(1, $expr.getClass().getName(), $expr.body) } } } diff --git a/spores3/src/spores/SporeLambdaBuilder.scala b/spores3/src/spores/SporeLambdaBuilder.scala index ea1a7a3..75e5bc4 100644 --- a/spores3/src/spores/SporeLambdaBuilder.scala +++ b/spores3/src/spores/SporeLambdaBuilder.scala @@ -3,12 +3,12 @@ package spores import upickle.default.* import spores.Reflection -import spores.Packed.* +import spores.AST /** Internal API. Used by the spores.jvm.Spore lambda factories. */ @Reflection.EnableReflectiveInstantiation -private[spores] trait SporeLambdaBuilder[+T](val fun: T) { +private[spores] trait SporeLambdaBuilder[+T](val body: T) { final inline def build(): Spore[T] = { ${ SporeLambdaBuilder.buildMacro('this) } @@ -21,6 +21,6 @@ private object SporeLambdaBuilder { def buildMacro[T](expr: Expr[SporeLambdaBuilder[T]])(using Type[T], Quotes): Expr[Spore[T]] = { // No checks needed, all relevant checks are done in the spores.jvm.Spore lambda factories. - '{ PackedLambda($expr.getClass().getName()) } + '{ AST.Body(2, $expr.getClass().getName(), $expr.body) } } } diff --git a/spores3/test/src-jvm/spores/jvm/AutoCaptureTests.scala b/spores3/test/src-jvm/spores/jvm/AutoCaptureTests.scala index 0b7d157..dfb331a 100644 --- a/spores3/test/src-jvm/spores/jvm/AutoCaptureTests.scala +++ b/spores3/test/src-jvm/spores/jvm/AutoCaptureTests.scala @@ -125,10 +125,10 @@ object AutoCaptureTests extends TestSuite { } test("testReadFun0123") { - val json0 = """{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.AutoCaptureTestsDefs$FunctionsToReadFromJSON$Lambda$1"}""" - val json1 = """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.AutoCaptureTestsDefs$FunctionsToReadFromJSON$Lambda$2"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"\"0123456789-1\"","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$StringRW$"}}}""" - val json2 = """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.AutoCaptureTestsDefs$FunctionsToReadFromJSON$Lambda$3"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"\"0123456789-1\"","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$StringRW$"}}},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"\"0123456789-2\"","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$StringRW$"}}}""" - val json3 = """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.AutoCaptureTestsDefs$FunctionsToReadFromJSON$Lambda$4"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"\"0123456789-1\"","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$StringRW$"}}},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"\"0123456789-2\"","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$StringRW$"}}},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"[\"a\",\"b\",\"c\"]","rw":{"$type":"spores.Packed.PackedWithCtx","packed":{"$type":"spores.Packed.PackedClass","className":"spores.ReadWriters$ListRW"},"packedEnv":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$StringRW$"}}}}""" + val json0 = """{"tag":"Body","kind":2,"className":"spores.jvm.AutoCaptureTestsDefs$FunctionsToReadFromJSON$Lambda$1"}""" + val json1 = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.AutoCaptureTestsDefs$FunctionsToReadFromJSON$Lambda$2"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"0123456789-1"}}""" + val json2 = """{"tag":"WithEnv","fun":{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.AutoCaptureTestsDefs$FunctionsToReadFromJSON$Lambda$3"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"0123456789-1"}},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"0123456789-2"}}""" + val json3 = """{"tag":"WithEnv","fun":{"tag":"WithEnv","fun":{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.AutoCaptureTestsDefs$FunctionsToReadFromJSON$Lambda$4"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"0123456789-1"}},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"0123456789-2"}},"env":{"tag":"Val","ev":{"tag":"WithCtx","fun":{"tag":"Body","kind":1,"className":"spores.ReadWriters$ListRW"},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"}},"value":["a","b","c"]}}""" val a1 = "0123456789-1" val a2 = "0123456789-2" diff --git a/spores3/test/src-jvm/spores/jvm/SporeLambdaTests.scala b/spores3/test/src-jvm/spores/jvm/SporeLambdaTests.scala index 9a4777f..422ccb1 100644 --- a/spores3/test/src-jvm/spores/jvm/SporeLambdaTests.scala +++ b/spores3/test/src-jvm/spores/jvm/SporeLambdaTests.scala @@ -67,7 +67,7 @@ object SporeLambdaTests extends TestSuite { } test("testPackedLambdaReadWriter") { - val json = """{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.SporeLambdaTestsDefs$Lambda$1"}""" + val json = """{"tag":"Body","kind":2,"className":"spores.jvm.SporeLambdaTestsDefs$Lambda$1"}""" val packed = upickle.default.write(lambda) assert(json == packed) @@ -80,7 +80,7 @@ object SporeLambdaTests extends TestSuite { } test("testNestedLambdaReadWriter") { - val json = """{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.SporeLambdaTestsDefs$NestedLambda$Lambda$3"}""" + val json = """{"tag":"Body","kind":2,"className":"spores.jvm.SporeLambdaTestsDefs$NestedLambda$Lambda$3"}""" val packed = upickle.default.write(NestedLambda.lambda) assert(json == packed) @@ -93,8 +93,8 @@ object SporeLambdaTests extends TestSuite { } test("testPackedLambdaWithEnvReadWriter") { - val json9 = """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.SporeLambdaTestsDefs$Lambda$1"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"9","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$IntRW$"}}}""" - val json11 = """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.SporeLambdaTestsDefs$Lambda$1"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"11","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$IntRW$"}}}""" + val json9 = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeLambdaTestsDefs$Lambda$1"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":9}}""" + val json11 = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeLambdaTestsDefs$Lambda$1"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":11}}""" val packed9 = upickle.default.write(lambda.withEnv(9)) val packed11 = upickle.default.write(lambda.withEnv(11)) @@ -108,7 +108,7 @@ object SporeLambdaTests extends TestSuite { } test("testLambdaWithEnvConstructorReadWriter") { - val json = """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedLambda","className":"spores.jvm.SporeLambdaTestsDefs$Lambda$2"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"11","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$IntRW$"}}}""" + val json = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeLambdaTestsDefs$Lambda$2"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":11}}""" val packed = upickle.default.write(lambdaWithEnv) assert(json == packed) diff --git a/spores3/test/src-jvm/spores/jvm/SporeSerializationJvmTests.scala b/spores3/test/src-jvm/spores/jvm/SporeSerializationJvmTests.scala new file mode 100644 index 0000000..67e1bcc --- /dev/null +++ b/spores3/test/src-jvm/spores/jvm/SporeSerializationJvmTests.scala @@ -0,0 +1,141 @@ +package spores.jvm + +import utest._ +import upickle.default.* + +import spores.default.* +import spores.default.given + + +object SporeSerializationJvmTestsDefs { + val lambda = Spore.apply[Int => String] { x => x.toString } + val lambdaWithEnv = Spore.applyWithEnv(12) { (env: Int) => (x: Int) => (env + x).toString } + val lambdaWithCtx = Spore.applyWithCtx(12) { summon[Int].toString } + val curriedLambda = Spore.apply[Int => Int => String] { x => y => (x + y).toString() } + val higherOrderLambda = Spore.apply[Spore[Int => Boolean] => Int => Option[Int]] { env => x => + if env.unwrap().apply(x) then Some(x) else None + } + val intPredicate = Spore.apply[Int => Boolean] { x => x > 10 } + val lambdaReturningSpore = Spore.apply[Int => Spore[String]] { x => Env(x.toString()) } +} + +object SporeSerializationJvmTestsDeadCode { + val lambda = Spore.apply[Int => String] { x => x.toString } + val lambdaWithEnv = Spore.applyWithEnv(12) { (env: Int) => (x: Int) => (env + x).toString } + val lambdaWithCtx = Spore.applyWithCtx(12) { summon[Int].toString } + val curriedLambda = Spore.apply[Int => Int => String] { x => y => (x + y).toString() } + val higherOrderLambda = Spore.apply[Spore[Int => Boolean] => Int => Option[Int]] { env => x => + if env.unwrap().apply(x) then Some(x) else None + } + val intPredicate = Spore.apply[Int => Boolean] { x => x > 10 } + val lambdaReturningSpore = Spore.apply[Int => Spore[String]] { x => Env(x.toString()) } +} + +object SporeSerializationJvmTests extends TestSuite { + import SporeSerializationJvmTestsDefs.* + + val tests = Tests { + + test("testSerializeSporeApply") { + val expected = """{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDefs$Lambda$1"}""" + val json = write(lambda) + assert(expected == json) + val spore2 = read[Spore[Int => String]](json) + assert("12" == spore2.unwrap()(12)) + } + + test("testSerializeSporeApplyWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDefs$Lambda$2"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val json = write(lambdaWithEnv) + assert(expected == json) + val spore2 = read[Spore[Int => String]](json) + assert("20" == spore2.unwrap()(8)) + } + + test("testSerializeSporeApplyWithCtx") { + val expected = """{"tag":"WithCtx","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDefs$Lambda$3"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val json = write(lambdaWithCtx) + assert(expected == json) + val spore2 = read[Spore[String]](json) + assert("12" == spore2.unwrap()) + } + + test("testSerializeCurriedLambda") { + val expected = """{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDefs$Lambda$4"}""" + val json = write(curriedLambda) + assert(expected == json) + val spore2 = read[Spore[Int => Int => String]](json) + assert("7" == spore2.unwrap()(3)(4)) + } + + test("testSerializeCurriedLambdaWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDefs$Lambda$4"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":3}}""" + val spore = curriedLambda.withEnv(3) + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => String]](json) + assert("7" == spore2.unwrap()(4)) + } + + test("testSerializeHigherOrderWithSporeEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDefs$Lambda$5"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDefs$Lambda$6"}}}""" + val spore = higherOrderLambda.withEnv(intPredicate) + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => Option[Int]]](json) + assert(Some(11) == spore2.unwrap()(11)) + assert(None == spore2.unwrap()(9)) + } + + test("testSerializeLambdaReturningSpore") { + val expected = """{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDefs$Lambda$7"}""" + val json = write(lambdaReturningSpore) + assert(expected == json) + val spore2 = read[Spore[Int => Spore[String]]](json) + assert("12" == spore2.unwrap()(12).unwrap()) + } + + test("testSerializeDeadCodeLambda") { + val json = """{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDeadCode$Lambda$8"}""" + val spore2 = read[Spore[Int => String]](json) + assert("12" == spore2.unwrap()(12)) + } + + test("testSerializeDeadCodeLambdaWithEnv") { + val json = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDeadCode$Lambda$9"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val spore2 = read[Spore[Int => String]](json) + assert("20" == spore2.unwrap()(8)) + } + + test("testSerializeDeadCodeLambdaWithCtx") { + val json = """{"tag":"WithCtx","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDeadCode$Lambda$10"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val spore2 = read[Spore[String]](json) + assert("12" == spore2.unwrap()) + } + + test("testSerializeDeadCodeCurriedLambda") { + val json = """{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDeadCode$Lambda$11"}""" + val spore2 = read[Spore[Int => Int => String]](json) + assert("7" == spore2.unwrap()(3)(4)) + } + + test("testSerializeDeadCodeCurriedLambdaWithEnv") { + val json = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDeadCode$Lambda$11"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":3}}""" + val spore2 = read[Spore[Int => String]](json) + assert("7" == spore2.unwrap()(4)) + } + + test("testSerializeDeadCodeHigherOrderWithSporeEnv") { + val json = """{"tag":"WithEnv","fun":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDeadCode$Lambda$12"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDeadCode$Lambda$13"}}}""" + val spore2 = read[Spore[Int => Option[Int]]](json) + assert(Some(11) == spore2.unwrap()(11)) + assert(None == spore2.unwrap()(9)) + } + + test("testSerializeDeadCodeLambdaReturningSpore") { + val json = """{"tag":"Body","kind":2,"className":"spores.jvm.SporeSerializationJvmTestsDeadCode$Lambda$14"}""" + val spore2 = read[Spore[Int => Spore[String]]](json) + assert("12" == spore2.unwrap()(12).unwrap()) + } + } +} diff --git a/spores3/test/src/spores/SporeClassBuilderTests.scala b/spores3/test/src/spores/SporeClassBuilderTests.scala index f92e3f5..759ccd9 100644 --- a/spores3/test/src/spores/SporeClassBuilderTests.scala +++ b/spores3/test/src/spores/SporeClassBuilderTests.scala @@ -54,7 +54,7 @@ object SporeClassBuilderTests extends TestSuite { } test("testSporeClassBuilderReadWriter") { - val json = """{"$type":"spores.Packed.PackedClass","className":"spores.SporeClassBuilderTestsDefs$Predicate"}""" + val json = """{"tag":"Body","kind":1,"className":"spores.SporeClassBuilderTestsDefs$Predicate"}""" val packed = upickle.default.write(new Predicate().build()) assert(json == packed) @@ -67,7 +67,7 @@ object SporeClassBuilderTests extends TestSuite { } test("testSporeClassBuilderWithTypeParamReadWriter") { - val json = """{"$type":"spores.Packed.PackedClass","className":"spores.SporeClassBuilderTestsDefs$Flatten"}""" + val json = """{"tag":"Body","kind":1,"className":"spores.SporeClassBuilderTestsDefs$Flatten"}""" val packed = upickle.default.write(new Flatten[Int].build()) assert(json == packed) @@ -79,7 +79,7 @@ object SporeClassBuilderTests extends TestSuite { } test("testSporeClassBuilderWithEnvReadWriter") { - val json = """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedClass","className":"spores.SporeClassBuilderTestsDefs$FilterWithTypeParam"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"{\"$type\":\"spores.Packed.PackedClass\",\"className\":\"spores.SporeClassBuilderTestsDefs$Predicate\"}","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$SporeRW$"}}}""" + val json = """{"tag":"WithEnv","fun":{"tag":"Body","kind":1,"className":"spores.SporeClassBuilderTestsDefs$FilterWithTypeParam"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":1,"className":"spores.SporeClassBuilderTestsDefs$Predicate"}}}""" val predicate = new Predicate().build() val filter = new FilterWithTypeParam[Int].build().withEnv(predicate) diff --git a/spores3/test/src/spores/SporeObjectBuilderTests.scala b/spores3/test/src/spores/SporeObjectBuilderTests.scala index cd9757e..4337e7f 100644 --- a/spores3/test/src/spores/SporeObjectBuilderTests.scala +++ b/spores3/test/src/spores/SporeObjectBuilderTests.scala @@ -111,7 +111,7 @@ object SporeBuilderTests extends TestSuite { } test("testSporeReadWriter") { - val json = """{"$type":"spores.Packed.PackedObject","className":"spores.SporeBuilderTestsDefs$Predicate$"}""" + val json = """{"tag":"Body","kind":0,"className":"spores.SporeBuilderTestsDefs$Predicate$"}""" val packed = upickle.default.write(Predicate.build()) assert(packed == json) @@ -124,7 +124,7 @@ object SporeBuilderTests extends TestSuite { } test("testNestedSporeReadWriter") { - val json = """{"$type":"spores.Packed.PackedObject","className":"spores.SporeBuilderTestsDefs$NestedBuilder$Predicate$"}""" + val json = """{"tag":"Body","kind":0,"className":"spores.SporeBuilderTestsDefs$NestedBuilder$Predicate$"}""" val packed = upickle.default.write(NestedBuilder.Predicate.build()) assert(packed == json) @@ -137,7 +137,7 @@ object SporeBuilderTests extends TestSuite { } test("testSporeReadWriterWithEnv") { - val json = """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedObject","className":"spores.SporeBuilderTestsDefs$HigherLevelFilter$"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"{\"$type\":\"spores.Packed.PackedObject\",\"className\":\"spores.SporeBuilderTestsDefs$Predicate$\"}","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$SporeRW$"}}}""" + val json = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeBuilderTestsDefs$HigherLevelFilter$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":0,"className":"spores.SporeBuilderTestsDefs$Predicate$"}}}""" val predicate = Predicate.build() val filter = HigherLevelFilter.build().withEnv(predicate) diff --git a/spores3/test/src/spores/SporeSerializationTests.scala b/spores3/test/src/spores/SporeSerializationTests.scala new file mode 100644 index 0000000..2628c88 --- /dev/null +++ b/spores3/test/src/spores/SporeSerializationTests.scala @@ -0,0 +1,894 @@ +package spores + +import utest._ +import upickle.default.* + +import spores.default.* +import spores.default.given + + +object SporeSerializationTestsDefs { + + object IntSpore extends SporeBuilder[Int](12) + object StringSpore extends SporeBuilder[String]("Hello") + object BooleanSpore extends SporeBuilder[Boolean](true) + object DoubleSpore extends SporeBuilder[Double](0.5) + object FloatSpore extends SporeBuilder[Float](1.5f) + object LongSpore extends SporeBuilder[Long](987654321L) + object ShortSpore extends SporeBuilder[Short](12.toShort) + object ByteSpore extends SporeBuilder[Byte](7.toByte) + object CharSpore extends SporeBuilder[Char]('a') + object UnitSpore extends SporeBuilder[Unit](()) + + object IntToIntSpore extends SporeBuilder[Int => Int](x => x + 12) + object IntToStringSpore extends SporeBuilder[Int => String](x => x.toString()) + object StringToIntSpore extends SporeBuilder[String => Int](s => s.length) + object IntPredicateSpore extends SporeBuilder[Int => Boolean](x => x > 10) + object IntCtxToStringSpore extends SporeBuilder[Int ?=> String](summon[Int].toString()) + object IntToSporeStringSpore extends SporeBuilder[Int => Spore[String]](x => Env(x.toString())) + + object IntIntToIntSpore extends SporeBuilder[(Int, Int) => Int]((x, y) => x + y + 12) + object IntIntToStringSpore extends SporeBuilder[(Int, Int) => String]((x, y) => (x + y).toString()) + + class Identity[T] extends SporeClassBuilder[T => T](x => x) + class IntToStringClassSpore extends SporeClassBuilder[Int => String](x => x.toString()) + class IntCtxToStringClassSpore extends SporeClassBuilder[Int ?=> String](summon[Int].toString()) + class TToStringClassSpore[T] extends SporeClassBuilder[T => String](x => x.toString()) + class TToTClassSpore[T] extends SporeClassBuilder[T => (T => T) => T](t => f => f(t)) + object SporeToString extends SporeBuilder[Spore[Int => Boolean] => Int => String](env => x => if env.unwrap()(x) then "yes" else "no") + object IntToStringCurried extends SporeBuilder[Int => Int => String](x => y => (x + y).toString()) + + case class Foo(x: Int, y: String) + object FooRW extends SporeBuilder[ReadWriter[Foo]]({ macroRW }) + given Spore[ReadWriter[Foo]] = FooRW.build() + + object FooToStringSpore extends SporeBuilder[Foo => String](p => "x: " + p.x.toString() + ", y: " + p.y) + + val intEnv: Spore[Int] = Env(12) + val stringEnv: Spore[String] = Env("Hello") + val booleanEnv: Spore[Boolean] = Env(true) + val doubleEnv: Spore[Double] = Env(0.5) + val floatEnv: Spore[Float] = Env(1.5f) + val longEnv: Spore[Long] = Env(987654321L) + val shortEnv: Spore[Short] = Env(12.toShort) + val byteEnv: Spore[Byte] = Env(7.toByte) + val charEnv: Spore[Char] = Env('a') + val unitEnv: Spore[Unit] = Env(()) + val sporeIntEnv: Spore[Spore[Int]] = Env(IntSpore.build()) + val fooEnv: Spore[Foo] = Env(Foo(1, "337")) + val someIntEnv: Spore[Some[Int]] = Env(Some(12)) + val noneEnv: Spore[None.type] = Env(None) + val listIntEnv: Spore[List[Int]] = Env(List(1, 2, 3)) + val tuple1Env: Spore[Tuple1[Int]] = Env(Tuple1(12)) + val tuple2Env: Spore[(Int, String)] = Env((1, "a")) + val tuple3Env: Spore[(Int, String, Boolean)] = Env((1, "a", true)) + val eitherRightEnv: Spore[Either[String, Int]] = Env(Right(12): Either[String, Int]) + val eitherLeftEnv: Spore[Either[String, Int]] = Env(Left("err"): Either[String, Int]) + val nestedSporeEnv: Spore[Spore[Int => Boolean]] = Env(IntPredicateSpore.build()) +} + +object SporeSerializationTestsDeadCode { + + object IntSpore extends SporeBuilder[Int](12) + object StringSpore extends SporeBuilder[String]("Hello") + object BooleanSpore extends SporeBuilder[Boolean](true) + object DoubleSpore extends SporeBuilder[Double](0.5) + object FloatSpore extends SporeBuilder[Float](1.5f) + object LongSpore extends SporeBuilder[Long](987654321L) + object ShortSpore extends SporeBuilder[Short](12.toShort) + object ByteSpore extends SporeBuilder[Byte](7.toByte) + object CharSpore extends SporeBuilder[Char]('a') + object UnitSpore extends SporeBuilder[Unit](()) + + object IntToIntSpore extends SporeBuilder[Int => Int](x => x + 12) + object IntToStringSpore extends SporeBuilder[Int => String](x => x.toString()) + object StringToIntSpore extends SporeBuilder[String => Int](s => s.length) + object IntPredicateSpore extends SporeBuilder[Int => Boolean](x => x > 10) + object IntCtxToStringSpore extends SporeBuilder[Int ?=> String](summon[Int].toString()) + object IntToSporeStringSpore extends SporeBuilder[Int => Spore[String]](x => Env(x.toString())) + + object IntIntToIntSpore extends SporeBuilder[(Int, Int) => Int]((x, y) => x + y + 12) + object IntIntToStringSpore extends SporeBuilder[(Int, Int) => String]((x, y) => (x + y).toString()) + + class Identity[T] extends SporeClassBuilder[T => T](x => x) + class IntToStringClassSpore extends SporeClassBuilder[Int => String](x => x.toString()) + class IntCtxToStringClassSpore extends SporeClassBuilder[Int ?=> String](summon[Int].toString()) + class TToStringClassSpore[T] extends SporeClassBuilder[T => String](x => x.toString()) + class TToTClassSpore[T] extends SporeClassBuilder[T => (T => T) => T](t => f => f(t)) + object SporeToString extends SporeBuilder[Spore[Int => Boolean] => Int => String](env => x => if env.unwrap()(x) then "yes" else "no") + object IntToStringCurried extends SporeBuilder[Int => Int => String](x => y => (x + y).toString()) + + case class Foo(x: Int, y: String) + object FooRW extends SporeBuilder[ReadWriter[Foo]]({ macroRW }) + given Spore[ReadWriter[Foo]] = FooRW.build() + + object FooToStringSpore extends SporeBuilder[Foo => String](p => "x: " + p.x.toString() + ", y: " + p.y) +} + +object SporeSerializationTests extends TestSuite { + import SporeSerializationTestsDefs.* + + val tests = Tests { + + test("testSerializeIntSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntSpore$"}""" + val spore = IntSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int]](json) + assert(12 == spore2.unwrap()) + } + + test("testSerializeStringSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$StringSpore$"}""" + val spore = StringSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[String]](json) + assert("Hello" == spore2.unwrap()) + } + + test("testSerializeBooleanSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$BooleanSpore$"}""" + val spore = BooleanSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Boolean]](json) + assert(true == spore2.unwrap()) + } + + test("testSerializeDoubleSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$DoubleSpore$"}""" + val spore = DoubleSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Double]](json) + assert(0.5 == spore2.unwrap()) + } + + test("testSerializeFloatSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$FloatSpore$"}""" + val spore = FloatSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Float]](json) + assert(1.5f == spore2.unwrap()) + } + + test("testSerializeLongSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$LongSpore$"}""" + val spore = LongSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Long]](json) + assert(987654321L == spore2.unwrap()) + } + + test("testSerializeShortSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$ShortSpore$"}""" + val spore = ShortSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Short]](json) + assert(12.toShort == spore2.unwrap()) + } + + test("testSerializeByteSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$ByteSpore$"}""" + val spore = ByteSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Byte]](json) + assert(7.toByte == spore2.unwrap()) + } + + test("testSerializeCharSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$CharSpore$"}""" + val spore = CharSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Char]](json) + assert('a' == spore2.unwrap()) + } + + test("testSerializeUnitSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$UnitSpore$"}""" + val spore = UnitSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Unit]](json) + assert(() == spore2.unwrap()) + } + + test("testSerializeIntToIntSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToIntSpore$"}""" + val spore = IntToIntSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => Int]](json) + assert(24 == spore2.unwrap()(12)) + } + + test("testSerializeIntToStringSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToStringSpore$"}""" + val spore = IntToStringSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => String]](json) + assert("12" == spore2.unwrap()(12)) + } + + test("testSerializeStringToIntSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$StringToIntSpore$"}""" + val spore = StringToIntSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[String => Int]](json) + assert(5 == spore2.unwrap()("Hello")) + } + + test("testSerializeIntPredicateSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntPredicateSpore$"}""" + val spore = IntPredicateSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => Boolean]](json) + assert(true == spore2.unwrap()(11)) + assert(false == spore2.unwrap()(9)) + } + + test("testSerializeIntCtxToStringSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntCtxToStringSpore$"}""" + val spore = IntCtxToStringSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int ?=> String]](json) + assert("12" == spore2.unwrap()(using 12)) + } + + test("testSerializeIntToSporeStringSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToSporeStringSpore$"}""" + val spore = IntToSporeStringSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => Spore[String]]](json) + assert("12" == spore2.unwrap()(12).unwrap()) + } + + test("testSerializeIntIntToIntSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntIntToIntSpore$"}""" + val spore = IntIntToIntSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[(Int, Int) => Int]](json) + assert(24 == spore2.unwrap()(5, 7)) + } + + test("testSerializeIntIntToStringSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntIntToStringSpore$"}""" + val spore = IntIntToStringSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[(Int, Int) => String]](json) + assert("12" == spore2.unwrap()(5, 7)) + } + + test("testSerializeIntToStringClassSpore") { + val expected = """{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDefs$IntToStringClassSpore"}""" + val spore = new IntToStringClassSpore().build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => String]](json) + assert("12" == spore2.unwrap()(12)) + } + + test("testSerializeIntCtxToStringClassSpore") { + val expected = """{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDefs$IntCtxToStringClassSpore"}""" + val spore = new IntCtxToStringClassSpore().build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int ?=> String]](json) + assert("12" == spore2.unwrap()(using 12)) + } + + test("testSerializeTToStringClassSpore") { + val expected = """{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDefs$TToStringClassSpore"}""" + val spore = new TToStringClassSpore[Int]().build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => String]](json) + assert("12" == spore2.unwrap()(12)) + } + + test("testSerializeTToTClassSpore") { + val expected = """{"tag":"WithEnv","fun":{"tag":"WithEnv","fun":{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDefs$TToTClassSpore"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":5}},"env":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToIntSpore$"}}""" + val spore = new TToTClassSpore[Int]().build().withEnv(5).withEnv2(IntToIntSpore.build()) + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int]](json) + assert(17 == spore2.unwrap()) + } + + test("testSerializeIdentity") { + val expected = """{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDefs$Identity"}""" + val spore = new Identity[Int]().build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => Int]](json) + assert(12 == spore2.unwrap()(12)) + } + + test("testSerializeSporeToString") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$SporeToString$"}""" + val spore = SporeToString.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Spore[Int => Boolean] => Int => String]](json) + assert("yes" == spore2.unwrap()(IntPredicateSpore.build())(11)) + assert("no" == spore2.unwrap()(IntPredicateSpore.build())(9)) + } + + test("testSerializeIntToStringCurried") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToStringCurried$"}""" + val spore = IntToStringCurried.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Int => Int => String]](json) + assert("7" == spore2.unwrap()(3)(4)) + } + + test("testSerializeIntToIntWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToIntSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val json = write(IntToIntSpore.build().withEnv(12)) + assert(expected == json) + val spore2 = read[Spore[Int]](json) + assert(24 == spore2.unwrap()) + } + + test("testSerializeIntToStringWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToStringSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val json = write(IntToStringSpore.build().withEnv(12)) + assert(expected == json) + val spore2 = read[Spore[String]](json) + assert("12" == spore2.unwrap()) + } + + test("testSerializeStringToIntWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$StringToIntSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"Hello"}}""" + val json = write(StringToIntSpore.build().withEnv("Hello")) + assert(expected == json) + val spore2 = read[Spore[Int]](json) + assert(5 == spore2.unwrap()) + } + + test("testSerializeIntToSporeStringWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToSporeStringSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val json = write(IntToSporeStringSpore.build().withEnv(12)) + assert(expected == json) + val spore2 = read[Spore[Spore[String]]](json) + assert("12" == spore2.unwrap().unwrap()) + } + + test("testSerializeIntToStringCurriedWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToStringCurried$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":3}}""" + val json = write(IntToStringCurried.build().withEnv(3)) + assert(expected == json) + val spore2 = read[Spore[Int => String]](json) + assert("7" == spore2.unwrap()(4)) + } + + test("testSerializeSporeToStringWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$SporeToString$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntPredicateSpore$"}}}""" + val json = write(SporeToString.build().withEnv(IntPredicateSpore.build())) + assert(expected == json) + val spore2 = read[Spore[Int => String]](json) + assert("yes" == spore2.unwrap()(11)) + assert("no" == spore2.unwrap()(9)) + } + + test("testSerializeIntEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}""" + val json = write(intEnv) + assert(expected == json) + val spore2 = read[Spore[Int]](json) + assert(12 == spore2.unwrap()) + } + + test("testSerializeStringEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"Hello"}""" + val json = write(stringEnv) + assert(expected == json) + val spore2 = read[Spore[String]](json) + assert("Hello" == spore2.unwrap()) + } + + test("testSerializeBooleanEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$BooleanRW$"},"value":true}""" + val json = write(booleanEnv) + assert(expected == json) + val spore2 = read[Spore[Boolean]](json) + assert(true == spore2.unwrap()) + } + + test("testSerializeDoubleEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$DoubleRW$"},"value":0.5}""" + val json = write(doubleEnv) + assert(expected == json) + val spore2 = read[Spore[Double]](json) + assert(0.5 == spore2.unwrap()) + } + + test("testSerializeFloatEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$FloatRW$"},"value":1.5}""" + val json = write(floatEnv) + assert(expected == json) + val spore2 = read[Spore[Float]](json) + assert(1.5f == spore2.unwrap()) + } + + test("testSerializeLongEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$LongRW$"},"value":987654321}""" + val json = write(longEnv) + assert(expected == json) + val spore2 = read[Spore[Long]](json) + assert(987654321L == spore2.unwrap()) + } + + test("testSerializeShortEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$ShortRW$"},"value":12}""" + val json = write(shortEnv) + assert(expected == json) + val spore2 = read[Spore[Short]](json) + assert(12.toShort == spore2.unwrap()) + } + + test("testSerializeByteEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$ByteRW$"},"value":7}""" + val json = write(byteEnv) + assert(expected == json) + val spore2 = read[Spore[Byte]](json) + assert(7.toByte == spore2.unwrap()) + } + + test("testSerializeCharEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$CharRW$"},"value":"a"}""" + val json = write(charEnv) + assert(expected == json) + val spore2 = read[Spore[Char]](json) + assert('a' == spore2.unwrap()) + } + + test("testSerializeUnitEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$UnitRW$"},"value":null}""" + val json = write(unitEnv) + assert(expected == json) + val spore2 = read[Spore[Unit]](json) + assert(() == spore2.unwrap()) + } + + test("testSerializeSporeIntEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntSpore$"}}""" + val json = write(sporeIntEnv) + assert(expected == json) + val spore2 = read[Spore[Spore[Int]]](json) + assert(12 == spore2.unwrap().unwrap()) + } + + test("testSerializeFooEnv") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$FooRW$"},"value":{"x":1,"y":"337"}}""" + val json = write(fooEnv) + assert(expected == json) + val spore2 = read[Spore[Foo]](json) + assert(Foo(1, "337") == spore2.unwrap()) + } + + test("testSerializeOptionSome") { + val expected = """{"tag":"Val","ev":{"tag":"WithCtx","fun":{"tag":"Body","kind":1,"className":"spores.ReadWriters$SomeRW"},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"}},"value":12}""" + val json = write(someIntEnv) + assert(expected == json) + val spore2 = read[Spore[Some[Int]]](json) + assert(Some(12) == spore2.unwrap()) + } + + test("testSerializeOptionNone") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$NoneRW$"},"value":null}""" + val json = write(noneEnv) + assert(expected == json) + val spore2 = read[Spore[None.type]](json) + assert(None == spore2.unwrap()) + } + + test("testSerializeList") { + val expected = """{"tag":"Val","ev":{"tag":"WithCtx","fun":{"tag":"Body","kind":1,"className":"spores.ReadWriters$ListRW"},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"}},"value":[1,2,3]}""" + val json = write(listIntEnv) + assert(expected == json) + val spore2 = read[Spore[List[Int]]](json) + assert(List(1, 2, 3) == spore2.unwrap()) + } + + test("testSerializeTuple1") { + val expected = """{"tag":"Val","ev":{"tag":"WithCtx","fun":{"tag":"Body","kind":1,"className":"spores.ReadWriters$Tuple1RW"},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"}},"value":[12]}""" + val json = write(tuple1Env) + assert(expected == json) + val spore2 = read[Spore[Tuple1[Int]]](json) + assert(Tuple1(12) == spore2.unwrap()) + } + + test("testSerializeTuple2") { + val expected = """{"tag":"Val","ev":{"tag":"WithCtx","fun":{"tag":"WithCtx","fun":{"tag":"Body","kind":1,"className":"spores.ReadWriters$Tuple2RW"},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"}},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"}},"value":[1,"a"]}""" + val json = write(tuple2Env) + assert(expected == json) + val spore2 = read[Spore[(Int, String)]](json) + assert((1, "a") == spore2.unwrap()) + } + + test("testSerializeTuple3") { + val expected = """{"tag":"Val","ev":{"tag":"WithCtx","fun":{"tag":"WithCtx","fun":{"tag":"WithCtx","fun":{"tag":"Body","kind":1,"className":"spores.ReadWriters$Tuple3RW"},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"}},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"}},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$BooleanRW$"}},"value":[1,"a",true]}""" + val json = write(tuple3Env) + assert(expected == json) + val spore2 = read[Spore[(Int, String, Boolean)]](json) + assert((1, "a", true) == spore2.unwrap()) + } + + test("testSerializeEitherRight") { + val expected = """{"tag":"Val","ev":{"tag":"WithCtx","fun":{"tag":"WithCtx","fun":{"tag":"Body","kind":1,"className":"spores.ReadWriters$EitherRW"},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"}},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"}},"value":[1,12]}""" + val json = write(eitherRightEnv) + assert(expected == json) + val spore2 = read[Spore[Either[String, Int]]](json) + assert(Right(12) == spore2.unwrap()) + } + + test("testSerializeEitherLeft") { + val expected = """{"tag":"Val","ev":{"tag":"WithCtx","fun":{"tag":"WithCtx","fun":{"tag":"Body","kind":1,"className":"spores.ReadWriters$EitherRW"},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"}},"env":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"}},"value":[0,"err"]}""" + val json = write(eitherLeftEnv) + assert(expected == json) + val spore2 = read[Spore[Either[String, Int]]](json) + assert(Left("err") == spore2.unwrap()) + } + + test("testSerializeNestedSpore") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntPredicateSpore$"}}""" + val json = write(nestedSporeEnv) + assert(expected == json) + val spore2 = read[Spore[Spore[Int => Boolean]]](json) + assert(spore2.unwrap().unwrap()(11)) + assert(!spore2.unwrap().unwrap()(9)) + } + + test("testSerializeWithEnv") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntPredicateSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":11}}""" + val spore = IntPredicateSpore.build().withEnv(11) + val json = write(spore) + assert(expected == json) + val spore2 = IntPredicateSpore.build().withEnv2(Env(11)) + val json2 = write(spore2) + assert(expected == json2) + val spore3 = read[Spore[Boolean]](json) + assert(true == spore3.unwrap()) + val expectedFalse = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntPredicateSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":9}}""" + val sporeFalse = IntPredicateSpore.build().withEnv(9) + val jsonFalse = write(sporeFalse) + assert(expectedFalse == jsonFalse) + val spore4 = read[Spore[Boolean]](jsonFalse) + assert(false == spore4.unwrap()) + } + + test("testSerializeWithCtx") { + val expected = """{"tag":"WithCtx","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntCtxToStringSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val spore = IntCtxToStringSpore.build().withCtx(12) + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[String]](json) + assert("12" == spore2.unwrap()) + } + + test("testSerializeWithCtx2") { + val expected = """{"tag":"WithCtx","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntCtxToStringSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val spore = IntCtxToStringSpore.build().withCtx2(Env(12)) + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[String]](json) + assert("12" == spore2.unwrap()) + } + + test("testSerializeMap") { + val expected = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$IntToStringSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""" + val spore = Env(12).map(IntToStringSpore.build()) + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[String]](json) + assert("12" == spore2.unwrap()) + } + + test("testSerializeFlatMap") { + val expected = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"12"}""" + val spore = Env(12).flatMap(IntToSporeStringSpore.build()) + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[String]](json) + assert("12" == spore2.unwrap()) + } + + test("testSerializeFooToStringSpore") { + val expected = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDefs$FooToStringSpore$"}""" + val spore = FooToStringSpore.build() + val json = write(spore) + assert(expected == json) + val spore2 = read[Spore[Foo => String]](json) + assert("x: 1, y: a" == spore2.unwrap()(Foo(1, "a"))) + } + + test("testReadPrettyPrintedIntSpore") { + val json = """{ + | "tag": "Body", + | "kind": 0, + | "className": "spores.SporeSerializationTestsDefs$IntSpore$" + |}""".stripMargin + val spore = read[Spore[Int]](json) + assert(12 == spore.unwrap()) + } + + test("testReadPrettyPrintedIntToIntSpore") { + val json = """{ + | "tag" : "Body", + | "kind" : 0, + | "className" : "spores.SporeSerializationTestsDefs$IntToIntSpore$" + |}""".stripMargin + val spore = read[Spore[Int => Int]](json) + assert(24 == spore.unwrap()(12)) + } + + test("testReadSpacedSporeToString") { + val json1 = """{"tag": "Body", "kind":0, "className": "spores.SporeSerializationTestsDefs$SporeToString$"}""" + val spore1 = read[Spore[Spore[Int => Boolean] => Int => String]](json1) + assert("yes" == spore1.unwrap()(IntPredicateSpore.build())(11)) + assert("no" == spore1.unwrap()(IntPredicateSpore.build())(9)) + val json2 = """{ + |"tag": "WithEnv", + |"fun": {"tag": "Body", "kind": 0, "className": "spores.SporeSerializationTestsDefs$SporeToString$"}, + |"env": {"tag": "Val", "ev": {"tag": "Body", "kind": 0, "className": "spores.ReadWriters$SporeRW$"}, "value": {"tag": "Body", "kind": 0, "className": "spores.SporeSerializationTestsDefs$IntPredicateSpore$"}} + |}""".stripMargin + val spore2 = read[Spore[Int => String]](json2) + assert("yes" == spore2.unwrap()(11)) + assert("no" == spore2.unwrap()(9)) + } + + test("testReadSpacedIntToStringCurried") { + val json1 = """{"tag":"Body", "kind":0, "className":"spores.SporeSerializationTestsDefs$IntToStringCurried$"}""" + val spore1 = read[Spore[Int => Int => String]](json1) + assert("7" == spore1.unwrap()(3)(4)) + val json2 = """{ + | "tag": "WithEnv", + | "fun": {"tag": "Body", "kind": 0, + | "className": "spores.SporeSerializationTestsDefs$IntToStringCurried$"}, + | "env": {"tag": "Val", + | "ev": {"tag": "Body", "kind": 0, "className": "spores.ReadWriters$IntRW$"}, + | "value": 3} + |}""".stripMargin + val spore2 = read[Spore[Int => String]](json2) + assert("7" == spore2.unwrap()(4)) + } + + test("testReadTabIndentedIntEnv") { + val json = "{\n\t\"tag\": \"Val\",\n\t\"ev\": {\n\t\t\"tag\": \"Body\",\n\t\t\"kind\": 0,\n\t\t\"className\": \"spores.ReadWriters$IntRW$\"\n\t},\n\t\"value\": 12\n}" + val spore = read[Spore[Int]](json) + assert(12 == spore.unwrap()) + } + + test("testReadExtraWhitespaceFooEnv") { + val json = """{ + | + | "tag" : "Val" , + | + | "ev" : { + | "tag" : "Body" , + | "kind" : 0 , + | "className" : "spores.SporeSerializationTestsDefs$FooRW$" + | } , + | + | "value" : { "x" : 1 , "y" : "337" } + | + |}""".stripMargin + val spore = read[Spore[Foo]](json) + assert(Foo(1, "337") == spore.unwrap()) + } + + test("testReadNewlinesOnlySporeIntEnv") { + val json = """{ + |"tag":"Val", + |"ev":{ + |"tag":"Body", + |"kind":0, + |"className":"spores.ReadWriters$SporeRW$" + |}, + |"value":{ + |"tag":"Body", + |"kind":0, + |"className":"spores.SporeSerializationTestsDefs$IntSpore$" + |} + |}""".stripMargin + val spore = read[Spore[Spore[Int]]](json) + assert(12 == spore.unwrap().unwrap()) + } + + test("testReadMixedFormattingListIntEnv") { + val json = """{ + | "tag": "Val", + | "ev": {"tag": "WithCtx", "fun": {"tag": "Body", "kind": 1, "className": "spores.ReadWriters$ListRW"}, "env": {"tag": "Body", "kind": 0, "className": "spores.ReadWriters$IntRW$"}}, + | "value": [ 1 , 2 , 3 ] + |}""".stripMargin + val spore = read[Spore[List[Int]]](json) + assert(List(1, 2, 3) == spore.unwrap()) + } + + test("testSerializeDeadCodeInt") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntSpore$"}""" + val spore2 = read[Spore[Int]](json) + assert(12 == spore2.unwrap()) + } + + test("testSerializeDeadCodeString") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$StringSpore$"}""" + val spore2 = read[Spore[String]](json) + assert("Hello" == spore2.unwrap()) + } + + test("testSerializeDeadCodeBoolean") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$BooleanSpore$"}""" + val spore2 = read[Spore[Boolean]](json) + assert(true == spore2.unwrap()) + } + + test("testSerializeDeadCodeDouble") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$DoubleSpore$"}""" + val spore2 = read[Spore[Double]](json) + assert(0.5 == spore2.unwrap()) + } + + test("testSerializeDeadCodeFloat") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$FloatSpore$"}""" + val spore2 = read[Spore[Float]](json) + assert(1.5f == spore2.unwrap()) + } + + test("testSerializeDeadCodeLong") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$LongSpore$"}""" + val spore2 = read[Spore[Long]](json) + assert(987654321L == spore2.unwrap()) + } + + test("testSerializeDeadCodeShort") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$ShortSpore$"}""" + val spore2 = read[Spore[Short]](json) + assert(12.toShort == spore2.unwrap()) + } + + test("testSerializeDeadCodeByte") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$ByteSpore$"}""" + val spore2 = read[Spore[Byte]](json) + assert(7.toByte == spore2.unwrap()) + } + + test("testSerializeDeadCodeChar") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$CharSpore$"}""" + val spore2 = read[Spore[Char]](json) + assert('a' == spore2.unwrap()) + } + + test("testSerializeDeadCodeUnit") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$UnitSpore$"}""" + val spore2 = read[Spore[Unit]](json) + assert(() == spore2.unwrap()) + } + + test("testSerializeDeadCodeIntToInt") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntToIntSpore$"}""" + val spore2 = read[Spore[Int => Int]](json) + assert(24 == spore2.unwrap()(12)) + } + + test("testSerializeDeadCodeIntToString") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntToStringSpore$"}""" + val spore2 = read[Spore[Int => String]](json) + assert("12" == spore2.unwrap()(12)) + } + + test("testSerializeDeadCodeStringToInt") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$StringToIntSpore$"}""" + val spore2 = read[Spore[String => Int]](json) + assert(5 == spore2.unwrap()("Hello")) + } + + test("testSerializeDeadCodeIntPredicate") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntPredicateSpore$"}""" + val spore2 = read[Spore[Int => Boolean]](json) + assert(true == spore2.unwrap()(11)) + assert(false == spore2.unwrap()(9)) + } + + test("testSerializeDeadCodeIntCtxToString") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntCtxToStringSpore$"}""" + val spore2 = read[Spore[Int ?=> String]](json) + assert("12" == spore2.unwrap()(using 12)) + } + + test("testSerializeDeadCodeIntToSporeString") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntToSporeStringSpore$"}""" + val spore2 = read[Spore[Int => Spore[String]]](json) + assert("12" == spore2.unwrap()(12).unwrap()) + } + + test("testSerializeDeadCodeIntIntToInt") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntIntToIntSpore$"}""" + val spore2 = read[Spore[(Int, Int) => Int]](json) + assert(24 == spore2.unwrap()(5, 7)) + } + + test("testSerializeDeadCodeIntIntToString") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntIntToStringSpore$"}""" + val spore2 = read[Spore[(Int, Int) => String]](json) + assert("12" == spore2.unwrap()(5, 7)) + } + + test("testSerializeDeadCodeIntToStringClass") { + val json = """{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDeadCode$IntToStringClassSpore"}""" + val spore2 = read[Spore[Int => String]](json) + assert("12" == spore2.unwrap()(12)) + } + + test("testSerializeDeadCodeIntCtxToStringClass") { + val json = """{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDeadCode$IntCtxToStringClassSpore"}""" + val spore2 = read[Spore[Int ?=> String]](json) + assert("12" == spore2.unwrap()(using 12)) + } + + test("testSerializeDeadCodeTToStringClass") { + val json = """{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDeadCode$TToStringClassSpore"}""" + val spore2 = read[Spore[Int => String]](json) + assert("42" == spore2.unwrap()(42)) + } + + test("testSerializeDeadCodeIdentity") { + val json = """{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDeadCode$Identity"}}""" + val spore2 = read[Spore[Spore[Int => Int]]](json) + assert(12 == spore2.unwrap().unwrap()(12)) + } + + test("testSerializeDeadCodeTToTClass") { + val json = """{"tag":"WithEnv","fun":{"tag":"WithEnv","fun":{"tag":"Body","kind":1,"className":"spores.SporeSerializationTestsDeadCode$TToTClassSpore"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":5}},"env":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntToIntSpore$"}}""" + val spore2 = read[Spore[Int]](json) + assert(17 == spore2.unwrap()) + } + + test("testSerializeDeadCodeIntToStringCurried") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntToStringCurried$"}""" + val spore2 = read[Spore[Int => Int => String]](json) + assert("7" == spore2.unwrap()(3)(4)) + } + + test("testSerializeDeadCodeSporeToString") { + val json = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$SporeToString$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$SporeRW$"},"value":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntPredicateSpore$"}}}""" + val spore2 = read[Spore[Int => String]](json) + assert("yes" == spore2.unwrap()(11)) + assert("no" == spore2.unwrap()(9)) + } + + test("testSerializeDeadCodeFooToString") { + val json = """{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$FooToStringSpore$"}""" + val spore2 = read[Spore[SporeSerializationTestsDeadCode.Foo => String]](json) + assert("x: 1, y: a" == spore2.unwrap()(SporeSerializationTestsDeadCode.Foo(1, "a"))) + } + + test("testSerializeDeadCodeWithEnv") { + val json = """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntPredicateSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":11}}""" + val spore2 = read[Spore[Boolean]](json) + assert(true == spore2.unwrap()) + } + + test("testSerializeDeadCodeWithCtx") { + val json = """{"tag":"WithCtx","fun":{"tag":"Body","kind":0,"className":"spores.SporeSerializationTestsDeadCode$IntCtxToStringSpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":7}}""" + val spore2 = read[Spore[String]](json) + assert("7" == spore2.unwrap()) + } + } +} diff --git a/spores3/test/src/spores/upickle/test/PickleTests.scala b/spores3/test/src/spores/upickle/test/PickleTests.scala index aea2af7..99b3d6a 100644 --- a/spores3/test/src/spores/upickle/test/PickleTests.scala +++ b/spores3/test/src/spores/upickle/test/PickleTests.scala @@ -13,7 +13,7 @@ object PickleTests extends TestSuite { val tests = Tests { test("testReflection") { val b = Reflection.loadModuleFieldValue[SporeBuilder[Int => Int => Int]]("spores.pickle.test.MySpore$") - val fun = b.fun + val fun = b.body val res = fun(12)(3) assert(16 == res) } @@ -24,7 +24,7 @@ object PickleTests extends TestSuite { // pickle spore val pickled = write(spore) - assert(pickled == """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedObject","className":"spores.pickle.test.MySpore$"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"12","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$IntRW$"}}}""") + assert(pickled == """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.pickle.test.MySpore$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$IntRW$"},"value":12}}""") // unpickle spore val unpickled = read[Spore[Int => Int]](pickled) @@ -36,7 +36,7 @@ object PickleTests extends TestSuite { val spore: Spore[Int => Int] = SporeWithoutEnv.build() val pickled = write(spore) - assert(pickled == """{"$type":"spores.Packed.PackedObject","className":"spores.pickle.test.SporeWithoutEnv$"}""") + assert(pickled == """{"tag":"Body","kind":0,"className":"spores.pickle.test.SporeWithoutEnv$"}""") val unpickled = read[Spore[Int => Int]](pickled) assert(4 == unpickled.unwrap()(3)) @@ -47,7 +47,7 @@ object PickleTests extends TestSuite { val spore: Spore[List[String] => List[String]] = AppendString.build().withEnv("three") val pickled = write(spore) - assert(pickled == """{"$type":"spores.Packed.PackedWithEnv","packed":{"$type":"spores.Packed.PackedObject","className":"spores.pickle.test.AppendString$"},"packedEnv":{"$type":"spores.Packed.PackedEnv","env":"\"three\"","rw":{"$type":"spores.Packed.PackedObject","className":"spores.ReadWriters$StringRW$"}}}""") + assert(pickled == """{"tag":"WithEnv","fun":{"tag":"Body","kind":0,"className":"spores.pickle.test.AppendString$"},"env":{"tag":"Val","ev":{"tag":"Body","kind":0,"className":"spores.ReadWriters$StringRW$"},"value":"three"}}""") val unpickled = read[Spore[List[String] => List[String]]](pickled) val l3 = List("four")