Rename Gradle targets Android (#2226)

This commit is contained in:
Bart Louwers
2024-04-02 23:15:50 +02:00
committed by GitHub
parent 37170ed61c
commit 9f8ae7a98b
1081 changed files with 132 additions and 370 deletions

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools">
<uses-sdk tools:overrideLibrary="android_libs.ub_uiautomator"/>
</manifest>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
package org.maplibre.android
import com.microsoft.appcenter.espresso.Factory
import org.junit.After
import org.junit.Rule
abstract class AppCenter {
@get:Rule
var reportHelper = Factory.getReportHelper()!!
@After
open fun afterTest() {
reportHelper.label(javaClass.simpleName)
}
}

View File

@@ -0,0 +1,10 @@
package org.maplibre.android
import org.maplibre.android.testapp.MapLibreApplication
class InstrumentationApplication : MapLibreApplication() {
fun initializeLeakCanary(): Boolean {
// do not initialize leak canary during instrumentation tests
return true
}
}

View File

@@ -0,0 +1,11 @@
package org.maplibre.android
import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner
class InstrumentationRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, InstrumentationApplication::class.java.name, context)
}
}

View File

@@ -0,0 +1,22 @@
package org.maplibre.android.benchmark
import android.app.Activity
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.maplibre.android.testapp.activity.benchmark.BenchmarkActivity
@RunWith(AndroidJUnit4ClassRunner::class)
class Benchmark {
@Test
fun worldTourBenchmark() {
val scenario = ActivityScenario.launchActivityForResult(BenchmarkActivity::class.java)
while (scenario.state !== Lifecycle.State.DESTROYED) {
Thread.sleep(1000)
}
assertEquals(scenario.result.resultCode, Activity.RESULT_OK)
}
}

View File

@@ -0,0 +1,186 @@
package org.maplibre.android.camera
import android.graphics.PointF
import androidx.test.annotation.UiThreadTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.geometry.LatLngBounds
import org.maplibre.android.testapp.activity.BaseTest
import org.maplibre.android.testapp.activity.espresso.DeviceIndependentTestActivity
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
class CameraUpdateFactoryTest : BaseTest() {
override fun getActivityClass(): Class<*> {
return DeviceIndependentTestActivity::class.java
}
@Test
@UiThreadTest
fun testLatLngBoundsUntiltedUnrotated() {
maplibreMap.cameraPosition = CameraPosition.Builder()
.target(LatLng(60.0, 24.0))
.bearing(0.0)
.tilt(0.0)
.build()
val bounds: LatLngBounds = LatLngBounds.Builder()
.include(LatLng(62.0, 26.0))
.include(LatLng(58.0, 22.0))
.build()
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
val cameraPosition = maplibreMap.cameraPosition
assertEquals("latitude should match:", 60.0, cameraPosition.target!!.latitude, 0.1)
assertEquals("longitude should match:", 24.0, cameraPosition.target!!.longitude, 0.1)
assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1)
assertEquals("zoom should match", 5.5, cameraPosition.zoom, 0.1)
assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1)
}
@Test
@UiThreadTest
fun testLatLngBoundsTilted() {
maplibreMap.cameraPosition = CameraPosition.Builder()
.target(LatLng(60.0, 24.0))
.bearing(0.0)
.tilt(45.0)
.build()
val bounds: LatLngBounds = LatLngBounds.Builder()
.include(LatLng(62.0, 26.0))
.include(LatLng(58.0, 22.0))
.build()
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
val cameraPosition = maplibreMap.cameraPosition
assertEquals("latitude should match:", 60.0, cameraPosition.target!!.latitude, 0.1)
assertEquals("longitude should match:", 24.0, cameraPosition.target!!.longitude, 0.1)
assertEquals("bearing should match:", 0.0, cameraPosition.bearing, 0.1)
assertEquals("zoom should match", 6.0, cameraPosition.zoom, 0.1)
assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1)
}
@Test
@UiThreadTest
fun testLatLngBoundsRotated() {
maplibreMap.cameraPosition = CameraPosition.Builder()
.target(LatLng(60.0, 24.0))
.bearing(30.0)
.tilt(0.0)
.build()
val bounds: LatLngBounds = LatLngBounds.Builder()
.include(LatLng(62.0, 26.0))
.include(LatLng(58.0, 22.0))
.build()
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
val cameraPosition = maplibreMap.cameraPosition
assertEquals("latitude should match:", 60.0, cameraPosition.target!!.latitude, 0.1)
assertEquals("longitude should match:", 24.0, cameraPosition.target!!.longitude, 0.1)
assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
assertEquals("zoom should match", 5.3, cameraPosition.zoom, 0.1)
assertEquals("tilt should match:", 0.0, cameraPosition.tilt, 0.1)
}
@Test
@UiThreadTest
fun testLatLngBoundsTiltedRotated() {
maplibreMap.cameraPosition = CameraPosition.Builder()
.target(LatLng(60.0, 24.0))
.bearing(30.0)
.tilt(45.0)
.build()
val bounds: LatLngBounds = LatLngBounds.Builder()
.include(LatLng(62.0, 26.0))
.include(LatLng(58.0, 22.0))
.build()
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0))
val cameraPosition = maplibreMap.cameraPosition
assertEquals("latitude should match:", 60.0, cameraPosition.target!!.latitude, 0.1)
assertEquals("longitude should match:", 24.0, cameraPosition.target!!.longitude, 0.1)
assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1)
assertEquals("tilt should match:", 45.0, cameraPosition.tilt, 0.1)
}
@Test
@UiThreadTest
fun testLatLngBoundsWithProvidedTiltAndRotation() {
maplibreMap.cameraPosition = CameraPosition.Builder()
.target(LatLng(60.0, 24.0))
.bearing(0.0)
.tilt(0.0)
.build()
val bounds: LatLngBounds = LatLngBounds.Builder()
.include(LatLng(62.0, 26.0))
.include(LatLng(58.0, 22.0))
.build()
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30.0, 40.0, 0))
val cameraPosition = maplibreMap.cameraPosition
assertEquals("latitude should match:", 60.0, cameraPosition.target!!.latitude, 0.1)
assertEquals("longitude should match:", 24.0, cameraPosition.target!!.longitude, 0.1)
assertEquals("bearing should match:", 30.0, cameraPosition.bearing, 0.1)
assertEquals("zoom should match", 5.6, cameraPosition.zoom, 0.1)
assertEquals("tilt should match:", 40.0, cameraPosition.tilt, 0.1)
}
@Test
@UiThreadTest
fun withPadding_cameraInvalidated_paddingPersisting() {
val initialCameraPosition = maplibreMap.cameraPosition
val initialPoint = maplibreMap.projection.toScreenLocation(initialCameraPosition.target!!)
val bottomPadding = mapView.height / 4
val leftPadding = mapView.width / 4
val padding = doubleArrayOf(leftPadding.toDouble(), 0.0, 0.0, bottomPadding.toDouble())
maplibreMap.moveCamera(CameraUpdateFactory.paddingTo(leftPadding.toDouble(), 0.0, 0.0, bottomPadding.toDouble()))
Assert.assertArrayEquals(intArrayOf(leftPadding, 0, 0, bottomPadding), maplibreMap.padding)
val resultingCameraPosition = maplibreMap.cameraPosition
assertEquals(initialCameraPosition.target!!, resultingCameraPosition.target!!)
assertEquals(
PointF(initialPoint.x + leftPadding / 2, initialPoint.y - bottomPadding / 2),
maplibreMap.projection.toScreenLocation(resultingCameraPosition.target!!)
)
Assert.assertArrayEquals(padding, resultingCameraPosition.padding, 0.0001)
}
@Test
@UiThreadTest
fun withLatLngPadding_cameraInvalidated_paddingPersisting() {
val expectedTarget = LatLng(2.0, 2.0)
val topPadding = mapView.height / 4
val rightPadding = mapView.width / 4
val padding = doubleArrayOf(0.0, topPadding.toDouble(), rightPadding.toDouble(), 0.0)
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngPadding(expectedTarget, 0.0, topPadding.toDouble(), rightPadding.toDouble(), 0.0))
Assert.assertArrayEquals(intArrayOf(0, topPadding, rightPadding, 0), maplibreMap.padding)
val resultingCameraPosition = maplibreMap.cameraPosition
assertEquals(expectedTarget.latitude, resultingCameraPosition.target!!.latitude, 0.1)
assertEquals(expectedTarget!!.longitude, resultingCameraPosition.target!!.longitude, 0.1)
val centerLatLng = maplibreMap.projection.fromScreenLocation(PointF((mapView.width / 2).toFloat(), (mapView.height / 2).toFloat()))
assertTrue(centerLatLng.latitude > resultingCameraPosition.target!!.latitude)
assertTrue(centerLatLng.longitude > resultingCameraPosition.target!!.longitude)
Assert.assertArrayEquals(padding, resultingCameraPosition.padding, 0.0001)
}
}

View File

@@ -0,0 +1,40 @@
package org.maplibre.android.integration
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.*
import org.maplibre.android.AppCenter
import org.junit.Before
const val TIMEOUT_UI_SEARCH_WAIT = 5000L
abstract class BaseIntegrationTest : AppCenter() {
protected lateinit var device: UiDevice
@Before
open fun beforeTest() {
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
}
}
/**
* Launches an activity with FLAG_ACTIVITY_NEW_TASK.
* <p>
* To resume an activity, you need to add a single instance launchmode to your manifest configuration.
* <p>
*/
fun UiDevice.launchActivity(context: Context, clazz: Class<*>) {
val applicationPackage = InstrumentationRegistry.getInstrumentation().targetContext.packageName
val intent = Intent(context, clazz)
intent.addFlags(FLAG_ACTIVITY_NEW_TASK)
InstrumentationRegistry.getInstrumentation().context.startActivity(intent)
wait(Until.hasObject(By.pkg(applicationPackage).depth(0)), TIMEOUT_UI_SEARCH_WAIT)
}
fun UiDevice.scrollRecyclerViewTo(recycleItem: String) {
val appView = UiScrollable(UiSelector().scrollable(true))
appView.scrollIntoView(UiSelector().text(recycleItem))
}

View File

@@ -0,0 +1,45 @@
package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.UiSelector
import org.maplibre.android.testapp.activity.fragment.FragmentBackStackActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Regression test that validates MapFragment integration on the backstack
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class FragmentBackStackTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<FragmentBackStackActivity> = ActivityTestRule(FragmentBackStackActivity::class.java)
@Test
@LargeTest
fun backPressedOnBackStackResumed() {
device.waitForIdle()
clickReplaceFragmentButton()
device.pressHome()
device.waitForIdle()
device.launchActivity(activityRule.activity.applicationContext, FragmentBackStackActivity::class.java)
backPressBackStack()
device.waitForIdle()
}
private fun clickReplaceFragmentButton() {
device.findObject(UiSelector().description(textDescription)).click()
}
private fun backPressBackStack() {
device.pressBack() // pops fragment, showing map
device.pressBack() // finish activity
}
private companion object {
const val textDescription = "btn_change_fragment"
}
}

View File

@@ -0,0 +1,29 @@
package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.testapp.activity.maplayout.SimpleMapActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Regression test that validates reopening an Activity with a GLSurfaceView
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class GLSurfaceViewReopenTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<SimpleMapActivity> = ActivityTestRule(SimpleMapActivity::class.java)
@Test
@LargeTest
fun reopenSimpleMapActivity() {
device.waitForIdle()
device.pressHome()
device.waitForIdle()
device.launchActivity(activityRule.activity, SimpleMapActivity::class.java)
device.waitForIdle()
}
}

View File

@@ -0,0 +1,29 @@
package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.testapp.activity.maplayout.GLSurfaceRecyclerViewActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Regression test that validates if a GLSurfaceView surface can be recreated without crashing.
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class GLSurfaceViewReuseTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<GLSurfaceRecyclerViewActivity> = ActivityTestRule(GLSurfaceRecyclerViewActivity::class.java)
@Test
@LargeTest
fun scrollRecyclerView() {
device.waitForIdle()
device.scrollRecyclerViewTo("Twenty-one")
device.waitForIdle()
device.scrollRecyclerViewTo("One")
device.waitForIdle()
}
}

View File

@@ -0,0 +1,32 @@
package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.testapp.activity.maplayout.SimpleMapActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
class OrientationChangeTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<SimpleMapActivity> = ActivityTestRule(SimpleMapActivity::class.java)
@Test
@LargeTest
fun rotateSimpleMap() {
device.setOrientationLeft()
device.waitForIdle()
device.setOrientationNatural()
device.waitForIdle()
device.setOrientationRight()
device.waitForIdle()
device.setOrientationNatural()
device.setOrientationLeft()
device.setOrientationNatural()
device.setOrientationRight()
device.setOrientationNatural()
}
}

View File

@@ -0,0 +1,30 @@
package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.testapp.activity.feature.QueryRenderedFeaturesBoxCountActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Regression test that validates reopening an Activity and querying the map before surface recreation #14394
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class QueryRenderedFeaturesBoxCountTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<QueryRenderedFeaturesBoxCountActivity> =
ActivityTestRule(QueryRenderedFeaturesBoxCountActivity::class.java)
@Test
@LargeTest
fun reopenQueryRendererFeaturesActivity() {
device.waitForIdle()
device.pressHome()
device.waitForIdle()
device.launchActivity(activityRule.activity, QueryRenderedFeaturesBoxCountActivity::class.java)
device.waitForIdle()
}
}

View File

@@ -0,0 +1,29 @@
package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.testapp.activity.textureview.TextureViewDebugModeActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Regression test that validates reopening an Activity with a TextureView
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class TextureViewReopenTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<TextureViewDebugModeActivity> = ActivityTestRule(TextureViewDebugModeActivity::class.java)
@Test
@LargeTest
fun reopenTextureViewDebugActivity() {
device.waitForIdle()
device.pressHome()
device.waitForIdle()
device.launchActivity(activityRule.activity, TextureViewDebugModeActivity::class.java)
device.waitForIdle()
}
}

View File

@@ -0,0 +1,29 @@
package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.testapp.activity.maplayout.TextureRecyclerViewActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Regression test that validates if a GLSurfaceView surface can be recreated without crashing.
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class TextureViewReuseTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<TextureRecyclerViewActivity> = ActivityTestRule(TextureRecyclerViewActivity::class.java)
@Test
@LargeTest
fun scrollRecyclerView() {
device.waitForIdle()
device.scrollRecyclerViewTo("Twenty-one")
device.waitForIdle()
device.scrollRecyclerViewTo("One")
device.waitForIdle()
}
}

View File

@@ -0,0 +1,36 @@
package org.maplibre.android.integration
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.UiSelector
import org.maplibre.android.testapp.activity.fragment.ViewPagerActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Regression test that validates MapFragment integration with a ViewPager
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class ViewPagerScrollTest : BaseIntegrationTest() {
@get:Rule
var activityRule: ActivityTestRule<ViewPagerActivity> = ActivityTestRule(ViewPagerActivity::class.java)
@Test
@LargeTest
fun scrollViewPager() {
for (i in 1..4) {
clickTab(i)
}
for (i in 3 downTo 0) {
clickTab(i)
}
}
private fun clickTab(index: Int) {
device.findObject(UiSelector().text("Page $index")).click()
}
}

View File

@@ -0,0 +1,351 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.location;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.location.LocationPropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for LocationIndicatorLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class LocationIndicatorLayerTest extends BaseLayerTest {
private LocationIndicatorLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new LocationIndicatorLayer("my-layer");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testTopImageAsConstant() {
Timber.i("top-image");
assertNotNull(layer);
assertNull(layer.getTopImage().getValue());
// Set and Get
String propertyValue = "undefined";
layer.setProperties(topImage(propertyValue));
assertEquals(layer.getTopImage().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testBearingImageAsConstant() {
Timber.i("bearing-image");
assertNotNull(layer);
assertNull(layer.getBearingImage().getValue());
// Set and Get
String propertyValue = "undefined";
layer.setProperties(bearingImage(propertyValue));
assertEquals(layer.getBearingImage().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testShadowImageAsConstant() {
Timber.i("shadow-image");
assertNotNull(layer);
assertNull(layer.getShadowImage().getValue());
// Set and Get
String propertyValue = "undefined";
layer.setProperties(shadowImage(propertyValue));
assertEquals(layer.getShadowImage().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testPerspectiveCompensationAsConstant() {
Timber.i("perspective-compensation");
assertNotNull(layer);
assertNull(layer.getPerspectiveCompensation().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(perspectiveCompensation(propertyValue));
assertEquals(layer.getPerspectiveCompensation().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testImageTiltDisplacementAsConstant() {
Timber.i("image-tilt-displacement");
assertNotNull(layer);
assertNull(layer.getImageTiltDisplacement().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(imageTiltDisplacement(propertyValue));
assertEquals(layer.getImageTiltDisplacement().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testBearingAsConstant() {
Timber.i("bearing");
assertNotNull(layer);
assertNull(layer.getBearing().getValue());
// Set and Get
Double propertyValue = 0.3;
layer.setProperties(bearing(propertyValue));
assertEquals(layer.getBearing().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLocationTransition() {
Timber.i("locationTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLocationTransition(options);
assertEquals(layer.getLocationTransition(), options);
}
@Test
@UiThreadTest
public void testLocationAsConstant() {
Timber.i("location");
assertNotNull(layer);
assertNull(layer.getLocation().getValue());
// Set and Get
Double[] propertyValue = new Double[] {0.0, 0.0, 0.0};
layer.setProperties(location(propertyValue));
assertEquals(layer.getLocation().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testAccuracyRadiusTransition() {
Timber.i("accuracy-radiusTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setAccuracyRadiusTransition(options);
assertEquals(layer.getAccuracyRadiusTransition(), options);
}
@Test
@UiThreadTest
public void testAccuracyRadiusAsConstant() {
Timber.i("accuracy-radius");
assertNotNull(layer);
assertNull(layer.getAccuracyRadius().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(accuracyRadius(propertyValue));
assertEquals(layer.getAccuracyRadius().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testTopImageSizeTransition() {
Timber.i("top-image-sizeTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setTopImageSizeTransition(options);
assertEquals(layer.getTopImageSizeTransition(), options);
}
@Test
@UiThreadTest
public void testTopImageSizeAsConstant() {
Timber.i("top-image-size");
assertNotNull(layer);
assertNull(layer.getTopImageSize().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(topImageSize(propertyValue));
assertEquals(layer.getTopImageSize().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testBearingImageSizeTransition() {
Timber.i("bearing-image-sizeTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setBearingImageSizeTransition(options);
assertEquals(layer.getBearingImageSizeTransition(), options);
}
@Test
@UiThreadTest
public void testBearingImageSizeAsConstant() {
Timber.i("bearing-image-size");
assertNotNull(layer);
assertNull(layer.getBearingImageSize().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(bearingImageSize(propertyValue));
assertEquals(layer.getBearingImageSize().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testShadowImageSizeTransition() {
Timber.i("shadow-image-sizeTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setShadowImageSizeTransition(options);
assertEquals(layer.getShadowImageSizeTransition(), options);
}
@Test
@UiThreadTest
public void testShadowImageSizeAsConstant() {
Timber.i("shadow-image-size");
assertNotNull(layer);
assertNull(layer.getShadowImageSize().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(shadowImageSize(propertyValue));
assertEquals(layer.getShadowImageSize().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testAccuracyRadiusColorTransition() {
Timber.i("accuracy-radius-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setAccuracyRadiusColorTransition(options);
assertEquals(layer.getAccuracyRadiusColorTransition(), options);
}
@Test
@UiThreadTest
public void testAccuracyRadiusColorAsConstant() {
Timber.i("accuracy-radius-color");
assertNotNull(layer);
assertNull(layer.getAccuracyRadiusColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(accuracyRadiusColor(propertyValue));
assertEquals(layer.getAccuracyRadiusColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testAccuracyRadiusColorAsIntConstant() {
Timber.i("accuracy-radius-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(accuracyRadiusColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getAccuracyRadiusColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testAccuracyRadiusBorderColorTransition() {
Timber.i("accuracy-radius-border-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setAccuracyRadiusBorderColorTransition(options);
assertEquals(layer.getAccuracyRadiusBorderColorTransition(), options);
}
@Test
@UiThreadTest
public void testAccuracyRadiusBorderColorAsConstant() {
Timber.i("accuracy-radius-border-color");
assertNotNull(layer);
assertNull(layer.getAccuracyRadiusBorderColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(accuracyRadiusBorderColor(propertyValue));
assertEquals(layer.getAccuracyRadiusBorderColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testAccuracyRadiusBorderColorAsIntConstant() {
Timber.i("accuracy-radius-border-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(accuracyRadiusBorderColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getAccuracyRadiusBorderColorAsInt(), Color.argb(127, 255, 127, 0));
}
}

View File

@@ -0,0 +1,792 @@
package org.maplibre.android.location
import android.Manifest
import android.content.Context
import android.graphics.Color
import android.location.Location
import androidx.test.annotation.UiThreadTest
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.UiController
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.GrantPermissionRule
import androidx.test.rule.GrantPermissionRule.grant
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.location.LocationComponentConstants.*
import org.maplibre.android.location.modes.RenderMode
import org.maplibre.android.location.utils.*
import org.maplibre.android.location.utils.MapLibreTestingUtils.Companion.MAPBOX_HEAVY_STYLE
import org.maplibre.android.location.utils.MapLibreTestingUtils.Companion.pushSourceUpdates
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style
import org.maplibre.android.style.sources.GeoJsonSource
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.activity.EspressoTest
import org.maplibre.android.testapp.utils.TestingAsyncUtils
import org.maplibre.android.utils.BitmapUtils
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.Matchers.equalTo
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.math.abs
@RunWith(AndroidJUnit4ClassRunner::class)
class LocationLayerControllerTest : EspressoTest() {
@Rule
@JvmField
val permissionRule: GrantPermissionRule = grant(Manifest.permission.ACCESS_FINE_LOCATION)
private lateinit var styleChangeIdlingResource: StyleChangeIdlingResource
private val location: Location by lazy {
val initLocation = Location("")
initLocation.latitude = 15.0
initLocation.longitude = 17.0
initLocation.bearing = 10f
initLocation.accuracy = 150f
initLocation
}
@Before
override fun beforeTest() {
super.beforeTest()
styleChangeIdlingResource = StyleChangeIdlingResource()
IdlingRegistry.getInstance().register(styleChangeIdlingResource)
}
//
// Location Source
//
@Test
fun renderModeNormal_sourceDoesGetAdded() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.renderMode = RenderMode.NORMAL
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(style.getSource(LOCATION_SOURCE), notNullValue())
}
}
executeComponentTest(componentAction)
}
//
// Location Layers
//
@Test
fun renderModeNormal_trackingNormalLayersDoGetAdded() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.renderMode = RenderMode.NORMAL
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(maplibreMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(SHADOW_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BEARING_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(PULSING_CIRCLE_LAYER), `is`(false))
}
}
executeComponentTest(componentAction)
}
@Test
fun onMapChange_locationComponentPulsingCircleLayerGetsRedrawn() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.locationComponentOptions(
LocationComponentOptions.builder(context)
.pulseEnabled(true)
.build()
)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.renderMode = RenderMode.NORMAL
component.forceLocationUpdate(location)
styleChangeIdlingResource.waitForStyle(maplibreMap, Style.getPredefinedStyle("Bright"))
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(component.renderMode, `is`(equalTo(RenderMode.NORMAL)))
// Check that the Source has been re-added to the new map style
val source: GeoJsonSource? = maplibreMap.style!!.getSourceAs(LOCATION_SOURCE)
assertThat(source, notNullValue())
// Check that the pulsing circle layer visibilities is set to visible
assertThat(maplibreMap.isLayerVisible(PULSING_CIRCLE_LAYER), `is`(true))
}
}
}
@Test
fun renderModeCompass_bearingLayersDoGetAdded() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.renderMode = RenderMode.COMPASS
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(maplibreMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(SHADOW_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BEARING_LAYER), `is`(true))
}
}
executeComponentTest(componentAction)
}
@Test
fun pulsingCircle_enableLocationComponent_pulsingLayerVisibility() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
component.applyStyle(
LocationComponentOptions.builder(context)
.pulseEnabled(true).build()
)
assertThat(maplibreMap.isLayerVisible(PULSING_CIRCLE_LAYER), `is`(true))
}
}
executeComponentTest(componentAction)
}
@Test
fun pulsingCircle_disableLocationComponent_pulsingLayerVisibility() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = false
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(maplibreMap.isLayerVisible(PULSING_CIRCLE_LAYER), `is`(false))
}
}
executeComponentTest(componentAction)
}
@Test
fun pulsingCircle_changeColorCheck() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
component.applyStyle(
LocationComponentOptions.builder(context)
.pulseEnabled(true)
.pulseColor(Color.RED)
.build()
)
component.applyStyle(
LocationComponentOptions.builder(context)
.pulseEnabled(true)
.pulseColor(Color.BLUE)
.build()
)
maplibreMap.style.apply {
assertThat(component.locationComponentOptions.pulseColor(), `is`(Color.BLUE))
}
}
}
executeComponentTest(componentAction)
}
@Test
fun pulsingCircle_changeSpeedCheck() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
component.applyStyle(
LocationComponentOptions.builder(context)
.pulseEnabled(true)
.pulseSingleDuration(8000f)
.build()
)
component.applyStyle(
LocationComponentOptions.builder(context)
.pulseEnabled(true)
.pulseSingleDuration(400f)
.build()
)
maplibreMap.style.apply {
assertThat(component.locationComponentOptions.pulseSingleDuration(), `is`(400f))
}
}
}
executeComponentTest(componentAction)
}
@Test
fun renderModeGps_navigationLayersDoGetAdded() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.renderMode = RenderMode.GPS
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(maplibreMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(SHADOW_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(BEARING_LAYER), `is`(false))
}
}
executeComponentTest(componentAction)
}
@Test
fun dontShowPuckWhenRenderModeSetAndComponentDisabled() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.forceLocationUpdate(location)
component.isLocationComponentEnabled = false
TestingAsyncUtils.waitForLayer(uiController, mapView)
component.renderMode = RenderMode.GPS
assertThat(maplibreMap.isLayerVisible(FOREGROUND_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(BACKGROUND_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(SHADOW_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(BEARING_LAYER), `is`(false))
}
}
executeComponentTest(componentAction)
}
@Test
fun whenLocationComponentDisabled_doesSetAllLayersToVisibilityNone() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.renderMode = RenderMode.NORMAL
component.forceLocationUpdate(location)
component.isLocationComponentEnabled = false
TestingAsyncUtils.waitForLayer(uiController, mapView)
// Check that all layers visibilities are set to none
assertThat(maplibreMap.isLayerVisible(FOREGROUND_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(BACKGROUND_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(SHADOW_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(ACCURACY_LAYER), `is`(false))
assertThat(maplibreMap.isLayerVisible(BEARING_LAYER), `is`(false))
}
}
executeComponentTest(componentAction)
}
@Test
fun onMapChange_locationComponentLayersDoGetRedrawn() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.renderMode = RenderMode.NORMAL
component.forceLocationUpdate(location)
styleChangeIdlingResource.waitForStyle(maplibreMap, Style.getPredefinedStyle("Bright"))
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(component.renderMode, `is`(equalTo(RenderMode.NORMAL)))
// Check that the Source has been re-added to the new map style
val source: GeoJsonSource? = maplibreMap.style!!.getSourceAs(LOCATION_SOURCE)
assertThat(source, notNullValue())
// Check that all layers visibilities are set to visible
assertThat(maplibreMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(SHADOW_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BEARING_LAYER), `is`(false))
}
}
executeComponentTest(componentAction)
}
@Test
fun whenStyleChanged_continuesUsingStaleIcons() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.applyStyle(LocationComponentOptions.builder(context).staleStateTimeout(100).build())
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
uiController.loopMainThreadForAtLeast(150)
assertThat(
maplibreMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE),
`is`(true)
)
maplibreMap.setStyle(Style.Builder().fromUrl(Style.getPredefinedStyle("Bright")))
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(
maplibreMap.querySourceFeatures(LOCATION_SOURCE)[0].getBooleanProperty(PROPERTY_LOCATION_STALE),
`is`(true)
)
}
}
executeComponentTest(componentAction)
}
@Test
fun whenStyleChanged_isDisabled_hasLayerBelow_staysHidden() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
component.isLocationComponentEnabled = false
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(maplibreMap.queryRenderedFeatures(location, FOREGROUND_LAYER).isEmpty(), `is`(true))
val options = component.locationComponentOptions
.toBuilder()
.layerBelow("road-label")
.build()
component.applyStyle(options)
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(maplibreMap.queryRenderedFeatures(location, FOREGROUND_LAYER).isEmpty(), `is`(true))
}
}
executeComponentTest(componentAction)
}
@Test
fun whenStyleChanged_staleStateChanges() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.applyStyle(LocationComponentOptions.builder(context).staleStateTimeout(1).build())
styleChangeIdlingResource.waitForStyle(maplibreMap, MAPBOX_HEAVY_STYLE)
pushSourceUpdates(styleChangeIdlingResource) {
component.forceLocationUpdate(location)
}
}
}
executeComponentTest(componentAction)
// Waiting for style to finish loading while pushing updates
onView(withId(android.R.id.content)).check(matches(isDisplayed()))
}
@Test
fun whenStyleChanged_layerVisibilityUpdates() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
styleChangeIdlingResource.waitForStyle(maplibreMap, MAPBOX_HEAVY_STYLE)
uiController.loopMainThreadForAtLeast(100)
var show = true
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, maplibreMap.style!!)
.useDefaultLocationEngine(false)
.build()
)
pushSourceUpdates(styleChangeIdlingResource) {
component.isLocationComponentEnabled = show
show = !show
}
TestingAsyncUtils.waitForLayer(uiController, mapView)
}
}
executeComponentTest(componentAction)
// Waiting for style to finish loading while pushing updates
onView(withId(android.R.id.content)).check(matches(isDisplayed()))
}
@Test
fun accuracy_visibleWithNewLocation() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(location), 16.0))
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
uiController.loopMainThreadForAtLeast(ACCURACY_RADIUS_ANIMATION_DURATION)
assertEquals(
Utils.calculateZoomLevelRadius(maplibreMap, location) /*meters projected to radius on zoom 16*/,
maplibreMap.querySourceFeatures(LOCATION_SOURCE)[0]
.getNumberProperty(PROPERTY_ACCURACY_RADIUS).toFloat(),
0.1f
)
}
}
executeComponentTest(componentAction)
}
@Test
fun accuracy_visibleWhenCameraEased() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.forceLocationUpdate(location)
val target = LatLng(location)
val zoom = 16.0
maplibreMap.easeCamera(CameraUpdateFactory.newLatLngZoom(target, zoom), 300)
uiController.loopMainThreadForAtLeast(300)
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(
Math.abs(zoom - maplibreMap.cameraPosition.zoom) < 0.1 &&
Math.abs(target.latitude - maplibreMap.cameraPosition.target!!.latitude) < 0.1 &&
Math.abs(target!!.longitude - maplibreMap.cameraPosition.target!!.longitude) < 0.1,
`is`(true)
)
val expectedRadius =
Utils.calculateZoomLevelRadius(maplibreMap, location) /*meters projected to radius on zoom 16*/
assertThat(
Math.abs(
expectedRadius - maplibreMap.querySourceFeatures(LOCATION_SOURCE)[0].getNumberProperty(
PROPERTY_ACCURACY_RADIUS
).toFloat()
) < 0.1,
`is`(true)
)
}
}
executeComponentTest(componentAction)
}
@Test
fun accuracy_visibleWhenCameraMoved() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.forceLocationUpdate(location)
val target = LatLng(location)
val zoom = 16.0
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(target, zoom))
TestingAsyncUtils.waitForLayer(uiController, mapView)
assertThat(
abs(zoom - maplibreMap.cameraPosition.zoom) < 0.1 &&
abs(target.latitude - maplibreMap.cameraPosition.target!!.latitude) < 0.1 &&
abs(target!!.longitude - maplibreMap.cameraPosition.target!!.longitude) < 0.1,
`is`(true)
)
val expectedRadius =
Utils.calculateZoomLevelRadius(maplibreMap, location) /*meters projected to radius on zoom 16*/
assertThat(
Math.abs(
expectedRadius - maplibreMap.querySourceFeatures(LOCATION_SOURCE)[0].getNumberProperty(
PROPERTY_ACCURACY_RADIUS
).toFloat()
) < 0.1,
`is`(true)
)
}
}
executeComponentTest(componentAction)
}
@Test
fun applyStyle_layerBelow_restoreLayerVisibility() {
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
override fun onLocationComponentAction(
component: LocationComponent,
maplibreMap: MapLibreMap,
style: Style,
uiController: UiController,
context: Context
) {
component.activateLocationComponent(
LocationComponentActivationOptions
.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
component.isLocationComponentEnabled = true
component.forceLocationUpdate(location)
TestingAsyncUtils.waitForLayer(uiController, mapView)
component.applyStyle(LocationComponentOptions.builder(context).layerBelow("road-label").build())
assertThat(maplibreMap.isLayerVisible(FOREGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BACKGROUND_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(SHADOW_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
assertThat(maplibreMap.isLayerVisible(BEARING_LAYER), `is`(false))
}
}
executeComponentTest(componentAction)
}
@Test
@UiThreadTest
fun test_15026_missingShadowGradientRadius() {
// test for https://github.com/mapbox/mapbox-gl-native/issues/15026
val shadowDrawable = BitmapUtils.getDrawableFromRes(context, R.drawable.mapbox_user_icon_shadow_0px_test)
Utils.generateShadow(shadowDrawable, 0f)
}
@After
override fun afterTest() {
super.afterTest()
IdlingRegistry.getInstance().unregister(styleChangeIdlingResource)
}
private fun executeComponentTest(listener: LocationComponentAction.OnPerformLocationComponentAction) {
onView(withId(android.R.id.content)).perform(LocationComponentAction(maplibreMap, listener))
}
}

View File

@@ -0,0 +1,39 @@
package org.maplibre.android.location.utils
import android.content.Context
import android.view.View
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import org.maplibre.android.location.LocationComponent
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style
import org.hamcrest.Matcher
class LocationComponentAction(
private val maplibreMap: MapLibreMap,
private val onPerformLocationComponentAction: OnPerformLocationComponentAction
) : ViewAction {
override fun getConstraints(): Matcher<View> {
return isDisplayed()
}
override fun getDescription(): String {
return javaClass.simpleName
}
override fun perform(uiController: UiController, view: View) {
onPerformLocationComponentAction.onLocationComponentAction(
maplibreMap.locationComponent,
maplibreMap,
maplibreMap.style!!,
uiController,
view.context
)
}
interface OnPerformLocationComponentAction {
fun onLocationComponentAction(component: LocationComponent, maplibreMap: MapLibreMap, style: Style, uiController: UiController, context: Context)
}
}

View File

@@ -0,0 +1,82 @@
package org.maplibre.android.location.utils
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.location.Location
import android.os.Handler
import android.os.Looper
import com.mapbox.geojson.Feature
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.style.layers.Property
import org.maplibre.android.style.sources.GeoJsonSource
fun MapLibreMap.querySourceFeatures(sourceId: String): List<Feature> {
return this.style!!.getSourceAs<GeoJsonSource>(sourceId)?.querySourceFeatures(null) ?: emptyList()
}
fun MapLibreMap.queryRenderedFeatures(location: Location, layerId: String): List<Feature> {
val latLng = LatLng(location.latitude, location.longitude)
val point = this.projection.toScreenLocation(latLng)
return this.queryRenderedFeatures(point, layerId)
}
fun MapLibreMap.isLayerVisible(layerId: String): Boolean {
return this.style!!.getLayer(layerId)?.visibility?.value?.equals(Property.VISIBLE)!!
}
class MapLibreTestingUtils {
companion object {
/**
* Used to increase style load time for stress testing.
*/
const val MAPBOX_HEAVY_STYLE = "asset://heavy_style.json"
private const val DATA_PUSH_INTERVAL = 1L
/**
* Pushes data updates every [DATA_PUSH_INTERVAL] milliseconds until the style has been loaded,
* checked with [StyleChangeIdlingResource].
*/
fun pushSourceUpdates(styleChangeIdlingResource: StyleChangeIdlingResource, update: () -> Unit) {
val mainHandler = Handler(Looper.getMainLooper())
val runnable = object : Runnable {
override fun run() {
update.invoke()
if (!styleChangeIdlingResource.isIdleNow) {
mainHandler.postDelayed(this, DATA_PUSH_INTERVAL)
}
}
}
if (!styleChangeIdlingResource.isIdleNow) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run()
} else {
mainHandler.post(runnable)
}
}
}
}
}
fun MapLibreMap.addImageFromDrawable(string: String, drawable: Drawable) {
val bitmapFromDrawable = getBitmapFromDrawable(drawable)
this.style!!.addImage(string, bitmapFromDrawable)
}
private fun getBitmapFromDrawable(drawable: Drawable): Bitmap {
if (drawable is BitmapDrawable) return drawable.bitmap
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}

View File

@@ -0,0 +1,40 @@
package org.maplibre.android.location.utils
import androidx.test.espresso.IdlingResource
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style
/**
* Resource, that's idling until the provided style is loaded.
* Remember to add any espresso action (like view assertion) after the [waitForStyle] call
* for the test to keep running.
*/
class StyleChangeIdlingResource : IdlingResource {
private var callback: IdlingResource.ResourceCallback? = null
private var isIdle = true
override fun getName(): String {
return javaClass.simpleName
}
override fun isIdleNow(): Boolean {
return isIdle
}
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
this.callback = callback
}
private fun setIdle() {
isIdle = true
callback?.onTransitionToIdle()
}
fun waitForStyle(maplibreMap: MapLibreMap, styleUrl: String) {
isIdle = false
maplibreMap.setStyle(Style.Builder().fromUri(styleUrl)) {
setIdle()
}
}
}

View File

@@ -0,0 +1,27 @@
package org.maplibre.android.maps
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.platform.app.InstrumentationRegistry
import org.maplibre.android.AppCenter
import org.maplibre.android.style.layers.Layer
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
abstract class BaseLayerTest : AppCenter() {
private lateinit var nativeMapView: NativeMap
companion object {
const val WIDTH = 500
const val HEIGHT = WIDTH
}
fun before() {
val context = InstrumentationRegistry.getInstrumentation().context
nativeMapView = NativeMapView(context, false, null, null, NativeMapViewTest.DummyRenderer(context))
nativeMapView.resizeView(WIDTH, HEIGHT)
}
fun setupLayer(layer: Layer) {
nativeMapView.addLayer(layer)
}
}

View File

@@ -0,0 +1,95 @@
package org.maplibre.android.maps
import androidx.test.annotation.UiThreadTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.platform.app.InstrumentationRegistry
import org.maplibre.android.AppCenter
import org.maplibre.android.MapLibre
import org.maplibre.android.WellKnownTileServer
import org.maplibre.android.storage.FileSource
import org.maplibre.android.util.TileServerOptions
import org.junit.*
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
class BootstrapTest : AppCenter() {
var apiKeyBackup: String? = null
@Before
@UiThreadTest
fun before() {
apiKeyBackup = MapLibre.getApiKey()
}
@After
@UiThreadTest
fun after() {
val context = InstrumentationRegistry.getInstrumentation().context
MapLibre.getInstance(context)
}
@Test
@UiThreadTest
fun defaultBootstrap() {
val context = InstrumentationRegistry.getInstrumentation().context
MapLibre.getInstance(context)
val tileServerOptions = TileServerOptions.get(WellKnownTileServer.MapLibre)
Assert.assertTrue(
Style.getPredefinedStyles().count() == tileServerOptions.defaultStyles.count()
)
Assert.assertTrue(
Style.getPredefinedStyles().first().url == tileServerOptions.defaultStyles.first().url
)
val fileSource = FileSource.getInstance(context)
Assert.assertEquals(fileSource.apiBaseUrl, tileServerOptions.baseURL)
}
@Test
@UiThreadTest
fun maptilerBootstrap() {
val context = InstrumentationRegistry.getInstrumentation().context
val key = "abcdef"
MapLibre.getInstance(context, key, WellKnownTileServer.MapTiler)
val tileServerOptions = TileServerOptions.get(WellKnownTileServer.MapTiler)
Assert.assertTrue(
Style.getPredefinedStyles().count() == tileServerOptions.defaultStyles.count()
)
Assert.assertTrue(
Style.getPredefinedStyles().first().url == tileServerOptions.defaultStyles.first().url
)
val fileSource = FileSource.getInstance(context)
Assert.assertEquals(fileSource.apiBaseUrl, tileServerOptions.baseURL)
Assert.assertEquals(fileSource.apiKey, key)
}
@Test
@UiThreadTest
fun duplicateBootstrap() {
val context = InstrumentationRegistry.getInstrumentation().context
val key = "pk.abcdef"
MapLibre.getInstance(context, key, WellKnownTileServer.MapTiler)
MapLibre.getInstance(context)
MapLibre.getInstance(context, key, WellKnownTileServer.Mapbox)
val tileServerOptions = TileServerOptions.get(WellKnownTileServer.Mapbox)
Assert.assertTrue(
Style.getPredefinedStyles().count() == tileServerOptions.defaultStyles.count()
)
Assert.assertTrue(
Style.getPredefinedStyles().first().url == tileServerOptions.defaultStyles.first().url
)
val fileSource = FileSource.getInstance(context)
Assert.assertEquals(fileSource.apiBaseUrl, tileServerOptions.baseURL)
Assert.assertEquals(fileSource.apiKey, key)
}
}

View File

@@ -0,0 +1,517 @@
package org.maplibre.android.maps
import android.graphics.PointF
import android.os.SystemClock
import android.view.MotionEvent
import android.view.View
import androidx.test.espresso.Espresso
import androidx.test.espresso.InjectEventSecurityException
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers
import org.hamcrest.Matcher
object GesturesUiTestUtils {
private const val DEFAULT_GESTURE_DURATION = 500L
fun pinch(
startSpan: Float,
endSpan: Float,
center: PointF? = null,
duration: Long = DEFAULT_GESTURE_DURATION
): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View> {
return ViewMatchers.isEnabled()
}
override fun getDescription(): String = "Pinch $startSpan -> $endSpan"
override fun perform(uiController: UiController, view: View) {
var middlePosition = center
if (middlePosition == null) {
middlePosition = getCenterPointF(view)
}
val startPoint1 = PointF(middlePosition.x - startSpan / 2f, middlePosition.y)
val startPoint2 = PointF(middlePosition.x + startSpan / 2f, middlePosition.y)
val endPoint1 = PointF(middlePosition.x - endSpan / 2f, middlePosition.y)
val endPoint2 = PointF(middlePosition.x + endSpan / 2f, middlePosition.y)
performPinch(uiController, startPoint1, startPoint2, endPoint1, endPoint2, duration)
}
}
}
fun quickScale(
deltaY: Float,
startPoint: PointF? = null,
withVelocity: Boolean = true,
duration: Long = DEFAULT_GESTURE_DURATION,
interrupt: Boolean = false
): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View> = ViewMatchers.isEnabled()
override fun getDescription(): String = "quick scale ${deltaY}Y"
override fun perform(uiController: UiController, view: View) {
var middlePosition = startPoint
if (middlePosition == null) {
middlePosition = getCenterPointF(view)
}
val endPoint = PointF(middlePosition.x, middlePosition.y + deltaY)
performQuickScale(uiController, middlePosition, endPoint, withVelocity, duration, interrupt)
}
}
}
fun move(
deltaX: Float,
deltaY: Float,
startPoint: PointF? = null,
withVelocity: Boolean = true,
duration: Long = DEFAULT_GESTURE_DURATION
): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View> = ViewMatchers.isEnabled()
override fun getDescription(): String = "move ${deltaX}X, ${deltaY}Y"
override fun perform(uiController: UiController, view: View) {
var middlePosition = startPoint
if (middlePosition == null) {
middlePosition = getCenterPointF(view)
}
val endPoint = PointF(middlePosition.x + deltaX, middlePosition.y + deltaY)
performMove(uiController, middlePosition, endPoint, withVelocity, duration)
}
}
}
private fun getCenterPointF(view: View): PointF {
val locationOnScreen = IntArray(2)
view.getLocationOnScreen(locationOnScreen)
val viewHeight = view.height * view.scaleY
val viewWidth = view.width * view.scaleX
return PointF(
(locationOnScreen[0] + viewWidth / 2).toInt().toFloat(),
(locationOnScreen[1] + viewHeight / 2).toInt().toFloat()
)
}
// https://stackoverflow.com/a/46443628/9126211
private fun performPinch(
uiController: UiController,
startPoint1: PointF,
startPoint2: PointF,
endPoint1: PointF,
endPoint2: PointF,
duration: Long
) {
val eventMinInterval: Long = 10
val startTime = SystemClock.uptimeMillis()
var eventTime = startTime
var event: MotionEvent
var eventX1: Float = startPoint1.x
var eventY1: Float = startPoint1.y
var eventX2: Float = startPoint2.x
var eventY2: Float = startPoint2.y
// Specify the property for the two touch points
val properties = arrayOfNulls<MotionEvent.PointerProperties>(2)
val pp1 = MotionEvent.PointerProperties()
pp1.id = 0
pp1.toolType = MotionEvent.TOOL_TYPE_FINGER
val pp2 = MotionEvent.PointerProperties()
pp2.id = 1
pp2.toolType = MotionEvent.TOOL_TYPE_FINGER
properties[0] = pp1
properties[1] = pp2
// Specify the coordinations of the two touch points
// NOTE: you MUST set the pressure and size value, or it doesn't work
val pointerCoords = arrayOfNulls<MotionEvent.PointerCoords>(2)
val pc1 = MotionEvent.PointerCoords()
pc1.x = eventX1
pc1.y = eventY1
pc1.pressure = 1f
pc1.size = 1f
val pc2 = MotionEvent.PointerCoords()
pc2.x = eventX2
pc2.y = eventY2
pc2.pressure = 1f
pc2.size = 1f
pointerCoords[0] = pc1
pointerCoords[1] = pc2
/*
* Events sequence of zoom gesture:
*
* 1. Send ACTION_DOWN event of one start point
* 2. Send ACTION_POINTER_DOWN of two start points
* 3. Send ACTION_MOVE of two middle points
* 4. Repeat step 3 with updated middle points (x,y), until reach the end points
* 5. Send ACTION_POINTER_UP of two end points
* 6. Send ACTION_UP of one end point
*/
try {
// Step 1
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_DOWN, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
// Step 2
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_POINTER_DOWN + (pp2.id shl MotionEvent.ACTION_POINTER_INDEX_SHIFT), 2,
properties, pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
// Step 3, 4
val moveEventNumber = duration / eventMinInterval
val stepX1: Float
val stepY1: Float
val stepX2: Float
val stepY2: Float
stepX1 = (endPoint1.x - startPoint1.x) / moveEventNumber
stepY1 = (endPoint1.y - startPoint1.y) / moveEventNumber
stepX2 = (endPoint2.x - startPoint2.x) / moveEventNumber
stepY2 = (endPoint2.y - startPoint2.y) / moveEventNumber
for (i in 0 until moveEventNumber) {
// Update the move events
eventTime += eventMinInterval
eventX1 += stepX1
eventY1 += stepY1
eventX2 += stepX2
eventY2 += stepY2
pc1.x = eventX1
pc1.y = eventY1
pc2.x = eventX2
pc2.y = eventY2
pointerCoords[0] = pc1
pointerCoords[1] = pc2
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_MOVE, 2, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
}
// Step 5
pc1.x = endPoint1.x
pc1.y = endPoint1.y
pc2.x = endPoint2.x
pc2.y = endPoint2.y
pointerCoords[0] = pc1
pointerCoords[1] = pc2
eventTime += eventMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_POINTER_UP + (pp2.id shl MotionEvent.ACTION_POINTER_INDEX_SHIFT), 2, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
// Step 6
eventTime += eventMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_UP, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
} catch (ex: InjectEventSecurityException) {
throw RuntimeException("Could not perform pinch", ex)
}
}
private fun performQuickScale(
uiController: UiController,
startPoint: PointF,
endPoint: PointF,
withVelocity: Boolean,
duration: Long,
interrupt: Boolean
) {
val eventMinInterval: Long = 10
val tapDownMinInterval: Long = 40 // ViewConfiguration#DOUBLE_TAP_MIN_TIME = 40
val startTime = SystemClock.uptimeMillis()
var eventTime = startTime
var event: MotionEvent
var eventX1: Float = startPoint.x
var eventY1: Float = startPoint.y
var properties = arrayOfNulls<MotionEvent.PointerProperties>(1)
val pp1 = MotionEvent.PointerProperties()
pp1.id = 0
pp1.toolType = MotionEvent.TOOL_TYPE_FINGER
properties[0] = pp1
var pointerCoords = arrayOfNulls<MotionEvent.PointerCoords>(1)
val pc1 = MotionEvent.PointerCoords()
pc1.x = eventX1
pc1.y = eventY1
pc1.pressure = 1f
pc1.size = 1f
pointerCoords[0] = pc1
/*
* Events sequence of quick scale gesture:
*
* 1. Send ACTION_DOWN
* 2. Send ACTION_UP
* 3. Send ACTION_DOWN
* 4. Send ACTION_MOVE with updated middle points (x,y), until reach the end points
* 5. Send ACTION_UP of one end point
*/
try {
// Step 1
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_DOWN, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
// Step 2
eventTime += eventMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_UP, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
// Step 3
eventTime += tapDownMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_DOWN, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
// Step 4
val moveEventNumber = duration / eventMinInterval
val stepX1: Float
val stepY1: Float
stepX1 = (endPoint.x - startPoint.x) / moveEventNumber
stepY1 = (endPoint.y - startPoint.y) / moveEventNumber
var interrupted = false
for (i in 0 until moveEventNumber) {
// Update the move events
eventTime += eventMinInterval
eventX1 += stepX1
eventY1 += stepY1
pc1.x = eventX1
pc1.y = eventY1
pointerCoords[0] = pc1
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_MOVE, if (interrupted) 2 else 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
if (interrupt && i == moveEventNumber / 2) {
// Specify the property for the two touch points
properties = arrayOfNulls<MotionEvent.PointerProperties>(2)
val pp2 = MotionEvent.PointerProperties()
pp2.id = 1
pp2.toolType = MotionEvent.TOOL_TYPE_FINGER
properties[0] = pp1
properties[1] = pp2
// Specify the coordinations of the two touch points
// NOTE: you MUST set the pressure and size value, or it doesn't work
pointerCoords = arrayOfNulls<MotionEvent.PointerCoords>(2)
val pc2 = MotionEvent.PointerCoords()
pc2.x = startPoint.x
pc2.y = startPoint.y
pc2.pressure = 1f
pc2.size = 1f
pointerCoords[0] = pc1
pointerCoords[1] = pc2
eventTime += eventMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_POINTER_DOWN + (pp2.id shl MotionEvent.ACTION_POINTER_INDEX_SHIFT), 2,
properties, pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
interrupted = true
}
}
if (!withVelocity) {
eventTime += eventMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_MOVE, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
}
// Step 5
eventTime += eventMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_UP, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
} catch (ex: InjectEventSecurityException) {
throw RuntimeException("Could not perform quick scale", ex)
}
}
private fun performMove(
uiController: UiController,
startPoint: PointF,
endPoint: PointF,
withVelocity: Boolean,
duration: Long
) {
val eventMinInterval: Long = 10
val startTime = SystemClock.uptimeMillis()
var eventTime = startTime
var event: MotionEvent
var eventX1: Float = startPoint.x
var eventY1: Float = startPoint.y
val properties = arrayOfNulls<MotionEvent.PointerProperties>(1)
val pp1 = MotionEvent.PointerProperties()
pp1.id = 0
pp1.toolType = MotionEvent.TOOL_TYPE_FINGER
properties[0] = pp1
val pointerCoords = arrayOfNulls<MotionEvent.PointerCoords>(1)
val pc1 = MotionEvent.PointerCoords()
pc1.x = eventX1
pc1.y = eventY1
pc1.pressure = 1f
pc1.size = 1f
pointerCoords[0] = pc1
/*
* Events sequence of move gesture:
*
* 1. Send ACTION_DOWN
* 2. Send ACTION_MOVE with updated middle points (x,y), until reach the end points
* 3. Send ACTION_UP
*/
try {
// Step 1
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_DOWN, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
// Step 2
val moveEventNumber = duration / eventMinInterval
val stepX1: Float
val stepY1: Float
stepX1 = (endPoint.x - startPoint.x) / moveEventNumber
stepY1 = (endPoint.y - startPoint.y) / moveEventNumber
for (i in 0 until moveEventNumber) {
// Update the move events
eventTime += eventMinInterval
eventX1 += stepX1
eventY1 += stepY1
pc1.x = eventX1
pc1.y = eventY1
pointerCoords[0] = pc1
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_MOVE, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
}
if (!withVelocity) {
eventTime += eventMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_MOVE, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
}
// Step 3
eventTime += eventMinInterval
event = MotionEvent.obtain(
startTime, eventTime,
MotionEvent.ACTION_UP, 1, properties,
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0
)
injectMotionEventToUiController(uiController, event)
} catch (ex: InjectEventSecurityException) {
throw RuntimeException("Could not perform quick scale", ex)
}
}
/**
* Safely call uiController.injectMotionEvent(event): Detect any error and "convert" it to an
* IllegalStateException
*/
@Throws(InjectEventSecurityException::class)
private fun injectMotionEventToUiController(uiController: UiController, event: MotionEvent) {
val injectEventSucceeded = uiController.injectMotionEvent(event)
if (!injectEventSucceeded) {
throw IllegalStateException("Error performing event $event")
}
}
}
fun Int.loopFor(millis: Long) {
Espresso.onView(ViewMatchers.withId(this)).perform(object : ViewAction {
override fun getDescription(): String = "waiting for $millis"
override fun getConstraints(): Matcher<View> = ViewMatchers.isEnabled()
override fun perform(uiController: UiController?, view: View?) {
uiController?.loopMainThreadForAtLeast(millis)
}
})
}

View File

@@ -0,0 +1,35 @@
package org.maplibre.android.maps
import org.maplibre.android.annotations.Icon
import timber.log.Timber
class IconManagerResolver(maplibreMap: MapLibreMap?) {
private var iconManager: IconManager? = null
init {
try {
val annotationManagerField = MapLibreMap::class.java.getDeclaredField("annotationManager")
annotationManagerField.isAccessible = true
val annotationManager = annotationManagerField[maplibreMap] as AnnotationManager
val iconManagerField = AnnotationManager::class.java.getDeclaredField("iconManager")
iconManagerField.isAccessible = true
iconManager = iconManagerField[annotationManager] as IconManager
} catch (exception: Exception) {
Timber.e(exception, "Could not create IconManagerResolver, unable to reflect.")
}
}
val iconMap: Map<Icon, Int>
get() {
try {
val field = IconManager::class.java.getDeclaredField("iconMap")
field.isAccessible = true
return field[iconManager] as Map<Icon, Int>
} catch (exception: NoSuchFieldException) {
Timber.e(exception, "Could not getIconMap, unable to reflect.")
} catch (exception: IllegalAccessException) {
Timber.e(exception, "Could not getIconMap, unable to reflect.")
}
return HashMap()
}
}

View File

@@ -0,0 +1,193 @@
package org.maplibre.android.maps
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.withId
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.constants.MapLibreConstants
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.GesturesUiTestUtils.move
import org.maplibre.android.maps.GesturesUiTestUtils.quickScale
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.activity.BaseTest
import org.maplibre.android.testapp.activity.maplayout.SimpleMapActivity
import org.junit.Assert
import org.junit.Before
import org.junit.Test
class MapGestureDetectorTest : BaseTest() {
override fun getActivityClass() = SimpleMapActivity::class.java
private var maxWidth: Int = 0
private var maxHeight: Int = 0
@Before
fun setup() {
maxWidth = mapView.width
maxHeight = mapView.height
}
@Test
fun sanity_quickZoom() {
validateTestSetup()
var initialZoom: Double? = null
rule.runOnUiThread {
initialZoom = maplibreMap.cameraPosition.zoom
}
onView(withId(R.id.mapView)).perform(quickScale(maxHeight / 2f, withVelocity = false))
rule.runOnUiThread {
Assert.assertTrue(maplibreMap.cameraPosition.zoom > initialZoom!!)
}
}
@Test
fun quickZoomDisabled_phantomQuickZoom_moveStillEnabled_15091() {
// regression test for https://github.com/mapbox/mapbox-gl-native/issues/15091
validateTestSetup()
var initialCameraPosition: CameraPosition? = null
rule.runOnUiThread {
// zoom in so we can move vertically
maplibreMap.moveCamera(CameraUpdateFactory.zoomTo(4.0))
initialCameraPosition = maplibreMap.cameraPosition
maplibreMap.uiSettings.isQuickZoomGesturesEnabled = false
}
onView(withId(R.id.mapView)).perform(quickScale(maxHeight / 2f))
rule.runOnUiThread {
// camera did not move
Assert.assertEquals(initialCameraPosition!!, maplibreMap.cameraPosition)
}
// move to expected target
onView(withId(R.id.mapView)).perform(move(-maxWidth / 2f, -maxHeight / 2f, withVelocity = false))
rule.runOnUiThread {
Assert.assertNotEquals(initialCameraPosition!!.target!!.latitude, maplibreMap.cameraPosition.target!!.latitude, 1.0)
Assert.assertNotEquals(initialCameraPosition!!.target!!.longitude, maplibreMap.cameraPosition.target!!.longitude, 1.0)
}
}
@Test
fun quickZoom_doNotMove_14227() {
// test for https://github.com/mapbox/mapbox-gl-native/issues/14227
validateTestSetup()
var initialTarget: LatLng? = null
rule.runOnUiThread {
initialTarget = maplibreMap.cameraPosition.target!!
}
onView(withId(R.id.mapView)).perform(quickScale(maxHeight / 2f))
rule.runOnUiThread {
// camera did not move
Assert.assertEquals(initialTarget!!.latitude, maplibreMap.cameraPosition.target!!.latitude, 1.0)
Assert.assertEquals(initialTarget!!.longitude, maplibreMap.cameraPosition.target!!.longitude, 1.0)
}
}
@Test
fun quickZoom_interrupted_moveStillEnabled_14598() {
// test for https://github.com/mapbox/mapbox-gl-native/issues/14598
validateTestSetup()
onView(withId(R.id.mapView)).perform(quickScale(maxHeight / 2f, interrupt = true))
var initialCameraPosition: CameraPosition? = null
rule.runOnUiThread {
// zoom in so we can move vertically
maplibreMap.moveCamera(CameraUpdateFactory.zoomTo(4.0))
initialCameraPosition = maplibreMap.cameraPosition
maplibreMap.uiSettings.isQuickZoomGesturesEnabled = false
}
// move to expected target
onView(withId(R.id.mapView)).perform(move(-maxWidth / 2f, -maxHeight / 2f, withVelocity = false))
rule.runOnUiThread {
Assert.assertNotEquals(initialCameraPosition!!.target!!.latitude, maplibreMap.cameraPosition.target!!.latitude, 1.0)
Assert.assertNotEquals(initialCameraPosition!!.target!!.longitude, maplibreMap.cameraPosition.target!!.longitude, 1.0)
}
}
@Test
fun quickZoom_ignoreDoubleTap() {
// test for https://github.com/mapbox/mapbox-gl-native/issues/14013
validateTestSetup()
var initialZoom: Double? = null
rule.runOnUiThread {
maplibreMap.moveCamera(CameraUpdateFactory.zoomTo(2.0))
initialZoom = maplibreMap.cameraPosition.zoom
}
onView(withId(R.id.mapView)).perform(quickScale(-(maplibreMap.gesturesManager.standardScaleGestureDetector.spanSinceStartThreshold * 2), withVelocity = false, duration = 1000L))
R.id.mapView.loopFor(MapLibreConstants.ANIMATION_DURATION.toLong())
rule.runOnUiThread {
Assert.assertTrue(maplibreMap.cameraPosition.zoom < initialZoom!!)
}
}
@Test
fun doubleTap_minimalMovement() {
validateTestSetup()
var initialZoom: Double? = null
rule.runOnUiThread {
initialZoom = maplibreMap.cameraPosition.zoom
}
onView(withId(R.id.mapView)).perform(quickScale(maplibreMap.gesturesManager.standardScaleGestureDetector.spanSinceStartThreshold / 2, withVelocity = false, duration = 50L))
R.id.mapView.loopFor(MapLibreConstants.ANIMATION_DURATION.toLong())
rule.runOnUiThread {
Assert.assertEquals(initialZoom!! + 1, maplibreMap.cameraPosition.zoom, 0.1)
}
}
@Test
fun doubleTap_overMaxThreshold_ignore_14013() {
// test for https://github.com/mapbox/mapbox-gl-native/issues/14013
validateTestSetup()
var initialZoom: Double? = null
rule.runOnUiThread {
initialZoom = maplibreMap.cameraPosition.zoom
maplibreMap.uiSettings.isQuickZoomGesturesEnabled = false
}
onView(withId(R.id.mapView)).perform(quickScale(maplibreMap.gesturesManager.standardScaleGestureDetector.spanSinceStartThreshold * 2, withVelocity = false, duration = 50L))
R.id.mapView.loopFor(MapLibreConstants.ANIMATION_DURATION.toLong())
rule.runOnUiThread {
Assert.assertEquals(initialZoom!!, maplibreMap.cameraPosition.zoom, 0.01)
}
}
@Test
fun doubleTap_interrupted_moveStillEnabled() {
validateTestSetup()
rule.runOnUiThread {
maplibreMap.moveCamera(CameraUpdateFactory.zoomTo(4.0))
}
onView(withId(R.id.mapView)).perform(quickScale(maplibreMap.gesturesManager.standardScaleGestureDetector.spanSinceStartThreshold / 2, withVelocity = false, duration = 50L, interrupt = true))
var initialCameraPosition: CameraPosition? = null
rule.runOnUiThread {
// zoom in so we can move vertically
maplibreMap.moveCamera(CameraUpdateFactory.zoomTo(4.0))
initialCameraPosition = maplibreMap.cameraPosition
maplibreMap.uiSettings.isQuickZoomGesturesEnabled = false
}
// move to expected target
onView(withId(R.id.mapView)).perform(move(-maxWidth / 2f, -maxHeight / 2f, withVelocity = false))
rule.runOnUiThread {
Assert.assertNotEquals(initialCameraPosition!!.target!!.latitude, maplibreMap.cameraPosition.target!!.latitude, 1.0)
Assert.assertNotEquals(initialCameraPosition!!.target!!.longitude, maplibreMap.cameraPosition.target!!.longitude, 1.0)
}
}
@Test
fun quickZoom_roundTripping() {
validateTestSetup()
rule.runOnUiThread {
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(51.0, 16.0), 3.0))
}
onView(withId(R.id.mapView)).perform(quickScale(300f, withVelocity = false, duration = 750L))
onView(withId(R.id.mapView)).perform(quickScale(-300f, withVelocity = false, duration = 750L))
rule.runOnUiThread {
Assert.assertEquals(3.0, maplibreMap.cameraPosition.zoom, 0.001)
}
}
}

View File

@@ -0,0 +1,454 @@
package org.maplibre.android.maps
import android.graphics.Color
import android.view.View
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers
import junit.framework.TestCase
import org.hamcrest.Matcher
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.maplibre.android.annotations.BaseMarkerOptions
import org.maplibre.android.annotations.Marker
import org.maplibre.android.annotations.MarkerOptions
import org.maplibre.android.annotations.PolygonOptions
import org.maplibre.android.annotations.PolylineOptions
import org.maplibre.android.exceptions.InvalidMarkerPositionException
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapLibreMap.InfoWindowAdapter
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.activity.EspressoTest
// J2K: IDE suggest "Add 'fun' modifier
/**
* This test is responsible for testing the public API.
*
*
* Methods executed on MapboxMap are called from a ViewAction to ensure correct synchronisation
* with the application UI-thread.
*
*/
@Deprecated("remove this file when removing deprecated annotations")
class MapLibreMapTest : EspressoTest() {
//
// InfoWindow
//
@Test
fun testConcurrentInfoWindowEnabled() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
maplibreMap.isAllowConcurrentMultipleOpenInfoWindows = true
assertTrue("ConcurrentWindows should be true", maplibreMap.isAllowConcurrentMultipleOpenInfoWindows)
})
}
@Test
fun testConcurrentInfoWindowDisabled() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
maplibreMap.isAllowConcurrentMultipleOpenInfoWindows = false
TestCase.assertFalse("ConcurrentWindows should be false", maplibreMap.isAllowConcurrentMultipleOpenInfoWindows)
})
}
@Test
fun testInfoWindowAdapter() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val infoWindowAdapter = InfoWindowAdapter { marker: Marker? -> null }
maplibreMap.infoWindowAdapter = infoWindowAdapter
assertEquals("InfoWindowAdpter should be the same", infoWindowAdapter, maplibreMap.infoWindowAdapter)
})
}
//
// Annotations
//
@Test
fun testAddMarker() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerOptions = MarkerOptions().position(LatLng())
val marker = maplibreMap.addMarker(markerOptions)
assertTrue("Marker should be contained", maplibreMap.markers.contains(marker))
})
}
@Test(expected = InvalidMarkerPositionException::class)
fun testAddMarkerInvalidPosition() {
MarkerOptions().marker
}
@Test
fun testAddMarkers() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerList: MutableList<BaseMarkerOptions<*, *>> = ArrayList()
val markerOptions1 = MarkerOptions().position(LatLng()).title("a")
val markerOptions2 = MarkerOptions().position(LatLng()).title("b")
markerList.add(markerOptions1)
markerList.add(markerOptions2)
val markers = maplibreMap.addMarkers(markerList)
assertEquals("Markers size should be 2", 2, maplibreMap.markers.size.toLong())
assertTrue(maplibreMap.markers.contains(markers[0]))
assertTrue(maplibreMap.markers.contains(markers[1]))
})
}
@Test
fun testAddMarkersEmpty() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerList: List<BaseMarkerOptions<*, *>> = ArrayList()
maplibreMap.addMarkers(markerList)
assertEquals("Markers size should be 0", 0, maplibreMap.markers.size.toLong())
})
}
@Test
fun testAddMarkersSingleMarker() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerList: MutableList<BaseMarkerOptions<*, *>> = ArrayList()
val markerOptions = MarkerOptions().title("a").position(LatLng())
markerList.add(markerOptions)
val markers = maplibreMap.addMarkers(markerList)
assertEquals("Markers size should be 1", 1, maplibreMap.markers.size.toLong())
assertTrue(maplibreMap.markers.contains(markers[0]))
})
}
@Test
fun testAddPolygon() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polygonOptions = PolygonOptions().add(LatLng())
val polygon = maplibreMap.addPolygon(polygonOptions)
assertTrue("Polygon should be contained", maplibreMap.polygons.contains(polygon))
})
}
@Test
fun testAddEmptyPolygon() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polygonOptions = PolygonOptions()
val polygon = maplibreMap.addPolygon(polygonOptions)
assertTrue("Polygon should be ignored", !maplibreMap.polygons.contains(polygon))
})
}
@Test
fun testAddPolygons() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polygonList: MutableList<PolygonOptions> = ArrayList()
val polygonOptions1 = PolygonOptions().fillColor(Color.BLACK).add(LatLng())
val polygonOptions2 = PolygonOptions().fillColor(Color.WHITE).add(LatLng())
val polygonOptions3 = PolygonOptions()
polygonList.add(polygonOptions1)
polygonList.add(polygonOptions2)
polygonList.add(polygonOptions3)
maplibreMap.addPolygons(polygonList)
assertEquals("Polygons size should be 2", 2, maplibreMap.polygons.size.toLong())
assertTrue(maplibreMap.polygons.contains(polygonOptions1.polygon))
assertTrue(maplibreMap.polygons.contains(polygonOptions2.polygon))
assertTrue("Polygon should be ignored", !maplibreMap.polygons.contains(polygonOptions3.polygon))
})
}
@Test
fun addPolygonsEmpty() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
maplibreMap.addPolygons(ArrayList())
assertEquals("Polygons size should be 0", 0, maplibreMap.polygons.size.toLong())
})
}
@Test
fun addPolygonsSingle() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polygonList: MutableList<PolygonOptions> = ArrayList()
val polygonOptions = PolygonOptions().fillColor(Color.BLACK).add(LatLng())
polygonList.add(polygonOptions)
maplibreMap.addPolygons(polygonList)
assertEquals("Polygons size should be 1", 1, maplibreMap.polygons.size.toLong())
assertTrue(maplibreMap.polygons.contains(polygonOptions.polygon))
})
}
@Test
fun testAddPolyline() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polylineOptions = PolylineOptions().add(LatLng())
val polyline = maplibreMap.addPolyline(polylineOptions)
assertTrue("Polyline should be contained", maplibreMap.polylines.contains(polyline))
})
}
@Test
fun testAddEmptyPolyline() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polylineOptions = PolylineOptions()
val polyline = maplibreMap.addPolyline(polylineOptions)
assertTrue("Polyline should be ignored", !maplibreMap.polylines.contains(polyline))
})
}
@Test
fun testAddPolylines() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polylineList: MutableList<PolylineOptions> = ArrayList()
val polygonOptions1 = PolylineOptions().color(Color.BLACK).add(LatLng())
val polygonOptions2 = PolylineOptions().color(Color.WHITE).add(LatLng())
val polygonOptions3 = PolylineOptions()
polylineList.add(polygonOptions1)
polylineList.add(polygonOptions2)
polylineList.add(polygonOptions3)
maplibreMap.addPolylines(polylineList)
assertEquals("Polygons size should be 2", 2, maplibreMap.polylines.size.toLong())
assertTrue(maplibreMap.polylines.contains(polygonOptions1.polyline))
assertTrue(maplibreMap.polylines.contains(polygonOptions2.polyline))
assertTrue(
"Polyline should be ignored", !maplibreMap.polylines.contains(polygonOptions3.polyline)
)
})
}
@Test
fun testAddPolylinesEmpty() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
maplibreMap.addPolylines(ArrayList())
assertEquals("Polygons size should be 0", 0, maplibreMap.polylines.size.toLong())
})
}
@Test
fun testAddPolylinesSingle() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polylineList: MutableList<PolylineOptions> = ArrayList()
val polygonOptions = PolylineOptions().color(Color.BLACK).add(LatLng())
polylineList.add(polygonOptions)
maplibreMap.addPolylines(polylineList)
assertEquals("Polygons size should be 1", 1, maplibreMap.polylines.size.toLong())
assertTrue(maplibreMap.polylines.contains(polygonOptions.polyline))
})
}
@Test
fun testRemoveMarker() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerOptions = MarkerOptions().position(LatLng())
val marker = maplibreMap.addMarker(markerOptions)
maplibreMap.removeMarker(marker)
assertTrue("Markers should be empty", maplibreMap.markers.isEmpty())
})
}
@Test
fun testRemovePolygon() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polygonOptions = PolygonOptions()
val polygon = maplibreMap.addPolygon(polygonOptions)
maplibreMap.removePolygon(polygon)
assertTrue("Polygons should be empty", maplibreMap.polylines.isEmpty())
})
}
@Test
fun testRemovePolyline() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val polylineOptions = PolylineOptions()
val polyline = maplibreMap.addPolyline(polylineOptions)
maplibreMap.removePolyline(polyline)
assertTrue("Polylines should be empty", maplibreMap.polylines.isEmpty())
})
}
@Test
fun testRemoveAnnotation() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerOptions = MarkerOptions().position(LatLng())
val marker = maplibreMap.addMarker(markerOptions)
maplibreMap.removeAnnotation(marker)
assertTrue("Annotations should be empty", maplibreMap.annotations.isEmpty())
})
}
@Test
fun testRemoveAnnotationById() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerOptions = MarkerOptions().position(LatLng())
maplibreMap.addMarker(markerOptions)
// id will always be 0 in unit tests
maplibreMap.removeAnnotation(0)
assertTrue("Annotations should be empty", maplibreMap.annotations.isEmpty())
})
}
@Test
fun testRemoveAnnotations() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerList: MutableList<BaseMarkerOptions<*, *>> = ArrayList()
val markerOptions1 = MarkerOptions().title("a").position(LatLng())
val markerOptions2 = MarkerOptions().title("b").position(LatLng())
markerList.add(markerOptions1)
markerList.add(markerOptions2)
maplibreMap.addMarkers(markerList)
maplibreMap.removeAnnotations()
assertTrue("Annotations should be empty", maplibreMap.annotations.isEmpty())
})
}
@Test
fun testClear() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerList: MutableList<BaseMarkerOptions<*, *>> = ArrayList()
val markerOptions1 = MarkerOptions().title("a").position(LatLng())
val markerOptions2 = MarkerOptions().title("b").position(LatLng())
markerList.add(markerOptions1)
markerList.add(markerOptions2)
maplibreMap.addMarkers(markerList)
maplibreMap.clear()
assertTrue("Annotations should be empty", maplibreMap.annotations.isEmpty())
})
}
@Test
fun testRemoveAnnotationsByList() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerList: MutableList<BaseMarkerOptions<*, *>> = ArrayList()
val markerOptions1 = MarkerOptions().title("a").position(LatLng())
val markerOptions2 = MarkerOptions().title("b").position(LatLng())
markerList.add(markerOptions1)
markerList.add(markerOptions2)
val markers = maplibreMap.addMarkers(markerList)
val marker = maplibreMap.addMarker(MarkerOptions().position(LatLng()).title("c"))
maplibreMap.removeAnnotations(markers)
assertTrue("Annotations should not be empty", maplibreMap.annotations.size == 1)
assertTrue("Marker should be contained", maplibreMap.annotations.contains(marker))
})
}
@Test
fun testGetAnnotationById() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerOptions = MarkerOptions().position(LatLng())
val initialMarker = maplibreMap.addMarker(markerOptions)
val retrievedMarker = maplibreMap.getAnnotation(0) as Marker?
assertEquals("Markers should match", initialMarker, retrievedMarker)
})
}
@Test
fun testGetAnnotations() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(
MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Annotations should be non null", maplibreMap.annotations) }
)
}
@Test
fun testGetMarkers() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(
MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Markers should be non null", maplibreMap.markers) }
)
}
@Test
fun testGetPolygons() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Polygons should be non null", maplibreMap.polygons) }
)
}
@Test
fun testGetPolylines() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Polylines should be non null", maplibreMap.polylines) }
)
}
@Test
fun testGetSelectedMarkers() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? -> TestCase.assertNotNull("Selected markers should be non null", maplibreMap.selectedMarkers) }
)
}
@Test
fun testSelectMarker() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerOptions = MarkerOptions().position(LatLng())
val marker = maplibreMap.addMarker(markerOptions)
maplibreMap.selectMarker(marker)
assertTrue("Marker should be contained", maplibreMap.selectedMarkers.contains(marker))
})
}
@Test
fun testDeselectMarker() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerOptions = MarkerOptions().position(LatLng())
val marker = maplibreMap.addMarker(markerOptions)
maplibreMap.selectMarker(marker)
maplibreMap.deselectMarker(marker)
assertTrue("Selected markers should be empty", maplibreMap.selectedMarkers.isEmpty())
})
}
@Test
fun testDeselectMarkers() {
validateTestSetup()
onView(ViewMatchers.withId(R.id.mapView)).perform(MapLibreMapAction { uiController: UiController?, view: View? ->
val markerOptions = MarkerOptions().position(LatLng())
val marker1 = maplibreMap.addMarker(markerOptions)
val marker2 = maplibreMap.addMarker(markerOptions)
maplibreMap.selectMarker(marker1)
maplibreMap.selectMarker(marker2)
maplibreMap.deselectMarkers()
assertTrue("Selected markers should be empty", maplibreMap.selectedMarkers.isEmpty())
})
}
inner class MapLibreMapAction internal constructor(private val invokeViewAction: InvokeViewAction) : ViewAction {
override fun getConstraints(): Matcher<View> {
return ViewMatchers.isDisplayed()
}
override fun getDescription(): String {
return javaClass.simpleName
}
override fun perform(uiController: UiController, view: View) {
invokeViewAction.onViewAction(uiController, view)
}
}
internal fun interface InvokeViewAction {
fun onViewAction(uiController: UiController?, view: View?)
}
}

View File

@@ -0,0 +1,62 @@
package org.maplibre.android.maps
import androidx.test.annotation.UiThreadTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.maplibre.android.AppCenter
import org.maplibre.android.MapLibre.*
import org.maplibre.android.exceptions.MapLibreConfigurationException
@RunWith(AndroidJUnit4ClassRunner::class)
class MapLibreTest : AppCenter() {
private var realToken: String? = null
@Before
fun setup() {
realToken = getApiKey()
}
@Test
@UiThreadTest
fun testConnected() {
assertTrue(isConnected())
// test manual connectivity
setConnected(true)
assertTrue(isConnected())
setConnected(false)
assertFalse(isConnected())
// reset to Android connectivity
setConnected(null)
assertTrue(isConnected())
}
@Test
@UiThreadTest
fun setApiKey() {
setApiKey(API_KEY)
assertSame(API_KEY, getApiKey())
setApiKey(API_KEY_2)
assertSame(API_KEY_2, getApiKey())
}
@Test
@UiThreadTest
fun setNullApiKey() {
assertThrows(MapLibreConfigurationException::class.java) { setApiKey(null) }
}
@After
fun tearDown() {
setApiKey(realToken)
}
companion object {
private const val API_KEY = "pk.0000000001"
private const val API_KEY_2 = "pk.0000000002"
}
}

View File

@@ -0,0 +1,451 @@
package org.maplibre.android.maps
import android.content.Context
import android.graphics.PointF
import androidx.test.annotation.UiThreadTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.platform.app.InstrumentationRegistry
import org.maplibre.android.AppCenter
import org.maplibre.android.MapLibre
import org.maplibre.android.WellKnownTileServer
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.geometry.LatLngBounds
import org.maplibre.android.geometry.ProjectedMeters
import org.maplibre.android.maps.renderer.MapRenderer
import org.maplibre.android.style.layers.TransitionOptions
import org.maplibre.android.testapp.utils.TestConstants
import org.junit.After
import org.junit.Assert
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
class NativeMapViewTest : AppCenter() {
private lateinit var nativeMapView: NativeMap
companion object {
const val DELTA = 0.000001
const val DELTA_BIG = 1.0
const val BEARING_TEST = 60.0
const val PITCH_TEST = 40.0
const val ZOOM_TEST = 16.0
val PADDING_TEST = doubleArrayOf(80.0, 150.0, 0.0, 0.0)
const val WIDTH = 500
const val HEIGHT = WIDTH
val LATLNG_TEST = LatLng(12.0, 34.0)
}
@Before
@UiThreadTest
fun before() {
val context = InstrumentationRegistry.getInstrumentation().context
val apiKey = MapLibre.getApiKey()
MapLibre.getInstance(context, apiKey, WellKnownTileServer.MapTiler)
nativeMapView = NativeMapView(context, 2.0f, false, null, null, DummyRenderer(context))
nativeMapView.resizeView(WIDTH, HEIGHT)
}
@After
@UiThreadTest
fun after() {
val context = InstrumentationRegistry.getInstrumentation().context
val apiKey = MapLibre.getApiKey()
MapLibre.getInstance(context)
nativeMapView.destroy()
}
@Test
@UiThreadTest
fun testSetStyleUrl() {
val expected = Style.getPredefinedStyle("Pastel")
nativeMapView.styleUri = expected
val actual = nativeMapView.styleUri
assertEquals("Style URL should match", expected, actual)
}
@Test
@UiThreadTest
fun testSetStyleJson() {
val expected = "{}"
nativeMapView.styleJson = expected
val actual = nativeMapView.styleJson
assertEquals("Style JSON should match", expected, actual)
}
@Test
@UiThreadTest
fun testBearing() {
val expected = BEARING_TEST
nativeMapView.setBearing(expected, 0)
val actual = nativeMapView.bearing
assertEquals("Bearing should match", expected, actual, DELTA)
}
@Test
@UiThreadTest
fun testLatLng() {
val expected = LATLNG_TEST
nativeMapView.setLatLng(expected, 0)
val actual = nativeMapView.latLng
assertEquals("Latitude should match", expected.latitude, actual.latitude, DELTA)
assertEquals("Longitude should match", expected.longitude, actual.longitude, DELTA)
}
@Test
@UiThreadTest
fun testLatLngPadding() {
val expected = LATLNG_TEST
nativeMapView.contentPadding = PADDING_TEST
nativeMapView.setLatLng(expected, 0)
val actual = nativeMapView.latLng
assertEquals("Latitude should match", expected.latitude, actual.latitude, DELTA)
assertEquals("Longitude should match", expected.longitude, actual.longitude, DELTA)
assertArrayEquals(PADDING_TEST, nativeMapView.cameraPosition.padding, DELTA)
}
@Test
@UiThreadTest
fun testLatLngDefault() {
val expected = LatLng()
val actual = nativeMapView.latLng
assertEquals("Latitude should match", expected.latitude, actual.latitude, DELTA)
assertEquals("Longitude should match", expected.longitude, actual.longitude, DELTA)
}
@Test
@UiThreadTest
fun testBearingDefault() {
val expected = 0.0
val actual = nativeMapView.bearing
assertEquals("Bearing should match", expected, actual, DELTA)
}
@Test
@UiThreadTest
fun testPitch() {
val expected = PITCH_TEST
nativeMapView.setPitch(expected, 0)
val actual = nativeMapView.pitch
assertEquals("Pitch should match", expected, actual, DELTA)
}
@Test
@UiThreadTest
fun testPitchDefault() {
val expected = 0.0
val actual = nativeMapView.pitch
assertEquals("Pitch should match", expected, actual, DELTA)
}
@Test
@UiThreadTest
fun testZoom() {
val expected = ZOOM_TEST
nativeMapView.setZoom(expected, PointF(0.0f, 0.0f), 0)
val actual = nativeMapView.zoom
assertEquals("Zoom should match", expected, actual, DELTA)
}
@Test
@UiThreadTest
fun testZoomDefault() {
val expected = 0.0
val actual = nativeMapView.zoom
assertEquals("Zoom should match", expected, actual, DELTA)
}
@Test
@UiThreadTest
fun testJumpTo() {
val expected = CameraPosition.Builder()
.bearing(BEARING_TEST)
.target(LATLNG_TEST)
.tilt(PITCH_TEST)
.zoom(ZOOM_TEST)
.padding(PADDING_TEST)
.build()
// verify that the lazily set padding is ignored when a value is provided with the camera
nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0)
nativeMapView.jumpTo(LATLNG_TEST, ZOOM_TEST, PITCH_TEST, BEARING_TEST, PADDING_TEST)
val actual = nativeMapView.cameraPosition
assertEquals("Latitude should match", expected.target!!.latitude, actual.target!!.latitude, DELTA)
assertEquals("Longitude should match", expected.target!!.longitude, actual.target!!.longitude, DELTA)
assertEquals("Bearing should match", expected.bearing, actual.bearing, DELTA)
assertEquals("Pitch should match", expected.tilt, actual.tilt, DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, DELTA)
assertArrayEquals(expected.padding, actual.padding, DELTA)
}
@Test
@UiThreadTest
fun testLatLngForPixel() {
val expected = LATLNG_TEST
nativeMapView.setLatLng(LATLNG_TEST, 0)
val actual = nativeMapView.latLngForPixel(
PointF((WIDTH / 2).toFloat(), (HEIGHT / 2).toFloat())
)
assertEquals("Latitude should match", expected.latitude, actual.latitude, DELTA_BIG)
assertEquals("Longitude should match", expected.longitude, actual.longitude, DELTA_BIG)
}
@Test
@UiThreadTest
fun testPixelForLatLng() {
val expected = PointF((WIDTH / 2).toFloat(), (HEIGHT / 2).toFloat())
nativeMapView.setLatLng(LATLNG_TEST, 0)
val actual = nativeMapView.pixelForLatLng(LATLNG_TEST)
assertEquals("X should match", expected.x.toDouble(), actual.x.toDouble(), DELTA_BIG)
assertEquals("Y should match", expected.y.toDouble(), actual.y.toDouble(), DELTA_BIG)
}
@Test
@UiThreadTest
fun testPrefetchTilesTrue() {
val expected = true
nativeMapView.prefetchTiles = true
val actual = nativeMapView.prefetchTiles
assertEquals("Flag should match", expected, actual)
}
@Test
@UiThreadTest
fun testPrefetchTilesFalse() {
val expected = false
nativeMapView.prefetchTiles = false
val actual = nativeMapView.prefetchTiles
assertEquals("Flag should match", expected, actual)
}
@Test
@UiThreadTest
fun testPrefetchTilesDefault() {
val expected = true
val actual = nativeMapView.prefetchTiles
assertEquals("Flag should match", expected, actual)
}
@Test
@UiThreadTest
fun testPrefetchZoomDelta() {
val expected = 2
nativeMapView.prefetchZoomDelta = 2
val actual = nativeMapView.prefetchZoomDelta
assertEquals("Prefetch zoom delta should match", expected, actual)
}
@Test
@UiThreadTest
fun testPrefetchZoomDeltaDefault() {
val expected = 4
val actual = nativeMapView.prefetchZoomDelta
assertEquals("Prefetch zoom delta should match", expected, actual)
}
@Test
@UiThreadTest
fun testSetContentPadding() {
val expected = doubleArrayOf(1.0, 2.0, 3.0, 4.0)
nativeMapView.contentPadding = expected
val actual = nativeMapView.contentPadding
assertEquals("Left should match", expected[0], actual[0], 0.0)
assertEquals("Top should match", expected[1], actual[1], 0.0)
assertEquals("Right should match", expected[2], actual[2], 0.0)
assertEquals("Bottom should match", expected[3], actual[3], 0.0)
}
@Test
@UiThreadTest
fun testSetMinZoom() {
val expected = 12.0
nativeMapView.minZoom = expected
val actual = nativeMapView.minZoom
assertEquals("Min zoom should match", expected, actual, 0.0)
}
@Test
@UiThreadTest
fun testSetMaxZoom() {
val expected = 12.0
nativeMapView.maxZoom = expected
val actual = nativeMapView.maxZoom
assertEquals("Max zoom should match", expected, actual, 0.0)
}
@Test
@UiThreadTest
fun testSetMinPitch() {
val expected = 60.0
nativeMapView.minPitch = expected
val actual = nativeMapView.minPitch
assertEquals("Min Pitch should match", expected, actual, 0.01)
}
@Test
@UiThreadTest
fun testSetMaxPitch() {
val expected = 60.0
nativeMapView.maxPitch = expected
val actual = nativeMapView.maxPitch
assertEquals("Max Pitch should match", expected, actual, 0.01)
}
@Test
@UiThreadTest
fun testGetProjectedMetersAtLatitude() {
val expected = 77973.67021115532
val actual = nativeMapView.getMetersPerPixelAtLatitude(5.0)
assertEquals("Get projected meters should match", expected, actual, DELTA)
}
@Test
@UiThreadTest
fun testLatLngForProjectedMeters() {
val expected = LatLng(0.01796630538796444, 0.02694945852363162)
val actual = nativeMapView.latLngForProjectedMeters(ProjectedMeters(2000.0, 3000.0))
assertEquals("Lat for projected meters", expected.latitude, actual.latitude, DELTA)
assertEquals("Lng for projected meters", expected.longitude, actual.longitude, DELTA)
}
@Test
@UiThreadTest
fun testFlyTo() {
val expected = CameraPosition.Builder()
.zoom(12.0)
.tilt(30.0)
.target(LatLng(12.0, 14.0))
.bearing(20.0)
.padding(PADDING_TEST)
.build()
// verify that the lazily set padding is ignored when a value is provided with the camera
nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0)
nativeMapView.flyTo(expected.target!!, expected.zoom, expected.bearing, expected.tilt, PADDING_TEST, 0)
val actual = nativeMapView.cameraPosition
assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA)
assertEquals("Latitude should match", expected.target!!.latitude, actual.target!!.latitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Longitude should match", expected.target!!.longitude, actual.target!!.longitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA)
Assert.assertArrayEquals(expected.padding, actual.padding, DELTA)
}
@Test
@UiThreadTest
fun testEaseTo() {
val expected = CameraPosition.Builder()
.zoom(12.0)
.tilt(30.0)
.target(LatLng(12.0, 14.0))
.bearing(20.0)
.padding(PADDING_TEST)
.build()
// verify that the lazily set padding is ignored when a value is provided with the camera
nativeMapView.contentPadding = doubleArrayOf(1.0, 2.0, 3.0, 4.0)
nativeMapView.easeTo(expected.target!!, expected.zoom, expected.bearing, expected.tilt, PADDING_TEST, 0, false)
val actual = nativeMapView.cameraPosition
assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA)
assertEquals("Latitude should match", expected.target!!.latitude, actual.target!!.latitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Longitude should match", expected.target!!.longitude, actual.target!!.longitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA)
assertArrayEquals(expected.padding, actual.padding, DELTA)
}
@Test
@UiThreadTest
fun testResetPosition() {
val expected = CameraPosition.Builder()
.zoom(0.0)
.tilt(0.0)
.target(LatLng(0.0, 0.0))
.bearing(0.0)
.padding(PADDING_TEST)
.build()
nativeMapView.jumpTo(LatLng(1.0, 2.0), 12.0, 23.0, 1.0, PADDING_TEST)
nativeMapView.resetPosition()
val actual = nativeMapView.cameraPosition
assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA)
assertEquals("Latitude should match", expected.target!!.latitude, actual.target!!.latitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Longitude should match", expected.target!!.longitude, actual.target!!.longitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA)
assertArrayEquals(expected.padding, actual.padding, DELTA)
}
@Test
@UiThreadTest
fun testGetCameraForLatLngBounds() {
val expected = CameraPosition.Builder()
.zoom(3.5258764777024005)
.tilt(0.0)
.target(LatLng(23.182767623652808, 13.999999999994088))
.bearing(0.0)
.build()
val actual = nativeMapView.getCameraForLatLngBounds(
LatLngBounds.from(30.0, 16.0, 16.0, 12.0),
intArrayOf(0, 0, 0, 0),
0.0,
0.0
)
assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA)
assertEquals("Latitude should match", expected.target!!.latitude, actual.target!!.latitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Longitude should match", expected.target!!.longitude, actual.target!!.longitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA)
}
@Test
@UiThreadTest
fun testMoveBy() {
val expected = CameraPosition.Builder()
.zoom(0.0)
.tilt(0.0)
.target(LatLng(4.21494310024160, -4.218749958739409))
.bearing(0.0)
.build()
nativeMapView.moveBy(12.0, 12.0, 0)
val actual = nativeMapView.cameraPosition
assertEquals("Bearing should match", expected.bearing, actual.bearing, TestConstants.BEARING_DELTA)
assertEquals("Latitude should match", expected.target!!.latitude, actual.target!!.latitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Longitude should match", expected.target!!.longitude, actual.target!!.longitude, TestConstants.LAT_LNG_DELTA)
assertEquals("Tilt should match", expected.tilt, actual.tilt, TestConstants.TILT_DELTA)
assertEquals("Zoom should match", expected.zoom, actual.zoom, TestConstants.ZOOM_DELTA)
}
@Test
@UiThreadTest
fun testTransitionOptions() {
val transitionOptions = TransitionOptions(500, 500)
nativeMapView.transitionOptions = transitionOptions
assertTrue(transitionOptions.isEnablePlacementTransitions)
assertEquals(transitionOptions, nativeMapView.transitionOptions)
}
@Test
@UiThreadTest
fun testTransitionOptions_disablePlacementTransitions() {
val transitionOptions = TransitionOptions(500, 500, false)
nativeMapView.transitionOptions = transitionOptions
assertFalse(transitionOptions.isEnablePlacementTransitions)
assertEquals(transitionOptions, nativeMapView.transitionOptions)
}
class DummyRenderer(context: Context) : MapRenderer(context, null) {
override fun requestRender() {
// no-op
}
override fun queueEvent(runnable: Runnable?) {
// no-op
}
override fun waitForEmpty(timeoutMillis: Long): Long {
// no-op
return 0
}
}
}

View File

@@ -0,0 +1,33 @@
package org.maplibre.android.maps
import androidx.test.espresso.UiController
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.testapp.action.MapLibreMapAction.invoke
import org.maplibre.android.testapp.activity.EspressoTest
import org.junit.Assert.assertEquals
import org.junit.Test
class TransformTest : EspressoTest() {
companion object {
val initialCameraUpdate = CameraUpdateFactory.newLatLngZoom(LatLng(12.0, 12.0), 12.0)!!
}
@Test
fun mapboxMapScrollByWithPadding() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(initialCameraUpdate)
maplibreMap.scrollBy(400.0f, 0.0f)
val expectedCameraPosition = maplibreMap.cameraPosition
maplibreMap.moveCamera(initialCameraUpdate)
maplibreMap.setPadding(250, 250, 0, 0)
maplibreMap.scrollBy(400.0f, 0.0f)
val actualCameraPosition = maplibreMap.cameraPosition
assertEquals("Camera position should match", expectedCameraPosition, actualCameraPosition)
}
}
}

View File

@@ -0,0 +1,520 @@
package org.maplibre.android.maps
import android.graphics.PointF
import androidx.test.espresso.UiController
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.geometry.LatLngBounds
import org.maplibre.android.testapp.action.MapLibreMapAction.invoke
import org.maplibre.android.testapp.activity.BaseTest
import org.maplibre.android.testapp.activity.espresso.PixelTestActivity
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class VisibleRegionTest : BaseTest() {
override fun getActivityClass(): Class<*> {
return PixelTestActivity::class.java
}
override
fun beforeTest() {
super.beforeTest()
mapView = (rule.activity as PixelTestActivity).mapView
}
@Test
fun visibleRegionTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
val visibleRegion = maplibreMap.projection.visibleRegion
assertTrue(latLngs.all { visibleRegion.latLngBounds.contains(it) })
}
}
@Test
fun paddedVisibleRegionTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(
mapView.width / 4,
mapView.height / 4,
mapView.width / 4,
mapView.height / 4
)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 1)
assertTrue(filtered.contains(maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)))
}
}
@Test
fun paddedLeftVisibleRegionTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(mapView.width / 4, 0, 0, 0)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 6)
assertFalse(filtered.contains(maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f)))
}
}
@Test
fun paddedTopVisibleRegionTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(0, mapView.height / 4, 0, 0)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 6)
assertFalse(filtered.contains(maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f)))
}
}
@Test
fun paddedRightVisibleRegionTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(0, 0, mapView.width / 4, 0)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 6)
assertFalse(filtered.contains(maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)))
}
}
@Test
fun paddedBottomVisibleRegionTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(0, 0, 0, mapView.height / 4)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 6)
assertFalse(filtered.contains(maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat())))
}
}
@Test
fun visibleRegionOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat())
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
val visibleRegion = maplibreMap.projection.visibleRegion
assertTrue(latLngs.all { visibleRegion.latLngBounds.contains(it) })
}
}
@Test
fun paddedVisibleRegionOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(
mapView.width / 4,
mapView.height / 4,
mapView.width / 4,
mapView.height / 4
)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 1)
assertTrue(filtered.contains(maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)))
}
}
@Test
fun paddedLeftVisibleRegionOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat())
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(mapView.width / 4, 0, 0, 0)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 6)
assertFalse(filtered.contains(maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f)))
}
}
@Test
fun paddedTopVisibleRegionOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { ui: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
ui.loopMainThreadForAtLeast(5000)
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat())
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(0, mapView.height / 4, 0, 0)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 6)
assertFalse(filtered.contains(maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f)))
}
}
@Test
fun paddedRightVisibleRegionOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat())
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(0, 0, mapView.width / 4, 0)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 6)
assertFalse(filtered.contains(maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)))
}
}
@Test
fun paddedBottomVisibleRegionOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat())
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
maplibreMap.setPadding(0, 0, 0, mapView.height / 4)
val visibleRegion = maplibreMap.projection.getVisibleRegion(false)
val filtered = latLngs.filter { visibleRegion.latLngBounds.contains(it) }
assertTrue(filtered.size == 6)
assertFalse(filtered.contains(maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat())))
}
}
@Test
fun visibleRotatedRegionTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val d = Math.min(maplibreMap.width, maplibreMap.height) / 4
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f - d / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f + d / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f - d / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f + d / 2f)
)
for (bearing in 45 until 360 step 45) {
maplibreMap.moveCamera(CameraUpdateFactory.bearingTo(bearing.toDouble()))
val visibleRegion = maplibreMap.projection.visibleRegion
assertTrue(latLngs.all { visibleRegion.latLngBounds.contains(it) })
}
}
}
@Test
fun visibleRotatedRegionOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
val d = Math.min(maplibreMap.width, maplibreMap.height) / 4
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f - d / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f + d / 2f, mapView.height / 2f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f - d / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f + d / 2f)
)
for (bearing in 45 until 360 step 45) {
maplibreMap.moveCamera(CameraUpdateFactory.bearingTo(bearing.toDouble()))
val visibleRegion = maplibreMap.projection.visibleRegion
assertTrue(latLngs.all { visibleRegion.latLngBounds.contains(it) })
}
}
}
@Test
fun visibleRegionWithBoundsEqualTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0)
maplibreMap.projection.getVisibleCoordinateBounds(bounds)
val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3])
val visibleRegion = maplibreMap.projection.visibleRegion
assertTrue(latLngBounds == visibleRegion.latLngBounds)
assertTrue(latLngs.all { latLngBounds.contains(it) })
}
}
@Test
fun visibleRegionBoundsOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(0f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f),
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f)
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat())
.also { it.longitude += 360 },
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()),
maplibreMap.getLatLngFromScreenCoords(0f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f)
)
val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0)
maplibreMap.projection.getVisibleCoordinateBounds(bounds)
val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3])
assertTrue(latLngs.all { latLngBounds.contains(it) })
}
}
@Test
fun visibleRegionBoundsOverDatelineLatitudeZeroTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0))
val shift = maplibreMap.getLatLngFromScreenCoords(0f, 0f)
maplibreMap.moveCamera(CameraUpdateFactory.newLatLng(LatLng(0.0, 180.0 - shift.longitude)))
val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0)
maplibreMap.projection.getVisibleCoordinateBounds(bounds)
val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3])
val visibleRegion = maplibreMap.projection.visibleRegion
assertTrue(latLngBounds == visibleRegion.latLngBounds)
}
}
@Test
fun visibleRotatedRegionBoundEqualTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0))
val d = Math.min(maplibreMap.width, maplibreMap.height) / 4
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f - d / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f + d / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f - d / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f + d / 2f)
)
for (bearing in 45 until 360 step 45) {
maplibreMap.moveCamera(CameraUpdateFactory.bearingTo(bearing.toDouble()))
val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0)
maplibreMap.projection.getVisibleCoordinateBounds(bounds)
val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3])
val visibleRegion = maplibreMap.projection.visibleRegion
assertTrue(latLngBounds == visibleRegion.latLngBounds)
assertTrue(latLngs.all { latLngBounds.contains(it) })
}
}
}
@Test
fun visibleRotatedRegionBoundsOverDatelineTest() {
validateTestSetup()
invoke(maplibreMap) { _: UiController, maplibreMap: MapLibreMap ->
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 179.0), 8.0))
val d = Math.min(maplibreMap.width, maplibreMap.height) / 4
val latLngs = listOf(
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f - d / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f + d / 2f, mapView.height / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f - d / 2f),
maplibreMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f + d / 2f)
)
for (bearing in 45 until 360 step 45) {
maplibreMap.moveCamera(CameraUpdateFactory.bearingTo(bearing.toDouble()))
val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0)
maplibreMap.projection.getVisibleCoordinateBounds(bounds)
val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3])
assertTrue(latLngs.all { latLngBounds.contains(it) })
}
}
}
private fun MapLibreMap.getLatLngFromScreenCoords(x: Float, y: Float): LatLng {
return this.projection.fromScreenLocation(PointF(x, y))
}
}

View File

@@ -0,0 +1,86 @@
package org.maplibre.android.offline
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import com.mapbox.geojson.Point
import org.maplibre.android.log.Logger
import org.maplibre.android.maps.Style
import org.maplibre.android.testapp.activity.FeatureOverviewActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
/**
* Integration test that validates downloading an offline region from a point geometry at zoomlevel 17
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class OfflineDownloadTest : OfflineRegion.OfflineRegionObserver {
@Rule
@JvmField
var rule = ActivityTestRule(FeatureOverviewActivity::class.java)
private val countDownLatch = CountDownLatch(1)
private lateinit var offlineRegion: OfflineRegion
@Test(timeout = 60000)
fun offlineDownload() {
rule.runOnUiThreadActivity {
OfflineManager.getInstance(rule.activity).createOfflineRegion(
createTestRegionDefinition(),
ByteArray(0),
object : OfflineManager.CreateOfflineRegionCallback {
override fun onCreate(region: OfflineRegion) {
offlineRegion = region
offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE)
offlineRegion.setObserver(this@OfflineDownloadTest)
}
override fun onError(error: String) {
Logger.e(TAG, "Error while creating offline region: $error")
}
}
)
}
if (!countDownLatch.await(60, TimeUnit.SECONDS)) {
throw TimeoutException()
}
}
override fun onStatusChanged(status: OfflineRegionStatus) {
Logger.i(TAG, "Download percentage ${100.0 * status.completedResourceCount / status.requiredResourceCount}")
if (status.isComplete) {
offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE)
countDownLatch.countDown()
}
}
override fun onError(error: OfflineRegionError) {
Logger.e(TAG, "Error while downloading offline region: $error")
}
override fun mapboxTileCountLimitExceeded(limit: Long) {
Logger.e(TAG, "Tile count limited exceeded: $limit")
}
private fun createTestRegionDefinition(): OfflineRegionDefinition {
return OfflineGeometryRegionDefinition(
Style.getPredefinedStyle("Streets"),
Point.fromLngLat(50.847857, 4.360137),
17.0,
17.0,
1.0f,
false
)
}
companion object {
const val TAG = "OfflineDownloadTest"
}
}
fun ActivityTestRule<*>.runOnUiThreadActivity(runnable: () -> Unit) = activity.runOnUiThread(runnable)

View File

@@ -0,0 +1,66 @@
package org.maplibre.android.snapshotter
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.Style
import org.maplibre.android.style.layers.BackgroundLayer
import org.maplibre.android.style.layers.PropertyFactory
import org.maplibre.android.testapp.activity.FeatureOverviewActivity
import org.junit.Assert
import org.junit.Assert.assertNotNull
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
/**
* Integration test that validates if a snapshotter creation
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class MapSnapshotterTest {
@Rule
@JvmField
var rule = ActivityTestRule(FeatureOverviewActivity::class.java)
private val countDownLatch = CountDownLatch(1)
@Test
fun mapSnapshotter() {
var mapSnapshotter: MapSnapshotter?
rule.activity.runOnUiThread {
val bg = BackgroundLayer("rand_tint")
bg.setProperties(PropertyFactory.backgroundColor("rgba(255,128,0,0.7)"))
val options = MapSnapshotter.Options(512, 512)
.withPixelRatio(1.0f)
.withStyleBuilder(
Style.Builder().fromUri(Style.getPredefinedStyle("Satellite Hybrid"))
.withLayerAbove(bg, "country-label")
)
.withCameraPosition(
CameraPosition.Builder()
.zoom(12.0)
.target(LatLng(51.145495, 5.742234))
.build()
)
mapSnapshotter = MapSnapshotter(rule.activity, options)
mapSnapshotter!!.start(
{
assertNotNull(it)
assertNotNull(it.bitmap)
countDownLatch.countDown()
},
{
Assert.fail(it)
}
)
}
if (!countDownLatch.await(30, TimeUnit.SECONDS)) {
throw TimeoutException()
}
}
}

View File

@@ -0,0 +1,22 @@
package org.maplibre.android.style.sources
import androidx.test.annotation.UiThreadTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
@UiThreadTest
class RasterSourceTest {
@Test
fun rasterSource_constructor() {
val tiles = arrayOf("https://example.com")
val tileSet = TileSet("2.1.0", *tiles)
// set bounds on tile set
val bounds = floatArrayOf(-180.0f, -85.05113f, 180.0f, 85.05113f)
tileSet.setBounds(*bounds)
// create RasterSource
RasterSource("source_id", tileSet)
}
}

View File

@@ -0,0 +1,51 @@
package org.maplibre.android.testapp.action;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import org.maplibre.android.maps.MapLibreMap;
import org.hamcrest.Matcher;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
public class MapLibreMapAction implements ViewAction {
private OnInvokeActionListener invokeViewAction;
private MapLibreMap maplibreMap;
public MapLibreMapAction(OnInvokeActionListener invokeViewAction, MapLibreMap maplibreMap) {
this.invokeViewAction = invokeViewAction;
this.maplibreMap = maplibreMap;
}
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
@Override
public void perform(UiController uiController, View view) {
invokeViewAction.onInvokeAction(uiController, maplibreMap);
}
public static void invoke(MapLibreMap maplibreMap, OnInvokeActionListener invokeViewAction) {
onView(withId(android.R.id.content)).perform(new MapLibreMapAction(invokeViewAction, maplibreMap));
}
public interface OnInvokeActionListener {
void onInvokeAction(@NonNull UiController uiController, @NonNull MapLibreMap maplibreMap);
}
}

View File

@@ -0,0 +1,80 @@
package org.maplibre.android.testapp.action;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ActivityInfo;
import android.view.View;
import android.view.ViewGroup;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import org.hamcrest.Matcher;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
public class OrientationAction implements ViewAction {
private final int orientation;
private OrientationAction(int orientation) {
this.orientation = orientation;
}
public static ViewAction orientationLandscape() {
return new OrientationAction(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
public static ViewAction orientationPortrait() {
return new OrientationAction(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public static ViewAction orientationLandscapeReverse() {
return new OrientationAction(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
}
public static ViewAction orientationPortraitReverse() {
return new OrientationAction(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
}
@Override
public Matcher<View> getConstraints() {
return isRoot();
}
@Override
public String getDescription() {
return "change orientation to " + orientation;
}
@Override
public void perform(UiController uiController, View view) {
uiController.loopMainThreadUntilIdle();
Activity activity = getActivity(view.getContext());
if (activity == null && view instanceof ViewGroup) {
ViewGroup v = (ViewGroup) view;
int c = v.getChildCount();
for (int i = 0; i < c && activity == null; ++i) {
activity = getActivity(v.getChildAt(i).getContext());
}
}
activity.setRequestedOrientation(orientation);
}
public static void invoke(ViewAction action) {
onView(isRoot()).perform(action);
}
private Activity getActivity(Context context) {
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
return (Activity) context;
}
context = ((ContextWrapper) context).getBaseContext();
}
return null;
}
}

View File

@@ -0,0 +1,41 @@
package org.maplibre.android.testapp.action;
import android.view.View;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import org.hamcrest.Matcher;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
public final class WaitAction implements ViewAction {
private final long loopTime;
public WaitAction(long loopTime) {
this.loopTime = loopTime;
}
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
@Override
public void perform(UiController uiController, View view) {
uiController.loopMainThreadForAtLeast(loopTime);
}
public static void invoke(long loopTime) {
onView(isRoot()).perform(new WaitAction(loopTime));
}
}

View File

@@ -0,0 +1,116 @@
package org.maplibre.android.testapp.activity;
import android.content.Context;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import androidx.test.rule.ActivityTestRule;
import androidx.test.rule.GrantPermissionRule;
import org.maplibre.android.AppCenter;
import org.maplibre.android.MapLibre;
import org.maplibre.android.maps.MapView;
import org.maplibre.android.maps.MapLibreMap;
import org.maplibre.android.testapp.R;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import timber.log.Timber;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
/**
* Base class for all Activity test hooking into an existing Activity that will load style.
*/
public abstract class BaseTest extends AppCenter {
private static final int WAIT_TIMEOUT = 30; //seconds
@Rule
public ActivityTestRule rule = new ActivityTestRule<>(getActivityClass());
@Rule
public TestName testName = new TestName();
@Rule
public GrantPermissionRule grantLocationPermissionRule = GrantPermissionRule
.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
protected MapLibreMap maplibreMap;
protected MapView mapView;
private final CountDownLatch latch = new CountDownLatch(1);
@Before
@CallSuper
public void beforeTest() {
initialiseMap();
holdTestRunnerForStyleLoad();
}
@After
@CallSuper
public void afterTest() {
super.afterTest();
}
@UiThread
@CallSuper
protected void initMap(MapLibreMap maplibreMap) {
this.maplibreMap = maplibreMap;
maplibreMap.getStyle(style -> latch.countDown());
}
protected void validateTestSetup() {
if (!MapLibre.isConnected()) {
Timber.e("Not connected to the internet while running test");
}
assertNotNull("MapView isn't initialised", mapView);
assertNotNull("MapLibreMap isn't initialised", maplibreMap);
assertNotNull("Style isn't initialised", maplibreMap.getStyle());
assertTrue("Style isn't fully loaded", maplibreMap.getStyle().isFullyLoaded());
}
protected abstract Class getActivityClass();
private void initialiseMap() {
try {
rule.runOnUiThread(() -> {
mapView = rule.getActivity().findViewById(R.id.mapView);
if (mapView != null) {
mapView.getMapAsync(this::initMap);
} else {
Timber.w("Skipping map load test since mapView is not found.");
latch.countDown();
}
});
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
private void holdTestRunnerForStyleLoad() {
boolean interrupted;
try {
interrupted = latch.await(WAIT_TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException ignore) {
interrupted = true;
}
if (!interrupted) {
Timber.e("Timeout occurred for %s", testName.getMethodName());
validateTestSetup();
}
}
protected Context getContext() {
return rule.getActivity();
}
}

View File

@@ -0,0 +1,25 @@
package org.maplibre.android.testapp.activity
import androidx.annotation.UiThread
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style
import org.maplibre.android.testapp.activity.espresso.EspressoTestActivity
/**
* Base class for all tests using EspressoTestActivity as wrapper.
*
*
* Loads "assets/streets.json" as style.
*
*/
open class EspressoTest : BaseTest() {
override fun getActivityClass(): Class<*> {
return EspressoTestActivity::class.java
}
@UiThread
override fun initMap(maplibreMap: MapLibreMap) {
maplibreMap.setStyle(Style.Builder().fromUri("asset://streets.json"))
super.initMap(maplibreMap)
}
}

View File

@@ -0,0 +1,141 @@
package org.maplibre.android.testapp.annotations;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
import androidx.core.content.res.ResourcesCompat;
import androidx.test.annotation.UiThreadTest;
import org.maplibre.android.annotations.Icon;
import org.maplibre.android.annotations.IconFactory;
import org.maplibre.android.annotations.Marker;
import org.maplibre.android.annotations.MarkerOptions;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.maps.IconManagerResolver;
import org.maplibre.android.testapp.R;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.maplibre.android.testapp.utils.IconUtils;
import org.junit.Before;
import org.junit.Test;
import java.util.Map;
/**
* Tests integration between Icons and Markers
*/
public class IconTest extends EspressoTest {
private Map<Icon, Integer> iconMap;
@Before
public void beforeTest() {
super.beforeTest();
iconMap = new IconManagerResolver(maplibreMap).getIconMap();
}
@Test
@UiThreadTest
public void testAddSameIconMarker() {
validateTestSetup();
Icon defaultMarker = IconFactory.getInstance(rule.getActivity()).defaultMarker();
maplibreMap.addMarker(new MarkerOptions().position(new LatLng()));
maplibreMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
assertEquals(1, iconMap.size());
assertEquals(2, iconMap.get(defaultMarker), 0);
}
@Test
@UiThreadTest
public void testAddDifferentIconMarker() {
validateTestSetup();
Icon icon = IconFactory.getInstance(rule.getActivity()).fromResource(R.drawable.maplibre_logo_icon);
maplibreMap.addMarker(new MarkerOptions().icon(icon).position(new LatLng()));
maplibreMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
assertEquals(iconMap.size(), 2);
assertTrue(iconMap.containsKey(icon));
assertTrue(iconMap.get(icon) == 1);
}
@Test
@UiThreadTest
public void testAddRemoveIconMarker() {
validateTestSetup();
Icon icon = IconFactory.getInstance(rule.getActivity()).fromResource(R.drawable.maplibre_logo_icon);
Marker marker = maplibreMap.addMarker(new MarkerOptions().icon(icon).position(new LatLng()));
maplibreMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
assertEquals(iconMap.size(), 2);
assertTrue(iconMap.containsKey(icon));
assertTrue(iconMap.get(icon) == 1);
maplibreMap.removeMarker(marker);
assertEquals(iconMap.size(), 1);
assertFalse(iconMap.containsKey(icon));
}
@Test
@UiThreadTest
public void testAddRemoveDefaultMarker() {
validateTestSetup();
Marker marker = maplibreMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
assertEquals(iconMap.size(), 1);
maplibreMap.removeMarker(marker);
assertEquals(iconMap.size(), 0);
maplibreMap.addMarker(new MarkerOptions().position(new LatLng()));
assertEquals(iconMap.size(), 1);
}
@Test
@UiThreadTest
public void testAddRemoveMany() {
validateTestSetup();
Activity activity = rule.getActivity();
IconFactory iconFactory = IconFactory.getInstance(activity);
// add 2 default icon markers
Marker defaultMarkerOne = maplibreMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
Marker defaultMarkerTwo = maplibreMap.addMarker(new MarkerOptions().position(new LatLng(2, 1)));
// add 4 unique icon markers
maplibreMap.addMarker(new MarkerOptions()
.icon(iconFactory.fromResource(R.drawable.maplibre_logo_icon))
.position(new LatLng(3, 1))
);
maplibreMap.addMarker(new MarkerOptions()
.icon(iconFactory.fromResource(R.drawable.maplibre_compass_icon))
.position(new LatLng(4, 1))
);
maplibreMap.addMarker(new MarkerOptions()
.icon(IconUtils.drawableToIcon(activity, R.drawable.ic_stars,
ResourcesCompat.getColor(activity.getResources(),
R.color.blueAccent, activity.getTheme())))
.position(new LatLng(5, 1))
);
maplibreMap.addMarker(new MarkerOptions()
.icon(iconFactory.fromResource(R.drawable.ic_android))
.position(new LatLng(6, 1))
);
assertEquals("Amount of icons should match 5", 5, iconMap.size());
assertEquals("Refcounter of default marker should match 2", 2, iconMap.get(iconFactory.defaultMarker()), 0);
maplibreMap.removeMarker(defaultMarkerOne);
assertEquals("Amount of icons should match 5", 5, iconMap.size());
assertEquals("Refcounter of default marker should match 1", 1, iconMap.get(iconFactory.defaultMarker()), 0);
maplibreMap.removeMarker(defaultMarkerTwo);
assertEquals("Amount of icons should match 4", 4, iconMap.size());
assertNull("DefaultMarker shouldn't exist anymore", iconMap.get(iconFactory.defaultMarker()));
maplibreMap.clear();
assertEquals("Amount of icons should match 0", 0, iconMap.size());
}
}

View File

@@ -0,0 +1,63 @@
package org.maplibre.android.testapp.annotations;
import org.maplibre.android.annotations.Marker;
import org.maplibre.android.annotations.MarkerOptions;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.testapp.action.MapLibreMapAction;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.maplibre.android.testapp.utils.TestConstants;
import org.junit.Ignore;
import org.junit.Test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class MarkerTest extends EspressoTest {
private Marker marker;
@Test
@Ignore
public void addMarkerTest() {
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
assertEquals("Markers should be empty", 0, mapboxMap.getMarkers().size());
MarkerOptions options = new MarkerOptions();
options.setPosition(new LatLng());
options.setSnippet(TestConstants.TEXT_MARKER_SNIPPET);
options.setTitle(TestConstants.TEXT_MARKER_TITLE);
marker = mapboxMap.addMarker(options);
assertEquals("Markers size should be 1, ", 1, mapboxMap.getMarkers().size());
assertEquals("Marker id should be 0", 0, marker.getId());
assertEquals("Marker target should match", new LatLng(), marker.getPosition());
assertEquals("Marker snippet should match", TestConstants.TEXT_MARKER_SNIPPET, marker.getSnippet());
assertEquals("Marker target should match", TestConstants.TEXT_MARKER_TITLE, marker.getTitle());
mapboxMap.clear();
assertEquals("Markers should be empty", 0, mapboxMap.getMarkers().size());
});
}
@Test
@Ignore
public void showInfoWindowTest() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
final MarkerOptions options = new MarkerOptions();
options.setPosition(new LatLng());
options.setSnippet(TestConstants.TEXT_MARKER_SNIPPET);
options.setTitle(TestConstants.TEXT_MARKER_TITLE);
marker = mapboxMap.addMarker(options);
mapboxMap.selectMarker(marker);
});
onView(withText(TestConstants.TEXT_MARKER_TITLE)).check(matches(isDisplayed()));
onView(withText(TestConstants.TEXT_MARKER_SNIPPET)).check(matches(isDisplayed()));
}
}

View File

@@ -0,0 +1,46 @@
package org.maplibre.android.testapp.annotations;
import android.graphics.Color;
import org.maplibre.android.annotations.Polygon;
import org.maplibre.android.annotations.PolygonOptions;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.junit.Ignore;
import org.junit.Test;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class PolygonTest extends EspressoTest {
@Test
@Ignore
public void addPolygonTest() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLngOne = new LatLng();
LatLng latLngTwo = new LatLng(1, 0);
LatLng latLngThree = new LatLng(1, 1);
assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
final PolygonOptions options = new PolygonOptions();
options.strokeColor(Color.BLUE);
options.fillColor(Color.RED);
options.add(latLngOne);
options.add(latLngTwo);
options.add(latLngThree);
Polygon polygon = mapboxMap.addPolygon(options);
assertEquals("Polygons should be 1", 1, mapboxMap.getPolygons().size());
assertEquals("Polygon id should be 0", 0, polygon.getId());
assertEquals("Polygon points size should match", 3, polygon.getPoints().size());
assertEquals("Polygon stroke color should match", Color.BLUE, polygon.getStrokeColor());
assertEquals("Polygon target should match", Color.RED, polygon.getFillColor());
mapboxMap.clear();
assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
});
}
}

View File

@@ -0,0 +1,42 @@
package org.maplibre.android.testapp.annotations;
import android.graphics.Color;
import org.maplibre.android.annotations.Polyline;
import org.maplibre.android.annotations.PolylineOptions;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.junit.Ignore;
import org.junit.Test;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class PolylineTest extends EspressoTest {
@Test
@Ignore
public void addPolylineTest() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLngOne = new LatLng();
LatLng latLngTwo = new LatLng(1, 0);
assertEquals("Polygons should be empty", 0, mapboxMap.getPolygons().size());
final PolylineOptions options = new PolylineOptions();
options.color(Color.BLUE);
options.add(latLngOne);
options.add(latLngTwo);
Polyline polyline = mapboxMap.addPolyline(options);
assertEquals("Polylines should be 1", 1, mapboxMap.getPolylines().size());
assertEquals("Polyline id should be 0", 0, polyline.getId());
assertEquals("Polyline points size should match", 2, polyline.getPoints().size());
assertEquals("Polyline stroke color should match", Color.BLUE, polyline.getColor());
mapboxMap.clear();
assertEquals("Polyline should be empty", 0, mapboxMap.getPolylines().size());
});
}
}

View File

@@ -0,0 +1,11 @@
package org.maplibre.android.testapp.camera;
import org.maplibre.android.camera.CameraUpdate;
import org.maplibre.android.maps.MapLibreMap;
public class CameraAnimateTest extends CameraTest {
@Override
void executeCameraMovement(CameraUpdate cameraUpdate, MapLibreMap.CancelableCallback callback) {
maplibreMap.animateCamera(cameraUpdate, callback);
}
}

View File

@@ -0,0 +1,12 @@
package org.maplibre.android.testapp.camera;
import org.maplibre.android.camera.CameraUpdate;
import org.maplibre.android.maps.MapLibreMap;
public class CameraEaseTest extends CameraTest {
@Override
void executeCameraMovement(CameraUpdate cameraUpdate, MapLibreMap.CancelableCallback callback) {
maplibreMap.easeCamera(cameraUpdate, callback);
}
}

View File

@@ -0,0 +1,262 @@
package org.maplibre.android.testapp.camera;
import androidx.annotation.NonNull;
import androidx.test.annotation.UiThreadTest;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.camera.CameraPosition;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.geometry.LatLngBounds;
import org.maplibre.android.testapp.activity.BaseTest;
import org.maplibre.android.testapp.activity.espresso.DeviceIndependentTestActivity;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class CameraForTest extends BaseTest {
@Test
@UiThreadTest
public void testGetCameraForLatLngBounds() {
validateTestSetup();
CameraPosition actualPosition = maplibreMap.getCameraForLatLngBounds(
LatLngBounds.from(10, 10, -10, -10));
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng()).zoom(4.16).tilt(0).bearing(0).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Bearing should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match", expectedPosition.tilt, actualPosition.tilt, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForLatLngBoundsPadding() {
validateTestSetup();
CameraPosition actualPosition = maplibreMap.getCameraForLatLngBounds(
LatLngBounds.from(10, 10, -10, -10), new int[] {5, 5, 5, 5});
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng()).zoom(4.13).tilt(0).bearing(0).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForLatLngBoundsBearing() {
validateTestSetup();
CameraPosition actualPosition = maplibreMap.getCameraForLatLngBounds(
LatLngBounds.from(10, 10, -10, -10), 45, 0);
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng()).zoom(3.66).tilt(0).bearing(45).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForLatLngBoundsTilt() {
validateTestSetup();
CameraPosition actualPosition = maplibreMap.getCameraForLatLngBounds(
LatLngBounds.from(10, 10, -10, -10), 0, 45);
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng(-0.264576975267, 0)).zoom(4.13).tilt(45).bearing(0).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForLatLngBoundsAll() {
validateTestSetup();
CameraPosition actualPosition = maplibreMap.getCameraForLatLngBounds(
LatLngBounds.from(10, 10, -10, -10), new int[] {5, 5, 5, 5}, 45, 45);
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng(-0.3732134634, -0.3713191053)).zoom(3.63).tilt(45).bearing(45).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForGeometry() {
validateTestSetup();
List<List<Point>> polygonDefinition = getPolygonDefinition();
CameraPosition actualPosition = maplibreMap.getCameraForGeometry(Polygon.fromLngLats(polygonDefinition));
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng()).zoom(4.16).tilt(0).bearing(0).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Bearing should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match", expectedPosition.tilt, actualPosition.tilt, 0.01f);
}
@NonNull
private List<List<Point>> getPolygonDefinition() {
return new ArrayList<List<Point>>() {
{
add(new ArrayList<Point>() {
{
add(Point.fromLngLat(10, 10));
add(Point.fromLngLat(-10, 10));
add(Point.fromLngLat(-10, -10));
add(Point.fromLngLat(10, -10));
}
});
}
};
}
@Test
@UiThreadTest
public void testGetCameraForGeometryPadding() {
validateTestSetup();
List<List<Point>> polygonDefinition = getPolygonDefinition();
CameraPosition actualPosition = maplibreMap.getCameraForGeometry(Polygon.fromLngLats(polygonDefinition),
new int[] {5, 5, 5, 5});
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng()).zoom(4.13).tilt(0).bearing(0).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForGeometryBearing() {
validateTestSetup();
List<List<Point>> polygonDefinition = getPolygonDefinition();
CameraPosition actualPosition = maplibreMap.getCameraForGeometry(Polygon.fromLngLats(polygonDefinition), 45, 0);
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng()).zoom(3.66).tilt(0).bearing(45).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForGeometryTilt() {
validateTestSetup();
List<List<Point>> polygonDefinition = getPolygonDefinition();
CameraPosition actualPosition = maplibreMap.getCameraForGeometry(Polygon.fromLngLats(polygonDefinition), 0, 45);
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng(-0.2645769752, 0)).zoom(4.13).tilt(45).bearing(0).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForGeometryAll() {
validateTestSetup();
List<List<Point>> polygonDefinition = getPolygonDefinition();
CameraPosition actualPosition = maplibreMap.getCameraForGeometry(Polygon.fromLngLats(polygonDefinition),
new int[] {5, 5, 5, 5}, 45, 45);
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng(-0.373213463, -0.37131910534)).zoom(3.63).tilt(45).bearing(45).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Test
@UiThreadTest
public void testGetCameraForGeometryDeprecatedApi() {
validateTestSetup();
List<List<Point>> polygonDefinition = getPolygonDefinition();
CameraPosition actualPosition = maplibreMap.getCameraForGeometry(
Polygon.fromLngLats(polygonDefinition),
new int[] {5, 5, 5, 5},
45, 0);
CameraPosition expectedPosition = new CameraPosition.Builder()
.target(new LatLng()).zoom(3.63).tilt(0).bearing(45).build();
assertEquals("Latitude should match",
expectedPosition.target.getLatitude(), actualPosition.target.getLatitude(), 0.00001f);
assertEquals("Longitude should match",
expectedPosition.target.getLongitude(), actualPosition.target.getLongitude(), 0.00001f);
assertEquals("Zoom should match",
expectedPosition.zoom, actualPosition.zoom, 0.01f);
assertEquals("Tilt should match",
expectedPosition.tilt, actualPosition.tilt, 0.01f);
assertEquals("Bearing should match",
expectedPosition.bearing, actualPosition.bearing, 0.01f);
}
@Override
protected Class getActivityClass() {
return DeviceIndependentTestActivity.class;
}
}

View File

@@ -0,0 +1,12 @@
package org.maplibre.android.testapp.camera;
import org.maplibre.android.camera.CameraUpdate;
import org.maplibre.android.maps.MapLibreMap;
public class CameraMoveTest extends CameraTest {
@Override
void executeCameraMovement(CameraUpdate cameraUpdate, MapLibreMap.CancelableCallback callback) {
maplibreMap.moveCamera(cameraUpdate, callback);
}
}

View File

@@ -0,0 +1,308 @@
package org.maplibre.android.testapp.camera;
import org.maplibre.android.camera.CameraPosition;
import org.maplibre.android.camera.CameraUpdate;
import org.maplibre.android.camera.CameraUpdateFactory;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.geometry.LatLngBounds;
import org.maplibre.android.maps.MapLibreMap;
import org.maplibre.android.testapp.activity.BaseTest;
import org.maplibre.android.testapp.activity.espresso.DeviceIndependentTestActivity;
import org.maplibre.android.testapp.utils.TestConstants;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public abstract class CameraTest extends BaseTest {
private CountDownLatch latch;
@Override
protected Class getActivityClass() {
return DeviceIndependentTestActivity.class;
}
@Override
public void beforeTest() {
super.beforeTest();
latch = new CountDownLatch(1);
}
@Test
public void testToCameraPositionTarget() throws InterruptedException {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
float zoom = 1.0f;
LatLng moveTarget = new LatLng(1, 1);
CameraPosition initialPosition = new CameraPosition.Builder().target(
new LatLng()).zoom(zoom).bearing(0).tilt(0).padding(new double[4]).build();
CameraPosition cameraPosition = mapboxMap.getCameraPosition();
assertEquals("Default camera position should match default", cameraPosition, initialPosition);
executeCameraMovement(CameraUpdateFactory.newLatLng(moveTarget), new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0, new double[4]);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, moveTarget, zoom, 0, 0, new double[4]);
latch.countDown();
}
});
});
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
}
@Test
public void testToCameraPositionTargetZoom() throws InterruptedException {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
final float moveZoom = 15.5f;
final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
executeCameraMovement(CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom),
new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0, new double[4]);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, moveTarget, moveZoom, 0, 0, new double[4]);
latch.countDown();
}
});
});
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
}
@Test
public void testToCameraPosition() throws InterruptedException {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
final float moveZoom = 15.5f;
final float moveTilt = 45.5f;
final float moveBearing = 12.5f;
final double[] movePadding = new double[] {0, 500, 350, 1};
executeCameraMovement(CameraUpdateFactory.newCameraPosition(
new CameraPosition.Builder()
.target(moveTarget)
.zoom(moveZoom)
.tilt(moveTilt)
.bearing(moveBearing)
.padding(movePadding)
.build()),
new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt, movePadding);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, moveTarget, moveZoom, moveBearing, moveTilt, movePadding);
latch.countDown();
}
});
});
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
}
@Test
public void testToBounds() throws InterruptedException {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
final LatLng centerBounds = new LatLng(1, 1);
LatLng cornerOne = new LatLng();
LatLng cornerTwo = new LatLng(2, 2);
final LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(cornerOne);
builder.include(cornerTwo);
executeCameraMovement(CameraUpdateFactory.newLatLngBounds(builder.build(), 0),
new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0, new double[4]);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, centerBounds, mapboxMap.getCameraPosition().zoom, 0, 0, new double[4]);
latch.countDown();
}
});
});
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
}
@Test
public void testToZoomIn() throws InterruptedException {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
float zoom = 1.0f;
executeCameraMovement(CameraUpdateFactory.zoomIn(), new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0, new double[4]);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + 1, 0, 0, new double[4]);
latch.countDown();
}
});
});
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
}
@Test
public void testToZoomOut() throws InterruptedException {
float zoom = 10.0f;
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) ->
executeCameraMovement(CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom),
new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0, new double[4]);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom, 0, 0, new double[4]);
latch.countDown();
}
})
);
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
latch = new CountDownLatch(1);
invoke(maplibreMap, (uiController, mapboxMap) -> {
executeCameraMovement(CameraUpdateFactory.zoomOut(), new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0, new double[4]);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom - 1, 0, 0, new double[4]);
latch.countDown();
}
});
});
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
}
@Test
public void testToZoomBy() throws InterruptedException {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
float zoom = 1.0f;
final float zoomBy = 2.45f;
executeCameraMovement(CameraUpdateFactory.zoomBy(zoomBy), new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0, new double[4]);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoom + zoomBy, 0, 0, new double[4]);
latch.countDown();
}
});
});
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
}
@Test
public void testToZoomTo() throws InterruptedException {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
final float zoomTo = 2.45f;
executeCameraMovement(CameraUpdateFactory.zoomTo(zoomTo), new MapLibreMap.CancelableCallback() {
@Override
public void onCancel() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0, new double[4]);
latch.countDown();
}
@Override
public void onFinish() {
verifyCameraPosition(mapboxMap, mapboxMap.getCameraPosition().target, zoomTo, 0, 0, new double[4]);
latch.countDown();
}
});
});
if (!latch.await(10, TimeUnit.SECONDS)) {
Assert.fail("timeout");
}
}
abstract void executeCameraMovement(CameraUpdate cameraUpdate, MapLibreMap.CancelableCallback callback);
private void verifyCameraPosition(MapLibreMap maplibreMap, LatLng moveTarget, double moveZoom, double moveBearing,
double moveTilt, double[] padding) {
CameraPosition cameraPosition = maplibreMap.getCameraPosition();
assertEquals("Moved camera position latitude should match", cameraPosition.target.getLatitude(),
moveTarget.getLatitude(), TestConstants.LAT_LNG_DELTA);
assertEquals("Moved camera position longitude should match", cameraPosition.target.getLongitude(),
moveTarget.getLongitude(), TestConstants.LAT_LNG_DELTA);
assertEquals("Moved zoom should match", cameraPosition.zoom, moveZoom, TestConstants.ZOOM_DELTA);
assertEquals("Moved zoom should match", cameraPosition.tilt, moveTilt, TestConstants.TILT_DELTA);
assertEquals("Moved bearing should match", cameraPosition.bearing, moveBearing, TestConstants.BEARING_DELTA);
assertArrayEquals("Moved padding should match", cameraPosition.padding, padding, TestConstants.PADDING_DELTA);
}
}

View File

@@ -0,0 +1,35 @@
package org.maplibre.android.testapp.fragment
import androidx.test.espresso.Espresso
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.AppCenter
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.activity.maplayout.MapInDialogActivity
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Regression test that validates that a map inside a DialogFragment can be opened and closed.
*/
@RunWith(AndroidJUnit4ClassRunner::class)
@LargeTest
class MapDialogFragmentTest : AppCenter() {
@get:Rule
var activityRule: ActivityTestRule<MapInDialogActivity> = ActivityTestRule(MapInDialogActivity::class.java)
@Test
@Ignore("Probably does not work")
fun openCloseDialog() {
onView(withId(R.id.button_open_dialog)).perform(click())
Thread.sleep(2500)
Espresso.pressBack()
}
}

View File

@@ -0,0 +1,150 @@
package org.maplibre.android.testapp.geometry;
import androidx.test.annotation.UiThreadTest;
import com.google.gson.JsonArray;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.PropertyFactory;
import org.maplibre.android.style.layers.SymbolLayer;
import org.maplibre.android.style.sources.GeoJsonSource;
import org.maplibre.android.testapp.action.MapLibreMapAction;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.maplibre.android.testapp.utils.TestingAsyncUtils;
import org.junit.Test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static com.mapbox.geojson.Feature.fromGeometry;
import static com.mapbox.geojson.FeatureCollection.fromFeatures;
import static com.mapbox.geojson.GeometryCollection.fromGeometries;
import static com.mapbox.geojson.LineString.fromLngLats;
import static com.mapbox.geojson.MultiLineString.fromLineString;
import static com.mapbox.geojson.MultiPolygon.fromPolygon;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertFalse;
/**
* Instrumentation test to validate java geojson conversion to c++
*/
public class GeoJsonConversionTest extends EspressoTest {
// Regression test for #12343
@Test
@UiThreadTest
public void testEmptyFeatureCollection() {
validateTestSetup();
maplibreMap.getStyle().addSource(
new GeoJsonSource("test-id",
fromFeatures(singletonList(fromGeometry(fromGeometries(emptyList()))))
)
);
maplibreMap.getStyle().addLayer(new SymbolLayer("test-id", "test-id"));
}
@Test
@UiThreadTest
public void testPointFeatureCollection() {
validateTestSetup();
maplibreMap.getStyle().addSource(
new GeoJsonSource("test-id",
fromFeatures(singletonList(fromGeometry(Point.fromLngLat(0.0, 0.0))))
)
);
maplibreMap.getStyle().addLayer(new SymbolLayer("test-id", "test-id"));
}
@Test
@UiThreadTest
public void testMultiPointFeatureCollection() {
validateTestSetup();
maplibreMap.getStyle().addSource(
new GeoJsonSource("test-id",
fromFeatures(singletonList(fromGeometry(fromLngLats(emptyList()))))
)
);
maplibreMap.getStyle().addLayer(new SymbolLayer("test-id", "test-id"));
}
@Test
@UiThreadTest
public void testPolygonFeatureCollection() {
validateTestSetup();
maplibreMap.getStyle().addSource(
new GeoJsonSource("test-id",
fromFeatures(singletonList(fromGeometry(Polygon.fromLngLats(emptyList()))))
)
);
maplibreMap.getStyle().addLayer(new SymbolLayer("test-id", "test-id"));
}
@Test
@UiThreadTest
public void testMultiPolygonFeatureCollection() {
validateTestSetup();
maplibreMap.getStyle().addSource(
new GeoJsonSource("test-id",
fromFeatures(singletonList(fromGeometry(fromPolygon(Polygon.fromLngLats(emptyList())))))
)
);
maplibreMap.getStyle().addLayer(new SymbolLayer("test-id", "test-id"));
}
@Test
@UiThreadTest
public void testLineStringFeatureCollection() {
validateTestSetup();
maplibreMap.getStyle().addSource(
new GeoJsonSource("test-id",
fromFeatures(singletonList(fromGeometry(fromLngLats(emptyList()))))
)
);
maplibreMap.getStyle().addLayer(new SymbolLayer("test-id", "test-id"));
}
@Test
@UiThreadTest
public void testMultiLineStringFeatureCollection() {
validateTestSetup();
maplibreMap.getStyle().addSource(
new GeoJsonSource("test-id",
fromFeatures(singletonList(fromGeometry(fromLineString(fromLngLats(emptyList())))))
)
);
maplibreMap.getStyle().addLayer(new SymbolLayer("test-id", "test-id"));
}
@Test
public void testNegativeNumberPropertyConversion() {
validateTestSetup();
onView(isRoot()).perform(new MapLibreMapAction((uiController, mapboxMap) -> {
LatLng latLng = new LatLng();
Feature feature = Feature.fromGeometry(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()));
JsonArray foregroundJsonArray = new JsonArray();
foregroundJsonArray.add(0f);
foregroundJsonArray.add(-3f);
feature.addProperty("property", foregroundJsonArray);
GeoJsonSource source = new GeoJsonSource("source", feature);
mapboxMap.getStyle().addSource(source);
SymbolLayer layer = new SymbolLayer("layer", "source")
.withProperties(
PropertyFactory.iconOffset(Expression.get("property")),
PropertyFactory.iconImage("zoo-15")
);
mapboxMap.getStyle().addLayer(layer);
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng)).isEmpty());
}, maplibreMap));
}
}

View File

@@ -0,0 +1,66 @@
package org.maplibre.android.testapp.geometry;
import static org.junit.Assert.assertEquals;
import org.maplibre.android.camera.CameraUpdateFactory;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.geometry.LatLngBounds;
import org.maplibre.android.testapp.action.MapLibreMapAction;
import org.maplibre.android.testapp.activity.BaseTest;
import org.maplibre.android.testapp.activity.feature.QueryRenderedFeaturesBoxHighlightActivity;
import org.maplibre.android.testapp.utils.TestConstants;
import org.junit.Test;
/**
* Instrumentation test to validate integration of LatLngBounds
*/
public class LatLngBoundsTest extends BaseTest {
private static final double MAP_BEARING = 50;
@Override
protected Class getActivityClass() {
return QueryRenderedFeaturesBoxHighlightActivity.class;
}
@Test
public void testLatLngBounds() {
// regression test for #9322
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLngBounds bounds = new LatLngBounds.Builder()
.include(new LatLng(48.8589506, 2.2773457))
.include(new LatLng(47.2383171, -1.6309316))
.build();
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
});
}
@Test
public void testLatLngBoundsBearing() {
// regression test for #12549
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLngBounds bounds = new LatLngBounds.Builder()
.include(new LatLng(48.8589506, 2.2773457))
.include(new LatLng(47.2383171, -1.6309316))
.build();
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(MAP_BEARING));
assertEquals(
"Initial bearing should match for latlngbounds",
mapboxMap.getCameraPosition().bearing,
MAP_BEARING,
TestConstants.BEARING_DELTA
);
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
assertEquals("Bearing should match after resetting latlngbounds",
mapboxMap.getCameraPosition().bearing,
MAP_BEARING,
TestConstants.BEARING_DELTA);
});
}
}

View File

@@ -0,0 +1,157 @@
package org.maplibre.android.testapp.maps
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.AppCenter
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.Style
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.activity.espresso.EspressoTestActivity
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@RunWith(AndroidJUnit4ClassRunner::class)
class ImageMissingTest : AppCenter() {
@Rule
@JvmField
var rule = ActivityTestRule(EspressoTestActivity::class.java)
private lateinit var mapView: MapView
private lateinit var latch: CountDownLatch
@Before
fun setup() {
latch = CountDownLatch(1)
}
@Test
fun testMissingImage() {
rule.runOnUiThread {
initMap(styleJson).addOnStyleImageMissingListener {
assertEquals("missing-icon", it)
latch.countDown()
}
}
if (!latch.await(10, TimeUnit.SECONDS)) {
throw TimeoutException()
}
}
@Test
fun testMissingImage_invalidSprite() {
rule.runOnUiThread {
initMap(styleJsonInvalidSprite).addOnStyleImageMissingListener {
assertEquals("missing-icon", it)
latch.countDown()
}
}
if (!latch.await(10, TimeUnit.SECONDS)) {
throw TimeoutException()
}
}
private fun initMap(style: String): MapView {
mapView = rule.activity.findViewById(R.id.mapView)
mapView.getMapAsync { it.setStyle(Style.Builder().fromJson(style)) }
return mapView
}
companion object {
private const val styleJson =
"""
{
"version": 8,
"name": "Streets",
"sprite": "maptiler://sprites/streets/sprite",
"glyphs": "maptiler://fonts/{fontstack}/{range}.pbf",
"sources": {
"point": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [0, 0]
}
}
}
},
"layers": [{
"id": "bg",
"type": "background",
"paint": {
"background-color": "#f00"
}
}, {
"id": "point",
"type": "circle",
"source": "point",
"paint": {
"circle-radius": 100
}
}, {
"id": "icon",
"type": "symbol",
"source": "point",
"layout": {
"icon-image": "missing-icon"
}
}]
}
"""
private const val styleJsonInvalidSprite =
"""
{
"version": 8,
"name": "Streets",
"sprite": "maptiler://sprites/streets/sprite",
"glyphs": "maptiler://fonts/{fontstack}/{range}.pbf",
"sources": {
"point": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [0, 0]
}
}
}
},
"layers": [{
"id": "bg",
"type": "background",
"paint": {
"background-color": "#f00"
}
}, {
"id": "point",
"type": "circle",
"source": "point",
"paint": {
"circle-radius": 100
}
}, {
"id": "icon",
"type": "symbol",
"source": "point",
"layout": {
"icon-image": "missing-icon"
}
}]
}
"""
}
}

View File

@@ -0,0 +1,164 @@
package org.maplibre.android.testapp.maps
import android.graphics.Bitmap
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.AppCenter
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.activity.espresso.EspressoTestActivity
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@RunWith(AndroidJUnit4ClassRunner::class)
class RemoveUnusedImagesTest : AppCenter() {
@Rule
@JvmField
var rule = ActivityTestRule(EspressoTestActivity::class.java)
private lateinit var mapView: MapView
private lateinit var maplibreMap: MapLibreMap
private val latch = CountDownLatch(1)
@Before
fun setup() {
rule.runOnUiThread {
mapView = rule.activity.findViewById(R.id.mapView)
mapView.getMapAsync {
maplibreMap = it
maplibreMap.setStyle(Style.Builder().fromJson(styleJson))
}
}
}
@Test
fun testRemoveUnusedImagesUserProvidedListener() {
var callbackLatch = CountDownLatch(2)
rule.runOnUiThread {
mapView.addOnStyleImageMissingListener {
maplibreMap.style!!.addImage(it, Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888))
}
// Remove layer and source, so that rendered tiles are no longer used, therefore, map must
// notify client about unused images.
mapView.addOnDidBecomeIdleListener {
maplibreMap.style!!.removeLayer("icon")
maplibreMap.style!!.removeSource("geojson")
}
mapView.addOnCanRemoveUnusedStyleImageListener {
callbackLatch.countDown()
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 120.0), 8.0))
mapView.addOnDidFinishRenderingFrameListener{ _, _, _ ->
assertNotNull(maplibreMap.style!!.getImage("small"))
assertNotNull(maplibreMap.style!!.getImage("large"))
latch.countDown()
}
return@addOnCanRemoveUnusedStyleImageListener false
}
}
if (!latch.await(5, TimeUnit.SECONDS) && !callbackLatch.await(5, TimeUnit.SECONDS)) {
throw TimeoutException()
}
}
@Test
fun testRemoveUnusedImagesDefaultListener() {
rule.runOnUiThread {
mapView.addOnStyleImageMissingListener {
maplibreMap.style!!.addImage(it, Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888))
}
// Remove layer and source, so that rendered tiles are no longer used, thus
// map must request removal of unused images.
mapView.addOnDidBecomeIdleListener {
maplibreMap.style!!.removeLayer("icon")
maplibreMap.style!!.removeSource("geojson")
maplibreMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 120.0), 8.0))
// Wait for the next frame and check that images were removed from the style.
mapView.addOnDidFinishRenderingFrameListener { _, _, _ ->
if (maplibreMap.style!!.getImage("small") == null && maplibreMap.style!!.getImage("large") == null) {
latch.countDown()
}
}
}
}
if (!latch.await(5, TimeUnit.SECONDS)) {
throw TimeoutException()
}
}
companion object {
private const val styleJson =
"""
{
"version": 8,
"name": "MapLibre Streets",
"sources": {
"geojson": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"image": "small"
},
"geometry": {
"type": "Point",
"coordinates": [
0,
0
]
}
},
{
"type": "Feature",
"properties": {
"image": "large"
},
"geometry": {
"type": "Point",
"coordinates": [
1,
1
]
}
}
]
}
}
},
"layers": [{
"id": "bg",
"type": "background",
"paint": {
"background-color": "#f00"
}
},{
"id": "icon",
"type": "symbol",
"source": "geojson",
"layout": {
"icon-image": ["get", "image"]
}
}]
}
"""
}
}

View File

@@ -0,0 +1,52 @@
package org.maplibre.android.testapp.maps
import android.view.TextureView
import android.view.ViewGroup
import androidx.test.annotation.UiThreadTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.AppCenter
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapLibreMapOptions
import org.maplibre.android.maps.renderer.glsurfaceview.MapLibreGLSurfaceView
import org.maplibre.android.testapp.activity.FeatureOverviewActivity
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4ClassRunner::class)
class RenderViewGetterTest : AppCenter() {
@Rule
@JvmField
var rule = ActivityTestRule(FeatureOverviewActivity::class.java)
private lateinit var rootView: ViewGroup
private lateinit var mapView: MapView
private val latch: CountDownLatch = CountDownLatch(1)
@Test
@UiThreadTest
fun testGLSurfaceView() {
rootView = rule.activity.findViewById(android.R.id.content)
mapView = MapView(rule.activity)
assertNotNull(mapView.renderView)
assertTrue(mapView.renderView is MapLibreGLSurfaceView)
}
@Test
@UiThreadTest
fun testTextureView() {
rootView = rule.activity.findViewById(android.R.id.content)
mapView = MapView(
rule.activity,
MapLibreMapOptions.createFromAttributes(rule.activity, null)
.textureMode(true)
)
assertNotNull(mapView.renderView)
assertTrue(mapView.renderView is TextureView)
}
}

View File

@@ -0,0 +1,129 @@
package org.maplibre.android.testapp.maps
import android.graphics.PointF
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.junit.Assert.assertArrayEquals
import org.maplibre.android.AppCenter
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapView
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.activity.espresso.DeviceIndependentTestActivity
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@RunWith(AndroidJUnit4ClassRunner::class)
class ScreenCoordinateConversion : AppCenter() {
@Rule
@JvmField
var rule = ActivityTestRule(DeviceIndependentTestActivity::class.java)
private lateinit var mapView: MapView
private lateinit var latch: CountDownLatch
@Before
fun setup() {
latch = CountDownLatch(1)
}
@Test
fun testToScreenCoordinates() {
rule.runOnUiThread {
mapView = rule.activity.findViewById(R.id.mapView)
val expected = doubleArrayOf(261.6888889445351, 253.15541110551123, 267.37777788907033, 247.4627648056528)
val actual = doubleArrayOf(0.0, 0.0, 0.0, 0.0)
mapView.getMapAsync {
val projection = it.projection
projection.toScreenLocations(doubleArrayOf(1.0, 2.0, 3.0, 4.0), actual)
// check all values
assertTrue(expected.contentEquals(actual))
// check first coordinate value
val pointFirst = PointF(expected[0].toFloat(), expected[1].toFloat())
val convertedFirst = projection.toScreenLocation(LatLng(1.0, 2.0))
assertEquals(
pointFirst.x,
convertedFirst.x,
DELTA_FLOAT
)
assertEquals(
pointFirst.y,
convertedFirst.y,
DELTA_FLOAT
)
// check second latlng value
val pointSecond = PointF(expected[2].toFloat(), expected[3].toFloat())
val convertedSecond = projection.toScreenLocation(LatLng(3.0, 4.0))
assertEquals(
pointSecond.x,
convertedSecond.x,
DELTA_FLOAT
)
assertEquals(
pointSecond.y,
convertedSecond.y,
DELTA_FLOAT
)
latch.countDown()
}
}
latch.await(10, TimeUnit.SECONDS)
}
@Test
fun testFromScreenCoordinates() {
rule.runOnUiThread {
mapView = rule.activity.findViewById(R.id.mapView)
val expected = doubleArrayOf(66.2314571265913, -89.64843662311537, 65.94647142295494, -88.94531162999371)
val actual = doubleArrayOf(0.0, 0.0, 0.0, 0.0)
mapView.getMapAsync {
val projection = it.projection
projection.fromScreenLocations(doubleArrayOf(1.0, 2.0, 3.0, 4.0), actual)
// check all values at once
assertArrayEquals(expected, actual, DELTA_DOUBLE)
// check first latlng value
val latLngFirst = LatLng(expected[0], expected[1])
val convertedFirst = projection.fromScreenLocation(PointF(1.0f, 2.0f))
assertEquals(
latLngFirst.latitude,
convertedFirst.latitude,
DELTA_DOUBLE
)
assertEquals(
latLngFirst.longitude,
convertedFirst.longitude,
DELTA_DOUBLE
)
// check second latlng value
val latLngSecond = LatLng(expected[2], expected[3])
val convertedSecond = projection.fromScreenLocation(PointF(3.0f, 4.0f))
assertEquals(
latLngSecond.latitude,
convertedSecond.latitude,
DELTA_DOUBLE
)
assertEquals(
latLngSecond.longitude,
convertedSecond.longitude,
DELTA_DOUBLE
)
latch.countDown()
}
}
latch.await(10, TimeUnit.SECONDS)
}
companion object {
const val DELTA_DOUBLE = 0.000001
const val DELTA_FLOAT = 0.000001f
}
}

View File

@@ -0,0 +1,31 @@
package org.maplibre.android.testapp.maps
import androidx.test.espresso.UiController
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style
import org.maplibre.android.style.layers.SymbolLayer
import org.maplibre.android.style.sources.GeoJsonSource
import org.maplibre.android.testapp.action.MapLibreMapAction
import org.maplibre.android.testapp.activity.EspressoTest
import org.maplibre.android.testapp.utils.TestingAsyncUtils
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
class StyleLoadTest : EspressoTest() {
@Test
fun updateSourceAfterStyleLoad() {
validateTestSetup()
MapLibreMapAction.invoke(maplibreMap) { uiController: UiController, maplibreMap: MapLibreMap ->
val source = GeoJsonSource("id")
val layer = SymbolLayer("id", "id")
maplibreMap.setStyle(Style.Builder().withSource(source).withLayer(layer))
TestingAsyncUtils.waitForLayer(uiController, mapView)
maplibreMap.setStyle(Style.Builder().fromUrl(Style.getPredefinedStyle("Streets")))
TestingAsyncUtils.waitForLayer(uiController, mapView)
source.setGeoJson("{}")
}
}
}

View File

@@ -0,0 +1,199 @@
package org.maplibre.android.testapp.maps.widgets;
import android.app.Instrumentation;
import android.content.Intent;
import android.net.Uri;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.view.View;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.intent.Intents;
import org.maplibre.android.maps.MapLibreMap;
import org.maplibre.android.style.sources.Source;
import org.maplibre.android.testapp.R;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasData;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anything;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsNot.not;
public class AttributionTest extends EspressoTest {
private URLSpan[] urlSpans;
@Before
public void beforeTest() {
super.beforeTest();
Intents.init();
}
@Test
public void testDisabled() {
validateTestSetup();
// Default
onView(withTagValue(is("attrView"))).check(matches(isDisplayed()));
// Disabled
onView(withTagValue(is("attrView")))
.perform(new DisableAction(maplibreMap))
.check(matches(not(isDisplayed())));
}
@Test
@Ignore
public void testMapboxStreetsMapboxAttributionLink() {
validateTestSetup();
if (urlSpans == null) {
buildUrlSpans();
}
// click on View to open dialog
onView(withTagValue(is("attrView"))).perform(click());
onView(withText(R.string.maplibre_attributionsDialogTitle)).check(matches(isDisplayed()));
// test for trigger url intent
Matcher<Intent> expectedIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(Uri.parse(urlSpans[0].getURL())));
intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));
// click item and test for url
onData(anything()).inAdapterView(withId(R.id.select_dialog_listview)).atPosition(0).perform(click());
intended(expectedIntent);
}
@Test
@Ignore
public void testMapboxStreetsOpenStreetMapAttributionLink() {
validateTestSetup();
if (urlSpans == null) {
buildUrlSpans();
}
// click on View to open dialog
onView(withTagValue(is("attrView"))).perform(click());
onView(withText(R.string.maplibre_attributionsDialogTitle)).check(matches(isDisplayed()));
// test for trigger url intent
Matcher<Intent> expectedIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(Uri.parse(urlSpans[1].getURL())));
intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));
// click item and test for url
onData(anything()).inAdapterView(withId(R.id.select_dialog_listview)).atPosition(1).perform(click());
intended(expectedIntent);
}
@Test
@Ignore
public void testImproveMapLink() {
validateTestSetup();
if (urlSpans == null) {
buildUrlSpans();
}
// click on View to open dialog
onView(withTagValue(is("attrView"))).perform(click());
onView(withText(R.string.maplibre_attributionsDialogTitle)).check(matches(isDisplayed()));
// test for trigger url intent
Matcher<Intent> expectedIntent = hasAction(Intent.ACTION_VIEW);
intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));
// click item and test for url
onData(anything()).inAdapterView(withId(R.id.select_dialog_listview)).atPosition(2).perform(click());
intended(expectedIntent);
}
@After
public void afterTest() {
super.afterTest();
Intents.release();
}
private void buildUrlSpans() {
onView(withId(R.id.mapView)).perform(new MapLibreMapAction((uiController, view) -> {
for (Source source : maplibreMap.getStyle().getSources()) {
String attributionSource = source.getAttribution();
if (!TextUtils.isEmpty(attributionSource)) {
SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) Html.fromHtml(attributionSource);
urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class);
}
}
}));
}
private class DisableAction implements ViewAction {
private MapLibreMap maplibreMap;
DisableAction(MapLibreMap map) {
maplibreMap = map;
}
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
@Override
public void perform(UiController uiController, View view) {
maplibreMap.getUiSettings().setAttributionEnabled(false);
}
}
private class MapLibreMapAction implements ViewAction {
private InvokeViewAction invokeViewAction;
MapLibreMapAction(InvokeViewAction invokeViewAction) {
this.invokeViewAction = invokeViewAction;
}
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
@Override
public void perform(UiController uiController, View view) {
invokeViewAction.onViewAction(uiController, view);
}
}
interface InvokeViewAction {
void onViewAction(UiController uiController, View view);
}
}

View File

@@ -0,0 +1,68 @@
package org.maplibre.android.testapp.maps.widgets;
import org.maplibre.android.camera.CameraPosition;
import org.maplibre.android.camera.CameraUpdateFactory;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.testapp.action.WaitAction;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.maplibre.android.testapp.utils.TestConstants;
import org.junit.Ignore;
import org.junit.Test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
public class CompassViewTest extends EspressoTest {
@Test
public void testDefault() {
validateTestSetup();
onView(withTagValue(is("compassView"))).check(matches(not(isDisplayed())));
}
@Test
@Ignore("No explanation given")
public void testVisible() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
new CameraPosition.Builder()
.bearing(45)
.zoom(1)
.target(new LatLng())
.build()
));
uiController.loopMainThreadForAtLeast(500);
});
onView(withTagValue(is("compassView"))).check(matches(isDisplayed()));
}
@Test
@Ignore("No explanation given")
public void testClick() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
new CameraPosition.Builder()
.bearing(45)
.zoom(1)
.target(new LatLng())
.build()
)));
onView(withTagValue(is("compassView"))).perform(click());
WaitAction.invoke(500);
onView(withTagValue(is("compassView"))).check(matches(not(isDisplayed())));
invoke(maplibreMap, (uiController, mapboxMap) -> {
CameraPosition cameraPosition = mapboxMap.getCameraPosition();
assertEquals("Camera bearing should face north, ", 0, cameraPosition.bearing, TestConstants.BEARING_DELTA);
});
}
}

View File

@@ -0,0 +1,61 @@
package org.maplibre.android.testapp.maps.widgets;
import android.view.View;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import org.maplibre.android.maps.MapLibreMap;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.hamcrest.Matcher;
import org.junit.Test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
public class LogoTest extends EspressoTest {
@Test
public void testDefault() {
validateTestSetup();
onView(withTagValue(is("logoView"))).check(matches(isDisplayed()));
}
@Test
public void testDisabled() {
validateTestSetup();
onView(withTagValue(is("logoView")))
.perform(new DisableAction(maplibreMap))
.check(matches(not(isDisplayed())));
}
private class DisableAction implements ViewAction {
private MapLibreMap maplibreMap;
DisableAction(MapLibreMap map) {
maplibreMap = map;
}
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
@Override
public void perform(UiController uiController, View view) {
maplibreMap.getUiSettings().setLogoEnabled(false);
}
}
}

View File

@@ -0,0 +1,107 @@
package org.maplibre.android.testapp.offline
import android.content.Context
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.offline.OfflineManager
import org.maplibre.android.testapp.activity.FeatureOverviewActivity
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4ClassRunner::class)
class CacheTest {
@Rule
@JvmField
var rule = ActivityTestRule(FeatureOverviewActivity::class.java)
private val context: Context by lazy { rule.activity }
private val countDownLatch = CountDownLatch(1)
@Test
fun testSetMaximumAmbientCacheSize() {
rule.activity.runOnUiThread {
OfflineManager.getInstance(context).setMaximumAmbientCacheSize(
10000000,
object : OfflineManager.FileSourceCallback {
override fun onSuccess() {
countDownLatch.countDown()
}
override fun onError(message: String) {
Assert.assertNull("onError should not be called", message)
}
}
)
}
countDownLatch.await()
}
@Test
fun testSetClearAmbientCache() {
rule.activity.runOnUiThread {
OfflineManager.getInstance(context).clearAmbientCache(object : OfflineManager.FileSourceCallback {
override fun onSuccess() {
countDownLatch.countDown()
}
override fun onError(message: String) {
Assert.assertNull("onError should not be called", message)
}
})
}
countDownLatch.await()
}
@Test
fun testSetInvalidateAmbientCache() {
rule.activity.runOnUiThread {
OfflineManager.getInstance(context).invalidateAmbientCache(object : OfflineManager.FileSourceCallback {
override fun onSuccess() {
countDownLatch.countDown()
}
override fun onError(message: String) {
Assert.assertNull("onError should not be called", message)
}
})
}
countDownLatch.await()
}
@Test
fun testSetResetDatabase() {
rule.activity.runOnUiThread {
OfflineManager.getInstance(context).resetDatabase(object : OfflineManager.FileSourceCallback {
override fun onSuccess() {
countDownLatch.countDown()
}
override fun onError(message: String) {
Assert.assertNull("onError should not be called", message)
}
})
}
countDownLatch.await()
}
@Test
fun testSetPackDatabase() {
rule.activity.runOnUiThread {
OfflineManager.getInstance(context).packDatabase(object : OfflineManager.FileSourceCallback {
override fun onSuccess() {
countDownLatch.countDown()
}
override fun onError(message: String) {
Assert.assertNull("onError should not be called", message)
}
})
}
countDownLatch.await()
}
}

View File

@@ -0,0 +1,164 @@
package org.maplibre.android.testapp.offline
import android.content.Context
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import java.io.IOException
import java.util.concurrent.CountDownLatch
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.maplibre.android.AppCenter
import org.maplibre.android.offline.OfflineManager
import org.maplibre.android.offline.OfflineRegion
import org.maplibre.android.storage.FileSource
import org.maplibre.android.testapp.activity.FeatureOverviewActivity
import org.maplibre.android.testapp.utils.FileUtils
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(AndroidJUnit4ClassRunner::class)
class OfflineManagerTest : AppCenter() {
companion object {
private const val TEST_DB_FILE_NAME = "offline_test.db"
}
@Rule
@JvmField
var rule = ActivityTestRule(FeatureOverviewActivity::class.java)
private val context: Context by lazy { rule.activity }
@Before
@After
fun resetDatabase() {
val latch = CountDownLatch(1)
OfflineManager.getInstance(context).resetDatabase(object : OfflineManager.FileSourceCallback {
override fun onSuccess() {
latch.countDown()
}
override fun onError(message: String) {
throw IOException("Unable to reset database before / after tests.")
}
})
latch.await()
}
@Test(timeout = 30_000)
fun combinedTest() {
lateinit var mergedRegion: OfflineRegion
// Copy file from assets
val latch1 = CountDownLatch(1)
rule.activity.runOnUiThread {
FileUtils.CopyFileFromAssetsTask(
rule.activity,
object : FileUtils.OnFileCopiedFromAssetsListener {
override fun onFileCopiedFromAssets() {
latch1.countDown()
}
override fun onError() {
throw IOException("Unable to copy DB file.")
}
}
).execute(TEST_DB_FILE_NAME, FileSource.getResourcesCachePath(rule.activity))
}
latch1.await()
// Merge a second region
val latch2 = CountDownLatch(1)
rule.activity.runOnUiThread {
OfflineManager.getInstance(context).mergeOfflineRegions(
FileSource.getResourcesCachePath(rule.activity) + "/" + TEST_DB_FILE_NAME,
object : OfflineManager.MergeOfflineRegionsCallback {
override fun onMerge(offlineRegions: Array<OfflineRegion>?) {
Assert.assertEquals(1, offlineRegions?.size)
latch2.countDown()
}
override fun onError(error: String) {
throw RuntimeException("Unable to merge external offline database. $error")
}
}
)
}
latch2.await()
// List regions (ensure merge yielded one region only)
val latch3 = CountDownLatch(1)
rule.activity.runOnUiThread {
OfflineManager.getInstance(context).listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
override fun onList(offlineRegions: Array<OfflineRegion>?) {
Assert.assertEquals(1, offlineRegions?.size)
mergedRegion = offlineRegions!![0]
latch3.countDown()
}
override fun onError(error: String) {
throw RuntimeException("Unable to list regions in offline database. $error")
}
})
}
latch3.await()
// Invalidate region
val latch4 = CountDownLatch(1)
rule.activity.runOnUiThread {
mergedRegion.invalidate(object : OfflineRegion.OfflineRegionInvalidateCallback {
override fun onInvalidate() {
latch4.countDown()
}
override fun onError(error: String) {
throw RuntimeException("Unable to invalidate region")
}
})
}
latch4.await()
// Delete region
val latch5 = CountDownLatch(1)
rule.activity.runOnUiThread {
mergedRegion.delete(object : OfflineRegion.OfflineRegionDeleteCallback {
override fun onDelete() {
latch5.countDown()
}
override fun onError(error: String) {
throw RuntimeException("Unable to delete region")
}
})
}
latch5.await()
// List regions (ensure deletion was successful)
val latch6 = CountDownLatch(1)
rule.activity.runOnUiThread {
OfflineManager.getInstance(context).listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
override fun onList(offlineRegions: Array<OfflineRegion>?) {
Assert.assertEquals(0, offlineRegions?.size)
latch6.countDown()
}
override fun onError(error: String) {
throw RuntimeException("Unable to list regions in offline database. $error")
}
})
}
latch6.await()
}
}

View File

@@ -0,0 +1,41 @@
package org.maplibre.android.testapp.offline
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.maplibre.android.AppCenter
import org.maplibre.android.testapp.activity.offline.OfflineActivity
import org.maplibre.android.testapp.utils.OfflineUtils.convertRegionName
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import java.io.UnsupportedEncodingException
import java.util.*
@RunWith(AndroidJUnit4ClassRunner::class)
class OfflineUtilsTest : AppCenter() {
@Test
@Throws(UnsupportedEncodingException::class)
fun testOfflineUtilsConvertToBytes() {
val expected = CONVERTED_REGION_NAME.toByteArray(OfflineActivity.JSON_CHARSET)
val actual = convertRegionName(REGION_NAME)
Assert.assertTrue("Bytes arrays should match", Arrays.equals(expected, actual))
}
@Test
@Throws(UnsupportedEncodingException::class)
fun testOfflineUtilsConvertToString() {
val actual =
convertRegionName(CONVERTED_REGION_NAME.toByteArray(OfflineActivity.JSON_CHARSET))
Assert.assertEquals("Strings should match", REGION_NAME, actual)
}
@Test
fun testOfflineUtilsConvertNoOp() {
val convertNoOp = convertRegionName(convertRegionName(REGION_NAME)!!)
Assert.assertEquals("Strings should match", REGION_NAME, convertNoOp)
}
companion object {
private const val REGION_NAME = "hello world"
private const val CONVERTED_REGION_NAME = "{\"FIELD_REGION_NAME\":\"hello world\"}"
}
}

View File

@@ -0,0 +1,77 @@
package org.maplibre.android.testapp.render;
import android.Manifest;
import androidx.test.espresso.IdlingPolicies;
import androidx.test.espresso.IdlingRegistry;
import androidx.test.espresso.IdlingResourceTimeoutException;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import androidx.test.rule.ActivityTestRule;
import androidx.test.rule.GrantPermissionRule;
import org.maplibre.android.AppCenter;
import org.maplibre.android.testapp.activity.render.RenderTestActivity;
import org.maplibre.android.testapp.utils.SnapshotterIdlingResource;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.TimeUnit;
import timber.log.Timber;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
/**
* Instrumentation render tests
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class RenderTest extends AppCenter {
private static final int RENDER_TEST_TIMEOUT = 30;
private SnapshotterIdlingResource idlingResource;
@Rule
public ActivityTestRule<RenderTestActivity> activityRule = new ActivityTestRule<>(RenderTestActivity.class);
@Rule
public GrantPermissionRule writeRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
@Rule
public GrantPermissionRule readRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);
@Before
public void beforeTest() {
IdlingPolicies.setMasterPolicyTimeout(RENDER_TEST_TIMEOUT, TimeUnit.MINUTES);
setupIdlingResource();
}
private void setupIdlingResource() {
try {
Timber.e("@Before test: register idle resource");
IdlingPolicies.setIdlingResourceTimeout(RENDER_TEST_TIMEOUT, TimeUnit.MINUTES);
IdlingRegistry.getInstance().register(idlingResource = new SnapshotterIdlingResource(activityRule.getActivity()));
} catch (IdlingResourceTimeoutException idlingResourceTimeoutException) {
throw new RuntimeException("Idling out!");
}
}
@Test
@Ignore
public void testRender() {
onView(withId(android.R.id.content)).check(matches(isDisplayed()));
}
@After
public void afterTest() {
Timber.e("@After test: unregister idle resource");
IdlingRegistry.getInstance().unregister(idlingResource);
}
}

View File

@@ -0,0 +1,61 @@
package org.maplibre.android.testapp.storage
import androidx.test.annotation.UiThreadTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.AppCenter
import org.maplibre.android.storage.FileSource
import org.maplibre.android.testapp.activity.espresso.EspressoTestActivity
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestName
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4ClassRunner::class)
open class FileSourceMapTest : AppCenter() {
private lateinit var fileSourceTestUtils: FileSourceTestUtils
@get:Rule
val rule = ActivityTestRule(EspressoTestActivity::class.java)
@get:Rule
val testName = TestName()
@Before
@UiThreadTest
fun setup() {
fileSourceTestUtils = FileSourceTestUtils(rule.activity)
fileSourceTestUtils.setup()
}
@Test
fun changeResourcesPathWhileMapVisible() {
val latch = CountDownLatch(1)
rule.activity.runOnUiThread {
FileSource.setResourcesCachePath(
fileSourceTestUtils.testPath,
object : FileSource.ResourcesCachePathChangeCallback {
override fun onSuccess(path: String) {
latch.countDown()
Assert.assertEquals(fileSourceTestUtils.testPath, path)
}
override fun onError(message: String) {
Assert.fail("Resources path can be changed while the map is running")
}
}
)
}
latch.await()
}
@After
fun cleanup() {
fileSourceTestUtils.cleanup()
}
}

View File

@@ -0,0 +1,112 @@
package org.maplibre.android.testapp.storage
import androidx.test.annotation.UiThreadTest
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import androidx.test.rule.ActivityTestRule
import org.maplibre.android.AppCenter
import org.maplibre.android.storage.FileSource
import org.maplibre.android.testapp.activity.FeatureOverviewActivity
import org.junit.*
import org.junit.rules.TestName
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@RunWith(AndroidJUnit4ClassRunner::class)
class FileSourceStandaloneTest : AppCenter() {
private lateinit var fileSourceTestUtils: FileSourceTestUtils
private lateinit var fileSource: FileSource
@get:Rule
val rule = ActivityTestRule(FeatureOverviewActivity::class.java)
@get:Rule
val testName = TestName()
@Before
@UiThreadTest
fun setup() {
fileSource = FileSource.getInstance(rule.activity)
fileSourceTestUtils = FileSourceTestUtils(rule.activity)
fileSourceTestUtils.setup()
}
@Test
@UiThreadTest
fun testDefault() {
Assert.assertTrue("FileSource should be active", fileSource.isActivated)
}
@Test
@UiThreadTest
fun testActivateDeactivate() {
Assert.assertTrue("1) FileSource should be active", fileSource.isActivated)
fileSource.deactivate()
Assert.assertFalse("2) FileSource should not be active", fileSource.isActivated)
fileSource.activate()
Assert.assertTrue("3) FileSource should be active", fileSource.isActivated)
}
@Test
fun pathChangeTest() {
Assert.assertTrue("FileSource should be active", fileSource.isActivated)
Assert.assertEquals(fileSourceTestUtils.originalPath, FileSource.getResourcesCachePath(rule.activity))
fileSourceTestUtils.changePath(fileSourceTestUtils.testPath)
Assert.assertEquals(fileSourceTestUtils.testPath, FileSource.getResourcesCachePath(rule.activity))
Assert.assertTrue("FileSource should be active", fileSource.isActivated)
fileSourceTestUtils.changePath(fileSourceTestUtils.originalPath)
Assert.assertEquals(fileSourceTestUtils.originalPath, FileSource.getResourcesCachePath(rule.activity))
Assert.assertTrue("FileSource should be active", fileSource.isActivated)
}
@Test
fun overridePathChangeCallTest() {
val firstLatch = CountDownLatch(1)
val secondLatch = CountDownLatch(1)
rule.activity.runOnUiThread {
FileSource.setResourcesCachePath(
fileSourceTestUtils.testPath,
object : FileSource.ResourcesCachePathChangeCallback {
override fun onSuccess(path: String) {
Assert.assertEquals(fileSourceTestUtils.testPath, path)
firstLatch.countDown()
}
override fun onError(message: String) {
Assert.fail("First attempt should succeed.")
}
}
)
FileSource.setResourcesCachePath(
fileSourceTestUtils.testPath2,
object : FileSource.ResourcesCachePathChangeCallback {
override fun onSuccess(path: String) {
Assert.fail("Second attempt should fail because first one is in progress.")
}
override fun onError(message: String) {
Assert.assertEquals("Another resources cache path change is in progress", message)
secondLatch.countDown()
}
}
)
}
if (!secondLatch.await(5, TimeUnit.SECONDS)) {
Assert.fail("Second attempt should fail.")
}
if (!firstLatch.await(5, TimeUnit.SECONDS)) {
Assert.fail("First attempt should succeed.")
}
}
@After
fun cleanup() {
fileSourceTestUtils.cleanup()
}
}

View File

@@ -0,0 +1,60 @@
package org.maplibre.android.testapp.storage
import android.app.Activity
import androidx.annotation.WorkerThread
import org.maplibre.android.AppCenter
import org.maplibre.android.storage.FileSource
import org.junit.Assert
import java.io.File
import java.util.concurrent.CountDownLatch
class FileSourceTestUtils(private val activity: Activity) : AppCenter() {
val originalPath = FileSource.getResourcesCachePath(activity)
val testPath = "$originalPath/test"
val testPath2 = "$originalPath/test2"
private val paths = listOf(testPath, testPath2)
fun setup() {
for (path in paths) {
val testFile = File(path)
testFile.mkdirs()
}
}
@WorkerThread
fun cleanup() {
val currentPath = FileSource.getResourcesCachePath(activity)
if (currentPath != originalPath) {
changePath(originalPath)
}
for (path in paths) {
val testFile = File(path)
if (testFile.exists()) {
testFile.deleteRecursively()
}
}
}
@WorkerThread
fun changePath(requestedPath: String) {
val latch = CountDownLatch(1)
activity.runOnUiThread {
FileSource.setResourcesCachePath(
requestedPath,
object : FileSource.ResourcesCachePathChangeCallback {
override fun onSuccess(path: String) {
Assert.assertEquals(requestedPath, path)
latch.countDown()
}
override fun onError(message: String) {
Assert.fail("Resource path change failed - path: $requestedPath, message: $message")
}
}
)
}
latch.await()
}
}

View File

@@ -0,0 +1,49 @@
package org.maplibre.android.testapp.string;
import static org.junit.Assert.assertEquals;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Test verifying if String#toUpperCase and String#toLowerCase produces desired results
* <p>
* See core test in https://github.com/mapbox/mapbox-gl-native/blob/master/test/util/text_conversions.test.cpp
* </p>
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class UppperLowerCaseTest extends EspressoTest {
@Test
public void testToUpperCase() {
assertEquals("STREET", "strEEt".toUpperCase()); // EN
assertEquals("ROAD", "rOAd".toUpperCase()); // EN
assertEquals("STRASSE", "straße".toUpperCase()); // DE
assertEquals("MASSE", "maße".toUpperCase()); // DE
assertEquals("WEISSKOPFSEEADLER", "weißkopfseeadler".toUpperCase()); // DE
assertEquals("BÊNÇÃO", "bênção".toUpperCase()); // PT
assertEquals("AZƏRBAYCAN", "Azərbaycan".toUpperCase()); // AZ
assertEquals("ὈΔΥΣΣΕΎΣ", "Ὀδυσσεύς".toUpperCase()); // GR
}
@Test
public void testToLowerCase() {
assertEquals("street", "strEEt".toLowerCase()); // EN
assertEquals("road", "rOAd".toLowerCase()); // EN
assertEquals("straße", "Straße".toLowerCase()); // DE
assertEquals("strasse", "STRASSE".toLowerCase()); // DE
assertEquals("masse", "MASSE".toLowerCase()); // DE
assertEquals("weisskopfseeadler", "weiSSkopfseeadler".toLowerCase()); // DE
assertEquals("bênção", "BÊNÇÃO".toLowerCase()); // PT
assertEquals("azərbaycan", "AZƏRBAYCAN".toLowerCase()); //
}
}

View File

@@ -0,0 +1,163 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.BackgroundLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for BackgroundLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class BackgroundLayerTest extends BaseLayerTest {
private BackgroundLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new BackgroundLayer("my-layer");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testBackgroundColorTransition() {
Timber.i("background-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setBackgroundColorTransition(options);
assertEquals(layer.getBackgroundColorTransition(), options);
}
@Test
@UiThreadTest
public void testBackgroundColorAsConstant() {
Timber.i("background-color");
assertNotNull(layer);
assertNull(layer.getBackgroundColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(backgroundColor(propertyValue));
assertEquals(layer.getBackgroundColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testBackgroundColorAsIntConstant() {
Timber.i("background-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(backgroundColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getBackgroundColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testBackgroundPatternTransition() {
Timber.i("background-patternTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setBackgroundPatternTransition(options);
assertEquals(layer.getBackgroundPatternTransition(), options);
}
@Test
@UiThreadTest
public void testBackgroundPatternAsConstant() {
Timber.i("background-pattern");
assertNotNull(layer);
assertNull(layer.getBackgroundPattern().getValue());
// Set and Get
String propertyValue = "pedestrian-polygon";
layer.setProperties(backgroundPattern(propertyValue));
assertEquals(layer.getBackgroundPattern().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testBackgroundOpacityTransition() {
Timber.i("background-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setBackgroundOpacityTransition(options);
assertEquals(layer.getBackgroundOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testBackgroundOpacityAsConstant() {
Timber.i("background-opacity");
assertNotNull(layer);
assertNull(layer.getBackgroundOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(backgroundOpacity(propertyValue));
assertEquals(layer.getBackgroundOpacity().getValue(), propertyValue);
}
}

View File

@@ -0,0 +1,555 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.CircleLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for CircleLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class CircleLayerTest extends BaseLayerTest {
private CircleLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new CircleLayer("my-layer", "composite");
layer.setSourceLayer("composite");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testSourceLayer() {
Timber.i("SourceLayer");
assertNotNull(layer);
// Get initial
assertEquals(layer.getSourceLayer(), "composite");
// Set
final String sourceLayer = "test";
layer.setSourceLayer(sourceLayer);
assertEquals(layer.getSourceLayer(), sourceLayer);
}
@Test
@UiThreadTest
public void testFilter() {
Timber.i("Filter");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// Set
Expression filter = eq(get("undefined"), literal(1.0));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// Set constant
filter = literal(true);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterDistance() {
Timber.i("FilterDistance");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// distance with Point
Expression filter = lt(distance(Point.fromLngLat(1.0, 1.0)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with LineString
filter = lt(distance(LineString.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiPoint.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiLineString.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with Polygon
filter = lt(distance(Polygon.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPolygon
filter = lt(distance(MultiPolygon.fromLngLats(Collections
.singletonList(Collections.singletonList(pointsList)))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterWithin() {
Timber.i("FilterWithin");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
Expression filter = within(Polygon.fromLngLats(Collections.singletonList(pointsList)));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testCircleSortKeyAsConstant() {
Timber.i("circle-sort-key");
assertNotNull(layer);
assertNull(layer.getCircleSortKey().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(circleSortKey(propertyValue));
assertEquals(layer.getCircleSortKey().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleSortKeyAsExpression() {
Timber.i("circle-sort-key-expression");
assertNotNull(layer);
assertNull(layer.getCircleSortKey().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(circleSortKey(expression));
assertEquals(layer.getCircleSortKey().getExpression(), expression);
}
@Test
@UiThreadTest
public void testCircleRadiusTransition() {
Timber.i("circle-radiusTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setCircleRadiusTransition(options);
assertEquals(layer.getCircleRadiusTransition(), options);
}
@Test
@UiThreadTest
public void testCircleRadiusAsConstant() {
Timber.i("circle-radius");
assertNotNull(layer);
assertNull(layer.getCircleRadius().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(circleRadius(propertyValue));
assertEquals(layer.getCircleRadius().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleRadiusAsExpression() {
Timber.i("circle-radius-expression");
assertNotNull(layer);
assertNull(layer.getCircleRadius().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(circleRadius(expression));
assertEquals(layer.getCircleRadius().getExpression(), expression);
}
@Test
@UiThreadTest
public void testCircleColorTransition() {
Timber.i("circle-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setCircleColorTransition(options);
assertEquals(layer.getCircleColorTransition(), options);
}
@Test
@UiThreadTest
public void testCircleColorAsConstant() {
Timber.i("circle-color");
assertNotNull(layer);
assertNull(layer.getCircleColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(circleColor(propertyValue));
assertEquals(layer.getCircleColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleColorAsExpression() {
Timber.i("circle-color-expression");
assertNotNull(layer);
assertNull(layer.getCircleColor().getExpression());
// Set and Get
Expression expression = toColor(Expression.get("undefined"));
layer.setProperties(circleColor(expression));
assertEquals(layer.getCircleColor().getExpression(), expression);
}
@Test
@UiThreadTest
public void testCircleColorAsIntConstant() {
Timber.i("circle-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(circleColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getCircleColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testCircleBlurTransition() {
Timber.i("circle-blurTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setCircleBlurTransition(options);
assertEquals(layer.getCircleBlurTransition(), options);
}
@Test
@UiThreadTest
public void testCircleBlurAsConstant() {
Timber.i("circle-blur");
assertNotNull(layer);
assertNull(layer.getCircleBlur().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(circleBlur(propertyValue));
assertEquals(layer.getCircleBlur().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleBlurAsExpression() {
Timber.i("circle-blur-expression");
assertNotNull(layer);
assertNull(layer.getCircleBlur().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(circleBlur(expression));
assertEquals(layer.getCircleBlur().getExpression(), expression);
}
@Test
@UiThreadTest
public void testCircleOpacityTransition() {
Timber.i("circle-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setCircleOpacityTransition(options);
assertEquals(layer.getCircleOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testCircleOpacityAsConstant() {
Timber.i("circle-opacity");
assertNotNull(layer);
assertNull(layer.getCircleOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(circleOpacity(propertyValue));
assertEquals(layer.getCircleOpacity().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleOpacityAsExpression() {
Timber.i("circle-opacity-expression");
assertNotNull(layer);
assertNull(layer.getCircleOpacity().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(circleOpacity(expression));
assertEquals(layer.getCircleOpacity().getExpression(), expression);
}
@Test
@UiThreadTest
public void testCircleTranslateTransition() {
Timber.i("circle-translateTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setCircleTranslateTransition(options);
assertEquals(layer.getCircleTranslateTransition(), options);
}
@Test
@UiThreadTest
public void testCircleTranslateAsConstant() {
Timber.i("circle-translate");
assertNotNull(layer);
assertNull(layer.getCircleTranslate().getValue());
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(circleTranslate(propertyValue));
assertEquals(layer.getCircleTranslate().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleTranslateAnchorAsConstant() {
Timber.i("circle-translate-anchor");
assertNotNull(layer);
assertNull(layer.getCircleTranslateAnchor().getValue());
// Set and Get
String propertyValue = CIRCLE_TRANSLATE_ANCHOR_MAP;
layer.setProperties(circleTranslateAnchor(propertyValue));
assertEquals(layer.getCircleTranslateAnchor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCirclePitchScaleAsConstant() {
Timber.i("circle-pitch-scale");
assertNotNull(layer);
assertNull(layer.getCirclePitchScale().getValue());
// Set and Get
String propertyValue = CIRCLE_PITCH_SCALE_MAP;
layer.setProperties(circlePitchScale(propertyValue));
assertEquals(layer.getCirclePitchScale().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCirclePitchAlignmentAsConstant() {
Timber.i("circle-pitch-alignment");
assertNotNull(layer);
assertNull(layer.getCirclePitchAlignment().getValue());
// Set and Get
String propertyValue = CIRCLE_PITCH_ALIGNMENT_MAP;
layer.setProperties(circlePitchAlignment(propertyValue));
assertEquals(layer.getCirclePitchAlignment().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleStrokeWidthTransition() {
Timber.i("circle-stroke-widthTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setCircleStrokeWidthTransition(options);
assertEquals(layer.getCircleStrokeWidthTransition(), options);
}
@Test
@UiThreadTest
public void testCircleStrokeWidthAsConstant() {
Timber.i("circle-stroke-width");
assertNotNull(layer);
assertNull(layer.getCircleStrokeWidth().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(circleStrokeWidth(propertyValue));
assertEquals(layer.getCircleStrokeWidth().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleStrokeWidthAsExpression() {
Timber.i("circle-stroke-width-expression");
assertNotNull(layer);
assertNull(layer.getCircleStrokeWidth().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(circleStrokeWidth(expression));
assertEquals(layer.getCircleStrokeWidth().getExpression(), expression);
}
@Test
@UiThreadTest
public void testCircleStrokeColorTransition() {
Timber.i("circle-stroke-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setCircleStrokeColorTransition(options);
assertEquals(layer.getCircleStrokeColorTransition(), options);
}
@Test
@UiThreadTest
public void testCircleStrokeColorAsConstant() {
Timber.i("circle-stroke-color");
assertNotNull(layer);
assertNull(layer.getCircleStrokeColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(circleStrokeColor(propertyValue));
assertEquals(layer.getCircleStrokeColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleStrokeColorAsExpression() {
Timber.i("circle-stroke-color-expression");
assertNotNull(layer);
assertNull(layer.getCircleStrokeColor().getExpression());
// Set and Get
Expression expression = toColor(Expression.get("undefined"));
layer.setProperties(circleStrokeColor(expression));
assertEquals(layer.getCircleStrokeColor().getExpression(), expression);
}
@Test
@UiThreadTest
public void testCircleStrokeColorAsIntConstant() {
Timber.i("circle-stroke-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(circleStrokeColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getCircleStrokeColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testCircleStrokeOpacityTransition() {
Timber.i("circle-stroke-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setCircleStrokeOpacityTransition(options);
assertEquals(layer.getCircleStrokeOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testCircleStrokeOpacityAsConstant() {
Timber.i("circle-stroke-opacity");
assertNotNull(layer);
assertNull(layer.getCircleStrokeOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(circleStrokeOpacity(propertyValue));
assertEquals(layer.getCircleStrokeOpacity().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testCircleStrokeOpacityAsExpression() {
Timber.i("circle-stroke-opacity-expression");
assertNotNull(layer);
assertNull(layer.getCircleStrokeOpacity().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(circleStrokeOpacity(expression));
assertEquals(layer.getCircleStrokeOpacity().getExpression(), expression);
}
}

View File

@@ -0,0 +1,116 @@
package org.maplibre.android.testapp.style
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import org.maplibre.android.style.sources.CustomGeometrySource.Companion.THREAD_POOL_LIMIT
import org.maplibre.android.style.sources.CustomGeometrySource.Companion.THREAD_PREFIX
import org.maplibre.android.testapp.action.MapLibreMapAction.invoke
import org.maplibre.android.testapp.action.OrientationAction.orientationLandscape
import org.maplibre.android.testapp.action.OrientationAction.orientationPortrait
import org.maplibre.android.testapp.action.WaitAction
import org.maplibre.android.testapp.activity.BaseTest
import org.maplibre.android.testapp.activity.style.GridSourceActivity
import org.maplibre.android.testapp.activity.style.GridSourceActivity.Companion.ID_GRID_LAYER
import org.maplibre.android.testapp.activity.style.GridSourceActivity.Companion.ID_GRID_SOURCE
import org.maplibre.android.testapp.utils.TestingAsyncUtils
import org.junit.Assert
import org.junit.Assert.*
import org.junit.Test
class CustomGeometrySourceTest : BaseTest() {
override fun getActivityClass(): Class<*> = GridSourceActivity::class.java
@Test
fun sourceNotLeakingThreadsTest() {
validateTestSetup()
WaitAction.invoke(4000)
onView(isRoot()).perform(orientationLandscape())
WaitAction.invoke(2000)
onView(isRoot()).perform(orientationPortrait())
WaitAction.invoke(2000)
Assert.assertFalse(
"Threads should be shutdown when the source is destroyed.",
Thread.getAllStackTraces().keys.filter {
it.name.startsWith(THREAD_PREFIX)
}.count() > THREAD_POOL_LIMIT
)
}
@Test
fun threadsShutdownWhenSourceRemovedTest() {
validateTestSetup()
invoke(maplibreMap) { uiController, mapboxMap ->
mapboxMap.style!!.removeLayer(ID_GRID_LAYER)
TestingAsyncUtils.waitForLayer(uiController, mapView)
mapboxMap.style!!.removeSource(ID_GRID_SOURCE)
TestingAsyncUtils.waitForLayer(uiController, mapView)
Assert.assertTrue(
"There should be no threads running when the source is removed.",
Thread.getAllStackTraces().keys.filter {
it.name.startsWith(THREAD_PREFIX)
}.count() == 0
)
}
}
@Test
fun threadsRestartedWhenSourceReAddedTest() {
validateTestSetup()
invoke(maplibreMap) { uiController, mapboxMap ->
mapboxMap.style!!.removeLayer((rule.activity as GridSourceActivity).layer!!)
TestingAsyncUtils.waitForLayer(uiController, mapView)
mapboxMap.style!!.removeSource(ID_GRID_SOURCE)
TestingAsyncUtils.waitForLayer(uiController, mapView)
mapboxMap.style!!.addSource((rule.activity as GridSourceActivity).source!!)
mapboxMap.style!!.addLayer((rule.activity as GridSourceActivity).layer!!)
TestingAsyncUtils.waitForLayer(uiController, mapView)
Assert.assertTrue(
"Threads should be restarted when the source is re-added to the map.",
Thread.getAllStackTraces().keys.filter {
it.name.startsWith(THREAD_PREFIX)
}.count() == THREAD_POOL_LIMIT
)
}
}
@Test
fun sourceZoomDeltaTest() {
validateTestSetup()
invoke(maplibreMap) { uiController, mapboxMap ->
mapboxMap.prefetchZoomDelta = 3
mapboxMap.style!!.getSource(ID_GRID_SOURCE)!!.let {
assertNull(it.prefetchZoomDelta)
it.prefetchZoomDelta = 5
assertNotNull(it.prefetchZoomDelta)
assertEquals(5, it.prefetchZoomDelta!!)
it.prefetchZoomDelta = null
assertNull(it.prefetchZoomDelta)
}
}
}
@Test
fun isVolatileTest() {
validateTestSetup()
invoke(maplibreMap) { uiController, mapboxMap ->
mapboxMap.style!!.getSource(ID_GRID_SOURCE)!!.let {
assertFalse(it.isVolatile)
it.isVolatile = true
assertTrue(it.isVolatile)
}
}
}
@Test
fun minimumTileUpdateIntervalTest() {
validateTestSetup()
invoke(maplibreMap) { uiController, mapboxMap ->
mapboxMap.style!!.getSource(ID_GRID_SOURCE)!!.let {
assertEquals(0, it.minimumTileUpdateInterval)
it.minimumTileUpdateInterval = 1000
assertEquals(1000, it.minimumTileUpdateInterval)
}
}
}
}

View File

@@ -0,0 +1,784 @@
package org.maplibre.android.testapp.style;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.Point;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.CircleLayer;
import org.maplibre.android.style.layers.FillLayer;
import org.maplibre.android.style.layers.Layer;
import org.maplibre.android.style.layers.SymbolLayer;
import org.maplibre.android.style.sources.GeoJsonSource;
import org.maplibre.android.style.sources.Source;
import org.maplibre.android.style.types.Formatted;
import org.maplibre.android.style.types.FormattedSection;
import org.maplibre.android.testapp.R;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.maplibre.android.testapp.utils.ResourceUtils;
import org.maplibre.android.testapp.utils.TestingAsyncUtils;
import org.maplibre.android.utils.ColorUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import android.graphics.Color;
import java.util.HashMap;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import timber.log.Timber;
import static org.maplibre.android.style.expressions.Expression.FormatOption.formatFontScale;
import static org.maplibre.android.style.expressions.Expression.FormatOption.formatTextColor;
import static org.maplibre.android.style.expressions.Expression.FormatOption.formatTextFont;
import static org.maplibre.android.style.expressions.Expression.NumberFormatOption.currency;
import static org.maplibre.android.style.expressions.Expression.NumberFormatOption.locale;
import static org.maplibre.android.style.expressions.Expression.NumberFormatOption.maxFractionDigits;
import static org.maplibre.android.style.expressions.Expression.NumberFormatOption.minFractionDigits;
import static org.maplibre.android.style.expressions.Expression.collator;
import static org.maplibre.android.style.expressions.Expression.color;
import static org.maplibre.android.style.expressions.Expression.eq;
import static org.maplibre.android.style.expressions.Expression.exponential;
import static org.maplibre.android.style.expressions.Expression.format;
import static org.maplibre.android.style.expressions.Expression.formatEntry;
import static org.maplibre.android.style.expressions.Expression.get;
import static org.maplibre.android.style.expressions.Expression.interpolate;
import static org.maplibre.android.style.expressions.Expression.literal;
import static org.maplibre.android.style.expressions.Expression.match;
import static org.maplibre.android.style.expressions.Expression.number;
import static org.maplibre.android.style.expressions.Expression.numberFormat;
import static org.maplibre.android.style.expressions.Expression.rgb;
import static org.maplibre.android.style.expressions.Expression.rgba;
import static org.maplibre.android.style.expressions.Expression.step;
import static org.maplibre.android.style.expressions.Expression.stop;
import static org.maplibre.android.style.expressions.Expression.string;
import static org.maplibre.android.style.expressions.Expression.switchCase;
import static org.maplibre.android.style.expressions.Expression.toColor;
import static org.maplibre.android.style.expressions.Expression.zoom;
import static org.maplibre.android.style.layers.PropertyFactory.circleColor;
import static org.maplibre.android.style.layers.PropertyFactory.fillAntialias;
import static org.maplibre.android.style.layers.PropertyFactory.fillColor;
import static org.maplibre.android.style.layers.PropertyFactory.fillOutlineColor;
import static org.maplibre.android.style.layers.PropertyFactory.textColor;
import static org.maplibre.android.style.layers.PropertyFactory.textField;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@RunWith(AndroidJUnit4ClassRunner.class)
public class ExpressionTest extends EspressoTest {
private FillLayer layer;
@Test
public void testConstantExpressionConversion() {
validateTestSetup();
setupStyle();
Timber.i("camera function");
invoke(maplibreMap, (uiController, mapboxMap) -> {
// create color expression
Expression inputExpression = rgba(255.0f, 0.0f, 0.0f, 1.0f);
// set color expression
layer.setProperties(
fillColor(inputExpression)
);
// get color value
int color = layer.getFillColor().getColorInt();
// compare
assertEquals("input expression should match", Color.RED, color);
});
}
@Test
public void testGetExpressionWrapping() {
validateTestSetup();
setupStyle();
Timber.i("camera function");
invoke(maplibreMap, (uiController, mapboxMap) -> {
// create get expression
Expression inputExpression = get("fill");
// set get expression
layer.setProperties(
fillColor(inputExpression)
);
// get actual expression
Expression actualExpression = layer.getFillColor().getExpression();
// create wrapped expected expression
Expression expectedExpression = toColor(get("fill"));
// compare
assertEquals("input expression should match", expectedExpression, actualExpression);
});
}
@Test
public void testCameraFunction() {
validateTestSetup();
setupStyle();
Timber.i("camera function");
invoke(maplibreMap, (uiController, mapboxMap) -> {
// create camera function expression
Expression inputExpression = interpolate(
exponential(0.5f), zoom(),
stop(1.0f, rgba(255.0f, 0.0f, 0.0f, 1.0f)),
stop(5.0f, rgba(0.0f, 0.0f, 255.0f, 1.0f)),
stop(10.0f, rgba(0.0f, 255.0f, 0.0f, 1.0f))
);
// set camera function expression
layer.setProperties(
fillColor(inputExpression)
);
// get camera function expression
Expression outputExpression = layer.getFillColor().getExpression();
// compare
assertEquals("input expression should match", inputExpression, outputExpression);
});
}
@Test
public void testSourceFunction() {
validateTestSetup();
setupStyle();
Timber.i("camera function");
invoke(maplibreMap, (uiController, mapboxMap) -> {
// create camera function expression
Expression inputExpression = toColor(get("fill"));
// set camera function expression
layer.setProperties(
fillColor(inputExpression)
);
// get camera function expression
Expression outputExpression = layer.getFillColor().getExpression();
// compare
assertEquals("input expression should match", inputExpression, outputExpression);
});
}
@Test
public void testCompositeFunction() {
validateTestSetup();
setupStyle();
Timber.i("camera function");
invoke(maplibreMap, (uiController, mapboxMap) -> {
// create camera function expression
Expression inputExpression = step(zoom(),
rgba(255.0f, 255.0f, 255.0f, 1.0f),
stop(7.0f, match(
string(get("name")),
literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
rgba(255.0f, 255.0f, 255.0f, 1.0f)
)),
stop(8.0f, match(
string(get("name")),
literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
rgba(255.0f, 255.0f, 255.0f, 1.0f)
))
);
// set camera function expression
layer.setProperties(
fillColor(inputExpression)
);
// get camera function expression
Expression outputExpression = layer.getFillColor().getExpression();
// compare
assertEquals("input expression should match", inputExpression, outputExpression);
});
}
@Test
public void testLiteralProperty() {
validateTestSetup();
setupStyle();
invoke(maplibreMap, (uiController, mapboxMap) -> {
layer.setProperties(
fillColor(literal("#4286f4"))
);
});
}
@Test
public void testLiteralMatchExpression() {
validateTestSetup();
setupStyle();
invoke(maplibreMap, (uiController, mapboxMap) -> {
Expression expression = match(literal("something"), literal(0f),
stop("1", get("1")),
stop("2", get("2")),
stop("3", get("3")),
stop("4", get("4"))
);
layer.setProperties(
fillColor(expression)
);
expression.toArray();
});
}
@Test
public void testCollatorExpression() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
Expression expression1 = eq(literal("Łukasz"), literal("lukasz"), collator(true, true));
Expression expression2 = eq(literal("Łukasz"), literal("lukasz"), collator(literal(false), eq(literal(1),
literal(1)), literal("en")));
Expression expression3 = eq(literal("Łukasz"), literal("lukasz"), collator(literal(false), eq(literal(2),
literal(1))));
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
Layer layer = new CircleLayer("layer", "source")
.withProperties(circleColor(
switchCase(
expression1, literal(ColorUtils.colorToRgbaString(Color.GREEN)),
literal(ColorUtils.colorToRgbaString(Color.RED))
)
));
mapboxMap.getStyle().addLayer(layer);
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
layer.setProperties(circleColor(
switchCase(
expression2, literal(ColorUtils.colorToRgbaString(Color.GREEN)),
literal(ColorUtils.colorToRgbaString(Color.RED))
)
));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
layer.setProperties(circleColor(
switchCase(
expression3, literal(ColorUtils.colorToRgbaString(Color.GREEN)),
literal(ColorUtils.colorToRgbaString(Color.RED))
)
));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
});
}
@Test
public void testConstFormatExpression() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression expression = format(
formatEntry("test")
);
layer.setProperties(textField(expression));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
assertNull(layer.getTextField().getExpression());
assertEquals(new Formatted(new FormattedSection("test")), layer.getTextField().getValue());
});
}
@Test
public void testConstFormatExpressionFontScaleParam() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression expression = format(
formatEntry("test", formatFontScale(1.75))
);
layer.setProperties(textField(expression));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
assertNull(layer.getTextField().getExpression());
assertEquals(new Formatted(new FormattedSection("test", 1.75)), layer.getTextField().getValue());
});
}
@Test
public void testConstFormatExpressionTextFontParam() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression expression = format(
formatEntry(
literal("test"),
formatTextFont(new String[]{"DIN Offc Pro Regular", "Arial Unicode MS Regular"})
)
);
layer.setProperties(textField(expression));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(
mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
);
assertNull(layer.getTextField().getExpression());
assertEquals(new Formatted(
new FormattedSection("test",
new String[]{"DIN Offc Pro Regular", "Arial Unicode MS Regular"})
), layer.getTextField().getValue());
});
}
@Test
public void testConstFormatExpressionTextColorParam() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression expression = format(
formatEntry(
literal("test"),
formatTextColor(literal("yellow"))
)
);
layer.setProperties(textField(expression));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(
mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
);
assertNull(layer.getTextField().getExpression());
assertEquals(new Formatted(
new FormattedSection("test", null, null, "rgba(255,255,0,1)")
), layer.getTextField().getValue());
});
}
@Test
public void testConstFormatExpressionAllParams() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression expression = format(
formatEntry(
"test",
formatFontScale(0.5),
formatTextFont(new String[]{"DIN Offc Pro Regular", "Arial Unicode MS Regular"}),
formatTextColor(rgb(126, 0, 0))
)
);
layer.setProperties(textField(expression));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(
mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
);
assertNull(layer.getTextField().getExpression());
assertEquals(new Formatted(
new FormattedSection("test",
0.5,
new String[]{"DIN Offc Pro Regular", "Arial Unicode MS Regular"},
"rgba(126,0,0,1)")
), layer.getTextField().getValue());
});
}
@Test
public void testConstFormatExpressionMultipleInputs() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression expression = format(
formatEntry(
"test",
formatFontScale(1.5),
formatTextFont(new String[]{"DIN Offc Pro Regular", "Arial Unicode MS Regular"})
),
formatEntry("\ntest2", formatFontScale(2), formatTextColor(Color.BLUE)),
formatEntry("\ntest3", formatFontScale(2.5),
formatTextColor(toColor(literal("rgba(0, 128, 255, 0.5)"))))
);
layer.setProperties(textField(expression));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(
mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty()
);
assertNull(layer.getTextField().getExpression());
assertEquals(new Formatted(
new FormattedSection("test", 1.5,
new String[]{"DIN Offc Pro Regular", "Arial Unicode MS Regular"}),
new FormattedSection("\ntest2", 2.0, null, "rgba(0,0,255,1)"),
new FormattedSection("\ntest3", 2.5, null, "rgba(0,128,255,0.5)")
), layer.getTextField().getValue());
});
}
@Test
public void testVariableFormatExpression() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
Feature feature = Feature.fromGeometry(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()));
feature.addStringProperty("test_property", "test");
feature.addNumberProperty("test_property_number", 1.5);
feature.addStringProperty("test_property_color", "green");
mapboxMap.getStyle().addSource(new GeoJsonSource("source", feature));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression expression = format(
formatEntry(
get("test_property"),
Expression.FormatOption.formatFontScale(number(get("test_property_number"))),
formatTextFont(new String[]{"Arial Unicode MS Regular", "DIN Offc Pro Regular"}),
formatTextColor(toColor(get("test_property_color")))
)
);
layer.setProperties(textField(expression));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
assertEquals(expression, layer.getTextField().getExpression());
assertNull(layer.getTextField().getValue());
});
}
@Test
public void testVariableFormatExpressionMultipleInputs() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
Feature feature = Feature.fromGeometry(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()));
feature.addStringProperty("test_property", "test");
feature.addNumberProperty("test_property_number", 1.5);
feature.addStringProperty("test_property_color", "rgba(0, 255, 0, 1)");
mapboxMap.getStyle().addSource(new GeoJsonSource("source", feature));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression expression = format(
formatEntry(
get("test_property"),
formatFontScale(1.25),
formatTextFont(new String[]{"Arial Unicode MS Regular", "DIN Offc Pro Regular"}),
formatTextColor(toColor(get("test_property_color")))
),
formatEntry("\ntest2", formatFontScale(2))
);
layer.setProperties(textField(expression), textColor("rgba(128, 0, 0, 1)"));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
assertEquals(expression, layer.getTextField().getExpression());
assertNull(layer.getTextField().getValue());
});
}
@Test
public void testFormatExpressionPlainTextCoercion() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
layer.setProperties(textField("test"));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
assertNull(layer.getTextField().getExpression());
assertEquals(new Formatted(
new FormattedSection("test")), layer.getTextField().getValue());
});
}
@Test
public void testTextFieldFormattedArgument() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Formatted formatted = new Formatted(
new FormattedSection("test", 1.5),
new FormattedSection("\ntest", 0.5, new String[]{"Arial Unicode MS Regular",
"DIN Offc Pro Regular"}),
new FormattedSection("test", null, null, "rgba(0,255,0,1)")
);
layer.setProperties(textField(formatted), textColor("rgba(128,0,0,1)"));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(mapboxMap.getProjection().toScreenLocation(latLng), "layer")
.isEmpty());
assertNull(layer.getTextField().getExpression());
assertEquals(formatted, layer.getTextField().getValue());
});
}
@Test
public void testNumberFormatCurrencyExpression() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
layer.setProperties(
textField(
numberFormat(12.345, locale("en-US"), currency("USD"))
)
);
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(
mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
);
assertNull(layer.getTextField().getExpression());
assertEquals("$12.35", layer.getTextField().getValue().getFormattedSections()[0].getText());
});
}
@Test
public void testNumberFormatMaxExpression() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
layer.setProperties(
textField(
numberFormat(12.34567890, maxFractionDigits(5), minFractionDigits(0))
)
);
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(
mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
);
assertNull(layer.getTextField().getExpression());
assertEquals("12.34568", layer.getTextField().getValue().getFormattedSections()[0].getText());
});
}
@Test
public void testNumberFormatMinExpression() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
layer.setProperties(
textField(
numberFormat(12.0000001, maxFractionDigits(5), minFractionDigits(0))
)
);
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(
mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
);
assertNull(layer.getTextField().getExpression());
assertEquals("12", layer.getTextField().getValue().getFormattedSections()[0].getText());
});
}
@Test
public void testNumberFormatLocaleExpression() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle()
.addSource(new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude())));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
layer.setProperties(
textField(
numberFormat(12.0000001, locale("nl-BE"), maxFractionDigits(5),
minFractionDigits(1))
)
);
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(
mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
);
assertNull(layer.getTextField().getExpression());
assertEquals("12,0", layer.getTextField().getValue().getFormattedSections()[0].getText());
});
}
@Test
public void testNumberFormatNonConstantExpression() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
Feature feature = Feature.fromGeometry(Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()));
feature.addNumberProperty("number_value", 12.345678);
feature.addStringProperty("locale_value", "nl-BE");
feature.addNumberProperty("max_value", 5);
feature.addNumberProperty("min_value", 1);
mapboxMap.getStyle().addSource(new GeoJsonSource("source", feature));
SymbolLayer layer = new SymbolLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression numberFormatExpression = numberFormat(
number(number(get("number_value"))),
locale(string(get("locale_value"))),
maxFractionDigits(number(get("max_value"))),
minFractionDigits(number(get("min_value")))
);
layer.setProperties(textField(numberFormatExpression));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertFalse(mapboxMap.queryRenderedFeatures(
mapboxMap.getProjection().toScreenLocation(latLng), "layer").isEmpty()
);
assertNotNull(layer.getTextField().getExpression());
// Expressions evaluated to string are wrapped by a format expression, take array index 1 to get original
Object[] returnExpression = (Object[]) layer.getTextField().getExpression().toArray()[1];
Object[] setExpression = numberFormatExpression.toArray();
assertEquals("Number format should match", returnExpression[0], setExpression[0]);
assertArrayEquals("Get value expression should match",
(Object[]) returnExpression[1],
(Object[]) setExpression[1]
);
// number format objects
HashMap<String, Object> returnMap = (HashMap<String, Object>) returnExpression[2];
HashMap<String, Object> setMap = (HashMap<String, Object>) returnExpression[2];
assertArrayEquals("Number format min fraction digits should match ",
(Object[]) returnMap.get("min-fraction-digits"),
(Object[]) setMap.get("min-fraction-digits")
);
assertArrayEquals("Number format max fraction digits should match ",
(Object[]) returnMap.get("max-fraction-digits"),
(Object[]) setMap.get("max-fraction-digits")
);
assertArrayEquals("Number format min fraction digits should match ",
(Object[]) returnMap.get("locale"),
(Object[]) setMap.get("locale")
);
});
}
/**
* Regression test for #15532
*/
@Test
public void testDoubleConversion() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
LatLng latLng = new LatLng(51, 17);
mapboxMap.getStyle().addSource(
new GeoJsonSource("source", Point.fromLngLat(latLng.getLongitude(), latLng.getLatitude()))
);
CircleLayer layer = new CircleLayer("layer", "source");
mapboxMap.getStyle().addLayer(layer);
Expression input = interpolate(
exponential(0.5f), zoom(),
stop(-0.1, color(Color.RED)),
stop(0, color(Color.BLUE))
);
layer.setProperties(circleColor(input));
Expression output = layer.getCircleColor().getExpression();
assertArrayEquals("Expression should match", input.toArray(), output.toArray());
});
}
private void setupStyle() {
invoke(maplibreMap, (uiController, mapboxMap) -> {
// Add a source
Source source;
source = new GeoJsonSource("amsterdam-parks-source",
ResourceUtils.readRawResource(rule.getActivity(), R.raw.amsterdam));
mapboxMap.getStyle().addSource(source);
// Add a fill layer
mapboxMap.getStyle().addLayer(layer = new FillLayer("amsterdam-parks-layer", source.getId())
.withProperties(
fillColor(rgba(0.0f, 0.0f, 0.0f, 0.5f)),
fillOutlineColor(rgb(0, 0, 255)),
fillAntialias(true)
)
);
});
}
}

View File

@@ -0,0 +1,416 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.FillExtrusionLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for FillExtrusionLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class FillExtrusionLayerTest extends BaseLayerTest {
private FillExtrusionLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new FillExtrusionLayer("my-layer", "composite");
layer.setSourceLayer("composite");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testSourceLayer() {
Timber.i("SourceLayer");
assertNotNull(layer);
// Get initial
assertEquals(layer.getSourceLayer(), "composite");
// Set
final String sourceLayer = "test";
layer.setSourceLayer(sourceLayer);
assertEquals(layer.getSourceLayer(), sourceLayer);
}
@Test
@UiThreadTest
public void testFilter() {
Timber.i("Filter");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// Set
Expression filter = eq(get("undefined"), literal(1.0));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// Set constant
filter = literal(true);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterDistance() {
Timber.i("FilterDistance");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// distance with Point
Expression filter = lt(distance(Point.fromLngLat(1.0, 1.0)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with LineString
filter = lt(distance(LineString.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiPoint.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiLineString.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with Polygon
filter = lt(distance(Polygon.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPolygon
filter = lt(distance(MultiPolygon.fromLngLats(Collections
.singletonList(Collections.singletonList(pointsList)))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterWithin() {
Timber.i("FilterWithin");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
Expression filter = within(Polygon.fromLngLats(Collections.singletonList(pointsList)));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFillExtrusionOpacityTransition() {
Timber.i("fill-extrusion-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillExtrusionOpacityTransition(options);
assertEquals(layer.getFillExtrusionOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testFillExtrusionOpacityAsConstant() {
Timber.i("fill-extrusion-opacity");
assertNotNull(layer);
assertNull(layer.getFillExtrusionOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(fillExtrusionOpacity(propertyValue));
assertEquals(layer.getFillExtrusionOpacity().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillExtrusionColorTransition() {
Timber.i("fill-extrusion-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillExtrusionColorTransition(options);
assertEquals(layer.getFillExtrusionColorTransition(), options);
}
@Test
@UiThreadTest
public void testFillExtrusionColorAsConstant() {
Timber.i("fill-extrusion-color");
assertNotNull(layer);
assertNull(layer.getFillExtrusionColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(fillExtrusionColor(propertyValue));
assertEquals(layer.getFillExtrusionColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillExtrusionColorAsExpression() {
Timber.i("fill-extrusion-color-expression");
assertNotNull(layer);
assertNull(layer.getFillExtrusionColor().getExpression());
// Set and Get
Expression expression = toColor(Expression.get("undefined"));
layer.setProperties(fillExtrusionColor(expression));
assertEquals(layer.getFillExtrusionColor().getExpression(), expression);
}
@Test
@UiThreadTest
public void testFillExtrusionColorAsIntConstant() {
Timber.i("fill-extrusion-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(fillExtrusionColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getFillExtrusionColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testFillExtrusionTranslateTransition() {
Timber.i("fill-extrusion-translateTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillExtrusionTranslateTransition(options);
assertEquals(layer.getFillExtrusionTranslateTransition(), options);
}
@Test
@UiThreadTest
public void testFillExtrusionTranslateAsConstant() {
Timber.i("fill-extrusion-translate");
assertNotNull(layer);
assertNull(layer.getFillExtrusionTranslate().getValue());
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(fillExtrusionTranslate(propertyValue));
assertEquals(layer.getFillExtrusionTranslate().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillExtrusionTranslateAnchorAsConstant() {
Timber.i("fill-extrusion-translate-anchor");
assertNotNull(layer);
assertNull(layer.getFillExtrusionTranslateAnchor().getValue());
// Set and Get
String propertyValue = FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP;
layer.setProperties(fillExtrusionTranslateAnchor(propertyValue));
assertEquals(layer.getFillExtrusionTranslateAnchor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillExtrusionPatternTransition() {
Timber.i("fill-extrusion-patternTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillExtrusionPatternTransition(options);
assertEquals(layer.getFillExtrusionPatternTransition(), options);
}
@Test
@UiThreadTest
public void testFillExtrusionPatternAsConstant() {
Timber.i("fill-extrusion-pattern");
assertNotNull(layer);
assertNull(layer.getFillExtrusionPattern().getValue());
// Set and Get
String propertyValue = "pedestrian-polygon";
layer.setProperties(fillExtrusionPattern(propertyValue));
assertEquals(layer.getFillExtrusionPattern().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillExtrusionPatternAsExpression() {
Timber.i("fill-extrusion-pattern-expression");
assertNotNull(layer);
assertNull(layer.getFillExtrusionPattern().getExpression());
// Set and Get
Expression expression = image(string(Expression.get("undefined")));
layer.setProperties(fillExtrusionPattern(expression));
assertEquals(layer.getFillExtrusionPattern().getExpression(), expression);
}
@Test
@UiThreadTest
public void testFillExtrusionHeightTransition() {
Timber.i("fill-extrusion-heightTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillExtrusionHeightTransition(options);
assertEquals(layer.getFillExtrusionHeightTransition(), options);
}
@Test
@UiThreadTest
public void testFillExtrusionHeightAsConstant() {
Timber.i("fill-extrusion-height");
assertNotNull(layer);
assertNull(layer.getFillExtrusionHeight().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(fillExtrusionHeight(propertyValue));
assertEquals(layer.getFillExtrusionHeight().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillExtrusionHeightAsExpression() {
Timber.i("fill-extrusion-height-expression");
assertNotNull(layer);
assertNull(layer.getFillExtrusionHeight().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(fillExtrusionHeight(expression));
assertEquals(layer.getFillExtrusionHeight().getExpression(), expression);
}
@Test
@UiThreadTest
public void testFillExtrusionBaseTransition() {
Timber.i("fill-extrusion-baseTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillExtrusionBaseTransition(options);
assertEquals(layer.getFillExtrusionBaseTransition(), options);
}
@Test
@UiThreadTest
public void testFillExtrusionBaseAsConstant() {
Timber.i("fill-extrusion-base");
assertNotNull(layer);
assertNull(layer.getFillExtrusionBase().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(fillExtrusionBase(propertyValue));
assertEquals(layer.getFillExtrusionBase().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillExtrusionBaseAsExpression() {
Timber.i("fill-extrusion-base-expression");
assertNotNull(layer);
assertNull(layer.getFillExtrusionBase().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(fillExtrusionBase(expression));
assertEquals(layer.getFillExtrusionBase().getExpression(), expression);
}
@Test
@UiThreadTest
public void testFillExtrusionVerticalGradientAsConstant() {
Timber.i("fill-extrusion-vertical-gradient");
assertNotNull(layer);
assertNull(layer.getFillExtrusionVerticalGradient().getValue());
// Set and Get
Boolean propertyValue = true;
layer.setProperties(fillExtrusionVerticalGradient(propertyValue));
assertEquals(layer.getFillExtrusionVerticalGradient().getValue(), propertyValue);
}
}

View File

@@ -0,0 +1,428 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.FillLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for FillLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class FillLayerTest extends BaseLayerTest {
private FillLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new FillLayer("my-layer", "composite");
layer.setSourceLayer("composite");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testSourceLayer() {
Timber.i("SourceLayer");
assertNotNull(layer);
// Get initial
assertEquals(layer.getSourceLayer(), "composite");
// Set
final String sourceLayer = "test";
layer.setSourceLayer(sourceLayer);
assertEquals(layer.getSourceLayer(), sourceLayer);
}
@Test
@UiThreadTest
public void testFilter() {
Timber.i("Filter");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// Set
Expression filter = eq(get("undefined"), literal(1.0));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// Set constant
filter = literal(true);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterDistance() {
Timber.i("FilterDistance");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// distance with Point
Expression filter = lt(distance(Point.fromLngLat(1.0, 1.0)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with LineString
filter = lt(distance(LineString.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiPoint.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiLineString.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with Polygon
filter = lt(distance(Polygon.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPolygon
filter = lt(distance(MultiPolygon.fromLngLats(Collections
.singletonList(Collections.singletonList(pointsList)))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterWithin() {
Timber.i("FilterWithin");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
Expression filter = within(Polygon.fromLngLats(Collections.singletonList(pointsList)));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFillSortKeyAsConstant() {
Timber.i("fill-sort-key");
assertNotNull(layer);
assertNull(layer.getFillSortKey().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(fillSortKey(propertyValue));
assertEquals(layer.getFillSortKey().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillSortKeyAsExpression() {
Timber.i("fill-sort-key-expression");
assertNotNull(layer);
assertNull(layer.getFillSortKey().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(fillSortKey(expression));
assertEquals(layer.getFillSortKey().getExpression(), expression);
}
@Test
@UiThreadTest
public void testFillAntialiasAsConstant() {
Timber.i("fill-antialias");
assertNotNull(layer);
assertNull(layer.getFillAntialias().getValue());
// Set and Get
Boolean propertyValue = true;
layer.setProperties(fillAntialias(propertyValue));
assertEquals(layer.getFillAntialias().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillOpacityTransition() {
Timber.i("fill-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillOpacityTransition(options);
assertEquals(layer.getFillOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testFillOpacityAsConstant() {
Timber.i("fill-opacity");
assertNotNull(layer);
assertNull(layer.getFillOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(fillOpacity(propertyValue));
assertEquals(layer.getFillOpacity().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillOpacityAsExpression() {
Timber.i("fill-opacity-expression");
assertNotNull(layer);
assertNull(layer.getFillOpacity().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(fillOpacity(expression));
assertEquals(layer.getFillOpacity().getExpression(), expression);
}
@Test
@UiThreadTest
public void testFillColorTransition() {
Timber.i("fill-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillColorTransition(options);
assertEquals(layer.getFillColorTransition(), options);
}
@Test
@UiThreadTest
public void testFillColorAsConstant() {
Timber.i("fill-color");
assertNotNull(layer);
assertNull(layer.getFillColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(fillColor(propertyValue));
assertEquals(layer.getFillColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillColorAsExpression() {
Timber.i("fill-color-expression");
assertNotNull(layer);
assertNull(layer.getFillColor().getExpression());
// Set and Get
Expression expression = toColor(Expression.get("undefined"));
layer.setProperties(fillColor(expression));
assertEquals(layer.getFillColor().getExpression(), expression);
}
@Test
@UiThreadTest
public void testFillColorAsIntConstant() {
Timber.i("fill-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(fillColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getFillColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testFillOutlineColorTransition() {
Timber.i("fill-outline-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillOutlineColorTransition(options);
assertEquals(layer.getFillOutlineColorTransition(), options);
}
@Test
@UiThreadTest
public void testFillOutlineColorAsConstant() {
Timber.i("fill-outline-color");
assertNotNull(layer);
assertNull(layer.getFillOutlineColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(fillOutlineColor(propertyValue));
assertEquals(layer.getFillOutlineColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillOutlineColorAsExpression() {
Timber.i("fill-outline-color-expression");
assertNotNull(layer);
assertNull(layer.getFillOutlineColor().getExpression());
// Set and Get
Expression expression = toColor(Expression.get("undefined"));
layer.setProperties(fillOutlineColor(expression));
assertEquals(layer.getFillOutlineColor().getExpression(), expression);
}
@Test
@UiThreadTest
public void testFillOutlineColorAsIntConstant() {
Timber.i("fill-outline-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(fillOutlineColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getFillOutlineColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testFillTranslateTransition() {
Timber.i("fill-translateTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillTranslateTransition(options);
assertEquals(layer.getFillTranslateTransition(), options);
}
@Test
@UiThreadTest
public void testFillTranslateAsConstant() {
Timber.i("fill-translate");
assertNotNull(layer);
assertNull(layer.getFillTranslate().getValue());
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(fillTranslate(propertyValue));
assertEquals(layer.getFillTranslate().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillTranslateAnchorAsConstant() {
Timber.i("fill-translate-anchor");
assertNotNull(layer);
assertNull(layer.getFillTranslateAnchor().getValue());
// Set and Get
String propertyValue = FILL_TRANSLATE_ANCHOR_MAP;
layer.setProperties(fillTranslateAnchor(propertyValue));
assertEquals(layer.getFillTranslateAnchor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillPatternTransition() {
Timber.i("fill-patternTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setFillPatternTransition(options);
assertEquals(layer.getFillPatternTransition(), options);
}
@Test
@UiThreadTest
public void testFillPatternAsConstant() {
Timber.i("fill-pattern");
assertNotNull(layer);
assertNull(layer.getFillPattern().getValue());
// Set and Get
String propertyValue = "pedestrian-polygon";
layer.setProperties(fillPattern(propertyValue));
assertEquals(layer.getFillPattern().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testFillPatternAsExpression() {
Timber.i("fill-pattern-expression");
assertNotNull(layer);
assertNull(layer.getFillPattern().getExpression());
// Set and Get
Expression expression = image(string(Expression.get("undefined")));
layer.setProperties(fillPattern(expression));
assertEquals(layer.getFillPattern().getExpression(), expression);
}
}

View File

@@ -0,0 +1,171 @@
package org.maplibre.android.testapp.style;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.style.layers.CircleLayer;
import org.maplibre.android.style.layers.Layer;
import org.maplibre.android.style.sources.GeoJsonSource;
import org.maplibre.android.testapp.R;
import org.maplibre.android.testapp.action.MapLibreMapAction;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.maplibre.android.testapp.utils.ResourceUtils;
import org.maplibre.android.testapp.utils.TestingAsyncUtils;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.RawRes;
import androidx.test.espresso.ViewAction;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link GeoJsonSource}
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class GeoJsonSourceTests extends EspressoTest {
@Test
public void testFeatureCollection() {
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
GeoJsonSource source = null;
source = new GeoJsonSource("source", FeatureCollection
.fromJson(ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_collection)));
mapboxMap.getStyle().addSource(source);
mapboxMap.getStyle().addLayer(new CircleLayer("layer", source.getId()));
});
}
@Test
public void testPointGeometry() {
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
GeoJsonSource source = new GeoJsonSource("source", Point.fromLngLat(0d, 0d));
mapboxMap.getStyle().addSource(source);
mapboxMap.getStyle().addLayer(new CircleLayer("layer", source.getId()));
});
}
@Test
public void testFeatureProperties() {
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
GeoJsonSource source = null;
source = new GeoJsonSource("source",
ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_properties));
mapboxMap.getStyle().addSource(source);
mapboxMap.getStyle().addLayer(new CircleLayer("layer", source.getId()));
});
}
@Test
public void testUpdateCoalescing() {
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
GeoJsonSource source = new GeoJsonSource("source");
mapboxMap.getStyle().addSource(source);
mapboxMap.getStyle().addLayer(new CircleLayer("layer", source.getId()));
source.setGeoJson(Point.fromLngLat(0, 0));
source.setGeoJson(Point.fromLngLat(-25, -25));
source.setGeoJson(ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_properties));
source.setGeoJson(Point.fromLngLat(20, 55));
TestingAsyncUtils.INSTANCE.waitForLayer(uiController, mapView);
assertEquals(1, mapboxMap.queryRenderedFeatures(
mapboxMap.getProjection().toScreenLocation(
new LatLng(55, 20)), "layer").size());
});
}
@Test
public void testClearCollectionDuringConversion() {
// https://github.com/mapbox/mapbox-gl-native/issues/14565
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
for (int j = 0; j < 1000; j++) {
List<Feature> features = new ArrayList<>();
for (int i = 0; i < 100; i++) {
features.add(Feature.fromGeometry(Point.fromLngLat(0, 0)));
}
mapboxMap.getStyle().addSource(new GeoJsonSource("source" + j, FeatureCollection.fromFeatures(features)));
features.clear();
}
});
}
@Test
public void testPointFeature() {
testFeatureFromResource(R.raw.test_point_feature);
}
@Test
public void testLineStringFeature() {
testFeatureFromResource(R.raw.test_line_string_feature);
}
@Test
public void testPolygonFeature() {
testFeatureFromResource(R.raw.test_polygon_feature);
}
@Test
public void testPolygonWithHoleFeature() {
testFeatureFromResource(R.raw.test_polygon_with_hole_feature);
}
@Test
public void testMultiPointFeature() {
testFeatureFromResource(R.raw.test_multi_point_feature);
}
@Test
public void testMultiLineStringFeature() {
testFeatureFromResource(R.raw.test_multi_line_string_feature);
}
@Test
public void testMultiPolygonFeature() {
testFeatureFromResource(R.raw.test_multi_polygon_feature);
}
protected void testFeatureFromResource(final @RawRes int resource) {
validateTestSetup();
MapLibreMapAction.invoke(maplibreMap, (uiController, mapboxMap) -> {
GeoJsonSource source = new GeoJsonSource("source");
mapboxMap.getStyle().addSource(source);
Layer layer = new CircleLayer("layer", source.getId());
mapboxMap.getStyle().addLayer(layer);
source.setGeoJson(Feature.fromJson(ResourceUtils.readRawResource(rule.getActivity(), resource)));
mapboxMap.getStyle().removeLayer(layer);
mapboxMap.getStyle().removeSource(source);
});
}
public abstract class BaseViewAction implements ViewAction {
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
}
}

View File

@@ -0,0 +1,291 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.HeatmapLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for HeatmapLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class HeatmapLayerTest extends BaseLayerTest {
private HeatmapLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new HeatmapLayer("my-layer", "composite");
layer.setSourceLayer("composite");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testSourceLayer() {
Timber.i("SourceLayer");
assertNotNull(layer);
// Get initial
assertEquals(layer.getSourceLayer(), "composite");
// Set
final String sourceLayer = "test";
layer.setSourceLayer(sourceLayer);
assertEquals(layer.getSourceLayer(), sourceLayer);
}
@Test
@UiThreadTest
public void testFilter() {
Timber.i("Filter");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// Set
Expression filter = eq(get("undefined"), literal(1.0));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// Set constant
filter = literal(true);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterDistance() {
Timber.i("FilterDistance");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// distance with Point
Expression filter = lt(distance(Point.fromLngLat(1.0, 1.0)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with LineString
filter = lt(distance(LineString.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiPoint.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiLineString.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with Polygon
filter = lt(distance(Polygon.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPolygon
filter = lt(distance(MultiPolygon.fromLngLats(Collections
.singletonList(Collections.singletonList(pointsList)))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterWithin() {
Timber.i("FilterWithin");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
Expression filter = within(Polygon.fromLngLats(Collections.singletonList(pointsList)));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testHeatmapRadiusTransition() {
Timber.i("heatmap-radiusTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHeatmapRadiusTransition(options);
assertEquals(layer.getHeatmapRadiusTransition(), options);
}
@Test
@UiThreadTest
public void testHeatmapRadiusAsConstant() {
Timber.i("heatmap-radius");
assertNotNull(layer);
assertNull(layer.getHeatmapRadius().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(heatmapRadius(propertyValue));
assertEquals(layer.getHeatmapRadius().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHeatmapRadiusAsExpression() {
Timber.i("heatmap-radius-expression");
assertNotNull(layer);
assertNull(layer.getHeatmapRadius().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(heatmapRadius(expression));
assertEquals(layer.getHeatmapRadius().getExpression(), expression);
}
@Test
@UiThreadTest
public void testHeatmapWeightAsConstant() {
Timber.i("heatmap-weight");
assertNotNull(layer);
assertNull(layer.getHeatmapWeight().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(heatmapWeight(propertyValue));
assertEquals(layer.getHeatmapWeight().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHeatmapWeightAsExpression() {
Timber.i("heatmap-weight-expression");
assertNotNull(layer);
assertNull(layer.getHeatmapWeight().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(heatmapWeight(expression));
assertEquals(layer.getHeatmapWeight().getExpression(), expression);
}
@Test
@UiThreadTest
public void testHeatmapIntensityTransition() {
Timber.i("heatmap-intensityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHeatmapIntensityTransition(options);
assertEquals(layer.getHeatmapIntensityTransition(), options);
}
@Test
@UiThreadTest
public void testHeatmapIntensityAsConstant() {
Timber.i("heatmap-intensity");
assertNotNull(layer);
assertNull(layer.getHeatmapIntensity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(heatmapIntensity(propertyValue));
assertEquals(layer.getHeatmapIntensity().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHeatmapOpacityTransition() {
Timber.i("heatmap-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHeatmapOpacityTransition(options);
assertEquals(layer.getHeatmapOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testHeatmapOpacityAsConstant() {
Timber.i("heatmap-opacity");
assertNotNull(layer);
assertNull(layer.getHeatmapOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(heatmapOpacity(propertyValue));
assertEquals(layer.getHeatmapOpacity().getValue(), propertyValue);
}
}

View File

@@ -0,0 +1,245 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.HillshadeLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for HillshadeLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class HillshadeLayerTest extends BaseLayerTest {
private HillshadeLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new HillshadeLayer("my-layer", "composite");
layer.setSourceLayer("composite");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testHillshadeIlluminationDirectionAsConstant() {
Timber.i("hillshade-illumination-direction");
assertNotNull(layer);
assertNull(layer.getHillshadeIlluminationDirection().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(hillshadeIlluminationDirection(propertyValue));
assertEquals(layer.getHillshadeIlluminationDirection().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeIlluminationAnchorAsConstant() {
Timber.i("hillshade-illumination-anchor");
assertNotNull(layer);
assertNull(layer.getHillshadeIlluminationAnchor().getValue());
// Set and Get
String propertyValue = HILLSHADE_ILLUMINATION_ANCHOR_MAP;
layer.setProperties(hillshadeIlluminationAnchor(propertyValue));
assertEquals(layer.getHillshadeIlluminationAnchor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeExaggerationTransition() {
Timber.i("hillshade-exaggerationTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHillshadeExaggerationTransition(options);
assertEquals(layer.getHillshadeExaggerationTransition(), options);
}
@Test
@UiThreadTest
public void testHillshadeExaggerationAsConstant() {
Timber.i("hillshade-exaggeration");
assertNotNull(layer);
assertNull(layer.getHillshadeExaggeration().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(hillshadeExaggeration(propertyValue));
assertEquals(layer.getHillshadeExaggeration().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeShadowColorTransition() {
Timber.i("hillshade-shadow-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHillshadeShadowColorTransition(options);
assertEquals(layer.getHillshadeShadowColorTransition(), options);
}
@Test
@UiThreadTest
public void testHillshadeShadowColorAsConstant() {
Timber.i("hillshade-shadow-color");
assertNotNull(layer);
assertNull(layer.getHillshadeShadowColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(hillshadeShadowColor(propertyValue));
assertEquals(layer.getHillshadeShadowColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeShadowColorAsIntConstant() {
Timber.i("hillshade-shadow-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(hillshadeShadowColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getHillshadeShadowColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testHillshadeHighlightColorTransition() {
Timber.i("hillshade-highlight-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHillshadeHighlightColorTransition(options);
assertEquals(layer.getHillshadeHighlightColorTransition(), options);
}
@Test
@UiThreadTest
public void testHillshadeHighlightColorAsConstant() {
Timber.i("hillshade-highlight-color");
assertNotNull(layer);
assertNull(layer.getHillshadeHighlightColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(hillshadeHighlightColor(propertyValue));
assertEquals(layer.getHillshadeHighlightColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeHighlightColorAsIntConstant() {
Timber.i("hillshade-highlight-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(hillshadeHighlightColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getHillshadeHighlightColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testHillshadeAccentColorTransition() {
Timber.i("hillshade-accent-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setHillshadeAccentColorTransition(options);
assertEquals(layer.getHillshadeAccentColorTransition(), options);
}
@Test
@UiThreadTest
public void testHillshadeAccentColorAsConstant() {
Timber.i("hillshade-accent-color");
assertNotNull(layer);
assertNull(layer.getHillshadeAccentColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(hillshadeAccentColor(propertyValue));
assertEquals(layer.getHillshadeAccentColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testHillshadeAccentColorAsIntConstant() {
Timber.i("hillshade-accent-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(hillshadeAccentColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getHillshadeAccentColorAsInt(), Color.argb(127, 255, 127, 0));
}
}

View File

@@ -0,0 +1,75 @@
package org.maplibre.android.testapp.style
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.action.MapLibreMapAction
import org.maplibre.android.testapp.activity.EspressoTest
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*
/**
* CRUD tests around Image
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class ImageTest : EspressoTest() {
companion object {
private const val IMAGE_ID = "test.image"
}
@Test
fun testAddGetImage() {
validateTestSetup()
MapLibreMapAction.invoke(maplibreMap) { uiController, mapboxMap ->
val drawable = rule.activity.resources.getDrawable(R.drawable.ic_launcher_round)
assertTrue(drawable is BitmapDrawable)
val bitmapSet = (drawable as BitmapDrawable).bitmap
mapboxMap.style!!.addImage(IMAGE_ID, bitmapSet)
// adding an image requires converting the image with an asynctask
uiController.loopMainThreadForAtLeast(200)
val bitmapGet = mapboxMap.style!!.getImage(IMAGE_ID)
assertTrue(bitmapGet!!.similarTo(bitmapSet))
mapboxMap.style!!.removeImage(IMAGE_ID)
assertNull(mapboxMap.style!!.getImage(IMAGE_ID))
}
}
}
/**
* Alternative implementation of Bitmap.sameAs #14060
*/
fun Bitmap.similarTo(other: Bitmap): Boolean {
if (invalidConfig(other)) {
return false
}
// Allocate arrays
val argb = IntArray(width * height)
val argbOther = IntArray(other.width * other.height)
getPixels(argb, 0, width, 0, 0, width, height)
other.getPixels(argbOther, 0, width, 0, 0, width, height)
// Alpha channel special check
if (config == Bitmap.Config.ALPHA_8) {
// in this case we have to manually compare the alpha channel as the rest is garbage.
val length = width * height
for (i in 0 until length) {
if (argb[i] and -0x1000000 != argbOther[i] and -0x1000000) {
return false
}
}
return true
}
return Arrays.equals(argb, argbOther)
}
fun Bitmap.invalidConfig(other: Bitmap): Boolean = this.config != other.config || this.width != other.width || this.height != other.height

View File

@@ -0,0 +1,172 @@
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import android.view.View;
import org.maplibre.android.style.light.Light;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.FillExtrusionLayer;
import org.maplibre.android.style.layers.TransitionOptions;
import org.maplibre.android.style.light.Position;
import org.maplibre.android.testapp.R;
import org.maplibre.android.testapp.activity.BaseTest;
import org.maplibre.android.testapp.activity.style.FillExtrusionStyleTestActivity;
import timber.log.Timber;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.maplibre.android.style.expressions.Expression.eq;
import static org.maplibre.android.style.layers.Property.ANCHOR_MAP;
import static org.maplibre.android.style.layers.PropertyFactory.fillExtrusionBase;
import static org.maplibre.android.style.layers.PropertyFactory.fillExtrusionColor;
import static org.maplibre.android.style.layers.PropertyFactory.fillExtrusionHeight;
import static org.maplibre.android.style.layers.PropertyFactory.fillExtrusionOpacity;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(AndroidJUnit4ClassRunner.class)
public class LightTest extends BaseTest {
private Light light;
@Test
public void testAnchor() {
validateTestSetup();
setupLight();
Timber.i("anchor");
invoke(maplibreMap, (uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
light.setAnchor(ANCHOR_MAP);
assertEquals("Anchor should match", ANCHOR_MAP, light.getAnchor());
});
}
@Test
public void testPositionTransition() {
validateTestSetup();
setupLight();
Timber.i("positionTransitionOptions");
invoke(maplibreMap, (uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
light.setPositionTransition(options);
assertEquals("Transition options should match", options, light.getPositionTransition());
});
}
@Test
public void testPosition() {
validateTestSetup();
setupLight();
Timber.i("position");
invoke(maplibreMap,(uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
Position position = new Position(1, 2, 3);
light.setPosition(position);
assertEquals("Position should match", position, light.getPosition());
});
}
@Test
public void testColorTransition() {
validateTestSetup();
setupLight();
Timber.i("colorTransitionOptions");
invoke(maplibreMap, (uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
light.setColorTransition(options);
assertEquals("Transition options should match", options, light.getColorTransition());
});
}
@Test
public void testColor() {
validateTestSetup();
setupLight();
Timber.i("color");
invoke(maplibreMap, (uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
light.setColor("rgba(255,128,0,0.7)");
assertEquals("Color should match", "rgba(255,128,0,0.7)", light.getColor());
});
}
@Test
public void testIntensityTransition() {
validateTestSetup();
setupLight();
Timber.i("intensityTransitionOptions");
invoke(maplibreMap, (uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
light.setIntensityTransition(options);
assertEquals("Transition options should match", options, light.getIntensityTransition());
});
}
@Test
public void testIntensity() {
validateTestSetup();
setupLight();
Timber.i("intensity");
invoke(maplibreMap, (uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
light.setIntensity(0.3f);
assertEquals("Intensity should match", 0.3f, light.getIntensity(), 0f);
});
}
private void setupLight() {
onView(withId(R.id.mapView)).perform(new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
@Override
public void perform(UiController uiController, View view) {
light = maplibreMap.getStyle().getLight();
FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite");
fillExtrusionLayer.setSourceLayer("building");
fillExtrusionLayer.setFilter(eq(Expression.get("extrude"), "true"));
fillExtrusionLayer.setMinZoom(15);
fillExtrusionLayer.setProperties(
fillExtrusionColor(Color.LTGRAY),
fillExtrusionHeight(Expression.get("height")),
fillExtrusionBase(Expression.get("min_height")),
fillExtrusionOpacity(0.6f)
);
maplibreMap.getStyle().addLayer(fillExtrusionLayer);
}
});
}
@Override
protected Class getActivityClass() {
return FillExtrusionStyleTestActivity.class;
}
}

View File

@@ -0,0 +1,595 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.LineLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for LineLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class LineLayerTest extends BaseLayerTest {
private LineLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new LineLayer("my-layer", "composite");
layer.setSourceLayer("composite");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testSourceLayer() {
Timber.i("SourceLayer");
assertNotNull(layer);
// Get initial
assertEquals(layer.getSourceLayer(), "composite");
// Set
final String sourceLayer = "test";
layer.setSourceLayer(sourceLayer);
assertEquals(layer.getSourceLayer(), sourceLayer);
}
@Test
@UiThreadTest
public void testFilter() {
Timber.i("Filter");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// Set
Expression filter = eq(get("undefined"), literal(1.0));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// Set constant
filter = literal(true);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterDistance() {
Timber.i("FilterDistance");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// distance with Point
Expression filter = lt(distance(Point.fromLngLat(1.0, 1.0)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with LineString
filter = lt(distance(LineString.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiPoint.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiLineString.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with Polygon
filter = lt(distance(Polygon.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPolygon
filter = lt(distance(MultiPolygon.fromLngLats(Collections
.singletonList(Collections.singletonList(pointsList)))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterWithin() {
Timber.i("FilterWithin");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
Expression filter = within(Polygon.fromLngLats(Collections.singletonList(pointsList)));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testLineCapAsConstant() {
Timber.i("line-cap");
assertNotNull(layer);
assertNull(layer.getLineCap().getValue());
// Set and Get
String propertyValue = LINE_CAP_BUTT;
layer.setProperties(lineCap(propertyValue));
assertEquals(layer.getLineCap().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineJoinAsConstant() {
Timber.i("line-join");
assertNotNull(layer);
assertNull(layer.getLineJoin().getValue());
// Set and Get
String propertyValue = LINE_JOIN_BEVEL;
layer.setProperties(lineJoin(propertyValue));
assertEquals(layer.getLineJoin().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineJoinAsExpression() {
Timber.i("line-join-expression");
assertNotNull(layer);
assertNull(layer.getLineJoin().getExpression());
// Set and Get
Expression expression = string(Expression.get("undefined"));
layer.setProperties(lineJoin(expression));
assertEquals(layer.getLineJoin().getExpression(), expression);
}
@Test
@UiThreadTest
public void testLineMiterLimitAsConstant() {
Timber.i("line-miter-limit");
assertNotNull(layer);
assertNull(layer.getLineMiterLimit().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(lineMiterLimit(propertyValue));
assertEquals(layer.getLineMiterLimit().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineRoundLimitAsConstant() {
Timber.i("line-round-limit");
assertNotNull(layer);
assertNull(layer.getLineRoundLimit().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(lineRoundLimit(propertyValue));
assertEquals(layer.getLineRoundLimit().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineSortKeyAsConstant() {
Timber.i("line-sort-key");
assertNotNull(layer);
assertNull(layer.getLineSortKey().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(lineSortKey(propertyValue));
assertEquals(layer.getLineSortKey().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineSortKeyAsExpression() {
Timber.i("line-sort-key-expression");
assertNotNull(layer);
assertNull(layer.getLineSortKey().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(lineSortKey(expression));
assertEquals(layer.getLineSortKey().getExpression(), expression);
}
@Test
@UiThreadTest
public void testLineOpacityTransition() {
Timber.i("line-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLineOpacityTransition(options);
assertEquals(layer.getLineOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testLineOpacityAsConstant() {
Timber.i("line-opacity");
assertNotNull(layer);
assertNull(layer.getLineOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(lineOpacity(propertyValue));
assertEquals(layer.getLineOpacity().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineOpacityAsExpression() {
Timber.i("line-opacity-expression");
assertNotNull(layer);
assertNull(layer.getLineOpacity().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(lineOpacity(expression));
assertEquals(layer.getLineOpacity().getExpression(), expression);
}
@Test
@UiThreadTest
public void testLineColorTransition() {
Timber.i("line-colorTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLineColorTransition(options);
assertEquals(layer.getLineColorTransition(), options);
}
@Test
@UiThreadTest
public void testLineColorAsConstant() {
Timber.i("line-color");
assertNotNull(layer);
assertNull(layer.getLineColor().getValue());
// Set and Get
String propertyValue = "rgba(255,128,0,0.7)";
layer.setProperties(lineColor(propertyValue));
assertEquals(layer.getLineColor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineColorAsExpression() {
Timber.i("line-color-expression");
assertNotNull(layer);
assertNull(layer.getLineColor().getExpression());
// Set and Get
Expression expression = toColor(Expression.get("undefined"));
layer.setProperties(lineColor(expression));
assertEquals(layer.getLineColor().getExpression(), expression);
}
@Test
@UiThreadTest
public void testLineColorAsIntConstant() {
Timber.i("line-color");
assertNotNull(layer);
// Set and Get
layer.setProperties(lineColor(Color.argb(127, 255, 127, 0)));
assertEquals(layer.getLineColorAsInt(), Color.argb(127, 255, 127, 0));
}
@Test
@UiThreadTest
public void testLineTranslateTransition() {
Timber.i("line-translateTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLineTranslateTransition(options);
assertEquals(layer.getLineTranslateTransition(), options);
}
@Test
@UiThreadTest
public void testLineTranslateAsConstant() {
Timber.i("line-translate");
assertNotNull(layer);
assertNull(layer.getLineTranslate().getValue());
// Set and Get
Float[] propertyValue = new Float[] {0f, 0f};
layer.setProperties(lineTranslate(propertyValue));
assertEquals(layer.getLineTranslate().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineTranslateAnchorAsConstant() {
Timber.i("line-translate-anchor");
assertNotNull(layer);
assertNull(layer.getLineTranslateAnchor().getValue());
// Set and Get
String propertyValue = LINE_TRANSLATE_ANCHOR_MAP;
layer.setProperties(lineTranslateAnchor(propertyValue));
assertEquals(layer.getLineTranslateAnchor().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineWidthTransition() {
Timber.i("line-widthTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLineWidthTransition(options);
assertEquals(layer.getLineWidthTransition(), options);
}
@Test
@UiThreadTest
public void testLineWidthAsConstant() {
Timber.i("line-width");
assertNotNull(layer);
assertNull(layer.getLineWidth().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(lineWidth(propertyValue));
assertEquals(layer.getLineWidth().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineWidthAsExpression() {
Timber.i("line-width-expression");
assertNotNull(layer);
assertNull(layer.getLineWidth().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(lineWidth(expression));
assertEquals(layer.getLineWidth().getExpression(), expression);
}
@Test
@UiThreadTest
public void testLineGapWidthTransition() {
Timber.i("line-gap-widthTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLineGapWidthTransition(options);
assertEquals(layer.getLineGapWidthTransition(), options);
}
@Test
@UiThreadTest
public void testLineGapWidthAsConstant() {
Timber.i("line-gap-width");
assertNotNull(layer);
assertNull(layer.getLineGapWidth().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(lineGapWidth(propertyValue));
assertEquals(layer.getLineGapWidth().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineGapWidthAsExpression() {
Timber.i("line-gap-width-expression");
assertNotNull(layer);
assertNull(layer.getLineGapWidth().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(lineGapWidth(expression));
assertEquals(layer.getLineGapWidth().getExpression(), expression);
}
@Test
@UiThreadTest
public void testLineOffsetTransition() {
Timber.i("line-offsetTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLineOffsetTransition(options);
assertEquals(layer.getLineOffsetTransition(), options);
}
@Test
@UiThreadTest
public void testLineOffsetAsConstant() {
Timber.i("line-offset");
assertNotNull(layer);
assertNull(layer.getLineOffset().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(lineOffset(propertyValue));
assertEquals(layer.getLineOffset().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineBlurTransition() {
Timber.i("line-blurTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLineBlurTransition(options);
assertEquals(layer.getLineBlurTransition(), options);
}
@Test
@UiThreadTest
public void testLineBlurAsConstant() {
Timber.i("line-blur");
assertNotNull(layer);
assertNull(layer.getLineBlur().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(lineBlur(propertyValue));
assertEquals(layer.getLineBlur().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLineBlurAsExpression() {
Timber.i("line-blur-expression");
assertNotNull(layer);
assertNull(layer.getLineBlur().getExpression());
// Set and Get
Expression expression = number(Expression.get("undefined"));
layer.setProperties(lineBlur(expression));
assertEquals(layer.getLineBlur().getExpression(), expression);
}
@Test
@UiThreadTest
public void testLineDasharrayTransition() {
Timber.i("line-dasharrayTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLineDasharrayTransition(options);
assertEquals(layer.getLineDasharrayTransition(), options);
}
@Test
@UiThreadTest
public void testLineDasharrayAsConstant() {
Timber.i("line-dasharray");
assertNotNull(layer);
assertNull(layer.getLineDasharray().getValue());
// Set and Get
Float[] propertyValue = new Float[] {};
layer.setProperties(lineDasharray(propertyValue));
assertEquals(layer.getLineDasharray().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLinePatternTransition() {
Timber.i("line-patternTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setLinePatternTransition(options);
assertEquals(layer.getLinePatternTransition(), options);
}
@Test
@UiThreadTest
public void testLinePatternAsConstant() {
Timber.i("line-pattern");
assertNotNull(layer);
assertNull(layer.getLinePattern().getValue());
// Set and Get
String propertyValue = "pedestrian-polygon";
layer.setProperties(linePattern(propertyValue));
assertEquals(layer.getLinePattern().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testLinePatternAsExpression() {
Timber.i("line-pattern-expression");
assertNotNull(layer);
assertNull(layer.getLinePattern().getExpression());
// Set and Get
Expression expression = image(string(Expression.get("undefined")));
layer.setProperties(linePattern(expression));
assertEquals(layer.getLinePattern().getExpression(), expression);
}
}

View File

@@ -0,0 +1,262 @@
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.RasterLayer;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
import static org.maplibre.android.style.layers.PropertyFactory.*;
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for RasterLayer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class RasterLayerTest extends BaseLayerTest {
private RasterLayer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
layer = new RasterLayer("my-layer", "composite");
layer.setSourceLayer("composite");
setupLayer(layer);
}
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
@Test
@UiThreadTest
public void testRasterOpacityTransition() {
Timber.i("raster-opacityTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setRasterOpacityTransition(options);
assertEquals(layer.getRasterOpacityTransition(), options);
}
@Test
@UiThreadTest
public void testRasterOpacityAsConstant() {
Timber.i("raster-opacity");
assertNotNull(layer);
assertNull(layer.getRasterOpacity().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(rasterOpacity(propertyValue));
assertEquals(layer.getRasterOpacity().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testRasterHueRotateTransition() {
Timber.i("raster-hue-rotateTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setRasterHueRotateTransition(options);
assertEquals(layer.getRasterHueRotateTransition(), options);
}
@Test
@UiThreadTest
public void testRasterHueRotateAsConstant() {
Timber.i("raster-hue-rotate");
assertNotNull(layer);
assertNull(layer.getRasterHueRotate().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(rasterHueRotate(propertyValue));
assertEquals(layer.getRasterHueRotate().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testRasterBrightnessMinTransition() {
Timber.i("raster-brightness-minTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setRasterBrightnessMinTransition(options);
assertEquals(layer.getRasterBrightnessMinTransition(), options);
}
@Test
@UiThreadTest
public void testRasterBrightnessMinAsConstant() {
Timber.i("raster-brightness-min");
assertNotNull(layer);
assertNull(layer.getRasterBrightnessMin().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(rasterBrightnessMin(propertyValue));
assertEquals(layer.getRasterBrightnessMin().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testRasterBrightnessMaxTransition() {
Timber.i("raster-brightness-maxTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setRasterBrightnessMaxTransition(options);
assertEquals(layer.getRasterBrightnessMaxTransition(), options);
}
@Test
@UiThreadTest
public void testRasterBrightnessMaxAsConstant() {
Timber.i("raster-brightness-max");
assertNotNull(layer);
assertNull(layer.getRasterBrightnessMax().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(rasterBrightnessMax(propertyValue));
assertEquals(layer.getRasterBrightnessMax().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testRasterSaturationTransition() {
Timber.i("raster-saturationTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setRasterSaturationTransition(options);
assertEquals(layer.getRasterSaturationTransition(), options);
}
@Test
@UiThreadTest
public void testRasterSaturationAsConstant() {
Timber.i("raster-saturation");
assertNotNull(layer);
assertNull(layer.getRasterSaturation().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(rasterSaturation(propertyValue));
assertEquals(layer.getRasterSaturation().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testRasterContrastTransition() {
Timber.i("raster-contrastTransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.setRasterContrastTransition(options);
assertEquals(layer.getRasterContrastTransition(), options);
}
@Test
@UiThreadTest
public void testRasterContrastAsConstant() {
Timber.i("raster-contrast");
assertNotNull(layer);
assertNull(layer.getRasterContrast().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(rasterContrast(propertyValue));
assertEquals(layer.getRasterContrast().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testRasterResamplingAsConstant() {
Timber.i("raster-resampling");
assertNotNull(layer);
assertNull(layer.getRasterResampling().getValue());
// Set and Get
String propertyValue = RASTER_RESAMPLING_LINEAR;
layer.setProperties(rasterResampling(propertyValue));
assertEquals(layer.getRasterResampling().getValue(), propertyValue);
}
@Test
@UiThreadTest
public void testRasterFadeDurationAsConstant() {
Timber.i("raster-fade-duration");
assertNotNull(layer);
assertNull(layer.getRasterFadeDuration().getValue());
// Set and Get
Float propertyValue = 0.3f;
layer.setProperties(rasterFadeDuration(propertyValue));
assertEquals(layer.getRasterFadeDuration().getValue(), propertyValue);
}
}

View File

@@ -0,0 +1,375 @@
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import android.graphics.PointF;
import android.view.View;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import org.maplibre.android.style.layers.CannotAddLayerException;
import org.maplibre.android.style.layers.CircleLayer;
import org.maplibre.android.style.layers.FillLayer;
import org.maplibre.android.style.layers.Layer;
import org.maplibre.android.style.layers.LineLayer;
import org.maplibre.android.style.layers.Property;
import org.maplibre.android.style.layers.PropertyFactory;
import org.maplibre.android.style.sources.CannotAddSourceException;
import org.maplibre.android.style.sources.GeoJsonSource;
import org.maplibre.android.style.sources.RasterSource;
import org.maplibre.android.style.sources.Source;
import org.maplibre.android.style.sources.VectorSource;
import org.maplibre.android.testapp.R;
import org.maplibre.android.testapp.activity.EspressoTest;
import org.hamcrest.Matcher;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
import timber.log.Timber;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
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;
/**
* Basic smoke tests for Layer and Source
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class RuntimeStyleTests extends EspressoTest {
@Test
public void testListLayers() {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
List<Layer> layers = maplibreMap.getStyle().getLayers();
assertNotNull(layers);
assertTrue(layers.size() > 0);
for (Layer layer : layers) {
assertNotNull(layer);
}
}
});
}
@Test
public void testGetAddRemoveLayer() {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new AddRemoveLayerAction());
}
@Test
public void testAddLayerAbove() {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
List<Layer> layers = maplibreMap.getStyle().getLayers();
Source source = maplibreMap.getStyle().getSources().get(0);
// Test inserting with invalid above-id
try {
maplibreMap.getStyle().addLayerAbove(
new CircleLayer("invalid-id-layer-test", source.getId()), "no-such-layer-here-man"
);
fail("Should have thrown exception");
} catch (CannotAddLayerException ex) {
// Yeah
assertNotNull(ex.getMessage());
}
// Insert as last
CircleLayer last = new CircleLayer("this is the last one", source.getId());
maplibreMap.getStyle().addLayerAbove(last, layers.get(layers.size() - 1).getId());
layers = maplibreMap.getStyle().getLayers();
assertEquals(last.getId(), layers.get(layers.size() - 1).getId());
// Insert
CircleLayer second = new CircleLayer("this is the second one", source.getId());
maplibreMap.getStyle().addLayerAbove(second, layers.get(0).getId());
layers = maplibreMap.getStyle().getLayers();
assertEquals(second.getId(), layers.get(1).getId());
}
});
}
@Test
public void testRemoveLayerAt() {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
// Remove by index
Layer firstLayer = maplibreMap.getStyle().getLayers().get(0);
boolean removed = maplibreMap.getStyle().removeLayerAt(0);
assertTrue(removed);
assertNotNull(firstLayer);
// Test remove by index bounds checks
Timber.i("Remove layer at index > size");
assertFalse(maplibreMap.getStyle().removeLayerAt(Integer.MAX_VALUE));
}
});
}
@Test
public void testAddLayerAt() {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
List<Layer> layers = maplibreMap.getStyle().getLayers();
Source source = maplibreMap.getStyle().getSources().get(0);
// Test inserting out of range
try {
maplibreMap.getStyle().addLayerAt(new CircleLayer("invalid-id-layer-test", source.getId()), layers.size());
fail("Should have thrown exception");
} catch (CannotAddLayerException ex) {
// Yeah
assertNotNull(ex.getMessage());
}
// Insert at current last position
CircleLayer last = new CircleLayer("this is the last one", source.getId());
maplibreMap.getStyle().addLayerAt(last, layers.size() - 1);
layers = maplibreMap.getStyle().getLayers();
assertEquals(last.getId(), layers.get(layers.size() - 2).getId());
// Insert at start
CircleLayer second = new CircleLayer("this is the first one", source.getId());
maplibreMap.getStyle().addLayerAt(second, 0);
layers = maplibreMap.getStyle().getLayers();
assertEquals(second.getId(), layers.get(0).getId());
}
});
}
@Test
public void testListSources() {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
List<Source> sources = maplibreMap.getStyle().getSources();
assertNotNull(sources);
assertTrue(sources.size() > 0);
for (Source source : sources) {
assertNotNull(source);
}
}
});
}
@Test
public void testAddRemoveSource() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
mapboxMap.getStyle().addSource(new VectorSource("my-source", "maptiler://sources/hillshades"));
mapboxMap.getStyle().removeSource("my-source");
// Add initial source
mapboxMap.getStyle().addSource(new VectorSource("my-source", "maptiler://sources/hillshades"));
// Remove
boolean removeOk = mapboxMap.getStyle().removeSource("my-source");
assertTrue(removeOk);
assertNull(mapboxMap.getStyle().getLayer("my-source"));
// Add
Source source = new VectorSource("my-source", "maptiler://sources/hillshades");
mapboxMap.getStyle().addSource(source);
// Remove, preserving the reference
mapboxMap.getStyle().removeSource(source);
// Re-add the reference...
mapboxMap.getStyle().addSource(source);
// Ensure it's there
Assert.assertNotNull(mapboxMap.getStyle().getSource(source.getId()));
// Test adding a duplicate source
try {
Source source2 = new VectorSource("my-source", "maptiler://sources/hillshades");
mapboxMap.getStyle().addSource(source2);
fail("Should not have been allowed to add a source with a duplicate id");
} catch (CannotAddSourceException cannotAddSourceException) {
// OK
}
});
}
@Test
public void testVectorSourceUrlGetter() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
VectorSource source = new VectorSource("my-source", "maptiler://sources/hillshades");
mapboxMap.getStyle().addSource(source);
assertEquals("maptiler://sources/hillshades", source.getUri());
});
}
@Test
public void testRasterSourceUrlGetter() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
RasterSource source = new RasterSource("my-source", "maptiler://sources/hillshades");
mapboxMap.getStyle().addSource(source);
assertEquals("maptiler://sources/hillshades", source.getUri());
});
}
@Test
public void testGeoJsonSourceUrlGetter() {
validateTestSetup();
invoke(maplibreMap, (uiController, mapboxMap) -> {
GeoJsonSource source = new GeoJsonSource("my-source");
mapboxMap.getStyle().addSource(source);
assertNull(source.getUri());
source.setUri("http://mapbox.com/my-file.json");
assertEquals("http://mapbox.com/my-file.json", source.getUri());
});
}
@Test
public void testRemoveSourceInUse() {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
maplibreMap.getStyle().addSource(new VectorSource("my-source", "maptiler://sources/hillshades"));
maplibreMap.getStyle().addLayer(new LineLayer("my-layer", "my-source"));
maplibreMap.getStyle().removeSource("my-source");
assertNotNull(maplibreMap.getStyle().getSource("my-source"));
}
});
}
@Test
public void testRemoveNonExistingSource() {
invoke(maplibreMap, (uiController, mapboxMap) -> mapboxMap.getStyle().removeSource("source"));
}
@Test
public void testRemoveNonExistingLayer() {
invoke(maplibreMap, (uiController, mapboxMap) -> {
assertFalse(mapboxMap.getStyle().removeLayer("layer"));
assertFalse(mapboxMap.getStyle().removeLayerAt(mapboxMap.getStyle().getLayers().size() + 1));
assertFalse(mapboxMap.getStyle().removeLayerAt(-1));
});
}
@Test
public void testRemoveExistingLayer() {
invoke(maplibreMap, (uiController, mapboxMap) -> {
Layer firstLayer = mapboxMap.getStyle().getLayers().get(0);
assertTrue(mapboxMap.getStyle().removeLayer(firstLayer));
firstLayer = mapboxMap.getStyle().getLayers().get(0);
assertTrue(mapboxMap.getStyle().removeLayer(firstLayer.getId()));
assertTrue(mapboxMap.getStyle().removeLayerAt(0));
});
}
/**
* https://github.com/mapbox/mapbox-gl-native/issues/7973
*/
@Test
public void testQueryRenderedFeaturesInputHandling() {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
String[] layerIds = new String[600];
for (int i = 0; i < layerIds.length; i++) {
layerIds[i] = "layer-" + i;
}
maplibreMap.queryRenderedFeatures(new PointF(100, 100), layerIds);
}
});
}
private class AddRemoveLayerAction extends BaseViewAction {
@Override
public void perform(UiController uiController, View view) {
// Get initial
assertNotNull(maplibreMap.getStyle().getLayer("building"));
// Remove
boolean removed = maplibreMap.getStyle().removeLayer("building");
assertTrue(removed);
assertNull(maplibreMap.getStyle().getLayer("building"));
// Add
FillLayer layer = new FillLayer("building", "composite");
layer.setSourceLayer("building");
maplibreMap.getStyle().addLayer(layer);
assertNotNull(maplibreMap.getStyle().getLayer("building"));
// Assure the reference still works
layer.setProperties(PropertyFactory.visibility(Property.VISIBLE));
// Remove, preserving the reference
maplibreMap.getStyle().removeLayer(layer);
// Property setters should still work
layer.setProperties(PropertyFactory.fillColor(Color.RED));
// Re-add the reference...
maplibreMap.getStyle().addLayer(layer);
// Ensure it's there
Assert.assertNotNull(maplibreMap.getStyle().getLayer(layer.getId()));
// Test adding a duplicate layer
try {
maplibreMap.getStyle().addLayer(new FillLayer("building", "composite"));
fail("Should not have been allowed to add a layer with a duplicate id");
} catch (CannotAddLayerException cannotAddLayerException) {
// OK
}
}
}
public abstract class BaseViewAction implements ViewAction {
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
}
}

View File

@@ -0,0 +1,27 @@
package org.maplibre.android.testapp.style;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import org.maplibre.android.testapp.activity.BaseTest;
import org.maplibre.android.testapp.activity.style.RuntimeStyleTimingTestActivity;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Basic smoke tests for adding Layer and Source as early as possible (in onCreate)
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class RuntimeStyleTimingTests extends BaseTest {
@Override
protected Class getActivityClass() {
return RuntimeStyleTimingTestActivity.class;
}
@Test
public void testGetAddRemoveLayer() {
validateTestSetup();
// We're good if it didn't crash
}
}

View File

@@ -0,0 +1,74 @@
package org.maplibre.android.testapp.style
import android.view.View
import androidx.test.espresso.UiController
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.action.MapLibreMapAction
import org.maplibre.android.testapp.activity.EspressoTest
import org.maplibre.android.testapp.utils.ResourceUtils.readRawResource
import org.junit.Assert
import org.junit.Test
import java.io.IOException
/**
* Tests around style loading
*/
class StyleLoaderTest : EspressoTest() {
@Test
fun testSetGetStyleJsonString() {
validateTestSetup()
MapLibreMapAction.invoke(
maplibreMap
) { uiController: UiController?, maplibreMap: MapLibreMap ->
try {
val expected =
readRawResource(
rule.activity,
R.raw.local_style
)
maplibreMap.setStyle(Style.Builder().fromJson(expected))
val actual = maplibreMap.style!!.json
Assert.assertEquals("Style json should match", expected, actual)
} catch (exception: IOException) {
exception.printStackTrace()
}
}
}
@Test
fun testDefaultStyleLoadWithActivityLifecycleChange() {
validateTestSetup()
MapLibreMapAction.invoke(
maplibreMap
) { uiController: UiController?, maplibreMap: MapLibreMap ->
try {
val expected =
readRawResource(
rule.activity,
R.raw.local_style
)
maplibreMap.setStyle(Style.Builder().fromJson(expected))
// fake activity stop/start
val mapView =
rule.activity.findViewById<View>(R.id.mapView) as MapView
mapView.onPause()
mapView.onStop()
mapView.onStart()
mapView.onResume()
val actual = maplibreMap.style!!.json
Assert.assertEquals(
"Style URL should be empty",
"",
maplibreMap.style!!.uri
)
Assert.assertEquals("Style json should match", expected, actual)
} catch (exception: IOException) {
exception.printStackTrace()
}
}
}
}

View File

@@ -0,0 +1,285 @@
<%
const type = locals.type;
const properties = locals.properties;
-%>
// This file is generated. Edit scripts/generate-style-code.js, then run `make style-code`.
<% if (type === 'location-indicator') { -%>
package org.maplibre.android.location;
<% } else { -%>
package org.maplibre.android.testapp.style;
<% } -%>
import android.graphics.Color;
import androidx.test.annotation.UiThreadTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import com.mapbox.geojson.LineString;
import com.mapbox.geojson.MultiLineString;
import com.mapbox.geojson.MultiPoint;
import com.mapbox.geojson.MultiPolygon;
import com.mapbox.geojson.Point;
import com.mapbox.geojson.Polygon;
import org.maplibre.android.maps.BaseLayerTest;
import org.junit.Before;
import timber.log.Timber;
import org.maplibre.android.style.expressions.Expression;
<% if (type !== 'location-indicator') { -%>
import org.maplibre.android.style.layers.<%- camelize(type) %>Layer;
<% } -%>
<% if (type === 'symbol') { -%>
import org.maplibre.android.style.types.Formatted;
import org.maplibre.android.style.types.FormattedSection;
<% } -%>
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.maplibre.android.style.expressions.Expression.*;
import static org.junit.Assert.*;
import static org.maplibre.android.style.layers.Property.*;
<% if (type === 'location-indicator') { -%>
import static org.maplibre.android.location.LocationPropertyFactory.*;
<% } else { -%>
import static org.maplibre.android.style.layers.PropertyFactory.*;
<% } -%>
import org.maplibre.android.style.layers.TransitionOptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Basic smoke tests for <%- camelize(type) %>Layer
*/
@RunWith(AndroidJUnit4ClassRunner.class)
public class <%- camelize(type) %>LayerTest extends BaseLayerTest {
private <%- camelize(type) %>Layer layer;
private final List<Point> pointsList = new ArrayList<Point>() {
{
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
add(Point.fromLngLat(55.29743486255916, 25.25827212207261));
add(Point.fromLngLat(55.28978863411328, 25.251356725509737));
add(Point.fromLngLat(55.300027931336984, 25.246425506635504));
add(Point.fromLngLat(55.307474692951274, 25.244200378933655));
add(Point.fromLngLat(55.31212891895635, 25.256408010450187));
add(Point.fromLngLat(55.30774064871093, 25.26266169122738));
add(Point.fromLngLat(55.301357710197806, 25.264946609615492));
add(Point.fromLngLat(55.30122473231012, 25.26476622289597));
}
};
@Before
@UiThreadTest
public void beforeTest(){
super.before();
<% if (type === 'background' || type === 'location-indicator') { -%>
layer = new <%- camelize(type) %>Layer("my-layer");
<% } else { -%>
layer = new <%- camelize(type) %>Layer("my-layer", "composite");
layer.setSourceLayer("composite");
<% } -%>
setupLayer(layer);
}
<% if (type !== 'background' && type !== 'location-indicator') { -%>
@Test
@UiThreadTest
public void testSourceId() {
Timber.i("SourceId");
assertNotNull(layer);
assertEquals(layer.getSourceId(), "composite");
}
<% } -%>
@Test
@UiThreadTest
public void testSetVisibility() {
Timber.i("Visibility");
assertNotNull(layer);
// Get initial
assertEquals(layer.getVisibility().getValue(), VISIBLE);
// Set
layer.setProperties(visibility(NONE));
assertEquals(layer.getVisibility().getValue(), NONE);
}
<% if (!(type === 'background' || type === 'location-indicator' || type === 'raster' || type === 'hillshade')) { -%>
@Test
@UiThreadTest
public void testSourceLayer() {
Timber.i("SourceLayer");
assertNotNull(layer);
// Get initial
assertEquals(layer.getSourceLayer(), "composite");
// Set
final String sourceLayer = "test";
layer.setSourceLayer(sourceLayer);
assertEquals(layer.getSourceLayer(), sourceLayer);
}
@Test
@UiThreadTest
public void testFilter() {
Timber.i("Filter");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// Set
Expression filter = eq(get("undefined"), literal(1.0));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// Set constant
filter = literal(true);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterDistance() {
Timber.i("FilterDistance");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
// distance with Point
Expression filter = lt(distance(Point.fromLngLat(1.0, 1.0)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with LineString
filter = lt(distance(LineString.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiPoint.fromLngLats(pointsList)), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPoint
filter = lt(distance(MultiLineString.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with Polygon
filter = lt(distance(Polygon.fromLngLats(Collections.singletonList(pointsList))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
// distance with MultiPolygon
filter = lt(distance(MultiPolygon.fromLngLats(Collections
.singletonList(Collections.singletonList(pointsList)))), 50);
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
@Test
@UiThreadTest
public void testFilterWithin() {
Timber.i("FilterWithin");
assertNotNull(layer);
// Get initial
assertEquals(layer.getFilter(), null);
Expression filter = within(Polygon.fromLngLats(Collections.singletonList(pointsList)));
layer.setFilter(filter);
assertEquals(layer.getFilter().toString(), filter.toString());
}
<% } -%>
<% for (const property of properties) { -%>
<% if (property['property-type'] !== 'color-ramp') { -%>
<% if (property.transition) { -%>
@Test
@UiThreadTest
public void test<%- camelize(property.name) %>Transition() {
Timber.i("<%- property.name %>TransitionOptions");
assertNotNull(layer);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
layer.set<%- camelize(property.name) %>Transition(options);
assertEquals(layer.get<%- camelize(property.name) %>Transition(), options);
}
<% } -%>
@Test
@UiThreadTest
public void test<%- camelize(property.name) %>AsConstant() {
Timber.i("<%- property.name %>");
assertNotNull(layer);
assertNull(layer.get<%- camelize(property.name) %>().getValue());
// Set and Get
<%- propertyType(property) %> propertyValue = <%- defaultValueJava(property) %>;
<% if (property.type === 'formatted') { -%>
layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>("default"));
assertEquals(layer.get<%- camelize(property.name) %>().getValue(), propertyValue);
<% } -%>
layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(propertyValue));
assertEquals(layer.get<%- camelize(property.name) %>().getValue(), propertyValue);
<% if (property.tokens) { -%>
layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>("{token}"));
<% if (property.type === 'formatted') { -%>
assertEquals(layer.get<%- camelize(property.name) %>().getExpression(), <%- defaultExpressionJava(property) %>(Expression.formatEntry(Expression.toString(Expression.get("token")))));
<% } else if (property.type === 'resolvedImage') {-%>
assertEquals(layer.get<%- camelize(property.name) %>().getExpression(), <%- defaultExpressionJava(property) %>(Expression.toString(Expression.get("token"))));
<% } else {-%>
assertEquals(layer.get<%- camelize(property.name) %>().getExpression(), Expression.toString(Expression.get("token")));
<% } -%>
<% } -%>
}
<% if (property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven') { -%>
<% if (!(property.name.endsWith("-font")||property.name.endsWith("-offset"))) { -%>
@Test
@UiThreadTest
public void test<%- camelize(property.name) %>AsExpression() {
Timber.i("<%- property.name %>-expression");
assertNotNull(layer);
assertNull(layer.get<%- camelize(property.name) %>().getExpression());
// Set and Get
<% if (property.type === 'formatted') { -%>
Expression expression = <%- defaultExpressionJava(property) %>(Expression.formatEntry(Expression.get("undefined"), FormatOption.formatFontScale(2.0), FormatOption.formatTextFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"})));
<% } else if (property.type === 'resolvedImage') { -%>
Expression expression = <%- defaultExpressionJava(property) %>(string(Expression.get("undefined")));
<% } else { -%>
Expression expression = <%- defaultExpressionJava(property) %>(Expression.get("undefined"));
<% } -%>
layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(expression));
assertEquals(layer.get<%- camelize(property.name) %>().getExpression(), expression);
}
<% } -%>
<% } -%>
<% if (property.type == 'color') { -%>
@Test
@UiThreadTest
public void test<%- camelize(property.name) %>AsIntConstant() {
Timber.i("<%- property.name %>");
assertNotNull(layer);
// Set and Get
layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(Color.argb(127, 255, 127, 0)));
assertEquals(layer.get<%- camelize(property.name) %>AsInt(), Color.argb(127, 255, 127, 0));
}
<% } -%>
<% } -%>
<% } -%>
}

View File

@@ -0,0 +1,132 @@
<%
const properties = locals.properties;
-%>
package org.maplibre.android.testapp.style;
import android.graphics.Color;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import android.view.View;
import org.maplibre.android.style.light.Light;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.FillExtrusionLayer;
import org.maplibre.android.style.layers.TransitionOptions;
import org.maplibre.android.style.light.Position;
import org.maplibre.android.testapp.R;
import org.maplibre.android.testapp.activity.BaseTest;
import org.maplibre.android.testapp.activity.style.FillExtrusionStyleTestActivity;
import timber.log.Timber;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.maplibre.android.style.expressions.Expression.eq;
import static org.maplibre.android.style.layers.Property.ANCHOR_MAP;
import static org.maplibre.android.style.layers.PropertyFactory.fillExtrusionBase;
import static org.maplibre.android.style.layers.PropertyFactory.fillExtrusionColor;
import static org.maplibre.android.style.layers.PropertyFactory.fillExtrusionHeight;
import static org.maplibre.android.style.layers.PropertyFactory.fillExtrusionOpacity;
import static org.maplibre.android.testapp.action.MapLibreMapAction.invoke;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(AndroidJUnit4ClassRunner.class)
public class LightTest extends BaseTest {
private Light light;
<% for (const property of properties) { -%>
<% if (property.transition) { -%>
@Test
public void test<%- camelize(property.name) %>Transition() {
validateTestSetup();
setupLight();
Timber.i("<%- property.name %>TransitionOptions");
invoke(maplibreMap, (uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
TransitionOptions options = new TransitionOptions(300, 100);
light.set<%- camelize(property.name) %>Transition(options);
assertEquals("Transition options should match", options, light.get<%- camelize(property.name) %>Transition());
});
}
<% } -%>
<% if (property.name == "position") { -%>
@Test
public void test<%- camelize(property.name) %>() {
validateTestSetup();
setupLight();
Timber.i("<%- property.name %>");
invoke(maplibreMap,(uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
Position position = new Position(1, 2, 3);
light.set<%- camelize(property.name) %>(position);
assertEquals("Position should match", position, light.get<%- camelize(property.name) %>());
});
}
<% } else { -%>
@Test
public void test<%- camelize(property.name) %>() {
validateTestSetup();
setupLight();
Timber.i("<%- property.name %>");
invoke(maplibreMap, (uiController, maplibreMap) -> {
assertNotNull(light);
// Set and Get
light.set<%- camelize(property.name) %>(<%- defaultValueJava(property) %>);
<% if (property.name == 'color') { -%>
assertEquals("<%- camelize(property.name) %> should match", <%- defaultValueJava(property) %>, light.get<%- camelize(property.name) %>());
<% } else { -%>
assertEquals("<%- camelize(property.name) %> should match", <%- defaultValueJava(property) %>, light.get<%- camelize(property.name) %>()<%- property.type === 'number' ? ', 0f' : '' %>);
<% } -%>
});
}
<% } -%>
<% } -%>
private void setupLight() {
onView(withId(R.id.mapView)).perform(new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isDisplayed();
}
@Override
public String getDescription() {
return getClass().getSimpleName();
}
@Override
public void perform(UiController uiController, View view) {
light = maplibreMap.getStyle().getLight();
FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite");
fillExtrusionLayer.setSourceLayer("building");
fillExtrusionLayer.setFilter(eq(Expression.get("extrude"), "true"));
fillExtrusionLayer.setMinZoom(15);
fillExtrusionLayer.setProperties(
fillExtrusionColor(Color.LTGRAY),
fillExtrusionHeight(Expression.get("height")),
fillExtrusionBase(Expression.get("min_height")),
fillExtrusionOpacity(0.6f)
);
maplibreMap.getStyle().addLayer(fillExtrusionLayer);
}
});
}
@Override
protected Class getActivityClass() {
return FillExtrusionStyleTestActivity.class;
}
}

View File

@@ -0,0 +1,38 @@
package org.maplibre.android.testapp.utils;
import androidx.test.espresso.IdlingResource;
import org.maplibre.android.testapp.activity.render.RenderTestActivity;
public class SnapshotterIdlingResource implements IdlingResource, RenderTestActivity.OnRenderTestCompletionListener {
private IdlingResource.ResourceCallback resourceCallback;
private boolean isSnapshotReady;
public SnapshotterIdlingResource(RenderTestActivity activity) {
activity.setOnRenderTestCompletionListener(this);
}
@Override
public String getName() {
return "SnapshotterIdlingResource";
}
@Override
public boolean isIdleNow() {
return isSnapshotReady;
}
@Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.resourceCallback = resourceCallback;
}
@Override
public void onFinish() {
isSnapshotReady = true;
if (resourceCallback != null) {
resourceCallback.onTransitionToIdle();
}
}
}

View File

@@ -0,0 +1,12 @@
package org.maplibre.android.testapp.utils;
public class TestConstants {
public static final double LAT_LNG_DELTA = 0.1;
public static final double BEARING_DELTA = 0.1;
public static final double TILT_DELTA = 0.3;
public static final double ZOOM_DELTA = 0.3;
public static final double PADDING_DELTA = 0.0001;
public static final String TEXT_MARKER_TITLE = "Marker";
public static final String TEXT_MARKER_SNIPPET = "Snippet";
}

View File

@@ -0,0 +1,18 @@
package org.maplibre.android.testapp.utils
import androidx.test.espresso.UiController
import org.maplibre.android.maps.MapView
object TestingAsyncUtils {
private const val DEFAULT_TIMEOUT = 15_000L
fun waitForLayer(uiController: UiController, mapView: MapView) {
val start = System.nanoTime() / 1E6
var isIdle = false
mapView.addOnDidBecomeIdleListener { isIdle = true }
while (!isIdle && System.nanoTime() / 1E6 - start < DEFAULT_TIMEOUT) {
uiController.loopMainThreadForAtLeast(100)
}
}
}

View File

@@ -0,0 +1,31 @@
package org.maplibre.android.utils
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.maplibre.android.constants.MapLibreConstants
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
class FontUtilsTest {
@Test
fun testExtractedFontShouldMatchDefault() {
val fonts = arrayOf("foo", "bar")
val actual = FontUtils.extractValidFont(*fonts)
Assert.assertEquals("Selected font should match", MapLibreConstants.DEFAULT_FONT, actual)
}
@Test
fun testExtractedFontShouldMatchMonospace() {
val expected = "monospace"
val fonts = arrayOf("foo", expected)
val actual = FontUtils.extractValidFont(*fonts)
Assert.assertEquals("Selected font should match", expected, actual)
}
@Test
fun testExtractedFontShouldBeNull() {
val actual = FontUtils.extractValidFont()
Assert.assertNull(actual)
}
}