Skip to content

Commit adf610d

Browse files
committed
Add class: ConsoleIOHelper
1 parent 133841a commit adf610d

File tree

5 files changed

+224
-175
lines changed

5 files changed

+224
-175
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>cf.maybelambda</groupId>
88
<artifactId>fedora-setup-script</artifactId>
9-
<version>3.0.2</version>
9+
<version>3.0.3</version>
1010

1111
<properties>
1212
<maven.compiler.source>21</maven.compiler.source>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package cf.maybelambda.fedora;
2+
3+
import java.io.Console;
4+
import java.io.IOException;
5+
import java.util.ArrayList;
6+
import java.util.HashSet;
7+
import java.util.List;
8+
import java.util.Scanner;
9+
import java.util.Set;
10+
11+
public class ConsoleIOHelper {
12+
static final String RESET = "\u001B[0m";
13+
static final String YELLOW = "\u001B[33m";
14+
static final String GREEN = "\u001B[32m";
15+
static final String RED = "\u001B[31m";
16+
static final String BLUE = "\u001B[34m";
17+
18+
static boolean confirm(Scanner scanner, String prompt) {
19+
System.out.print(prompt + " [y/N]: ");
20+
String input = scanner.nextLine().trim().toLowerCase();
21+
return input.equals("y") || input.equals("yes");
22+
}
23+
24+
static List<String> promptForExclusions(List<String> packages, Scanner scanner) {
25+
for (int i = 0; i < packages.size(); i++) {
26+
System.out.printf("%2d. %s\n", i + 1, packages.get(i));
27+
}
28+
System.out.print("Enter the numbers of any packages to exclude (comma-separated), or press Enter to keep all: ");
29+
30+
String excludeInput = scanner.nextLine().trim();
31+
String validInputRE = "^$|^\\d+$|^(\\d+,)+\\d$";
32+
if (!excludeInput.matches(validInputRE)) {
33+
throw new RuntimeException("Invalid selection");
34+
}
35+
36+
Set<Integer> excludeIndexes = new HashSet<>();
37+
if (!excludeInput.isEmpty()) {
38+
for (String part : excludeInput.split(",")) {
39+
try {
40+
int idx = Integer.parseInt(part.trim());
41+
if (idx >= 1 && idx <= packages.size()) {
42+
excludeIndexes.add(idx - 1);
43+
}
44+
} catch (NumberFormatException ignored) {}
45+
}
46+
}
47+
List<String> filtered = new ArrayList<>();
48+
for (int i = 0; i < packages.size(); i++) {
49+
if (!excludeIndexes.contains(i)) {
50+
filtered.add(packages.get(i));
51+
}
52+
}
53+
54+
return filtered;
55+
}
56+
57+
static void printHelp() {
58+
try {
59+
List<String> lines = ConfigManager.getHelpText();
60+
System.out.println(String.join(System.lineSeparator(), lines));
61+
} catch (IOException e) {
62+
System.err.println("Error reading help file: " + e.getMessage());
63+
}
64+
}
65+
66+
static String color(String str, String ansiColorCode) {
67+
return isANSISupported(System.getenv("TERM"), System.console()) ? ansiColorCode + str + RESET : str;
68+
}
69+
70+
static boolean isANSISupported(String term, Console console) {
71+
return console != null && term != null && !"dumb".equals(term);
72+
}
73+
}

src/main/java/cf/maybelambda/fedora/PostInstallUpdater.java

Lines changed: 12 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
11
package cf.maybelambda.fedora;
22

33
import java.io.BufferedReader;
4-
import java.io.Console;
54
import java.io.IOException;
65
import java.io.InputStreamReader;
7-
import java.util.ArrayList;
86
import java.util.Arrays;
9-
import java.util.HashSet;
107
import java.util.List;
118
import java.util.NoSuchElementException;
129
import java.util.Scanner;
13-
import java.util.Set;
1410

15-
public class PostInstallUpdater {
16-
static final String RESET = "\u001B[0m";
17-
static final String YELLOW = "\u001B[33m";
18-
static final String GREEN = "\u001B[32m";
19-
private static final String RED = "\u001B[31m";
20-
private static final String BLUE = "\u001B[34m";
11+
import static cf.maybelambda.fedora.ConsoleIOHelper.BLUE;
12+
import static cf.maybelambda.fedora.ConsoleIOHelper.GREEN;
13+
import static cf.maybelambda.fedora.ConsoleIOHelper.RED;
14+
import static cf.maybelambda.fedora.ConsoleIOHelper.YELLOW;
15+
import static cf.maybelambda.fedora.ConsoleIOHelper.color;
16+
import static cf.maybelambda.fedora.ConsoleIOHelper.confirm;
17+
import static cf.maybelambda.fedora.ConsoleIOHelper.promptForExclusions;
2118

22-
private static boolean dryRun = false;
19+
public class PostInstallUpdater {
20+
private static boolean dryRun;
2321

2422
public static void main(String[] args) {
2523
if (Arrays.asList(args).contains("-h") || Arrays.asList(args).contains("--help")) {
26-
printHelp();
24+
ConsoleIOHelper.printHelp();
2725
return;
2826
}
2927

@@ -32,7 +30,7 @@ public static void main(String[] args) {
3230
List<String> flatpakInstallPackages = ConfigManager.getFlatpakInstallPackages();
3331
Scanner scanner = new Scanner(System.in);
3432

35-
System.out.println(color("Fedora Post Install Actions\n", GREEN));
33+
System.out.println(color("]|I{•------» Fedora Post-Install Actions «------•}I|[\n", GREEN));
3634
setDryRun(Arrays.asList(args).contains("--dry-run"));
3735
if (isDryRun()) {
3836
System.out.println(color("---[Dry Run Mode] Shell Commands will not be executed.---\n", RED));
@@ -116,7 +114,7 @@ public static void main(String[] args) {
116114
runCommand(new String[]{"sudo", "systemctl", "enable", "--now", "cockpit.socket"});
117115
}
118116

119-
System.out.println(color("\nAll actions completed. Goodbye.", GREEN));
117+
System.out.println(color("\n.o0×X×0o. All actions completed. Goodbye. .o0×X×0o.", GREEN));
120118
}
121119

122120
static boolean isDryRun() {
@@ -127,12 +125,6 @@ static void setDryRun(boolean dryRun) {
127125
PostInstallUpdater.dryRun = dryRun;
128126
}
129127

130-
static boolean confirm(Scanner scanner, String prompt) {
131-
System.out.print(prompt + " [y/N]: ");
132-
String input = scanner.nextLine().trim().toLowerCase();
133-
return input.equals("y") || input.equals("yes");
134-
}
135-
136128
static ProcessBuilder createProcessBuilder(String[] cmd) {
137129
return new ProcessBuilder(cmd);
138130
}
@@ -163,49 +155,4 @@ static int runCommand(String[] command) {
163155

164156
return exitCode;
165157
}
166-
167-
static List<String> promptForExclusions(List<String> packages, Scanner scanner) {
168-
for (int i = 0; i < packages.size(); i++) {
169-
System.out.printf("%2d. %s\n", i + 1, packages.get(i));
170-
}
171-
System.out.print("Enter the numbers of any packages to exclude (comma-separated), or press Enter to keep all: ");
172-
173-
String excludeInput = scanner.nextLine().trim();
174-
Set<Integer> excludeIndexes = new HashSet<>();
175-
if (excludeInput.matches("^\\d+$|^(\\d+,)+\\d$")) {
176-
for (String part : excludeInput.split(",")) {
177-
try {
178-
int idx = Integer.parseInt(part.trim());
179-
if (idx >= 1 && idx <= packages.size()) {
180-
excludeIndexes.add(idx - 1);
181-
}
182-
} catch (NumberFormatException ignored) {}
183-
}
184-
}
185-
List<String> filtered = new ArrayList<>();
186-
for (int i = 0; i < packages.size(); i++) {
187-
if (!excludeIndexes.contains(i)) {
188-
filtered.add(packages.get(i));
189-
}
190-
}
191-
192-
return filtered;
193-
}
194-
195-
static void printHelp() {
196-
try {
197-
List<String> lines = ConfigManager.getHelpText();
198-
System.out.println(String.join(System.lineSeparator(), lines));
199-
} catch (IOException e) {
200-
System.err.println("Error reading help file: " + e.getMessage());
201-
}
202-
}
203-
204-
static String color(String str, String ansiColorCode) {
205-
return isANSISupported(System.getenv("TERM"), System.console()) ? ansiColorCode + str + RESET : str;
206-
}
207-
208-
static boolean isANSISupported(String term, Console console) {
209-
return console != null && term != null && !"dumb".equals(term);
210-
}
211158
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package cf.maybelambda.fedora;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.mockito.MockedStatic;
5+
6+
import java.io.ByteArrayInputStream;
7+
import java.io.Console;
8+
import java.nio.charset.StandardCharsets;
9+
import java.util.List;
10+
import java.util.Scanner;
11+
12+
import static cf.maybelambda.fedora.ConsoleIOHelper.GREEN;
13+
import static cf.maybelambda.fedora.ConsoleIOHelper.RESET;
14+
import static cf.maybelambda.fedora.ConsoleIOHelper.YELLOW;
15+
import static cf.maybelambda.fedora.ConsoleIOHelper.color;
16+
import static cf.maybelambda.fedora.ConsoleIOHelper.confirm;
17+
import static cf.maybelambda.fedora.ConsoleIOHelper.isANSISupported;
18+
import static cf.maybelambda.fedora.ConsoleIOHelper.promptForExclusions;
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertFalse;
21+
import static org.junit.jupiter.api.Assertions.assertThrows;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
import static org.mockito.ArgumentMatchers.any;
24+
import static org.mockito.Mockito.CALLS_REAL_METHODS;
25+
import static org.mockito.Mockito.mock;
26+
import static org.mockito.Mockito.mockStatic;
27+
import static org.mockito.Mockito.when;
28+
29+
public class ConsoleIOHelperTests {
30+
@Test
31+
void confirmYesInput() {
32+
Scanner scanner = new Scanner(new ByteArrayInputStream("y\n".getBytes(StandardCharsets.UTF_8)));
33+
34+
assertTrue(confirm(scanner, "Install packages?"));
35+
}
36+
37+
@Test
38+
void confirmYesFullWord() {
39+
Scanner scanner = new Scanner(new ByteArrayInputStream("yes\n".getBytes(StandardCharsets.UTF_8)));
40+
41+
assertTrue(confirm(scanner, "Confirm?"));
42+
}
43+
44+
@Test
45+
void confirmNoInput() {
46+
Scanner scanner = new Scanner(new ByteArrayInputStream("no\n".getBytes(StandardCharsets.UTF_8)));
47+
48+
assertFalse(confirm(scanner, "Confirm?"));
49+
}
50+
51+
@Test
52+
void confirmEmptyDefaultsToNo() {
53+
Scanner scanner = new Scanner(new ByteArrayInputStream("\n".getBytes(StandardCharsets.UTF_8)));
54+
55+
assertFalse(confirm(scanner, "Proceed?"));
56+
}
57+
58+
@Test
59+
void promptForExclusionsKeepsAllWhenInputIsEmpty() {
60+
List<String> pkgs = List.of("nano", "vim", "htop");
61+
Scanner scanner = new Scanner(new ByteArrayInputStream("\n".getBytes(StandardCharsets.UTF_8)));
62+
63+
List<String> result = promptForExclusions(pkgs, scanner);
64+
65+
assertEquals(pkgs, result);
66+
}
67+
68+
@Test
69+
void promptForExclusionsRemovesSome() {
70+
List<String> pkgs = List.of("nano", "vim", "htop", "curl");
71+
Scanner scanner = new Scanner(new ByteArrayInputStream("2,4\n".getBytes(StandardCharsets.UTF_8)));
72+
73+
List<String> result = promptForExclusions(pkgs, scanner);
74+
75+
assertEquals(List.of("nano", "htop"), result);
76+
}
77+
78+
@Test
79+
void promptForExclusionsHandlesInvalidIndexes() {
80+
List<String> pkgs = List.of("pkg1", "pkg2", "pkg3");
81+
Scanner scanner = new Scanner(new ByteArrayInputStream("0,5,2\n".getBytes(StandardCharsets.UTF_8)));
82+
83+
List<String> result = promptForExclusions(pkgs, scanner);
84+
85+
assertEquals(List.of("pkg1", "pkg3"), result);
86+
}
87+
88+
@Test
89+
void promptForExclusionsThrowsRuntimeExceptionWhenInvalidInputRead() {
90+
List<String> pkgs = List.of("pkg");
91+
Scanner scanner = mock(Scanner.class);
92+
when(scanner.nextLine()).thenReturn("qwerty");
93+
94+
assertThrows(RuntimeException.class, () -> promptForExclusions(pkgs, scanner));
95+
}
96+
97+
@Test
98+
void colorAppliesAnsiWhenisANSISupportedTrue() {
99+
try (MockedStatic<ConsoleIOHelper> mocked = mockStatic(ConsoleIOHelper.class, CALLS_REAL_METHODS)) {
100+
mocked.when(() -> isANSISupported(any(), any())).thenReturn(true);
101+
102+
String result = color("Hello", YELLOW);
103+
104+
assertEquals(YELLOW + "Hello" + RESET, result);
105+
}
106+
}
107+
108+
@Test
109+
void colorReturnsPlainTextWhenisANSISupportedFalse() {
110+
try (MockedStatic<ConsoleIOHelper> mocked = mockStatic(ConsoleIOHelper.class, CALLS_REAL_METHODS)) {
111+
mocked.when(() -> isANSISupported(any(), any())).thenReturn(false);
112+
113+
String result = color("World", GREEN);
114+
115+
assertEquals("World", result);
116+
}
117+
}
118+
119+
@Test
120+
void isANSISupportedReturnsTrueWhenConsolePresentAndTermValid() {
121+
assertTrue(isANSISupported("xterm-256color", mock(Console.class)));
122+
}
123+
124+
@Test
125+
void isANSISupportedReturnsFalseWhenConsoleIsNull() {
126+
assertFalse(isANSISupported("xterm-256color", null));
127+
}
128+
129+
@Test
130+
void isANSISupportedReturnsFalseWhenTermIsNull() {
131+
assertFalse(isANSISupported(null, mock(Console.class)));
132+
}
133+
134+
@Test
135+
void isANSISupportedReturnsFalseWhenTermIsDumb() {
136+
assertFalse(isANSISupported("dumb", mock(Console.class)));
137+
}
138+
}

0 commit comments

Comments
 (0)