Skip to content

Commit

Permalink
Add tests for adding local tile dources and remote PMTiles
Browse files Browse the repository at this point in the history
  • Loading branch information
simonpoole committed Sep 9, 2023
1 parent 9b81e68 commit 5027dde
Show file tree
Hide file tree
Showing 4 changed files with 335 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
package de.blau.android.layer;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import com.orhanobut.mockwebserverplus.MockWebServerPlus;

import android.app.Instrumentation;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiScrollable;
import androidx.test.uiautomator.UiSelector;
import androidx.test.uiautomator.Until;
import de.blau.android.App;
import de.blau.android.JavaResources;
import de.blau.android.LayerUtils;
import de.blau.android.Logic;
import de.blau.android.Main;
import de.blau.android.Map;
import de.blau.android.MockTileServer;
import de.blau.android.PMTilesDispatcher;
import de.blau.android.R;
import de.blau.android.TestUtils;
import de.blau.android.TileDispatcher;
import de.blau.android.gpx.GpxTest;
import de.blau.android.layer.data.MapOverlay;
import de.blau.android.osm.BoundingBox;
import de.blau.android.osm.Node;
import de.blau.android.osm.StorageDelegator;
import de.blau.android.osm.Way;
import de.blau.android.prefs.AdvancedPrefDatabase;
import de.blau.android.prefs.Preferences;
import de.blau.android.resources.TileLayerDatabase;
import de.blau.android.resources.TileLayerSource;
import de.blau.android.views.layers.MapTilesLayer;
import okhttp3.mockwebserver.MockWebServer;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class LayerDialogCustomImageryTest {

public static final int EXTENT_BUTTON = 1;
public static final int MENU_BUTTON = 3;

AdvancedPrefDatabase prefDB = null;
Main main = null;
UiDevice device = null;
Map map = null;
Instrumentation instrumentation = null;

@Rule
public ActivityTestRule<Main> mActivityRule = new ActivityTestRule<>(Main.class);

/**
* Pre-test setup
*/
@Before
public void setup() {
instrumentation = InstrumentationRegistry.getInstrumentation();
instrumentation.getTargetContext().deleteDatabase(TileLayerDatabase.DATABASE_NAME);
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
main = mActivityRule.getActivity();
TestUtils.grantPermissons(device);
LayerUtils.removeImageryLayers(main);
Preferences prefs = new Preferences(main);
map = main.getMap();
map.setPrefs(main, prefs);
TestUtils.dismissStartUpDialogs(device, main);
TestUtils.stopEasyEdit(main);
}

/**
* Post-test teardown
*/
@After
public void teardown() {

}

/**
* Test adding and then modifiy custom imagery
*/
@Test
public void customImagery() {
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/layers", true));
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/add", true));
TestUtils.scrollTo(main.getString(R.string.layer_add_custom_imagery), false);
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.layer_add_custom_imagery), true));
assertTrue(TestUtils.findText(device, false, main.getString(R.string.add_layer_title)));
UiObject name = device.findObject(new UiSelector().resourceId(device.getCurrentPackageName() + ":id/name"));
try {
name.setText("Custom imagery");
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}
UiObject url = device.findObject(new UiSelector().resourceId(device.getCurrentPackageName() + ":id/url"));
try {
url.setText("https://test/");
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.save_and_set), true));
try (TileLayerDatabase db = new TileLayerDatabase(ApplicationProvider.getApplicationContext())) {
TileLayerSource tls = TileLayerDatabase.getLayerWithUrl(main, db.getReadableDatabase(), "https://test/");
assertNotNull(tls);
assertEquals("Custom imagery", tls.getName());
}
TestUtils.clickText(device, true, main.getString(R.string.done), true, false);
UiObject2 menuButton = TestUtils.getLayerButton(device, "Custom imagery", MENU_BUTTON);
menuButton.clickAndWait(Until.newWindow(), 2000);
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.layer_edit_custom_imagery_configuration), true));
assertTrue(TestUtils.findText(device, false, main.getString(R.string.edit_layer_title)));
url = device.findObject(new UiSelector().resourceId(device.getCurrentPackageName() + ":id/url"));
try {
url.setText("https://test2/");
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.save), true));
try (TileLayerDatabase db = new TileLayerDatabase(ApplicationProvider.getApplicationContext())) {
TileLayerSource tls = TileLayerDatabase.getLayerWithUrl(main, db.getReadableDatabase(), "https://test2/");
assertNotNull(tls);
}
}

/**
* Test adding a MBT source
*/
@Test
public void customImageryMBT() {
final String fileName = "ersatz_background.mbt";
try {
JavaResources.copyFileFromResources(main, fileName, null, "/");
} catch (IOException e) {
fail("copying " + fileName + " failed");
}
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/layers", true));
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/add", true));
TestUtils.scrollTo(main.getString(R.string.layer_add_custom_imagery), false);
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.layer_add_custom_imagery), true));
assertTrue(TestUtils.findText(device, false, main.getString(R.string.add_layer_title)));
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/file_button", true));
TestUtils.selectFile(device, main, null, fileName, true);
assertTrue(TestUtils.findText(device, false, "Vespucci Test"));
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.save_and_set), true));
assertTrue(TestUtils.textGone(device, main.getString(R.string.layer_add_custom_imagery), 2000));
assertTrue(TestUtils.findText(device, false, "Vespucci Test")); // layer dialog
try (TileLayerDatabase db = new TileLayerDatabase(ApplicationProvider.getApplicationContext())) {
TileLayerSource tls = TileLayerDatabase.getLayer(main, db.getReadableDatabase(), "VESPUCCITEST");
assertNotNull(tls);
assertEquals(TileLayerSource.TYPE_TMS, tls.getType());
assertEquals(TileLayerSource.TileType.BITMAP, tls.getTileType());
}
}

/**
* Test adding a (local) PMTiles source
*/
@Test
public void customImageryLocalPMTiles() {
final String fileName = "protomaps(vector)ODbL_firenze.pmtiles";
try {
JavaResources.copyFileFromResources(main, fileName, null, "/");
} catch (IOException e) {
fail("copying " + fileName + " failed");
}
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/layers", true));
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/add", true));
TestUtils.scrollTo(main.getString(R.string.layer_add_custom_imagery), false);
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.layer_add_custom_imagery), true));
assertTrue(TestUtils.findText(device, false, main.getString(R.string.add_layer_title)));
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/file_button", true));
TestUtils.selectFile(device, main, null, fileName, true);
assertTrue(TestUtils.findText(device, false, "protomaps 2023-01"));
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.save_and_set), true));
assertTrue(TestUtils.textGone(device, main.getString(R.string.layer_add_custom_imagery), 2000));
assertTrue(TestUtils.findText(device, false, "protomaps 2023-01")); // layer dialog
try (TileLayerDatabase db = new TileLayerDatabase(ApplicationProvider.getApplicationContext())) {
TileLayerSource tls = TileLayerDatabase.getLayer(main, db.getReadableDatabase(), "PROTOMAPS20230118T074939Z");
assertNotNull(tls);
assertEquals(TileLayerSource.TYPE_PMT_3, tls.getType());
assertEquals(TileLayerSource.TileType.MVT, tls.getTileType());
}
}

/**
* Test adding a (remote) PMTiles source
*/
@Test
public void customImageryRemotePMTiles() {
final String fileName = "protomaps(vector)ODbL_firenze.pmtiles";
try (MockWebServer tileServer = new MockWebServer()) {

PMTilesDispatcher tileDispatcher = new PMTilesDispatcher(main, fileName);
tileServer.setDispatcher(tileDispatcher);

String tileUrl = tileServer.url("/").toString() + "firenze.pmtiles";

assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/layers", true));
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/add", true));
TestUtils.scrollTo(main.getString(R.string.layer_add_custom_imagery), false);
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.layer_add_custom_imagery), true));
assertTrue(TestUtils.findText(device, false, main.getString(R.string.add_layer_title)));
UiObject url = device.findObject(new UiSelector().resourceId(device.getCurrentPackageName() + ":id/url"));
try {
url.setText(tileUrl);
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.save), true));

assertTrue(TestUtils.findText(device, false, "protomaps 2023-01"));
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.save_and_set), true));
assertTrue(TestUtils.textGone(device, main.getString(R.string.layer_add_custom_imagery), 2000));
assertTrue(TestUtils.findText(device, false, "protomaps 2023-01")); // layer dialog
try (TileLayerDatabase db = new TileLayerDatabase(ApplicationProvider.getApplicationContext())) {
TileLayerSource tls = TileLayerDatabase.getLayer(main, db.getReadableDatabase(), "PROTOMAPS20230118T074939Z");
assertNotNull(tls);
assertEquals(TileLayerSource.TYPE_PMT_3, tls.getType());
assertEquals(TileLayerSource.TileType.MVT, tls.getTileType());
}
} catch (IOException e) {
fail("setting up tiles server with " + fileName + " failed");
}
}
}
48 changes: 1 addition & 47 deletions src/androidTest/java/de/blau/android/layer/LayerDialogTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ public void layerTestModal() {
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.layer_test), true, false));
assertTrue(TestUtils.findText(device, false, main.getString(R.string.tile_fail), 2000, true));
assertTrue(TestUtils.clickText(device, true, main.getString(R.string.done), true, false));
assertTrue(TestUtils.clickText(device, true, main.getString(R.string.done), true, false));
assertTrue(TestUtils.clickText(device, true, main.getString(R.string.done), true, false));
TestUtils.zoomToLevel(device, main, 19);
map.getViewBox().moveTo(map, (int) (8.3642221 * 1E7D), (int) (47.4176842 * 1E7D));
map.invalidate();
Expand Down Expand Up @@ -411,52 +411,6 @@ public void layerFilter() {
assertTrue(TestUtils.clickText(device, true, "Terrain Test", false, false));
}

/**
* Test adding and then modifiy custom imagery
*/
@Test
public void customImagery() {
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/layers", true));
assertTrue(TestUtils.clickResource(device, true, device.getCurrentPackageName() + ":id/add", true));
TestUtils.scrollTo(main.getString(R.string.layer_add_custom_imagery), false);
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.layer_add_custom_imagery), true));
assertTrue(TestUtils.findText(device, false, main.getString(R.string.add_layer_title)));
UiObject name = device.findObject(new UiSelector().resourceId(device.getCurrentPackageName() + ":id/name"));
try {
name.setText("Custom imagery");
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}
UiObject url = device.findObject(new UiSelector().resourceId(device.getCurrentPackageName() + ":id/url"));
try {
url.setText("https://test/");
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.save_and_set), true));
try (TileLayerDatabase db = new TileLayerDatabase(ApplicationProvider.getApplicationContext())) {
TileLayerSource tls = TileLayerDatabase.getLayerWithUrl(main, db.getReadableDatabase(), "https://test/");
assertNotNull(tls);
assertEquals("Custom imagery", tls.getName());
}
TestUtils.clickText(device, true, main.getString(R.string.done), true, false);
UiObject2 menuButton = TestUtils.getLayerButton(device, "Custom imagery", MENU_BUTTON);
menuButton.clickAndWait(Until.newWindow(), 2000);
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.layer_edit_custom_imagery_configuration), true));
assertTrue(TestUtils.findText(device, false, main.getString(R.string.edit_layer_title)));
url = device.findObject(new UiSelector().resourceId(device.getCurrentPackageName() + ":id/url"));
try {
url.setText("https://test2/");
} catch (UiObjectNotFoundException e) {
fail(e.getMessage());
}
assertTrue(TestUtils.clickText(device, false, main.getString(R.string.save), true));
try (TileLayerDatabase db = new TileLayerDatabase(ApplicationProvider.getApplicationContext())) {
TileLayerSource tls = TileLayerDatabase.getLayerWithUrl(main, db.getReadableDatabase(), "https://test2/");
assertNotNull(tls);
}
}

/**
* Test querying and adding a layer from a WMS endpoint
*/
Expand Down
83 changes: 83 additions & 0 deletions src/testCommon/java/de/blau/android/PMTilesDispatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package de.blau.android;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.RecordedRequest;
import okio.Buffer;

public class PMTilesDispatcher extends Dispatcher {
private static final String DEBUG_TAG = PMTilesDispatcher.class.getSimpleName();

private static final String RANGE_HEADER = "Range";
private static final String ETAG_HEADER = "ETag";

private static final Pattern RANGE_PATTERN = Pattern.compile("^bytes=([0-9]+)-([0-9]+)");

final FileChannel channel;

/**
* Construct a new dispatcher that will return tiles from a PMTiles source
*
* @param context an Android Context
* @param source the name of the source
* @throws IOException if the source can't copied out of the assets and opened
*/
public PMTilesDispatcher(@NonNull Context context, @NonNull String source) throws IOException {
try {
File destinationDir = ContextCompat.getExternalCacheDirs(context)[0];
File file = new File(destinationDir, source);
JavaResources.copyFileFromResources(source, null, file);
if (!file.exists()) {
throw new IOException(file.getAbsolutePath() + " doesn't exist");
}
channel = new FileInputStream(file).getChannel();
} catch (IOException e) {
Log.e(DEBUG_TAG, "file " + source + " not found " + e.getMessage());
throw e;
}
}

@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
Log.i(DEBUG_TAG, "request " + request);
try (Buffer data = new Buffer()) {
Matcher matcher = RANGE_PATTERN.matcher(request.getHeader(RANGE_HEADER));
if (matcher.find()) {
long start = Long.parseLong(matcher.group(1));
long end = Long.parseLong(matcher.group(2));
ByteBuffer buffer = ByteBuffer.allocate((int) (end - start + 1));
channel.read(buffer, start);
if (buffer.capacity() != 0) {
data.write(buffer.array());
return new MockResponse().setResponseCode(HttpURLConnection.HTTP_OK).setBody(data);
}
}
return new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_FOUND);
} catch (IOException | NumberFormatException e) {
Log.e(DEBUG_TAG, "dispatch failed for " + request + " " + e.getMessage());
return new MockResponse().setResponseCode(HttpURLConnection.HTTP_INTERNAL_ERROR);
}
}

@Override
public void shutdown() {
try {
channel.close();
} catch (IOException e) {
// Ignore
}
}
}
Binary file not shown.

0 comments on commit 5027dde

Please sign in to comment.