-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTextUITester.java
More file actions
176 lines (156 loc) · 6.7 KB
/
Copy pathTextUITester.java
File metadata and controls
176 lines (156 loc) · 6.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.IOException;
import java.util.Scanner;
/**
* This class can be used to test text based user interactions by 1) specifying a String of text
* input (that will be fed to System.in as if entered by the user), and then 2) capturing the output
* printed to System.out and System.err in String form so that it can be compared to the expect
* output.
*
* @date 2024.09
*/
public class TextUITester {
/**
* This main method demonstrates the use of a TextUITester object to check the behavior of the
* following run method.
*
* @param args from the commandline are not used in this example
*/
public static void main(String[] args) {
// 1. Create a new TextUITester object for each test, and
// pass the text that you'd like to simulate a user typing
// as only argument. It's IMPORTANT that this be created
// before you create an Scanner objects reading from System.in
TextUITester tester = new TextUITester("apple\n3.14\nq\n");
// 2. Create a Scanner, and run then code that you want to test here:
run(); // (this code reads from System.in and writes to System.out)
// Note hat you cannot see output printed by your program between the
// constructor call above and the checkOutput call below. This is
// is useful for your final tests. For debugging purposes you can
// pass an extra false argument to the TextUITester's constructor to
// be able to see what is happening while the test is running: for
// debugging purposes.
// 3. Check whether the output printed to System.out is correct
String output = tester.checkOutput();
if (output.startsWith("Welcome to the run program.") && output.contains("apple4.14"))
System.out.println("Test passed.");
else
System.out.println("Test FAILED.");
}
/**
* This is the code being tested by the main method above. It 1) prints out a welcome message, 2)
* reads a String, a double, and a character from System.in, and then 3) prints out the string, 4)
* if the char is a lowercase q, it prints out a number that is one larger than the entered double
* value.
*/
public static void run() {
// Note: AVOID instantiating more than one Scanner reading from
// System.in, as this leads to undefined behavior!
Scanner in = new Scanner(System.in);
System.out.println("Welcome to the run program.");
System.out.println("Enter a string, a double, and then q to quit: ");
String s = in.nextLine();
double d = in.nextDouble();
in.nextLine(); // read newline after double
char c = in.nextLine().charAt(0);
if (c == 'q')
System.out.println(s + (d + 1.0));
in.close();
}
// Below is the code that actually implements the redirection of System.in
// and System.out. You are welcome to ignore this code: focusing instead
// on how the constructor and checkOutput() method are used in the example
// above.
// save initial stream references
private PrintStream saveSystemOut;
private PrintStream saveSystemErr;
private InputStream saveSystemIn;
// streams to use in place of those standard ones
private ByteArrayOutputStream redirectedOut;
private ByteArrayOutputStream redirectedErr;
private boolean hideOutput;
/**
* Creates a new test object with the specified string of simulated user input text. The output
* printed to System.out will be hidden, if true is passed for the second argument. This can be
* helpful to change while debugging a failing test.
*
* @param programInput the String of text that you want to simulate being typed in by the user.
* @param hideOutput determines whether this test's output is hidden from System.out while the
* test is running.
*/
public TextUITester(String programInput, boolean hideOutput) {
// backup standard io before redirecting for tests
this.saveSystemOut = System.out;
this.saveSystemErr = System.err;
this.saveSystemIn = System.in;
this.hideOutput = hideOutput;
// create alternative location to write output, and to read input from
this.redirectedOut = new ByteArrayOutputStream();
if (hideOutput) {
System.setOut(new PrintStream(this.redirectedOut));
} else {
this.saveSystemOut.println("TextUITester's capture starts:");
System.setOut(new PrintStream(new SplitStream(this.saveSystemOut, this.redirectedOut)));
}
this.redirectedErr = new ByteArrayOutputStream();
if (hideOutput) {
System.setErr(new PrintStream(this.redirectedErr));
} else {
System.setErr(new PrintStream(new SplitStream(this.saveSystemErr, this.redirectedErr)));
}
System.setIn(new ByteArrayInputStream(programInput.getBytes()));
}
public TextUITester(String programInput) {
this(programInput, true);
}
/**
* Call this method after running your test code to check whether the expected text was printed
* out to System.out and System.err. Calling this method will also un-redirect standard io, so
* that the console can be used as normal again.
*
* @return captured text that was printed to System.out and System.err durring test.
*/
public String checkOutput() {
try {
String programOutput = redirectedOut.toString() + redirectedErr.toString();
return programOutput;
} finally {
// restore standard io to their pre-test states
if (!this.hideOutput)
this.saveSystemOut.println("TextUITester's capture ends.");
System.out.close();
System.setOut(saveSystemOut);
System.err.close();
System.setErr(saveSystemErr);
System.setIn(saveSystemIn);
}
}
/**
* This helper class splits the output stream that System.out is directed to so that it can both
* be captured and displayed (for debugging purposes) at the same time.
*/
private static class SplitStream extends OutputStream {
private OutputStream left;
private OutputStream right;
public SplitStream(OutputStream left, OutputStream right) {
this.left = left;
this.right = right;
}
public void write(byte[] bytes) throws IOException {
this.left.write(bytes);
this.right.write(bytes);
}
public void write(byte[] bytes, int offset, int length) throws IOException {
this.left.write(bytes, offset, length);
this.right.write(bytes, offset, length);
}
public void write(int oneByte) throws IOException {
left.write(oneByte);
right.write(oneByte);
}
}
}