()
// Context for tokenized services in checkpoints
- private lateinit var serializationContext: SerializeAsTokenContext
+ private val serializationContext by lazy {
+ SerializeAsTokenContext(tokenizableServices, quasarKryoPool, serviceHub)
+ }
/** Returns a list of all state machines executing the given flow logic at the top level (subflows do not count) */
fun , T> findStateMachines(flowClass: Class
): List>> {
@@ -170,8 +174,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
*/
val changes: Observable = mutex.content.changesPublisher.wrapWithDatabaseTransaction()
- fun start(tokenizableServices: List) {
- serializationContext = SerializeAsTokenContext(tokenizableServices, quasarKryoPool, serviceHub)
+ fun start() {
restoreFibersFromCheckpoints()
listenToLedgerTransactions()
serviceHub.networkMapCache.mapServiceRegistered.then(executor) { resumeRestoredFibers() }
@@ -348,7 +351,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
val initiatedFlowFactory = serviceHub.getFlowFactory(sessionInit.initiatingFlowClass)
if (initiatedFlowFactory == null) {
logger.warn("${sessionInit.initiatingFlowClass} has not been registered: $sessionInit")
- sendSessionReject("${sessionInit.initiatingFlowClass.name} has not been registered with a service flow")
+ sendSessionReject("${sessionInit.initiatingFlowClass.name} has not been registered")
return
}
diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappScanningTest.kt b/node/src/smoke-test/kotlin/net/corda/node/CordappScanningTest.kt
similarity index 62%
rename from node/src/integration-test/kotlin/net/corda/node/CordappScanningTest.kt
rename to node/src/smoke-test/kotlin/net/corda/node/CordappScanningTest.kt
index ba98a0adbc..41f95a52e1 100644
--- a/node/src/integration-test/kotlin/net/corda/node/CordappScanningTest.kt
+++ b/node/src/smoke-test/kotlin/net/corda/node/CordappScanningTest.kt
@@ -18,40 +18,57 @@ import net.corda.core.utilities.unwrap
import net.corda.node.driver.driver
import net.corda.node.services.startFlowPermission
import net.corda.nodeapi.User
+import net.corda.smoketesting.NodeConfig
+import net.corda.smoketesting.NodeProcess
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.nio.file.Paths
+import java.util.concurrent.atomic.AtomicInteger
class CordappScanningTest {
+ private companion object {
+ val user = User("user1", "test", permissions = setOf("ALL"))
+ val port = AtomicInteger(15100)
+ }
+
+ private val factory = NodeProcess.Factory()
+
+ private val aliceConfig = NodeConfig(
+ party = ALICE,
+ p2pPort = port.andIncrement,
+ rpcPort = port.andIncrement,
+ webPort = port.andIncrement,
+ extraServices = emptyList(),
+ users = listOf(user)
+ )
+
@Test
fun `CorDapp jar in plugins directory is scanned`() {
- // If the CorDapp jar does't exist then run the integrationTestClasses gradle task
+ // If the CorDapp jar does't exist then run the smokeTestClasses gradle task
val cordappJar = Paths.get(javaClass.getResource("/trader-demo.jar").toURI())
- driver {
- val pluginsDir = (baseDirectory(ALICE.name) / "plugins").createDirectories()
- cordappJar.copyToDirectory(pluginsDir)
+ val pluginsDir = (factory.baseDirectory(aliceConfig) / "plugins").createDirectories()
+ cordappJar.copyToDirectory(pluginsDir)
- val user = User("u", "p", emptySet())
- val alice = startNode(ALICE.name, rpcUsers = listOf(user)).getOrThrow()
- val rpc = alice.rpcClientToNode().start(user.username, user.password)
- // If the CorDapp wasn't scanned then SellerFlow won't have been picked up as an RPC flow
- assertThat(rpc.proxy.registeredFlows()).contains("net.corda.traderdemo.flow.SellerFlow")
+ factory.create(aliceConfig).use {
+ it.connect().use {
+ // If the CorDapp wasn't scanned then SellerFlow won't have been picked up as an RPC flow
+ assertThat(it.proxy.registeredFlows()).contains("net.corda.traderdemo.flow.SellerFlow")
+ }
}
}
@Test
fun `empty plugins directory`() {
- driver {
- val baseDirectory = baseDirectory(ALICE.name)
- (baseDirectory / "plugins").createDirectories()
- startNode(ALICE.name).getOrThrow()
- }
+ (factory.baseDirectory(aliceConfig) / "plugins").createDirectories()
+ factory.create(aliceConfig).close()
}
@Test
fun `sub-classed initiated flow pointing to the same initiating flow as its super-class`() {
val user = User("u", "p", setOf(startFlowPermission()))
- driver(systemProperties = mapOf("net.corda.node.cordapp.scan.package" to "net.corda.node")) {
+ // We don't use the factory for this test because we want the node to pick up the annotated flows below. The driver
+ // will do just that.
+ driver {
val (alice, bob) = Futures.allAsList(
startNode(ALICE.name, rpcUsers = listOf(user)),
startNode(BOB.name)).getOrThrow()
diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt
index 5f2cf0cb23..71754dd4d5 100644
--- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt
+++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt
@@ -99,7 +99,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
smmHasRemovedAllFlows.countDown()
}
}
- mockSMM.start(listOf(services, scheduler))
+ mockSMM.start()
services.smm = mockSMM
scheduler.start()
}
@@ -124,7 +124,9 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
override fun isRelevant(ourKeys: Set): Boolean = true
- override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? = ScheduledActivity(flowLogicRef, instant)
+ override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
+ return ScheduledActivity(flowLogicRef, instant)
+ }
override val contract: Contract
get() = throw UnsupportedOperationException()
diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt
index cecc34d12e..54412d9e67 100644
--- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt
+++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt
@@ -625,7 +625,7 @@ class FlowFrameworkTests {
@Test
fun `unsupported new flow version`() {
- node2.registerFlowFactory(
+ node2.internalRegisterFlowFactory(
UpgradedFlow::class.java,
InitiatedFlowFactory.CorDapp(version = 1, factory = ::DoubleInlinedSubFlow),
DoubleInlinedSubFlow::class.java,
@@ -675,7 +675,7 @@ class FlowFrameworkTests {
initiatingFlowClass: KClass>,
noinline flowFactory: (Party) -> P): ListenableFuture
{
- val observable = registerFlowFactory(initiatingFlowClass.java, object : InitiatedFlowFactory
{
+ val observable = internalRegisterFlowFactory(initiatingFlowClass.java, object : InitiatedFlowFactory
{
override fun createFlow(platformVersion: Int, otherParty: Party, sessionInit: SessionInit): P {
return flowFactory(otherParty)
}
diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt
index 9639272865..337311b1dc 100644
--- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt
+++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt
@@ -33,11 +33,7 @@ class IRSDemoTest : IntegrationTestCategory {
@Test
fun `runs IRS demo`() {
- driver(
- useTestClock = true,
- isDebug = true,
- systemProperties = mapOf("net.corda.node.cordapp.scan.package" to "net.corda.irs"))
- {
+ driver(useTestClock = true, isDebug = true) {
val (controller, nodeA, nodeB) = Futures.allAsList(
startNode(DUMMY_NOTARY.name, setOf(ServiceInfo(SimpleNotaryService.type), ServiceInfo(NodeInterestRates.Oracle.type))),
startNode(DUMMY_BANK_A.name, rpcUsers = listOf(rpcUser)),
diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt
index a2ef62ec66..5c07e8852b 100644
--- a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt
+++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt
@@ -55,7 +55,8 @@ object NodeInterestRates {
@Suspendable
override fun call() {
val request = receive(otherParty).unwrap { it }
- send(otherParty, serviceHub.cordaService(Oracle::class.java).sign(request.ftx))
+ val oracle = serviceHub.cordaService(Oracle::class.java)
+ send(otherParty, oracle.sign(request.ftx))
}
}
@@ -70,7 +71,8 @@ object NodeInterestRates {
override fun call(): Unit {
val request = receive(otherParty).unwrap { it }
progressTracker.currentStep = RECEIVED
- val answers = serviceHub.cordaService(Oracle::class.java).query(request.queries, request.deadline)
+ val oracle = serviceHub.cordaService(Oracle::class.java)
+ val answers = oracle.query(request.queries, request.deadline)
progressTracker.currentStep = SENDING
send(otherParty, answers)
}
diff --git a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt
index 69283f7ce6..f5f7bf8b36 100644
--- a/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt
+++ b/samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt
@@ -32,7 +32,7 @@ class SimmValuationTest : IntegrationTestCategory {
@Test
fun `runs SIMM valuation demo`() {
- driver(isDebug = true, systemProperties = mapOf("net.corda.node.cordapp.scan.package" to "net.corda.vega")) {
+ driver(isDebug = true) {
startNode(DUMMY_NOTARY.name, setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow()
val (nodeA, nodeB) = Futures.allAsList(startNode(nodeALegalName), startNode(nodeBLegalName)).getOrThrow()
val (nodeAApi, nodeBApi) = Futures.allAsList(startWebserver(nodeA), startWebserver(nodeB))
diff --git a/settings.gradle b/settings.gradle
index c433b9b935..1e35747a78 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -20,6 +20,7 @@ include 'experimental:sandbox'
include 'experimental:quasar-hook'
include 'verifier'
include 'test-utils'
+include 'smoke-test-utils'
include 'tools:explorer'
include 'tools:explorer:capsule'
include 'tools:demobench'
diff --git a/smoke-test-utils/build.gradle b/smoke-test-utils/build.gradle
new file mode 100644
index 0000000000..dcd94fae66
--- /dev/null
+++ b/smoke-test-utils/build.gradle
@@ -0,0 +1,8 @@
+apply plugin: 'kotlin'
+
+description 'Utilities needed for smoke tests in Corda'
+
+dependencies {
+ // Smoke tests do NOT have any Node code on the classpath!
+ compile project(':client:rpc')
+}
diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/NodeConfig.kt b/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt
similarity index 79%
rename from client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/NodeConfig.kt
rename to smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt
index 75c4074be1..338c88d656 100644
--- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/NodeConfig.kt
+++ b/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt
@@ -1,6 +1,10 @@
-package net.corda.kotlin.rpc
+package net.corda.smoketesting
-import com.typesafe.config.*
+import com.typesafe.config.Config
+import com.typesafe.config.ConfigFactory.empty
+import com.typesafe.config.ConfigRenderOptions
+import com.typesafe.config.ConfigValue
+import com.typesafe.config.ConfigValueFactory
import net.corda.core.crypto.commonName
import net.corda.core.identity.Party
import net.corda.nodeapi.User
@@ -18,13 +22,13 @@ class NodeConfig(
val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false)
}
- val commonName: String = party.name.commonName
+ val commonName: String get() = party.name.commonName
/*
* The configuration object depends upon the networkMap,
* which is mutable.
*/
- fun toFileConfig(): Config = ConfigFactory.empty()
+ fun toFileConfig(): Config = empty()
.withValue("myLegalName", valueFor(party.name.toString()))
.withValue("p2pAddress", addressValueFor(p2pPort))
.withValue("extraAdvertisedServiceIds", valueFor(extraServices))
@@ -42,7 +46,6 @@ class NodeConfig(
private fun valueFor(any: T): ConfigValue? = ConfigValueFactory.fromAnyRef(any)
private fun addressValueFor(port: Int) = valueFor("localhost:$port")
private inline fun optional(path: String, obj: T?, body: (Config, T) -> Config): Config {
- val config = ConfigFactory.empty()
- return if (obj == null) config else body(config, obj).atPath(path)
+ return if (obj == null) empty() else body(empty(), obj).atPath(path)
}
}
diff --git a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/NodeProcess.kt b/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt
similarity index 73%
rename from client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/NodeProcess.kt
rename to smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt
index 3d81f9ad83..c939bd4758 100644
--- a/client/rpc/src/smoke-test/kotlin/net/corda/kotlin/rpc/NodeProcess.kt
+++ b/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt
@@ -1,16 +1,18 @@
-package net.corda.kotlin.rpc
+package net.corda.smoketesting
import com.google.common.net.HostAndPort
import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCConnection
+import net.corda.core.createDirectories
+import net.corda.core.div
import net.corda.core.utilities.loggerFor
-import java.io.File
-import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
+import java.time.Instant
+import java.time.ZoneId.systemDefault
+import java.time.format.DateTimeFormatter
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit.SECONDS
-import kotlin.test.*
class NodeProcess(
val config: NodeConfig,
@@ -21,9 +23,7 @@ class NodeProcess(
private companion object {
val log = loggerFor()
val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java")
- val corda = File(this::class.java.getResource("/corda.jar").toURI())
- val buildDir: Path = Paths.get(System.getProperty("build.dir"))
- val capsuleDir: Path = buildDir.resolve("capsule")
+ val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(systemDefault())
}
fun connect(): CordaRPCConnection {
@@ -40,16 +40,20 @@ class NodeProcess(
}
log.info("Deleting Artemis directories, because they're large!")
- nodeDir.resolve("artemis").toFile().deleteRecursively()
+ (nodeDir / "artemis").toFile().deleteRecursively()
}
- class Factory(val nodesDir: Path) {
+ class Factory(val buildDirectory: Path = Paths.get("build"),
+ val cordaJar: Path = Paths.get(this::class.java.getResource("/corda.jar").toURI())) {
+ val nodesDirectory = buildDirectory / formatter.format(Instant.now())
init {
- assertTrue(nodesDir.toFile().forceDirectory(), "Directory '$nodesDir' does not exist")
+ nodesDirectory.createDirectories()
}
+ fun baseDirectory(config: NodeConfig): Path = nodesDirectory / config.commonName
+
fun create(config: NodeConfig): NodeProcess {
- val nodeDir = Files.createTempDirectory(nodesDir, config.commonName)
+ val nodeDir = baseDirectory(config).createDirectories()
log.info("Node directory: {}", nodeDir)
val confFile = nodeDir.resolve("node.conf").toFile()
@@ -78,7 +82,7 @@ class NodeProcess(
}, 5, 1, SECONDS)
val setupOK = setupExecutor.awaitTermination(120, SECONDS)
- assertTrue(setupOK && process.isAlive, "Failed to create RPC connection")
+ check(setupOK && process.isAlive) { "Failed to create RPC connection" }
} catch (e: Exception) {
process.destroyForcibly()
throw e
@@ -91,17 +95,14 @@ class NodeProcess(
private fun startNode(nodeDir: Path): Process {
val builder = ProcessBuilder()
- .command(javaPath.toString(), "-jar", corda.path)
+ .command(javaPath.toString(), "-jar", cordaJar.toString())
.directory(nodeDir.toFile())
builder.environment().putAll(mapOf(
- "CAPSULE_CACHE_DIR" to capsuleDir.toString()
+ "CAPSULE_CACHE_DIR" to (buildDirectory / "capsule").toString()
))
return builder.start()
}
}
}
-
-private fun File.forceDirectory(): Boolean = this.isDirectory || this.mkdirs()
-