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 }