mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
CORDA-2088: Simplified the TestCordapp public API (#4064)
The entry point to the API has been simplified to just requireing a list of packages to scan, with sensible defaults provided for the metadata. Because of the wither methods, having parameters for the metadata (with default values) seems unnecessary. Also the ability to scan just individual classes has been made internal, as it seems unlikely app developers would need that level of control when testing their apps. TestCordappImpl is a data class and thus acts as a natural key for the Jar caching, where previously the key was the package names. This fixes an issue where it was not possible to create two CorDapp Jars of the same package but different metadata.
This commit is contained in:
parent
b769ad80bd
commit
2c9a942e1a
@ -5822,8 +5822,6 @@ public interface net.corda.testing.driver.DriverDSL
|
|||||||
@NotNull
|
@NotNull
|
||||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.NodeHandle> startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List<net.corda.testing.node.User>, net.corda.testing.driver.VerifierType, java.util.Map<String, ?>, Boolean, String)
|
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.NodeHandle> startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List<net.corda.testing.node.User>, net.corda.testing.driver.VerifierType, java.util.Map<String, ?>, Boolean, String)
|
||||||
@NotNull
|
@NotNull
|
||||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.NodeHandle> startNode(net.corda.testing.driver.NodeParameters, net.corda.core.identity.CordaX500Name, java.util.List<net.corda.testing.node.User>, net.corda.testing.driver.VerifierType, java.util.Map<String, ?>, Boolean, String, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>, boolean)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle)
|
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle)
|
||||||
@NotNull
|
@NotNull
|
||||||
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle, String)
|
public abstract net.corda.core.concurrent.CordaFuture<net.corda.testing.driver.WebserverHandle> startWebserver(net.corda.testing.driver.NodeHandle, String)
|
||||||
@ -5832,10 +5830,7 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O
|
|||||||
public <init>()
|
public <init>()
|
||||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters)
|
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters)
|
||||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, boolean)
|
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, boolean)
|
||||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Map<String, ?>, boolean, boolean, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
|
||||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
|
||||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean)
|
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean)
|
||||||
public <init>(boolean, java.nio.file.Path, net.corda.testing.driver.PortAllocation, net.corda.testing.driver.PortAllocation, java.util.Map<String, String>, boolean, boolean, boolean, java.util.List<net.corda.testing.node.NotarySpec>, java.util.List<String>, net.corda.testing.driver.JmxPolicy, net.corda.core.node.NetworkParameters, boolean, boolean, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
|
||||||
public final boolean component1()
|
public final boolean component1()
|
||||||
@NotNull
|
@NotNull
|
||||||
public final java.util.List<String> component10()
|
public final java.util.List<String> component10()
|
||||||
@ -6028,75 +6023,6 @@ public static final class net.corda.testing.driver.PortAllocation$Incremental ex
|
|||||||
public final java.util.concurrent.atomic.AtomicInteger getPortCounter()
|
public final java.util.concurrent.atomic.AtomicInteger getPortCounter()
|
||||||
public int nextPort()
|
public int nextPort()
|
||||||
##
|
##
|
||||||
@DoNotImplement
|
|
||||||
public interface net.corda.testing.driver.TestCorDapp
|
|
||||||
@NotNull
|
|
||||||
public abstract java.util.Set<Class<?>> getClasses()
|
|
||||||
@NotNull
|
|
||||||
public abstract String getName()
|
|
||||||
@NotNull
|
|
||||||
public abstract java.util.Set<java.net.URL> getResources()
|
|
||||||
@NotNull
|
|
||||||
public abstract String getTitle()
|
|
||||||
@NotNull
|
|
||||||
public abstract String getVendor()
|
|
||||||
@NotNull
|
|
||||||
public abstract String getVersion()
|
|
||||||
@NotNull
|
|
||||||
public abstract java.nio.file.Path packageAsJarInDirectory(java.nio.file.Path)
|
|
||||||
public abstract void packageAsJarWithPath(java.nio.file.Path)
|
|
||||||
##
|
|
||||||
public static final class net.corda.testing.driver.TestCorDapp$Factory extends java.lang.Object
|
|
||||||
public <init>()
|
|
||||||
@NotNull
|
|
||||||
public static final net.corda.testing.driver.TestCorDapp$Mutable create(String, String, String, String, java.util.Set<? extends Class<?>>, kotlin.jvm.functions.Function2<? super String, ? super java.net.URL, Boolean>)
|
|
||||||
public static final net.corda.testing.driver.TestCorDapp$Factory$Companion Companion
|
|
||||||
##
|
|
||||||
public static final class net.corda.testing.driver.TestCorDapp$Factory$Companion extends java.lang.Object
|
|
||||||
@NotNull
|
|
||||||
public final net.corda.testing.driver.TestCorDapp$Mutable create(String, String, String, String, java.util.Set<? extends Class<?>>, kotlin.jvm.functions.Function2<? super String, ? super java.net.URL, Boolean>)
|
|
||||||
##
|
|
||||||
@DoNotImplement
|
|
||||||
public static interface net.corda.testing.driver.TestCorDapp$Mutable extends net.corda.testing.driver.TestCorDapp
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minus(Class<?>)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackage(Package)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackage(String)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(Package, Package...)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(String, String...)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusPackages(java.util.Set<String>)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable minusResource(String, java.net.URL)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plus(Class<?>)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackage(Package)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackage(String)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(Package, Package...)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(String, String...)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusPackages(java.util.Set<String>)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable plusResource(String, java.net.URL)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withClasses(java.util.Set<? extends Class<?>>)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withName(String)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withTitle(String)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withVendor(String)
|
|
||||||
@NotNull
|
|
||||||
public abstract net.corda.testing.driver.TestCorDapp$Mutable withVersion(String)
|
|
||||||
##
|
|
||||||
public final class net.corda.testing.driver.VerifierType extends java.lang.Enum
|
public final class net.corda.testing.driver.VerifierType extends java.lang.Enum
|
||||||
protected <init>()
|
protected <init>()
|
||||||
public static net.corda.testing.driver.VerifierType valueOf(String)
|
public static net.corda.testing.driver.VerifierType valueOf(String)
|
||||||
@ -6238,9 +6164,9 @@ public class net.corda.testing.node.MockNetwork extends java.lang.Object
|
|||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
public final net.corda.testing.node.StartedMockNode createNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.StartedMockNode createNode(net.corda.testing.node.MockNodeParameters)
|
public final net.corda.testing.node.StartedMockNode createNode(net.corda.testing.node.MockNodeParameters)
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -6256,9 +6182,9 @@ public class net.corda.testing.node.MockNetwork extends java.lang.Object
|
|||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.core.identity.CordaX500Name, Integer, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters)
|
public final net.corda.testing.node.UnstartedMockNode createUnstartedNode(net.corda.testing.node.MockNodeParameters)
|
||||||
@NotNull
|
@NotNull
|
||||||
@ -6339,8 +6265,7 @@ public final class net.corda.testing.node.MockNetworkParameters extends java.lan
|
|||||||
public final class net.corda.testing.node.MockNodeParameters extends java.lang.Object
|
public final class net.corda.testing.node.MockNodeParameters extends java.lang.Object
|
||||||
public <init>()
|
public <init>()
|
||||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||||
public <init>(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public final Integer component1()
|
public final Integer component1()
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -6352,9 +6277,7 @@ public final class net.corda.testing.node.MockNodeParameters extends java.lang.O
|
|||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>)
|
||||||
@NotNull
|
@NotNull
|
||||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.List<String>)
|
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Collection<? extends net.corda.testing.node.TestCordapp>)
|
||||||
@NotNull
|
|
||||||
public final net.corda.testing.node.MockNodeParameters copy(Integer, net.corda.core.identity.CordaX500Name, java.math.BigInteger, kotlin.jvm.functions.Function1<? super net.corda.node.services.config.NodeConfiguration, ?>, java.util.Set<? extends net.corda.testing.driver.TestCorDapp>)
|
|
||||||
public boolean equals(Object)
|
public boolean equals(Object)
|
||||||
@NotNull
|
@NotNull
|
||||||
public final kotlin.jvm.functions.Function1<net.corda.node.services.config.NodeConfiguration, Object> getConfigOverrides()
|
public final kotlin.jvm.functions.Function1<net.corda.node.services.config.NodeConfiguration, Object> getConfigOverrides()
|
||||||
|
@ -15,7 +15,6 @@ import net.corda.core.serialization.deserialize
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.node.VersionInfo
|
|
||||||
import net.corda.node.cordapp.CordappLoader
|
import net.corda.node.cordapp.CordappLoader
|
||||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||||
@ -25,16 +24,13 @@ import net.corda.testing.core.SerializationEnvironmentRule
|
|||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.internal.MockCordappConfigProvider
|
import net.corda.testing.internal.MockCordappConfigProvider
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
|
import net.corda.testing.node.internal.TestCordappDirectories
|
||||||
import net.corda.testing.node.internal.cordappsForPackages
|
import net.corda.testing.node.internal.cordappsForPackages
|
||||||
import net.corda.testing.node.internal.getTimestampAsDirectoryName
|
|
||||||
import net.corda.testing.node.internal.packageInDirectory
|
|
||||||
import net.corda.testing.services.MockAttachmentStorage
|
import net.corda.testing.services.MockAttachmentStorage
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
class AttachmentsClassLoaderStaticContractTests {
|
class AttachmentsClassLoaderStaticContractTests {
|
||||||
private companion object {
|
private companion object {
|
||||||
@ -101,22 +97,11 @@ class AttachmentsClassLoaderStaticContractTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `verify that contract DummyContract is in classPath`() {
|
fun `verify that contract DummyContract is in classPath`() {
|
||||||
val contractClass = Class.forName("net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract")
|
val contractClass = Class.forName("net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract")
|
||||||
val contract = contractClass.newInstance() as Contract
|
assertThat(contractClass.newInstance()).isInstanceOf(Contract::class.java)
|
||||||
|
|
||||||
assertNotNull(contract)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
private fun cordappLoaderForPackages(packages: Collection<String>): CordappLoader {
|
||||||
|
val dirs = cordappsForPackages(packages).map { TestCordappDirectories.getJarDirectory(it) }
|
||||||
val cordapps = cordappsForPackages(packages)
|
return JarScanningCordappLoader.fromDirectories(dirs)
|
||||||
return testDirectory().let { directory ->
|
|
||||||
cordapps.packageInDirectory(directory)
|
|
||||||
JarScanningCordappLoader.fromDirectories(listOf(directory), VersionInfo.UNKNOWN)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private fun testDirectory(): Path {
|
|
||||||
|
|
||||||
return Paths.get("build", getTimestampAsDirectoryName())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,6 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.concurrent.transpose
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.internal.packageName
|
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.unwrap
|
import net.corda.core.utilities.unwrap
|
||||||
@ -12,8 +11,8 @@ import net.corda.testing.core.ALICE_NAME
|
|||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.testing.core.singleIdentity
|
import net.corda.testing.core.singleIdentity
|
||||||
import net.corda.testing.driver.DriverParameters
|
import net.corda.testing.driver.DriverParameters
|
||||||
import net.corda.testing.driver.TestCorDapp
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
|
import net.corda.testing.node.internal.cordappForClasses
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -46,23 +45,18 @@ class AsymmetricCorDappsTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun noSharedCorDappsWithAsymmetricSpecificClasses() {
|
fun `no shared cordapps with asymmetric specific classes`() {
|
||||||
|
|
||||||
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = emptySet())) {
|
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = emptySet())) {
|
||||||
|
val nodeA = startNode(providedName = ALICE_NAME, additionalCordapps = setOf(cordappForClasses(Ping::class.java))).getOrThrow()
|
||||||
val nodeA = startNode(providedName = ALICE_NAME, additionalCordapps = setOf(TestCorDapp.Factory.create("Szymon CorDapp", "1.0", classes = setOf(Ping::class.java)))).getOrThrow()
|
val nodeB = startNode(providedName = BOB_NAME, additionalCordapps = setOf(cordappForClasses(Ping::class.java, Pong::class.java))).getOrThrow()
|
||||||
val nodeB = startNode(providedName = BOB_NAME, additionalCordapps = setOf(TestCorDapp.Factory.create("Szymon CorDapp", "1.0", classes = setOf(Ping::class.java, Pong::class.java)))).getOrThrow()
|
|
||||||
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
|
nodeA.rpc.startFlow(::Ping, nodeB.nodeInfo.singleIdentity(), 1).returnValue.getOrThrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun sharedCorDappsWithAsymmetricSpecificClasses() {
|
fun `shared cordapps with asymmetric specific classes`() {
|
||||||
|
val sharedCordapp = cordappForClasses(Ping::class.java)
|
||||||
val resourceName = "cordapp.properties"
|
val cordappForNodeB = cordappForClasses(Pong::class.java)
|
||||||
val cordappPropertiesResource = this::class.java.getResource(resourceName)
|
|
||||||
val sharedCordapp = TestCorDapp.Factory.create("shared", "1.0", classes = setOf(Ping::class.java)).plusResource("${AsymmetricCorDappsTests::class.java.packageName}.$resourceName", cordappPropertiesResource)
|
|
||||||
val cordappForNodeB = TestCorDapp.Factory.create("nodeB_only", "1.0", classes = setOf(Pong::class.java))
|
|
||||||
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = setOf(sharedCordapp))) {
|
driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = setOf(sharedCordapp))) {
|
||||||
|
|
||||||
val (nodeA, nodeB) = listOf(startNode(providedName = ALICE_NAME), startNode(providedName = BOB_NAME, additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
|
val (nodeA, nodeB) = listOf(startNode(providedName = ALICE_NAME), startNode(providedName = BOB_NAME, additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
|
||||||
@ -71,12 +65,9 @@ class AsymmetricCorDappsTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun sharedCorDappsWithAsymmetricSpecificClassesInProcess() {
|
fun `shared cordapps with asymmetric specific classes in process`() {
|
||||||
|
val sharedCordapp = cordappForClasses(Ping::class.java)
|
||||||
val resourceName = "cordapp.properties"
|
val cordappForNodeB = cordappForClasses(Pong::class.java)
|
||||||
val cordappPropertiesResource = this::class.java.getResource(resourceName)
|
|
||||||
val sharedCordapp = TestCorDapp.Factory.create("shared", "1.0", classes = setOf(Ping::class.java)).plusResource("${AsymmetricCorDappsTests::class.java.packageName}.$resourceName", cordappPropertiesResource)
|
|
||||||
val cordappForNodeB = TestCorDapp.Factory.create("nodeB_only", "1.0", classes = setOf(Pong::class.java))
|
|
||||||
driver(DriverParameters(startNodesInProcess = true, cordappsForAllNodes = setOf(sharedCordapp))) {
|
driver(DriverParameters(startNodesInProcess = true, cordappsForAllNodes = setOf(sharedCordapp))) {
|
||||||
|
|
||||||
val (nodeA, nodeB) = listOf(startNode(providedName = ALICE_NAME), startNode(providedName = BOB_NAME, additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
|
val (nodeA, nodeB) = listOf(startNode(providedName = ALICE_NAME), startNode(providedName = BOB_NAME, additionalCordapps = setOf(cordappForNodeB))).transpose().getOrThrow()
|
||||||
|
@ -1,27 +1,30 @@
|
|||||||
package net.corda.node.flows
|
package net.corda.node.flows
|
||||||
|
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.list
|
import net.corda.core.internal.list
|
||||||
|
import net.corda.core.internal.moveTo
|
||||||
import net.corda.core.internal.readLines
|
import net.corda.core.internal.readLines
|
||||||
import net.corda.core.messaging.startTrackedFlow
|
import net.corda.core.messaging.startTrackedFlow
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.CheckpointIncompatibleException
|
import net.corda.node.internal.CheckpointIncompatibleException
|
||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.node.services.Permissions.Companion.invokeRpc
|
import net.corda.testMessage.*
|
||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
|
||||||
import net.corda.testMessage.Message
|
|
||||||
import net.corda.testMessage.MessageState
|
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.testing.core.singleIdentity
|
import net.corda.testing.core.singleIdentity
|
||||||
|
import net.corda.testing.driver.DriverDSL
|
||||||
import net.corda.testing.driver.DriverParameters
|
import net.corda.testing.driver.DriverParameters
|
||||||
import net.corda.testing.driver.TestCorDapp
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.TestCordapp
|
||||||
import net.corda.testing.node.internal.ListenProcessDeathException
|
import net.corda.testing.node.internal.ListenProcessDeathException
|
||||||
|
import net.corda.testing.node.internal.TestCordappDirectories
|
||||||
|
import net.corda.testing.node.internal.cordappForClasses
|
||||||
|
import net.test.cordapp.v1.Record
|
||||||
import net.test.cordapp.v1.SendMessageFlow
|
import net.test.cordapp.v1.SendMessageFlow
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
@ -30,125 +33,111 @@ import kotlin.test.assertNotNull
|
|||||||
class FlowCheckpointVersionNodeStartupCheckTest {
|
class FlowCheckpointVersionNodeStartupCheckTest {
|
||||||
companion object {
|
companion object {
|
||||||
val message = Message("Hello world!")
|
val message = Message("Hello world!")
|
||||||
val classes = setOf(net.corda.testMessage.MessageState::class.java,
|
val defaultCordapp = cordappForClasses(
|
||||||
net.corda.testMessage.MessageContract::class.java,
|
MessageState::class.java,
|
||||||
net.test.cordapp.v1.SendMessageFlow::class.java,
|
MessageContract::class.java,
|
||||||
net.corda.testMessage.MessageSchema::class.java,
|
SendMessageFlow::class.java,
|
||||||
net.corda.testMessage.MessageSchemaV1::class.java,
|
MessageSchema::class.java,
|
||||||
net.test.cordapp.v1.Record::class.java)
|
MessageSchemaV1::class.java,
|
||||||
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery"), invokeRpc("vaultTrack")))
|
Record::class.java
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `restart node successfully with suspended flow`() {
|
fun `restart node successfully with suspended flow`() {
|
||||||
|
return driver(parametersForRestartingNodes(listOf(defaultCordapp))) {
|
||||||
val cordapps = setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes))
|
createSuspendedFlowInBob(cordapps = emptySet())
|
||||||
|
// Bob will resume the flow
|
||||||
return driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, cordappsForAllNodes = cordapps)) {
|
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
{
|
startNode(providedName = BOB_NAME).getOrThrow()
|
||||||
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME).getOrThrow()
|
val page = alice.rpc.vaultTrack(MessageState::class.java)
|
||||||
val bob = startNode(rpcUsers = listOf(user), providedName = BOB_NAME).getOrThrow()
|
val result = if (page.snapshot.states.isNotEmpty()) {
|
||||||
alice.stop()
|
page.snapshot.states.first()
|
||||||
CordaRPCClient(bob.rpcAddress).start(user.username, user.password).use {
|
} else {
|
||||||
val flowTracker = it.proxy.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
|
val r = page.updates.timeout(5, TimeUnit.SECONDS).take(1).toBlocking().single()
|
||||||
//wait until Bob progresses as far as possible because alice node is off
|
if (r.consumed.isNotEmpty()) r.consumed.first() else r.produced.first()
|
||||||
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
|
}
|
||||||
}
|
|
||||||
bob.stop()
|
|
||||||
}()
|
|
||||||
val result = {
|
|
||||||
//Bob will resume the flow
|
|
||||||
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false)).getOrThrow()
|
|
||||||
startNode(providedName = BOB_NAME, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to false)).getOrThrow()
|
|
||||||
CordaRPCClient(alice.rpcAddress).start(user.username, user.password).use {
|
|
||||||
val page = it.proxy.vaultTrack(MessageState::class.java)
|
|
||||||
if (page.snapshot.states.isNotEmpty()) {
|
|
||||||
page.snapshot.states.first()
|
|
||||||
} else {
|
|
||||||
val r = page.updates.timeout(5, TimeUnit.SECONDS).take(1).toBlocking().single()
|
|
||||||
if (r.consumed.isNotEmpty()) r.consumed.first() else r.produced.first()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
assertNotNull(result)
|
assertNotNull(result)
|
||||||
assertEquals(message, result.state.data.message)
|
assertEquals(message, result.state.data.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertNodeRestartFailure(
|
@Test
|
||||||
cordapps: Set<TestCorDapp>?,
|
fun `restart node with incompatible version of suspended flow due to different jar name`() {
|
||||||
cordappsVersionAtStartup: Set<TestCorDapp>,
|
driver(parametersForRestartingNodes()) {
|
||||||
cordappsVersionAtRestart: Set<TestCorDapp>,
|
val cordapp = defaultCordapp.withName("different-jar-name-test-${UUID.randomUUID()}")
|
||||||
reuseAdditionalCordappsAtRestart: Boolean,
|
// Create the CorDapp jar file manually first to get hold of the directory that will contain it so that we can
|
||||||
assertNodeLogs: String
|
// rename the filename later. The cordappDir, which acts as pointer to the jar file, does not get renamed.
|
||||||
) {
|
val cordappDir = TestCordappDirectories.getJarDirectory(cordapp)
|
||||||
|
val cordappJar = cordappDir.list().single()
|
||||||
|
|
||||||
return driver(DriverParameters(
|
createSuspendedFlowInBob(setOf(cordapp))
|
||||||
startNodesInProcess = false, // start nodes in separate processes to ensure CordappLoader is not shared between restarts
|
|
||||||
inMemoryDB = false, // ensure database is persisted between node restarts so we can keep suspended flow in Bob's node
|
|
||||||
cordappsForAllNodes = cordapps)
|
|
||||||
) {
|
|
||||||
val bobLogFolder = {
|
|
||||||
val alice = startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, additionalCordapps = cordappsVersionAtStartup).getOrThrow()
|
|
||||||
val bob = startNode(rpcUsers = listOf(user), providedName = BOB_NAME, additionalCordapps = cordappsVersionAtStartup).getOrThrow()
|
|
||||||
alice.stop()
|
|
||||||
CordaRPCClient(bob.rpcAddress).start(user.username, user.password).use {
|
|
||||||
val flowTracker = it.proxy.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
|
|
||||||
// wait until Bob progresses as far as possible because Alice node is offline
|
|
||||||
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
|
|
||||||
}
|
|
||||||
val logFolder = bob.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
|
|
||||||
// SendMessageFlow suspends in Bob node
|
|
||||||
bob.stop()
|
|
||||||
logFolder
|
|
||||||
}()
|
|
||||||
|
|
||||||
startNode(rpcUsers = listOf(user), providedName = ALICE_NAME, customOverrides = mapOf("devMode" to false),
|
// Rename the jar file. TestCordappDirectories caches the location of the jar file but the use of the random
|
||||||
additionalCordapps = cordappsVersionAtRestart, regenerateCordappsOnStart = !reuseAdditionalCordappsAtRestart).getOrThrow()
|
// UUID in the name means there's zero chance of contaminating another test.
|
||||||
|
cordappJar.moveTo(cordappDir / "renamed-${cordappJar.fileName}")
|
||||||
|
|
||||||
assertFailsWith(ListenProcessDeathException::class) {
|
assertBobFailsToStartWithLogMessage(
|
||||||
startNode(providedName = BOB_NAME, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to false),
|
setOf(cordapp),
|
||||||
additionalCordapps = cordappsVersionAtRestart, regenerateCordappsOnStart = !reuseAdditionalCordappsAtRestart).getOrThrow()
|
CheckpointIncompatibleException.FlowNotInstalledException(SendMessageFlow::class.java).message
|
||||||
}
|
)
|
||||||
|
|
||||||
val logFile = bobLogFolder.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }
|
|
||||||
val numberOfNodesThatLogged = logFile.readLines { it.filter { assertNodeLogs in it }.count() }
|
|
||||||
assertEquals(1, numberOfNodesThatLogged)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `restart nodes with incompatible version of suspended flow due to different jar name`() {
|
fun `restart node with incompatible version of suspended flow due to different jar hash`() {
|
||||||
|
driver(parametersForRestartingNodes()) {
|
||||||
|
val originalCordapp = defaultCordapp.withName("different-jar-hash-test-${UUID.randomUUID()}")
|
||||||
|
val originalCordappJar = TestCordappDirectories.getJarDirectory(originalCordapp).list().single()
|
||||||
|
|
||||||
assertNodeRestartFailure(
|
createSuspendedFlowInBob(setOf(originalCordapp))
|
||||||
emptySet(),
|
|
||||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
// The vendor is part of the MANIFEST so changing it is sufficient to change the jar hash
|
||||||
setOf(TestCorDapp.Factory.create("testJar2", "1.0", classes = classes)),
|
val modifiedCordapp = originalCordapp.withVendor("${originalCordapp.vendor}-modified")
|
||||||
false,
|
val modifiedCordappJar = TestCordappDirectories.getJarDirectory(modifiedCordapp).list().single()
|
||||||
CheckpointIncompatibleException.FlowNotInstalledException(SendMessageFlow::class.java).message)
|
modifiedCordappJar.moveTo(originalCordappJar, REPLACE_EXISTING)
|
||||||
|
|
||||||
|
assertBobFailsToStartWithLogMessage(
|
||||||
|
setOf(originalCordapp),
|
||||||
|
// The part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
|
||||||
|
"that is incompatible with the current installed version of"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private fun DriverDSL.createSuspendedFlowInBob(cordapps: Set<TestCordapp>) {
|
||||||
fun `restart nodes with incompatible version of suspended flow`() {
|
val (alice, bob) = listOf(ALICE_NAME, BOB_NAME)
|
||||||
|
.map { startNode(providedName = it, additionalCordapps = cordapps) }
|
||||||
assertNodeRestartFailure(
|
.transpose()
|
||||||
emptySet(),
|
.getOrThrow()
|
||||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
alice.stop()
|
||||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes + net.test.cordapp.v1.SendMessageFlow::class.java)),
|
val flowTracker = bob.rpc.startTrackedFlow(::SendMessageFlow, message, defaultNotaryIdentity, alice.nodeInfo.singleIdentity()).progress
|
||||||
false,
|
// Wait until Bob progresses as far as possible because Alice node is offline
|
||||||
// the part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
|
flowTracker.takeFirst { it == SendMessageFlow.Companion.FINALISING_TRANSACTION.label }.toBlocking().single()
|
||||||
"that is incompatible with the current installed version of")
|
bob.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private fun DriverDSL.assertBobFailsToStartWithLogMessage(cordapps: Collection<TestCordapp>, logMessage: String) {
|
||||||
fun `restart nodes with incompatible version of suspended flow due to different timestamps only`() {
|
assertFailsWith(ListenProcessDeathException::class) {
|
||||||
|
startNode(
|
||||||
|
providedName = BOB_NAME,
|
||||||
|
customOverrides = mapOf("devMode" to false),
|
||||||
|
additionalCordapps = cordapps,
|
||||||
|
regenerateCordappsOnStart = true
|
||||||
|
).getOrThrow()
|
||||||
|
}
|
||||||
|
|
||||||
assertNodeRestartFailure(
|
val logDir = baseDirectory(BOB_NAME) / NodeStartup.LOGS_DIRECTORY_NAME
|
||||||
emptySet(),
|
val logFile = logDir.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }
|
||||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
val matchingLineCount = logFile.readLines { it.filter { line -> logMessage in line }.count() }
|
||||||
setOf(TestCorDapp.Factory.create("testJar", "1.0", classes = classes)),
|
assertEquals(1, matchingLineCount)
|
||||||
false,
|
|
||||||
// the part of the log message generated by CheckpointIncompatibleException.FlowVersionIncompatibleException
|
|
||||||
"that is incompatible with the current installed version of")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private fun parametersForRestartingNodes(cordappsForAllNodes: List<TestCordapp> = emptyList()): DriverParameters {
|
||||||
|
return DriverParameters(
|
||||||
|
startNodesInProcess = false, // Start nodes in separate processes to ensure CordappLoader is not shared between restarts
|
||||||
|
inMemoryDB = false, // Ensure database is persisted between node restarts so we can keep suspended flows
|
||||||
|
cordappsForAllNodes = cordappsForAllNodes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,17 +3,11 @@ package net.corda.node.services
|
|||||||
import com.nhaarman.mockito_kotlin.doReturn
|
import com.nhaarman.mockito_kotlin.doReturn
|
||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
import net.corda.core.CordaRuntimeException
|
import net.corda.core.CordaRuntimeException
|
||||||
import net.corda.core.contracts.Contract
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.ContractState
|
|
||||||
import net.corda.core.contracts.PartyAndReference
|
|
||||||
import net.corda.core.contracts.StateAndRef
|
|
||||||
import net.corda.core.contracts.StateRef
|
|
||||||
import net.corda.core.contracts.TransactionState
|
|
||||||
import net.corda.core.cordapp.CordappProvider
|
import net.corda.core.cordapp.CordappProvider
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.concurrent.transpose
|
|
||||||
import net.corda.core.internal.toLedgerTransaction
|
import net.corda.core.internal.toLedgerTransaction
|
||||||
import net.corda.core.node.NetworkParameters
|
import net.corda.core.node.NetworkParameters
|
||||||
import net.corda.core.node.ServicesForResolution
|
import net.corda.core.node.ServicesForResolution
|
||||||
@ -21,19 +15,16 @@ import net.corda.core.node.services.AttachmentStorage
|
|||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.serialization.SerializationFactory
|
import net.corda.core.serialization.SerializationFactory
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.contextLogger
|
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
|
||||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||||
|
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
import net.corda.testing.core.SerializationEnvironmentRule
|
import net.corda.testing.core.SerializationEnvironmentRule
|
||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.driver.DriverDSL
|
|
||||||
import net.corda.testing.driver.DriverParameters
|
import net.corda.testing.driver.DriverParameters
|
||||||
import net.corda.testing.driver.NodeHandle
|
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.internal.MockCordappConfigProvider
|
import net.corda.testing.internal.MockCordappConfigProvider
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
@ -59,7 +50,6 @@ class AttachmentLoadingTests {
|
|||||||
private val appContext get() = provider.getAppContext(cordapp)
|
private val appContext get() = provider.getAppContext(cordapp)
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
private val logger = contextLogger()
|
|
||||||
val isolatedJAR = AttachmentLoadingTests::class.java.getResource("isolated.jar")!!
|
val isolatedJAR = AttachmentLoadingTests::class.java.getResource("isolated.jar")!!
|
||||||
const val ISOLATED_CONTRACT_ID = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
const val ISOLATED_CONTRACT_ID = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||||
|
|
||||||
@ -70,12 +60,6 @@ class AttachmentLoadingTests {
|
|||||||
.asSubclass(FlowLogic::class.java)
|
.asSubclass(FlowLogic::class.java)
|
||||||
val DUMMY_BANK_A = TestIdentity(DUMMY_BANK_A_NAME, 40).party
|
val DUMMY_BANK_A = TestIdentity(DUMMY_BANK_A_NAME, 40).party
|
||||||
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
|
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
|
||||||
private fun DriverDSL.createTwoNodes(): List<NodeHandle> {
|
|
||||||
return listOf(
|
|
||||||
startNode(providedName = bankAName),
|
|
||||||
startNode(providedName = bankBName)
|
|
||||||
).transpose().getOrThrow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val services = object : ServicesForResolution {
|
private val services = object : ServicesForResolution {
|
||||||
|
@ -61,11 +61,13 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths:
|
|||||||
/**
|
/**
|
||||||
* Creates a CordappLoader from multiple directories.
|
* Creates a CordappLoader from multiple directories.
|
||||||
*
|
*
|
||||||
* @param corDappDirectories Directories used to scan for CorDapp JARs.
|
* @param cordappDirs Directories used to scan for CorDapp JARs.
|
||||||
*/
|
*/
|
||||||
fun fromDirectories(corDappDirectories: Iterable<Path>, versionInfo: VersionInfo = VersionInfo.UNKNOWN, extraCordapps: List<CordappImpl> = emptyList()): JarScanningCordappLoader {
|
fun fromDirectories(cordappDirs: Collection<Path>,
|
||||||
logger.info("Looking for CorDapps in ${corDappDirectories.distinct().joinToString(", ", "[", "]")}")
|
versionInfo: VersionInfo = VersionInfo.UNKNOWN,
|
||||||
val paths = corDappDirectories.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }
|
extraCordapps: List<CordappImpl> = emptyList()): JarScanningCordappLoader {
|
||||||
|
logger.info("Looking for CorDapps in ${cordappDirs.distinct().joinToString(", ", "[", "]")}")
|
||||||
|
val paths = cordappDirs.distinct().flatMap(this::jarUrlsInDirectory).map { it.restricted() }
|
||||||
return JarScanningCordappLoader(paths, versionInfo, extraCordapps)
|
return JarScanningCordappLoader(paths, versionInfo, extraCordapps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import net.corda.core.internal.cordapp.CordappImpl.Info.Companion.UNKNOWN_VALUE
|
|||||||
import java.util.jar.Attributes
|
import java.util.jar.Attributes
|
||||||
import java.util.jar.Manifest
|
import java.util.jar.Manifest
|
||||||
|
|
||||||
fun createTestManifest(name: String, title: String, version: String, vendor: String): Manifest {
|
fun createTestManifest(name: String, title: String, version: String, vendor: String, targetVersion: Int): Manifest {
|
||||||
val manifest = Manifest()
|
val manifest = Manifest()
|
||||||
|
|
||||||
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
|
// Mandatory manifest attribute. If not present, all other entries are silently skipped.
|
||||||
@ -20,6 +20,7 @@ fun createTestManifest(name: String, title: String, version: String, vendor: Str
|
|||||||
manifest["Implementation-Title"] = title
|
manifest["Implementation-Title"] = title
|
||||||
manifest["Implementation-Version"] = version
|
manifest["Implementation-Version"] = version
|
||||||
manifest["Implementation-Vendor"] = vendor
|
manifest["Implementation-Vendor"] = vendor
|
||||||
|
manifest["Target-Platform-Version"] = targetVersion.toString()
|
||||||
|
|
||||||
return manifest
|
return manifest
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
key=value
|
|
@ -2,14 +2,12 @@ package net.corda.node.internal.cordapp
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.*
|
||||||
|
import net.corda.core.internal.packageName
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.cordapp.CordappLoader
|
import net.corda.testing.node.internal.TestCordappDirectories
|
||||||
import net.corda.testing.node.internal.cordappsForPackages
|
import net.corda.testing.node.internal.cordappForPackages
|
||||||
import net.corda.testing.node.internal.getTimestampAsDirectoryName
|
|
||||||
import net.corda.testing.node.internal.packageInDirectory
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
|
||||||
@InitiatingFlow
|
@InitiatingFlow
|
||||||
@ -38,7 +36,6 @@ class DummyRPCFlow : FlowLogic<Unit>() {
|
|||||||
|
|
||||||
class JarScanningCordappLoaderTest {
|
class JarScanningCordappLoaderTest {
|
||||||
private companion object {
|
private companion object {
|
||||||
const val testScanPackage = "net.corda.node.internal.cordapp"
|
|
||||||
const val isolatedContractId = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
const val isolatedContractId = "net.corda.finance.contracts.isolated.AnotherDummyContract"
|
||||||
const val isolatedFlowName = "net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator"
|
const val isolatedFlowName = "net.corda.finance.contracts.isolated.IsolatedDummyFlow\$Initiator"
|
||||||
}
|
}
|
||||||
@ -70,32 +67,18 @@ class JarScanningCordappLoaderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `flows are loaded by loader`() {
|
fun `flows are loaded by loader`() {
|
||||||
val loader = cordappLoaderForPackages(listOf(testScanPackage))
|
val dir = TestCordappDirectories.getJarDirectory(cordappForPackages(javaClass.packageName))
|
||||||
|
val loader = JarScanningCordappLoader.fromDirectories(listOf(dir))
|
||||||
|
|
||||||
val actual = loader.cordapps.toTypedArray()
|
|
||||||
// One cordapp from this source tree. In gradle it will also pick up the node jar.
|
// One cordapp from this source tree. In gradle it will also pick up the node jar.
|
||||||
assertThat(actual.size == 0 || actual.size == 1).isTrue()
|
assertThat(loader.cordapps).isNotEmpty
|
||||||
|
|
||||||
val actualCordapp = actual.single { !it.initiatedFlows.isEmpty() }
|
val actualCordapp = loader.cordapps.single { !it.initiatedFlows.isEmpty() }
|
||||||
assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java)
|
assertThat(actualCordapp.initiatedFlows).first().hasSameClassAs(DummyFlow::class.java)
|
||||||
assertThat(actualCordapp.rpcFlows).first().hasSameClassAs(DummyRPCFlow::class.java)
|
assertThat(actualCordapp.rpcFlows).first().hasSameClassAs(DummyRPCFlow::class.java)
|
||||||
assertThat(actualCordapp.schedulableFlows).first().hasSameClassAs(DummySchedulableFlow::class.java)
|
assertThat(actualCordapp.schedulableFlows).first().hasSameClassAs(DummySchedulableFlow::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `duplicate packages are ignored`() {
|
|
||||||
val loader = cordappLoaderForPackages(listOf(testScanPackage, testScanPackage))
|
|
||||||
val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
|
|
||||||
assertThat(cordapps).hasSize(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `sub-packages are ignored`() {
|
|
||||||
val loader = cordappLoaderForPackages(listOf("net.corda.core", testScanPackage))
|
|
||||||
val cordapps = loader.cordapps.filter { LoaderTestFlow::class.java in it.initiatedFlows }
|
|
||||||
assertThat(cordapps).hasSize(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This test exists because the appClassLoader is used by serialisation and we need to ensure it is the classloader
|
// This test exists because the appClassLoader is used by serialisation and we need to ensure it is the classloader
|
||||||
// being used internally. Later iterations will use a classloader per cordapp and this test can be retired.
|
// being used internally. Later iterations will use a classloader per cordapp and this test can be retired.
|
||||||
@Test
|
@Test
|
||||||
@ -159,16 +142,4 @@ class JarScanningCordappLoaderTest {
|
|||||||
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2))
|
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jar), VersionInfo.UNKNOWN.copy(platformVersion = 2))
|
||||||
assertThat(loader.cordapps).hasSize(1)
|
assertThat(loader.cordapps).hasSize(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cordappLoaderForPackages(packages: Iterable<String>): CordappLoader {
|
|
||||||
val cordapps = cordappsForPackages(packages)
|
|
||||||
return testDirectory().let { directory ->
|
|
||||||
cordapps.packageInDirectory(directory)
|
|
||||||
JarScanningCordappLoader.fromDirectories(listOf(directory))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun testDirectory(): Path {
|
|
||||||
return Paths.get("build", getTimestampAsDirectoryName())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,7 @@ import net.corda.node.services.statemachine.StaffedFlowHospital.MedicalRecord.Ke
|
|||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.testing.core.singleIdentity
|
import net.corda.testing.core.singleIdentity
|
||||||
import net.corda.testing.driver.TestCorDapp
|
import net.corda.testing.node.internal.*
|
||||||
import net.corda.testing.node.internal.InternalMockNetwork
|
|
||||||
import net.corda.testing.node.internal.InternalMockNodeParameters
|
|
||||||
import net.corda.testing.node.internal.TestStartedNode
|
|
||||||
import net.corda.testing.node.internal.startFlow
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -38,8 +34,7 @@ class FinalityHandlerTest {
|
|||||||
// CorDapp. Bob's FinalityHandler will error when validating the tx.
|
// CorDapp. Bob's FinalityHandler will error when validating the tx.
|
||||||
mockNet = InternalMockNetwork()
|
mockNet = InternalMockNetwork()
|
||||||
|
|
||||||
val assertCordapp = TestCorDapp.Factory.create("net.corda.finance.contracts.asset", "1.0").plusPackage("net.corda.finance.contracts.asset")
|
val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, additionalCordapps = setOf(FINANCE_CORDAPP)))
|
||||||
val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, additionalCordapps = setOf(assertCordapp)))
|
|
||||||
|
|
||||||
var bob = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
|
var bob = mockNet.createNode(InternalMockNodeParameters(legalName = BOB_NAME))
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import net.corda.testing.core.DUMMY_NOTARY_NAME
|
|||||||
import net.corda.testing.driver.PortAllocation.Incremental
|
import net.corda.testing.driver.PortAllocation.Incremental
|
||||||
import net.corda.testing.driver.internal.internalServices
|
import net.corda.testing.driver.internal.internalServices
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
|
import net.corda.testing.node.TestCordapp
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.User
|
||||||
import net.corda.testing.node.internal.*
|
import net.corda.testing.node.internal.*
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -134,8 +135,8 @@ abstract class PortAllocation {
|
|||||||
* in. If null the Driver-level value will be used.
|
* in. If null the Driver-level value will be used.
|
||||||
* @property maximumHeapSize The maximum JVM heap size to use for the node.
|
* @property maximumHeapSize The maximum JVM heap size to use for the node.
|
||||||
* @property logLevel Logging level threshold.
|
* @property logLevel Logging level threshold.
|
||||||
* @property additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
* @property additionalCordapps Additional [TestCordapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||||
* @property regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
* @property regenerateCordappsOnStart Whether existing [TestCordapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
data class NodeParameters(
|
data class NodeParameters(
|
||||||
@ -146,7 +147,7 @@ data class NodeParameters(
|
|||||||
val startInSameProcess: Boolean? = null,
|
val startInSameProcess: Boolean? = null,
|
||||||
val maximumHeapSize: String = "512m",
|
val maximumHeapSize: String = "512m",
|
||||||
val logLevel: String? = null,
|
val logLevel: String? = null,
|
||||||
val additionalCordapps: Set<TestCorDapp> = emptySet(),
|
val additionalCordapps: Collection<TestCordapp> = emptySet(),
|
||||||
val regenerateCordappsOnStart: Boolean = false
|
val regenerateCordappsOnStart: Boolean = false
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
@ -226,8 +227,8 @@ data class NodeParameters(
|
|||||||
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
* @param startInSameProcess Determines if the node should be started inside the same process the Driver is running
|
||||||
* in. If null the Driver-level value will be used.
|
* in. If null the Driver-level value will be used.
|
||||||
* @param maximumHeapSize The maximum JVM heap size to use for the node.
|
* @param maximumHeapSize The maximum JVM heap size to use for the node.
|
||||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
* @param additionalCordapps Additional [TestCordapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||||
* @param regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
* @param regenerateCordappsOnStart Whether existing [TestCordapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
providedName: CordaX500Name?,
|
providedName: CordaX500Name?,
|
||||||
@ -236,7 +237,7 @@ data class NodeParameters(
|
|||||||
customOverrides: Map<String, Any?>,
|
customOverrides: Map<String, Any?>,
|
||||||
startInSameProcess: Boolean?,
|
startInSameProcess: Boolean?,
|
||||||
maximumHeapSize: String,
|
maximumHeapSize: String,
|
||||||
additionalCordapps: Set<TestCorDapp> = emptySet(),
|
additionalCordapps: Set<TestCordapp> = emptySet(),
|
||||||
regenerateCordappsOnStart: Boolean = false
|
regenerateCordappsOnStart: Boolean = false
|
||||||
) : this(
|
) : this(
|
||||||
providedName,
|
providedName,
|
||||||
@ -291,7 +292,7 @@ data class NodeParameters(
|
|||||||
fun withStartInSameProcess(startInSameProcess: Boolean?): NodeParameters = copy(startInSameProcess = startInSameProcess)
|
fun withStartInSameProcess(startInSameProcess: Boolean?): NodeParameters = copy(startInSameProcess = startInSameProcess)
|
||||||
fun withMaximumHeapSize(maximumHeapSize: String): NodeParameters = copy(maximumHeapSize = maximumHeapSize)
|
fun withMaximumHeapSize(maximumHeapSize: String): NodeParameters = copy(maximumHeapSize = maximumHeapSize)
|
||||||
fun withLogLevel(logLevel: String?): NodeParameters = copy(logLevel = logLevel)
|
fun withLogLevel(logLevel: String?): NodeParameters = copy(logLevel = logLevel)
|
||||||
fun withAdditionalCordapps(additionalCordapps: Set<TestCorDapp>): NodeParameters = copy(additionalCordapps = additionalCordapps)
|
fun withAdditionalCordapps(additionalCordapps: Set<TestCordapp>): NodeParameters = copy(additionalCordapps = additionalCordapps)
|
||||||
fun withDeleteExistingCordappsDirectory(regenerateCordappsOnStart: Boolean): NodeParameters = copy(regenerateCordappsOnStart = regenerateCordappsOnStart)
|
fun withDeleteExistingCordappsDirectory(regenerateCordappsOnStart: Boolean): NodeParameters = copy(regenerateCordappsOnStart = regenerateCordappsOnStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +380,7 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
|
|||||||
* @property inMemoryDB Whether to use in-memory H2 for new nodes rather then on-disk (the node starts quicker, however
|
* @property inMemoryDB Whether to use in-memory H2 for new nodes rather then on-disk (the node starts quicker, however
|
||||||
* the data is not persisted between node restarts). Has no effect if node is configured
|
* the data is not persisted between node restarts). Has no effect if node is configured
|
||||||
* in any way to use database other than H2.
|
* in any way to use database other than H2.
|
||||||
* @property cordappsForAllNodes [TestCorDapp]s that will be added to each node started by the [DriverDSL].
|
* @property cordappsForAllNodes [TestCordapp]s that will be added to each node started by the [DriverDSL].
|
||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
data class DriverParameters(
|
data class DriverParameters(
|
||||||
@ -398,7 +399,7 @@ data class DriverParameters(
|
|||||||
val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||||
val initialiseSerialization: Boolean = true,
|
val initialiseSerialization: Boolean = true,
|
||||||
val inMemoryDB: Boolean = true,
|
val inMemoryDB: Boolean = true,
|
||||||
val cordappsForAllNodes: Set<TestCorDapp>? = null
|
val cordappsForAllNodes: Collection<TestCordapp>? = null
|
||||||
) {
|
) {
|
||||||
constructor(
|
constructor(
|
||||||
isDebug: Boolean = false,
|
isDebug: Boolean = false,
|
||||||
@ -480,7 +481,7 @@ data class DriverParameters(
|
|||||||
extraCordappPackagesToScan: List<String>,
|
extraCordappPackagesToScan: List<String>,
|
||||||
jmxPolicy: JmxPolicy,
|
jmxPolicy: JmxPolicy,
|
||||||
networkParameters: NetworkParameters,
|
networkParameters: NetworkParameters,
|
||||||
cordappsForAllNodes: Set<TestCorDapp>? = null
|
cordappsForAllNodes: Collection<TestCordapp>? = null
|
||||||
) : this(
|
) : this(
|
||||||
isDebug,
|
isDebug,
|
||||||
driverDirectory,
|
driverDirectory,
|
||||||
@ -549,7 +550,7 @@ data class DriverParameters(
|
|||||||
networkParameters: NetworkParameters,
|
networkParameters: NetworkParameters,
|
||||||
initialiseSerialization: Boolean,
|
initialiseSerialization: Boolean,
|
||||||
inMemoryDB: Boolean,
|
inMemoryDB: Boolean,
|
||||||
cordappsForAllNodes: Set<TestCorDapp>? = null
|
cordappsForAllNodes: Set<TestCordapp>? = null
|
||||||
) : this(
|
) : this(
|
||||||
isDebug,
|
isDebug,
|
||||||
driverDirectory,
|
driverDirectory,
|
||||||
@ -584,7 +585,7 @@ data class DriverParameters(
|
|||||||
fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters)
|
fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters)
|
||||||
fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides)
|
fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides)
|
||||||
fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB)
|
fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB)
|
||||||
fun withCordappsForAllNodes(cordappsForAllNodes: Set<TestCorDapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes)
|
fun withCordappsForAllNodes(cordappsForAllNodes: Set<TestCordapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes)
|
||||||
|
|
||||||
fun copy(
|
fun copy(
|
||||||
isDebug: Boolean,
|
isDebug: Boolean,
|
||||||
@ -660,7 +661,7 @@ data class DriverParameters(
|
|||||||
extraCordappPackagesToScan: List<String>,
|
extraCordappPackagesToScan: List<String>,
|
||||||
jmxPolicy: JmxPolicy,
|
jmxPolicy: JmxPolicy,
|
||||||
networkParameters: NetworkParameters,
|
networkParameters: NetworkParameters,
|
||||||
cordappsForAllNodes: Set<TestCorDapp>?
|
cordappsForAllNodes: Set<TestCordapp>?
|
||||||
) = this.copy(
|
) = this.copy(
|
||||||
isDebug = isDebug,
|
isDebug = isDebug,
|
||||||
driverDirectory = driverDirectory,
|
driverDirectory = driverDirectory,
|
||||||
@ -693,7 +694,7 @@ data class DriverParameters(
|
|||||||
jmxPolicy: JmxPolicy,
|
jmxPolicy: JmxPolicy,
|
||||||
networkParameters: NetworkParameters,
|
networkParameters: NetworkParameters,
|
||||||
initialiseSerialization: Boolean,
|
initialiseSerialization: Boolean,
|
||||||
cordappsForAllNodes: Set<TestCorDapp>?
|
cordappsForAllNodes: Set<TestCordapp>?
|
||||||
) = this.copy(
|
) = this.copy(
|
||||||
isDebug = isDebug,
|
isDebug = isDebug,
|
||||||
driverDirectory = driverDirectory,
|
driverDirectory = driverDirectory,
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.internal.concurrent.map
|
import net.corda.core.internal.concurrent.map
|
||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
|
import net.corda.testing.node.TestCordapp
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.User
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -95,8 +96,8 @@ interface DriverDSL {
|
|||||||
* @param maximumHeapSize The maximum JVM heap size to use for the node as a [String]. By default a number is interpreted
|
* @param maximumHeapSize The maximum JVM heap size to use for the node as a [String]. By default a number is interpreted
|
||||||
* as being in bytes. Append the letter 'k' or 'K' to the value to indicate Kilobytes, 'm' or 'M' to indicate
|
* as being in bytes. Append the letter 'k' or 'K' to the value to indicate Kilobytes, 'm' or 'M' to indicate
|
||||||
* megabytes, and 'g' or 'G' to indicate gigabytes. The default value is "512m" = 512 megabytes.
|
* megabytes, and 'g' or 'G' to indicate gigabytes. The default value is "512m" = 512 megabytes.
|
||||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
* @param additionalCordapps Additional [TestCordapp]s that this node will have available, in addition to the ones common to all nodes managed by the [DriverDSL].
|
||||||
* @param regenerateCordappsOnStart Whether existing [TestCorDapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
* @param regenerateCordappsOnStart Whether existing [TestCordapp]s unique to this node will be re-generated on start. Useful when stopping and restarting the same node.
|
||||||
* @return A [CordaFuture] on the [NodeHandle] to the node. The future will complete when the node is available and
|
* @return A [CordaFuture] on the [NodeHandle] to the node. The future will complete when the node is available and
|
||||||
* it sees all previously started nodes, including the notaries.
|
* it sees all previously started nodes, including the notaries.
|
||||||
*/
|
*/
|
||||||
@ -108,7 +109,7 @@ interface DriverDSL {
|
|||||||
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
customOverrides: Map<String, Any?> = defaultParameters.customOverrides,
|
||||||
startInSameProcess: Boolean? = defaultParameters.startInSameProcess,
|
startInSameProcess: Boolean? = defaultParameters.startInSameProcess,
|
||||||
maximumHeapSize: String = defaultParameters.maximumHeapSize,
|
maximumHeapSize: String = defaultParameters.maximumHeapSize,
|
||||||
additionalCordapps: Set<TestCorDapp> = defaultParameters.additionalCordapps,
|
additionalCordapps: Collection<TestCordapp> = defaultParameters.additionalCordapps,
|
||||||
regenerateCordappsOnStart: Boolean = defaultParameters.regenerateCordappsOnStart
|
regenerateCordappsOnStart: Boolean = defaultParameters.regenerateCordappsOnStart
|
||||||
): CordaFuture<NodeHandle>
|
): CordaFuture<NodeHandle>
|
||||||
|
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
package net.corda.testing.driver
|
|
||||||
|
|
||||||
import net.corda.core.DoNotImplement
|
|
||||||
import net.corda.testing.node.internal.MutableTestCorDapp
|
|
||||||
import java.net.URL
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents information about a CorDapp. Used to generate CorDapp JARs in tests.
|
|
||||||
*/
|
|
||||||
@DoNotImplement
|
|
||||||
interface TestCorDapp {
|
|
||||||
|
|
||||||
val name: String
|
|
||||||
val title: String
|
|
||||||
val version: String
|
|
||||||
val vendor: String
|
|
||||||
|
|
||||||
val classes: Set<Class<*>>
|
|
||||||
|
|
||||||
val resources: Set<URL>
|
|
||||||
|
|
||||||
fun packageAsJarInDirectory(parentDirectory: Path): Path
|
|
||||||
|
|
||||||
fun packageAsJarWithPath(jarFilePath: Path)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responsible of creating [TestCorDapp]s.
|
|
||||||
*/
|
|
||||||
class Factory {
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a builder-style [TestCorDapp] to easily generate different [TestCorDapp]s that have something in common.
|
|
||||||
*/
|
|
||||||
@JvmStatic
|
|
||||||
fun create(name: String, version: String, vendor: String = "R3", title: String = name, classes: Set<Class<*>> = emptySet(), willResourceBeAddedBeToCorDapp: (fullyQualifiedName: String, url: URL) -> Boolean = MutableTestCorDapp.Companion::filterTestCorDappClass): TestCorDapp.Mutable {
|
|
||||||
|
|
||||||
return MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedBeToCorDapp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotImplement
|
|
||||||
interface Mutable : TestCorDapp {
|
|
||||||
|
|
||||||
fun withName(name: String): TestCorDapp.Mutable
|
|
||||||
|
|
||||||
fun withTitle(title: String): TestCorDapp.Mutable
|
|
||||||
|
|
||||||
fun withVersion(version: String): TestCorDapp.Mutable
|
|
||||||
|
|
||||||
fun withVendor(vendor: String): TestCorDapp.Mutable
|
|
||||||
|
|
||||||
fun withClasses(classes: Set<Class<*>>): TestCorDapp.Mutable
|
|
||||||
|
|
||||||
fun plusPackages(pckgs: Set<String>): TestCorDapp.Mutable
|
|
||||||
|
|
||||||
fun minusPackages(pckgs: Set<String>): TestCorDapp.Mutable
|
|
||||||
|
|
||||||
fun plusPackage(pckg: String): TestCorDapp.Mutable = plusPackages(setOf(pckg))
|
|
||||||
|
|
||||||
fun minusPackage(pckg: String): TestCorDapp.Mutable = minusPackages(setOf(pckg))
|
|
||||||
|
|
||||||
fun plusPackage(pckg: Package): TestCorDapp.Mutable = plusPackages(pckg.name)
|
|
||||||
|
|
||||||
fun minusPackage(pckg: Package): TestCorDapp.Mutable = minusPackages(pckg.name)
|
|
||||||
|
|
||||||
operator fun plus(clazz: Class<*>): TestCorDapp.Mutable = withClasses(classes + clazz)
|
|
||||||
|
|
||||||
operator fun minus(clazz: Class<*>): TestCorDapp.Mutable = withClasses(classes - clazz)
|
|
||||||
|
|
||||||
fun plusPackages(pckg: String, vararg pckgs: String): TestCorDapp.Mutable = plusPackages(setOf(pckg, *pckgs))
|
|
||||||
|
|
||||||
fun plusPackages(pckg: Package, vararg pckgs: Package): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs).map { it.name }.toSet())
|
|
||||||
|
|
||||||
fun minusPackages(pckg: String, vararg pckgs: String): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs))
|
|
||||||
|
|
||||||
fun minusPackages(pckg: Package, vararg pckgs: Package): TestCorDapp.Mutable = minusPackages(setOf(pckg, *pckgs).map { it.name }.toSet())
|
|
||||||
|
|
||||||
fun plusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable
|
|
||||||
|
|
||||||
fun minusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,6 @@ import net.corda.node.internal.InitiatedFlowFactory
|
|||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
import net.corda.testing.driver.TestCorDapp
|
|
||||||
import net.corda.testing.node.internal.*
|
import net.corda.testing.node.internal.*
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
@ -34,36 +33,29 @@ import java.util.concurrent.Future
|
|||||||
* @property entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
* @property entropyRoot the initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||||
* @property configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
* @property configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||||
* @property additionalCordapps [TestCorDapp]s that will be added to this node in addition to the ones shared by all nodes, which get specified at [MockNetwork] level.
|
* @property additionalCordapps [TestCordapp]s that will be added to this node in addition to the ones shared by all nodes, which get specified at [MockNetwork] level.
|
||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
data class MockNodeParameters constructor(
|
data class MockNodeParameters(
|
||||||
val forcedID: Int? = null,
|
val forcedID: Int? = null,
|
||||||
val legalName: CordaX500Name? = null,
|
val legalName: CordaX500Name? = null,
|
||||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
val configOverrides: (NodeConfiguration) -> Any? = {},
|
val configOverrides: (NodeConfiguration) -> Any? = {},
|
||||||
val additionalCordapps: Set<TestCorDapp>) {
|
val additionalCordapps: Collection<TestCordapp> = emptyList()) {
|
||||||
|
|
||||||
@JvmOverloads
|
constructor(forcedID: Int? = null,
|
||||||
constructor(
|
legalName: CordaX500Name? = null,
|
||||||
forcedID: Int? = null,
|
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
legalName: CordaX500Name? = null,
|
configOverrides: (NodeConfiguration) -> Any? = {}
|
||||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
) : this(forcedID, legalName, entropyRoot, configOverrides, emptyList())
|
||||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
|
||||||
extraCordappPackages: List<String> = emptyList()
|
|
||||||
) : this(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps = cordappsForPackages(extraCordappPackages))
|
|
||||||
|
|
||||||
fun withForcedID(forcedID: Int?): MockNodeParameters = copy(forcedID = forcedID)
|
fun withForcedID(forcedID: Int?): MockNodeParameters = copy(forcedID = forcedID)
|
||||||
fun withLegalName(legalName: CordaX500Name?): MockNodeParameters = copy(legalName = legalName)
|
fun withLegalName(legalName: CordaX500Name?): MockNodeParameters = copy(legalName = legalName)
|
||||||
fun withEntropyRoot(entropyRoot: BigInteger): MockNodeParameters = copy(entropyRoot = entropyRoot)
|
fun withEntropyRoot(entropyRoot: BigInteger): MockNodeParameters = copy(entropyRoot = entropyRoot)
|
||||||
fun withConfigOverrides(configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters = copy(configOverrides = configOverrides)
|
fun withConfigOverrides(configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters = copy(configOverrides = configOverrides)
|
||||||
fun withExtraCordappPackages(extraCordappPackages: List<String>): MockNodeParameters = copy(forcedID = forcedID, legalName = legalName, entropyRoot = entropyRoot, configOverrides = configOverrides, extraCordappPackages = extraCordappPackages)
|
fun withAdditionalCordapps(additionalCordapps: Collection<TestCordapp>): MockNodeParameters = copy(additionalCordapps = additionalCordapps)
|
||||||
fun withAdditionalCordapps(additionalCordapps: Set<TestCorDapp>): MockNodeParameters = copy(additionalCordapps = additionalCordapps)
|
|
||||||
fun copy(forcedID: Int?, legalName: CordaX500Name?, entropyRoot: BigInteger, configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters {
|
fun copy(forcedID: Int?, legalName: CordaX500Name?, entropyRoot: BigInteger, configOverrides: (NodeConfiguration) -> Any?): MockNodeParameters {
|
||||||
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps = emptySet())
|
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides)
|
||||||
}
|
|
||||||
fun copy(forcedID: Int?, legalName: CordaX500Name?, entropyRoot: BigInteger, configOverrides: (NodeConfiguration) -> Any?, extraCordappPackages: List<String> = emptyList()): MockNodeParameters {
|
|
||||||
return MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, extraCordappPackages)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +285,7 @@ inline fun <reified F : FlowLogic<*>> StartedMockNode.registerResponderFlow(
|
|||||||
* @property notarySpecs The notaries to use in the mock network. By default you get one mock notary and that is usually sufficient.
|
* @property notarySpecs The notaries to use in the mock network. By default you get one mock notary and that is usually sufficient.
|
||||||
* @property networkParameters The network parameters to be used by all the nodes. [NetworkParameters.notaries] must be
|
* @property networkParameters The network parameters to be used by all the nodes. [NetworkParameters.notaries] must be
|
||||||
* empty as notaries are defined by [notarySpecs].
|
* empty as notaries are defined by [notarySpecs].
|
||||||
* @property cordappsForAllNodes [TestCorDapp]s that will be added to each node started by the [MockNetwork].
|
* @property cordappsForAllNodes [TestCordapp]s that will be added to each node started by the [MockNetwork].
|
||||||
*/
|
*/
|
||||||
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
||||||
open class MockNetwork(
|
open class MockNetwork(
|
||||||
@ -304,7 +296,7 @@ open class MockNetwork(
|
|||||||
val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
val servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||||
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||||
val networkParameters: NetworkParameters = defaultParameters.networkParameters,
|
val networkParameters: NetworkParameters = defaultParameters.networkParameters,
|
||||||
val cordappsForAllNodes: Set<TestCorDapp> = cordappsForPackages(cordappPackages)) {
|
val cordappsForAllNodes: Collection<TestCordapp> = cordappsForPackages(cordappPackages)) {
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters)
|
constructor(cordappPackages: List<String>, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters)
|
||||||
@ -345,7 +337,9 @@ open class MockNetwork(
|
|||||||
fun createPartyNode(legalName: CordaX500Name? = null): StartedMockNode = StartedMockNode.create(internalMockNetwork.createPartyNode(legalName))
|
fun createPartyNode(legalName: CordaX500Name? = null): StartedMockNode = StartedMockNode.create(internalMockNetwork.createPartyNode(legalName))
|
||||||
|
|
||||||
/** Create a started node with the given parameters. **/
|
/** Create a started node with the given parameters. **/
|
||||||
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode = StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
|
fun createNode(parameters: MockNodeParameters = MockNodeParameters()): StartedMockNode {
|
||||||
|
return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a started node with the given parameters.
|
* Create a started node with the given parameters.
|
||||||
@ -375,13 +369,13 @@ open class MockNetwork(
|
|||||||
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||||
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [MockNetwork].
|
* @param additionalCordapps Additional [TestCordapp]s that this node will have available, in addition to the ones common to all nodes managed by the [MockNetwork].
|
||||||
*/
|
*/
|
||||||
fun createNode(legalName: CordaX500Name? = null,
|
fun createNode(legalName: CordaX500Name? = null,
|
||||||
forcedID: Int? = null,
|
forcedID: Int? = null,
|
||||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||||
additionalCordapps: Set<TestCorDapp>): StartedMockNode {
|
additionalCordapps: Collection<TestCordapp>): StartedMockNode {
|
||||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps)
|
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps)
|
||||||
return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
|
return StartedMockNode.create(internalMockNetwork.createNode(InternalMockNodeParameters(parameters)))
|
||||||
}
|
}
|
||||||
@ -417,13 +411,13 @@ open class MockNetwork(
|
|||||||
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
* @param entropyRoot The initial entropy value to use when generating keys. Defaults to an (insecure) random value,
|
||||||
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
* but can be overridden to cause nodes to have stable or colliding identity/service keys.
|
||||||
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
* @param configOverrides Add/override behaviour of the [NodeConfiguration] mock object.
|
||||||
* @param additionalCordapps Additional [TestCorDapp]s that this node will have available, in addition to the ones common to all nodes managed by the [MockNetwork].
|
* @param additionalCordapps Additional [TestCordapp]s that this node will have available, in addition to the ones common to all nodes managed by the [MockNetwork].
|
||||||
*/
|
*/
|
||||||
fun createUnstartedNode(legalName: CordaX500Name? = null,
|
fun createUnstartedNode(legalName: CordaX500Name? = null,
|
||||||
forcedID: Int? = null,
|
forcedID: Int? = null,
|
||||||
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
configOverrides: (NodeConfiguration) -> Any? = {},
|
configOverrides: (NodeConfiguration) -> Any? = {},
|
||||||
additionalCordapps: Set<TestCorDapp>): UnstartedMockNode {
|
additionalCordapps: Collection<TestCordapp>): UnstartedMockNode {
|
||||||
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps)
|
val parameters = MockNodeParameters(forcedID, legalName, entropyRoot, configOverrides, additionalCordapps)
|
||||||
return UnstartedMockNode.create(internalMockNetwork.createUnstartedNode(InternalMockNodeParameters(parameters)))
|
return UnstartedMockNode.create(internalMockNetwork.createUnstartedNode(InternalMockNodeParameters(parameters)))
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,7 @@ import net.corda.testing.common.internal.testNetworkParameters
|
|||||||
import net.corda.testing.core.TestIdentity
|
import net.corda.testing.core.TestIdentity
|
||||||
import net.corda.testing.internal.DEV_ROOT_CA
|
import net.corda.testing.internal.DEV_ROOT_CA
|
||||||
import net.corda.testing.internal.MockCordappProvider
|
import net.corda.testing.internal.MockCordappProvider
|
||||||
import net.corda.testing.node.internal.MockKeyManagementService
|
import net.corda.testing.node.internal.*
|
||||||
import net.corda.testing.node.internal.MockTransactionStorage
|
|
||||||
import net.corda.testing.node.internal.TestCordappDirectories
|
|
||||||
import net.corda.testing.node.internal.getCallerPackage
|
|
||||||
import net.corda.testing.services.MockAttachmentStorage
|
import net.corda.testing.services.MockAttachmentStorage
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
@ -70,8 +67,7 @@ open class MockServices private constructor(
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private fun cordappLoaderForPackages(packages: Iterable<String>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): CordappLoader {
|
private fun cordappLoaderForPackages(packages: Iterable<String>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): CordappLoader {
|
||||||
|
val cordappPaths = cordappsForPackages(packages).map { TestCordappDirectories.getJarDirectory(it) }
|
||||||
val cordappPaths = TestCordappDirectories.forPackages(packages)
|
|
||||||
return JarScanningCordappLoader.fromDirectories(cordappPaths, versionInfo)
|
return JarScanningCordappLoader.fromDirectories(cordappPaths, versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
package net.corda.testing.node
|
||||||
|
|
||||||
|
import net.corda.core.DoNotImplement
|
||||||
|
import net.corda.core.internal.PLATFORM_VERSION
|
||||||
|
import net.corda.testing.node.internal.TestCordappImpl
|
||||||
|
import net.corda.testing.node.internal.simplifyScanPackages
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents information about a CorDapp. Used to generate CorDapp JARs in tests.
|
||||||
|
*/
|
||||||
|
@DoNotImplement
|
||||||
|
interface TestCordapp {
|
||||||
|
/** Returns the name, defaults to "test-cordapp" if not specified. */
|
||||||
|
val name: String
|
||||||
|
|
||||||
|
/** Returns the title, defaults to "test-title" if not specified. */
|
||||||
|
val title: String
|
||||||
|
|
||||||
|
/** Returns the version string, defaults to "1.0" if not specified. */
|
||||||
|
val version: String
|
||||||
|
|
||||||
|
/** Returns the vendor string, defaults to "Corda" if not specified. */
|
||||||
|
val vendor: String
|
||||||
|
|
||||||
|
/** Returns the target platform version, defaults to the current platform version if not specified. */
|
||||||
|
val targetVersion: Int
|
||||||
|
|
||||||
|
/** Returns the set of package names scanned for this test CorDapp. */
|
||||||
|
val packages: Set<String>
|
||||||
|
|
||||||
|
/** Return a copy of this [TestCordapp] but with the specified name. */
|
||||||
|
fun withName(name: String): TestCordapp
|
||||||
|
|
||||||
|
/** Return a copy of this [TestCordapp] but with the specified title. */
|
||||||
|
fun withTitle(title: String): TestCordapp
|
||||||
|
|
||||||
|
/** Return a copy of this [TestCordapp] but with the specified version string. */
|
||||||
|
fun withVersion(version: String): TestCordapp
|
||||||
|
|
||||||
|
/** Return a copy of this [TestCordapp] but with the specified vendor string. */
|
||||||
|
fun withVendor(vendor: String): TestCordapp
|
||||||
|
|
||||||
|
/** Return a copy of this [TestCordapp] but with the specified target platform version. */
|
||||||
|
fun withTargetVersion(targetVersion: Int): TestCordapp
|
||||||
|
|
||||||
|
class Factory {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun fromPackages(vararg packageNames: String): TestCordapp = fromPackages(packageNames.asList())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a [TestCordapp] object by scanning the given packages. The meta data on the CorDapp will be the
|
||||||
|
* default values, which can be specified with the wither methods.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun fromPackages(packageNames: Collection<String>): TestCordapp {
|
||||||
|
return TestCordappImpl(
|
||||||
|
name = "test-cordapp",
|
||||||
|
version = "1.0",
|
||||||
|
vendor = "Corda",
|
||||||
|
title = "test-title",
|
||||||
|
targetVersion = PLATFORM_VERSION,
|
||||||
|
packages = simplifyScanPackages(packageNames),
|
||||||
|
classes = emptySet()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,6 @@ import net.corda.node.services.Permissions
|
|||||||
import net.corda.node.services.config.*
|
import net.corda.node.services.config.*
|
||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NodeRegistrationHelper
|
import net.corda.node.utilities.registration.NodeRegistrationHelper
|
||||||
import net.corda.core.internal.PLATFORM_VERSION
|
|
||||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.addShutdownHook
|
import net.corda.nodeapi.internal.addShutdownHook
|
||||||
@ -38,6 +37,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
|
|||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
|
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
|
||||||
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
|
||||||
|
import net.corda.testing.node.TestCordapp
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.core.BOB_NAME
|
import net.corda.testing.core.BOB_NAME
|
||||||
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||||
@ -91,7 +91,7 @@ class DriverDSLImpl(
|
|||||||
val networkParameters: NetworkParameters,
|
val networkParameters: NetworkParameters,
|
||||||
val notaryCustomOverrides: Map<String, Any?>,
|
val notaryCustomOverrides: Map<String, Any?>,
|
||||||
val inMemoryDB: Boolean,
|
val inMemoryDB: Boolean,
|
||||||
val cordappsForAllNodes: Set<TestCorDapp>
|
val cordappsForAllNodes: Collection<TestCordapp>
|
||||||
) : InternalDriverDSL {
|
) : InternalDriverDSL {
|
||||||
|
|
||||||
private var _executorService: ScheduledExecutorService? = null
|
private var _executorService: ScheduledExecutorService? = null
|
||||||
@ -184,7 +184,25 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startNode(defaultParameters: NodeParameters, providedName: CordaX500Name?, rpcUsers: List<User>, verifierType: VerifierType, customOverrides: Map<String, Any?>, startInSameProcess: Boolean?, maximumHeapSize: String) = startNode(defaultParameters, providedName, rpcUsers, verifierType, customOverrides, startInSameProcess, maximumHeapSize, defaultParameters.additionalCordapps, defaultParameters.regenerateCordappsOnStart)
|
override fun startNode(defaultParameters: NodeParameters,
|
||||||
|
providedName: CordaX500Name?,
|
||||||
|
rpcUsers: List<User>,
|
||||||
|
verifierType: VerifierType,
|
||||||
|
customOverrides: Map<String, Any?>,
|
||||||
|
startInSameProcess: Boolean?,
|
||||||
|
maximumHeapSize: String): CordaFuture<NodeHandle> {
|
||||||
|
return startNode(
|
||||||
|
defaultParameters,
|
||||||
|
providedName,
|
||||||
|
rpcUsers,
|
||||||
|
verifierType,
|
||||||
|
customOverrides,
|
||||||
|
startInSameProcess,
|
||||||
|
maximumHeapSize,
|
||||||
|
defaultParameters.additionalCordapps,
|
||||||
|
defaultParameters.regenerateCordappsOnStart
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun startNode(
|
override fun startNode(
|
||||||
defaultParameters: NodeParameters,
|
defaultParameters: NodeParameters,
|
||||||
@ -194,7 +212,7 @@ class DriverDSLImpl(
|
|||||||
customOverrides: Map<String, Any?>,
|
customOverrides: Map<String, Any?>,
|
||||||
startInSameProcess: Boolean?,
|
startInSameProcess: Boolean?,
|
||||||
maximumHeapSize: String,
|
maximumHeapSize: String,
|
||||||
additionalCordapps: Set<TestCorDapp>,
|
additionalCordapps: Collection<TestCordapp>,
|
||||||
regenerateCordappsOnStart: Boolean
|
regenerateCordappsOnStart: Boolean
|
||||||
): CordaFuture<NodeHandle> {
|
): CordaFuture<NodeHandle> {
|
||||||
val p2pAddress = portAllocation.nextHostAndPort()
|
val p2pAddress = portAllocation.nextHostAndPort()
|
||||||
@ -225,7 +243,7 @@ class DriverDSLImpl(
|
|||||||
startInSameProcess: Boolean? = null,
|
startInSameProcess: Boolean? = null,
|
||||||
maximumHeapSize: String = "512m",
|
maximumHeapSize: String = "512m",
|
||||||
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(),
|
p2pAddress: NetworkHostAndPort = portAllocation.nextHostAndPort(),
|
||||||
additionalCordapps: Set<TestCorDapp> = emptySet(),
|
additionalCordapps: Collection<TestCordapp> = emptySet(),
|
||||||
regenerateCordappsOnStart: Boolean = false): CordaFuture<NodeHandle> {
|
regenerateCordappsOnStart: Boolean = false): CordaFuture<NodeHandle> {
|
||||||
val rpcAddress = portAllocation.nextHostAndPort()
|
val rpcAddress = portAllocation.nextHostAndPort()
|
||||||
val rpcAdminAddress = portAllocation.nextHostAndPort()
|
val rpcAdminAddress = portAllocation.nextHostAndPort()
|
||||||
@ -535,16 +553,12 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sharedCordappsDirectories: Iterable<Path> by lazy {
|
|
||||||
TestCordappDirectories.cached(cordappsForAllNodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startNodeInternal(specifiedConfig: NodeConfig,
|
private fun startNodeInternal(specifiedConfig: NodeConfig,
|
||||||
webAddress: NetworkHostAndPort,
|
webAddress: NetworkHostAndPort,
|
||||||
startInProcess: Boolean?,
|
startInProcess: Boolean?,
|
||||||
maximumHeapSize: String,
|
maximumHeapSize: String,
|
||||||
localNetworkMap: LocalNetworkMap?,
|
localNetworkMap: LocalNetworkMap?,
|
||||||
additionalCordapps: Set<TestCorDapp>,
|
additionalCordapps: Collection<TestCordapp>,
|
||||||
regenerateCordappsOnStart: Boolean = false): CordaFuture<NodeHandle> {
|
regenerateCordappsOnStart: Boolean = false): CordaFuture<NodeHandle> {
|
||||||
val visibilityHandle = networkVisibilityController.register(specifiedConfig.corda.myLegalName)
|
val visibilityHandle = networkVisibilityController.register(specifiedConfig.corda.myLegalName)
|
||||||
val baseDirectory = specifiedConfig.corda.baseDirectory.createDirectories()
|
val baseDirectory = specifiedConfig.corda.baseDirectory.createDirectories()
|
||||||
@ -558,11 +572,17 @@ class DriverDSLImpl(
|
|||||||
|
|
||||||
val useHTTPS = specifiedConfig.typesafe.run { hasPath("useHTTPS") && getBoolean("useHTTPS") }
|
val useHTTPS = specifiedConfig.typesafe.run { hasPath("useHTTPS") && getBoolean("useHTTPS") }
|
||||||
|
|
||||||
val existingCorDappDirectoriesOption = if (regenerateCordappsOnStart) emptyList<String>() else if (specifiedConfig.typesafe.hasPath(NodeConfiguration.cordappDirectoriesKey)) specifiedConfig.typesafe.getStringList(NodeConfiguration.cordappDirectoriesKey) else emptyList()
|
val existingCorDappDirectoriesOption = if (regenerateCordappsOnStart) {
|
||||||
|
emptyList()
|
||||||
|
} else if (specifiedConfig.typesafe.hasPath(NodeConfiguration.cordappDirectoriesKey)) {
|
||||||
|
specifiedConfig.typesafe.getStringList(NodeConfiguration.cordappDirectoriesKey)
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
val cordappDirectories = existingCorDappDirectoriesOption + sharedCordappsDirectories.map { it.toString() } + TestCordappDirectories.cached(additionalCordapps, regenerateCordappsOnStart).map { it.toString() }
|
val cordappDirectories = existingCorDappDirectoriesOption + (cordappsForAllNodes + additionalCordapps).map { TestCordappDirectories.getJarDirectory(it).toString() }
|
||||||
|
|
||||||
val config = NodeConfig(specifiedConfig.typesafe.withValue(NodeConfiguration.cordappDirectoriesKey, ConfigValueFactory.fromIterable(cordappDirectories)))
|
val config = NodeConfig(specifiedConfig.typesafe.withValue(NodeConfiguration.cordappDirectoriesKey, ConfigValueFactory.fromIterable(cordappDirectories.toSet())))
|
||||||
|
|
||||||
if (startInProcess ?: startNodesInProcess) {
|
if (startInProcess ?: startNodesInProcess) {
|
||||||
val nodeAndThreadFuture = startInProcessNode(executorService, config)
|
val nodeAndThreadFuture = startInProcessNode(executorService, config)
|
||||||
@ -687,8 +707,13 @@ class DriverDSLImpl(
|
|||||||
|
|
||||||
private fun <A> oneOf(array: Array<A>) = array[Random().nextInt(array.size)]
|
private fun <A> oneOf(array: Array<A>) = array[Random().nextInt(array.size)]
|
||||||
|
|
||||||
fun cordappsInCurrentAndAdditionalPackages(packagesToScan: Iterable<String> = emptySet()): Set<TestCorDapp> = cordappsForPackages(getCallerPackage() + packagesToScan)
|
fun cordappsInCurrentAndAdditionalPackages(packagesToScan: Collection<String> = emptySet()): List<TestCordapp> {
|
||||||
fun cordappsInCurrentAndAdditionalPackages(firstPackage: String, vararg otherPackages: String): Set<TestCorDapp> = cordappsInCurrentAndAdditionalPackages(otherPackages.toList() + firstPackage)
|
return cordappsForPackages(getCallerPackage() + packagesToScan)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cordappsInCurrentAndAdditionalPackages(firstPackage: String, vararg otherPackages: String): List<TestCordapp> {
|
||||||
|
return cordappsInCurrentAndAdditionalPackages(otherPackages.asList() + firstPackage)
|
||||||
|
}
|
||||||
|
|
||||||
private fun startInProcessNode(
|
private fun startInProcessNode(
|
||||||
executorService: ScheduledExecutorService,
|
executorService: ScheduledExecutorService,
|
||||||
@ -1085,7 +1110,7 @@ fun <A> internalDriver(
|
|||||||
compatibilityZone: CompatibilityZoneParams? = null,
|
compatibilityZone: CompatibilityZoneParams? = null,
|
||||||
notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides,
|
notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides,
|
||||||
inMemoryDB: Boolean = DriverParameters().inMemoryDB,
|
inMemoryDB: Boolean = DriverParameters().inMemoryDB,
|
||||||
cordappsForAllNodes: Set<TestCorDapp> = DriverParameters().cordappsForAllNodes(),
|
cordappsForAllNodes: Collection<TestCordapp> = DriverParameters().cordappsForAllNodes(),
|
||||||
dsl: DriverDSLImpl.() -> A
|
dsl: DriverDSLImpl.() -> A
|
||||||
): A {
|
): A {
|
||||||
return genericDriver(
|
return genericDriver(
|
||||||
@ -1125,7 +1150,7 @@ private fun Config.toNodeOnly(): Config {
|
|||||||
return if (hasPath("webAddress")) withoutPath("webAddress").withoutPath("useHTTPS") else this
|
return if (hasPath("webAddress")) withoutPath("webAddress").withoutPath("useHTTPS") else this
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun DriverParameters.cordappsForAllNodes(): Set<TestCorDapp> = cordappsForAllNodes
|
internal fun DriverParameters.cordappsForAllNodes(): Collection<TestCordapp> = cordappsForAllNodes
|
||||||
?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan)
|
?: cordappsInCurrentAndAdditionalPackages(extraCordappPackagesToScan)
|
||||||
|
|
||||||
fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture<NodeHandle> {
|
fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture<NodeHandle> {
|
||||||
|
@ -51,8 +51,8 @@ import net.corda.nodeapi.internal.config.User
|
|||||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
|
import net.corda.testing.node.TestCordapp
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.driver.TestCorDapp
|
|
||||||
import net.corda.testing.internal.rigorousMock
|
import net.corda.testing.internal.rigorousMock
|
||||||
import net.corda.testing.internal.setGlobalSerialization
|
import net.corda.testing.internal.setGlobalSerialization
|
||||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||||
@ -89,7 +89,7 @@ data class InternalMockNodeParameters(
|
|||||||
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
val entropyRoot: BigInteger = BigInteger.valueOf(random63BitValue()),
|
||||||
val configOverrides: (NodeConfiguration) -> Any? = {},
|
val configOverrides: (NodeConfiguration) -> Any? = {},
|
||||||
val version: VersionInfo = MOCK_VERSION_INFO,
|
val version: VersionInfo = MOCK_VERSION_INFO,
|
||||||
val additionalCordapps: Set<TestCorDapp>? = null) {
|
val additionalCordapps: Collection<TestCordapp>? = null) {
|
||||||
constructor(mockNodeParameters: MockNodeParameters) : this(
|
constructor(mockNodeParameters: MockNodeParameters) : this(
|
||||||
mockNodeParameters.forcedID,
|
mockNodeParameters.forcedID,
|
||||||
mockNodeParameters.legalName,
|
mockNodeParameters.legalName,
|
||||||
@ -148,7 +148,7 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||||
val networkParameters: NetworkParameters = testNetworkParameters(),
|
val networkParameters: NetworkParameters = testNetworkParameters(),
|
||||||
val defaultFactory: (MockNodeArgs) -> MockNode = { args -> MockNode(args) },
|
val defaultFactory: (MockNodeArgs) -> MockNode = { args -> MockNode(args) },
|
||||||
val cordappsForAllNodes: Set<TestCorDapp> = emptySet(),
|
val cordappsForAllNodes: Collection<TestCordapp> = emptySet(),
|
||||||
val autoVisibleNodes: Boolean = true) : AutoCloseable {
|
val autoVisibleNodes: Boolean = true) : AutoCloseable {
|
||||||
init {
|
init {
|
||||||
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
|
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
|
||||||
@ -174,10 +174,6 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
}
|
}
|
||||||
private val sharedUserCount = AtomicInteger(0)
|
private val sharedUserCount = AtomicInteger(0)
|
||||||
|
|
||||||
private val sharedCorDappsDirectories: Iterable<Path> by lazy {
|
|
||||||
TestCordappDirectories.cached(cordappsForAllNodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A read only view of the current set of nodes. */
|
/** A read only view of the current set of nodes. */
|
||||||
val nodes: List<MockNode> get() = _nodes
|
val nodes: List<MockNode> get() = _nodes
|
||||||
|
|
||||||
@ -453,8 +449,8 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
|||||||
parameters.configOverrides(it)
|
parameters.configOverrides(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cordapps: Set<TestCorDapp> = parameters.additionalCordapps ?: emptySet()
|
val cordapps = (parameters.additionalCordapps ?: emptySet()) + cordappsForAllNodes
|
||||||
val cordappDirectories = sharedCorDappsDirectories + TestCordappDirectories.cached(cordapps)
|
val cordappDirectories = cordapps.map { TestCordappDirectories.getJarDirectory(it) }.distinct()
|
||||||
doReturn(cordappDirectories).whenever(config).cordappDirectories
|
doReturn(cordappDirectories).whenever(config).cordappDirectories
|
||||||
|
|
||||||
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version))
|
val node = nodeFactory(MockNodeArgs(config, this, id, parameters.entropyRoot, parameters.version))
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
package net.corda.testing.node.internal
|
|
||||||
|
|
||||||
import net.corda.core.internal.div
|
|
||||||
import net.corda.testing.driver.TestCorDapp
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URL
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
internal class MutableTestCorDapp private constructor(override val name: String, override val version: String, override val vendor: String, override val title: String, private val willResourceBeAddedToCorDapp: (String, URL) -> Boolean, private val jarEntries: Set<JarEntryInfo>) : TestCorDapp.Mutable {
|
|
||||||
|
|
||||||
constructor(name: String, version: String, vendor: String, title: String, classes: Set<Class<*>>, willResourceBeAddedToCorDapp: (String, URL) -> Boolean) : this(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntriesFromClasses(classes))
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val jarExtension = ".jar"
|
|
||||||
private const val whitespace = " "
|
|
||||||
private const val whitespaceReplacement = "_"
|
|
||||||
private val productionPathSegments = setOf<(String) -> String>({ "out${File.separator}production${File.separator}classes" }, { fullyQualifiedName -> "main${File.separator}${fullyQualifiedName.packageToJarPath()}" })
|
|
||||||
private val excludedCordaPackages = setOf("net.corda.core", "net.corda.node")
|
|
||||||
|
|
||||||
fun filterTestCorDappClass(fullyQualifiedName: String, url: URL): Boolean {
|
|
||||||
|
|
||||||
return isTestResource(fullyQualifiedName, url) || !isInExcludedCordaPackage(fullyQualifiedName)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isTestResource(fullyQualifiedName: String, url: URL): Boolean {
|
|
||||||
|
|
||||||
return productionPathSegments.map { it.invoke(fullyQualifiedName) }.none { url.toString().contains(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isInExcludedCordaPackage(packageName: String): Boolean {
|
|
||||||
|
|
||||||
return excludedCordaPackages.any { packageName.startsWith(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun jarEntriesFromClasses(classes: Set<Class<*>>): Set<JarEntryInfo> {
|
|
||||||
|
|
||||||
val illegal = classes.filter { it.protectionDomain?.codeSource?.location == null }
|
|
||||||
if (illegal.isNotEmpty()) {
|
|
||||||
throw IllegalArgumentException("Some classes do not have a location, typically because they are part of Java or Kotlin. Offending types were: ${illegal.joinToString(", ", "[", "]") { it.simpleName }}")
|
|
||||||
}
|
|
||||||
return classes.map(Class<*>::jarEntryInfo).toSet()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val classes: Set<Class<*>> = jarEntries.filterIsInstance(JarEntryInfo.ClassJarEntryInfo::class.java).map(JarEntryInfo.ClassJarEntryInfo::clazz).toSet()
|
|
||||||
|
|
||||||
override val resources: Set<URL> = jarEntries.map(JarEntryInfo::url).toSet()
|
|
||||||
|
|
||||||
override fun withName(name: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
|
||||||
|
|
||||||
override fun withTitle(title: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
|
||||||
|
|
||||||
override fun withVersion(version: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
|
||||||
|
|
||||||
override fun withVendor(vendor: String) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
|
||||||
|
|
||||||
override fun withClasses(classes: Set<Class<*>>) = MutableTestCorDapp(name, version, vendor, title, classes, willResourceBeAddedToCorDapp)
|
|
||||||
|
|
||||||
override fun plusPackages(pckgs: Set<String>) = withClasses(pckgs.map { allClassesForPackage(it) }.fold(classes) { all, packageClasses -> all + packageClasses })
|
|
||||||
|
|
||||||
override fun minusPackages(pckgs: Set<String>) = withClasses(pckgs.map { allClassesForPackage(it) }.fold(classes) { all, packageClasses -> all - packageClasses })
|
|
||||||
|
|
||||||
override fun plusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable = MutableTestCorDapp(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntries + JarEntryInfo.ResourceJarEntryInfo(fullyQualifiedName, url))
|
|
||||||
|
|
||||||
override fun minusResource(fullyQualifiedName: String, url: URL): TestCorDapp.Mutable = MutableTestCorDapp(name, version, vendor, title, willResourceBeAddedToCorDapp, jarEntries - JarEntryInfo.ResourceJarEntryInfo(fullyQualifiedName, url))
|
|
||||||
|
|
||||||
override fun packageAsJarWithPath(jarFilePath: Path) = jarEntries.packageToCorDapp(jarFilePath, name, version, vendor, title, willResourceBeAddedToCorDapp)
|
|
||||||
|
|
||||||
override fun packageAsJarInDirectory(parentDirectory: Path): Path = (parentDirectory / defaultJarName()).also { packageAsJarWithPath(it) }
|
|
||||||
|
|
||||||
private fun defaultJarName(): String = "${name}_$version$jarExtension".replace(whitespace, whitespaceReplacement)
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ import net.corda.core.internal.createDirectories
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.loggerFor
|
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.internal.Node
|
import net.corda.node.internal.Node
|
||||||
import net.corda.node.internal.NodeWithInfo
|
import net.corda.node.internal.NodeWithInfo
|
||||||
@ -108,9 +107,9 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
|||||||
|
|
||||||
val existingCorDappDirectoriesOption = if (config.hasPath(NodeConfiguration.cordappDirectoriesKey)) config.getStringList(NodeConfiguration.cordappDirectoriesKey) else emptyList()
|
val existingCorDappDirectoriesOption = if (config.hasPath(NodeConfiguration.cordappDirectoriesKey)) config.getStringList(NodeConfiguration.cordappDirectoriesKey) else emptyList()
|
||||||
|
|
||||||
val cordappDirectories = existingCorDappDirectoriesOption + TestCordappDirectories.cached(cordapps).map { it.toString() }
|
val cordappDirectories = existingCorDappDirectoriesOption + cordapps.map { TestCordappDirectories.getJarDirectory(it).toString() }
|
||||||
|
|
||||||
val specificConfig = config.withValue(NodeConfiguration.cordappDirectoriesKey, ConfigValueFactory.fromIterable(cordappDirectories))
|
val specificConfig = config.withValue(NodeConfiguration.cordappDirectoriesKey, ConfigValueFactory.fromIterable(cordappDirectories.toSet()))
|
||||||
|
|
||||||
val parsedConfig = specificConfig.parseAsNodeConfiguration().also { nodeConfiguration ->
|
val parsedConfig = specificConfig.parseAsNodeConfiguration().also { nodeConfiguration ->
|
||||||
val errors = nodeConfiguration.validate()
|
val errors = nodeConfiguration.validate()
|
||||||
|
@ -24,11 +24,11 @@ import net.corda.node.services.messaging.RPCServerConfiguration
|
|||||||
import net.corda.nodeapi.RPCApi
|
import net.corda.nodeapi.RPCApi
|
||||||
import net.corda.nodeapi.internal.ArtemisTcpTransport
|
import net.corda.nodeapi.internal.ArtemisTcpTransport
|
||||||
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
|
import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
|
||||||
|
import net.corda.testing.node.TestCordapp
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.core.MAX_MESSAGE_SIZE
|
import net.corda.testing.core.MAX_MESSAGE_SIZE
|
||||||
import net.corda.testing.driver.JmxPolicy
|
import net.corda.testing.driver.JmxPolicy
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.TestCorDapp
|
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.User
|
||||||
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
import net.corda.testing.node.internal.DriverDSLImpl.Companion.cordappsInCurrentAndAdditionalPackages
|
||||||
@ -118,7 +118,7 @@ fun <A> rpcDriver(
|
|||||||
networkParameters: NetworkParameters = testNetworkParameters(),
|
networkParameters: NetworkParameters = testNetworkParameters(),
|
||||||
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||||
inMemoryDB: Boolean = true,
|
inMemoryDB: Boolean = true,
|
||||||
cordappsForAllNodes: Set<TestCorDapp> = cordappsInCurrentAndAdditionalPackages(),
|
cordappsForAllNodes: Collection<TestCordapp> = cordappsInCurrentAndAdditionalPackages(),
|
||||||
dsl: RPCDriverDSL.() -> A
|
dsl: RPCDriverDSL.() -> A
|
||||||
): A {
|
): A {
|
||||||
return genericDriver(
|
return genericDriver(
|
||||||
|
@ -1,70 +1,46 @@
|
|||||||
package net.corda.testing.node.internal
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
|
import net.corda.core.crypto.sha256
|
||||||
import net.corda.core.internal.createDirectories
|
import net.corda.core.internal.createDirectories
|
||||||
import net.corda.core.internal.deleteRecursively
|
import net.corda.core.internal.deleteRecursively
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.exists
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.testing.driver.TestCorDapp
|
import net.corda.testing.node.TestCordapp
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ConcurrentMap
|
|
||||||
|
|
||||||
internal object TestCordappDirectories {
|
|
||||||
|
|
||||||
|
object TestCordappDirectories {
|
||||||
private val logger = loggerFor<TestCordappDirectories>()
|
private val logger = loggerFor<TestCordappDirectories>()
|
||||||
|
|
||||||
private const val whitespace = " "
|
private val whitespace = "\\s".toRegex()
|
||||||
private const val whitespaceReplacement = "_"
|
|
||||||
|
|
||||||
private val cordappsCache: ConcurrentMap<List<String>, Path> = ConcurrentHashMap<List<String>, Path>()
|
private val testCordappsCache = ConcurrentHashMap<TestCordappImpl, Path>()
|
||||||
|
|
||||||
internal fun cached(cordapps: Iterable<TestCorDapp>, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Iterable<Path> {
|
fun getJarDirectory(cordapp: TestCordapp, cordappsDirectory: Path = defaultCordappsDirectory): Path {
|
||||||
|
cordapp as TestCordappImpl
|
||||||
cordappsDirectory.toFile().deleteOnExit()
|
return testCordappsCache.computeIfAbsent(cordapp) {
|
||||||
return cordapps.map { cached(it, replaceExistingOnes, cordappsDirectory) }
|
val cordappDir = (cordappsDirectory / UUID.randomUUID().toString()).createDirectories()
|
||||||
}
|
val uniqueScanString = if (cordapp.packages.size == 1 && cordapp.classes.isEmpty()) {
|
||||||
|
cordapp.packages.first()
|
||||||
internal fun cached(cordapp: TestCorDapp, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Path {
|
} else {
|
||||||
|
"${cordapp.packages}${cordapp.classes.joinToString { it.name }}".toByteArray().sha256().toString()
|
||||||
cordappsDirectory.toFile().deleteOnExit()
|
|
||||||
val cacheKey = cordapp.resources.map { it.toExternalForm() }.sorted()
|
|
||||||
return if (replaceExistingOnes) {
|
|
||||||
cordappsCache.remove(cacheKey)
|
|
||||||
cordappsCache.getOrPut(cacheKey) {
|
|
||||||
|
|
||||||
val cordappDirectory = (cordappsDirectory / "${cordapp.name}_${cordapp.version}".replace(whitespace, whitespaceReplacement)).toAbsolutePath()
|
|
||||||
cordappDirectory.createDirectories()
|
|
||||||
cordapp.packageAsJarInDirectory(cordappDirectory)
|
|
||||||
cordappDirectory
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cordappsCache.getOrPut(cacheKey) {
|
|
||||||
|
|
||||||
val cordappDirectory = (cordappsDirectory / "${cordapp.name}_${cordapp.version}".replace(whitespace, whitespaceReplacement)).toAbsolutePath()
|
|
||||||
cordappDirectory.createDirectories()
|
|
||||||
cordapp.packageAsJarInDirectory(cordappDirectory)
|
|
||||||
cordappDirectory
|
|
||||||
}
|
}
|
||||||
|
val jarFileName = cordapp.run { "${name}_${vendor}_${title}_${version}_${targetVersion}_$uniqueScanString.jar".replace(whitespace, "-") }
|
||||||
|
val jarFile = cordappDir / jarFileName
|
||||||
|
cordapp.packageAsJar(jarFile)
|
||||||
|
logger.debug { "$cordapp packaged into $jarFile" }
|
||||||
|
cordappDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun forPackages(packages: Iterable<String>, replaceExistingOnes: Boolean = false, cordappsDirectory: Path = defaultCordappsDirectory): Iterable<Path> {
|
|
||||||
|
|
||||||
cordappsDirectory.toFile().deleteOnExit()
|
|
||||||
val cordapps = simplifyScanPackages(packages).distinct().fold(emptySet<TestCorDapp>()) { all, packageName -> all + testCorDapp(packageName) }
|
|
||||||
return cached(cordapps, replaceExistingOnes, cordappsDirectory)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val defaultCordappsDirectory: Path by lazy {
|
private val defaultCordappsDirectory: Path by lazy {
|
||||||
|
|
||||||
val cordappsDirectory = (Paths.get("build") / "tmp" / getTimestampAsDirectoryName() / "generated-test-cordapps").toAbsolutePath()
|
val cordappsDirectory = (Paths.get("build") / "tmp" / getTimestampAsDirectoryName() / "generated-test-cordapps").toAbsolutePath()
|
||||||
logger.info("Initialising generated test CorDapps directory in $cordappsDirectory")
|
logger.info("Initialising generated test CorDapps directory in $cordappsDirectory")
|
||||||
if (cordappsDirectory.exists()) {
|
cordappsDirectory.toFile().deleteOnExit()
|
||||||
cordappsDirectory.deleteRecursively()
|
cordappsDirectory.deleteRecursively()
|
||||||
}
|
|
||||||
cordappsDirectory.createDirectories()
|
cordappsDirectory.createDirectories()
|
||||||
cordappsDirectory
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
|
import net.corda.testing.node.TestCordapp
|
||||||
|
|
||||||
|
data class TestCordappImpl(override val name: String,
|
||||||
|
override val version: String,
|
||||||
|
override val vendor: String,
|
||||||
|
override val title: String,
|
||||||
|
override val targetVersion: Int,
|
||||||
|
override val packages: Set<String>,
|
||||||
|
val classes: Set<Class<*>>) : TestCordapp {
|
||||||
|
|
||||||
|
override fun withName(name: String): TestCordappImpl = copy(name = name)
|
||||||
|
|
||||||
|
override fun withVersion(version: String): TestCordappImpl = copy(version = version)
|
||||||
|
|
||||||
|
override fun withVendor(vendor: String): TestCordappImpl = copy(vendor = vendor)
|
||||||
|
|
||||||
|
override fun withTitle(title: String): TestCordappImpl = copy(title = title)
|
||||||
|
|
||||||
|
override fun withTargetVersion(targetVersion: Int): TestCordappImpl = copy(targetVersion = targetVersion)
|
||||||
|
|
||||||
|
fun withClasses(vararg classes: Class<*>): TestCordappImpl {
|
||||||
|
return copy(classes = classes.filter { clazz -> packages.none { clazz.name.startsWith("$it.") } }.toSet())
|
||||||
|
}
|
||||||
|
}
|
@ -1,100 +1,34 @@
|
|||||||
package net.corda.testing.node.internal
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
import io.github.classgraph.ClassGraph
|
import io.github.classgraph.ClassGraph
|
||||||
import net.corda.core.internal.createDirectories
|
|
||||||
import net.corda.core.internal.deleteIfExists
|
|
||||||
import net.corda.core.internal.outputStream
|
import net.corda.core.internal.outputStream
|
||||||
import net.corda.node.internal.cordapp.createTestManifest
|
import net.corda.node.internal.cordapp.createTestManifest
|
||||||
import net.corda.testing.driver.TestCorDapp
|
import net.corda.testing.node.TestCordapp
|
||||||
import org.apache.commons.io.IOUtils
|
|
||||||
import java.io.OutputStream
|
|
||||||
import java.net.URI
|
|
||||||
import java.net.URL
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.attribute.FileTime
|
import java.nio.file.attribute.FileTime
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
|
||||||
import java.util.jar.JarOutputStream
|
import java.util.jar.JarOutputStream
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipOutputStream
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
@JvmField
|
||||||
* Packages some [JarEntryInfo] into a CorDapp JAR with specified [path].
|
val FINANCE_CORDAPP: TestCordappImpl = cordappForPackages("net.corda.finance")
|
||||||
* @param path The path of the JAR.
|
|
||||||
* @param willResourceBeAddedBeToCorDapp A filter for the inclusion of [JarEntryInfo] in the JAR.
|
|
||||||
*/
|
|
||||||
internal fun Iterable<JarEntryInfo>.packageToCorDapp(path: Path, name: String, version: String, vendor: String, title: String = name, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean = { _, _ -> true }) {
|
|
||||||
|
|
||||||
var hasContent = false
|
/** Creates a [TestCordappImpl] for each package. */
|
||||||
try {
|
fun cordappsForPackages(vararg packageNames: String): List<TestCordappImpl> = cordappsForPackages(packageNames.asList())
|
||||||
hasContent = packageToCorDapp(path.outputStream(), name, version, vendor, title, willResourceBeAddedBeToCorDapp)
|
|
||||||
} finally {
|
fun cordappsForPackages(packageNames: Iterable<String>): List<TestCordappImpl> {
|
||||||
if (!hasContent) {
|
return simplifyScanPackages(packageNames).map { cordappForPackages(it) }
|
||||||
path.deleteIfExists()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Creates a single [TestCordappImpl] containing all the given packges. */
|
||||||
* Packages some [JarEntryInfo] into a CorDapp JAR using specified [outputStream].
|
fun cordappForPackages(vararg packageNames: String): TestCordappImpl {
|
||||||
* @param outputStream The [OutputStream] for the JAR.
|
return TestCordapp.Factory.fromPackages(*packageNames) as TestCordappImpl
|
||||||
* @param willResourceBeAddedBeToCorDapp A filter for the inclusion of [JarEntryInfo] in the JAR.
|
|
||||||
*/
|
|
||||||
internal fun Iterable<JarEntryInfo>.packageToCorDapp(outputStream: OutputStream, name: String, version: String, vendor: String, title: String = name, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean = { _, _ -> true }): Boolean {
|
|
||||||
|
|
||||||
val manifest = createTestManifest(name, title, version, vendor)
|
|
||||||
return JarOutputStream(outputStream, manifest).use { jos -> zip(jos, willResourceBeAddedBeToCorDapp) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun cordappForClasses(vararg classes: Class<*>): TestCordappImpl = cordappForPackages().withClasses(*classes)
|
||||||
* Transforms a [Class] into a [JarEntryInfo].
|
|
||||||
*/
|
|
||||||
internal fun Class<*>.jarEntryInfo(): JarEntryInfo {
|
|
||||||
|
|
||||||
return JarEntryInfo.ClassJarEntryInfo(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Packages some [TestCorDapp]s under a root [directory], each with it's own JAR.
|
|
||||||
* @param directory The parent directory in which CorDapp JAR will be created.
|
|
||||||
*/
|
|
||||||
fun Iterable<TestCorDapp>.packageInDirectory(directory: Path) {
|
|
||||||
|
|
||||||
directory.createDirectories()
|
|
||||||
forEach { cordapp -> cordapp.packageAsJarInDirectory(directory) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all classes within the [targetPackage].
|
|
||||||
*/
|
|
||||||
fun allClassesForPackage(targetPackage: String): Set<Class<*>> {
|
|
||||||
return ClassGraph()
|
|
||||||
.whitelistPackages(targetPackage)
|
|
||||||
.enableAllInfo()
|
|
||||||
.scan()
|
|
||||||
.use { it.allClasses.loadClasses() }
|
|
||||||
.toSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps each package to a [TestCorDapp] with resources found in that package.
|
|
||||||
*/
|
|
||||||
fun cordappsForPackages(packages: Iterable<String>): Set<TestCorDapp> {
|
|
||||||
|
|
||||||
return simplifyScanPackages(packages).toSet().fold(emptySet()) { all, packageName -> all + testCorDapp(packageName) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps each package to a [TestCorDapp] with resources found in that package.
|
|
||||||
*/
|
|
||||||
fun cordappsForPackages(firstPackage: String, vararg otherPackages: String): Set<TestCorDapp> {
|
|
||||||
|
|
||||||
return cordappsForPackages(setOf(*otherPackages) + firstPackage)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCallerClass(directCallerClass: KClass<*>): Class<*>? {
|
fun getCallerClass(directCallerClass: KClass<*>): Class<*>? {
|
||||||
|
|
||||||
val stackTrace = Throwable().stackTrace
|
val stackTrace = Throwable().stackTrace
|
||||||
val index = stackTrace.indexOfLast { it.className == directCallerClass.java.name }
|
val index = stackTrace.indexOfLast { it.className == directCallerClass.java.name }
|
||||||
if (index == -1) return null
|
if (index == -1) return null
|
||||||
@ -105,112 +39,42 @@ fun getCallerClass(directCallerClass: KClass<*>): Class<*>? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCallerPackage(directCallerClass: KClass<*>): String? {
|
fun getCallerPackage(directCallerClass: KClass<*>): String? = getCallerClass(directCallerClass)?.`package`?.name
|
||||||
|
|
||||||
return getCallerClass(directCallerClass)?.`package`?.name
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a [TestCorDapp] containing resources found in [packageName].
|
|
||||||
*/
|
|
||||||
internal fun testCorDapp(packageName: String): TestCorDapp {
|
|
||||||
|
|
||||||
val uuid = UUID.randomUUID()
|
|
||||||
val name = "$packageName-$uuid"
|
|
||||||
val version = "$uuid"
|
|
||||||
return TestCorDapp.Factory.create(name, version).plusPackage(packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Squashes child packages if the parent is present. Example: ["com.foo", "com.foo.bar"] into just ["com.foo"].
|
* Squashes child packages if the parent is present. Example: ["com.foo", "com.foo.bar"] into just ["com.foo"].
|
||||||
*/
|
*/
|
||||||
fun simplifyScanPackages(scanPackages: Iterable<String>): List<String> {
|
fun simplifyScanPackages(scanPackages: Iterable<String>): Set<String> {
|
||||||
|
return scanPackages.sorted().fold(emptySet()) { soFar, packageName ->
|
||||||
return scanPackages.sorted().fold(emptyList()) { listSoFar, packageName ->
|
|
||||||
when {
|
when {
|
||||||
listSoFar.isEmpty() -> listOf(packageName)
|
soFar.isEmpty() -> setOf(packageName)
|
||||||
packageName.startsWith(listSoFar.last()) -> listSoFar
|
packageName.startsWith("${soFar.last()}.") -> soFar
|
||||||
else -> listSoFar + packageName
|
else -> soFar + packageName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun TestCordappImpl.packageAsJar(file: Path) {
|
||||||
* Transforms a class or package name into a path segment.
|
// Don't mention "classes" in the error message as that feature is only available internally
|
||||||
*/
|
require(packages.isNotEmpty() || classes.isNotEmpty()) { "At least one package must be specified" }
|
||||||
internal fun String.packageToJarPath() = replace(".", "/")
|
|
||||||
|
|
||||||
private fun Iterable<JarEntryInfo>.zip(outputStream: ZipOutputStream, willResourceBeAddedBeToCorDapp: (String, URL) -> Boolean): Boolean {
|
val scanResult = ClassGraph()
|
||||||
|
.whitelistPackages(*packages.toTypedArray())
|
||||||
|
.whitelistClasses(*classes.map { it.name }.toTypedArray())
|
||||||
|
.scan()
|
||||||
|
|
||||||
val entries = filter { (fullyQualifiedName, url) -> willResourceBeAddedBeToCorDapp(fullyQualifiedName, url) }
|
scanResult.use {
|
||||||
if (entries.isNotEmpty()) {
|
val manifest = createTestManifest(name, title, version, vendor, targetVersion)
|
||||||
zip(outputStream, entries)
|
JarOutputStream(file.outputStream(), manifest).use { jos ->
|
||||||
}
|
val time = FileTime.from(Instant.now())
|
||||||
return entries.isNotEmpty()
|
// The same resource may be found in different locations (this will happen when running from gradle) so just
|
||||||
}
|
// pick the first one found.
|
||||||
|
scanResult.allResources.asMap().forEach { path, resourceList ->
|
||||||
private fun zip(outputStream: ZipOutputStream, allInfo: Iterable<JarEntryInfo>) {
|
val entry = ZipEntry(path).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time)
|
||||||
|
jos.putNextEntry(entry)
|
||||||
val time = FileTime.from(Instant.EPOCH)
|
resourceList[0].open().use { it.copyTo(jos) }
|
||||||
val classLoader = Thread.currentThread().contextClassLoader
|
jos.closeEntry()
|
||||||
allInfo.distinctBy { it.url }.sortedBy { it.url.toExternalForm() }.forEach { info ->
|
|
||||||
|
|
||||||
try {
|
|
||||||
val entry = ZipEntry(info.entryName).setCreationTime(time).setLastAccessTime(time).setLastModifiedTime(time)
|
|
||||||
outputStream.putNextEntry(entry)
|
|
||||||
classLoader.getResourceAsStream(info.entryName).use {
|
|
||||||
IOUtils.copy(it, outputStream)
|
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
outputStream.closeEntry()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a single resource to be added to a CorDapp JAR.
|
|
||||||
*/
|
|
||||||
internal sealed class JarEntryInfo(val fullyQualifiedName: String, val url: URL) {
|
|
||||||
|
|
||||||
abstract val entryName: String
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a class to be added to a CorDapp JAR.
|
|
||||||
*/
|
|
||||||
class ClassJarEntryInfo(val clazz: Class<*>) : JarEntryInfo(clazz.name, clazz.classFileURL()) {
|
|
||||||
|
|
||||||
override val entryName = "${fullyQualifiedName.packageToJarPath()}$fileExtensionSeparator$classFileExtension"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a resource file to be added to a CorDapp JAR.
|
|
||||||
*/
|
|
||||||
class ResourceJarEntryInfo(fullyQualifiedName: String, url: URL) : JarEntryInfo(fullyQualifiedName, url) {
|
|
||||||
|
|
||||||
override val entryName: String
|
|
||||||
get() {
|
|
||||||
val extensionIndex = fullyQualifiedName.lastIndexOf(fileExtensionSeparator)
|
|
||||||
return "${fullyQualifiedName.substring(0 until extensionIndex).packageToJarPath()}${fullyQualifiedName.substring(extensionIndex)}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun component1(): String = fullyQualifiedName
|
|
||||||
|
|
||||||
operator fun component2(): URL = url
|
|
||||||
|
|
||||||
private companion object {
|
|
||||||
|
|
||||||
private const val classFileExtension = "class"
|
|
||||||
private const val fileExtensionSeparator = "."
|
|
||||||
private const val whitespace = " "
|
|
||||||
private const val whitespaceReplacement = "%20"
|
|
||||||
|
|
||||||
private fun Class<*>.classFileURL(): URL {
|
|
||||||
|
|
||||||
require(protectionDomain?.codeSource?.location != null) { "Invalid class $name for test CorDapp. Classes without protection domain cannot be referenced. This typically happens for Java / Kotlin types." }
|
|
||||||
return URI.create("${protectionDomain.codeSource.location}/${name.packageToJarPath()}$fileExtensionSeparator$classFileExtension".escaped()).toURL()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.escaped(): String = this.replace(whitespace, whitespaceReplacement)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,93 @@
|
|||||||
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
|
import net.corda.core.internal.inputStream
|
||||||
|
import net.corda.node.internal.cordapp.get
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.TemporaryFolder
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.jar.JarInputStream
|
||||||
|
|
||||||
|
class TestCordappsUtilsTest {
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val tempFolder = TemporaryFolder()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test simplifyScanPackages`() {
|
||||||
|
assertThat(simplifyScanPackages(emptyList())).isEmpty()
|
||||||
|
assertThat(simplifyScanPackages(listOf("com.foo.bar"))).containsExactlyInAnyOrder("com.foo.bar")
|
||||||
|
assertThat(simplifyScanPackages(listOf("com.foo", "com.foo"))).containsExactlyInAnyOrder("com.foo")
|
||||||
|
assertThat(simplifyScanPackages(listOf("com.foo", "com.bar"))).containsExactlyInAnyOrder("com.foo", "com.bar")
|
||||||
|
assertThat(simplifyScanPackages(listOf("com.foo", "com.foo.bar"))).containsExactlyInAnyOrder("com.foo")
|
||||||
|
assertThat(simplifyScanPackages(listOf("com.foo.bar", "com.foo"))).containsExactlyInAnyOrder("com.foo")
|
||||||
|
assertThat(simplifyScanPackages(listOf("com.foobar", "com.foo.bar"))).containsExactlyInAnyOrder("com.foobar", "com.foo.bar")
|
||||||
|
assertThat(simplifyScanPackages(listOf("com.foobar", "com.foo"))).containsExactlyInAnyOrder("com.foobar", "com.foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `packageAsJar writes out the CorDapp info into the manifest`() {
|
||||||
|
val cordapp = cordappForPackages("net.corda.testing.node.internal")
|
||||||
|
.withTargetVersion(123)
|
||||||
|
.withName("TestCordappsUtilsTest")
|
||||||
|
|
||||||
|
val jarFile = packageAsJar(cordapp)
|
||||||
|
JarInputStream(jarFile.inputStream()).use {
|
||||||
|
assertThat(it.manifest["Target-Platform-Version"]).isEqualTo("123")
|
||||||
|
assertThat(it.manifest["Name"]).isEqualTo("TestCordappsUtilsTest")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `packageAsJar on leaf package`() {
|
||||||
|
val entries = packageAsJarThenReadBack(cordappForPackages("net.corda.testing.node.internal"))
|
||||||
|
|
||||||
|
assertThat(entries).contains(
|
||||||
|
"net/corda/testing/node/internal/TestCordappsUtilsTest.class",
|
||||||
|
"net/corda/testing/node/internal/resource.txt" // Make sure non-class resource files are also picked up
|
||||||
|
).doesNotContain(
|
||||||
|
"net/corda/testing/node/MockNetworkTest.class"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Make sure the MockNetworkTest class does actually exist to ensure the above is not a false-positive
|
||||||
|
assertThat(javaClass.classLoader.getResource("net/corda/testing/node/MockNetworkTest.class")).isNotNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `packageAsJar on package with sub-packages`() {
|
||||||
|
val entries = packageAsJarThenReadBack(cordappForPackages("net.corda.testing.node"))
|
||||||
|
|
||||||
|
assertThat(entries).contains(
|
||||||
|
"net/corda/testing/node/internal/TestCordappsUtilsTest.class",
|
||||||
|
"net/corda/testing/node/internal/resource.txt",
|
||||||
|
"net/corda/testing/node/MockNetworkTest.class"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `packageAsJar on single class`() {
|
||||||
|
val entries = packageAsJarThenReadBack(cordappForClasses(InternalMockNetwork::class.java))
|
||||||
|
|
||||||
|
assertThat(entries).containsOnly("${InternalMockNetwork::class.java.name.replace('.', '/')}.class")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun packageAsJar(cordapp: TestCordappImpl): Path {
|
||||||
|
val jarFile = tempFolder.newFile().toPath()
|
||||||
|
cordapp.packageAsJar(jarFile)
|
||||||
|
return jarFile
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun packageAsJarThenReadBack(cordapp: TestCordappImpl): List<String> {
|
||||||
|
val jarFile = packageAsJar(cordapp)
|
||||||
|
val entries = ArrayList<String>()
|
||||||
|
JarInputStream(jarFile.inputStream()).use {
|
||||||
|
while (true) {
|
||||||
|
val e = it.nextJarEntry ?: break
|
||||||
|
entries += e.name
|
||||||
|
it.closeEntry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user