CORDA-1747 - External serializes break for generic types (#3541)

* CORDA-1747 - External serializes break for generic types

* Review comments

* review comments
This commit is contained in:
Katelyn Baker
2018-07-10 17:48:06 +01:00
committed by GitHub
parent ab38c7a944
commit f4426ef172
5 changed files with 218 additions and 28 deletions

View File

@ -3,6 +3,7 @@ package net.corda.serialization.internal.amqp
import org.junit.Test
import net.corda.core.serialization.ClassWhitelist
import net.corda.core.serialization.SerializationCustomSerializer
import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.testutils.deserializeAndReturnEnvelope
import net.corda.serialization.internal.amqp.testutils.deserialize
import net.corda.serialization.internal.amqp.testutils.serializeAndReturnSchema
@ -12,9 +13,20 @@ import org.assertj.core.api.Assertions
import java.io.NotSerializableException
import kotlin.test.assertEquals
class CorDappSerializerTests {
data class NeedsProxy (val a: String)
data class NeedsProxy(val a: String)
private fun proxyFactory(
serializers: List<SerializationCustomSerializer<*, *>>
) = SerializerFactory(
AllWhitelist,
ClassLoader.getSystemClassLoader(),
onlyCustomSerializers = true
).apply {
serializers.forEach {
registerExternal(CorDappCustomSerializer(it, this))
}
}
class NeedsProxyProxySerializer : SerializationCustomSerializer<NeedsProxy, NeedsProxyProxySerializer.Proxy> {
data class Proxy(val proxy_a_: String)
@ -25,7 +37,7 @@ class CorDappSerializerTests {
// Standard proxy serializer used internally, here for comparison purposes
class InternalProxySerializer(factory: SerializerFactory) :
CustomSerializer.Proxy<NeedsProxy, InternalProxySerializer.Proxy> (
CustomSerializer.Proxy<NeedsProxy, InternalProxySerializer.Proxy>(
NeedsProxy::class.java,
InternalProxySerializer.Proxy::class.java,
factory) {
@ -48,14 +60,14 @@ class CorDappSerializerTests {
val msg = "help"
proxyFactory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory))
internalProxyFactory.register (InternalProxySerializer(internalProxyFactory))
proxyFactory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), proxyFactory))
internalProxyFactory.register(InternalProxySerializer(internalProxyFactory))
val needsProxy = NeedsProxy(msg)
val bAndSProxy = SerializationOutput(proxyFactory).serializeAndReturnSchema (needsProxy)
val bAndSInternal = SerializationOutput(internalProxyFactory).serializeAndReturnSchema (needsProxy)
val bAndSDefault = SerializationOutput(defaultFactory).serializeAndReturnSchema (needsProxy)
val bAndSProxy = SerializationOutput(proxyFactory).serializeAndReturnSchema(needsProxy)
val bAndSInternal = SerializationOutput(internalProxyFactory).serializeAndReturnSchema(needsProxy)
val bAndSDefault = SerializationOutput(defaultFactory).serializeAndReturnSchema(needsProxy)
val objFromDefault = DeserializationInput(defaultFactory).deserializeAndReturnEnvelope(bAndSDefault.obj)
val objFromInternal = DeserializationInput(internalProxyFactory).deserializeAndReturnEnvelope(bAndSInternal.obj)
@ -68,14 +80,14 @@ class CorDappSerializerTests {
@Test
fun proxiedTypeIsNested() {
data class A (val a: Int, val b: NeedsProxy)
data class A(val a: Int, val b: NeedsProxy)
val factory = testDefaultFactory()
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
factory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
val bAndS = SerializationOutput(factory).serializeAndReturnSchema (A(tv1, NeedsProxy(tv2)))
val bAndS = SerializationOutput(factory).serializeAndReturnSchema(A(tv1, NeedsProxy(tv2)))
val objFromDefault = DeserializationInput(factory).deserializeAndReturnEnvelope(bAndS.obj)
@ -85,7 +97,7 @@ class CorDappSerializerTests {
@Test
fun testWithWhitelistNotAllowed() {
data class A (val a: Int, val b: NeedsProxy)
data class A(val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
private val allowedClasses = emptySet<String>()
@ -94,7 +106,7 @@ class CorDappSerializerTests {
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
factory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
@ -105,7 +117,7 @@ class CorDappSerializerTests {
@Test
fun testWithWhitelistAllowed() {
data class A (val a: Int, val b: NeedsProxy)
data class A(val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
private val allowedClasses = setOf<String>(
@ -116,7 +128,7 @@ class CorDappSerializerTests {
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
factory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
@ -131,7 +143,7 @@ class CorDappSerializerTests {
// custom serializer bypasses the whitelist
@Test
fun testWithWhitelistAllowedOuterOnly() {
data class A (val a: Int, val b: NeedsProxy)
data class A(val a: Int, val b: NeedsProxy)
class WL : ClassWhitelist {
// explicitly don't add NeedsProxy
@ -141,7 +153,7 @@ class CorDappSerializerTests {
}
val factory = SerializerFactory(WL(), ClassLoader.getSystemClassLoader())
factory.registerExternal (CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
factory.registerExternal(CorDappCustomSerializer(NeedsProxyProxySerializer(), factory))
val tv1 = 100
val tv2 = "pants schmants"
@ -151,4 +163,136 @@ class CorDappSerializerTests {
assertEquals(tv1, obj.a)
assertEquals(tv2, obj.b.a)
}
data class NeedsProxyGen<T>(val a: T)
class NeedsProxyGenProxySerializer :
SerializationCustomSerializer<NeedsProxyGen<*>, NeedsProxyGenProxySerializer.Proxy> {
data class Proxy(val proxy_a_: Any?)
override fun fromProxy(proxy: Proxy) = NeedsProxyGen(proxy.proxy_a_)
override fun toProxy(obj: NeedsProxyGen<*>) = Proxy(obj.a)
}
// Tests CORDA-1747
@Test
fun proxiedGeneric() {
val proxyFactory = proxyFactory(listOf(NeedsProxyGenProxySerializer()))
val msg = "help"
val blob = SerializationOutput(proxyFactory).serialize(NeedsProxyGen(msg))
val objFromProxy = DeserializationInput(proxyFactory).deserialize(blob)
assertEquals(msg, objFromProxy.a)
}
// Need an interface to restrict the generic to in the following test
interface Bound {
fun wibbleIt(): String
}
// test class to be serialized whose generic arg is restricted to instances
// of the Bound interface declared above
data class NeedsProxyGenBounded<T : Bound>(val a: T)
// Proxy for our test class
class NeedsProxyGenBoundedProxySerializer :
SerializationCustomSerializer<NeedsProxyGenBounded<*>,
NeedsProxyGenBoundedProxySerializer.Proxy> {
data class Proxy(val proxy_a_: Bound)
override fun fromProxy(proxy: Proxy) = NeedsProxyGenBounded(proxy.proxy_a_)
override fun toProxy(obj: NeedsProxyGenBounded<*>) = Proxy(obj.a)
}
// Since we need a value for our test class that implements the interface
// we're restricting its generic property to, this is that class.
data class HasWibble(val a: String) : Bound {
override fun wibbleIt() = "wibble it, just a little bit!."
}
// Because we're enforcing all classes have proxy serializers we
// have to implement this to avoid the factory erroneously failing
class HasWibbleProxy :
SerializationCustomSerializer<HasWibble, HasWibbleProxy.Proxy> {
data class Proxy(val proxy_a_: String)
override fun fromProxy(proxy: Proxy) = HasWibble(proxy.proxy_a_)
override fun toProxy(obj: HasWibble) = Proxy(obj.a)
}
// Tests CORDA-1747 - Finally the actual bound generics test, on failure it will throw
@Test
fun proxiedBoundedGeneric() {
val proxyFactory = proxyFactory(listOf(NeedsProxyGenBoundedProxySerializer(), HasWibbleProxy()))
val blob = SerializationOutput(proxyFactory).serialize(NeedsProxyGenBounded(HasWibble("A")))
val objFromProxy = DeserializationInput(proxyFactory).deserialize(blob)
assertEquals("A", objFromProxy.a.a)
}
data class NeedsProxyGenContainer<T>(val a: List<T>)
class NeedsProxyGenContainerProxySerializer :
SerializationCustomSerializer<NeedsProxyGenContainer<*>,
NeedsProxyGenContainerProxySerializer.Proxy> {
data class Proxy(val proxy_a_: List<*>)
override fun fromProxy(proxy: Proxy) = NeedsProxyGenContainer(proxy.proxy_a_)
override fun toProxy(obj: NeedsProxyGenContainer<*>) = Proxy(obj.a)
}
// Tests CORDA-1747
@Test
fun proxiedGenericContainer() {
val proxyFactory = proxyFactory(listOf(NeedsProxyGenContainerProxySerializer()))
val blob1 = SerializationOutput(proxyFactory).serialize(NeedsProxyGenContainer(listOf(1, 2, 3)))
val obj1 = DeserializationInput(proxyFactory).deserialize(blob1)
assertEquals(listOf(1, 2, 3), obj1.a)
val blob2 = SerializationOutput(proxyFactory).serialize(NeedsProxyGenContainer(listOf("1", "2", "3")))
val obj2 = DeserializationInput(proxyFactory).deserialize(blob2)
assertEquals(listOf("1", "2", "3"), obj2.a)
val blob3 = SerializationOutput(proxyFactory).serialize(NeedsProxyGenContainer(listOf("1", 2, "3")))
val obj3 = DeserializationInput(proxyFactory).deserialize(blob3)
assertEquals(listOf("1", 2, "3"), obj3.a)
}
open class Base<T>(val a: T)
class Derived<T>(a: T, val b: String) : Base<T>(a)
class BaseProxy :
SerializationCustomSerializer<Base<*>, BaseProxy.Proxy> {
data class Proxy(val proxy_a_: Any?)
override fun fromProxy(proxy: Proxy) = Base(proxy.proxy_a_)
override fun toProxy(obj: Base<*>) = Proxy(obj.a)
}
class DerivedProxy :
SerializationCustomSerializer<Derived<*>, DerivedProxy.Proxy> {
data class Proxy(val proxy_a_: Any?, val proxy_b_: String)
override fun fromProxy(proxy: Proxy) = Derived(proxy.proxy_a_, proxy.proxy_b_)
override fun toProxy(obj: Derived<*>) = Proxy(obj.a, obj.b)
}
// Tests CORDA-1747
@Test
fun proxiedInheritableGenerics() {
val proxyFactory = proxyFactory(listOf(BaseProxy(), DerivedProxy()))
val blob1 = SerializationOutput(proxyFactory).serialize(Base(100L))
DeserializationInput(proxyFactory).deserialize(blob1)
val blob2 = SerializationOutput(proxyFactory).serialize(Derived(100L, "Hey pants"))
DeserializationInput(proxyFactory).deserialize(blob2)
}
}

View File

@ -17,9 +17,14 @@ import java.io.NotSerializableException
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
fun testDefaultFactoryNoEvolution(): SerializerFactory {
return SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(), evolutionSerializerGetter = EvolutionSerializerGetterTesting())
return SerializerFactory(
AllWhitelist,
ClassLoader.getSystemClassLoader(),
evolutionSerializerGetter = EvolutionSerializerGetterTesting())
}
fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader())
class TestSerializationOutput(