001 package ps6.test;
002
003 import java.util.Arrays;
004 import java.util.HashMap;
005 import java.util.HashSet;
006 import java.util.Iterator;
007 import java.util.Map;
008 import java.util.Set;
009
010 import junit.framework.TestCase;
011 import ps6.Directions;
012 import ps6.DirectionsFinder;
013 import ps6.InvalidAddressException;
014 import ps6.InvalidDatabaseException;
015 import ps6.NoPathException;
016
017 public class PS6ProgTestCase extends TestCase {
018 /** Threshold for comparing double values */
019 public static final double THRESHOLD = 0.001;
020
021 // Store the DirectionsDFinder in a static field, so that we only
022 // make one DirectionsFinder per database no matter how many tests we
023 // instantiate.
024 private static final Map<TestRecord.TestDB,DirectionsFinder> dfs = new HashMap<TestRecord.TestDB,DirectionsFinder>();
025 private static final Set<TestRecord.TestDB> dfsLoadFailed = new HashSet<TestRecord.TestDB>();
026
027 private final TestRecord test;
028 private ps2.WalkingRouteFormatter walkingFormatter;
029 private ps2.DrivingRouteFormatter drivingFormatter;
030
031 /**
032 * @effects creates a new test which runs the given test in the given mode
033 **/
034 public PS6ProgTestCase(ps6.test.TestRecord test) {
035 super(test.getTestName());
036 this.test = test;
037 this.walkingFormatter = new ps2.WalkingRouteFormatter();
038 this.drivingFormatter = new ps2.DrivingRouteFormatter();
039 }
040
041 private static void loadDatabase(TestRecord.TestDB db) {
042 // might be done already...
043 if (dfs.containsKey(db) && dfs.get(db) != null) {
044 return;
045 }
046
047 // might have failed already ...
048 if (dfsLoadFailed.contains(db)) {
049 fail("A previous attempt at loading the database has already failed");
050 }
051
052 // otherwise, give it a shot...
053 try {
054 dfs.put(db, DirectionsFinder.getDirectionsFinder(db.dbPath(), null));
055
056 } catch (InvalidDatabaseException e) {
057 dfsLoadFailed.add(db);
058 fail("Load of tiny database failed with an exception: " + e);
059 }
060 }
061
062 /**
063 * Runs the test against getDirections(Address x 2, RouteDirections)
064 **/
065 @Override
066 public void runTest() {
067 loadDatabase(test.getDb());
068
069 String usefulName = "query from '" + test.getStart() + "' to '" + test.getEnd() + "'";
070 switch(test.getType()) {
071 case DRIVING:
072 runProgADTDirectionsDriving(usefulName);
073 break;
074 case WALKING:
075 runProgADTDirectionsWalking(usefulName);
076 break;
077 case INVALID_ADDRESS:
078 runProgADTBadAddress(usefulName);
079 break;
080 case NO_PATH:
081 runProgADTNoPath(usefulName);
082 break;
083 case INVALID_DIR_TYPE:
084 //Ignore for Programmatic Tests
085 return;
086 default:
087 throw new IllegalStateException("Unknown desired result");
088 }
089 }
090
091 private void runProgADTBadAddress(String usefulName) {
092 try {
093 @SuppressWarnings("unused")
094 Directions result = dfs.get(test.getDb()).getDirections(test.getStart(), test.getEnd(), drivingFormatter);
095 fail("Expected InvalidAddressException on " + usefulName);
096 } catch (InvalidAddressException e) {
097 // this is what we want
098 return;
099 } catch (NoPathException e) {
100 fail("Unexpected exception on " + usefulName + ": " + e.toString());
101 }
102 }
103
104 private void runProgADTNoPath(String usefulName) {
105 try {
106 @SuppressWarnings("unused")
107 Directions result = dfs.get(test.getDb()).getDirections(test.getStart(), test.getEnd(), drivingFormatter);
108 fail("Expected NoPathException on " + usefulName);
109 } catch (InvalidAddressException e) {
110 fail("Unexpected exception on " + usefulName + ": " + e.toString());
111 } catch (NoPathException e) {
112 // this is what we want
113 return;
114 }
115 }
116
117 private void runProgADTDirectionsDriving(String usefulName) {
118 try {
119 Directions result = dfs.get(test.getDb()).getDirections(test.getStart(), test.getEnd(), drivingFormatter);
120
121 assertEquals("The start given by getStart() should match the starting point given in the query (" + usefulName + ")",
122 test.getStart(),
123 result.getStart());
124
125 assertEquals("The end given by getEnd() should match the ending point given in the query (" + usefulName + ")",
126 test.getEnd(),
127 result.getEnd());
128
129 assertEquals("The length given by getLength() should match the expected length for " + usefulName,
130 test.getLength(),
131 result.getLength(),
132 THRESHOLD);
133
134 Iterator<String> queryDirs = Arrays.asList(test.getDirections()).iterator();
135 Iterator<String> resultDirs = result.iterator();
136 assertEquals("getDirections(Address x 2, RouteDirections) " + usefulName,
137 queryDirs, resultDirs);
138
139 } catch (InvalidAddressException e) {
140 fail("Unexpected exception on " + usefulName + ": " + e.toString());
141 } catch (NoPathException e) {
142 fail("Unexpected exception on " + usefulName + ": " + e.toString());
143 }
144 }
145
146 private void runProgADTDirectionsWalking(String usefulName) {
147 try {
148 Directions result = dfs.get(test.getDb()).getDirections(test.getStart(), test.getEnd(), walkingFormatter);
149
150 assertEquals("The start given by getStart() should match the starting point given in the query (" + usefulName + ")",
151 test.getStart(),
152 result.getStart());
153
154 assertEquals("The end given by getEnd() should match the ending point given in the query (" + usefulName + ")",
155 test.getEnd(),
156 result.getEnd());
157
158 assertEquals("The length given by getLength() should match the expected length for " + usefulName,
159 test.getLength(),
160 result.getLength(),
161 THRESHOLD);
162
163 Iterator<String> queryDirs = Arrays.asList(test.getDirections()).iterator();
164 Iterator<String> resultDirs = result.iterator();
165 assertEquals("getDirections(Address x 2, RouteDirections) " + usefulName,
166 queryDirs, resultDirs);
167
168 } catch (InvalidAddressException e) {
169 fail("Unexpected exception on " + usefulName + ": " + e.toString());
170 } catch (NoPathException e) {
171 fail("Unexpected exception on " + usefulName + ": " + e.toString());
172 }
173 }
174
175 // (destructively) checks that two iterators return equal elements
176 protected static void assertEquals(String message, Iterator<?> expected, Iterator<?> actual) {
177 boolean expectedHasNext = expected.hasNext();
178 boolean actualHasNext = actual.hasNext();
179
180 // if both iterators are exhausted, we're fine
181 if (!expectedHasNext && !actualHasNext) {
182 return;
183 }
184
185 // if just one is exhausted, we fail
186 if (expectedHasNext && !actualHasNext) {
187 Object expectedNext = expected.next();
188 fail(message + ": expected had '" + expectedNext + "' but actual had no more elements");
189 }
190
191 // if just one is exhausted, we fail
192 if (!expectedHasNext && actualHasNext) {
193 Object actualNext = actual.next();
194 fail(message + ": actual had '" + actualNext + "' but expected had no more elements");
195 }
196
197 // assert that the current element pairing is equal
198 Object expectedNext = expected.next();
199 Object actualNext = actual.next();
200 assertEquals(message,
201 expectedNext, actualNext);
202
203 // check the rest recursively
204 assertEquals(message, expected, actual);
205 }
206 }