Improved unit tests.

Cleaned up project structure a bit.
This commit is contained in:
sofusmortensen 2016-03-22 12:15:47 +00:00
parent 3675675277
commit 527b0f4817
9 changed files with 211 additions and 59 deletions

2
.gitignore vendored
View File

@ -13,7 +13,7 @@ tags
.gradle
/build/
/contracts/build
/standalone.test/build
/contracts/isolated/build
/core/build
/docs/build/doctrees

View File

@ -41,6 +41,9 @@ repositories {
configurations {
quasar
// we don't want isolated.jar in classPath, since we want to test jar being dynamically loaded as an attachment
runtime.exclude module: 'isolated'
}
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
@ -48,6 +51,7 @@ configurations {
dependencies {
compile project(':contracts')
testCompile project(':contracts:isolated')
compile "com.google.code.findbugs:jsr305:3.0.1"
compile "org.slf4j:slf4j-jdk14:1.7.13"

View File

@ -0,0 +1,80 @@
import com.google.common.io.ByteStreams
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.nio.file.attribute.FileTime
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import java.util.zip.ZipFile
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "com.google.guava:guava:19.0"
}
}
// Custom Gradle plugin that attempts to make the resulting jar file deterministic.
// Ie. same contract definition should result when compiled in same jar file.
// This is done by removing date time stamps from the files inside the jar.
class CanonicalizerPlugin implements Plugin<Project> {
void apply(Project project) {
project.getTasks().getByName('jar').doLast() {
def zipPath = (String) project.jar.archivePath
def destPath = Files.createTempFile("processzip", null)
def zeroTime = FileTime.fromMillis(0)
def input = new ZipFile(zipPath)
def entries = input.entries().toList().sort { it.name }
def output = new ZipOutputStream(new FileOutputStream(destPath.toFile()))
output.setMethod(ZipOutputStream.DEFLATED)
entries.each {
def newEntry = new ZipEntry( it.name )
newEntry.setLastModifiedTime(zeroTime)
newEntry.setCreationTime(zeroTime)
newEntry.compressedSize = -1
newEntry.size = it.size
newEntry.crc = it.crc
output.putNextEntry(newEntry)
ByteStreams.copy(input.getInputStream(it), output)
output.closeEntry()
}
output.close()
input.close()
Files.move(destPath, Paths.get(zipPath), StandardCopyOption.REPLACE_EXISTING)
}
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: CanonicalizerPlugin
repositories {
mavenCentral()
mavenLocal()
mavenCentral()
jcenter()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
}
dependencies {
compile project(':core')
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
* set forth therein.
*
* All other rights reserved.
*/
package contracts.isolated
import core.Contract
import core.ContractState
import core.TransactionForVerification
import core.crypto.SecureHash
// The dummy contract doesn't do anything useful. It exists for testing purposes.
val ANOTHER_DUMMY_PROGRAM_ID = SecureHash.sha256("dummy")
class AnotherDummyContract : Contract {
class State : ContractState {
override val programRef: SecureHash = ANOTHER_DUMMY_PROGRAM_ID
}
override fun verify(tx: TransactionForVerification) {
// Always accepts.
}
// The "empty contract"
override val legalContractReference: SecureHash = SecureHash.sha256("https://anotherdummy.org")
}

View File

@ -87,7 +87,7 @@ inline fun <reified T : Any> OpaqueBytes.deserialize(kryo: Kryo = THREAD_LOCAL_K
// The more specific deserialize version results in the bytes being cached, which is faster.
@JvmName("SerializedBytesWireTransaction")
fun SerializedBytes<WireTransaction>.deserialize(): WireTransaction = WireTransaction.deserialize(this)
inline fun <reified T : Any> SerializedBytes<T>.deserialize(): T = bits.deserialize()
inline fun <reified T : Any> SerializedBytes<T>.deserialize(kryo: Kryo = THREAD_LOCAL_KRYO.get(), includeClassName: Boolean = false): T = bits.deserialize(kryo, includeClassName)
/**
* Can be called on any object to convert it to a byte array (wrapped by [SerializedBytes]), regardless of whether

View File

@ -1,4 +1,5 @@
rootProject.name = 'r3prototyping'
include 'contracts'
include 'contracts:isolated'
include 'core'
include 'standalone.test'

View File

@ -3,19 +3,36 @@ package core.node
import core.Contract
import core.MockAttachmentStorage
import core.crypto.SecureHash
import core.serialization.createKryo
import core.serialization.deserialize
import core.serialization.serialize
import org.apache.commons.io.IOUtils
import org.junit.Ignore
import org.junit.Test
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.FileInputStream
import java.net.URL
import java.net.URLClassLoader
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
class ClassLoaderTests {
val ISOLATED_CONTRACTS_JAR_PATH = "contracts/isolated/build/libs/isolated.jar"
@Test
fun `dynamically load AnotherDummyContract from isolated contracts jar`() {
var child = URLClassLoader(arrayOf(URL("file", "", ISOLATED_CONTRACTS_JAR_PATH)))
var contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, child)
var contract = contractClass.newInstance() as Contract
assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.legalContractReference)
}
fun fakeAttachment(filepath: String, content: String): ByteArray {
val bs = ByteArrayOutputStream()
val js = JarOutputStream(bs)
@ -29,7 +46,7 @@ class ClassLoaderTests {
@Test
fun `test MockAttachmentStorage open as jar`() {
val storage = MockAttachmentStorage()
val key = storage.importAttachment( FileInputStream("contracts/build/libs/contracts.jar") )
val key = storage.importAttachment( FileInputStream(ISOLATED_CONTRACTS_JAR_PATH) )
val attachment = storage.openAttachment(key)!!
val jar = attachment.openAsJAR()
@ -42,7 +59,7 @@ class ClassLoaderTests {
var storage = MockAttachmentStorage()
var att0 = storage.importAttachment( FileInputStream("contracts/build/libs/contracts.jar") )
var att0 = storage.importAttachment( FileInputStream(ISOLATED_CONTRACTS_JAR_PATH) )
var att1 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file.txt", "some data")) )
var att2 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file.txt", "some other data")) )
@ -58,7 +75,7 @@ class ClassLoaderTests {
var storage = MockAttachmentStorage()
var att0 = storage.importAttachment( FileInputStream("contracts/build/libs/contracts.jar") )
var att0 = storage.importAttachment( FileInputStream(ISOLATED_CONTRACTS_JAR_PATH) )
var att1 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file1.txt", "some data")) )
var att2 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")) )
@ -69,26 +86,91 @@ class ClassLoaderTests {
}
@Test
fun `loading class Cash`() {
fun `loading class AnotherDummyContract`() {
var storage = MockAttachmentStorage()
var att0 = storage.importAttachment( FileInputStream("contracts/build/libs/contracts.jar") )
var att0 = storage.importAttachment( FileInputStream(ISOLATED_CONTRACTS_JAR_PATH) )
var att1 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file1.txt", "some data")) )
var att2 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")) )
ClassLoader.create( arrayOf( att0, att1, att2 ).map { storage.openAttachment(it)!! } ).use {
var contractClass = Class.forName("contracts.Cash", true, it)
var contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, it)
var contract = contractClass.newInstance() as Contract
assertEquals(SecureHash.sha256("https://www.big-book-of-banking-law.gov/cash-claims.html"), contract.legalContractReference)
assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.legalContractReference)
}
}
@Ignore
@Test
fun `testing Kryo with ClassLoader`() {
assert(false) // todo
fun `verify that contract AnotherDummyContract is not in classPath`() {
assertFailsWith(ClassNotFoundException::class) {
var contractClass = Class.forName("contracts.isolated.AnotherDummyContract")
contractClass.newInstance() as Contract
}
}
@Test
fun `verify that contract DummyContract is in classPath`() {
var contractClass = Class.forName("contracts.DummyContract")
var contract = contractClass.newInstance() as Contract
assertNotNull(contract)
}
fun createContract2Cash() : Contract {
var child = URLClassLoader(arrayOf(URL("file", "", ISOLATED_CONTRACTS_JAR_PATH)))
var contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, child)
var contract = contractClass.newInstance() as Contract
return contract
}
@Test
fun `testing Kryo with ClassLoader (with top level class name)`() {
val contract = createContract2Cash()
val bytes = contract.serialize(includeClassName = true)
var storage = MockAttachmentStorage()
var att0 = storage.importAttachment( FileInputStream(ISOLATED_CONTRACTS_JAR_PATH) )
var att1 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file1.txt", "some data")) )
var att2 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")) )
val clsLoader = ClassLoader.create( arrayOf( att0, att1, att2 ).map { storage.openAttachment(it)!! } )
val kryo = createKryo()
kryo.classLoader = clsLoader
val state2 = bytes.deserialize(kryo, true)
assertNotNull(state2)
}
// top level wrapper
class Data( val contract: Contract )
@Test
fun `testing Kryo with ClassLoader (without top level class name)`() {
val data = Data( createContract2Cash() )
val bytes = data.serialize()
var storage = MockAttachmentStorage()
var att0 = storage.importAttachment( FileInputStream(ISOLATED_CONTRACTS_JAR_PATH) )
var att1 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file1.txt", "some data")) )
var att2 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")) )
val clsLoader = ClassLoader.create( arrayOf( att0, att1, att2 ).map { storage.openAttachment(it)!! } )
val kryo = createKryo()
kryo.classLoader = clsLoader
val state2 = bytes.deserialize(kryo)
assertNotNull(state2)
}
}

View File

@ -1,25 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "com.google.guava:guava:19.0"
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
repositories {
mavenCentral()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
}
dependencies {
testCompile 'junit:junit:4.12'
compile project(':core')
}

View File

@ -1,21 +0,0 @@
import core.Contract
import core.crypto.SecureHash
import org.junit.Test
import java.net.URL
import java.net.URLClassLoader
import java.util.jar.JarInputStream
import kotlin.test.assertEquals
class LoaderTests {
@Test
fun `dynamically load Cash class from contracts jar`() {
var child = URLClassLoader(arrayOf(URL("file", "", "../contracts/build/libs/contracts.jar")))
var contractClass = Class.forName("contracts.Cash", true, child)
var contract = contractClass.newInstance() as Contract
assertEquals(SecureHash.sha256("https://www.big-book-of-banking-law.gov/cash-claims.html"), contract.legalContractReference)
}
}