CORDA-2175 Fix attachment service tests on Windows (#4168)

* Switch to directory per test and suppress deletion exception on windows
to deal with the fact that NTFS keeps files locked to the running process
once opened.

* Closing files when we're done makes all the file locking issues go away.
This commit is contained in:
Christian Sailer 2018-11-06 11:31:23 +00:00 committed by GitHub
parent 015a36dad6
commit 1f5436dcfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -35,6 +35,7 @@ import net.corda.testing.node.internal.startFlow
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.* import org.junit.*
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.Closeable
import java.io.OutputStream import java.io.OutputStream
import java.net.URI import java.net.URI
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
@ -80,25 +81,30 @@ class NodeAttachmentServiceTest {
@After @After
fun tearDown() { fun tearDown() {
dir.list { subdir ->
subdir.forEach(Path::deleteRecursively)
}
database.close() database.close()
} }
@Test @Test
fun `importing a signed jar saves the signers to the storage`() { fun `importing a signed jar saves the signers to the storage`() {
val jarAndSigner = makeTestSignedContractJar("com.example.MyContract") SelfCleaningDir().use { file ->
val signedJar = jarAndSigner.first val jarAndSigner = makeTestSignedContractJar(file.path, "com.example.MyContract")
val attachmentId = storage.importAttachment(signedJar.inputStream(), "test", null) val signedJar = jarAndSigner.first
assertEquals(listOf(jarAndSigner.second.hash), storage.openAttachment(attachmentId)!!.signers.map { it.hash }) signedJar.inputStream().use { jarStream ->
val attachmentId = storage.importAttachment(jarStream, "test", null)
assertEquals(listOf(jarAndSigner.second.hash), storage.openAttachment(attachmentId)!!.signers.map { it.hash })
}
}
} }
@Test @Test
fun `importing a non-signed jar will save no signers`() { fun `importing a non-signed jar will save no signers`() {
val jarName = makeTestContractJar("com.example.MyContract") SelfCleaningDir().use {
val attachmentId = storage.importAttachment(dir.resolve(jarName).inputStream(), "test", null) val jarName = makeTestContractJar(it.path, "com.example.MyContract")
assertEquals(0, storage.openAttachment(attachmentId)!!.signers.size) it.path.resolve(jarName).inputStream().use { jarStream ->
val attachmentId = storage.importAttachment(jarStream, "test", null)
assertEquals(0, storage.openAttachment(attachmentId)!!.signers.size)
}
}
} }
@Test @Test
@ -127,25 +133,27 @@ class NodeAttachmentServiceTest {
@Test @Test
fun `insert contract attachment as an untrusted uploader and then as trusted CorDapp uploader`() { fun `insert contract attachment as an untrusted uploader and then as trusted CorDapp uploader`() {
val contractJarName = makeTestContractJar("com.example.MyContract") SelfCleaningDir().use { file ->
val testJar = dir.resolve(contractJarName) val contractJarName = makeTestContractJar(file.path, "com.example.MyContract")
val expectedHash = testJar.readAll().sha256() val testJar = file.path.resolve(contractJarName)
val expectedHash = testJar.readAll().sha256()
// PRIVILEGED_UPLOADERS = listOf(DEPLOYED_CORDAPP_UPLOADER, RPC_UPLOADER, P2P_UPLOADER, UNKNOWN_UPLOADER) // PRIVILEGED_UPLOADERS = listOf(DEPLOYED_CORDAPP_UPLOADER, RPC_UPLOADER, P2P_UPLOADER, UNKNOWN_UPLOADER)
// TRUSTED_UPLOADERS = listOf(DEPLOYED_CORDAPP_UPLOADER, RPC_UPLOADER) // TRUSTED_UPLOADERS = listOf(DEPLOYED_CORDAPP_UPLOADER, RPC_UPLOADER)
database.transaction { database.transaction {
val id = testJar.read { storage.privilegedImportOrGetAttachment(it, P2P_UPLOADER, null) } val id = testJar.read { storage.privilegedImportOrGetAttachment(it, P2P_UPLOADER, null) }
assertEquals(expectedHash, id) assertEquals(expectedHash, id)
val attachment1 = storage.openAttachment(expectedHash) val attachment1 = storage.openAttachment(expectedHash)
val id2 = testJar.read { storage.privilegedImportOrGetAttachment(it, DEPLOYED_CORDAPP_UPLOADER, null) } val id2 = testJar.read { storage.privilegedImportOrGetAttachment(it, DEPLOYED_CORDAPP_UPLOADER, null) }
assertEquals(expectedHash, id2) assertEquals(expectedHash, id2)
val attachment2 = storage.openAttachment(expectedHash) val attachment2 = storage.openAttachment(expectedHash)
assertNotEquals(attachment1, attachment2) assertNotEquals(attachment1, attachment2)
assertEquals(P2P_UPLOADER, (attachment1 as ContractAttachment).uploader) assertEquals(P2P_UPLOADER, (attachment1 as ContractAttachment).uploader)
assertEquals(DEPLOYED_CORDAPP_UPLOADER, (attachment2 as ContractAttachment).uploader) assertEquals(DEPLOYED_CORDAPP_UPLOADER, (attachment2 as ContractAttachment).uploader)
}
} }
} }
@ -350,20 +358,17 @@ class NodeAttachmentServiceTest {
return Pair(file, file.readAll().sha256()) return Pair(file, file.readAll().sha256())
} }
/**
* Class to create an automatically delete a temporary directory.
*/
class SelfCleaningDir : Closeable {
val path: Path = Files.createTempDirectory(NodeAttachmentServiceTest::class.simpleName)
override fun close() {
path.deleteRecursively()
}
}
companion object { companion object {
private val dir = Files.createTempDirectory(NodeAttachmentServiceTest::class.simpleName)
@BeforeClass
@JvmStatic
fun beforeClass() {
}
@AfterClass
@JvmStatic
fun afterClass() {
dir.deleteRecursively()
}
private fun makeTestJar(output: OutputStream, extraEntries: List<Pair<String, String>> = emptyList()) { private fun makeTestJar(output: OutputStream, extraEntries: List<Pair<String, String>> = emptyList()) {
output.use { output.use {
val jar = JarOutputStream(it) val jar = JarOutputStream(it)
@ -372,33 +377,33 @@ class NodeAttachmentServiceTest {
jar.closeEntry() jar.closeEntry()
jar.putNextEntry(JarEntry("test2.txt")) jar.putNextEntry(JarEntry("test2.txt"))
jar.write("Some more useful content".toByteArray()) jar.write("Some more useful content".toByteArray())
extraEntries.forEach { extraEntries.forEach { entry ->
jar.putNextEntry(JarEntry(it.first)) jar.putNextEntry(JarEntry(entry.first))
jar.write(it.second.toByteArray()) jar.write(entry.second.toByteArray())
} }
jar.closeEntry() jar.closeEntry()
} }
} }
private fun makeTestSignedContractJar(contractName: String): Pair<Path, PublicKey> { private fun makeTestSignedContractJar(workingDir: Path, contractName: String): Pair<Path, PublicKey> {
val alias = "testAlias" val alias = "testAlias"
val pwd = "testPassword" val pwd = "testPassword"
dir.generateKey(alias, pwd, ALICE_NAME.toString()) workingDir.generateKey(alias, pwd, ALICE_NAME.toString())
val jarName = makeTestContractJar(contractName) val jarName = makeTestContractJar(workingDir, contractName)
val signer = dir.signJar(jarName, alias, pwd) val signer = workingDir.signJar(jarName, alias, pwd)
return dir.resolve(jarName) to signer return workingDir.resolve(jarName) to signer
} }
private fun makeTestContractJar(contractName: String): String { private fun makeTestContractJar(workingDir: Path, contractName: String): String {
val packages = contractName.split(".") val packages = contractName.split(".")
val jarName = "testattachment.jar" val jarName = "testattachment.jar"
val className = packages.last() val className = packages.last()
createTestClass(className, packages.subList(0, packages.size - 1)) createTestClass(workingDir, className, packages.subList(0, packages.size - 1))
dir.createJar(jarName, "${contractName.replace(".", "/")}.class") workingDir.createJar(jarName, "${contractName.replace(".", "/")}.class")
return jarName return jarName
} }
private fun createTestClass(className: String, packages: List<String>): Path { private fun createTestClass(workingDir: Path, className: String, packages: List<String>): Path {
val newClass = """package ${packages.joinToString(".")}; val newClass = """package ${packages.joinToString(".")};
import net.corda.core.contracts.*; import net.corda.core.contracts.*;
import net.corda.core.transactions.*; import net.corda.core.transactions.*;
@ -410,15 +415,15 @@ class NodeAttachmentServiceTest {
} }
""".trimIndent() """.trimIndent()
val compiler = ToolProvider.getSystemJavaCompiler() val compiler = ToolProvider.getSystemJavaCompiler()
val source = object : SimpleJavaFileObject(URI.create("string:///${packages.joinToString("/")}/${className}.java"), JavaFileObject.Kind.SOURCE) { val source = object : SimpleJavaFileObject(URI.create("string:///${packages.joinToString("/")}/$className.java"), JavaFileObject.Kind.SOURCE) {
override fun getCharContent(ignoreEncodingErrors: Boolean): CharSequence { override fun getCharContent(ignoreEncodingErrors: Boolean): CharSequence {
return newClass return newClass
} }
} }
val fileManager = compiler.getStandardFileManager(null, null, null) val fileManager = compiler.getStandardFileManager(null, null, null)
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, listOf(dir.toFile())) fileManager.setLocation(StandardLocation.CLASS_OUTPUT, listOf(workingDir.toFile()))
val compile = compiler.getTask(System.out.writer(), fileManager, null, null, null, listOf(source)).call() compiler.getTask(System.out.writer(), fileManager, null, null, null, listOf(source)).call()
return Paths.get(fileManager.list(StandardLocation.CLASS_OUTPUT, "", setOf(JavaFileObject.Kind.CLASS), true).single().name) return Paths.get(fileManager.list(StandardLocation.CLASS_OUTPUT, "", setOf(JavaFileObject.Kind.CLASS), true).single().name)
} }
} }