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 }