Skip to content

Commit 852dd8b

Browse files
committed
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.
1 parent c7fdaf6 commit 852dd8b

File tree

3 files changed

+25
-10
lines changed

3 files changed

+25
-10
lines changed

core/src/main/java/org/elasticsearch/cli/Terminal.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,11 @@ public final void print(Verbosity verbosity, String msg) {
100100
public final boolean promptYesNo(String prompt, boolean defaultYes) {
101101
String answerPrompt = defaultYes ? " [Y/n]" : " [y/N]";
102102
while (true) {
103-
String answer = readText(prompt + answerPrompt).toLowerCase(Locale.ROOT);
104-
if (answer.isEmpty()) {
103+
String answer = readText(prompt + answerPrompt);
104+
if (answer == null || answer.isEmpty()) {
105105
return defaultYes;
106106
}
107+
answer = answer.toLowerCase(Locale.ROOT);
107108
boolean answerYes = answer.equals("y");
108109
if (answerYes == false && answer.equals("n") == false) {
109110
println("Did not understand answer '" + answer + "'");

core/src/test/java/org/elasticsearch/cli/TerminalTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public void testPromptYesNoDefault() throws Exception {
5252
assertTrue(terminal.promptYesNo("Answer?", true));
5353
terminal.addTextInput("");
5454
assertFalse(terminal.promptYesNo("Answer?", false));
55+
terminal.addTextInput(null);
56+
assertFalse(terminal.promptYesNo("Answer?", false));
5557
}
5658

5759
public void testPromptYesNoReprompt() throws Exception {

test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import java.io.UnsupportedEncodingException;
2626
import java.nio.charset.StandardCharsets;
2727
import java.util.ArrayDeque;
28+
import java.util.ArrayList;
2829
import java.util.Deque;
30+
import java.util.List;
2931

3032
/**
3133
* A terminal for tests which captures all output, and
@@ -35,27 +37,37 @@ public class MockTerminal extends Terminal {
3537

3638
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
3739
private final PrintWriter writer = new PrintWriter(new OutputStreamWriter(buffer, StandardCharsets.UTF_8));
38-
private final Deque<String> textInput = new ArrayDeque<>();
39-
private final Deque<String> secretInput = new ArrayDeque<>();
40+
41+
// A deque would be a perfect data structure for the FIFO queue of input values needed here. However,
42+
// to support the valid return value of readText being null (defined by Console), we need to be able
43+
// to store nulls. However, java the java Deque api does not allow nulls because it uses null as
44+
// a special return value from certain methods like peek(). So instead of deque, we use an array list here,
45+
// and keep track of the last position which was read. It means that we will hold onto all input
46+
// setup for the mock terminal during its lifetime, but this is normally a very small amount of data
47+
// so in reality it will not matter.
48+
private final List<String> textInput = new ArrayList<>();
49+
private int textIndex = 0;
50+
private final List<String> secretInput = new ArrayList<>();
51+
private int secretIndex = 0;
4052

4153
public MockTerminal() {
4254
super("\n"); // always *nix newlines for tests
4355
}
4456

4557
@Override
4658
public String readText(String prompt) {
47-
if (textInput.isEmpty()) {
59+
if (textIndex >= textInput.size()) {
4860
throw new IllegalStateException("No text input configured for prompt [" + prompt + "]");
4961
}
50-
return textInput.removeFirst();
62+
return textInput.get(textIndex++);
5163
}
5264

5365
@Override
5466
public char[] readSecret(String prompt) {
55-
if (secretInput.isEmpty()) {
67+
if (secretIndex >= secretInput.size()) {
5668
throw new IllegalStateException("No secret input configured for prompt [" + prompt + "]");
5769
}
58-
return secretInput.removeFirst().toCharArray();
70+
return secretInput.get(secretIndex++).toCharArray();
5971
}
6072

6173
@Override
@@ -65,12 +77,12 @@ public PrintWriter getWriter() {
6577

6678
/** Adds an an input that will be return from {@link #readText(String)}. Values are read in FIFO order. */
6779
public void addTextInput(String input) {
68-
textInput.addLast(input);
80+
textInput.add(input);
6981
}
7082

7183
/** Adds an an input that will be return from {@link #readText(String)}. Values are read in FIFO order. */
7284
public void addSecretInput(String input) {
73-
secretInput.addLast(input);
85+
secretInput.add(input);
7486
}
7587

7688
/** Returns all output written to this terminal. */

0 commit comments

Comments
 (0)