diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index d3fda9b9c7..5663251bc2 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -28,7 +28,7 @@ dependencies { compile "net.corda.plugins:cordform-common:$gradle_plugins_version" // Integration test helpers - integrationTestCompile "org.assertj:assertj-core:${assertj_version}" + testCompile "org.assertj:assertj-core:$assertj_version" integrationTestCompile "junit:junit:$junit_version" // Jetty dependencies for NetworkMapClient test. diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index 2ed3d9b65d..24b449e441 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -9,6 +9,7 @@ import net.corda.core.crypto.random63BitValue import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate +import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectory import net.corda.core.internal.uncheckedCast @@ -124,14 +125,14 @@ data class MockNodeArgs( * By default a single notary node is automatically started, which forms part of the network parameters for all the nodes. * This node is available by calling [defaultNotaryNode]. */ -class MockNetwork(private val cordappPackages: List, - defaultParameters: MockNetworkParameters = MockNetworkParameters(), - private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, - private val threadPerNode: Boolean = defaultParameters.threadPerNode, - servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, - private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, - initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, - private val notarySpecs: List = defaultParameters.notarySpecs) { +open class MockNetwork(private val cordappPackages: List, + defaultParameters: MockNetworkParameters = MockNetworkParameters(), + private val networkSendManuallyPumped: Boolean = defaultParameters.networkSendManuallyPumped, + private val threadPerNode: Boolean = defaultParameters.threadPerNode, + servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy, + private val defaultFactory: (MockNodeArgs) -> MockNode = defaultParameters.defaultFactory, + initialiseSerialization: Boolean = defaultParameters.initialiseSerialization, + private val notarySpecs: List = defaultParameters.notarySpecs) { /** Helper constructor for creating a [MockNetwork] with custom parameters from Java. */ @JvmOverloads constructor(cordappPackages: List, parameters: MockNetworkParameters = MockNetworkParameters()) : this(cordappPackages, defaultParameters = parameters) @@ -140,7 +141,7 @@ class MockNetwork(private val cordappPackages: List, // Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS. // This SFTP support loads BouncyCastle, which we want to avoid. // Please see https://issues.apache.org/jira/browse/SSHD-736 - it's easier then to create our own fork of SSHD - SecurityUtils.setAPrioriDisabledProvider("BC", true) + SecurityUtils.setAPrioriDisabledProvider("BC", true) // XXX: Why isn't this static? } var nextNodeId = 0 @@ -221,11 +222,17 @@ class MockNetwork(private val cordappPackages: List, } init { - filesystem.getPath("/nodes").createDirectory() - val notaryInfos = generateNotaryIdentities() - // The network parameters must be serialised before starting any of the nodes - networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos)) - notaryNodes = createNotaries() + try { + filesystem.getPath("/nodes").createDirectory() + val notaryInfos = generateNotaryIdentities() + // The network parameters must be serialised before starting any of the nodes + networkParameters = NetworkParametersCopier(testNetworkParameters(notaryInfos)) + @Suppress("LeakingThis") + notaryNodes = createNotaries() + } catch (t: Throwable) { + stopNodes() + throw t + } } private fun generateNotaryIdentities(): List { @@ -238,7 +245,8 @@ class MockNetwork(private val cordappPackages: List, } } - private fun createNotaries(): List> { + @VisibleForTesting + internal open fun createNotaries(): List> { return notarySpecs.map { spec -> createNode(MockNodeParameters(legalName = spec.name, configOverrides = { doReturn(NotaryConfig(spec.validating)).whenever(it).notary @@ -455,8 +463,11 @@ class MockNetwork(private val cordappPackages: List, } fun stopNodes() { - nodes.forEach { it.started?.dispose() } - serializationEnv.unset() + try { + nodes.forEach { it.started?.dispose() } + } finally { + serializationEnv.unset() // Must execute even if other parts of this method fail. + } messagingNetwork.stop() } diff --git a/testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt b/testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt new file mode 100644 index 0000000000..10d9358243 --- /dev/null +++ b/testing/node-driver/src/test/kotlin/net/corda/testing/node/MockNetworkTests.kt @@ -0,0 +1,18 @@ +package net.corda.testing.node + +import net.corda.core.serialization.internal.effectiveSerializationEnv +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Test + +class MockNetworkTests { + @Test + fun `does not leak serialization env if init fails`() { + val e = Exception("didn't work") + assertThatThrownBy { + object : MockNetwork(emptyList(), initialiseSerialization = true) { + override fun createNotaries() = throw e + } + }.isSameAs(e) + assertThatThrownBy { effectiveSerializationEnv }.isInstanceOf(IllegalStateException::class.java) + } +}