Merge remote-tracking branch 'corda/master' into christians_os_merge_20171031

This commit is contained in:
Christian Sailer
2017-11-02 14:41:18 +00:00
1211 changed files with 3627 additions and 432161 deletions

View File

@ -290,7 +290,7 @@ class FlowStackSnapshotTest {
@Test
fun `flowStackSnapshot object is serializable`() {
val mockNet = MockNetwork(threadPerNode = true)
mockNet.createNotaryNode(legalName = DUMMY_NOTARY.name)
mockNet.createNotaryNode()
val node = mockNet.createPartyNode()
node.internals.registerInitiatedFlow(DummyFlow::class.java)
node.services.startFlow(FlowStackSnapshotSerializationTestingFlow()).resultFuture.get()

View File

@ -28,13 +28,13 @@ fun ledger(
initialiseSerialization: Boolean = true,
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
if (initialiseSerialization) initialiseTestSerialization()
val serializationEnv = initialiseTestSerialization(initialiseSerialization)
try {
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(services))
dsl(ledgerDsl)
return ledgerDsl
} finally {
if (initialiseSerialization) resetTestSerialization()
serializationEnv.resetTestSerialization()
}
}
@ -59,7 +59,6 @@ fun testNodeConfiguration(
myLegalName: CordaX500Name): NodeConfiguration {
abstract class MockableNodeConfiguration : NodeConfiguration // Otherwise Mockito is defeated by val getters.
return rigorousMock<MockableNodeConfiguration>().also {
doReturn(true).whenever(it).noNetworkMapServiceMode
doReturn(baseDirectory).whenever(it).baseDirectory
doReturn(myLegalName).whenever(it).myLegalName
doReturn(1).whenever(it).minimumPlatformVersion
@ -77,7 +76,7 @@ fun testNodeConfiguration(
doReturn(VerifierType.InMemory).whenever(it).verifierType
doReturn(5).whenever(it).messageRedeliveryDelaySeconds
doReturn(0L).whenever(it).additionalNodeInfoPollingFrequencyMsec
doReturn(null).whenever(it).networkMapService
doReturn(null).whenever(it).devModeOptions
doCallRealMethod().whenever(it).certificatesDirectory
doCallRealMethod().whenever(it).trustStoreFile
doCallRealMethod().whenever(it).sslKeystore

View File

@ -31,7 +31,6 @@ import net.corda.node.services.network.NetworkMapService
import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.NodeInfoFilesCopier
import net.corda.nodeapi.User
import net.corda.nodeapi.config.parseAs
import net.corda.nodeapi.config.toConfig
import net.corda.nodeapi.internal.addShutdownHook
import net.corda.testing.*
@ -199,13 +198,13 @@ sealed class NodeHandle {
* will be added and that will be used.
*/
abstract val rpc: CordaRPCOps
abstract val configuration: FullNodeConfiguration
abstract val configuration: NodeConfiguration
abstract val webAddress: NetworkHostAndPort
data class OutOfProcess(
override val nodeInfo: NodeInfo,
override val rpc: CordaRPCOps,
override val configuration: FullNodeConfiguration,
override val configuration: NodeConfiguration,
override val webAddress: NetworkHostAndPort,
val debugPort: Int?,
val process: Process,
@ -224,7 +223,7 @@ sealed class NodeHandle {
data class InProcess(
override val nodeInfo: NodeInfo,
override val rpc: CordaRPCOps,
override val configuration: FullNodeConfiguration,
override val configuration: NodeConfiguration,
override val webAddress: NetworkHostAndPort,
val node: StartedNode<Node>,
val nodeThread: Thread,
@ -406,7 +405,7 @@ fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericD
coerce: (D) -> DI,
dsl: DI.() -> A
): A {
if (initialiseSerialization) initialiseTestSerialization()
val serializationEnv = initialiseTestSerialization(initialiseSerialization)
val shutdownHook = addShutdownHook(driverDsl::shutdown)
try {
driverDsl.start()
@ -417,7 +416,57 @@ fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericD
} finally {
driverDsl.shutdown()
shutdownHook.cancel()
if (initialiseSerialization) resetTestSerialization()
serializationEnv.resetTestSerialization()
}
}
/**
* This is a helper method to allow extending of the DSL, along the lines of
* interface SomeOtherExposedDSLInterface : DriverDSLExposedInterface
* interface SomeOtherInternalDSLInterface : DriverDSLInternalInterface, SomeOtherExposedDSLInterface
* class SomeOtherDSL(val driverDSL : DriverDSL) : DriverDSLInternalInterface by driverDSL, SomeOtherInternalDSLInterface
*
* @param coerce We need this explicit coercion witness because we can't put an extra DI : D bound in a `where` clause.
*/
fun <DI : DriverDSLExposedInterface, D : DriverDSLInternalInterface, A> genericDriver(
defaultParameters: DriverParameters = DriverParameters(),
isDebug: Boolean = defaultParameters.isDebug,
driverDirectory: Path = defaultParameters.driverDirectory,
portAllocation: PortAllocation = defaultParameters.portAllocation,
debugPortAllocation: PortAllocation = defaultParameters.debugPortAllocation,
systemProperties: Map<String, String> = defaultParameters.systemProperties,
useTestClock: Boolean = defaultParameters.useTestClock,
initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
driverDslWrapper: (DriverDSL) -> D,
coerce: (D) -> DI,
dsl: DI.() -> A
): A {
val serializationEnv = initialiseTestSerialization(initialiseSerialization)
val driverDsl = driverDslWrapper(
DriverDSL(
portAllocation = portAllocation,
debugPortAllocation = debugPortAllocation,
systemProperties = systemProperties,
driverDirectory = driverDirectory.toAbsolutePath(),
useTestClock = useTestClock,
isDebug = isDebug,
startNodesInProcess = startNodesInProcess,
extraCordappPackagesToScan = extraCordappPackagesToScan
)
)
val shutdownHook = addShutdownHook(driverDsl::shutdown)
try {
driverDsl.start()
return dsl(coerce(driverDsl))
} catch (exception: Throwable) {
log.error("Driver shutting down because of exception", exception)
throw exception
} finally {
driverDsl.shutdown()
shutdownHook.cancel()
serializationEnv.resetTestSerialization()
}
}
@ -652,7 +701,7 @@ class DriverDSL(
_executorService?.shutdownNow()
}
private fun establishRpc(config: FullNodeConfiguration, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
private fun establishRpc(config: NodeConfiguration, processDeathFuture: CordaFuture<out Process>): CordaFuture<CordaRPCOps> {
val rpcAddress = config.rpcAddress!!
val client = CordaRPCClient(rpcAddress)
val connectionFuture = poll(executorService, "RPC connection") {
@ -868,16 +917,8 @@ class DriverDSL(
return future
}
private fun startNodeInternal(name: CordaX500Name,config: Config, webAddress: NetworkHostAndPort, startInProcess: Boolean?, maximumHeapSize: String, logLevel: String? = null): CordaFuture<NodeHandle> {
val globalDataSourceProperties = mutableMapOf<String, Any?>()
val overriddenDatasourceUrl = systemProperties["dataSourceProperties.dataSource.url"]
overriddenDatasourceUrl?.let {
val connectionString = overriddenDatasourceUrl + "/" + databaseNamesByNode.computeIfAbsent(name, { UUID.randomUUID().toString() })
globalDataSourceProperties["dataSourceProperties.dataSource.url"] = connectionString
}
val enhancedConfig = config+ globalDataSourceProperties
val nodeConfiguration = (enhancedConfig).parseAs<FullNodeConfiguration>()
private fun startNodeInternal(config: Config, webAddress: NetworkHostAndPort, startInProcess: Boolean?, maximumHeapSize: String): CordaFuture<NodeHandle> {
val nodeConfiguration = config.parseAsNodeConfiguration()
nodeInfoFilesCopier.addConfig(nodeConfiguration.baseDirectory)
val onNodeExit: () -> Unit = {
nodeInfoFilesCopier.removeConfig(nodeConfiguration.baseDirectory)
@ -947,7 +988,7 @@ class DriverDSL(
private fun startInProcessNode(
executorService: ScheduledExecutorService,
nodeConf: FullNodeConfiguration,
nodeConf: NodeConfiguration,
config: Config,
cordappPackages: List<String>
): CordaFuture<Pair<StartedNode<Node>, Thread>> {
@ -966,7 +1007,7 @@ class DriverDSL(
private fun startOutOfProcessNode(
executorService: ScheduledExecutorService,
nodeConf: FullNodeConfiguration,
nodeConf: NodeConfiguration,
config: Config,
quasarJarPath: String,
debugPort: Int?,
@ -989,7 +1030,7 @@ class DriverDSL(
)
// See experimental/quasar-hook/README.md for how to generate.
val excludePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)"
val extraJvmArguments = systemProperties.map { "-D${it.key}=${it.value}" } +
val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } +
"-javaagent:$quasarJarPath=$excludePattern"
val loggingLevel = logLevel ?: if (debugPort == null) "INFO" else "DEBUG"
@ -1042,6 +1083,14 @@ class DriverDSL(
.let { Class.forName(it.className).`package`?.name }
?: throw IllegalStateException("Function instantiating driver must be defined in a package.")
}
/**
* We have an alternative way of specifying classpath for spawned process: by using "-cp" option. So duplicating the setting of this
* rather long string is un-necessary and can be harmful on Windows.
*/
private fun Map<String, Any>.removeResolvedClasspath(): Map<String, Any> {
return filterNot { it.key == "java.class.path" }
}
}
}

View File

@ -49,9 +49,12 @@ object ProcessUtilities {
addAll(arguments)
}
return ProcessBuilder(command).apply {
if (errorLogPath != null) redirectError(errorLogPath.toFile()) // FIXME: Undone by inheritIO.
inheritIO()
if (workingDirectory != null) directory(workingDirectory.toFile())
if (workingDirectory != null) {
redirectError((workingDirectory / "$className.stderr.log").toFile())
redirectOutput((workingDirectory / "$className.stdout.log").toFile())
directory(workingDirectory.toFile())
}
}.start()
}

View File

@ -0,0 +1,115 @@
package net.corda.testing.internal
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.fork
import net.corda.core.internal.concurrent.transpose
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.node.NodeInfo
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.config.ConfigHelper
import net.corda.node.services.config.configOf
import net.corda.node.services.config.parseAsNodeConfiguration
import net.corda.node.services.config.plus
import net.corda.nodeapi.User
import net.corda.testing.SerializationEnvironmentRule
import net.corda.testing.driver.addressMustNotBeBoundFuture
import net.corda.testing.getFreeLocalPorts
import net.corda.testing.node.MockServices
import org.apache.logging.log4j.Level
import org.junit.After
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import java.nio.file.Path
import java.util.concurrent.Executors
import kotlin.concurrent.thread
// TODO Some of the logic here duplicates what's in the driver
abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) {
companion object {
private val WHITESPACE = "\\s++".toRegex()
}
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule()
@Rule
@JvmField
val tempFolder = TemporaryFolder()
private val nodes = mutableListOf<StartedNode<Node>>()
private val nodeInfos = mutableListOf<NodeInfo>()
init {
System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase())
}
/**
* Stops the network map node and all the nodes started by [startNode]. This is called automatically after each test
* but can also be called manually within a test.
*/
@After
fun stopAllNodes() {
val shutdownExecutor = Executors.newScheduledThreadPool(nodes.size)
nodes.map { shutdownExecutor.fork(it::dispose) }.transpose().getOrThrow()
// Wait until ports are released
val portNotBoundChecks = nodes.flatMap {
listOf(
it.internals.configuration.p2pAddress.let { addressMustNotBeBoundFuture(shutdownExecutor, it) },
it.internals.configuration.rpcAddress?.let { addressMustNotBeBoundFuture(shutdownExecutor, it) }
)
}.filterNotNull()
nodes.clear()
portNotBoundChecks.transpose().getOrThrow()
}
@JvmOverloads
fun startNode(legalName: CordaX500Name,
platformVersion: Int = 1,
rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap()): StartedNode<Node> {
val baseDirectory = baseDirectory(legalName).createDirectories()
val localPort = getFreeLocalPorts("localhost", 2)
val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString()
val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory,
allowMissingConfig = true,
configOverrides = configOf(
"myLegalName" to legalName.toString(),
"p2pAddress" to p2pAddress,
"rpcAddress" to localPort[1].toString(),
"rpcUsers" to rpcUsers.map { it.toMap() }
) + configOverrides
)
val parsedConfig = config.parseAsNodeConfiguration()
val node = Node(
parsedConfig,
MockServices.MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
initialiseSerialization = false,
cordappLoader = CordappLoader.createDefaultWithTestPackages(parsedConfig, cordappPackages)).start()
nodes += node
ensureAllNetworkMapCachesHaveAllNodeInfos()
thread(name = legalName.organisation) {
node.internals.run()
}
return node
}
protected fun baseDirectory(legalName: CordaX500Name): Path {
return tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "")
}
private fun ensureAllNetworkMapCachesHaveAllNodeInfos() {
val runningNodes = nodes.filter { it.internals.started != null }
val runningNodesInfo = runningNodes.map { it.info }
for (node in runningNodes)
for (nodeInfo in runningNodesInfo) {
node.services.networkMapCache.addNode(nodeInfo)
}
}
}

View File

@ -1,9 +1,9 @@
package net.corda.testing.node
import co.paralleluniverse.common.util.VisibleForTesting
import net.corda.core.concurrent.CordaFuture
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache
import net.corda.core.utilities.NetworkHostAndPort
@ -27,32 +27,12 @@ class MockNetworkMapCache(database: CordaPersistence, configuration: NodeConfigu
}
override val changed: Observable<NetworkMapCache.MapChange> = PublishSubject.create<NetworkMapCache.MapChange>()
override val nodeReady: CordaFuture<Void?> get() = doneFuture(null)
init {
val mockNodeA = NodeInfo(listOf(BANK_C_ADDR), listOf(BANK_C), 1, serial = 1L)
val mockNodeB = NodeInfo(listOf(BANK_D_ADDR), listOf(BANK_D), 1, serial = 1L)
partyNodes.add(mockNodeA)
partyNodes.add(mockNodeB)
runWithoutMapService()
}
/**
* Directly add a registration to the internal cache. DOES NOT fire the change listeners, as it's
* not a change being received.
*/
@VisibleForTesting
fun addRegistration(node: NodeInfo) {
val previousIndex = partyNodes.indexOfFirst { it.legalIdentitiesAndCerts == node.legalIdentitiesAndCerts }
if (previousIndex != -1) partyNodes[previousIndex] = node
else partyNodes.add(node)
}
/**
* Directly remove a registration from the internal cache. DOES NOT fire the change listeners, as it's
* not a change being received.
*/
@VisibleForTesting
fun deleteRegistration(legalIdentity: Party): Boolean {
return partyNodes.removeIf { legalIdentity.owningKey in it.legalIdentitiesAndCerts.map { it.owningKey } }
}
}

View File

@ -8,7 +8,6 @@ import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.random63BitValue
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.internal.createDirectories
import net.corda.core.internal.createDirectory
import net.corda.core.internal.uncheckedCast
@ -17,12 +16,10 @@ import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService
import net.corda.core.node.services.PartyInfo
import net.corda.core.serialization.SerializationWhitelist
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.loggerFor
import net.corda.finance.utils.WorldMapLocation
import net.corda.node.internal.AbstractNode
import net.corda.node.internal.StartedNode
import net.corda.node.internal.cordapp.CordappLoader
@ -32,7 +29,7 @@ import net.corda.node.services.config.BFTSMaRtConfiguration
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.NotaryConfig
import net.corda.node.services.keys.E2ETestKeyManagementService
import net.corda.node.services.messaging.*
import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.network.InMemoryNetworkMapService
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
@ -42,10 +39,8 @@ import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.nodeapi.internal.ServiceInfo
import net.corda.testing.DUMMY_NOTARY
import net.corda.testing.initialiseTestSerialization
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import net.corda.testing.resetTestSerialization
import net.corda.testing.testNodeConfiguration
import org.apache.activemq.artemis.utils.ReusableLatch
import org.slf4j.Logger
@ -54,9 +49,9 @@ import java.math.BigInteger
import java.nio.file.Path
import java.security.KeyPair
import java.security.PublicKey
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
import net.corda.testing.testNodeConfiguration
fun StartedNode<MockNetwork.MockNode>.pumpReceive(block: Boolean = false): InMemoryMessagingNetwork.MessageTransfer? {
return (network as InMemoryMessagingNetwork.InMemoryMessaging).pumpReceive(block)
@ -125,11 +120,10 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
private val threadPerNode: Boolean = defaultParameters.threadPerNode,
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
private val defaultFactory: Factory<*> = defaultParameters.defaultFactory,
private val initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
initialiseSerialization: Boolean = defaultParameters.initialiseSerialization,
private val cordappPackages: List<String> = defaultParameters.cordappPackages) : Closeable {
/** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */
constructor(parameters: MockNetworkParameters) : this(defaultParameters = parameters)
var nextNodeId = 0
private set
private val filesystem = Jimfs.newFileSystem(unix())
@ -140,9 +134,9 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
private val _nodes = mutableListOf<MockNode>()
/** A read only view of the current set of executing nodes. */
val nodes: List<MockNode> get() = _nodes
private val serializationEnv = initialiseTestSerialization(initialiseSerialization)
init {
if (initialiseSerialization) initialiseTestSerialization()
filesystem.getPath("/nodes").createDirectory()
}
@ -185,7 +179,6 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
CordappLoader.createDefaultWithTestPackages(args.config, args.network.cordappPackages),
args.network.busyLatch) {
val mockNet = args.network
override val networkMapAddress = null
val id = args.id
internal val notaryIdentity = args.notaryIdentity
val entropyRoot = args.entropyRoot
@ -238,11 +231,11 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
return entropyToKeyPair(counter)
}
// It's OK to not have a network map service in the mock network.
override fun noNetworkMapConfigured() = doneFuture(Unit)
// There is no need to slow down the unit tests by initialising CityDatabase
open fun findMyLocation(): WorldMapLocation? = null // It's left only for NetworkVisualiserSimulation
/**
* MockNetwork will ensure nodes are connected to each other. The nodes themselves
* won't be able to tell if that happened already or not.
*/
override fun checkNetworkMapIsInitialized() = Unit
override fun makeTransactionVerifierService() = InMemoryTransactionVerifierService(1)
@ -253,13 +246,6 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
val testSerializationWhitelists by lazy { super.serializationWhitelists.toMutableList() }
override val serializationWhitelists: List<SerializationWhitelist>
get() = testSerializationWhitelists
// This does not indirect through the NodeInfo object so it can be called before the node is started.
// It is used from the network visualiser tool.
@Suppress("unused")
val place: WorldMapLocation
get() = findMyLocation()!!
private var dbCloser: (() -> Any?)? = null
override fun <T> initialiseDatabasePersistence(schemaService: SchemaService, insideTransaction: () -> T) = super.initialiseDatabasePersistence(schemaService) {
dbCloser = database::close
@ -354,10 +340,8 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
}
@JvmOverloads
fun createNotaryNode(legalName: CordaX500Name = DUMMY_NOTARY.name, validating: Boolean = true): StartedNode<MockNode> {
return createNode(MockNodeParameters(legalName = legalName, configOverrides = {
doReturn(NotaryConfig(validating)).whenever(it).notary
}))
fun createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name), validating: Boolean = true): StartedNode<MockNode> {
return createNotaryNode(parameters, validating, defaultFactory)
}
fun <N : MockNode> createNotaryNode(parameters: MockNodeParameters = MockNodeParameters(legalName = DUMMY_NOTARY.name),
@ -406,7 +390,7 @@ class MockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParamete
fun stopNodes() {
nodes.forEach { it.started?.dispose() }
if (initialiseSerialization) resetTestSerialization()
serializationEnv.resetTestSerialization()
}
// Test method to block until all scheduled activity, active flows

View File

@ -9,10 +9,7 @@ import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.FlowProgressHandle
import net.corda.core.node.AppServiceHub
import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub
import net.corda.core.node.StateLoader
import net.corda.core.node.*
import net.corda.core.node.services.*
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
@ -109,7 +106,7 @@ open class MockServices(
/**
* Makes database and mock services appropriate for unit tests.
* @param keys a list of [KeyPair] instances to be used by [MockServices]. Defualts to [MEGA_CORP_KEY]
* @param keys a list of [KeyPair] instances to be used by [MockServices]. Defaults to [MEGA_CORP_KEY]
* @param createIdentityService a lambda function returning an instance of [IdentityService]. Defauts to [InMemoryIdentityService].
*
* @return a pair where the first element is the instance of [CordaPersistence] and the second is [MockServices].
@ -126,14 +123,14 @@ open class MockServices(
val mockService = database.transaction {
object : MockServices(cordappLoader, *(keys.toTypedArray())) {
override val identityService: IdentityService = database.transaction { identityServiceRef }
override val vaultService = makeVaultService(database.hibernateConfig)
override val vaultService: VaultServiceInternal = makeVaultService(database.hibernateConfig)
override fun recordTransactions(notifyVault: Boolean, txs: Iterable<SignedTransaction>) {
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
for (stx in txs) {
validatedTransactions.addTransaction(stx)
}
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
vaultService.notifyAll(txs.map { it.tx })
vaultService.notifyAll(statesToRecord, txs.map { it.tx })
}
override fun jdbcSession(): Connection = database.createSession()
@ -150,7 +147,7 @@ open class MockServices(
val key: KeyPair get() = keys.first()
override fun recordTransactions(notifyVault: Boolean, txs: Iterable<SignedTransaction>) {
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
txs.forEach {
stateMachineRecordedTransactionMapping.addMapping(StateMachineRunId.createRandom(), it.id)
}

View File

@ -1,205 +0,0 @@
package net.corda.testing.node
import net.corda.core.concurrent.CordaFuture
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.concurrent.*
import net.corda.core.internal.createDirectories
import net.corda.core.internal.div
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow
import net.corda.node.internal.Node
import net.corda.node.internal.StartedNode
import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.services.config.*
import net.corda.node.utilities.ServiceIdentityGenerator
import net.corda.nodeapi.User
import net.corda.nodeapi.config.parseAs
import net.corda.nodeapi.config.toConfig
import net.corda.testing.DUMMY_MAP
import net.corda.testing.TestDependencyInjectionBase
import net.corda.testing.driver.addressMustNotBeBoundFuture
import net.corda.testing.getFreeLocalPorts
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
import org.apache.logging.log4j.Level
import org.junit.After
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import java.util.concurrent.Executors
import kotlin.concurrent.thread
/**
* Extend this class if you need to run nodes in a test. You could use the driver DSL but it's extremely slow for testing
* purposes. Use the driver if you need to run the nodes in separate processes otherwise this class will suffice.
*/
// TODO Some of the logic here duplicates what's in the driver
abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) : TestDependencyInjectionBase() {
companion object {
private val WHITESPACE = "\\s++".toRegex()
}
@Rule
@JvmField
val tempFolder = TemporaryFolder()
private val nodes = mutableListOf<StartedNode<Node>>()
private var _networkMapNode: StartedNode<Node>? = null
val networkMapNode: StartedNode<Node> get() = _networkMapNode ?: startNetworkMapNode()
init {
System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase())
}
/**
* Stops the network map node and all the nodes started by [startNode]. This is called automatically after each test
* but can also be called manually within a test.
*/
@After
fun stopAllNodes() {
val shutdownExecutor = Executors.newScheduledThreadPool(nodes.size)
nodes.map { shutdownExecutor.fork(it::dispose) }.transpose().getOrThrow()
// Wait until ports are released
val portNotBoundChecks = nodes.flatMap {
listOf(
it.internals.configuration.p2pAddress.let { addressMustNotBeBoundFuture(shutdownExecutor, it) },
it.internals.configuration.rpcAddress?.let { addressMustNotBeBoundFuture(shutdownExecutor, it) }
)
}.filterNotNull()
nodes.clear()
_networkMapNode = null
portNotBoundChecks.transpose().getOrThrow()
}
/**
* Clear network map data from nodes' databases.
*/
fun clearAllNodeInfoDb() {
nodes.forEach { it.services.networkMapCache.clearNetworkMapCache() }
}
/**
* You can use this method to start the network map node in a more customised manner. Otherwise it
* will automatically be started with the default parameters.
*/
fun startNetworkMapNode(legalName: CordaX500Name = DUMMY_MAP.name,
platformVersion: Int = 1,
rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap()): StartedNode<Node> {
check(_networkMapNode == null || _networkMapNode!!.info.legalIdentitiesAndCerts.first().name == legalName)
return startNodeInternal(legalName, platformVersion, rpcUsers, configOverrides).apply {
_networkMapNode = this
}
}
@JvmOverloads
fun startNode(legalName: CordaX500Name,
platformVersion: Int = 1,
rpcUsers: List<User> = emptyList(),
configOverrides: Map<String, Any> = emptyMap(),
noNetworkMap: Boolean = false,
waitForConnection: Boolean = true): CordaFuture<StartedNode<Node>> {
val networkMapConf = if (noNetworkMap) {
// Nonexistent network map service address.
mapOf(
"networkMapService" to mapOf(
"address" to "localhost:10000",
"legalName" to networkMapNode.info.legalIdentitiesAndCerts.first().name.toString()
)
)
} else {
mapOf(
"networkMapService" to mapOf(
"address" to networkMapNode.internals.configuration.p2pAddress.toString(),
"legalName" to networkMapNode.info.legalIdentitiesAndCerts.first().name.toString()
)
)
}
val node = startNodeInternal(
legalName,
platformVersion,
rpcUsers,
networkMapConf + configOverrides,
noNetworkMap)
return if (waitForConnection) node.internals.nodeReadyFuture.map { node } else doneFuture(node)
}
// TODO This method has been added temporarily, to be deleted once the set of notaries is defined at the network level.
fun startNotaryNode(name: CordaX500Name,
rpcUsers: List<User> = emptyList(),
validating: Boolean = true): CordaFuture<StartedNode<Node>> {
return startNode(name, rpcUsers = rpcUsers, configOverrides = mapOf("notary" to mapOf("validating" to validating)))
}
fun startNotaryCluster(notaryName: CordaX500Name, clusterSize: Int): CordaFuture<List<StartedNode<Node>>> {
fun notaryConfig(nodeAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): Map<String, Any> {
val clusterAddresses = if (clusterAddress != null) listOf(clusterAddress) else emptyList()
val config = NotaryConfig(validating = true, raft = RaftConfig(nodeAddress = nodeAddress, clusterAddresses = clusterAddresses))
return mapOf("notary" to config.toConfig().root().unwrapped())
}
ServiceIdentityGenerator.generateToDisk(
(0 until clusterSize).map { baseDirectory(notaryName.copy(organisation = "${notaryName.organisation}-$it")) },
notaryName)
val nodeAddresses = getFreeLocalPorts("localhost", clusterSize)
val masterNodeFuture = startNode(
CordaX500Name(organisation = "${notaryName.organisation}-0", locality = notaryName.locality, country = notaryName.country),
configOverrides = notaryConfig(nodeAddresses[0]) + mapOf(
"database" to mapOf(
"serverNameTablePrefix" to if (clusterSize > 1) "${notaryName.organisation}0".replace(Regex("[^0-9A-Za-z]+"), "") else ""
)
)
)
val remainingNodesFutures = (1 until clusterSize).map {
startNode(
CordaX500Name(organisation = "${notaryName.organisation}-$it", locality = notaryName.locality, country = notaryName.country),
configOverrides = notaryConfig(nodeAddresses[it], nodeAddresses[0]) + mapOf(
"database" to mapOf(
"serverNameTablePrefix" to "${notaryName.organisation}$it".replace(Regex("[^0-9A-Za-z]+"), "")
)
)
)
}
return remainingNodesFutures.transpose().flatMap { remainingNodes ->
masterNodeFuture.map { masterNode -> listOf(masterNode) + remainingNodes }
}
}
protected fun baseDirectory(legalName: CordaX500Name) = tempFolder.root.toPath() / legalName.organisation.replace(WHITESPACE, "")
private fun startNodeInternal(legalName: CordaX500Name,
platformVersion: Int,
rpcUsers: List<User>,
configOverrides: Map<String, Any>,
noNetworkMap: Boolean = false): StartedNode<Node> {
val baseDirectory = baseDirectory(legalName).createDirectories()
val localPort = getFreeLocalPorts("localhost", 2)
val p2pAddress = configOverrides["p2pAddress"] ?: localPort[0].toString()
val config = ConfigHelper.loadConfig(
baseDirectory = baseDirectory,
allowMissingConfig = true,
configOverrides = configOf(
"myLegalName" to legalName.toString(),
"p2pAddress" to p2pAddress,
"rpcAddress" to localPort[1].toString(),
"rpcUsers" to rpcUsers.map { it.toMap() },
"noNetworkMap" to noNetworkMap
) + configOverrides
)
val parsedConfig = config.parseAs<FullNodeConfiguration>()
val node = Node(
parsedConfig,
MOCK_VERSION_INFO.copy(platformVersion = platformVersion),
initialiseSerialization = false,
cordappLoader = CordappLoader.createDefaultWithTestPackages(parsedConfig, cordappPackages)).start()
nodes += node
thread(name = legalName.organisation) {
node.internals.run()
}
return node
}
}