CLI: Fix prompting for yes/no to handle console returning null (#23320)

Console.readText may return null in certain cases. This commit fixes a
bug in Terminal.promptYesNo which assumed a non-null return value.  It
also adds a test for this, and modifies mock terminal to be able to
handle null input values.
This commit is contained in:
Ryan Ernst 2017-02-24 20:20:17 -08:00 committed by GitHub
parent 37452dbe23
commit 48548f6c3d
3 changed files with 25 additions and 10 deletions

View File

@ -100,10 +100,11 @@ public abstract class Terminal {
public final boolean promptYesNo(String prompt, boolean defaultYes) {
String answerPrompt = defaultYes ? " [Y/n]" : " [y/N]";
while (true) {
String answer = readText(prompt + answerPrompt).toLowerCase(Locale.ROOT);
if (answer.isEmpty()) {
String answer = readText(prompt + answerPrompt);
if (answer == null || answer.isEmpty()) {
return defaultYes;
}
answer = answer.toLowerCase(Locale.ROOT);
boolean answerYes = answer.equals("y");
if (answerYes == false && answer.equals("n") == false) {
println("Did not understand answer '" + answer + "'");

View File

@ -52,6 +52,8 @@ public class TerminalTests extends ESTestCase {
assertTrue(terminal.promptYesNo("Answer?", true));
terminal.addTextInput("");
assertFalse(terminal.promptYesNo("Answer?", false));
terminal.addTextInput(null);
assertFalse(terminal.promptYesNo("Answer?", false));
}
public void testPromptYesNoReprompt() throws Exception {

View File

@ -25,7 +25,9 @@ import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
/**
* A terminal for tests which captures all output, and
@ -35,8 +37,18 @@ public class MockTerminal extends Terminal {
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private final PrintWriter writer = new PrintWriter(new OutputStreamWriter(buffer, StandardCharsets.UTF_8));
private final Deque<String> textInput = new ArrayDeque<>();
private final Deque<String> secretInput = new ArrayDeque<>();
// A deque would be a perfect data structure for the FIFO queue of input values needed here. However,
// to support the valid return value of readText being null (defined by Console), we need to be able
// to store nulls. However, java the java Deque api does not allow nulls because it uses null as
// a special return value from certain methods like peek(). So instead of deque, we use an array list here,
// and keep track of the last position which was read. It means that we will hold onto all input
// setup for the mock terminal during its lifetime, but this is normally a very small amount of data
// so in reality it will not matter.
private final List<String> textInput = new ArrayList<>();
private int textIndex = 0;
private final List<String> secretInput = new ArrayList<>();
private int secretIndex = 0;
public MockTerminal() {
super("\n"); // always *nix newlines for tests
@ -44,18 +56,18 @@ public class MockTerminal extends Terminal {
@Override
public String readText(String prompt) {
if (textInput.isEmpty()) {
if (textIndex >= textInput.size()) {
throw new IllegalStateException("No text input configured for prompt [" + prompt + "]");
}
return textInput.removeFirst();
return textInput.get(textIndex++);
}
@Override
public char[] readSecret(String prompt) {
if (secretInput.isEmpty()) {
if (secretIndex >= secretInput.size()) {
throw new IllegalStateException("No secret input configured for prompt [" + prompt + "]");
}
return secretInput.removeFirst().toCharArray();
return secretInput.get(secretIndex++).toCharArray();
}
@Override
@ -65,12 +77,12 @@ public class MockTerminal extends Terminal {
/** Adds an an input that will be return from {@link #readText(String)}. Values are read in FIFO order. */
public void addTextInput(String input) {
textInput.addLast(input);
textInput.add(input);
}
/** Adds an an input that will be return from {@link #readText(String)}. Values are read in FIFO order. */
public void addSecretInput(String input) {
secretInput.addLast(input);
secretInput.add(input);
}
/** Returns all output written to this terminal. */