EG-464 Corda returns incorrect exit code in case if node is started with unknown/missing option (#6010)

* EG-464

Corda returns incorrect exit code in case if node is started with unknown/missing option

* fixed empty else block

* We should not use the parent's exception handler as it will quit and we have our own one

* SampleCordaCliWrapper implemented and tests to verify error handling.

* addressing code review comments
This commit is contained in:
Stefano Franz
2020-03-05 15:38:56 +00:00
committed by GitHub
parent 9a406839fa
commit c3a59f5293
5 changed files with 149 additions and 13 deletions

View File

@ -0,0 +1,50 @@
package net.corda.testing.node.internal
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.matchesPattern
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.stream.Collectors
@RunWith(value = Parameterized::class)
class CordaCliWrapperErrorHandlingTests(val arguments: List<String>, val outputRegexPattern: String) {
companion object {
val className = "net.corda.testing.node.internal.SampleCordaCliWrapper"
private val stackTraceRegex = "^.+Exception[^\\n]++(\\s+at .++)+[\\s\\S]*"
private val exceptionWithoutStackTraceRegex ="${className}(\\s+.+)"
private val emptyStringRegex = "^$"
@JvmStatic
@Parameterized.Parameters
fun data() = listOf(
arrayOf(listOf("--throw-exception", "--verbose"), stackTraceRegex),
arrayOf(listOf("--throw-exception"), exceptionWithoutStackTraceRegex),
arrayOf(listOf("--sample-command"), emptyStringRegex)
)
}
@Test(timeout=300_000)
fun `Run CordaCliWrapper sample app with arguments and check error output matches regExp`() {
val process = ProcessUtilities.startJavaProcess(
className = className,
arguments = arguments,
inheritIO = false)
process.waitFor()
val processErrorOutput = BufferedReader(
InputStreamReader(process.errorStream))
.lines()
.collect(Collectors.joining("\n"))
.toString()
assertThat(processErrorOutput, matchesPattern(outputRegexPattern))
}
}

View File

@ -0,0 +1,36 @@
package net.corda.testing.node.internal
import net.corda.cliutils.ExitCodes
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import kotlin.test.assertEquals
@RunWith(value = Parameterized::class)
class RunCordaNodeReturnCodeTests(val argument: String, val exitCode: Int){
companion object {
@JvmStatic
@Parameterized.Parameters
fun data() = listOf(
arrayOf("--nonExistingOption", ExitCodes.FAILURE),
arrayOf("--help", ExitCodes.SUCCESS),
arrayOf("validate-configuration", ExitCodes.FAILURE),//Should fail as there is no node.conf
arrayOf("initial-registration", ExitCodes.FAILURE) //Missing required option
)
}
@Test(timeout=300_000)
fun runCordaWithArgumentAndAssertExitCode() {
val process = ProcessUtilities.startJavaProcess(
className = "net.corda.node.Corda",
arguments = listOf(argument)
)
process.waitFor()
assertEquals(exitCode, process.exitValue())
}
}

View File

@ -0,0 +1,38 @@
package net.corda.testing.node.internal
import net.corda.cliutils.CordaCliWrapper
import net.corda.cliutils.ExitCodes
import net.corda.cliutils.start
import picocli.CommandLine
class SampleCordaCliWrapperException(message: String) : Exception(message)
class SampleCordaCliWrapper: CordaCliWrapper("sampleCliWrapper", "Sample corda cliWrapper app") {
companion object {
@JvmStatic
fun main(args: Array<String>) {
SampleCordaCliWrapper().start(args)
}
}
@CommandLine.Option(names = ["--sample-command"],
description = [ "Sample command. Prints a message to the console."])
var sampleCommand: Boolean? = null
@CommandLine.Option(names = ["--throw-exception"], description = ["Specify this to throw an exception"])
var throwException: Boolean? = null
override fun runProgram(): Int {
if (throwException!=null) {
throw SampleCordaCliWrapperException("net.corda.testing.node.internal.SampleCordaCliWrapper test exception")
}
if (sampleCommand!=null) {
System.out.println("Sample command invoked.")
}
return ExitCodes.SUCCESS
}
}

View File

@ -37,7 +37,8 @@ object ProcessUtilities {
extraJvmArguments: List<String> = emptyList(),
maximumHeapSize: String? = null,
identifier: String = "",
environmentVariables: Map<String,String> = emptyMap()
environmentVariables: Map<String,String> = emptyMap(),
inheritIO: Boolean = true
): Process {
val command = mutableListOf<String>().apply {
add(javaPath)
@ -49,7 +50,7 @@ object ProcessUtilities {
addAll(arguments)
}
return ProcessBuilder(command).apply {
inheritIO()
if (inheritIO) inheritIO()
environment().putAll(environmentVariables)
environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator)
if (workingDirectory != null) {