Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit f0914e2

Browse files
author
Aleksi Salmela
committed
Improve the unit tests of terminal io.
1 parent 21c6734 commit f0914e2

2 files changed

Lines changed: 167 additions & 36 deletions

File tree

src/main/java/fi/helsinki/cs/tmc/cli/io/TerminalIo.java

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;
55

6+
import java.io.Console;
67
import java.util.Scanner;
78

89
public class TerminalIo extends Io {
@@ -16,55 +17,44 @@ public void print(String str) {
1617

1718
@Override
1819
public String readLine(String prompt) {
19-
System.out.print(prompt);
20+
print(prompt);
2021

2122
try (Scanner scanner = new Scanner(System.in)) {
2223
return scanner.nextLine();
2324
}
2425
}
2526

26-
@Override
27-
public boolean readConfirmation(String prompt, boolean defaultToYes) {
28-
String yesNo;
29-
String input;
30-
if (defaultToYes) {
31-
yesNo = " [Y/n] ";
32-
} else {
33-
yesNo = " [y/N] ";
34-
}
35-
System.out.print(prompt + yesNo);
36-
try (Scanner scanner = new Scanner(System.in)) {
37-
input = scanner.nextLine().toLowerCase();
38-
}
39-
if (input.isEmpty()) {
40-
return defaultToYes;
41-
} else if (input.charAt(0) == 'y') {
42-
return true;
43-
} else if (input.charAt(0) == 'n') {
44-
return false;
45-
}
46-
return defaultToYes;
47-
}
48-
4927
@Override
5028
public String readPassword(String prompt) {
51-
if (System.console() != null) {
52-
char[] pwd;
29+
Console console = System.console();
30+
if (console != null) {
5331
try {
54-
pwd = System.console().readPassword(prompt);
32+
return new String(console.readPassword(prompt));
5533
} catch (Exception e) {
5634
logger.warn("Password could not be read.", e);
57-
return null;
5835
}
59-
return new String(pwd);
36+
} else {
37+
logger.warn("Failed to read password due to System.console()");
6038
}
61-
62-
// Read the password in cleartext if no console is present (might happen
63-
// in some IDEs?)
64-
logger.warn("Failed to read password");
65-
println("System.console is not present, unable to read password "
66-
+ "securely. Reading password in cleartext.");
39+
println("Unable to read password securely. Reading password in cleartext.");
40+
println("Press Ctrl+C to abort");
6741
return readLine(prompt);
6842
}
6943

44+
@Override
45+
public boolean readConfirmation(String prompt, boolean defaultToYes) {
46+
String yesNo = (defaultToYes) ? " [Y/n] " : " [y/N] ";
47+
String input = readLine(prompt + yesNo).toLowerCase();
48+
49+
switch (input) {
50+
case "y": //fall through
51+
case "yes":
52+
return true;
53+
case "n": //fall through
54+
case "no":
55+
return false;
56+
default:
57+
return defaultToYes;
58+
}
59+
}
7060
}

src/test/java/fi/helsinki/cs/tmc/cli/io/TerminalIoTest.java

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,56 @@
11
package fi.helsinki.cs.tmc.cli.io;
22

3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertThat;
35
import static org.junit.Assert.assertTrue;
6+
import static org.junit.matchers.JUnitMatchers.containsString;
7+
import static org.mockito.Matchers.anyString;
8+
import static org.powermock.api.mockito.PowerMockito.mock;
9+
import static org.powermock.api.mockito.PowerMockito.spy;
10+
import static org.powermock.api.mockito.PowerMockito.when;
411

12+
import org.junit.After;
513
import org.junit.Before;
614
import org.junit.Test;
15+
import org.junit.runner.RunWith;
16+
import org.powermock.core.classloader.annotations.PrepareForTest;
17+
import org.powermock.modules.junit4.PowerMockRunner;
718

19+
import java.io.ByteArrayInputStream;
820
import java.io.ByteArrayOutputStream;
21+
import java.io.Console;
22+
import java.io.InputStream;
923
import java.io.OutputStream;
1024
import java.io.PrintStream;
25+
import java.nio.charset.StandardCharsets;
1126

27+
@RunWith(PowerMockRunner.class)
28+
@PrepareForTest({TerminalIo.class, Console.class, System.class})
1229
public class TerminalIoTest {
1330

1431
Io io;
1532
OutputStream os;
33+
InputStream oldInputStream;
1634

1735
@Before
18-
public void setUp() {
36+
public void setUp() throws Exception {
37+
oldInputStream = System.in;
1938
io = new TerminalIo();
2039
os = new ByteArrayOutputStream();
2140
PrintStream ps = new PrintStream(os);
2241
System.setOut(ps);
42+
43+
spy(System.class);
44+
}
45+
46+
@After
47+
public void cleanUp() {
48+
System.setIn(oldInputStream);
49+
}
50+
51+
public void writeString(String string) {
52+
byte[] bytes = string.getBytes(StandardCharsets.UTF_8);
53+
System.setIn(new ByteArrayInputStream(bytes));
2354
}
2455

2556
@Test
@@ -45,4 +76,114 @@ public void printInteger() {
4576
io.print(5);
4677
assertTrue(os.toString().equals("5"));
4778
}
79+
80+
@Test
81+
public void readLine() {
82+
writeString("test");
83+
assertEquals("test", io.readLine("abc "));
84+
assertEquals("abc ", os.toString());
85+
}
86+
87+
@Test
88+
public void readLineIgnoresRestOfTheLines() {
89+
writeString("test\nawugwufgv");
90+
assertEquals("test", io.readLine("abc "));
91+
assertEquals("abc ", os.toString());
92+
}
93+
94+
@Test
95+
public void readPassword() {
96+
Console mockConsole = mock(Console.class);
97+
when(mockConsole.readPassword(anyString())).thenReturn("password".toCharArray());
98+
when(System.console()).thenReturn(mockConsole);
99+
assertEquals("password", io.readPassword("abc "));
100+
}
101+
102+
@Test
103+
public void failToReadPasswordBecauseException() {
104+
Console mockConsole = mock(Console.class);
105+
when(mockConsole.readPassword(anyString())).thenThrow(new RuntimeException());
106+
writeString("password");
107+
when(System.console()).thenReturn(mockConsole);
108+
assertEquals("password", io.readPassword("abc "));
109+
assertThat(os.toString(),
110+
containsString("Unable to read password"));
111+
}
112+
113+
@Test
114+
public void failToReadPasswordBecauseConsoleIsNull() {
115+
when(System.console()).thenReturn(null);
116+
writeString("password");
117+
assertEquals("password", io.readPassword("abc "));
118+
assertThat(os.toString(),
119+
containsString("Unable to read password"));
120+
}
121+
122+
@Test
123+
public void printConfimationDialogWhenDefaultIsYes() {
124+
writeString("\n");
125+
io.readConfirmation("abc", true);
126+
assertEquals("abc [Y/n] ", os.toString());
127+
}
128+
129+
@Test
130+
public void printConfimationDialogWhenDefaultIsNo() {
131+
writeString("\n");
132+
io.readConfirmation("abc", false);
133+
assertEquals("abc [y/N] ", os.toString());
134+
}
135+
136+
@Test
137+
public void readUppercaseConfirmation() {
138+
writeString("YES");
139+
assertEquals(true, io.readConfirmation("abc ", false));
140+
}
141+
142+
@Test
143+
public void readYesConfirmation() {
144+
writeString("yes");
145+
assertEquals(true, io.readConfirmation("abc ", false));
146+
}
147+
148+
@Test
149+
public void readYConfirmation() {
150+
writeString("y");
151+
assertEquals(true, io.readConfirmation("abc ", false));
152+
}
153+
154+
@Test
155+
public void readNoConfirmation() {
156+
writeString("no");
157+
assertEquals(false, io.readConfirmation("abc ", true));
158+
}
159+
160+
@Test
161+
public void readNConfirmation() {
162+
writeString("n");
163+
assertEquals(false, io.readConfirmation("abc ", true));
164+
}
165+
166+
@Test
167+
public void readUnknownConfirmationAsDefaultYes() {
168+
writeString("def");
169+
assertEquals(true, io.readConfirmation("abc ", true));
170+
}
171+
172+
@Test
173+
public void readUnknownConfirmationAsDefaultNo() {
174+
writeString("def");
175+
assertEquals(false, io.readConfirmation("abc ", false));
176+
}
177+
178+
@Test
179+
public void readEmptyConfirmationAsDefaultYes() {
180+
writeString("\n");
181+
assertEquals(true, io.readConfirmation("abc ", true));
182+
}
183+
184+
@Test
185+
public void readEmptyConfirmationAsDefaultNo() {
186+
writeString("\n");
187+
assertEquals(false, io.readConfirmation("abc ", false));
188+
}
48189
}

0 commit comments

Comments
 (0)