001 package ps6.test;
002
003 import java.io.ByteArrayOutputStream;
004 import java.io.ByteArrayInputStream;
005 import java.io.InputStream;
006 import java.io.PrintStream;
007 import java.util.List;
008 import java.util.ArrayList;
009 import java.lang.reflect.InvocationTargetException;
010 import java.lang.reflect.Method;
011 import java.lang.reflect.Modifier;
012
013 import ps6.*;
014
015 import org.junit.Test;
016 import org.junit.runner.RunWith;
017 import org.junit.runners.Parameterized;
018 import org.junit.runners.Parameterized.Parameters;
019
020 import static org.junit.Assert.assertNotNull;
021 import static org.junit.Assert.assertEquals;
022 import static org.junit.Assert.fail;
023
024 /**
025 * Public (Specification) TextUI Test Suite
026 * @author tws
027 */
028
029 @RunWith(Parameterized.class)
030 public class PublicTextUITest {
031
032 /* TEST INSTANCE FIELDS */
033 private final InputStream testInput;
034 private final String expected;
035 private final Method mainMethod;
036 private final String[] mainParams;
037
038 /* STATIC FIELDS */
039 public static final String INPUT_PROMPTS =
040 "starting number? starting street? starting zipcode? " +
041 "destination number? destination street? destination zipcode? walking or driving [w/d]? ";
042
043 public static final String END_PROMPT = "starting number? ";
044 public static String EOL = System.getProperty("line.separator");
045
046 //this could instead be a different set, e.g. ValidateQueries.badQueries
047 private static final TestRecord[] QUERIES_TO_TEST = ValidateQueries.allQueries;
048 private static final int INITIAL_BUFFER_SIZE = 10240; // 10 kilobytes
049
050
051 /* TEST INSTANCE METHODS */
052 /**
053 * When instantiated, serves as an immutable TextUI test case for
054 * PS6. Runs the TextUI main method on a given input and compared
055 * the actual output to the expected output
056 *
057 * @author tws
058 *
059 * @param mainClass
060 * class that defines a main() that reads from System.in and
061 * writes to System.out
062 * @param testInput
063 * test input for the filter
064 * @param expected
065 * expected test output
066 */
067 public PublicTextUITest(String description, String mainParams[], Class<?> mainClass,
068 InputStream testInput, String expected) {
069
070 assertNotNull(mainClass);
071 assertNotNull(testInput);
072 assertNotNull(expected);
073 assertNotNull(mainParams);
074
075 this.mainParams = mainParams;
076 this.testInput = testInput;
077 this.expected = expected;
078
079 // grab the "main" method from mainClass
080 try {
081 this.mainMethod = mainClass.getMethod("main",
082 new Class[] { String[].class });
083 } catch (NoSuchMethodException e) {
084 throw new IllegalArgumentException(mainClass.getName()
085 + " does not define main()");
086 }
087 if (!Modifier.isPublic(mainMethod.getModifiers())
088 || !Modifier.isStatic(mainMethod.getModifiers()))
089 throw new IllegalArgumentException(mainClass.getName()
090 + " main() is not public and static");
091
092 }
093
094 @Test
095 /**
096 Actually runs the main method of the class under test.
097 */
098 public void TextUiTestCase() {
099 // save the old streams
100 PrintStream savedSystemOut = System.out;
101 InputStream savedSystemIn = System.in;
102
103 // read input from testInput and capture output in buffers
104 ByteArrayOutputStream outBuffer = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
105 PrintStream testOutput = new PrintStream(outBuffer, true); // autoflush
106
107 System.setIn(testInput);
108 System.setOut(testOutput);
109
110 try {
111 mainMethod.invoke(null, new Object[]{mainParams});
112 } catch (IllegalArgumentException e) {
113 fail("Internal Invocation Error:" + e.getCause().getMessage());
114 } catch (IllegalAccessException e) {
115 fail("Internal Invocation Error:" + e.getCause().getMessage());
116 } catch (InvocationTargetException e) {
117 fail("Internal Invocation Error:" + e.getCause().getMessage());
118 }
119
120 // flush streams
121 System.out.flush();
122 System.err.flush();
123
124 // compare actual output to expected
125 assertEquals(expected,outBuffer.toString());
126
127 // restore the old streams
128 System.setIn(savedSystemIn);
129 System.setOut(savedSystemOut);
130 }
131
132
133 /* CLASS METHODS */
134
135 /**
136 * Constructs a list of parameters for each instance of PublicTextUITest.
137 * The test runner Parameterized will instantiate a PublicTextUITest for every Object[] in the
138 * returned list. Since this call to the constructor is done through reflection, we can (and must)
139 * pass around opaque arrays of objects here.
140 */
141 @Parameters
142 public static List<Object[]> getTestParameters() {
143 List<Object[]> queryTestList = new ArrayList<Object[]>(PublicTextUITest.QUERIES_TO_TEST.length);
144 for (TestRecord record : PublicTextUITest.QUERIES_TO_TEST) {
145 queryTestList.add(makeUiTest(record));
146 }
147 return queryTestList;
148 }
149
150
151 public static Object[] makeUiTest(TestRecord record) {
152 return new Object[] {
153 record.getTestName(),
154 new String[] { record.getDb().dbPath() },
155 TextUI.class,
156 new ByteArrayInputStream((genTextUiStdIn(record) + "-1" + EOL).getBytes()),
157 INPUT_PROMPTS + genTextUiStdOut(record)
158 };
159 }
160
161 /**
162 * Generate the expected TextUI output for a test record
163 * @param record a PS6 test
164 * @return the expected TestUI output for a test
165 */
166 private static String genTextUiStdOut(TestRecord record) {
167 StringBuilder b = new StringBuilder();
168
169 switch (record.getType()) {
170 case INVALID_DIR_TYPE:
171 case NO_PATH:
172 b.append(record.getErrorMessage());
173 b.append(EOL);
174 b.append(END_PROMPT);
175 break;
176 case INVALID_ADDRESS:
177 b.append(record.getErrorMessage());
178 b.append(EOL);
179 b.append(END_PROMPT);
180 break;
181 case WALKING:
182 case DRIVING:
183 b.append("Start at "
184 + record.getStart().getNum() + " "
185 + record.getStart().getName()
186 + " " + record.getStart().getZipcode());
187
188 b.append(EOL);
189 for (String line : record.getDirections()) {
190 b.append(line);
191 b.append(EOL);
192 }
193 b.append(record.getTripLength());
194 b.append(EOL);
195 b.append(END_PROMPT);
196 break;
197 }
198
199 return b.toString();
200 }
201
202 /**
203 * Generate the TextUI input for a test record
204 * @param record a PS6 test
205 * @return the TextUI input for the test
206 */
207 private static String genTextUiStdIn(TestRecord record) {
208 StringBuilder b = new StringBuilder();
209 b.append(record.getStart().getNum());
210 b.append(EOL);
211 b.append(record.getStart().getName());
212 b.append(EOL);
213 b.append(record.getStart().getZipcode());
214 b.append(EOL);
215 b.append(record.getEnd().getNum());
216 b.append(EOL);
217 b.append(record.getEnd().getName());
218 b.append(EOL);
219 b.append(record.getEnd().getZipcode());
220 b.append(EOL);
221 b.append(record.getDirectionType());
222 b.append(EOL);
223 return b.toString();
224 }
225 }