package cgeo.geocaching.files; import static org.assertj.core.api.Java6Assertions.assertThat; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.connector.gc.GCConstants; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.LoadFlag; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.log.LogEntry; import cgeo.geocaching.log.LogType; import cgeo.geocaching.models.Geocache; import cgeo.geocaching.models.Waypoint; import cgeo.geocaching.sorting.GeocodeComparator; import cgeo.geocaching.storage.DataStore; import cgeo.geocaching.test.AbstractResourceInstrumentationTestCase; import cgeo.geocaching.test.R; import cgeo.geocaching.utils.CalendarUtils; import cgeo.geocaching.utils.SynchronizedDateFormat; import android.support.annotation.RawRes; import java.io.IOException; import java.io.InputStream; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import org.apache.commons.compress.utils.IOUtils; public class GPXParserTest extends AbstractResourceInstrumentationTestCase { private static final SynchronizedDateFormat LOG_DATE_FORMAT = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); // 2010-04-20T07:00:00Z public void testGPXVersion100() throws Exception { final Geocache cache = readAndAssertTreasureIsland(R.raw.gc1bkp3_gpx100); assertThat(cache).isNotNull(); } private Geocache readAndAssertTreasureIsland(@RawRes final int resourceId) throws IOException, ParserException { final List<Geocache> caches = readGPX10(resourceId); assertThat(caches).isNotNull(); assertThat(caches).hasSize(1); final Geocache cache = caches.get(0); assertThat(cache.getGeocode()).isEqualTo("GC1BKP3"); assertThat(cache.getGuid()).isEqualTo("9946f030-a514-46d8-a050-a60e92fd2e1a"); assertThat(cache.getType()).isEqualTo(CacheType.TRADITIONAL); assertThat(cache.isArchived()).isEqualTo(false); assertThat(cache.isDisabled()).isEqualTo(false); assertThat(cache.getName()).isEqualTo("Die Schatzinsel / treasure island"); assertThat(cache.getOwnerDisplayName()).isEqualTo("Die unbesiegbaren Geo - Geparden"); assertThat(cache.getOwnerUserId()).isEqualTo("Die unbesiegbaren Geo - Geparden"); assertThat(cache.getSize()).isEqualTo(CacheSize.MICRO); assertThat(cache.getDifficulty()).isEqualTo(1.0f); assertThat(cache.getTerrain()).isEqualTo(5.0f); assertThat(cache.getLocation()).isEqualTo("Baden-Württemberg, Germany"); assertThat(cache.getShortDescription()).isEqualTo("Ein alter Kindheitstraum, ein Schatz auf einer unbewohnten Insel.\nA old dream of my childhood, a treasure on a lonely island."); assertThat(cache.getCoords()).isEqualTo(new Geopoint(48.859683, 9.1874)); return cache; } public void testGPXVersion101() throws IOException, ParserException { final Geocache cache = readAndAssertTreasureIsland(R.raw.gc1bkp3_gpx101); assertThat(cache.getAttributes()).isNotNull(); assertThat(cache.getAttributes()).hasSize(10); } public void testOC() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.oc5952_gpx); final Geocache cache = caches.get(0); assertThat(cache.getGeocode()).isEqualTo("OC5952"); assertThat(cache.getType()).isEqualTo(CacheType.TRADITIONAL); assertThat(cache.isArchived()).isEqualTo(false); assertThat(cache.isDisabled()).isEqualTo(false); assertThat(cache.getName()).isEqualTo("Die Schatzinsel / treasure island"); assertThat(cache.getOwnerDisplayName()).isEqualTo("Die unbesiegbaren Geo - Geparden"); assertThat(cache.getOwnerUserId()).isEqualTo("Die unbesiegbaren Geo - Geparden"); assertThat(cache.getSize()).isEqualTo(CacheSize.SMALL); assertThat(cache.getDifficulty()).isEqualTo(1.0f); assertThat(cache.getTerrain()).isEqualTo(4.0f); assertThat(cache.getLocation()).isEqualTo("Baden-Württemberg, Germany"); assertThat(cache.getShortDescription()).isEqualTo("Ein alter Kindheitstraum, ein Schatz auf einer unbewohnten Insel. A old dream of my childhood, a treasure on a lonely is"); assertThat(cache.getCoords()).isEqualTo(new Geopoint(48.85968, 9.18740)); assertThat(cache.isReliableLatLon()).isTrue(); } public void testGc31j2h() throws IOException, ParserException { removeCacheCompletely("GC31J2H"); final List<Geocache> caches = readGPX10(R.raw.gc31j2h); assertThat(caches).hasSize(1); final Geocache cache = caches.get(0); assertGc31j2h(cache); assertThat(caches.get(0)).isSameAs(cache); // no waypoints without importing waypoint file assertThat(cache.getWaypoints()).isEmpty(); assertThat(cache.isReliableLatLon()).isTrue(); } public void testGc31j2hWpts() throws IOException, ParserException { removeCacheCompletely("GC31J2H"); final List<Geocache> caches = readGPX10(R.raw.gc31j2h, R.raw.gc31j2h_wpts); assertThat(caches).hasSize(1); final Geocache cache = caches.get(0); assertGc31j2h(cache); assertGc31j2hWaypoints(cache); } private static void checkWaypointType(final Collection<Geocache> caches, final String geocode, final int wpIndex, final WaypointType waypointType) { for (final Geocache cache : caches) { if (cache.getGeocode().equals(geocode)) { final List<Waypoint> waypoints = cache.getWaypoints(); assertThat(waypoints).isNotEmpty(); final Waypoint waypoint = waypoints.get(wpIndex); assertThat(waypoint).isNotNull(); assertThat(waypoint.getWaypointType()).isEqualTo(waypointType); return; } } fail("could not find cache with geocode " + geocode); } public void testRenamedWaypointTypes() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.renamed_waypoints, R.raw.renamed_waypoints_wpts); assertThat(caches).hasSize(25); checkWaypointType(caches, "GC3NBDE", 1, WaypointType.STAGE); // multi waypoint (now "physical stage") checkWaypointType(caches, "GC16CBG", 1, WaypointType.PUZZLE); // mystery waypoint (now "virtual stage") } public void testGc31j2hWptsWithoutCache() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.gc31j2h_wpts); assertThat(caches).isEmpty(); } public void testGc3abcd() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.gc3abcd); assertThat(caches.size()).isEqualTo(1); final Geocache gc3abcd = caches.get(0); assertThat(gc3abcd.getGeocode()).isEqualTo("GC3ABCD"); final List<Waypoint> waypoints = gc3abcd.getWaypoints(); assertThat(waypoints.size()).isEqualTo(2); } private static void assertGc31j2h(final Geocache cache) { assertThat(cache.getGeocode()).isEqualTo("GC31J2H"); assertThat(cache.getName()).isEqualTo("Hockenheimer City-Brunnen"); assertThat(cache.getShortDescription()).startsWith("Kurzer informativer Multi entlang der Brunnen"); assertThat(cache.getDescription()).startsWith("Cachemobile können kostenfrei am Messplatz geparkt werden."); assertThat(cache.hasTrackables()).isTrue(); assertEquals(2.0f, cache.getDifficulty(), 0.01f); assertEquals(1.0f, cache.getTerrain(), 0.01f); final Geopoint refCoordinates = new Geopoint("N 49° 19.122", "E 008° 32.739"); assertThat(cache.getCoords()).isEqualTo(refCoordinates); assertThat(cache.getOwnerDisplayName()).isEqualTo("vptsz"); assertThat(cache.getOwnerUserId()).isEqualTo("vptsz"); assertThat(cache.getSize()).isEqualTo(CacheSize.SMALL); assertThat(cache.getType()).isEqualTo(CacheType.MULTI); assertThat(cache.isArchived()).isFalse(); assertThat(cache.isDisabled()).isFalse(); assertThat(cache.isEventCache()).isFalse(); assertThat(cache.isPremiumMembersOnly()).isFalse(); assertThat(cache.isOwner()).isFalse(); assertThat(cache.isFound()).isTrue(); assertThat(cache.getHint()).isEqualTo("Station3: Der zerbrochene Stein zählt doppelt.\nFinal: Oben neben dem Tor"); // logs assertThat(cache.getLogs()).hasSize(6); final LogEntry log = cache.getLogs().get(5); assertThat(log.author).isEqualTo("Geoteufel"); assertThat(log.date).isEqualTo(parseTime("2011-09-11T07:00:00Z")); assertThat(log.found).isEqualTo(-1); assertThat(log.log).isEqualTo("Sehr schöne Runde und wir haben wieder etwas Neues über Hockenheim gelernt. Super Tarnung.\nTFTC, Geoteufel"); assertThat(log.isOwn()).isFalse(); assertThat(log.getDisplayText()).isEqualTo(log.log); assertThat(CalendarUtils.daysSince(log.date)).isGreaterThan(700); // following info is not contained in pocket query gpx file assertThat(cache.getAttributes()).isEmpty(); } private static long parseTime(final String time) { try { return LOG_DATE_FORMAT.parse(time).getTime(); } catch (final ParseException e) { return 0; } } private static void assertGc31j2hWaypoints(final Geocache cache) { assertThat(cache.getWaypoints()).isNotNull(); assertThat(cache.getWaypoints()).hasSize(2); Waypoint wp = cache.getWaypoints().get(0); assertThat(wp.getGeocode()).isEqualTo("GC31J2H"); assertThat(wp.getPrefix()).isEqualTo("00"); assertThat(wp.getLookup()).isEqualTo("---"); assertThat(wp.getName()).isEqualTo("Parkplatz"); assertThat(wp.getNote()).isEqualTo("Kostenfreies Parken (je nach Parkreihe Parkscheibe erforderlich)"); assertThat(wp.getWaypointType()).isEqualTo(WaypointType.PARKING); Geopoint waypointCoords = wp.getCoords(); assertThat(waypointCoords).isNotNull(); assert waypointCoords != null; // eclipse null analysis assertEquals(49.317517, waypointCoords.getLatitude(), 0.000001); assertEquals(8.545083, waypointCoords.getLongitude(), 0.000001); wp = cache.getWaypoints().get(1); assertThat(wp.getGeocode()).isEqualTo("GC31J2H"); assertThat(wp.getPrefix()).isEqualTo("S1"); assertThat(wp.getLookup()).isEqualTo("---"); assertThat(wp.getName()).isEqualTo("Station 1"); assertThat(wp.getNote()).isEqualTo("Ein zweiter Wegpunkt, der nicht wirklich existiert sondern nur zum Testen gedacht ist."); assertThat(wp.getWaypointType()).isEqualTo(WaypointType.STAGE); waypointCoords = wp.getCoords(); assertThat(waypointCoords).isNotNull(); assert waypointCoords != null; // eclipse null analysis assertEquals(49.317500, waypointCoords.getLatitude(), 0.000001); assertEquals(8.545100, waypointCoords.getLongitude(), 0.000001); } private List<Geocache> readGPX10(@RawRes final int... resourceIds) throws IOException, ParserException { final GPX10Parser parser = new GPX10Parser(getTemporaryListId()); return readVersionedGPX(parser, resourceIds); } private List<Geocache> readGPX11(final int... resourceIds) throws IOException, ParserException { final GPX11Parser parser = new GPX11Parser(getTemporaryListId()); return readVersionedGPX(parser, resourceIds); } private List<Geocache> readVersionedGPX(final GPXParser parser, @RawRes final int... resourceIds) throws IOException, ParserException { final Set<String> result = new HashSet<>(); for (final int resourceId : resourceIds) { final InputStream instream = getResourceStream(resourceId); try { final Collection<Geocache> caches = parser.parse(instream, null); assertThat(caches).isNotNull(); for (final Geocache cache : caches) { result.add(cache.getGeocode()); } } finally { IOUtils.closeQuietly(instream); } } // reload caches, because the parser only returns the minimum version of each cache return new ArrayList<>(DataStore.loadCaches(result, LoadFlags.LOAD_ALL_DB_ONLY)); } public static void testParseDateWithFractionalSeconds() throws ParseException { // was experienced in GSAK file final String dateString = "2011-08-13T02:52:18.103Z"; GPXParser.parseDate(dateString); } public static void testParseDateWithHugeFraction() throws ParseException { // see issue 821 final String dateString = "2011-11-07T00:00:00.0000000-07:00"; GPXParser.parseDate(dateString); } public void testSelfmadeGPXWithoutGeocodes() throws Exception { final List<Geocache> caches = readGPX11(R.raw.no_connector); assertThat(caches).hasSize(13); } public void testTexasChallenge2012() throws Exception { final List<Geocache> caches = readGPX10(R.raw.challenge); // previously these caches overwrote each other during parsing assertThat(caches).hasSize(130); } public void testGeoToad() throws Exception { final List<Geocache> caches = readGPX10(R.raw.geotoad); assertThat(caches).hasSize(2); final List<String> codes = new ArrayList<>(); for (final Geocache cache : caches) { codes.add(cache.getGeocode()); } assertThat(codes).contains("GC2KN6K", "GC1T3MK"); } public void testLazyLogLoading() throws IOException, ParserException { // this test should be in CacheTest, but it is easier to create here due to the GPX import abilities final String geocode = "GC31J2H"; removeCacheCompletely(geocode); final List<Geocache> caches = readGPX10(R.raw.lazy); assertThat(caches).hasSize(1); DataStore.removeAllFromCache(); // load only the minimum cache, it has several members missing final Geocache minimalCache = DataStore.loadCache(geocode, EnumSet.of(LoadFlag.DB_MINIMAL)); assert minimalCache != null; assertThat(minimalCache).isNotNull(); // now check that we load lazy members on demand assertThat(minimalCache.getAttributes()).isNotEmpty(); assertThat(minimalCache.getLogs()).isNotEmpty(); removeCacheCompletely(geocode); } public void testDuplicateImport() throws IOException, ParserException { final String geocode = "GC31J2H"; removeCacheCompletely(geocode); // first import List<Geocache> caches = readGPX10(R.raw.lazy); assertThat(caches).hasSize(1); assertThat(caches.get(0).getLogs()).hasSize(6); // second import caches = readGPX10(R.raw.lazy); assertThat(caches).hasSize(1); assertThat(caches.get(0).getLogs()).hasSize(6); removeCacheCompletely(geocode); } public void testWaymarking() throws Exception { final List<Geocache> caches = readGPX10(R.raw.waymarking_gpx); assertThat(caches).hasSize(1); final Geocache waymark = caches.get(0); assertThat(waymark).isNotNull(); assertThat(waymark.getGeocode()).isEqualTo("WM7BM7"); assertThat(waymark.getName()).isEqualTo("Roman water pipe Kornwestheim"); assertThat(waymark.getUrl()).isNotEmpty(); // connector must be able to create it assertThat(waymark.getType()).isEqualTo(CacheType.UNKNOWN); assertThat(waymark.getSize()).isEqualTo(CacheSize.UNKNOWN); } /** * This one uses geocodes where the first character is actually a digit, not a character */ public void testGCTour() throws Exception { final List<Geocache> caches = readGPX10(R.raw.gctour_gpx); assertThat(caches).hasSize(54); } /** * fake GPX with bad cache id, must be detected by GPX import */ public void testGDAKBadCacheId() throws Exception { final List<Geocache> caches = readGPX10(R.raw.gc31j2h2_bad_cacheid); assertThat(caches).hasSize(1); final Geocache cache = caches.get(0); assertThat(String.valueOf(GCConstants.gccodeToGCId(cache.getGeocode()))).isEqualTo(cache.getCacheId()); } private Geocache getFirstCache(@RawRes final int gpxResourceId) throws IOException, ParserException { final List<Geocache> caches = readGPX10(gpxResourceId); assertThat(caches).isNotNull(); assertThat(caches).hasSize(1); return caches.get(0); } public void testGsakFavPoints() throws IOException, ParserException { final Geocache cache = getFirstCache(R.raw.gc3t1xg_gsak); assertThat(cache.getFavoritePoints()).isEqualTo(258); } public void testGsakPersonalNote() throws IOException, ParserException { final Geocache cache = getFirstCache(R.raw.gc3t1xg_gsak); assertThat(cache.getPersonalNote()).isEqualTo("Personal Note Test"); } public void testGsakPremium() throws IOException, ParserException { final Geocache cache = getFirstCache(R.raw.gc3t1xg_gsak); assertThat(cache.isPremiumMembersOnly()).isTrue(); } public void testGsakModified() throws IOException, ParserException { final Geocache unmodified = getFirstCache(R.raw.gc3t1xg_gsak); assertThat(unmodified.hasUserModifiedCoords()).isFalse(); final Geocache cache = getFirstCache(R.raw.modified_gsak); assertThat(cache.hasUserModifiedCoords()).isTrue(); assertThat(cache.getWaypoints()).hasSize(1); final Waypoint waypoint = cache.getWaypoints().get(0); assertThat(waypoint.getWaypointType()).isEqualTo(WaypointType.ORIGINAL); assertThat(waypoint.getCoords()).isEqualTo(new Geopoint("51.223032", "6.026147")); assertThat(cache.getCoords()).isEqualTo(new Geopoint("51.223033", "6.027767")); } public void testGPXMysteryType() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.tc2012); final Geocache mystery = getCache(caches, "U017"); assertThat(mystery).isNotNull(); assert mystery != null; assertThat(mystery.getType()).isEqualTo(CacheType.MYSTERY); } private static Geocache getCache(final List<Geocache> caches, final String geocode) { for (final Geocache geocache : caches) { if (geocache.getName().equals(geocode)) { return geocache; } } return null; } public void testLabCaches() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.giga_lab_caches); assertThat(caches).hasSize(10); final Geocache lab = getCache(caches, "01_Munich Olympic Walk Of Stars_Updated-Project MUNICH2014 - Mia san Giga! Olympiapark"); assertThat(lab).isNotNull(); // parse labs as virtual for the time being assertThat(lab.getType()).isEqualTo(CacheType.VIRTUAL); // no difficulty and terrain rating assertThat(lab.getTerrain()).isEqualTo(0); assertThat(lab.getDifficulty()).isEqualTo(0); // geocodes are just big hashes assertThat(lab.getGeocode()).isEqualTo("01_Munich Olympic Walk Of Stars_Updated-Project MUNICH2014 - Mia san Giga! Olympiapark".toUpperCase(Locale.US)); // other normal cache properties assertThat(lab.getName()).isEqualTo("01_Munich Olympic Walk Of Stars_Updated-Project MUNICH2014 - Mia san Giga! Olympiapark"); assertThat(lab.getShortDescription()).isEqualTo("01_Munich Olympic Walk Of Stars_Updated (Giga! Olympia Park)-Project MUNICH2014 - Mia san Giga! Olympiapark"); assertThat(lab.getDescription()).startsWith("DEU:"); final IConnector unknownConnector = ConnectorFactory.getConnector("ABC123"); assertThat(ConnectorFactory.getConnector(lab)).isSameAs(unknownConnector); } public void testGSAKGeocode() throws IOException, ParserException { final List<Geocache> caches = readGPX10(R.raw.liptov_gpx); assertThat(caches).hasSize(1); final Geocache cache = caches.get(0); // make sure we get the right geocode, even though the name seems to start with a code, too assertThat(cache.getGeocode()).isEqualTo("GC3A5N1"); } public void testTerraCachingOldCollection() throws Exception { final List<Geocache> caches = readGPX11(R.raw.terracaching_gpx); assertThat(caches).hasSize(55); Collections.sort(caches, new GeocodeComparator()); final Geocache cache = caches.get(0); assertThat(cache.getGeocode()).isEqualTo("TCNZ"); assertThat(cache.getName()).isEqualTo("Het Witte Veen"); assertThat(cache.getOwnerDisplayName()).isEqualTo("Het Zwarte Schaap"); assertThat(cache.getType()).isEqualTo(CacheType.MULTI); assertThat(cache.getSize()).isEqualTo(CacheSize.SMALL); } public void testTerraCaching() throws Exception { final List<Geocache> caches = readGPX11(R.raw.tcehl_gpx); assertThat(caches).hasSize(1); final Geocache cache = caches.get(0); assertThat(cache.getGeocode()).isEqualTo("TCEHL"); assertThat(cache.getName()).isEqualTo("Joseph Schoeninger"); assertThat(cache.getOwnerDisplayName()).isEqualTo("Berengarius"); assertThat(cache.getType()).isEqualTo(CacheType.VIRTUAL); assertThat(cache.getLocation()).isEqualTo("Baden-Wurttemberg, Germany"); assertThat(cache.getUrl()).isEqualTo("http://www.terracaching.com/Cache/TCEHL"); assertThat(cache.getDescription()).startsWith("<b> Hier ruht </b>"); assertThat(cache.getHint()).isEmpty(); // make sure we don't parse the standard "GC_WayPoint1" assertThat(cache.getShortDescription()).isEmpty(); } public void testTerraCachingLogs() throws Exception { final List<Geocache> caches = readGPX11(R.raw.tcavl_gpx); assertThat(caches).hasSize(1); final List<LogEntry> logs = caches.get(0).getLogs(); assertThat(logs).hasSize(6); final LogEntry log = logs.get(0); assertThat(log.author).isEqualTo("toubiV"); assertThat(log.getType()).isEqualTo(LogType.FOUND_IT); assertThat(log.log).startsWith("Visited the nearby Geocache"); assertThat(log.log).endsWith("Nice location."); assertThat(log.date).isNotEqualTo(0); } public void testTerraCachingMulti() throws Exception { final List<Geocache> caches = readGPX11(R.raw.tc99un_gpx); assertThat(caches).hasSize(1); final Geocache cache = caches.get(0); assertThat(cache.getShortDescription()).isEmpty(); final List<Waypoint> waypoints = cache.getWaypoints(); assertThat(waypoints).hasSize(4); final Waypoint waypoint = waypoints.get(0); assertThat(waypoint.getNote()).startsWith("75 feet due south of large shoreside"); } public void testOpenCachingAttributes() throws Exception { final List<Geocache> caches = readGPX10(R.raw.oc1310f_gpx); assertThat(caches).hasSize(1); final Geocache cache = caches.get(0); final List<String> attributes = cache.getAttributes(); assertThat(attributes).hasSize(2); } }