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    }