CORDA-855 - Adding tests for wild card generics

Can't actually get something to go through the serializer with a
wild card in place as it seems that's an impossible situation

    * Review Changes
    * CORDA-855 - Review Comments
    * Review Comments
    * Review comments
This commit is contained in:
Katelyn Baker 2018-01-02 18:19:20 +00:00
parent 41220de816
commit 97793447d5
14 changed files with 322 additions and 66 deletions

1
.gitignore vendored
View File

@ -88,6 +88,7 @@ crashlytics-build.properties
# docs related # docs related
docs/virtualenv/ docs/virtualenv/
virtualenv/
# bft-smart # bft-smart
**/config/currentView **/config/currentView

View File

@ -54,7 +54,6 @@ class DeserializationInput(internal val serializerFactory: SerializerFactory) {
inline fun <reified T : Any> deserialize(bytes: SerializedBytes<T>): T = inline fun <reified T : Any> deserialize(bytes: SerializedBytes<T>): T =
deserialize(bytes, T::class.java) deserialize(bytes, T::class.java)
@Throws(NotSerializableException::class) @Throws(NotSerializableException::class)
inline internal fun <reified T : Any> deserializeAndReturnEnvelope(bytes: SerializedBytes<T>): ObjectAndEnvelope<T> = inline internal fun <reified T : Any> deserializeAndReturnEnvelope(bytes: SerializedBytes<T>): ObjectAndEnvelope<T> =
deserializeAndReturnEnvelope(bytes, T::class.java) deserializeAndReturnEnvelope(bytes, T::class.java)

View File

@ -153,7 +153,7 @@ class EvolutionSerializerGetter : EvolutionSerializerGetterBase() {
typeNotation: TypeNotation, typeNotation: TypeNotation,
newSerializer: AMQPSerializer<Any>, newSerializer: AMQPSerializer<Any>,
schemas: SerializationSchemas): AMQPSerializer<Any> = schemas: SerializationSchemas): AMQPSerializer<Any> =
factory.serializersByDescriptor.computeIfAbsent(typeNotation.descriptor.name!!) { factory.getSerializersByDescriptor().computeIfAbsent(typeNotation.descriptor.name!!) {
when (typeNotation) { when (typeNotation) {
is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, factory) is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, factory)
is RestrictedType -> EnumEvolutionSerializer.make(typeNotation, newSerializer, factory, schemas) is RestrictedType -> EnumEvolutionSerializer.make(typeNotation, newSerializer, factory, schemas)
@ -161,21 +161,3 @@ class EvolutionSerializerGetter : EvolutionSerializerGetterBase() {
} }
} }
/**
* 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<Any>,
schemas: SerializationSchemas): AMQPSerializer<Any> {
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}")
}
}

View File

@ -346,10 +346,11 @@ private fun Hasher.fingerprintWithCustomSerializerOrElse(factory: SerializerFact
} }
} }
// This method concatentates various elements of the types recursively as unencoded strings into the hasher, effectively // This method concatenates 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. // 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<Type>, private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: MutableSet<Type>,
hasher: Hasher, factory: SerializerFactory, offset: String = ""): Hasher { hasher: Hasher, factory: SerializerFactory, offset: Int = 4): Hasher {
// We don't include Example<?> and Example<T> where type is ? or T in this otherwise we // We don't include Example<?> and Example<T> where type is ? or T in this otherwise we
// generate different fingerprints for class Outer<T>(val a: Inner<T>) when serialising // generate different fingerprints for class Outer<T>(val a: Inner<T>) when serialising
// and deserializing (assuming deserialization is occurring in a factory that didn't // and deserializing (assuming deserialization is occurring in a factory that didn't
@ -369,21 +370,21 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta
hasher.putUnencodedChars(clazz.name) hasher.putUnencodedChars(clazz.name)
} else { } else {
hasher.fingerprintWithCustomSerializerOrElse(factory, clazz, type) { hasher.fingerprintWithCustomSerializerOrElse(factory, clazz, type) {
fingerprintForObject(type, type, alreadySeen, hasher, factory, "$offset ") fingerprintForObject(type, type, alreadySeen, hasher, factory, offset+4)
} }
} }
// ... and concatentate the type data for each parameter type. // ... and concatenate the type data for each parameter type.
type.actualTypeArguments.fold(startingHash) { orig, paramType -> type.actualTypeArguments.fold(startingHash) { orig, paramType ->
fingerprintForType(paramType, type, alreadySeen, orig, factory, "$offset ") fingerprintForType(paramType, type, alreadySeen, orig, factory, offset+4)
} }
} }
// Treat generic types as "any type" to prevent fingerprint mismatch. This case we fall into when // Treat generic types as "any type" to prevent fingerprint mismatch. This case we fall into when
// looking at A and B from Example<A, B> (remember we call this function recursively). When // looking at A and B from Example<A, B> (remember we call this function recursively). When
// serialising a concrete example of the type we have A and B which are TypeVariables<*>'s but // 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 // when deserializing we only have the wildcard placeholder ?, or AnyType
// //
// Note, TypeVariable<*> used to be encided as TYPE_VARIABLE_HASH but that again produces a // Note, TypeVariable<*> used to be encoded as TYPE_VARIABLE_HASH but that again produces a
// differing fingerprint on serialisation and deserialization // differing fingerprint on serialisation and deserialization
is SerializerFactory.AnyType, is SerializerFactory.AnyType,
is TypeVariable<*> -> { is TypeVariable<*> -> {
@ -391,7 +392,8 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta
} }
is Class<*> -> { is Class<*> -> {
if (type.isArray) { if (type.isArray) {
fingerprintForType(type.componentType, contextType, alreadySeen, hasher, factory, "$offset ").putUnencodedChars(ARRAY_HASH) fingerprintForType(type.componentType, contextType, alreadySeen, hasher, factory, offset+4)
.putUnencodedChars(ARRAY_HASH)
} else if (SerializerFactory.isPrimitive(type)) { } else if (SerializerFactory.isPrimitive(type)) {
hasher.putUnencodedChars(type.name) hasher.putUnencodedChars(type.name)
} else if (isCollectionOrMap(type)) { } else if (isCollectionOrMap(type)) {
@ -410,16 +412,18 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta
// to the CorDapp but maybe reference to the JAR in the short term. // to the CorDapp but maybe reference to the JAR in the short term.
hasher.putUnencodedChars(type.name) hasher.putUnencodedChars(type.name)
} else { } else {
fingerprintForObject(type, type, alreadySeen, hasher, factory, "$offset ") fingerprintForObject(type, type, alreadySeen, hasher, factory, offset+4)
} }
} }
} }
} }
// Hash the element type + some array hash // Hash the element type + some array hash
is GenericArrayType -> fingerprintForType(type.genericComponentType, contextType, alreadySeen, is GenericArrayType -> fingerprintForType(type.genericComponentType, contextType, alreadySeen,
hasher, factory, "$offset ").putUnencodedChars(ARRAY_HASH) hasher, factory, offset+4).putUnencodedChars(ARRAY_HASH)
// TODO: include bounds // TODO: include bounds
is WildcardType -> hasher.putUnencodedChars(type.typeName).putUnencodedChars(WILDCARD_TYPE_HASH) is WildcardType -> {
hasher.putUnencodedChars(type.typeName).putUnencodedChars(WILDCARD_TYPE_HASH)
}
else -> throw NotSerializableException("Don't know how to hash") else -> throw NotSerializableException("Don't know how to hash")
} }
} catch (e: NotSerializableException) { } catch (e: NotSerializableException) {
@ -433,15 +437,21 @@ private fun fingerprintForType(type: Type, contextType: Type?, alreadySeen: Muta
private fun isCollectionOrMap(type: Class<*>) = (Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type)) && private fun isCollectionOrMap(type: Class<*>) = (Collection::class.java.isAssignableFrom(type) || Map::class.java.isAssignableFrom(type)) &&
!EnumSet::class.java.isAssignableFrom(type) !EnumSet::class.java.isAssignableFrom(type)
private fun fingerprintForObject(type: Type, contextType: Type?, alreadySeen: MutableSet<Type>, hasher: Hasher, factory: SerializerFactory, offset: String = ""): Hasher { private fun fingerprintForObject(
type: Type,
contextType: Type?,
alreadySeen: MutableSet<Type>,
hasher: Hasher,
factory: SerializerFactory,
offset: Int = 0): Hasher {
// Hash the class + properties + interfaces // Hash the class + properties + interfaces
val name = type.asClass()?.name ?: throw NotSerializableException("Expected only Class or ParameterizedType but found $type") val name = type.asClass()?.name ?: throw NotSerializableException("Expected only Class or ParameterizedType but found $type")
propertiesForSerialization(constructorForDeserialization(type), contextType ?: type, factory).getters propertiesForSerialization(constructorForDeserialization(type), contextType ?: type, factory).getters
.fold(hasher.putUnencodedChars(name)) { orig, prop -> .fold(hasher.putUnencodedChars(name)) { orig, prop ->
fingerprintForType(prop.resolvedType, type, alreadySeen, orig, factory, "$offset ") fingerprintForType(prop.resolvedType, type, alreadySeen, orig, factory, offset+4)
.putUnencodedChars(prop.name) .putUnencodedChars(prop.name)
.putUnencodedChars(if (prop.mandatory) NOT_NULLABLE_HASH else NULLABLE_HASH) .putUnencodedChars(if (prop.mandatory) NOT_NULLABLE_HASH else NULLABLE_HASH)
} }
interfacesForSerialization(type, factory).map { fingerprintForType(it, type, alreadySeen, hasher, factory, "$offset ") } interfacesForSerialization(type, factory).map { fingerprintForType(it, type, alreadySeen, hasher, factory, offset+4) }
return hasher return hasher
} }

View File

@ -42,9 +42,9 @@ open class SerializerFactory(
cl: ClassLoader, cl: ClassLoader,
private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter()) { private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter()) {
private val serializersByType = ConcurrentHashMap<Type, AMQPSerializer<Any>>() private val serializersByType = ConcurrentHashMap<Type, AMQPSerializer<Any>>()
val serializersByDescriptor = ConcurrentHashMap<Any, AMQPSerializer<Any>>() private val serializersByDescriptor = ConcurrentHashMap<Any, AMQPSerializer<Any>>()
private val customSerializers = CopyOnWriteArrayList<SerializerFor>() private val customSerializers = CopyOnWriteArrayList<SerializerFor>()
val transformsCache = ConcurrentHashMap<String, EnumMap<TransformTypes, MutableList<Transform>>>() private val transformsCache = ConcurrentHashMap<String, EnumMap<TransformTypes, MutableList<Transform>>>()
open val classCarpenter = ClassCarpenter(cl, whitelist) open val classCarpenter = ClassCarpenter(cl, whitelist)
@ -55,6 +55,10 @@ open class SerializerFactory(
schemas: SerializationSchemas) schemas: SerializationSchemas)
= evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas) = evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas)
fun getSerializersByDescriptor() = serializersByDescriptor
fun getTransformsCache() = transformsCache
/** /**
* Look up, and manufacture if necessary, a serializer for the given type. * Look up, and manufacture if necessary, a serializer for the given type.
* *

View File

@ -200,7 +200,7 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
* @param sf the [SerializerFactory] building this transform set. Needed as each can define it's own * @param sf the [SerializerFactory] building this transform set. Needed as each can define it's own
* class loader and this dictates which classes we can and cannot see * class loader and this dictates which classes we can and cannot see
*/ */
fun get(name: String, sf: SerializerFactory) = sf.transformsCache.computeIfAbsent(name) { fun get(name: String, sf: SerializerFactory) = sf.getTransformsCache().computeIfAbsent(name) {
val transforms = EnumMap<TransformTypes, MutableList<Transform>>(TransformTypes::class.java) val transforms = EnumMap<TransformTypes, MutableList<Transform>>(TransformTypes::class.java)
try { try {
val clazz = sf.classloader.loadClass(name) val clazz = sf.classloader.loadClass(name)

View File

@ -31,7 +31,12 @@ public class ErrorMessageTests {
@Test @Test
public void testJavaConstructorAnnotations() { public void testJavaConstructorAnnotations() {
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); SerializationOutput ser = new SerializationOutput(factory1);
Assertions.assertThatThrownBy(() -> ser.serialize(new C(1))) Assertions.assertThatThrownBy(() -> ser.serialize(new C(1)))

View File

@ -0,0 +1,93 @@
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.io.NotSerializableException;
import static org.jgroups.util.Util.assertEquals;
public class JavaGenericsTest {
private static class Inner {
private final Integer v;
private Inner(Integer v) { this.v = v; }
public Integer getV() { return v; }
}
private static class A<T> {
private final T t;
private A(T t) { this.t = t; }
public T getT() { return t; }
}
@Test
public void basicGeneric() throws NotSerializableException {
A a1 = new A(1);
SerializerFactory factory = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
new EvolutionSerializerGetter());
SerializationOutput ser = new SerializationOutput(factory);
SerializedBytes<?> bytes = ser.serialize(a1);
DeserializationInput des = new DeserializationInput(factory);
A a2 = des.deserialize(bytes, A.class);
assertEquals(1, a2.getT());
}
private SerializedBytes<?> forceWildcardSerialize(A<?> a) throws NotSerializableException {
SerializerFactory factory = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
new EvolutionSerializerGetter());
return (new SerializationOutput(factory)).serialize(a);
}
private SerializedBytes<?> forceWildcardSerializeFactory(
A<?> a,
SerializerFactory factory) throws NotSerializableException {
return (new SerializationOutput(factory)).serialize(a);
}
private A<?> forceWildcardDeserialize(SerializedBytes<?> bytes) throws NotSerializableException {
SerializerFactory factory = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
new EvolutionSerializerGetter());
DeserializationInput des = new DeserializationInput(factory);
return des.deserialize(bytes, A.class);
}
private A<?> forceWildcardDeserializeFactory(
SerializedBytes<?> bytes,
SerializerFactory factory) throws NotSerializableException {
return (new DeserializationInput(factory)).deserialize(bytes, A.class);
}
@Test
public void forceWildcard() throws NotSerializableException {
SerializedBytes<?> bytes = forceWildcardSerialize(new A(new Inner(29)));
Inner i = (Inner)forceWildcardDeserialize(bytes).getT();
assertEquals(29, i.getV());
}
@Test
public void forceWildcardSharedFactory() throws NotSerializableException {
SerializerFactory factory = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
new EvolutionSerializerGetter());
SerializedBytes<?> bytes = forceWildcardSerializeFactory(new A(new Inner(29)), factory);
Inner i = (Inner)forceWildcardDeserializeFactory(bytes, factory).getT();
assertEquals(29, i.getV());
}
}

View File

@ -25,7 +25,9 @@ public class JavaPrivatePropertyTests {
@Test @Test
public void singlePrivateWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException { public void singlePrivateWithConstructor() throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader(),
evolutionSerializerGetter);
SerializationOutput ser = new SerializationOutput(factory); SerializationOutput ser = new SerializationOutput(factory);
DeserializationInput des = new DeserializationInput(factory); DeserializationInput des = new DeserializationInput(factory);
@ -53,7 +55,9 @@ public class JavaPrivatePropertyTests {
@Test @Test
public void singlePrivateWithConstructorAndGetter() public void singlePrivateWithConstructorAndGetter()
throws NotSerializableException, NoSuchFieldException, IllegalAccessException { throws NotSerializableException, NoSuchFieldException, IllegalAccessException {
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); EvolutionSerializerGetterBase evolutionSerializerGetter = new EvolutionSerializerGetter();
SerializerFactory factory = new SerializerFactory(AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(), evolutionSerializerGetter);
SerializationOutput ser = new SerializationOutput(factory); SerializationOutput ser = new SerializationOutput(factory);
DeserializationInput des = new DeserializationInput(factory); DeserializationInput des = new DeserializationInput(factory);

View File

@ -109,7 +109,12 @@ public class SetterConstructorTests {
// despite having no constructor we should still be able to serialise an instance of C // despite having no constructor we should still be able to serialise an instance of C
@Test @Test
public void serialiseC() throws NotSerializableException { public void serialiseC() throws NotSerializableException {
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); SerializationOutput ser = new SerializationOutput(factory1);
C c1 = new C(); C c1 = new C();
@ -178,7 +183,11 @@ public class SetterConstructorTests {
@Test @Test
public void deserialiseC() throws NotSerializableException { public void deserialiseC() throws NotSerializableException {
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
SerializerFactory factory1 = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
evolutionSerialiserGetter);
C cPre1 = new C(); C cPre1 = new C();
@ -241,7 +250,11 @@ public class SetterConstructorTests {
@Test @Test
public void serialiseOuterAndInner() throws NotSerializableException { public void serialiseOuterAndInner() throws NotSerializableException {
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
SerializerFactory factory1 = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
evolutionSerialiserGetter);
Inner1 i1 = new Inner1("Hello"); Inner1 i1 = new Inner1("Hello");
Inner2 i2 = new Inner2(); Inner2 i2 = new Inner2();
@ -263,7 +276,11 @@ public class SetterConstructorTests {
@Test @Test
public void typeMistmatch() throws NotSerializableException { public void typeMistmatch() throws NotSerializableException {
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
SerializerFactory factory1 = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
evolutionSerialiserGetter);
TypeMismatch tm = new TypeMismatch(); TypeMismatch tm = new TypeMismatch();
tm.setA(10); tm.setA(10);
@ -279,7 +296,11 @@ public class SetterConstructorTests {
@Test @Test
public void typeMistmatch2() throws NotSerializableException { public void typeMistmatch2() throws NotSerializableException {
SerializerFactory factory1 = new SerializerFactory(AllWhitelist.INSTANCE, ClassLoader.getSystemClassLoader()); EvolutionSerializerGetterBase evolutionSerialiserGetter = new EvolutionSerializerGetter();
SerializerFactory factory1 = new SerializerFactory(
AllWhitelist.INSTANCE,
ClassLoader.getSystemClassLoader(),
evolutionSerialiserGetter);
TypeMismatch2 tm = new TypeMismatch2(); TypeMismatch2 tm = new TypeMismatch2();
tm.setA("10"); tm.setA("10");

View File

@ -6,6 +6,8 @@ import org.assertj.core.api.Assertions
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
import java.io.NotSerializableException import java.io.NotSerializableException
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -387,21 +389,25 @@ class EnumEvolvabilityTests {
data class C1(val annotatedEnum: AnnotatedEnumOnce) data class C1(val annotatedEnum: AnnotatedEnumOnce)
val sf = testDefaultFactory() val sf = testDefaultFactory()
val f = sf.javaClass.getDeclaredField("transformsCache")
f.isAccessible = true
assertEquals(0, sf.transformsCache.size) val transformsCache = f.get(sf) as ConcurrentHashMap<String, EnumMap<TransformTypes, MutableList<Transform>>>
assertEquals(0, transformsCache.size)
val sb1 = TestSerializationOutput(VERBOSE, sf).serializeAndReturnSchema(C1(AnnotatedEnumOnce.D)) val sb1 = TestSerializationOutput(VERBOSE, sf).serializeAndReturnSchema(C1(AnnotatedEnumOnce.D))
assertEquals(2, sf.transformsCache.size) assertEquals(2, transformsCache.size)
assertTrue(sf.transformsCache.containsKey(C1::class.java.name)) assertTrue(transformsCache.containsKey(C1::class.java.name))
assertTrue(sf.transformsCache.containsKey(AnnotatedEnumOnce::class.java.name)) assertTrue(transformsCache.containsKey(AnnotatedEnumOnce::class.java.name))
val sb2 = TestSerializationOutput(VERBOSE, sf).serializeAndReturnSchema(C2(AnnotatedEnumOnce.D)) val sb2 = TestSerializationOutput(VERBOSE, sf).serializeAndReturnSchema(C2(AnnotatedEnumOnce.D))
assertEquals(3, sf.transformsCache.size) assertEquals(3, transformsCache.size)
assertTrue(sf.transformsCache.containsKey(C1::class.java.name)) assertTrue(transformsCache.containsKey(C1::class.java.name))
assertTrue(sf.transformsCache.containsKey(C2::class.java.name)) assertTrue(transformsCache.containsKey(C2::class.java.name))
assertTrue(sf.transformsCache.containsKey(AnnotatedEnumOnce::class.java.name)) assertTrue(transformsCache.containsKey(AnnotatedEnumOnce::class.java.name))
assertEquals(sb1.transformsSchema.types[AnnotatedEnumOnce::class.java.name], assertEquals(sb1.transformsSchema.types[AnnotatedEnumOnce::class.java.name],
sb2.transformsSchema.types[AnnotatedEnumOnce::class.java.name]) sb2.transformsSchema.types[AnnotatedEnumOnce::class.java.name])

View File

@ -0,0 +1,22 @@
package net.corda.nodeapi.internal.serialization.amqp
import java.io.NotSerializableException
/**
* 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<Any>,
schemas: SerializationSchemas): AMQPSerializer<Any> {
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}")
}
}

View File

@ -2,43 +2,50 @@ package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.nodeapi.internal.serialization.AllWhitelist import net.corda.nodeapi.internal.serialization.AllWhitelist
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
import org.junit.Test import org.junit.Test
import java.io.File
import java.net.URI
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.test.assertEquals import kotlin.test.assertEquals
class GenericsTests { class GenericsTests {
companion object { companion object {
val VERBOSE = false val VERBOSE = false
@Suppress("UNUSED")
var localPath = projectRootDir.toUri().resolve(
"node-api/src/test/resources/net/corda/nodeapi/internal/serialization/amqp")
} }
private fun printSeparator() = if(VERBOSE) println ("\n\n-------------------------------------------\n\n") else Unit private fun printSeparator() = if (VERBOSE) println("\n\n-------------------------------------------\n\n") else Unit
private fun<T : Any> BytesAndSchemas<T>.printSchema() = if (VERBOSE) println ("${this.schema}\n") else Unit private fun <T : Any> BytesAndSchemas<T>.printSchema() = if (VERBOSE) println("${this.schema}\n") else Unit
private fun ConcurrentHashMap<Any, AMQPSerializer<Any>>.printKeyToType() { private fun ConcurrentHashMap<Any, AMQPSerializer<Any>>.printKeyToType() {
if (!VERBOSE) return if (!VERBOSE) return
forEach { forEach {
println ("Key = ${it.key} - ${it.value.type.typeName}") println("Key = ${it.key} - ${it.value.type.typeName}")
} }
println() println()
} }
@Test @Test
fun twoDifferntTypesSameParameterizedOuter() { fun twoDifferentTypesSameParameterizedOuter() {
data class G<A>(val a: A) data class G<A>(val a: A)
val factory = testDefaultFactoryNoEvolution() val factory = testDefaultFactoryNoEvolution()
val bytes1 = SerializationOutput(factory).serializeAndReturnSchema(G("hi")).apply { printSchema() } val bytes1 = SerializationOutput(factory).serializeAndReturnSchema(G("hi")).apply { printSchema() }
factory.serializersByDescriptor.printKeyToType() factory.getSerializersByDescriptor().printKeyToType()
val bytes2 = SerializationOutput(factory).serializeAndReturnSchema(G(121)).apply { printSchema() } val bytes2 = SerializationOutput(factory).serializeAndReturnSchema(G(121)).apply { printSchema() }
factory.serializersByDescriptor.printKeyToType() factory.getSerializersByDescriptor().printKeyToType()
listOf (factory, testDefaultFactory()).forEach { f -> listOf(factory, testDefaultFactory()).forEach { f ->
DeserializationInput(f).deserialize(bytes1.obj).apply { assertEquals("hi", this.a) } DeserializationInput(f).deserialize(bytes1.obj).apply { assertEquals("hi", this.a) }
DeserializationInput(f).deserialize(bytes2.obj).apply { assertEquals(121, this.a) } DeserializationInput(f).deserialize(bytes2.obj).apply { assertEquals(121, this.a) }
} }
@ -69,14 +76,14 @@ class GenericsTests {
val bytes = ser.serializeAndReturnSchema(G("hi")).apply { printSchema() } val bytes = ser.serializeAndReturnSchema(G("hi")).apply { printSchema() }
factory.serializersByDescriptor.printKeyToType() factory.getSerializersByDescriptor().printKeyToType()
assertEquals("hi", DeserializationInput(factory).deserialize(bytes.obj).a) assertEquals("hi", DeserializationInput(factory).deserialize(bytes.obj).a)
assertEquals("hi", DeserializationInput(altContextFactory).deserialize(bytes.obj).a) assertEquals("hi", DeserializationInput(altContextFactory).deserialize(bytes.obj).a)
val bytes2 = ser.serializeAndReturnSchema(Wrapper(1, G("hi"))).apply { printSchema() } val bytes2 = ser.serializeAndReturnSchema(Wrapper(1, G("hi"))).apply { printSchema() }
factory.serializersByDescriptor.printKeyToType() factory.getSerializersByDescriptor().printKeyToType()
printSeparator() printSeparator()
@ -93,7 +100,7 @@ class GenericsTests {
@Test @Test
fun nestedGenericsReferencesByteArrayViaSerializedBytes() { fun nestedGenericsReferencesByteArrayViaSerializedBytes() {
data class G(val a : Int) data class G(val a: Int)
data class Wrapper<T : Any>(val a: Int, val b: SerializedBytes<T>) data class Wrapper<T : Any>(val a: Int, val b: SerializedBytes<T>)
val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()) val factory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
@ -128,27 +135,30 @@ class GenericsTests {
ser.serialize(Wrapper(Container(InnerA(1)))).apply { ser.serialize(Wrapper(Container(InnerA(1)))).apply {
factories.forEach { factories.forEach {
DeserializationInput(it).deserialize(this).apply { assertEquals(1, c.b.a_a) } DeserializationInput(it).deserialize(this).apply { assertEquals(1, c.b.a_a) }
it.getSerializersByDescriptor().printKeyToType(); printSeparator()
} }
} }
ser.serialize(Wrapper(Container(InnerB(1)))).apply { ser.serialize(Wrapper(Container(InnerB(1)))).apply {
factories.forEach { factories.forEach {
DeserializationInput(it).deserialize(this).apply { assertEquals(1, c.b.a_b) } DeserializationInput(it).deserialize(this).apply { assertEquals(1, c.b.a_b) }
it.getSerializersByDescriptor().printKeyToType(); printSeparator()
} }
} }
ser.serialize(Wrapper(Container(InnerC("Ho ho ho")))).apply { ser.serialize(Wrapper(Container(InnerC("Ho ho ho")))).apply {
factories.forEach { factories.forEach {
DeserializationInput(it).deserialize(this).apply { assertEquals("Ho ho ho", c.b.a_c) } DeserializationInput(it).deserialize(this).apply { assertEquals("Ho ho ho", c.b.a_c) }
it.getSerializersByDescriptor().printKeyToType(); printSeparator()
} }
} }
} }
@Test @Test
fun nestedSerializationWhereGenericDoesntImpactFingerprint() { fun nestedSerializationWhereGenericDoesntImpactFingerprint() {
data class Inner(val a : Int) data class Inner(val a: Int)
data class Container<T : Any>(val b: Inner) data class Container<T : Any>(val b: Inner)
data class Wrapper<T: Any>(val c: Container<T>) data class Wrapper<T : Any>(val c: Container<T>)
val factorys = listOf( val factorys = listOf(
SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()), SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()),
@ -168,4 +178,103 @@ class GenericsTests {
} }
} }
} }
data class ForceWildcard<out T>(val t: T)
private fun forceWildcardSerialize(
a: ForceWildcard<*>,
factory: SerializerFactory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())): SerializedBytes<*> {
val bytes = SerializationOutput(factory).serializeAndReturnSchema(a)
factory.getSerializersByDescriptor().printKeyToType()
bytes.printSchema()
return bytes.obj
}
@Suppress("UNCHECKED_CAST")
private fun forceWildcardDeserializeString(
bytes: SerializedBytes<*>,
factory: SerializerFactory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())) {
DeserializationInput(factory).deserialize(bytes as SerializedBytes<ForceWildcard<String>>)
}
@Suppress("UNCHECKED_CAST")
private fun forceWildcardDeserializeDouble(
bytes: SerializedBytes<*>,
factory: SerializerFactory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())) {
DeserializationInput(factory).deserialize(bytes as SerializedBytes<ForceWildcard<Double>>)
}
@Suppress("UNCHECKED_CAST")
private fun forceWildcardDeserialize(
bytes: SerializedBytes<*>,
factory: SerializerFactory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())) {
DeserializationInput(factory).deserialize(bytes as SerializedBytes<ForceWildcard<*>>)
}
@Test
fun forceWildcard() {
forceWildcardDeserializeString(forceWildcardSerialize(ForceWildcard("hello")))
forceWildcardDeserializeDouble(forceWildcardSerialize(ForceWildcard(3.0)))
}
@Test
fun forceWildcardSharedFactory() {
val f = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
forceWildcardDeserializeString(forceWildcardSerialize(ForceWildcard("hello"), f), f)
forceWildcardDeserializeDouble(forceWildcardSerialize(ForceWildcard(3.0), f), f)
}
@Test
fun forceWildcardDeserialize() {
forceWildcardDeserialize(forceWildcardSerialize(ForceWildcard("hello")))
forceWildcardDeserialize(forceWildcardSerialize(ForceWildcard(10)))
forceWildcardDeserialize(forceWildcardSerialize(ForceWildcard(20.0)))
}
@Test
fun forceWildcardDeserializeSharedFactory() {
val f = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())
forceWildcardDeserialize(forceWildcardSerialize(ForceWildcard("hello"), f), f)
forceWildcardDeserialize(forceWildcardSerialize(ForceWildcard(10), f), f)
forceWildcardDeserialize(forceWildcardSerialize(ForceWildcard(20.0), f), f)
}
@Test
fun loadGenericFromFile() {
val resource = "${javaClass.simpleName}.${testName()}"
val sf = testDefaultFactory()
// Uncomment to re-generate test files, needs to be done in three stages
// File(URI("$localPath/$resource")).writeBytes(forceWildcardSerialize(ForceWildcard("wibble")).bytes)
assertEquals("wibble",
DeserializationInput(sf).deserialize(SerializedBytes<ForceWildcard<*>>(
File(GenericsTests::class.java.getResource(resource).toURI()).readBytes())).t)
}
interface DifferentBounds {
fun go()
}
@Test
fun differentBounds() {
data class A (val a: Int): DifferentBounds {
override fun go() {
println(a)
}
}
data class G<out T : DifferentBounds>(val b: T)
val factorys = listOf(
SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()),
SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader()))
val ser = SerializationOutput(factorys[0])
ser.serialize(G(A(10))).apply {
factorys.forEach {
}
}
}
} }