diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt index f438548171..71a6c5cc11 100644 --- a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/driver/DriverTests.kt @@ -125,22 +125,22 @@ class DriverTests { } @Test - fun `driver rejects multiple nodes with the same name`() { - - driver(DriverParameters(startNodesInProcess = true)) { - - assertThatThrownBy { listOf(newNode(DUMMY_BANK_A_NAME)(), newNode(DUMMY_BANK_B_NAME)(), newNode(DUMMY_BANK_A_NAME)()).transpose().getOrThrow() }.isInstanceOf(IllegalArgumentException::class.java) + fun `driver rejects multiple nodes with the same name parallel`() { + driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { + val nodes = listOf(newNode(DUMMY_BANK_A_NAME), newNode(DUMMY_BANK_B_NAME), newNode(DUMMY_BANK_A_NAME)) + assertThatIllegalArgumentException().isThrownBy { + nodes.parallelStream().map { it.invoke() }.toList().transpose().getOrThrow() + } } } @Test - fun `driver rejects multiple nodes with the same name parallel`() { - - driver(DriverParameters(startNodesInProcess = true)) { - - val nodes = listOf(newNode(DUMMY_BANK_A_NAME), newNode(DUMMY_BANK_B_NAME), newNode(DUMMY_BANK_A_NAME)) - - assertThatThrownBy { nodes.parallelStream().map { it.invoke() }.toList().transpose().getOrThrow() }.isInstanceOf(IllegalArgumentException::class.java) + fun `driver rejects multiple nodes with the same organisation name`() { + driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) { + newNode(CordaX500Name(commonName = "Notary", organisation = "R3CEV", locality = "New York", country = "US"))().getOrThrow() + assertThatIllegalArgumentException().isThrownBy { + newNode(CordaX500Name(commonName = "Regulator", organisation = "R3CEV", locality = "New York", country = "US"))().getOrThrow() + } } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index e255cdd499..ee65455c12 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -917,6 +917,71 @@ class DriverDSLImpl( } } +/** + * Keeps track of how many nodes each node sees and gates nodes from completing their startNode [CordaFuture] until all + * current nodes see everyone. + */ +private class NetworkVisibilityController { + private val nodeVisibilityHandles = ThreadBox(HashMap()) + + fun register(name: CordaX500Name): VisibilityHandle { + val handle = VisibilityHandle() + nodeVisibilityHandles.locked { + require(name.organisation !in keys) { + "Node with organisation name ${name.organisation} is already started or starting" + } + put(name.organisation, handle) + } + return handle + } + + private fun checkIfAllVisible() { + nodeVisibilityHandles.locked { + val minView = values.stream().mapToInt { it.visibleNodeCount }.min().orElse(0) + if (minView >= size) { + values.forEach { it.future.set(Unit) } + } + } + } + + inner class VisibilityHandle : AutoCloseable { + internal val future = openFuture() + internal var visibleNodeCount = 0 + private var subscription: Subscription? = null + + fun listen(rpc: CordaRPCOps): CordaFuture { + check(subscription == null) + val (snapshot, updates) = rpc.networkMapFeed() + visibleNodeCount = snapshot.size + checkIfAllVisible() + subscription = updates.subscribe { + when (it) { + is NetworkMapCache.MapChange.Added -> { + visibleNodeCount++ + checkIfAllVisible() + } + is NetworkMapCache.MapChange.Removed -> { + visibleNodeCount-- + checkIfAllVisible() + } + is NetworkMapCache.MapChange.Modified -> { + // Nothing to do here but better being exhaustive. + } + } + } + return future + } + + override fun close() { + subscription?.unsubscribe() + nodeVisibilityHandles.locked { + values -= this@VisibilityHandle + checkIfAllVisible() + } + } + } +} + interface InternalDriverDSL : DriverDSL, CordformContext { private companion object { private val DEFAULT_POLL_INTERVAL = 500.millis