Skip to content

Commit

Permalink
Make DynamicValue.fromSchemaAndValue and codecs stack safe (#389)
Browse files Browse the repository at this point in the history
* Make DynamicValue.fromSchemaAndValue stack safe

* ScalaFix

* 2.12 fix

* Deprecation fix

* Generic stack safe schema&value processing, wip

* Stack safe protobuf encoder

* Thrift fixes

* Scalafix

* Side-effecting thrift encoder

* Stack safe thrift decoder

* Cleanup

* Stack safe protobuf decoder, WIP

* Work on stack-safe protobuf codec

* Map fix

* Protobuf and thrift fixes

* Cleanup

* scalafix

* Format
  • Loading branch information
vigoo authored Nov 7, 2022
1 parent 0e09815 commit 94ae4cb
Show file tree
Hide file tree
Showing 12 changed files with 4,114 additions and 3,836 deletions.
2 changes: 0 additions & 2 deletions .sbtopts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
-J-Xss512M

-J-Xms5120m
-J-Xmx5120m
-J-XX:MaxMetaspaceSize=1024m
Expand Down
163 changes: 90 additions & 73 deletions tests/shared/src/test/scala-2/zio/schema/DynamicValueSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,80 +10,97 @@ object DynamicValueSpec extends ZIOSpecDefault {

def spec: Spec[Environment, Any] =
suite("DynamicValueSpec")(
suite("Primitives")(primitiveTests: _*),
test("round-trips Records") {
check(SchemaGen.anyRecordOfRecordsAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
suite("round-trip")(
suite("Primitives")(primitiveTests: _*),
test("round-trips Records") {
check(SchemaGen.anyRecordOfRecordsAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Enumerations") {
check(SchemaGen.anyEnumerationAndValue) {
case (schema, a) =>
assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Eithers") {
check(SchemaGen.anyEitherAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Tuples") {
check(SchemaGen.anyTupleAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Optionals") {
check(SchemaGen.anyOptionalAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Transform") {
check(SchemaGen.anyTransformAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips CaseClass") {
check(SchemaGen.anyCaseClassAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Enum") {
check(SchemaGen.anyEnumAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips any un-nested schema") {
check(SchemaGen.anyLeafAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips any nested schema") {
check(SchemaGen.anyTree(1).flatMap(s => DynamicValueGen.anyDynamicValueOfSchema(s).map(s -> _))) {
case (schema, dynamic) =>
assert(schema.fromDynamic(dynamic))(isRight)
}
},
test("round-trips recursive data types") {
check(SchemaGen.anyRecursiveTypeAndValue) {
case (schema, a) =>
assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips sequence") {
check(SchemaGen.anySequenceAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips set") {
check(SchemaGen.anySetAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips map") {
check(SchemaGen.anyMapAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
}
},
test("round-trips Enumerations") {
check(SchemaGen.anyEnumerationAndValue) {
case (schema, a) =>
assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Eithers") {
check(SchemaGen.anyEitherAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Tuples") {
check(SchemaGen.anyTupleAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Optionals") {
check(SchemaGen.anyOptionalAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Transform") {
check(SchemaGen.anyTransformAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips CaseClass") {
check(SchemaGen.anyCaseClassAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips Enum") {
check(SchemaGen.anyEnumAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips any un-nested schema") {
check(SchemaGen.anyLeafAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips any nested schema") {
check(SchemaGen.anyTree(1).flatMap(s => DynamicValueGen.anyDynamicValueOfSchema(s).map(s -> _))) {
case (schema, dynamic) =>
assert(schema.fromDynamic(dynamic))(isRight)
}
},
test("round-trips recursive data types") {
check(SchemaGen.anyRecursiveTypeAndValue) {
case (schema, a) =>
assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips sequence") {
check(SchemaGen.anySequenceAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips set") {
check(SchemaGen.anySetAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
},
test("round-trips map") {
check(SchemaGen.anyMapAndValue) {
case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a)))
}
}
),
suite("stack safety")(
test("fromSchemaAndValue is stack safe") {
check(Json.genDeep) { json =>
val _ = DynamicValue.fromSchemaAndValue(Json.schema, json)
assertCompletes
}
} @@ TestAspect.sized(100),
test("toTyped is stack safe") {
check(Json.genDeep) { json =>
val dyn = DynamicValue.fromSchemaAndValue(Json.schema, json)
val json2 = dyn.toTypedValue(Json.schema)
assertTrue(json2 == Right(json))
}
} @@ TestAspect.sized(250)
)
)

val primitiveTests: List[Spec[Sized with TestConfig, Nothing]] = schemasAndGens.map {
Expand Down
18 changes: 18 additions & 0 deletions tests/shared/src/test/scala-2/zio/schema/SchemaGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,18 @@ object SchemaGen {
keys <- Gen.setOfN(3)(Gen.string)
values <- Gen.setOfN(3)(leafGen)
} yield JObject(keys.zip(values).toList)

val genDeep: Gen[Sized, Json] =
Gen.sized { max =>
Gen.bounded(1, max) { depth =>
leafGen.map { leaf =>
(1 to depth).foldLeft(leaf) {
case (gen, n) =>
JObject(List(n.toString -> gen))
}
}
}
}
}

lazy val anyRecursiveType: Gen[Sized, Schema[_]] =
Expand All @@ -643,6 +655,12 @@ object SchemaGen {
value <- Json.gen
} yield (schema, value)

lazy val anyDeepRecursiveTypeAndValue: Gen[Sized, SchemaAndValue[_]] =
for {
schema <- Gen.const(Schema[Json])
value <- Json.genDeep
} yield (schema, value)

lazy val anyDynamic: Gen[Any, Schema[DynamicValue]] = Gen.const(Schema.dynamicValue)

case class SchemaTest[A](name: String, schema: StandardType[A], gen: Gen[Sized, A])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,12 @@ object JsonCodecSpec extends ZIOSpecDefault {
assertEncodesThenDecodes(schema, value)
}
},
test("deep recursive data type") {
check(SchemaGen.anyDeepRecursiveTypeAndValue) {
case (schema, value) =>
assertEncodesThenDecodes(schema, value)
}
} @@ TestAspect.sized(200),
suite("dynamic")(
test("dynamic int") {
check(
Expand Down
Loading

0 comments on commit 94ae4cb

Please sign in to comment.