001 package ps6.test;
002
003 import java.util.*;
004
005 import ps6.Directions;
006 import ps6.DirectionsFinder;
007 import ps6.InvalidAddressException;
008 import ps6.InvalidDatabaseException;
009 import ps6.NoPathException;
010
011 import org.junit.Test;
012 import org.junit.runner.RunWith;
013 import org.junit.runners.Parameterized;
014 import org.junit.runners.Parameterized.Parameters;
015
016 import static org.junit.Assert.assertEquals;
017 import static org.junit.Assert.fail;
018
019 @RunWith(Parameterized.class)
020 public class PublicProgADTTest {
021
022 /* TEST INSTANCE FIELDS */
023 private final TestRecord test;
024 private final ps2.WalkingRouteFormatter walkingFormatter;
025 private final ps2.DrivingRouteFormatter drivingFormatter;
026
027 /* STATIC FIELDS */
028 public static final String INPUT_PROMPTS =
029 "starting number? starting street? starting zipcode? " +
030 "destination number? destination street? destination zipcode? walking or driving [w/d]? ";
031
032 public static final String END_PROMPT = "starting number? ";
033 public static String EOL = System.getProperty("line.separator");
034
035 //this could instead be a different set, e.g. ValidateQueries.badQueries
036 private static final TestRecord[] QUERIES_TO_TEST = ValidateQueries.allQueries;
037
038 /** Threshold for comparing double values */
039 public static final double THRESHOLD = 0.001;
040
041 // Store the DirectionsDFinder in a static field, so that we only
042 // make one DirectionsFinder per database no matter how many tests we
043 // instantiate.
044 private static final Map<TestRecord.TestDB,DirectionsFinder> dfs = new HashMap<TestRecord.TestDB,DirectionsFinder>();
045 private static final Set<TestRecord.TestDB> dfsLoadFailed = new HashSet<TestRecord.TestDB>();
046
047 /* TEST INSTANCE METHODS */
048 public PublicProgADTTest(TestRecord test) {
049 this.test = test;
050 this.walkingFormatter = new ps2.WalkingRouteFormatter();
051 this.drivingFormatter = new ps2.DrivingRouteFormatter();
052 }
053
054 /**
055 * Runs the test against getDirections(Address x 2, RouteDirections)
056 **/
057 @Test
058 public void ProgTestCase() {
059 loadDatabase(test.getDb());
060
061 String usefulName = "query from '" + test.getStart() + "' to '" + test.getEnd() + "'";
062 switch(test.getType()) {
063 case DRIVING:
064 runProgADTDirectionsDriving(usefulName);
065 break;
066 case WALKING:
067 runProgADTDirectionsWalking(usefulName);
068 break;
069 case INVALID_ADDRESS:
070 runProgADTBadAddress(usefulName);
071 break;
072 case NO_PATH:
073 runProgADTNoPath(usefulName);
074 break;
075 case INVALID_DIR_TYPE:
076 //Ignore for Programmatic Tests
077 return;
078 default:
079 throw new IllegalStateException("Unknown desired result");
080 }
081 }
082
083 private void runProgADTBadAddress(String usefulName) {
084 try {
085 @SuppressWarnings("unused")
086 Directions result = dfs.get(test.getDb()).getDirections(test.getStart(), test.getEnd(), drivingFormatter);
087 fail("Expected InvalidAddressException on " + usefulName);
088 } catch (InvalidAddressException e) {
089 // this is what we want
090 return;
091 } catch (NoPathException e) {
092 fail("Unexpected exception on " + usefulName + ": " + e.toString());
093 }
094 }
095
096 private void runProgADTNoPath(String usefulName) {
097 try {
098 @SuppressWarnings("unused")
099 Directions result = dfs.get(test.getDb()).getDirections(test.getStart(), test.getEnd(), drivingFormatter);
100 fail("Expected NoPathException on " + usefulName);
101 } catch (InvalidAddressException e) {
102 fail("Unexpected exception on " + usefulName + ": " + e.toString());
103 } catch (NoPathException e) {
104 // this is what we want
105 return;
106 }
107 }
108
109 private void runProgADTDirectionsDriving(String usefulName) {
110 try {
111 Directions result = dfs.get(test.getDb()).getDirections(test.getStart(), test.getEnd(), drivingFormatter);
112
113 assertEquals("The start given by getStart() should match the starting point given in the query (" + usefulName + ")",
114 test.getStart(),
115 result.getStart());
116
117 assertEquals("The end given by getEnd() should match the ending point given in the query (" + usefulName + ")",
118 test.getEnd(),
119 result.getEnd());
120
121 assertEquals("The length given by getLength() should match the expected length for " + usefulName,
122 test.getLength(),
123 result.getLength(),
124 THRESHOLD);
125
126 Iterator<String> queryDirs = Arrays.asList(test.getDirections()).iterator();
127 Iterator<String> resultDirs = result.iterator();
128 assertIteratedEquals("getDirections(Address x 2, RouteDirections) " + usefulName,
129 queryDirs, resultDirs);
130
131 } catch (InvalidAddressException e) {
132 fail("Unexpected exception on " + usefulName + ": " + e.toString());
133 } catch (NoPathException e) {
134 fail("Unexpected exception on " + usefulName + ": " + e.toString());
135 }
136 }
137
138 private void runProgADTDirectionsWalking(String usefulName) {
139 try {
140 Directions result = dfs.get(test.getDb()).getDirections(test.getStart(), test.getEnd(), walkingFormatter);
141
142 assertEquals("The start given by getStart() should match the starting point given in the query (" + usefulName + ")",
143 test.getStart(),
144 result.getStart());
145
146 assertEquals("The end given by getEnd() should match the ending point given in the query (" + usefulName + ")",
147 test.getEnd(),
148 result.getEnd());
149
150 assertEquals("The length given by getLength() should match the expected length for " + usefulName,
151 test.getLength(),
152 result.getLength(),
153 THRESHOLD);
154
155 Iterator<String> queryDirs = Arrays.asList(test.getDirections()).iterator();
156 Iterator<String> resultDirs = result.iterator();
157 assertIteratedEquals("getDirections(Address x 2, RouteDirections) " + usefulName,
158 queryDirs, resultDirs);
159
160 } catch (InvalidAddressException e) {
161 fail("Unexpected exception on " + usefulName + ": " + e.toString());
162 } catch (NoPathException e) {
163 fail("Unexpected exception on " + usefulName + ": " + e.toString());
164 }
165 }
166
167
168 /* CLASS METHODS */
169
170 /**
171 * Constructs a list of parameters for each instance of PublicProgADTTest.
172 * The test runner Parameterized will instantiate a PublicProgADTTest for every Object[] in the
173 * returned list. Since this call to the constructor is done through reflection, we can (and must)
174 * pass around opaque arrays of objects here.
175 */
176 @Parameters
177 public static List<Object[]> getTestParameters() {
178 List<Object[]> queryTestList = new ArrayList<Object[]>(PublicProgADTTest.QUERIES_TO_TEST.length);
179 for (TestRecord record : PublicProgADTTest.QUERIES_TO_TEST) {
180 queryTestList.add(new Object[]{ record });
181 }
182 return queryTestList;
183 }
184
185 private static void loadDatabase(TestRecord.TestDB db) {
186 // might be done already...
187 if (dfs.containsKey(db) && dfs.get(db) != null) {
188 return;
189 }
190
191 // might have failed already ...
192 if (dfsLoadFailed.contains(db)) {
193 fail("A previous attempt at loading the database has already failed");
194 }
195
196 // otherwise, give it a shot...
197 try {
198 dfs.put(db, DirectionsFinder.getDirectionsFinder(db.dbPath(), null));
199
200 } catch (InvalidDatabaseException e) {
201 dfsLoadFailed.add(db);
202 fail("Load of tiny database failed with an exception: " + e);
203 }
204 }
205
206 // (destructively) checks that two iterators return equal elements
207 protected static void assertIteratedEquals(String message, Iterator<?> expected, Iterator<?> actual) {
208 boolean expectedHasNext = expected.hasNext();
209 boolean actualHasNext = actual.hasNext();
210
211 // if both iterators are exhausted, we're fine
212 if (!expectedHasNext && !actualHasNext) {
213 return;
214 }
215
216 // if just one is exhausted, we fail
217 if (expectedHasNext && !actualHasNext) {
218 Object expectedNext = expected.next();
219 fail(message + ": expected had '" + expectedNext + "' but actual had no more elements");
220 }
221
222 // if just one is exhausted, we fail
223 if (!expectedHasNext && actualHasNext) {
224 Object actualNext = actual.next();
225 fail(message + ": actual had '" + actualNext + "' but expected had no more elements");
226 }
227
228 // assert that the current element pairing is equal
229 Object expectedNext = expected.next();
230 Object actualNext = actual.next();
231 assertEquals(message, expectedNext, actualNext);
232
233 // check the rest recursively
234 assertIteratedEquals(message, expected, actual);
235 }
236 }