From 41220de816ddd11832af36a9d5851447a00ad605 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Mon, 11 Dec 2017 20:01:46 +0000 Subject: [PATCH] CORDA-855 - Fix for fingerprinting generics in AMQP * Undo refactor --- .../amqp/EnumEvolutionSerializer.kt | 4 +- .../serialization/amqp/EvolutionSerializer.kt | 49 +++++++++++++ .../internal/serialization/amqp/Schema.kt | 67 +++++++++++------- .../serialization/amqp/SerializerFactory.kt | 69 ++++++++++--------- .../amqp/JavaSerialiseEnumTests.java | 4 +- .../amqp/JavaSerializationOutputTests.java | 7 +- .../amqp/ListsSerializationJavaTest.java | 4 +- .../serialization/amqp/AMQPTestUtils.kt | 2 + .../amqp/DeserializeAndReturnEnvelopeTests.kt | 4 +- .../serialization/amqp/DeserializeMapTests.kt | 2 +- .../DeserializeNeedingCarpentryOfEnumsTest.kt | 4 +- ...erializeNeedingCarpentrySimpleTypesTest.kt | 4 +- .../amqp/DeserializeNeedingCarpentryTests.kt | 2 +- .../amqp/DeserializeSimpleTypesTests.kt | 4 +- .../internal/serialization/amqp/EnumTests.kt | 2 +- .../serialization/amqp/GenericsTests.kt | 69 +++++++++++++++++-- .../amqp/SerializationOutputTests.kt | 6 +- .../amqp/SerializeAndReturnSchemaTest.kt | 2 +- ...ticInitialisationOfSerializedObjectTest.kt | 4 +- 19 files changed, 223 insertions(+), 86 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt index 88639d466b..6e96d4bad8 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumEvolutionSerializer.kt @@ -106,7 +106,7 @@ class EnumEvolutionSerializer( // to the name as it exists. We want to test any new constants have been added to the end // of the enum class val serialisedOrds = ((schemas.schema.types.find { it.name == old.name } as RestrictedType).choices - .associateBy ({ it.value.toInt() }, { conversions[it.name] })) + .associateBy({ it.value.toInt() }, { conversions[it.name] })) if (ordinals.filterNot { serialisedOrds[it.value] == it.key }.isNotEmpty()) { throw NotSerializableException("Constants have been reordered, additions must be appended to the end") @@ -133,4 +133,4 @@ class EnumEvolutionSerializer( override fun writeObject(obj: Any, data: Data, type: Type, output: SerializationOutput) { throw UnsupportedOperationException("It should be impossible to write an evolution serializer") } -} \ No newline at end of file +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt index 9293729306..e2d9ce671a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/EvolutionSerializer.kt @@ -130,3 +130,52 @@ class EvolutionSerializer( } } +/** + * Instances of this type are injected into a [SerializerFactory] at creation time to dictate the + * behaviour of evolution within that factory. Under normal circumstances this will simply + * be an object that returns an [EvolutionSerializer]. Of course, any implementation that + * extends this class can be written to invoke whatever behaviour is desired. + */ +abstract class EvolutionSerializerGetterBase { + abstract fun getEvolutionSerializer( + factory: SerializerFactory, + typeNotation: TypeNotation, + newSerializer: AMQPSerializer, + schemas: SerializationSchemas): AMQPSerializer +} + +/** + * The normal use case for generating an [EvolutionSerializer]'s based on the differences + * between the received schema and the class as it exists now on the class path, + */ +class EvolutionSerializerGetter : EvolutionSerializerGetterBase() { + override fun getEvolutionSerializer(factory: SerializerFactory, + typeNotation: TypeNotation, + newSerializer: AMQPSerializer, + schemas: SerializationSchemas): AMQPSerializer = + factory.serializersByDescriptor.computeIfAbsent(typeNotation.descriptor.name!!) { + when (typeNotation) { + is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, factory) + is RestrictedType -> EnumEvolutionSerializer.make(typeNotation, newSerializer, factory, schemas) + } + } +} + +/** + * An implementation of [EvolutionSerializerGetterBase] that disables all evolution within a + * [SerializerFactory]. This is most useful in testing where it is known that evolution should not be + * occurring and where bugs may be hidden by transparent invocation of an [EvolutionSerializer]. This + * prevents that by simply throwing an exception whenever such a serializer is requested. + */ +class EvolutionSerializerGetterTesting : EvolutionSerializerGetterBase() { + override fun getEvolutionSerializer(factory: SerializerFactory, + typeNotation: TypeNotation, + newSerializer: AMQPSerializer, + schemas: SerializationSchemas): AMQPSerializer { + throw NotSerializableException("No evolution should be occurring\n" + + " ${typeNotation.name}\n" + + " ${typeNotation.descriptor.name}\n" + + " ${newSerializer.type.typeName}\n" + + " ${newSerializer.typeDescriptor}\n\n${schemas.schema}") + } +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt index 79b2a57b93..dcfdfe9b7a 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/Schema.kt @@ -348,17 +348,50 @@ private fun Hasher.fingerprintWithCustomSerializerOrElse(factory: SerializerFact // This method concatentates various elements of the types recursively as unencoded strings into the hasher, effectively // creating a unique string for a type which we then hash in the calling function above. -private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: MutableSet, hasher: Hasher, factory: SerializerFactory): Hasher { - return if (type in alreadySeen) { +private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: MutableSet, + hasher: Hasher, factory: SerializerFactory, offset: String = ""): Hasher { + // We don't include Example and Example where type is ? or T in this otherwise we + // generate different fingerprints for class Outer(val a: Inner) when serialising + // and deserializing (assuming deserialization is occurring in a factory that didn't + // serialise the object in the first place (and thus the cache lookup fails). This is also + // true of Any, where we need Example and Example to have the same fingerprint + return if (type in alreadySeen && (type !is SerializerFactory.AnyType) && (type !is TypeVariable<*>)) { hasher.putUnencodedChars(ALREADY_SEEN_HASH) } else { alreadySeen += type try { when (type) { - is SerializerFactory.AnyType -> hasher.putUnencodedChars(ANY_TYPE_HASH) + is ParameterizedType -> { + // Hash the rawType + params + val clazz = type.rawType as Class<*> + + val startingHash = if (isCollectionOrMap(clazz)) { + hasher.putUnencodedChars(clazz.name) + } else { + hasher.fingerprintWithCustomSerializerOrElse(factory, clazz, type) { + fingerprintForObject(type, type, alreadySeen, hasher, factory, "$offset ") + } + } + + // ... and concatentate the type data for each parameter type. + type.actualTypeArguments.fold(startingHash) { orig, paramType -> + fingerprintForType(paramType, type, alreadySeen, orig, factory, "$offset ") + } + } + // Treat generic types as "any type" to prevent fingerprint mismatch. This case we fall into when + // looking at A and B from Example (remember we call this function recursively). When + // serialising a concrete example of the type we have A and B which are TypeVariables<*>'s but + // when deserializing we only have the wilcard placeholder ?, or AnyType + // + // Note, TypeVariable<*> used to be encided as TYPE_VARIABLE_HASH but that again produces a + // differing fingerprint on serialisation and deserialization + is SerializerFactory.AnyType, + is TypeVariable<*> -> { + hasher.putUnencodedChars("?").putUnencodedChars(ANY_TYPE_HASH) + } is Class<*> -> { if (type.isArray) { - fingerprintForType(type.componentType, contextType, alreadySeen, hasher, factory).putUnencodedChars(ARRAY_HASH) + fingerprintForType(type.componentType, contextType, alreadySeen, hasher, factory, "$offset ").putUnencodedChars(ARRAY_HASH) } else if (SerializerFactory.isPrimitive(type)) { hasher.putUnencodedChars(type.name) } else if (isCollectionOrMap(type)) { @@ -377,31 +410,15 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta // to the CorDapp but maybe reference to the JAR in the short term. hasher.putUnencodedChars(type.name) } else { - fingerprintForObject(type, type, alreadySeen, hasher, factory) + fingerprintForObject(type, type, alreadySeen, hasher, factory, "$offset ") } } } } - is ParameterizedType -> { - // Hash the rawType + params - val clazz = type.rawType as Class<*> - val startingHash = if (isCollectionOrMap(clazz)) { - hasher.putUnencodedChars(clazz.name) - } else { - hasher.fingerprintWithCustomSerializerOrElse(factory, clazz, type) { - fingerprintForObject(type, type, alreadySeen, hasher, factory) - } - } - // ... and concatentate the type data for each parameter type. - type.actualTypeArguments.fold(startingHash) { orig, paramType -> - fingerprintForType(paramType, type, alreadySeen, orig, factory) - } - } // Hash the element type + some array hash is GenericArrayType -> fingerprintForType(type.genericComponentType, contextType, alreadySeen, - hasher, factory).putUnencodedChars(ARRAY_HASH) + hasher, factory, "$offset ").putUnencodedChars(ARRAY_HASH) // TODO: include bounds - is TypeVariable<*> -> hasher.putUnencodedChars(type.name).putUnencodedChars(TYPE_VARIABLE_HASH) is WildcardType -> hasher.putUnencodedChars(type.typeName).putUnencodedChars(WILDCARD_TYPE_HASH) else -> throw NotSerializableException("Don't know how to hash") } @@ -416,15 +433,15 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta private fun isCollectionOrMap(type: Class<*>) = (Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type)) && !EnumSet::class.java.isAssignableFrom(type) -private fun fingerprintForObject(type: Type, contextType: Type?, alreadySeen: MutableSet, hasher: Hasher, factory: SerializerFactory): Hasher { +private fun fingerprintForObject(type: Type, contextType: Type?, alreadySeen: MutableSet, hasher: Hasher, factory: SerializerFactory, offset: String = ""): Hasher { // Hash the class + properties + interfaces val name = type.asClass()?.name ?: throw NotSerializableException("Expected only Class or ParameterizedType but found $type") propertiesForSerialization(constructorForDeserialization(type), contextType ?: type, factory).getters .fold(hasher.putUnencodedChars(name)) { orig, prop -> - fingerprintForType(prop.resolvedType, type, alreadySeen, orig, factory) + fingerprintForType(prop.resolvedType, type, alreadySeen, orig, factory, "$offset ") .putUnencodedChars(prop.name) .putUnencodedChars(if (prop.mandatory) NOT_NULLABLE_HASH else NULLABLE_HASH) } - interfacesForSerialization(type, factory).map { fingerprintForType(it, type, alreadySeen, hasher, factory) } + interfacesForSerialization(type, factory).map { fingerprintForType(it, type, alreadySeen, hasher, factory, "$offset ") } return hasher } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt index a6c587bc6e..2a7c6557bb 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializerFactory.kt @@ -20,6 +20,10 @@ data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typ /** * Factory of serializers designed to be shared across threads and invocations. + * + * @property evolutionSerializerGetter controls how evolution serializers are generated by the factory. The normal + * use case is an [EvolutionSerializer] type is returned. However, in some scenarios, primarily testing, this + * can be altered to fit the requirements of the test. */ // TODO: support for intern-ing of deserialized objects for some core types (e.g. PublicKey) for memory efficiency // TODO: maybe support for caching of serialized form of some core types for performance @@ -33,9 +37,12 @@ data class FactorySchemaAndDescriptor(val schemas: SerializationSchemas, val typ // TODO: need to rethink matching of constructor to properties in relation to implementing interfaces and needing those properties etc. // TODO: need to support super classes as well as interfaces with our current code base... what's involved? If we continue to ban, what is the impact? @ThreadSafe -open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { +open class SerializerFactory( + val whitelist: ClassWhitelist, + cl: ClassLoader, + private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter()) { private val serializersByType = ConcurrentHashMap>() - private val serializersByDescriptor = ConcurrentHashMap>() + val serializersByDescriptor = ConcurrentHashMap>() private val customSerializers = CopyOnWriteArrayList() val transformsCache = ConcurrentHashMap>>() @@ -44,17 +51,9 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { val classloader: ClassLoader get() = classCarpenter.classloader - private fun getEvolutionSerializer( - typeNotation: TypeNotation, - newSerializer: AMQPSerializer, - schemas: SerializationSchemas): AMQPSerializer { - return serializersByDescriptor.computeIfAbsent(typeNotation.descriptor.name!!) { - when (typeNotation) { - is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, this) - is RestrictedType -> EnumEvolutionSerializer.make(typeNotation, newSerializer, this, schemas) - } - } - } + private fun getEvolutionSerializer(typeNotation: TypeNotation, newSerializer: AMQPSerializer, + schemas: SerializationSchemas) + = evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas) /** * Look up, and manufacture if necessary, a serializer for the given type. @@ -93,7 +92,9 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { whitelist.requireWhitelisted(actualType) EnumSerializer(actualType, actualClass ?: declaredClass, this) } - else -> makeClassSerializer(actualClass ?: declaredClass, actualType, declaredType) + else -> { + makeClassSerializer(actualClass ?: declaredClass, actualType, declaredType) + } } serializersByDescriptor.putIfAbsent(serializer.typeDescriptor, serializer) @@ -102,23 +103,23 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { } /** - * Try and infer concrete types for any generics type variables for the actual class encountered, based on the declared - * type. + * Try and infer concrete types for any generics type variables for the actual class encountered, + * based on the declared type. */ // TODO: test GenericArrayType - private fun inferTypeVariables(actualClass: Class<*>?, declaredClass: Class<*>, declaredType: Type): Type? = - when (declaredType) { - is ParameterizedType -> inferTypeVariables(actualClass, declaredClass, declaredType) - // Nothing to infer, otherwise we'd have ParameterizedType - is Class<*> -> actualClass - is GenericArrayType -> { - val declaredComponent = declaredType.genericComponentType - inferTypeVariables(actualClass?.componentType, declaredComponent.asClass()!!, declaredComponent)?.asArray() - } - is TypeVariable<*> -> actualClass - is WildcardType -> actualClass - else -> null - } + private fun inferTypeVariables(actualClass: Class<*>?, declaredClass: Class<*>, + declaredType: Type) : Type? = when (declaredType) { + is ParameterizedType -> inferTypeVariables(actualClass, declaredClass, declaredType) + // Nothing to infer, otherwise we'd have ParameterizedType + is Class<*> -> actualClass + is GenericArrayType -> { + val declaredComponent = declaredType.genericComponentType + inferTypeVariables(actualClass?.componentType, declaredComponent.asClass()!!, declaredComponent)?.asArray() + } + is TypeVariable<*> -> actualClass + is WildcardType -> actualClass + else -> null + } /** * Try and infer concrete types for any generics type variables for the actual class encountered, based on the declared @@ -214,9 +215,9 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { try { val serialiser = processSchemaEntry(typeNotation) - // if we just successfully built a serialiser for the type but the type fingerprint + // if we just successfully built a serializer for the type but the type fingerprint // doesn't match that of the serialised object then we are dealing with different - // instance of the class, as such we need to build an EvolutionSerialiser + // instance of the class, as such we need to build an EvolutionSerializer if (serialiser.typeDescriptor != typeNotation.descriptor.name) { getEvolutionSerializer(typeNotation, serialiser, schemaAndDescriptor.schemas) } @@ -331,13 +332,15 @@ open class SerializerFactory(val whitelist: ClassWhitelist, cl: ClassLoader) { private val namesOfPrimitiveTypes: Map> = primitiveTypeNames.map { it.value to it.key }.toMap() - fun nameForType(type: Type): String = when (type) { + fun nameForType(type: Type): String = when (type) { is Class<*> -> { primitiveTypeName(type) ?: if (type.isArray) { "${nameForType(type.componentType)}${if (type.componentType.isPrimitive) "[p]" else "[]"}" } else type.name } - is ParameterizedType -> "${nameForType(type.rawType)}<${type.actualTypeArguments.joinToString { nameForType(it) }}>" + is ParameterizedType -> { + "${nameForType(type.rawType)}<${type.actualTypeArguments.joinToString { nameForType(it) }}>" + } is GenericArrayType -> "${nameForType(type.genericComponentType)}[]" is WildcardType -> "?" is TypeVariable<*> -> "?" diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java index e0b65ad27c..a64f9c3d9e 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerialiseEnumTests.java @@ -29,7 +29,9 @@ public class JavaSerialiseEnumTests { public void testJavaConstructorAnnotations() throws NotSerializableException { Bra bra = new Bra(Bras.UNDERWIRE); - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); + EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), + evolutionSerialiserGetter); SerializationOutput ser = new SerializationOutput(factory1); SerializedBytes bytes = ser.serialize(bra); } diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java index 1743c645b4..4379718003 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/JavaSerializationOutputTests.java @@ -172,8 +172,11 @@ public class JavaSerializationOutputTests { } private Object serdes(Object obj) throws NotSerializableException { - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); - SerializerFactory factory2 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); + EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), + evolutionSerialiserGetter); + SerializerFactory factory2 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), + evolutionSerialiserGetter); SerializationOutput ser = new SerializationOutput(factory1); SerializedBytes bytes = ser.serialize(obj); diff --git a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java index 6085546c37..c77f452b33 100644 --- a/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java +++ b/node-api/src/test/java/net/corda/nodeapi/internal/serialization/amqp/ListsSerializationJavaTest.java @@ -125,7 +125,9 @@ public class ListsSerializationJavaTest { // Have to have own version as Kotlin inline functions cannot be easily called from Java private static void assertEqualAfterRoundTripSerialization(T container, Class clazz) throws Exception { - SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); + EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter(); + SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(), + evolutionSerialiserGetter); SerializationOutput ser = new SerializationOutput(factory1); SerializedBytes bytes = ser.serialize(container); DeserializationInput des = new DeserializationInput(factory1); diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPTestUtils.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPTestUtils.kt index 8bbe597b26..d7701aed52 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPTestUtils.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPTestUtils.kt @@ -5,6 +5,8 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.EmptyWhitelist fun testDefaultFactory() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) +fun testDefaultFactoryNoEvolution() = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader(), + EvolutionSerializerGetterTesting()) fun testDefaultFactoryWithWhitelist() = SerializerFactory(EmptyWhitelist, ClassLoader.getSystemClassLoader()) class TestSerializationOutput( diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt index 7b9c7a4e39..d793c9f0d2 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeAndReturnEnvelopeTests.kt @@ -11,13 +11,14 @@ class DeserializeAndReturnEnvelopeTests { @Suppress("NOTHING_TO_INLINE") inline private fun classTestName(clazz: String) = "${this.javaClass.name}\$${testName()}\$$clazz" + val factory = testDefaultFactoryNoEvolution() + @Test fun oneType() { data class A(val a: Int, val b: String) val a = A(10, "20") - val factory = testDefaultFactory() fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz) val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(a)) @@ -33,7 +34,6 @@ class DeserializeAndReturnEnvelopeTests { val b = B(A(10, "20"), 30.0F) - val factory = testDefaultFactory() fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz) val obj = DeserializationInput(factory).deserializeAndReturnEnvelope(serialise(b)) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt index 68e106fd89..2e910bf19c 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeMapTests.kt @@ -12,7 +12,7 @@ class DeserializeMapTests { private const val VERBOSE = false } - private val sf = testDefaultFactory() + private val sf = testDefaultFactoryNoEvolution() @Test fun mapTest() { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt index de33b7cf18..caa3e06c4f 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryOfEnumsTest.kt @@ -18,7 +18,7 @@ class DeserializeNeedingCarpentryOfEnumsTest : AmqpCarpenterBase(AllWhitelist) { // // Setup the test // - val setupFactory = testDefaultFactory() + val setupFactory = testDefaultFactoryNoEvolution() val enumConstants = listOf("AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH", "III", "JJJ").associateBy({ it }, { EnumField() }) @@ -57,7 +57,7 @@ class DeserializeNeedingCarpentryOfEnumsTest : AmqpCarpenterBase(AllWhitelist) { // // Setup the test // - val setupFactory = testDefaultFactory() + val setupFactory = testDefaultFactoryNoEvolution() val enumConstants = listOf("AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH", "III", "JJJ").associateBy({ it }, { EnumField() }) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt index 2ed861152d..002adc5e24 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentrySimpleTypesTest.kt @@ -17,8 +17,8 @@ class DeserializeNeedingCarpentrySimpleTypesTest : AmqpCarpenterBase(AllWhitelis private const val VERBOSE = false } - private val sf = testDefaultFactory() - private val sf2 = testDefaultFactory() + private val sf = testDefaultFactoryNoEvolution() + private val sf2 = testDefaultFactoryNoEvolution() @Test fun singleInt() { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt index fdd981cd08..f54112b2dc 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeNeedingCarpentryTests.kt @@ -27,7 +27,7 @@ class DeserializeNeedingCarpentryTests : AmqpCarpenterBase(AllWhitelist) { private const val VERBOSE = false } - private val sf1 = testDefaultFactory() + private val sf1 = testDefaultFactoryNoEvolution() // Deserialize with whitelisting on to check that `CordaSerializable` annotation present. private val sf2 = testDefaultFactoryWithWhitelist() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt index 4c5c9311ec..6f2dcd3bc2 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/DeserializeSimpleTypesTests.kt @@ -16,8 +16,8 @@ class DeserializeSimpleTypesTests { private const val VERBOSE = false } - val sf1 = testDefaultFactory() - val sf2 = testDefaultFactory() + val sf1 = testDefaultFactoryNoEvolution() + val sf2 = testDefaultFactoryNoEvolution() @Test fun testChar() { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumTests.kt index 60805c994f..cc46273f08 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/EnumTests.kt @@ -65,7 +65,7 @@ class EnumTests { @Suppress("NOTHING_TO_INLINE") inline private fun classTestName(clazz: String) = "${this.javaClass.name}\$${testName()}\$$clazz" - private val sf1 = testDefaultFactory() + private val sf1 = testDefaultFactoryNoEvolution() @Test fun serialiseSimpleTest() { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/GenericsTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/GenericsTests.kt index e9ab19db4f..001ea67929 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/GenericsTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/GenericsTests.kt @@ -3,25 +3,82 @@ package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.serialization.SerializedBytes import net.corda.nodeapi.internal.serialization.AllWhitelist import org.junit.Test +import java.util.concurrent.ConcurrentHashMap import kotlin.test.assertEquals class GenericsTests { + companion object { + val VERBOSE = false + } + + private fun printSeparator() = if(VERBOSE) println ("\n\n-------------------------------------------\n\n") else Unit + + private fun BytesAndSchemas.printSchema() = if (VERBOSE) println ("${this.schema}\n") else Unit + + private fun ConcurrentHashMap>.printKeyToType() { + if (!VERBOSE) return + + forEach { + println ("Key = ${it.key} - ${it.value.type.typeName}") + } + println() + } + + @Test + fun twoDifferntTypesSameParameterizedOuter() { + data class G(val a: A) + + val factory = testDefaultFactoryNoEvolution() + + val bytes1 = SerializationOutput(factory).serializeAndReturnSchema(G("hi")).apply { printSchema() } + + factory.serializersByDescriptor.printKeyToType() + + val bytes2 = SerializationOutput(factory).serializeAndReturnSchema(G(121)).apply { printSchema() } + + factory.serializersByDescriptor.printKeyToType() + + listOf (factory, testDefaultFactory()).forEach { f -> + DeserializationInput(f).deserialize(bytes1.obj).apply { assertEquals("hi", this.a) } + DeserializationInput(f).deserialize(bytes2.obj).apply { assertEquals(121, this.a) } + } + } + + @Test + fun doWeIgnoreMultipleParams() { + data class G1(val a: T) + data class G2(val a: T) + data class Wrapper(val a: Int, val b: G1, val c: G2) + + val factory = testDefaultFactoryNoEvolution() + val factory2 = testDefaultFactoryNoEvolution() + + val bytes = SerializationOutput(factory).serializeAndReturnSchema(Wrapper(1, G1("hi"), G2("poop"))).apply { printSchema() } + printSeparator() + DeserializationInput(factory2).deserialize(bytes.obj) + } @Test fun nestedSerializationOfGenerics() { - data class G(val a: T) - data class Wrapper(val a: Int, val b: G) + data class G(val a: T) + data class Wrapper(val a: Int, val b: G) - val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) - val altContextFactory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) + val factory = testDefaultFactoryNoEvolution() + val altContextFactory = testDefaultFactoryNoEvolution() val ser = SerializationOutput(factory) - val bytes = ser.serializeAndReturnSchema(G("hi")) + val bytes = ser.serializeAndReturnSchema(G("hi")).apply { printSchema() } + + factory.serializersByDescriptor.printKeyToType() assertEquals("hi", DeserializationInput(factory).deserialize(bytes.obj).a) assertEquals("hi", DeserializationInput(altContextFactory).deserialize(bytes.obj).a) - val bytes2 = ser.serializeAndReturnSchema(Wrapper(1, G("hi"))) + val bytes2 = ser.serializeAndReturnSchema(Wrapper(1, G("hi"))).apply { printSchema() } + + factory.serializersByDescriptor.printKeyToType() + + printSeparator() DeserializationInput(factory).deserialize(bytes2.obj).apply { assertEquals(1, a) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt index 9871bff1c6..c6b1eeff4d 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializationOutputTests.kt @@ -169,9 +169,11 @@ class SerializationOutputTests { private inline fun serdes(obj: T, factory: SerializerFactory = SerializerFactory( - AllWhitelist, ClassLoader.getSystemClassLoader()), + AllWhitelist, ClassLoader.getSystemClassLoader(), + EvolutionSerializerGetterTesting()), freshDeserializationFactory: SerializerFactory = SerializerFactory( - AllWhitelist, ClassLoader.getSystemClassLoader()), + AllWhitelist, ClassLoader.getSystemClassLoader(), + EvolutionSerializerGetterTesting()), expectedEqual: Boolean = true, expectDeserializedEqual: Boolean = true): T { val ser = SerializationOutput(factory) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializeAndReturnSchemaTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializeAndReturnSchemaTest.kt index acd8c5b8fe..51248a4d4e 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializeAndReturnSchemaTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/SerializeAndReturnSchemaTest.kt @@ -10,7 +10,7 @@ class SerializeAndReturnSchemaTest { @Suppress("NOTHING_TO_INLINE") inline private fun classTestName(clazz: String) = "${this.javaClass.name}\$${testName()}\$$clazz" - val factory = testDefaultFactory() + val factory = testDefaultFactoryNoEvolution() // just a simple test to verify the internal test extension for serialize does // indeed give us the correct schema back. This is more useful in support of other diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/StaticInitialisationOfSerializedObjectTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/StaticInitialisationOfSerializedObjectTest.kt index 2533bdf182..1a85fc784b 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/StaticInitialisationOfSerializedObjectTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/amqp/StaticInitialisationOfSerializedObjectTest.kt @@ -45,7 +45,7 @@ class StaticInitialisationOfSerializedObjectTest { } @Test - fun KotlinObjectWithCompanionObject() { + fun kotlinObjectWithCompanionObject() { data class D(val c: C) val sf = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) @@ -104,7 +104,7 @@ class StaticInitialisationOfSerializedObjectTest { override val classCarpenter = ClassCarpenter(ClassLoader.getSystemClassLoader(), wl2) } - // This time have the serilization factory and the carpenter use different whitelists + // This time have the serialization factory and the carpenter use different whitelists @Test fun deserializeTest2() { data class D(val c: C2)