package cgeo.geocaching.files;
import static org.assertj.core.api.Java6Assertions.assertThat;
import cgeo.geocaching.SearchResult;
import cgeo.geocaching.connector.ConnectorFactory;
import cgeo.geocaching.connector.gc.GCConnector;
import cgeo.geocaching.connector.gc.GCConstants;
import cgeo.geocaching.enumerations.CacheType;
import cgeo.geocaching.enumerations.LoadFlags;
import cgeo.geocaching.models.Geocache;
import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.settings.TestSettings;
import cgeo.geocaching.storage.DataStore;
import cgeo.geocaching.test.AbstractResourceInstrumentationTestCase;
import cgeo.geocaching.test.R;
import cgeo.geocaching.utils.DisposableHandler;
import cgeo.geocaching.utils.Log;
import android.net.Uri;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
public class GPXImporterTest extends AbstractResourceInstrumentationTestCase {
private TestHandler importStepHandler;
private TestHandler progressHandler;
private int listId;
private File tempDir;
private boolean importCacheStaticMaps;
private boolean importWpStaticMaps;
private HandlerThread serviceThread;
public void testGetWaypointsFileNameForGpxFile() throws IOException {
final String[] gpxFiles = { "1234567.gpx", "1.gpx", "1234567.9.gpx",
"1234567.GPX", "gpx.gpx.gpx", ".gpx",
"1234567_query.gpx", "123-4.gpx", "123(5).gpx" };
final String[] wptsFiles = { "1234567-wpts.gpx", "1-wpts.gpx", "1234567.9-wpts.gpx",
"1234567-wpts.GPX", "gpx.gpx-wpts.gpx", "-wpts.gpx",
"1234567_query-wpts.gpx", "123-wpts-4.gpx", "123-wpts(5).gpx" };
for (int i = 0; i < gpxFiles.length; i++) {
final String gpxFileName = gpxFiles[i];
final String wptsFileName = wptsFiles[i];
final File gpx = new File(tempDir, gpxFileName);
final File wpts = new File(tempDir, wptsFileName);
// the files need to exist - we create them
assertThat(gpx.createNewFile()).isTrue();
assertThat(wpts.createNewFile()).isTrue();
// the "real" method check
assertThat(GPXImporter.getWaypointsFileNameForGpxFile(gpx)).isEqualTo(wptsFileName);
// they also need to be deleted, because of case sensitive tests that will not work correct on case insensitive file systems
FileUtils.deleteQuietly(gpx);
FileUtils.deleteQuietly(wpts);
}
final File gpx1 = new File(tempDir, "abc.gpx");
assertThat(GPXImporter.getWaypointsFileNameForGpxFile(gpx1)).isNull();
}
public void testImportGpx() throws IOException {
final String geocode = "GC31J2H";
removeCacheCompletely(geocode);
final File gc31j2h = new File(tempDir, "gc31j2h.gpx");
copyResourceToFile(R.raw.gc31j2h, gc31j2h);
final ImportGpxFileThread importThread = new ImportGpxFileThread(gc31j2h, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertThat(importStepHandler.messages).hasSize(4);
final Iterator<Message> iMsg = importStepHandler.messages.iterator();
assertThat(iMsg.next().what).isEqualTo(GPXImporter.IMPORT_STEP_START);
assertThat(iMsg.next().what).isEqualTo(GPXImporter.IMPORT_STEP_READ_FILE);
assertThat(iMsg.next().what).isEqualTo(GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS);
assertThat(iMsg.next().what).isEqualTo(GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getWaypoints()).isEmpty();
}
public void testImportOcGpx() throws IOException {
final String geocode = "OCDDD2";
removeCacheCompletely(geocode);
final File ocddd2 = new File(tempDir, "ocddd2.gpx");
copyResourceToFile(R.raw.ocddd2, ocddd2);
final ImportGpxFileThread importThread = new ImportGpxFileThread(ocddd2, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertThat(importStepHandler.messages).hasSize(4);
final Iterator<Message> iMsg = importStepHandler.messages.iterator();
assertThat(iMsg.next().what).isEqualTo(GPXImporter.IMPORT_STEP_START);
assertThat(iMsg.next().what).isEqualTo(GPXImporter.IMPORT_STEP_READ_FILE);
assertThat(iMsg.next().what).isEqualTo(GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS);
assertThat(iMsg.next().what).isEqualTo(GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getWaypoints()).as("Number of imported waypoints").hasSize(4);
}
private void runImportThread(final AbstractImportThread importThread) {
importThread.start();
try {
importThread.join();
} catch (final InterruptedException e) {
Log.e("GPXImporterTest.runImportThread", e);
}
importStepHandler.sendEmptyMessage(TestHandler.TERMINATION_MESSAGE); // send End Message
importStepHandler.waitForCompletion();
}
public void testImportGpxWithWaypoints() throws IOException {
final File gc31j2h = new File(tempDir, "gc31j2h.gpx");
copyResourceToFile(R.raw.gc31j2h, gc31j2h);
copyResourceToFile(R.raw.gc31j2h_wpts, new File(tempDir, "gc31j2h-wpts.gpx"));
final ImportGpxFileThread importThread = new ImportGpxFileThread(gc31j2h, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_READ_WPT_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache("GC31J2H", LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getWaypoints()).hasSize(2);
}
public void testImportGpxWithLowercaseNames() throws IOException {
final File tc2012 = new File(tempDir, "tc2012.gpx");
copyResourceToFile(R.raw.tc2012, tc2012);
final ImportGpxFileThread importThread = new ImportGpxFileThread(tc2012, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache("AID1", LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getName()).isEqualTo("First Aid Station #1");
}
private void assertImportStepMessages(final int... importSteps) {
for (int i = 0; i < Math.min(importSteps.length, importStepHandler.messages.size()); i++) {
assertThat(importStepHandler.messages.get(i).what).isEqualTo(importSteps[i]);
}
assertThat(importStepHandler.messages).hasSize(importSteps.length);
}
public void testImportLoc() throws IOException {
final File oc5952 = new File(tempDir, "oc5952.loc");
copyResourceToFile(R.raw.oc5952_loc, oc5952);
final ImportLocFileThread importThread = new ImportLocFileThread(oc5952, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache("OC5952", LoadFlags.LOAD_CACHE_OR_DB);
assertCacheProperties(cache);
}
private static void assertCacheProperties(final Geocache cache) {
assertThat(cache).isNotNull();
assertThat(cache.getLocation().startsWith(",")).isFalse();
assertThat(cache.isReliableLatLon()).isTrue();
if (GCConnector.getInstance().equals(ConnectorFactory.getConnector(cache))) {
assertThat(String.valueOf(GCConstants.gccodeToGCId(cache.getGeocode()))).isEqualTo(cache.getCacheId());
}
}
public void testImportGpxError() throws IOException {
final File gc31j2h = new File(tempDir, "gc31j2h.gpx");
copyResourceToFile(R.raw.gc31j2h_err, gc31j2h);
final ImportGpxFileThread importThread = new ImportGpxFileThread(gc31j2h, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_FINISHED_WITH_ERROR);
}
public void testImportGpxCancel() throws IOException {
final File gc31j2h = new File(tempDir, "gc31j2h.gpx");
copyResourceToFile(R.raw.gc31j2h, gc31j2h);
progressHandler.dispose();
final ImportGpxFileThread importThread = new ImportGpxFileThread(gc31j2h, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_CANCELED);
}
public void testImportGpxAttachment() {
final String geocode = "GC31J2H";
removeCacheCompletely(geocode);
final Uri uri = Uri.parse("android.resource://cgeo.geocaching.test/raw/gc31j2h");
final ImportGpxAttachmentThread importThread = new ImportGpxAttachmentThread(uri, getInstrumentation().getContext().getContentResolver(), listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getWaypoints()).isEmpty();
}
public void testImportGpxZip() throws IOException {
final String geocode = "GC31J2H";
removeCacheCompletely(geocode);
final File pq7545915 = new File(tempDir, "7545915.zip");
copyResourceToFile(R.raw.pq7545915, pq7545915);
final ImportGpxZipFileThread importThread = new ImportGpxZipFileThread(pq7545915, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_READ_WPT_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getWaypoints()).hasSize(1); // this is the original pocket query result without test waypoint
}
public void testImportGpxZipErr() throws IOException {
final File pqError = new File(tempDir, "pq_error.zip");
copyResourceToFile(R.raw.pq_error, pqError);
final ImportGpxZipFileThread importThread = new ImportGpxZipFileThread(pqError, listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_FINISHED_WITH_ERROR);
}
public void testImportGpxZipAttachment() {
final String geocode = "GC31J2H";
removeCacheCompletely(geocode);
final Uri uri = Uri.parse("android.resource://cgeo.geocaching.test/raw/pq7545915");
final ImportGpxZipAttachmentThread importThread = new ImportGpxZipAttachmentThread(uri, getInstrumentation().getContext().getContentResolver(), listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_READ_WPT_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getWaypoints()).hasSize(1); // this is the original pocket query result without test waypoint
}
public void testImportGpxZipAttachmentCp437() {
final String geocode = "GC448A";
removeCacheCompletely(geocode);
assertThat(R.raw.pq_cp437).isNotEqualTo(0); // avoid lint warning, fake usage of below resource
final Uri uri = Uri.parse("android.resource://cgeo.geocaching.test/raw/pq_cp437");
final ImportGpxZipAttachmentThread importThread = new ImportGpxZipAttachmentThread(uri, getInstrumentation().getContext().getContentResolver(), listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getWaypoints()).hasSize(0); // this is the original pocket query result without test waypoint
assertThat(importThread.getSourceDisplayName()).isEqualTo("17157344_Großer Ümlaut Täst.gpx");
}
public void testImportGpxZipAttachmentEntities() {
final String geocode = "GC448A";
removeCacheCompletely(geocode);
assertThat(R.raw.pq_entities).isNotEqualTo(0); // avoid lint warning, fake usage of below resource
final Uri uri = Uri.parse("android.resource://cgeo.geocaching.test/raw/pq_entities");
final ImportGpxZipAttachmentThread importThread = new ImportGpxZipAttachmentThread(uri, getInstrumentation().getContext().getContentResolver(), listId, importStepHandler, progressHandler);
runImportThread(importThread);
assertImportStepMessages(GPXImporter.IMPORT_STEP_START, GPXImporter.IMPORT_STEP_READ_FILE, GPXImporter.IMPORT_STEP_STORE_STATIC_MAPS, GPXImporter.IMPORT_STEP_FINISHED);
final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB);
assert cache != null;
assertThat(cache).isNotNull();
assertCacheProperties(cache);
assertThat(cache.getWaypoints()).hasSize(0); // this is the original pocket query result without test waypoint
assertThat(importThread.getSourceDisplayName()).isEqualTo("17157285_Großer Ümlaut Täst.gpx");
}
static class TestHandler extends DisposableHandler {
private final List<Message> messages = new ArrayList<>();
private long lastMessage = System.currentTimeMillis();
private boolean receivedTerminationMessage = false;
private static final int TERMINATION_MESSAGE = 9999;
TestHandler(final Looper serviceLooper) {
super(serviceLooper);
}
@Override
public synchronized void handleRegularMessage(final Message msg) {
final Message msg1 = Message.obtain();
msg1.copyFrom(msg);
if (msg1.what == TERMINATION_MESSAGE) {
receivedTerminationMessage = true;
} else {
messages.add(msg1);
}
lastMessage = System.currentTimeMillis();
notifyAll();
}
public synchronized void waitForCompletion(final long milliseconds) {
try {
while ((System.currentTimeMillis() - lastMessage <= milliseconds) && !hasTerminatingMessage()) {
wait(milliseconds);
}
} catch (final InterruptedException e) {
// intentionally left blank
}
}
private boolean hasTerminatingMessage() {
return receivedTerminationMessage;
}
public void waitForCompletion() {
// wait a maximum of 10 seconds
waitForCompletion(10000);
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
serviceThread = new HandlerThread("[" + getClass().getSimpleName() + "Thread]");
serviceThread.start();
final Looper serviceLooper = serviceThread.getLooper();
importStepHandler = new TestHandler(serviceLooper);
progressHandler = new TestHandler(serviceLooper);
final String globalTempDir = System.getProperty("java.io.tmpdir");
assertThat(StringUtils.isNotBlank(globalTempDir)).overridingErrorMessage("java.io.tmpdir is not defined").isTrue();
tempDir = new File(globalTempDir, "cgeogpxesTest");
cgeo.geocaching.utils.FileUtils.mkdirs(tempDir);
assertThat(tempDir).overridingErrorMessage("Could not create directory %s", tempDir.getPath()).exists();
// workaround to get storage initialized
DataStore.getAllHistoryCachesCount();
listId = DataStore.createList("cgeogpxesTest");
importCacheStaticMaps = Settings.isStoreOfflineMaps();
TestSettings.setStoreOfflineMaps(true);
importWpStaticMaps = Settings.isStoreOfflineWpMaps();
TestSettings.setStoreOfflineWpMaps(true);
}
@Override
protected void tearDown() throws Exception {
final SearchResult search = DataStore.getBatchOfStoredCaches(null, CacheType.ALL, listId);
final List<Geocache> cachesInList = new ArrayList<>();
cachesInList.addAll(search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB));
DataStore.markDropped(cachesInList);
DataStore.removeList(listId);
FileUtils.deleteDirectory(tempDir);
TestSettings.setStoreOfflineMaps(importCacheStaticMaps);
TestSettings.setStoreOfflineWpMaps(importWpStaticMaps);
serviceThread.quit();
super.tearDown();
}
}