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 }