Unit Tests for the amqp -> carpenter schema

Squahed commit mesages:
	* Better tests
	* WIP
	* WIP
This commit is contained in:
Katelyn Baker 2017-06-16 09:05:47 +01:00
parent 4cbf31681b
commit ce172887d0
9 changed files with 560 additions and 348 deletions

View File

@ -15,6 +15,8 @@ import java.lang.reflect.Type
import java.lang.reflect.TypeVariable
import java.util.*
import net.corda.core.serialization.ClassCarpenterSchema
// TODO: get an assigned number as per AMQP spec
val DESCRIPTOR_TOP_32BITS: Long = 0xc0da0000
@ -87,9 +89,37 @@ data class Schema(val types: List<TypeNotation>) : DescribedType {
override fun getDescribed(): Any = listOf(types)
override fun toString(): String = types.joinToString("\n")
fun carpenterSchema(loaders : List<ClassLoader> = listOf<ClassLoader>(ClassLoader.getSystemClassLoader()))
: List<ClassCarpenterSchema>
{
var rtn = mutableListOf<ClassCarpenterSchema>()
for (type in types) {
if (type is CompositeType) {
var foundIt = false
for (loader in loaders) {
try {
loader.loadClass(type.name)
foundIt = true
break
} catch (e: ClassNotFoundException) {
continue
}
}
if (foundIt) continue
else {
rtn.add(type.carpenterSchema())
}
}
}
return rtn
}
}
data class Descriptor(val name: String?, val code: UnsignedLong? = null) : DescribedType {
data class Descriptor(var name: String?, val code: UnsignedLong? = null) : DescribedType {
companion object : DescribedTypeConstructor<Descriptor> {
val DESCRIPTOR = UnsignedLong(3L or DESCRIPTOR_TOP_32BITS)
@ -127,7 +157,7 @@ data class Descriptor(val name: String?, val code: UnsignedLong? = null) : Descr
}
}
data class Field(val name: String, val type: String, val requires: List<String>, val default: String?, val label: String?, val mandatory: Boolean, val multiple: Boolean) : DescribedType {
data class Field(var name: String, val type: String, val requires: List<String>, val default: String?, val label: String?, val mandatory: Boolean, val multiple: Boolean) : DescribedType {
companion object : DescribedTypeConstructor<Field> {
val DESCRIPTOR = UnsignedLong(4L or DESCRIPTOR_TOP_32BITS)
@ -169,6 +199,16 @@ data class Field(val name: String, val type: String, val requires: List<String>,
return sb.toString()
}
inline fun isKnownClass (type : String) : Class<*> {
try {
return ClassLoader.getSystemClassLoader().loadClass(type)
}
catch (e: ClassNotFoundException) {
// call carpenter
throw IllegalArgumentException ("${type} - ${name} - pants")
}
}
fun getPrimType() = when (type) {
"int" -> Int::class.javaPrimitiveType!!
"string" -> String::class.java
@ -178,14 +218,8 @@ data class Field(val name: String, val type: String, val requires: List<String>,
"boolean" -> Boolean::class.javaPrimitiveType!!
"double" -> Double::class.javaPrimitiveType!!
"float" -> Double::class.javaPrimitiveType!!
else -> {
try {
ClassLoader.getSystemClassLoader().loadClass(type)
}
catch (e: ClassNotFoundException) {
throw IllegalArgumentException ("pants")
}
}
"*" -> isKnownClass(requires[0])
else -> isKnownClass(type)
}
}
@ -203,13 +237,13 @@ sealed class TypeNotation : DescribedType {
}
}
abstract val name: String
abstract var name: String
abstract val label: String?
abstract val provides: List<String>
abstract val descriptor: Descriptor
}
data class CompositeType(override val name: String, override val label: String?, override val provides: List<String>, override val descriptor: Descriptor, val fields: List<Field>) : TypeNotation() {
data class CompositeType(override var name: String, override val label: String?, override val provides: List<String>, override val descriptor: Descriptor, val fields: List<Field>) : TypeNotation() {
companion object : DescribedTypeConstructor<CompositeType> {
val DESCRIPTOR = UnsignedLong(5L or DESCRIPTOR_TOP_32BITS)
@ -253,16 +287,36 @@ data class CompositeType(override val name: String, override val label: String?,
return sb.toString()
}
fun carpenterSchema() : Map<String, Class<out Any?>> {
fun carpenterSchema(classLoaders: List<ClassLoader> = listOf<ClassLoader> (ClassLoader.getSystemClassLoader())) : ClassCarpenterSchema {
var m : MutableMap<String, Class<out Any?>> = mutableMapOf()
fields.forEach { m[it.name] = it.getPrimType() }
return m
var providesList = mutableListOf<Class<*>>()
for (iface in provides) {
var found = false
for (loader in classLoaders) {
try {
providesList.add (loader.loadClass(iface))
found = true
break
}
catch (e: ClassNotFoundException) {
continue
}
}
if (found == false) {
println ("This needs to work but it wont - ${iface}")
}
else println ("found it ${iface}")
}
return ClassCarpenterSchema (name, m, interfaces = providesList)
}
}
data class RestrictedType(override val name: String, override val label: String?, override val provides: List<String>, val source: String, override val descriptor: Descriptor, val choices: List<Choice>) : TypeNotation() {
data class RestrictedType(override var name: String, override val label: String?, override val provides: List<String>, val source: String, override val descriptor: Descriptor, val choices: List<Choice>) : TypeNotation() {
companion object : DescribedTypeConstructor<RestrictedType> {
val DESCRIPTOR = UnsignedLong(6L or DESCRIPTOR_TOP_32BITS)
@ -305,7 +359,7 @@ data class RestrictedType(override val name: String, override val label: String?
}
}
data class Choice(val name: String, val value: String) : DescribedType {
data class Choice(var name: String, val value: String) : DescribedType {
companion object : DescribedTypeConstructor<Choice> {
val DESCRIPTOR = UnsignedLong(7L or DESCRIPTOR_TOP_32BITS)

View File

@ -159,58 +159,18 @@ class ClassCarpenter {
}
}
/**
* A Schema represents a desired class.
*/
abstract class Schema(
val name: String,
fields: Map<String, Field>,
val superclass: Schema? = null,
val interfaces: List<Class<*>> = emptyList())
{
private fun Map<String, ClassCarpenter.Field>.descriptors() =
LinkedHashMap(this.mapValues { it.value.descriptor })
/* Fix the order up front if the user didn't, inject the name into the field as it's
neater when iterating */
val fields = LinkedHashMap(fields.mapValues { it.value.copy(it.key, it.value.field) })
fun fieldsIncludingSuperclasses(): Map<String, Field> =
(superclass?.fieldsIncludingSuperclasses() ?: emptyMap()) + LinkedHashMap(fields)
fun descriptorsIncludingSuperclasses(): Map<String, String> =
(superclass?.descriptorsIncludingSuperclasses() ?: emptyMap()) + fields.descriptors()
val jvmName: String
get() = name.replace(".", "/")
}
private val String.jvm: String get() = replace(".", "/")
class ClassSchema(
name: String,
fields: Map<String, Field>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList()
) : Schema(name, fields, superclass, interfaces)
class InterfaceSchema(
name: String,
fields: Map<String, Field>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList()
) : Schema(name, fields, superclass, interfaces)
private class CarpenterClassLoader : ClassLoader(Thread.currentThread().contextClassLoader) {
fun load(name: String, bytes: ByteArray) = defineClass(name, bytes, 0, bytes.size)
}
private val classloader = CarpenterClassLoader()
fun classLoader() = classloader as ClassLoader
private val _loaded = HashMap<String, Class<*>>()
/** Returns a snapshot of the currently loaded classes as a map of full class name (package names+dots) -> class object */
val loaded: Map<String, Class<*>> = HashMap(_loaded)
fun loaded() : Map<String, Class<*>> = HashMap(_loaded)
/**
* Generate bytecode for the given schema and load into the JVM. The returned class object can be used to
@ -218,18 +178,17 @@ class ClassCarpenter {
*
* @throws DuplicateNameException if the schema's name is already taken in this namespace (you can create a new ClassCarpenter if you're OK with ambiguous names)
*/
fun build(schema: Schema): Class<*> {
fun build(schema: ClassCarpenterSchema): Class<*> {
validateSchema(schema)
// Walk up the inheritance hierarchy and then start walking back down once we either hit the top, or
// find a class we haven't generated yet.
val hierarchy = ArrayList<Schema>()
val hierarchy = ArrayList<ClassCarpenterSchema>()
hierarchy += schema
var cursor = schema.superclass
while (cursor != null && cursor.name !in _loaded) {
hierarchy += cursor
cursor = cursor.superclass
}
hierarchy.reversed().forEach {
when (it) {
is InterfaceSchema -> generateInterface(it)
@ -346,6 +305,7 @@ class ClassCarpenter {
visitEnd()
}
}
}
private fun ClassWriter.generateAbstractGetters(schema: Schema) {
@ -374,14 +334,16 @@ class ClassCarpenter {
// Calculate the super call.
val superclassFields = schema.superclass?.fieldsIncludingSuperclasses() ?: emptyMap()
visitVarInsn(ALOAD, 0)
if (schema.superclass == null) {
val sc = schema.superclass
if (sc == null) {
visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
} else {
var slot = 1
for (fieldType in superclassFields.values)
slot += load(slot, fieldType)
val superDesc = schema.superclass.descriptorsIncludingSuperclasses().values.joinToString("")
visitMethodInsn(INVOKESPECIAL, schema.superclass.name.jvm, "<init>", "($superDesc)V", false)
//val superDesc = schema.superclass.descriptorsIncludingSuperclasses().values.joinToString("")
val superDesc = sc.descriptorsIncludingSuperclasses().values.joinToString("")
visitMethodInsn(INVOKESPECIAL, sc.name.jvm, "<init>", "($superDesc)V", false)
}
// Assign the fields from parameters.

View File

@ -0,0 +1,44 @@
package net.corda.core.serialization.carpenter
import org.objectweb.asm.Type
import java.util.LinkedHashMap
/**
* A Schema represents a desired class.
*/
abstract class Schema(
val name: String,
fields: Map<String, Field>,
val superclass: Schema? = null,
val interfaces: List<Class<*>> = emptyList())
{
private fun Map<String, ClassCarpenter.Field>.descriptors() =
LinkedHashMap(this.mapValues { it.value.descriptor })
/* Fix the order up front if the user didn't, inject the name into the field as it's
neater when iterating */
val fields = LinkedHashMap(fields.mapValues { it.value.copy(it.key, it.value.field) })
fun fieldsIncludingSuperclasses(): Map<String, Field> =
(superclass?.fieldsIncludingSuperclasses() ?: emptyMap()) + LinkedHashMap(fields)
fun descriptorsIncludingSuperclasses(): Map<String, String> =
(superclass?.descriptorsIncludingSuperclasses() ?: emptyMap()) + fields.descriptors()
val jvmName: String
get() = name.replace(".", "/")
}
class ClassSchema(
name: String,
fields: Map<String, Class<out Any?>>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList()
) : ClassCarpenter.Schema (name, fields, superclass, interfaces)
class InterfaceSchema(
name: String,
fields: Map<String, Class<out Any?>>,
superclass: Schema? = null,
interfaces: List<Class<*>> = emptyList()
) : ClassCarpenter.Schema (name, fields, superclass, interfaces)

View File

@ -8,11 +8,31 @@ import java.beans.Introspector
import kotlin.test.assertNotEquals
class ClassCarpenterTest {
/*
cw.visitInnerClass(
"net/corda/carpenter/ClassCarpenterTest$DummyInterface",
"net/corda/carpenter/ClassCarpenterTest",
"DummyInterface",
ACC_PUBLIC + ACC_STATIC + ACC_ABSTRACT + ACC_INTERFACE);
*/
interface DummyInterface {
val a: String
val b: Int
}
/*
cw.visitInnerClass(
"net/corda/carpenter/ClassCarpenterTest$Dummy",
"net/corda/carpenter/ClassCarpenterTest",
"Dummy",
ACC_PUBLIC + ACC_FINAL + ACC_STATIC);
*/
class Dummy (override val a: String, override val b: Int) : DummyInterface
val dummy = Dummy ("hi", 1)
val dummy2 = Dummy2 ("hi", 1)
val cc = ClassCarpenter()
// We have to ignore synthetic fields even though ClassCarpenter doesn't create any because the JaCoCo
@ -22,7 +42,7 @@ class ClassCarpenterTest {
@Test
fun empty() {
val clazz = cc.build(ClassCarpenter.ClassSchema("gen.EmptyClass", emptyMap(), null))
val clazz = cc.build(ClassSchema("gen.EmptyClass", emptyMap(), null))
assertEquals(0, clazz.nonSyntheticFields.size)
assertEquals(2, clazz.nonSyntheticMethods.size) // get, toString
assertEquals(0, clazz.declaredConstructors[0].parameterCount)
@ -69,7 +89,7 @@ class ClassCarpenterTest {
}
private fun genPerson(): Pair<Class<*>, Any> {
val clazz = cc.build(ClassCarpenter.ClassSchema("gen.Person", mapOf(
val clazz = cc.build(ClassSchema("gen.Person", mapOf(
"age" to Int::class.javaPrimitiveType!!,
"name" to String::class.java
).mapValues { ClassCarpenter.NonNullableField (it.value) } ))
@ -92,8 +112,8 @@ class ClassCarpenterTest {
@Test(expected = ClassCarpenter.DuplicateNameException::class)
fun duplicates() {
cc.build(ClassCarpenter.ClassSchema("gen.EmptyClass", emptyMap(), null))
cc.build(ClassCarpenter.ClassSchema("gen.EmptyClass", emptyMap(), null))
cc.build(ClassSchema("gen.EmptyClass", emptyMap(), null))
cc.build(ClassSchema("gen.EmptyClass", emptyMap(), null))
}
@Test
@ -134,6 +154,7 @@ class ClassCarpenterTest {
mapOf("b" to ClassCarpenter.NonNullableField(Int::class.java)),
schema1,
interfaces = listOf(DummyInterface::class.java))
val clazz = cc.build(schema2)
val i = clazz.constructors[0].newInstance("xa", 1) as DummyInterface
assertEquals("xa", i.a)

View File

@ -1,263 +0,0 @@
package net.corda.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.amqp.*
import org.junit.Test
import kotlin.test.assertEquals
class ClassCarpenterScehmaTestsSingleMemberComposite {
private var factory = SerializerFactory()
fun serialise (clazz : Any) = SerializationOutput(factory).serialize(clazz)
@Test
fun singleInteger() {
val test = 10
@CordaSerializable
data class A(val a : Int)
var a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
val amqpObj = obj.first as A
assertEquals (test, amqpObj.a)
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("int", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
@Test
fun singleString() {
val test = "ten"
@CordaSerializable
data class A(val a : String)
var a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
val amqpObj = obj.first as A
assertEquals (test, amqpObj.a)
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("string", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
/*
@Test
fun singleChar () {
val test = 'c'
@CordaSerializable
data class A(val a : Char)
var a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
val amqpObj = obj.first as A
assertEquals (test, amqpObj.a)
assertEquals (1, obj.second.schema.types.size)
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("char", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
*/
@Test
fun singleLong() {
val test = 10L
@CordaSerializable
data class A(val a : Long)
var a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
val amqpObj = obj.first as A
assertEquals (test, amqpObj.a)
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("long", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
@Test
fun singleShort() {
val test = 10.toShort()
@CordaSerializable
data class A(val a : Short)
var a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
val amqpObj = obj.first as A
assertEquals (test, amqpObj.a)
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("short", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
/*
@Test
fun singleBool() {
val test = true
@CordaSerializable
data class A(val a : Boolean)
var a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
val amqpObj = obj.first as A
assertEquals (test, amqpObj.a)
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("boolean", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
*/
@Test
fun singleDouble() {
val test = 10.0
@CordaSerializable
data class A(val a : Double)
var a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
val amqpObj = obj.first as A
assertEquals (test, amqpObj.a)
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("double", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val p = pinochio.constructors[0].newInstance (test)
assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
@Test
fun singleFloat() {
val test = 10.0F
@CordaSerializable
data class A(val a : Float)
var a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
val amqpObj = obj.first as A
assertEquals (test, amqpObj.a)
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("float", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val p = pinochio.constructors[0].newInstance (test)
// assertEquals (pinochio.getMethod("getA").invoke (p), amqpObj.a)
}
}

View File

@ -2,10 +2,11 @@ package net.corda.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.amqp.*
import net.corda.core.serialization.ClassCarpenterSchema
import org.junit.Test
import kotlin.test.assertEquals
class CompositeMemberCompositeSchemaToClassCarpenterTests {
class Inheritance {
private var factory = SerializerFactory()
fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz)
@ -35,6 +36,9 @@ class CompositeMemberCompositeSchemaToClassCarpenterTests {
assert(obj.second.schema.types[0] is CompositeType)
assert(obj.second.schema.types[1] is CompositeType)
println (obj.second.schema.types[0] as CompositeType)
println (obj.second.schema.types[1] as CompositeType)
var amqpSchemaA : CompositeType? = null
var amqpSchemaB : CompositeType? = null
@ -52,15 +56,15 @@ class CompositeMemberCompositeSchemaToClassCarpenterTests {
assertEquals("a", amqpSchemaA!!.fields[0].name)
assertEquals("int", amqpSchemaA!!.fields[0].type)
assertEquals(2, amqpSchemaB?.fields?.size)
assertEquals("a", amqpSchemaB!!.fields[0].name)
assertEquals("net.corda.carpenter.CompositeMemberCompositeSchemaToClassCarpenterTests\$nestedInts\$A",
amqpSchemaB!!.fields[0].type)
assertEquals("${this.javaClass.name}\$nestedInts\$A", amqpSchemaB!!.fields[0].type)
assertEquals("b", amqpSchemaB!!.fields[1].name)
assertEquals("int", amqpSchemaB!!.fields[1].type)
var ccA = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchemaA.name, amqpSchemaA.carpenterSchema()))
var ccB = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchemaB.name, amqpSchemaB.carpenterSchema()))
var ccA = ClassCarpenter().build(amqpSchemaA.carpenterSchema())
var ccB = ClassCarpenter().build(amqpSchemaB.carpenterSchema())
/*
* Since A is known to the JVM we can't constuct B with and instance of the carpented A but
@ -72,7 +76,7 @@ class CompositeMemberCompositeSchemaToClassCarpenterTests {
assertEquals (ccA.getMethod("getA").invoke(instanceA), amqpObj.a.a)
assertEquals ((ccB.getMethod("getA").invoke(instanceB) as A).a, amqpObj.a.a)
assertEquals (ccB.getMethod("getB").invoke(instanceB), amqpObj.b)
}
}
}

View File

@ -0,0 +1,389 @@
package net.corda.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.amqp.*
import org.junit.Test
import kotlin.test.assertEquals
/*******************************************************************************************************/
@CordaSerializable
interface J {
val j : Int
}
/*******************************************************************************************************/
@CordaSerializable
interface I {
val i : Int
}
@CordaSerializable
interface II {
val ii : Int
}
@CordaSerializable
interface III : I {
val iii : Int
override val i: Int
}
@CordaSerializable
interface IIII {
val iiii : Int
val i : I
}
/*******************************************************************************************************/
class InheritanceSchemaToClassCarpenterTests {
private var factory = SerializerFactory()
fun serialise(clazz: Any) = SerializationOutput(factory).serialize(clazz)
fun curruptName(name: String) = "${name}__carpenter"
/* given a list of class names work through the amqp envelope schema and alter any that
match in the fashion defined above */
fun curruptName (schema: Schema, names: List<String>) : Schema {
val types = schema.types
val newTypes : MutableList<TypeNotation> = mutableListOf()
for (type in types) {
val newName = if (type.name in names) curruptName (type.name) else type.name
val newProvides = type.provides.map {
it -> if (it in names) curruptName (it) else it
}
val newFields = (type as CompositeType).fields.map {
it -> if ( it.requires.isNotEmpty() && it.requires[0] in names)
it.copy (requires=listOf (curruptName (it.requires[0]))) else it
}
newTypes.add (type.copy (name=newName, provides=newProvides, fields=newFields))
}
return Schema (types=newTypes)
}
@Test
fun interfaceParent1() {
val testJ = 20
val testName = "interfaceParent1"
class A(override val j: Int) : J
val a = A(testJ)
assertEquals(testJ, a.j)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
assert(obj.first is A)
val serSchema = obj.second.schema
assertEquals(2, serSchema.types.size)
val l1 = serSchema.carpenterSchema()
/* since we're using an envelope generated by seilaising classes defined locally
it's extremely unlikely we'd need to carpent any classes */
assertEquals(0, l1.size)
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A"))
val l2 = curruptSchema.carpenterSchema()
assertEquals(1, l2.size)
assertEquals(curruptName("${this.javaClass.name}\$$testName\$A"), l2[0].name)
assertEquals(1, l2[0].interfaces.size)
assertEquals(net.corda.carpenter.J::class.java, l2[0].interfaces[0])
val aBuilder = ClassCarpenter().build(l2[0])
val objJ = aBuilder.constructors[0].newInstance(testJ)
val j = objJ as J
assertEquals(aBuilder.getMethod("getJ").invoke(objJ), testJ)
assertEquals(a.j, j.j)
}
@Test
fun interfaceParent2() {
val testJ = 20
val testJJ = 40
val testName = "interfaceParent2"
class A(override val j: Int, val jj: Int) : J
val a = A(testJ, testJJ)
assertEquals(testJ, a.j)
assertEquals(testJJ, a.jj)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
assert(obj.first is A)
val serSchema = obj.second.schema
assertEquals(2, serSchema.types.size)
val l1 = serSchema.carpenterSchema()
/* since we're using an envelope generated by seilaising classes defined locally
it's extremely unlikely we'd need to carpent any classes */
assertEquals(0, l1.size)
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A"))
val l2 = curruptSchema.carpenterSchema()
assertEquals(1, l2.size)
assertEquals(curruptName("${this.javaClass.name}\$$testName\$A"), l2[0].name)
assertEquals(1, l2[0].interfaces.size)
assertEquals(net.corda.carpenter.J::class.java, l2[0].interfaces[0])
val aBuilder = ClassCarpenter().build(l2[0])
val objJ = aBuilder.constructors[0].newInstance(testJ, testJJ)
val j = objJ as J
assertEquals(aBuilder.getMethod("getJ").invoke(objJ), testJ)
assertEquals(aBuilder.getMethod("getJj").invoke(objJ), testJJ)
assertEquals(a.j, j.j)
}
@Test
fun multipleInterfaces() {
val testI = 20
val testII = 40
val testName = "multipleInterfaces"
class A(override val i: Int, override val ii: Int) : I, II
val a = A(testI, testII)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
assert(obj.first is A)
val serSchema = obj.second.schema
assertEquals(3, serSchema.types.size)
val l1 = serSchema.carpenterSchema()
/* since we're using an envelope generated by seilaising classes defined locally
it's extremely unlikely we'd need to carpent any classes */
assertEquals(0, l1.size)
/* pretend we don't know the class we've been sent, i.e. it's unknown to the class loader, and thus
needs some carpentry */
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A"))
val l2 = curruptSchema.carpenterSchema()
assertEquals(1, l2.size)
assertEquals(curruptName("${this.javaClass.name}\$$testName\$A"), l2[0].name)
assertEquals(2, l2[0].interfaces.size)
assert (net.corda.carpenter.I::class.java in l2[0].interfaces)
assert (net.corda.carpenter.II::class.java in l2[0].interfaces)
val aBuilder = ClassCarpenter().build(l2[0])
val objA = aBuilder.constructors[0].newInstance(testI, testII)
val i = objA as I
val ii = objA as II
assertEquals(aBuilder.getMethod("getI").invoke(objA), testI)
assertEquals(aBuilder.getMethod("getIi").invoke(objA), testII)
assertEquals(a.i, i.i)
assertEquals(a.ii, ii.ii)
}
@Test
fun nestedInterfaces() {
val testI = 20
val testIII = 60
val testName = "nestedInterfaces"
class A(override val i: Int, override val iii : Int) : III
val a = A(testI, testIII)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(a))
assert(obj.first is A)
val serSchema = obj.second.schema
assertEquals(3, serSchema.types.size)
val l1 = serSchema.carpenterSchema()
/* since we're using an envelope generated by seilaising classes defined locally
it's extremely unlikely we'd need to carpent any classes */
assertEquals(0, l1.size)
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A"))
val l2 = curruptSchema.carpenterSchema()
assertEquals(1, l2.size)
assertEquals(curruptName("${this.javaClass.name}\$$testName\$A"), l2[0].name)
assertEquals(2, l2[0].interfaces.size)
assert (net.corda.carpenter.I::class.java in l2[0].interfaces)
assert (net.corda.carpenter.III::class.java in l2[0].interfaces)
val aBuilder = ClassCarpenter().build(l2[0])
val objA = aBuilder.constructors[0].newInstance(testI, testIII)
val i = objA as I
val iii = objA as III
assertEquals(aBuilder.getMethod("getI").invoke(objA), testI)
assertEquals(aBuilder.getMethod("getIii").invoke(objA), testIII)
assertEquals(a.i, i.i)
assertEquals(a.i, iii.i)
assertEquals(a.iii, iii.iii)
}
@Test
fun memberInterface() {
val testI = 25
val testIIII = 50
val testName = "memberInterface"
class A(override val i: Int) : I
class B(override val i : I, override val iiii : Int) : IIII
val a = A(testI)
val b = B(a, testIIII)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
assert(obj.first is B)
val serSchema = obj.second.schema
/*
* class A
* class A's interface (class I)
* class B
* class B's interface (class IIII)
*/
assertEquals(4, serSchema.types.size)
val curruptSchema = curruptName (serSchema, listOf ("${this.javaClass.name}\$$testName\$A",
"${this.javaClass.name}\$$testName\$B"))
val cSchema = curruptSchema.carpenterSchema()
assertEquals(2, cSchema.size)
val aCarpenterSchema = cSchema.find { it.name == curruptName("${this.javaClass.name}\$$testName\$A") }
val bCarpenterSchema = cSchema.find { it.name == curruptName("${this.javaClass.name}\$$testName\$B") }
assert(aCarpenterSchema != null)
assert(bCarpenterSchema != null)
val cc = ClassCarpenter()
val cc2 = ClassCarpenter()
val bBuilder = cc.build(bCarpenterSchema!!)
bBuilder.constructors[0].newInstance(a, testIIII)
val aBuilder = cc.build(aCarpenterSchema!!)
val objA = aBuilder.constructors[0].newInstance(testI)
/* build a second B this time using our constructed instane of A and not the
local one we pre defined */
bBuilder.constructors[0].newInstance(objA, testIIII)
/* whittle and instantiate a different A with a new class loader */
val aBuilder2 = cc2.build(aCarpenterSchema)
val objA2 = aBuilder2.constructors[0].newInstance(testI)
bBuilder.constructors[0].newInstance(objA2, testIIII)
}
/* this time remove the nested interface from out set of known classes forcing us
to whittle it */
@Test
fun memberInterface2() {
val testI = 25
val testIIII = 50
val testName = "memberInterface2"
class A(override val i: Int) : I
class B(override val i : I, override val iiii : Int) : IIII
val a = A(testI)
val b = B(a, testIIII)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise(b))
assert(obj.first is B)
val serSchema = obj.second.schema
/*
* The classes we're expecting to find:
* class A
* class A's interface (class I)
* class B
* class B's interface (class IIII)
*/
assertEquals(4, serSchema.types.size)
val curruptSchema = curruptName (serSchema, listOf (
"${this.javaClass.name}\$$testName\$A",
"${this.javaClass.getPackage().name}.I"))
val carpenterSchema = curruptSchema.carpenterSchema()
val aCarpenterSchema = carpenterSchema.find { it.name == curruptName("${this.javaClass.name}\$$testName\$A") }
// val bCarpenterSchema = carpenterSchema.find { it.name == curruptName("${this.javaClass.getPackage().name}.I") }
val cc = ClassCarpenter()
val aBuilder = cc.build (aCarpenterSchema!!)
aBuilder.constructors[0].newInstance(testI)
/*
var cc2 = ClassCarpenter()
var bBuilder = cc.build(bCarpenterSchema!!)
val objB = bBuilder.constructors[0].newInstance(a, testIIII)
var aBuilder = cc.build(aCarpenterSchema!!)
val objA = aBuilder.constructors[0].newInstance(testI)
/* build a second B this time using our constructed instane of A and not the
local one we pre defined */
val objB2 = bBuilder.constructors[0].newInstance(objA, testIIII)
/* whittle and instantiate a different A with a new class loader */
var aBuilder2 = cc2.build(aCarpenterSchema!!)
val objA2 = aBuilder2.constructors[0].newInstance(testI)
val objB3 = bBuilder.constructors[0].newInstance(objA2, testIIII)
*/
}
}

View File

@ -38,7 +38,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests {
assertEquals("b", amqpSchema.fields[1].name)
assertEquals("int", amqpSchema.fields[1].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val p = pinochio.constructors[0].newInstance(testA, testB)
@ -74,7 +74,7 @@ class MultiMemberCompositeSchemaToClassCarpenterTests {
assertEquals("b", amqpSchema.fields[1].name)
assertEquals("string", amqpSchema.fields[1].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val p = pinochio.constructors[0].newInstance(testA, testB)

View File

@ -1,6 +1,7 @@
package net.corda.carpenter
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.ClassCarpenterSchema
import net.corda.core.serialization.amqp.*
import org.junit.Test
import kotlin.test.assertEquals
@ -17,8 +18,8 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
@CordaSerializable
data class A(val a : Int)
var a = A (test)
val a = A (test)
val obj = DeserializationInput(factory).deserializeRtnEnvelope(serialise (a))
assert (obj.first is A)
@ -28,13 +29,13 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals (1, obj.second.schema.types.size)
assert (obj.second.schema.types[0] is CompositeType)
var amqpSchema = obj.second.schema.types[0] as CompositeType
val amqpSchema = obj.second.schema.types[0] as CompositeType
assertEquals (1, amqpSchema.fields.size)
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("int", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
val pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val p = pinochio.constructors[0].newInstance (test)
@ -64,7 +65,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("string", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val p = pinochio.constructors[0].newInstance (test)
@ -128,7 +129,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("long", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val p = pinochio.constructors[0].newInstance (test)
@ -159,7 +160,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("short", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val p = pinochio.constructors[0].newInstance (test)
@ -223,7 +224,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("double", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val p = pinochio.constructors[0].newInstance (test)
@ -254,7 +255,7 @@ class SingleMemberCompositeSchemaToClassCarpenterTests {
assertEquals ("a", amqpSchema.fields[0].name)
assertEquals ("float", amqpSchema.fields[0].type)
var pinochio = ClassCarpenter().build(ClassCarpenter.Schema(amqpSchema.name, amqpSchema.carpenterSchema()))
var pinochio = ClassCarpenter().build(amqpSchema.carpenterSchema())
val p = pinochio.constructors[0].newInstance (test)