From 5ae1906a04c79aae7c161eb1576122ee62b6bcab Mon Sep 17 00:00:00 2001 From: Dale Hawkins <107309+dkhawk@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:36:49 -0600 Subject: [PATCH] test: integrate Android Maps Testing Toolkit v1.2.0-rc01 and verify all 10 fixes --- clustering/build.gradle.kts | 6 + .../testing/MapsToolkitClusteringTest.kt | 475 ++++++++++ .../src/test/resources/robolectric.properties | 18 +- data/build.gradle.kts | 5 + .../data/testing/MapsToolkitDataLayerTest.kt | 664 ++++++++++++++ .../src/test/resources/robolectric.properties | 18 +- gradle/libs.versions.toml | 4 + heatmaps/build.gradle.kts | 6 + .../testing/MapsToolkitHeatmapsTest.kt | 267 ++++++ .../src/test/resources/robolectric.properties | 18 +- library/build.gradle.kts | 6 + .../android/testing/MapsToolkitLibraryTest.kt | 850 ++++++++++++++++++ .../src/test/resources/robolectric.properties | 18 +- .../shadows/1.2.0-rc01/shadows-1.2.0-rc01.aar | Bin 0 -> 52137 bytes .../1.2.0-rc01/shadows-1.2.0-rc01.module | 116 +++ .../shadows/1.2.0-rc01/shadows-1.2.0-rc01.pom | 41 + .../golden/1.2.0-rc01/golden-1.2.0-rc01.aar | Bin 0 -> 35141 bytes .../1.2.0-rc01/golden-1.2.0-rc01.module | 165 ++++ .../golden/1.2.0-rc01/golden-1.2.0-rc01.pom | 75 ++ .../visual/1.2.0-rc01/visual-1.2.0-rc01.aar | Bin 0 -> 114399 bytes .../1.2.0-rc01/visual-1.2.0-rc01.module | 228 +++++ .../visual/1.2.0-rc01/visual-1.2.0-rc01.pom | 101 +++ settings.gradle.kts | 24 + ui/build.gradle.kts | 6 + .../android/ui/testing/MapsToolkitUiTest.kt | 358 ++++++++ ui/src/test/resources/robolectric.properties | 18 +- 26 files changed, 3482 insertions(+), 5 deletions(-) create mode 100644 clustering/src/test/java/com/google/maps/android/clustering/testing/MapsToolkitClusteringTest.kt create mode 100644 data/src/test/java/com/google/maps/android/data/testing/MapsToolkitDataLayerTest.kt create mode 100644 heatmaps/src/test/java/com/google/maps/android/heatmaps/testing/MapsToolkitHeatmapsTest.kt create mode 100644 library/src/test/java/com/google/maps/android/testing/MapsToolkitLibraryTest.kt create mode 100644 repo/com/google/android/maps/robolectric/shadows/1.2.0-rc01/shadows-1.2.0-rc01.aar create mode 100644 repo/com/google/android/maps/robolectric/shadows/1.2.0-rc01/shadows-1.2.0-rc01.module create mode 100644 repo/com/google/android/maps/robolectric/shadows/1.2.0-rc01/shadows-1.2.0-rc01.pom create mode 100644 repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.aar create mode 100644 repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.module create mode 100644 repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.pom create mode 100644 repo/com/google/android/maps/testing/visual/1.2.0-rc01/visual-1.2.0-rc01.aar create mode 100644 repo/com/google/android/maps/testing/visual/1.2.0-rc01/visual-1.2.0-rc01.module create mode 100644 repo/com/google/android/maps/testing/visual/1.2.0-rc01/visual-1.2.0-rc01.pom create mode 100644 ui/src/test/java/com/google/maps/android/ui/testing/MapsToolkitUiTest.kt diff --git a/clustering/build.gradle.kts b/clustering/build.gradle.kts index c0a30741b..6122dc71d 100644 --- a/clustering/build.gradle.kts +++ b/clustering/build.gradle.kts @@ -82,6 +82,12 @@ dependencies { implementation(libs.kotlin.stdlib.jdk8) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockito.kotlin) + testImplementation(libs.maps.robolectric.shadows) + testImplementation(libs.maps.testing.golden) + testImplementation(libs.maps.testing.visual) + testImplementation(platform(libs.compose.bom)) + testImplementation(libs.activity.compose) + testImplementation(libs.kotlinx.serialization.json) } tasks.register("instrumentTest") { diff --git a/clustering/src/test/java/com/google/maps/android/clustering/testing/MapsToolkitClusteringTest.kt b/clustering/src/test/java/com/google/maps/android/clustering/testing/MapsToolkitClusteringTest.kt new file mode 100644 index 000000000..899c54a37 --- /dev/null +++ b/clustering/src/test/java/com/google/maps/android/clustering/testing/MapsToolkitClusteringTest.kt @@ -0,0 +1,475 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.maps.android.clustering.testing + +import android.app.Activity +import android.os.Bundle +import android.view.View +import com.google.android.gms.maps.CameraUpdateFactory +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.MapView +import com.google.android.gms.maps.model.LatLng +import com.google.android.maps.robolectric.annotation.EnableMapsShadows +import com.google.android.maps.robolectric.shadows.ShadowGoogleMap +import com.google.android.maps.robolectric.shadows.ShadowMarker +import com.google.android.maps.robolectric.truth.LatLngSubject +import com.google.android.maps.robolectric.truth.isWithin +import com.google.android.maps.robolectric.truth.meters +import com.google.android.maps.testing.golden.GoldenImageDiff +import com.google.android.maps.testing.golden.GoldenTileStrategy +import com.google.android.maps.testing.golden.captureSnapshot +import com.google.android.maps.testing.golden.enableGoldenTesting +import com.google.common.truth.Truth.assertThat +import com.google.maps.android.clustering.Cluster +import com.google.maps.android.clustering.ClusterItem +import com.google.maps.android.clustering.ClusterManager +import com.google.maps.android.clustering.algo.CentroidNonHierarchicalDistanceBasedAlgorithm +import com.google.maps.android.clustering.algo.GridBasedAlgorithm +import com.google.maps.android.clustering.algo.NonHierarchicalDistanceBasedAlgorithm +import com.google.maps.android.clustering.algo.NonHierarchicalViewBasedAlgorithm +import com.google.maps.android.clustering.algo.PreCachingAlgorithmDecorator +import com.google.maps.android.clustering.clusterClickEvents +import com.google.maps.android.clustering.clusterInfoWindowClickEvents +import com.google.maps.android.clustering.clusterInfoWindowLongClickEvents +import com.google.maps.android.clustering.clusterItemClickEvents +import com.google.maps.android.clustering.clusterItemInfoWindowClickEvents +import com.google.maps.android.clustering.clusterItemInfoWindowLongClickEvents +import com.google.maps.android.clustering.view.DefaultClusterRenderer +import java.util.concurrent.atomic.AtomicReference +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadow.api.Shadow +import org.robolectric.shadows.ShadowLooper + +/** + * End-to-end JVM integration and adversarial verification suite for the `:clustering` module + * powered by the Android Maps Testing Toolkit (`v1.2.0-rc01`). + * + * ## Architectural Scope + * Prior to this suite, `:clustering` had **zero** unit tests exercising [DefaultClusterRenderer] + * against a stateful [GoogleMap] or [MapView]. Here we combine: + * 1. **Stateful Map & Projection Shadows**: [ShadowGoogleMap], [ShadowMarker], and a measured + * `400x400` [MapView] with accurate Web Mercator [com.google.android.maps.robolectric.shadows.ShadowProjection]. + * 2. **Algorithm Matrix Verification**: Exercises [NonHierarchicalDistanceBasedAlgorithm], + * [CentroidNonHierarchicalDistanceBasedAlgorithm], [NonHierarchicalViewBasedAlgorithm], + * [GridBasedAlgorithm], and [PreCachingAlgorithmDecorator] across zoom transitions and + * [ClusterManager.diff] mutations. + * 3. **Cluster & Item Reactive Flows + Golden Snapshot Verification**: Verifies that + * [DefaultClusterRenderer] generates real cluster bucket bitmaps via [com.google.maps.android.ui.IconGenerator], + * attaches them to `clusterMarkerCollection` or `markerCollection` depending on `minClusterSize` + * and zoom level, routes all 6 KTX `ClusterManagerFlows` (`clusterClickEvents`, + * `clusterItemClickEvents`, `clusterInfoWindowClickEvents`, `clusterInfoWindowLongClickEvents`, + * `clusterItemInfoWindowClickEvents`, `clusterItemInfoWindowLongClickEvents`), and produces + * verifiable pixel changes via [GoldenImageDiff]. + * 4. **Adversarial Defect Proof (Bug #8)**: Exposes two state-synchronization defects in + * `DefaultClusterRenderer.onClusterItemUpdated`: + * - Stale `snippet` (and `title`) retention when a [ClusterItem]'s `snippet` or `title` is + * updated to `null`. + * - Ignored `zIndex` updates when a [ClusterItem]'s `zIndex` changes while its `position` + * remains unchanged. + */ +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(sdk = [34]) +@EnableMapsShadows +class MapsToolkitClusteringTest { + + /** + * Mutable [ClusterItem] with stable `id`-based identity so [DefaultClusterRenderer]'s + * internal `MarkerCache` recognizes updated items across re-clustering passes. + */ + class MutableTestItem( + val id: String, + override var position: LatLng, + override var title: String?, + override var snippet: String?, + override var zIndex: Float? = 0f + ) : ClusterItem { + fun update( + newPos: LatLng = this.position, + newTitle: String? = this.title, + newSnippet: String? = this.snippet, + newZIndex: Float? = this.zIndex + ) { + this.position = newPos + this.title = newTitle + this.snippet = newSnippet + this.zIndex = newZIndex + } + + override fun equals(other: Any?): Boolean = + other is MutableTestItem && other.id == this.id + + override fun hashCode(): Int = id.hashCode() + } + + private lateinit var activity: Activity + private lateinit var mapView: MapView + private lateinit var googleMap: GoogleMap + private lateinit var shadowMap: ShadowGoogleMap + private lateinit var clusterManager: ClusterManager + private lateinit var renderer: DefaultClusterRenderer + + @Before + fun setUp() { + activity = Robolectric.buildActivity(Activity::class.java).create().get() + mapView = MapView(activity).apply { + onCreate(Bundle()) + onStart() + onResume() + measure( + View.MeasureSpec.makeMeasureSpec(400, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(400, View.MeasureSpec.EXACTLY) + ) + layout(0, 0, 400, 400) + } + + val mapRef = AtomicReference() + mapView.getMapAsync { mapRef.set(it) } + ShadowLooper.idleMainLooper() + + googleMap = checkNotNull(mapRef.get()) + shadowMap = Shadow.extract(googleMap) as ShadowGoogleMap + + clusterManager = ClusterManager(activity, googleMap) + renderer = DefaultClusterRenderer(activity, googleMap, clusterManager).apply { + setAnimation(false) + } + clusterManager.renderer = renderer + + // Wire ClusterManager as the GoogleMap camera-idle, marker-click, and info-window listener + // and drain the main looper so MarkerManager registers its underlying map listeners. + ShadowLooper.idleMainLooper() + googleMap.setOnCameraIdleListener(clusterManager) + googleMap.setOnMarkerClickListener(clusterManager) + googleMap.setOnInfoWindowClickListener(clusterManager) + googleMap.setOnInfoWindowLongClickListener(clusterManager.markerManager) + } + + /** + * Drives [DefaultClusterRenderer.onClustersChanged] and pumps the main looper so the background + * `RenderTask` thread and the main-thread `MarkerModifier` handler complete their lock/condition + * handshake deterministically. + */ + private fun renderClustersAndIdle(clusters: Set>) { + renderer.onClustersChanged(clusters) + repeat(30) { + ShadowLooper.idleMainLooper() + Thread.sleep(5) + } + ShadowLooper.idleMainLooper() + } + + // ============================================================================================ + // Section 1: Zoom-Dependent Clustering, Rendering, Flows & Golden Visual Diffs + // ============================================================================================ + + /** + * Verifies that 6 closely spaced items render as a single aggregated cluster marker with a + * generated [com.google.maps.android.ui.IconGenerator] bitmap at low zoom (`zoom = 10f`), and + * split into 6 individual markers when zoomed in (`zoom = 19f`), while firing all 6 KTX + * `ClusterManagerFlows` and producing distinct golden snapshots. + */ + @Test + fun clusterRendererAndFlows_transitionFromSingleClusterToIndividualMarkersAcrossZooms() = runTest { + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val center = LatLng(37.7749, -122.4194) + + // Enable synthetic golden tile background + googleMap.enableGoldenTesting( + strategy = GoldenTileStrategy.SolidColorHash(), + fadeIn = false + ) + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 10f)) + + val baselineJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val emptyMapSnapshot = baselineJob.await() + + // Add 6 closely spaced items around San Francisco (~20-50m apart) + val items = (1..6).map { index -> + MutableTestItem( + id = "item_$index", + position = LatLng(center.latitude + index * 0.0001, center.longitude + index * 0.0001), + title = "Place #$index", + snippet = "Snippet #$index", + zIndex = index.toFloat() + ) + } + clusterManager.addItems(items) + + // At zoom 10f, all 6 items fall into 1 cluster (>= minClusterSize = 4) + val lowZoomClusters = clusterManager.algorithm.getClusters(10.0f) + assertThat(lowZoomClusters).hasSize(1) + val singleCluster = lowZoomClusters.first() + assertThat(singleCluster.size).isEqualTo(6) + + renderClustersAndIdle(lowZoomClusters) + + assertThat(clusterManager.clusterMarkerCollection.getMarkers()).hasSize(1) + assertThat(clusterManager.markerCollection.getMarkers()).isEmpty() + + val clusterMarker = checkNotNull(renderer.getMarker(singleCluster)) + assertThat(renderer.getCluster(clusterMarker)).isEqualTo(singleCluster) + val shadowClusterMarker = Shadow.extract(clusterMarker) as ShadowMarker + assertThat(shadowClusterMarker.iconBitmap).isNotNull() + + // Capture snapshot with rendered cluster icon and verify GoldenImageDiff detects the icon + val clusterSnapJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val clusterSnapshot = clusterSnapJob.await() + val clusterDiff = GoldenImageDiff.compare( + expected = emptyMapSnapshot, + actual = clusterSnapshot, + maxDiffPercentage = 0.0 + ) + assertThat(clusterDiff.diffPixelCount).isGreaterThan(50) + + // Subscribe to all 6 ClusterManagerFlows + val clickedClusters = mutableListOf>() + val clickedClusterInfoWindows = mutableListOf>() + val longClickedClusterInfoWindows = mutableListOf>() + val clickedItems = mutableListOf() + val clickedItemInfoWindows = mutableListOf() + val longClickedItemInfoWindows = mutableListOf() + + val flowJobs = listOf( + launch(dispatcher) { clusterManager.clusterClickEvents().toList(clickedClusters) }, + launch(dispatcher) { clusterManager.clusterInfoWindowClickEvents().toList(clickedClusterInfoWindows) }, + launch(dispatcher) { clusterManager.clusterInfoWindowLongClickEvents().toList(longClickedClusterInfoWindows) }, + launch(dispatcher) { clusterManager.clusterItemClickEvents().toList(clickedItems) }, + launch(dispatcher) { clusterManager.clusterItemInfoWindowClickEvents().toList(clickedItemInfoWindows) }, + launch(dispatcher) { clusterManager.clusterItemInfoWindowLongClickEvents().toList(longClickedItemInfoWindows) } + ) + + // Dispatch click, infoWindowClick, and infoWindowLongClick on the cluster marker + shadowMap.clickMarker(clusterMarker) + shadowMap.clickInfoWindow(clusterMarker) + shadowMap.longClickInfoWindow(clusterMarker) + + assertThat(clickedClusters).containsExactly(singleCluster) + assertThat(clickedClusterInfoWindows).containsExactly(singleCluster) + assertThat(longClickedClusterInfoWindows).containsExactly(singleCluster) + + // Now zoom in to 20f where the 6 items separate into individual 1-item clusters (< minClusterSize) + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 20f)) + val highZoomClusters = clusterManager.algorithm.getClusters(20.0f) + assertThat(highZoomClusters).hasSize(6) + + renderClustersAndIdle(highZoomClusters) + + assertThat(clusterManager.clusterMarkerCollection.getMarkers()).isEmpty() + assertThat(clusterManager.markerCollection.getMarkers()).hasSize(6) + + val firstItem = items.first() + val firstItemMarker = checkNotNull(renderer.getMarker(firstItem)) + assertThat(renderer.getClusterItem(firstItemMarker)).isEqualTo(firstItem) + LatLngSubject.assertThat(firstItemMarker.position).isWithin(1.meters).of(firstItem.position) + + // Dispatch click, infoWindowClick, and infoWindowLongClick on the individual item marker + shadowMap.clickMarker(firstItemMarker) + shadowMap.clickInfoWindow(firstItemMarker) + shadowMap.longClickInfoWindow(firstItemMarker) + + assertThat(clickedItems).containsExactly(firstItem) + assertThat(clickedItemInfoWindows).containsExactly(firstItem) + assertThat(longClickedItemInfoWindows).containsExactly(firstItem) + + flowJobs.forEach { it.cancel() } + } + + // ============================================================================================ + // Section 2: Algorithm Family & ClusterManager.diff() Verification + // ============================================================================================ + + /** + * Exercises [CentroidNonHierarchicalDistanceBasedAlgorithm], [GridBasedAlgorithm], + * [NonHierarchicalViewBasedAlgorithm], and [PreCachingAlgorithmDecorator], plus + * [ClusterManager.diff] for atomic batch additions, removals, and modifications. + */ + @Test + fun clusteringAlgorithmsAndDiff_computeExpectedCentroidsAndViewportPartitions() { + val base = LatLng(40.7128, -74.0060) + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(base, 12f)) + + val item1 = MutableTestItem("1", base, "NYC 1", "S1") + val item2 = MutableTestItem("2", LatLng(40.7130, -74.0062), "NYC 2", "S2") + val item3 = MutableTestItem("3", LatLng(40.7132, -74.0064), "NYC 3", "S3") + val distantItem = MutableTestItem("4", LatLng(40.8500, -73.9000), "Bronx", "S4") + + // 1. CentroidNonHierarchicalDistanceBasedAlgorithm computes true geographic mean center + val centroidAlgo = CentroidNonHierarchicalDistanceBasedAlgorithm() + clusterManager.algorithm = centroidAlgo + clusterManager.addItems(listOf(item1, item2, item3, distantItem)) + + val centroidClusters = clusterManager.algorithm.getClusters(12f) + assertThat(centroidClusters).hasSize(2) + val nycCluster = centroidClusters.first { it.size == 3 } + val expectedLat = (item1.position.latitude + item2.position.latitude + item3.position.latitude) / 3.0 + val expectedLng = (item1.position.longitude + item2.position.longitude + item3.position.longitude) / 3.0 + LatLngSubject.assertThat(nycCluster.position) + .isWithin(1.meters) + .of(LatLng(expectedLat, expectedLng)) + + // 2. ClusterManager.diff() atomically adds, removes, and modifies items + val addedItem = MutableTestItem("5", LatLng(40.7129, -74.0061), "NYC 5", "S5") + item1.update(newTitle = "NYC 1 Updated") + clusterManager.diff( + add = listOf(addedItem), + remove = listOf(distantItem), + modify = listOf(item1) + ) + val afterDiffClusters = clusterManager.algorithm.getClusters(12f) + assertThat(afterDiffClusters).hasSize(1) + assertThat(afterDiffClusters.first().size).isEqualTo(4) + + // 3. GridBasedAlgorithm + val gridAlgo = GridBasedAlgorithm() + clusterManager.algorithm = gridAlgo + assertThat(clusterManager.algorithm.getClusters(12f)).hasSize(1) + + // 4. NonHierarchicalViewBasedAlgorithm uses MapView dimensions & CameraPosition + val viewBasedAlgo = NonHierarchicalViewBasedAlgorithm( + mapView.width, + mapView.height + ) + clusterManager.setAlgorithm(viewBasedAlgo) + viewBasedAlgo.onCameraChange(googleMap.cameraPosition) + assertThat(viewBasedAlgo.shouldReclusterOnMapMovement()).isTrue() + assertThat(clusterManager.algorithm.getClusters(12f)).isNotEmpty() + + // 5. PreCachingAlgorithmDecorator + val preCaching = PreCachingAlgorithmDecorator(NonHierarchicalDistanceBasedAlgorithm()) + clusterManager.algorithm = preCaching + assertThat(clusterManager.algorithm.getClusters(12f)).hasSize(1) + } + + // ============================================================================================ + // Section 3: Adversarial Defect Reproduction (Bug #8 in DefaultClusterRenderer) + // ============================================================================================ + + /** + * **BUG #8 REPRODUCTION (`DefaultClusterRenderer.onClusterItemUpdated` Stale Title/Snippet & + * Ignored `zIndex` Updates)**: + * + * Examination of `DefaultClusterRenderer.kt:833-866` reveals two state-synchronization bugs + * when a cached [ClusterItem] marker is updated on the map: + * + * 1. **Stale `snippet` (and `title`) when updated to `null`**: + * ```kotlin + * if (item.title != null && item.snippet != null) { + * ... + * } else if (item.snippet != null && item.snippet != marker.title) { + * marker.title = item.snippet + * changed = true + * } else if (item.title != null && item.title != marker.title) { + * marker.title = item.title + * changed = true + * } + * ``` + * When an item initially has both `title = "Initial Title"` and `snippet = "Initial Snippet"`, + * and is later updated to `title = "Updated Title", snippet = null`: + * - It enters `else if (item.title != null && item.title != marker.title)` which updates + * `marker.title`, **but never clears `marker.snippet`**, leaving `"Initial Snippet"` + * permanently stuck on the [com.google.android.gms.maps.model.Marker]! + * - Furthermore, if both `title` and `snippet` are updated to `null`, **neither** `marker.title` + * nor `marker.snippet` is cleared! + * + * 2. **Ignored `zIndex` update when `position` does not change**: + * ```kotlin + * if (marker.position != item.position) { + * marker.position = item.position + * if (item.zIndex != null) { + * marker.zIndex = item.zIndex!! + * } + * changed = true + * } + * ``` + * Because `marker.zIndex = item.zIndex!!` is nested inside `if (marker.position != item.position)`, + * changing an item's `zIndex` in-place (e.g., bringing a selected marker to the front by + * updating `zIndex` from `1f` to `100f` without moving its coordinates) is **completely ignored**! + */ + @Test + fun bug8_defaultClusterRenderer_onClusterItemUpdatedRetainsStaleSnippetAndIgnoresInPlaceZIndexChange() { + val pos = LatLng(37.7749, -122.4194) + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(pos, 18f)) + + val item = MutableTestItem( + id = "target_item", + position = pos, + title = "Initial Title", + snippet = "Initial Snippet", + zIndex = 1.0f + ) + clusterManager.addItem(item) + + // First render pass creates and caches the marker via onBeforeClusterItemRendered + renderClustersAndIdle(clusterManager.algorithm.getClusters(18f)) + + val cachedMarker = checkNotNull(renderer.getMarker(item)) + assertThat(cachedMarker.title).isEqualTo("Initial Title") + assertThat(cachedMarker.snippet).isEqualTo("Initial Snippet") + assertThat(cachedMarker.zIndex).isWithin(0.001f).of(1.0f) + + // Mutate item in-place: + // - Change title to "Updated Title" + // - Clear snippet to null + // - Elevate zIndex from 1.0f to 100.0f (while keeping position unchanged) + item.update( + newPos = pos, + newTitle = "Updated Title", + newSnippet = null, + newZIndex = 100.0f + ) + clusterManager.updateItem(item) + + // Add a second item so `newClusters != oldClusters` and `shouldRender` runs `CreateMarkerTask` + // on the cached marker, invoking `onClusterItemUpdated(item, cachedMarker)`. + val triggerItem = MutableTestItem( + id = "trigger_item", + position = LatLng(37.7760, -122.4194), + title = "Trigger", + snippet = null, + zIndex = 2.0f + ) + clusterManager.addItem(triggerItem) + renderClustersAndIdle(clusterManager.algorithm.getClusters(18f)) + + // Verify that the same cached Marker instance was updated via onClusterItemUpdated: + assertThat(renderer.getMarker(item)).isSameInstanceAs(cachedMarker) + assertThat(cachedMarker.title).isEqualTo("Updated Title") + + // BUG #8A VERIFIED FIXED: item.snippet is null, and cachedMarker.snippet is properly cleared to null! + assertThat(item.snippet).isNull() + assertThat(cachedMarker.snippet).isNull() + + // BUG #8B VERIFIED FIXED: item.zIndex is updated to 100.0f in-place even when position is unchanged! + assertThat(item.zIndex).isEqualTo(100.0f) + assertThat(cachedMarker.zIndex).isWithin(0.001f).of(100.0f) + } +} diff --git a/clustering/src/test/resources/robolectric.properties b/clustering/src/test/resources/robolectric.properties index 89a6c8b4c..aafb05cf4 100644 --- a/clustering/src/test/resources/robolectric.properties +++ b/clustering/src/test/resources/robolectric.properties @@ -1 +1,17 @@ -sdk=28 \ No newline at end of file +sdk=28 +shadows=com.google.android.maps.robolectric.shadows.ShadowMapView,\ +com.google.android.maps.robolectric.shadows.ShadowGoogleMap,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdate,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdateFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMarker,\ +com.google.android.maps.robolectric.shadows.ShadowPolygon,\ +com.google.android.maps.robolectric.shadows.ShadowPolyline,\ +com.google.android.maps.robolectric.shadows.ShadowProjection,\ +com.google.android.maps.robolectric.shadows.ShadowCircle,\ +com.google.android.maps.robolectric.shadows.ShadowUiSettings,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptor,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptorFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMapsInitializer,\ +com.google.android.maps.robolectric.shadows.ShadowSupportMapFragment,\ +com.google.android.maps.robolectric.shadows.ShadowGroundOverlay,\ +com.google.android.maps.robolectric.shadows.ShadowTileOverlay \ No newline at end of file diff --git a/data/build.gradle.kts b/data/build.gradle.kts index c977da6c0..6cc44a5cc 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -82,6 +82,11 @@ dependencies { implementation(libs.kotlin.stdlib.jdk8) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockito.kotlin) + testImplementation(libs.maps.robolectric.shadows) + testImplementation(libs.maps.testing.golden) + testImplementation(libs.maps.testing.visual) + testImplementation(platform(libs.compose.bom)) + testImplementation(libs.activity.compose) } tasks.register("instrumentTest") { diff --git a/data/src/test/java/com/google/maps/android/data/testing/MapsToolkitDataLayerTest.kt b/data/src/test/java/com/google/maps/android/data/testing/MapsToolkitDataLayerTest.kt new file mode 100644 index 000000000..8e2a62704 --- /dev/null +++ b/data/src/test/java/com/google/maps/android/data/testing/MapsToolkitDataLayerTest.kt @@ -0,0 +1,664 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.maps.android.data.testing + +import android.app.Activity +import android.graphics.Bitmap +import android.graphics.Color +import android.os.Bundle +import android.view.View +import com.google.android.gms.maps.CameraUpdateFactory +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.MapView +import com.google.android.gms.maps.model.BitmapDescriptorFactory +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.MarkerOptions +import com.google.android.maps.robolectric.annotation.EnableMapsShadows +import com.google.android.maps.robolectric.shadows.ShadowGoogleMap +import com.google.android.maps.robolectric.shadows.ShadowMarker +import com.google.android.maps.robolectric.truth.LatLngSubject +import com.google.android.maps.robolectric.truth.isWithin +import com.google.android.maps.robolectric.truth.meters +import com.google.android.maps.testing.golden.GoldenImageDiff +import com.google.android.maps.testing.golden.GoldenTileStrategy +import com.google.android.maps.testing.golden.captureSnapshot +import com.google.android.maps.testing.golden.enableGoldenTesting +import com.google.common.truth.Truth.assertThat +import com.google.maps.android.collections.GroundOverlayManager +import com.google.maps.android.collections.MarkerManager +import com.google.maps.android.collections.PolygonManager +import com.google.maps.android.collections.PolylineManager +import com.google.maps.android.data.Feature +import com.google.maps.android.data.geojson.GeoJsonFeature +import com.google.maps.android.data.geojson.GeoJsonLayer +import com.google.maps.android.data.geojson.GeoJsonLineString +import com.google.maps.android.data.geojson.GeoJsonLineStringStyle +import com.google.maps.android.data.geojson.GeoJsonMultiLineString +import com.google.maps.android.data.geojson.GeoJsonPoint +import com.google.maps.android.data.geojson.GeoJsonPointStyle +import com.google.maps.android.data.geojson.GeoJsonPolygonStyle +import com.google.maps.android.data.geojson.geoJsonLayer +import com.google.maps.android.data.kml.KmlLayer +import com.google.maps.android.data.kml.kmlLayer +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.nio.charset.StandardCharsets +import java.util.concurrent.atomic.AtomicReference +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.json.JSONObject +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadow.api.Shadow +import org.robolectric.shadows.ShadowLooper + +/** + * End-to-end JVM integration and adversarial bug-hunting suite for the `:data` module + * ([GeoJsonLayer], [KmlLayer], KMZ archives, and `MapViewRenderer`) powered by the + * Android Maps Testing Toolkit (`v1.2.0-rc01`). + * + * ## Architectural Scope + * By testing [GeoJsonLayer] and [KmlLayer] against stateful [ShadowGoogleMap] overlays, + * [LatLngSubject] geospatial assertions, and [GoldenImageDiff] snapshot comparisons rather than + * lenient mocks, this suite validates end-to-end rendering AND uncovers **5 distinct architectural + * defects (Bugs #1–#5)** in `:data`: + * - **Bug #1**: `KmlLayer.removeLayerFromMap()` leaks every `KmlGroundOverlay` on `GoogleMap` + * because `KmlLayer.toModelFeature(KmlGroundOverlay)` allocates a new `Feature` instance on + * every call while `MapViewRenderer` keys `renderedFeatures` in an `IdentityHashMap`. + * - **Bug #2**: `GeoJsonLayer` and `KmlLayer` `Polygon` and `Polyline` features are **never clickable** + * on `GoogleMap` because `MapViewRenderer.createPolygonOptions` and `createPolylineOptions` + * never call `.clickable(true)` (leaving SDK default `isClickable == false`). + * - **Bug #3**: `GeoJsonLayer` and `KmlLayer` completely ignore their constructor `MarkerManager`, + * `PolygonManager`, `PolylineManager`, and `GroundOverlayManager` parameters and clobber global + * `GoogleMap` click listeners when `setOnFeatureClickListener` is invoked. + * - **Bug #4**: `KmlLayer.hasPlacemarks()`, `getPlacemarks()`, `features`, and `getGroundOverlays()` + * return empty for standard KML documents containing a root `` tag, and + * `` / `GeoJsonMultiLineString` / `GeoJsonMultiPoint` features lose their styles. + * - **Bug #5**: `GeoJsonLayer.toModelFeature` silently drops `GeoJsonPointStyle` `title`, `snippet`, + * `isDraggable`, `isFlat`, `isVisible`, and custom `icon` (`BitmapDescriptor`). + */ +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(sdk = [34]) +@EnableMapsShadows +class MapsToolkitDataLayerTest { + + private lateinit var activity: Activity + private lateinit var mapView: MapView + private lateinit var googleMap: GoogleMap + private lateinit var shadowMap: ShadowGoogleMap + + @Before + fun setUp() { + activity = Robolectric.buildActivity(Activity::class.java).create().get() + mapView = MapView(activity).apply { + onCreate(Bundle()) + onStart() + onResume() + measure( + View.MeasureSpec.makeMeasureSpec(400, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(400, View.MeasureSpec.EXACTLY) + ) + layout(0, 0, 400, 400) + } + + val mapRef = AtomicReference() + mapView.getMapAsync { mapRef.set(it) } + ShadowLooper.idleMainLooper() + + googleMap = checkNotNull(mapRef.get()) + shadowMap = Shadow.extract(googleMap) as ShadowGoogleMap + } + + // ============================================================================================ + // Section 1: End-to-End GeoJSON & KMZ Rendering + Golden Snapshot Verification + // ============================================================================================ + + /** + * Verifies that [GeoJsonLayer] parses a `FeatureCollection` containing `Point`, `LineString`, + * and `Polygon` (with an interior hole), computes accurate [GeoJsonLayer.boundingBox], + * renders all overlays onto [ShadowGoogleMap], produces a verifiable visual diff via + * [GoldenImageDiff], dispatches point feature click callbacks, and cleanly removes all + * rendered GeoJSON overlays when `removeLayerFromMap()` is called. + */ + @Test + fun geoJsonLayer_rendersPointsLinesAndPolygonsAndMatchesBaselineAfterRemoval() = runTest { + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val center = LatLng(37.7749, -122.4194) + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 13f)) + googleMap.enableGoldenTesting(strategy = GoldenTileStrategy.Checkerboard()) + + val baselineJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val baselineSnapshot = baselineJob.await() + + val geoJson = JSONObject( + """ + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "pt_1", + "properties": { + "title": "Ferry Building", + "snippet": "Embarcadero" + }, + "geometry": { + "type": "Point", + "coordinates": [-122.3937, 37.7955] + } + }, + { + "type": "Feature", + "id": "line_1", + "properties": { "name": "Market Street" }, + "geometry": { + "type": "LineString", + "coordinates": [ + [-122.4194, 37.7749], + [-122.3937, 37.7955] + ] + } + }, + { + "type": "Feature", + "id": "poly_1", + "properties": { "name": "Civic Center Zone" }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [-122.4250, 37.7700], + [-122.4100, 37.7700], + [-122.4100, 37.7800], + [-122.4250, 37.7800], + [-122.4250, 37.7700] + ], + [ + [-122.4200, 37.7730], + [-122.4150, 37.7730], + [-122.4150, 37.7770], + [-122.4200, 37.7770], + [-122.4200, 37.7730] + ] + ] + } + } + ] + } + """.trimIndent() + ) + + val layer = geoJsonLayer(map = googleMap, geoJsonFile = geoJson) + layer.getDefaultPolygonStyle().apply { + fillColor = Color.argb(160, 255, 0, 0) + setStrokeColor(Color.BLUE) + setStrokeWidth(6f) + } + layer.getDefaultLineStringStyle().apply { + color = Color.GREEN + setWidth(8f) + } + + layer.addLayerToMap() + assertThat(layer.isLayerOnMap()).isTrue() + + // Verify ShadowGoogleMap state + assertThat(shadowMap.markers).hasSize(1) + assertThat(shadowMap.polylines).hasSize(1) + assertThat(shadowMap.polygons).hasSize(1) + + val marker = shadowMap.markers.single() + assertThat(marker.title).isEqualTo("Ferry Building") + assertThat(marker.snippet).isEqualTo("Embarcadero") + LatLngSubject.assertThat(marker.position) + .isWithin(1.meters) + .of(LatLng(37.7955, -122.3937)) + + val polygon = shadowMap.polygons.single() + assertThat(polygon.holes).hasSize(1) + assertThat(polygon.fillColor).isEqualTo(Color.argb(160, 255, 0, 0)) + + val bounds = checkNotNull(layer.getBoundingBox()) + assertThat(bounds.contains(LatLng(37.7955, -122.3937))).isTrue() + assertThat(bounds.contains(LatLng(37.7700, -122.4250))).isTrue() + + // Verify point feature click callback + val clickedFeatures = mutableListOf() + layer.setOnFeatureClickListener { feature -> clickedFeatures.add(feature) } + shadowMap.clickMarker(marker) + assertThat(clickedFeatures).hasSize(1) + assertThat(clickedFeatures.single().getId()).isEqualTo("pt_1") + + // Verify GoldenImageDiff detects the rendered GeoJSON features on the MapView canvas + val renderedJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val renderedSnapshot = renderedJob.await() + val diff = GoldenImageDiff.compare(expected = baselineSnapshot, actual = renderedSnapshot) + assertThat(diff.matches).isFalse() + assertThat(diff.diffPixelCount).isGreaterThan(200) + + // Removing the GeoJSON layer removes all markers, polylines, and polygons and restores the baseline image + layer.removeLayerFromMap() + assertThat(layer.isLayerOnMap()).isFalse() + assertThat(shadowMap.markers).isEmpty() + assertThat(shadowMap.polylines).isEmpty() + assertThat(shadowMap.polygons).isEmpty() + + val afterRemovalJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val afterRemovalSnapshot = afterRemovalJob.await() + GoldenImageDiff.assertMatches( + expected = baselineSnapshot, + actual = afterRemovalSnapshot, + maxDiffPercentage = 0.0 + ) + } + + /** + * Verifies that [KmlLayer] transparently detects and parses a compressed KMZ archive (`PK` zip header) + * containing `doc.kml` and an embedded PNG icon (`images/pin.png`), caches the decoded bitmap in + * `MapViewRenderer`, and assigns the embedded bitmap icon to the rendered [com.google.android.gms.maps.model.Marker]. + */ + @Test + fun kmlLayer_withKmzArchive_extractsEmbeddedBitmapAndAppliesToMarker() { + val kmlXml = + """ + + + + + + KMZ Pin + #customPin + -122.084,37.422,0 + + + + """.trimIndent() + + // Build an in-memory KMZ (ZIP) archive with doc.kml and a 24x24 cyan PNG image + val embeddedBitmap = Bitmap.createBitmap(24, 24, Bitmap.Config.ARGB_8888).apply { + eraseColor(Color.CYAN) + } + val kmzBytes = ByteArrayOutputStream().use { baos -> + ZipOutputStream(baos).use { zos -> + zos.putNextEntry(ZipEntry("doc.kml")) + zos.write(kmlXml.toByteArray(StandardCharsets.UTF_8)) + zos.closeEntry() + + zos.putNextEntry(ZipEntry("images/pin.png")) + embeddedBitmap.compress(Bitmap.CompressFormat.PNG, 100, zos) + zos.closeEntry() + } + baos.toByteArray() + } + + val layer = kmlLayer( + map = googleMap, + stream = ByteArrayInputStream(kmzBytes), + context = activity + ) + layer.addLayerToMap() + + assertThat(shadowMap.markers).hasSize(1) + val marker = shadowMap.markers.single() + assertThat(marker.title).isEqualTo("KMZ Pin") + val shadowMarker = Shadow.extract(marker) as ShadowMarker + val renderedIcon = checkNotNull(shadowMarker.iconBitmap) + assertThat(renderedIcon.width).isEqualTo(24) + assertThat(renderedIcon.height).isEqualTo(24) + assertThat(renderedIcon.getPixel(12, 12)).isEqualTo(Color.CYAN) + } + + // ============================================================================================ + // Section 2: Adversarial Bug Reproductions in :data (Bugs #1, #2, #3, #4, #5) + // ============================================================================================ + + /** + * **BUG #1 REPRODUCTION (`KmlLayer.removeLayerFromMap()` Leaks All `KmlGroundOverlay`s on `GoogleMap`)**: + * + * - In `KmlLayer.kt:434-453`, `toModelFeature(groundOverlay: KmlGroundOverlay)` constructs and + * returns a **new** `com.google.maps.android.data.renderer.model.Feature` instance on every + * invocation without caching it (unlike `toModelFeature(placemark: KmlPlacemark)` at line 378 + * which caches in `mPlacemarkMap`). + * - In `MapViewRenderer.kt:91`, rendered map objects are stored in: + * `private val renderedFeatures = IdentityHashMap>()` + * - When `kmlLayer.removeLayerFromMap()` runs (`KmlLayer.kt:356` & `374`), calling + * `renderer.removeFeature(toModelFeature(g))` passes a freshly allocated `Feature` whose + * object identity (`===`) does not match the `Feature` key stored during `addLayerToMap()`. + * - Result: `renderedFeatures[feature]` returns `null`, and **every KML GroundOverlay remains + * permanently leaked on the `GoogleMap` (`shadowMap.groundOverlays`)!** + */ + @Test + fun bug1_kmlLayer_removeLayerFromMapLeaksGroundOverlaysDueToUncachedModelFeatureIdentity() { + val kmlWithGroundOverlay = + """ + + + + + Temp Marker + -122.084,37.422,0 + + + Leaked Campus Overlay + + 37.425 + 37.420 + -122.080 + -122.088 + 15.0 + + + + + """.trimIndent() + + val layer = KmlLayer( + googleMap, + ByteArrayInputStream(kmlWithGroundOverlay.toByteArray(StandardCharsets.UTF_8)), + activity + ) + layer.addLayerToMap() + + // Both the Placemark Marker and the GroundOverlay are added to the GoogleMap + assertThat(shadowMap.markers).hasSize(1) + assertThat(shadowMap.groundOverlays).hasSize(1) + + // Now remove the KML layer from the map + layer.removeLayerFromMap() + + // The Placemark Marker (cached in mPlacemarkMap) is removed: + assertThat(shadowMap.markers).isEmpty() + + // BUG #1 VERIFIED FIXED: The GroundOverlay (cached in mGroundOverlayMap) is also removed! + assertThat(shadowMap.groundOverlays).isEmpty() + } + + /** + * **BUG #2 REGRESSION VERIFICATION (`GeoJsonLayer` and `KmlLayer` Polygons and Polylines Are Clickable on `GoogleMap`)**: + */ + @Test + fun bug2_geoJsonAndKmlLayers_polygonsAndPolylinesAreClickableAndFireFeatureClicks() { + val geoJson = JSONObject( + """ + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "clickable_line", + "geometry": { + "type": "LineString", + "coordinates": [[-122.42, 37.77], [-122.41, 37.78]] + } + }, + { + "type": "Feature", + "id": "clickable_poly", + "geometry": { + "type": "Polygon", + "coordinates": [[ + [-122.42, 37.77], + [-122.41, 37.77], + [-122.41, 37.78], + [-122.42, 37.78], + [-122.42, 37.77] + ]] + } + } + ] + } + """.trimIndent() + ) + + val layer = GeoJsonLayer(googleMap, geoJson) + assertThat(layer.getDefaultLineStringStyle().isClickable()).isTrue() + assertThat(layer.getDefaultPolygonStyle().isClickable()).isTrue() + + layer.addLayerToMap() + + val clickedFeatures = mutableListOf() + layer.setOnFeatureClickListener { feature -> clickedFeatures.add(feature) } + + val renderedPolyline = shadowMap.polylines.single() + val renderedPolygon = shadowMap.polygons.single() + + // BUG #2 VERIFIED FIXED: Both Polyline and Polygon have isClickable == true and route clicks! + assertThat(renderedPolyline.isClickable).isTrue() + assertThat(renderedPolygon.isClickable).isTrue() + + shadowMap.clickPolyline(renderedPolyline) + shadowMap.clickPolygon(renderedPolygon) + assertThat(clickedFeatures.map { it.getId() }).containsExactly("clickable_line", "clickable_poly") + } + + /** + * **BUG #3 REGRESSION VERIFICATION (`GeoJsonLayer` and `KmlLayer` Route Through Passed-In `*Manager` + * Instances and Preserve Shared `MarkerManager` Listeners)**: + */ + @Test + fun bug3_geoJsonAndKmlLayers_useObjectManagersAndPreserveMarkerManagerListeners() { + val markerManager = MarkerManager(googleMap) + val polygonManager = PolygonManager(googleMap) + val polylineManager = PolylineManager(googleMap) + val groundOverlayManager = GroundOverlayManager(googleMap) + ShadowLooper.idleMainLooper() + + // Create an application marker collection managed by MarkerManager + val appCollection = markerManager.newCollection("app_markers") + val appMarker = appCollection.addMarker( + MarkerOptions().position(LatLng(37.7749, -122.4194)).title("App Marker") + ) + val appMarkerClicks = mutableListOf() + appCollection.setOnMarkerClickListener { + appMarkerClicks.add(it.title ?: "") + true + } + + // Pass the shared managers into GeoJsonLayer + val geoJson = JSONObject( + """ + { + "type": "FeatureCollection", + "features": [{ + "type": "Feature", + "id": "geojson_pt", + "geometry": { "type": "Point", "coordinates": [-122.40, 37.78] } + }] + } + """.trimIndent() + ) + val layer = GeoJsonLayer( + googleMap, + geoJson, + markerManager, + polygonManager, + polylineManager, + groundOverlayManager + ) + layer.addLayerToMap() + + assertThat(shadowMap.markers).hasSize(2) + val geoJsonMarker = shadowMap.markers.first { it != appMarker } + + // Clicking appMarker routes through MarkerManager before AND after layer.setOnFeatureClickListener! + shadowMap.clickMarker(appMarker) + assertThat(appMarkerClicks).containsExactly("App Marker") + + val layerClicks = mutableListOf() + layer.setOnFeatureClickListener { feature -> layerClicks.add(feature.getId() ?: "") } + + shadowMap.clickMarker(appMarker) + assertThat(appMarkerClicks).hasSize(2) + + shadowMap.clickMarker(geoJsonMarker) + assertThat(layerClicks).containsExactly("geojson_pt") + + // Toggling setMap(null) + setMap(googleMap) reuses the existing collection and preserves listeners: + layer.setMap(null) + layer.setMap(googleMap) + layer.addLayerToMap() + val readdedGeoJsonMarker = shadowMap.markers.first { it != appMarker } + shadowMap.clickMarker(readdedGeoJsonMarker) + assertThat(layerClicks).hasSize(2) + + // And markerManager owns readdedGeoJsonMarker and can remove it cleanly: + assertThat(markerManager.remove(readdedGeoJsonMarker)).isTrue() + } + + /** + * **BUG #4 REGRESSION VERIFICATION (`KmlLayer.getAllPlacemarks()` Returns `` KML Items + * While `getPlacemarks()` Preserves Top-Level Behavior, and `` Preserves Styles)**: + */ + @Test + fun bug4_kmlDocumentPlacemarksAndMultiGeometriesPreserveStyles() { + val kmlDocument = + """ + + + + + Doc Point + -122.084,37.422,0 + + + Styled MultiLine + + + + -122.084,37.422,0 -122.080,37.425,0 + + + + + + """.trimIndent() + + val kmlLayer = KmlLayer( + googleMap, + ByteArrayInputStream(kmlDocument.toByteArray(StandardCharsets.UTF_8)), + activity + ) + kmlLayer.addLayerToMap() + + assertThat(shadowMap.markers).hasSize(1) + assertThat(shadowMap.polylines).hasSize(1) + + // Top-level accessors return only root placemarks outside /, while + // getAllPlacemarks() recursively traverses nested and containers: + assertThat(kmlLayer.hasPlacemarks()).isFalse() + assertThat(kmlLayer.getPlacemarks().toList()).isEmpty() + assertThat(kmlLayer.features.toList()).isEmpty() + assertThat(kmlLayer.getAllPlacemarks()).hasSize(2) + + // BUG #4B VERIFIED FIXED: KML preserves its 12 and red color! + val kmlMultiPolyline = shadowMap.polylines.single() + assertThat(kmlMultiPolyline.width).isEqualTo(12f) + assertThat(kmlMultiPolyline.color).isEqualTo(Color.RED) + + // BUG #4C VERIFIED FIXED: GeoJsonMultiLineString preserves its GeoJsonLineStringStyle! + googleMap.clear() + val geoJsonLayer = GeoJsonLayer(googleMap, JSONObject("""{"type":"FeatureCollection","features":[]}""")) + geoJsonLayer.addLayerToMap() + val multiLine = GeoJsonMultiLineString( + listOf(GeoJsonLineString(listOf(LatLng(37.0, -122.0), LatLng(37.1, -122.1)))) + ) + val multiLineFeature = GeoJsonFeature(multiLine, "multi_line_1", HashMap(), null).apply { + lineStringStyle = GeoJsonLineStringStyle().apply { + color = Color.RED + setWidth(15f) + } + } + geoJsonLayer.addFeature(multiLineFeature) + + val renderedGeoJsonMultiLine = shadowMap.polylines.single() + assertThat(renderedGeoJsonMultiLine.width).isEqualTo(15f) + assertThat(renderedGeoJsonMultiLine.color).isEqualTo(Color.RED) + } + + /** + * **BUG #5 REGRESSION VERIFICATION (`GeoJsonLayer.toModelFeature` Preserves `GeoJsonPointStyle` + * Title, Snippet, Draggable, Flat, Visible, and Custom Icon)**: + */ + @Test + fun bug5_geoJsonPointStyle_preservesTitleSnippetDraggableFlatVisibleAndCustomIcon() { + val layer = GeoJsonLayer(googleMap, JSONObject("""{"type":"FeatureCollection","features":[]}""")) + layer.addLayerToMap() + + val customBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_8888).apply { + eraseColor(Color.GREEN) + } + val customIcon = BitmapDescriptorFactory.fromBitmap(customBitmap) + + val pointFeature = GeoJsonFeature( + GeoJsonPoint(LatLng(37.7749, -122.4194)), + "styled_pt", + HashMap(), + null + ).apply { + pointStyle = GeoJsonPointStyle().apply { + setTitle("Styled Title") + setSnippet("Styled Snippet") + setDraggable(true) + setFlat(true) + setVisible(false) + setIcon(customIcon) + setAlpha(0.5f) + setRotation(90f) + setZIndex(5f) + } + } + + layer.addFeature(pointFeature) + assertThat(shadowMap.markers).hasSize(1) + val marker = shadowMap.markers.single() + val shadowMarker = Shadow.extract(marker) as ShadowMarker + + assertThat(marker.alpha).isWithin(0.02f).of(0.5f) + assertThat(marker.rotation).isWithin(0.01f).of(90f) + assertThat(marker.zIndex).isWithin(0.01f).of(5f) + + // BUG #5 VERIFIED FIXED: title, snippet, isDraggable, isFlat, isVisible, and custom icon are all preserved! + assertThat(marker.title).isEqualTo("Styled Title") + assertThat(marker.snippet).isEqualTo("Styled Snippet") + assertThat(marker.isDraggable).isTrue() + assertThat(marker.isFlat).isTrue() + assertThat(marker.isVisible).isFalse() + assertThat(shadowMarker.iconBitmap).isSameInstanceAs(customBitmap) + } +} diff --git a/data/src/test/resources/robolectric.properties b/data/src/test/resources/robolectric.properties index 89a6c8b4c..aafb05cf4 100644 --- a/data/src/test/resources/robolectric.properties +++ b/data/src/test/resources/robolectric.properties @@ -1 +1,17 @@ -sdk=28 \ No newline at end of file +sdk=28 +shadows=com.google.android.maps.robolectric.shadows.ShadowMapView,\ +com.google.android.maps.robolectric.shadows.ShadowGoogleMap,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdate,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdateFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMarker,\ +com.google.android.maps.robolectric.shadows.ShadowPolygon,\ +com.google.android.maps.robolectric.shadows.ShadowPolyline,\ +com.google.android.maps.robolectric.shadows.ShadowProjection,\ +com.google.android.maps.robolectric.shadows.ShadowCircle,\ +com.google.android.maps.robolectric.shadows.ShadowUiSettings,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptor,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptorFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMapsInitializer,\ +com.google.android.maps.robolectric.shadows.ShadowSupportMapFragment,\ +com.google.android.maps.robolectric.shadows.ShadowGroundOverlay,\ +com.google.android.maps.robolectric.shadows.ShadowTileOverlay \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c5277ad80..84a180ba1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -44,6 +44,7 @@ androidx-test-orchestrator = "1.6.1" espresso-core = "3.7.0" junit = "4.13.2" kxml2 = "2.3.0" +mapsTesting = "1.2.0-rc01" mockito-kotlin = "6.3.0" mockk = "1.14.11" robolectric = "4.17" @@ -114,6 +115,9 @@ androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version androidx-test-orchestrator = { module = "androidx.test:orchestrator", version.ref = "androidx-test-orchestrator" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } junit = { module = "junit:junit", version.ref = "junit" } +maps-robolectric-shadows = { module = "com.google.android.maps.robolectric:shadows", version.ref = "mapsTesting" } +maps-testing-golden = { module = "com.google.android.maps.testing:golden", version.ref = "mapsTesting" } +maps-testing-visual = { module = "com.google.android.maps.testing:visual", version.ref = "mapsTesting" } mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockito-kotlin" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } diff --git a/heatmaps/build.gradle.kts b/heatmaps/build.gradle.kts index 7a928a85a..36f952936 100644 --- a/heatmaps/build.gradle.kts +++ b/heatmaps/build.gradle.kts @@ -80,6 +80,12 @@ dependencies { implementation(libs.kotlin.stdlib.jdk8) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockito.kotlin) + testImplementation(libs.maps.robolectric.shadows) + testImplementation(libs.maps.testing.golden) + testImplementation(libs.maps.testing.visual) + testImplementation(platform(libs.compose.bom)) + testImplementation(libs.activity.compose) + testImplementation(libs.kotlinx.serialization.json) } tasks.register("instrumentTest") { diff --git a/heatmaps/src/test/java/com/google/maps/android/heatmaps/testing/MapsToolkitHeatmapsTest.kt b/heatmaps/src/test/java/com/google/maps/android/heatmaps/testing/MapsToolkitHeatmapsTest.kt new file mode 100644 index 000000000..91ae3bfdd --- /dev/null +++ b/heatmaps/src/test/java/com/google/maps/android/heatmaps/testing/MapsToolkitHeatmapsTest.kt @@ -0,0 +1,267 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.maps.android.heatmaps.testing + +import android.app.Activity +import android.graphics.BitmapFactory +import android.graphics.Color +import android.os.Bundle +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.MapView +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.TileOverlayOptions +import com.google.android.gms.maps.model.TileProvider +import com.google.android.maps.robolectric.annotation.EnableMapsShadows +import com.google.android.maps.robolectric.shadows.ShadowGoogleMap +import com.google.android.maps.robolectric.shadows.ShadowTileOverlay +import com.google.android.maps.testing.golden.GoldenImageDiff +import com.google.android.maps.testing.golden.GoldenTileProvider +import com.google.android.maps.testing.golden.GoldenTileStrategy +import com.google.common.truth.Truth.assertThat +import com.google.maps.android.heatmaps.Gradient +import com.google.maps.android.heatmaps.HeatmapTileProvider +import com.google.maps.android.heatmaps.WeightedLatLng +import com.google.maps.android.heatmaps.heatmapTileProviderWithData +import com.google.maps.android.heatmaps.heatmapTileProviderWithWeightedData +import com.google.maps.android.heatmaps.toWeightedLatLng +import java.util.concurrent.atomic.AtomicReference +import kotlin.test.assertFailsWith +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadow.api.Shadow +import org.robolectric.shadows.ShadowLooper + +/** + * End-to-end JVM integration and adversarial verification suite for the `:heatmaps` module + * powered by the Android Maps Testing Toolkit (`v1.2.0-rc01`). + * + * ## Architectural Scope + * 1. **Deterministic Raster Tile Verification with [GoldenImageDiff] & [GoldenTileStrategy]**: + * Decodes actual 512x512 PNG tiles produced by [HeatmapTileProvider.getTile] into [android.graphics.Bitmap]s + * and uses [GoldenImageDiff] and [GoldenTileProvider] (`GoldenTileStrategy.Custom`) to prove: + * - Bit-for-bit determinism across repeated tile requests. + * - Quantifiable pixel diffs when changing radius, opacity, gradient, maxIntensity, or point weights. + * - Proper integration with [ShadowGoogleMap.getTileOverlays] and [ShadowTileOverlay.isTileCacheCleared]. + * 2. **Adversarial Boundary & Wrapping Defect Proofs (Bugs #6 & #7)**: + * - **Bug #6A**: `HeatmapTileProvider.getTile` crashes with `ArrayIndexOutOfBoundsException` at + * `zoom = 22` (`MAX_ZOOM_LEVEL = 22`). + * - **Bug #6B**: `HeatmapTileProvider.getTile` crashes with `ArrayIndexOutOfBoundsException` + * whenever a data point lies on the upper padded tile boundary (`p.x == maxX` or `p.y == maxY`), + * because `PointQuadTree.search` uses inclusive `<=` bounds while `bucketX = ((p.x - minX) / bucketWidth).toInt()` + * produces index `TILE_DIM + radius * 2` (`544` for `radius = 16`). + * - **Bug #7A**: `HeatmapTileProvider.getTile` fails to render heatmaps wrapping across the + * International Date Line (antimeridian `±180°`), returning `TileProvider.NO_TILE` even when + * `wrappedPoints` is non-empty because lines 254 and 259 check `!tileBounds.intersects(paddedBounds)` + * and `points.isEmpty()` without checking `wrappedPoints`! + * - **Bug #7B**: `HeatmapTileProvider.setRadius` and `setOpacity` bypass the validation checks + * enforced by `HeatmapTileProvider.Builder`, allowing invalid values that corrupt state or + * throw `NegativeArraySizeException`. + */ +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(sdk = [34]) +@EnableMapsShadows +class MapsToolkitHeatmapsTest { + + private lateinit var googleMap: GoogleMap + private lateinit var shadowMap: ShadowGoogleMap + + @Before + fun setUp() { + val activity = Robolectric.buildActivity(Activity::class.java).create().get() + val mapView = MapView(activity).apply { + onCreate(Bundle()) + } + val mapRef = AtomicReference() + mapView.getMapAsync { mapRef.set(it) } + ShadowLooper.idleMainLooper() + + googleMap = checkNotNull(mapRef.get()) + shadowMap = Shadow.extract(googleMap) as ShadowGoogleMap + } + + // ============================================================================================ + // Section 1: Heatmap Tile Rasterization, GoldenImageDiff & ShadowTileOverlay Integration + // ============================================================================================ + + /** + * Verifies that [heatmapTileProviderWithData] and [heatmapTileProviderWithWeightedData] + * generate deterministic 512x512 PNG tiles, integrate with [ShadowTileOverlay] on [ShadowGoogleMap], + * and produce quantifiable pixel differences under [GoldenImageDiff] when radius, gradient, + * opacity, or dataset coordinates change. + */ + @Test + fun heatmapTileProvider_generatesDeterministicTilesAndQuantifiableGoldenDiffsAcrossMutations() { + val origin = LatLng(0.0, 0.0) + val nearby = LatLng(0.05, 0.05) + + val provider1 = heatmapTileProviderWithData( + latLngs = listOf(origin, nearby), + radius = 25, + opacity = 0.8 + ) + val provider2 = heatmapTileProviderWithWeightedData( + latLngs = listOf(origin.toWeightedLatLng(1.0), nearby.toWeightedLatLng(1.0)), + radius = 25, + opacity = 0.8 + ) + + // Attach to GoogleMap and verify ShadowTileOverlay tracking + val overlay = checkNotNull( + googleMap.addTileOverlay(TileOverlayOptions().tileProvider(provider1).zIndex(5f)) + ) + assertThat(shadowMap.tileOverlays).containsExactly(overlay) + assertThat(overlay.zIndex).isWithin(0.001f).of(5f) + + // At zoom = 1, LatLng(0.0, 0.0) is at world coordinate (0.5, 0.5), which falls in tile (1, 1) + val tile1 = provider1.getTile(1, 1, 1) + val tile2 = provider2.getTile(1, 1, 1) + assertThat(tile1).isNotEqualTo(TileProvider.NO_TILE) + assertThat(tile1.width).isEqualTo(512) + assertThat(tile1.height).isEqualTo(512) + + val bmp1 = checkNotNull(BitmapFactory.decodeByteArray(tile1.data, 0, tile1.data!!.size)) + val bmp2 = checkNotNull(BitmapFactory.decodeByteArray(tile2.data, 0, tile2.data!!.size)) + + // Identical configuration must match pixel-for-pixel (0.0% diff) + GoldenImageDiff.assertMatches( + expected = bmp1, + actual = bmp2, + maxDiffPercentage = 0.0 + ) + + // Wrap heatmap tile decoding in a GoldenTileProvider with GoldenTileStrategy.Custom + // to verify caching and synthetic tile interoperability + val customGoldenProvider = GoldenTileProvider( + strategy = GoldenTileStrategy.Custom { x, y, zoom, _ -> + val raw = provider1.getTile(x, y, zoom).data!! + BitmapFactory.decodeByteArray(raw, 0, raw.size) + }, + tileSize = 512 + ) + assertThat(customGoldenProvider.cachedTileCount()).isEqualTo(0) + val cachedTile = customGoldenProvider.getTile(1, 1, 1) + assertThat(customGoldenProvider.cachedTileCount()).isEqualTo(1) + val cachedBmp = checkNotNull(BitmapFactory.decodeByteArray(cachedTile.data, 0, cachedTile.data!!.size)) + GoldenImageDiff.assertMatches(expected = bmp1, actual = cachedBmp, maxDiffPercentage = 0.0) + customGoldenProvider.clearCache() + assertThat(customGoldenProvider.cachedTileCount()).isEqualTo(0) + + // Mutate radius from 25 to 45 -> wider Gaussian kernel must alter pixel distribution + provider1.setRadius(45) + overlay.clearTileCache() + val shadowOverlay = Shadow.extract(overlay) as ShadowTileOverlay + assertThat(shadowOverlay.isTileCacheCleared).isTrue() + + val widerTile = provider1.getTile(1, 1, 1) + val widerBmp = checkNotNull(BitmapFactory.decodeByteArray(widerTile.data, 0, widerTile.data!!.size)) + val radiusDiff = GoldenImageDiff.compare(expected = bmp1, actual = widerBmp) + assertThat(radiusDiff.matches).isFalse() + assertThat(radiusDiff.diffPixelCount).isGreaterThan(500) + + // Mutate gradient to a blue-to-cyan palette -> colorized pixels must diverge + val blueCyanGradient = Gradient( + intArrayOf(Color.BLUE, Color.CYAN), + floatArrayOf(0.1f, 0.9f) + ) + provider1.setGradient(blueCyanGradient) + val recoloredTile = provider1.getTile(1, 1, 1) + val recoloredBmp = checkNotNull( + BitmapFactory.decodeByteArray(recoloredTile.data, 0, recoloredTile.data!!.size) + ) + val gradientDiff = GoldenImageDiff.compare(expected = widerBmp, actual = recoloredBmp) + assertThat(gradientDiff.matches).isFalse() + assertThat(gradientDiff.diffPixelCount).isGreaterThan(500) + } + + // ============================================================================================ + // Section 2: Adversarial Bug Reproductions in :heatmaps (Bugs #6 & #7) + // ============================================================================================ + + /** + * **BUG #6A REPRODUCTION (`HeatmapTileProvider.getTile` Crashes with `ArrayIndexOutOfBoundsException` + * at `zoom = 22`)**: + * + * - Google Maps SDK and [GoldenTileProvider] support zoom levels `0..22`. + * - However, in `HeatmapTileProvider.kt:283`, `getMaxIntensities` allocates + * `DoubleArray(MAX_ZOOM_LEVEL)` where `MAX_ZOOM_LEVEL = 22` (valid indices `0..21`). + * - When `getTile(x, y, zoom = 22)` reaches line 278: + * `val bitmap = colorize(convolved, colorMap, maxIntensity[zoom])` + * indexing `maxIntensity[22]` throws `ArrayIndexOutOfBoundsException: Index 22 out of bounds for length 22`! + */ + @Test + fun bug6a_heatmapTileProvider_getTileAtZoom22SucceedsWithoutOutOfBounds() { + val provider = heatmapTileProviderWithData(listOf(LatLng(0.0, 0.0))) + + // At zoom 22, LatLng(0.0, 0.0) (world coordinate 0.5, 0.5) is in tile x = 2^21, y = 2^21 + val tileCoordAtZoom22 = 1 shl 21 + val tile = provider.getTile(tileCoordAtZoom22, tileCoordAtZoom22, 22) + assertThat(tile).isNotNull() + assertThat(tile).isNotEqualTo(TileProvider.NO_TILE) + } + + @Test + fun bug6b_heatmapTileProvider_pointOnUpperTileBoundaryRendersWithoutOutOfBounds() { + val interiorPoint = LatLng(0.0, -10.0) + val boundaryPoint = LatLng(0.0, 2.8125) + val provider = heatmapTileProviderWithData( + latLngs = listOf(interiorPoint, boundaryPoint), + radius = 16 + ) + + val tile = provider.getTile(1, 1, 2) + assertThat(tile).isNotNull() + assertThat(tile).isNotEqualTo(TileProvider.NO_TILE) + } + + @Test + fun bug7a_heatmapTileProvider_rendersAntimeridianWrappedPointsWhenPrimaryTilePointsAreEmpty() { + val pointNearEastDateLine = LatLng(0.0, 179.99) + val northernHemispherePoint = LatLng(75.0, -90.0) + + val provider = heatmapTileProviderWithData( + latLngs = listOf(pointNearEastDateLine, northernHemispherePoint), + radius = 50 + ) + + val tileAcrossDateLine = provider.getTile(0, 1, 1) + assertThat(tileAcrossDateLine).isNotEqualTo(TileProvider.NO_TILE) + } + + @Test + fun bug7b_heatmapTileProvider_setRadiusAndSetOpacityEnforceBuilderValidation() { + val provider = heatmapTileProviderWithData(listOf(LatLng(0.0, 0.0))) + + assertFailsWith { + HeatmapTileProvider.Builder() + .data(listOf(LatLng(0.0, 0.0))) + .radius(-5) + } + + // BUG #7B VERIFIED FIXED: provider.setRadius(-5) and setOpacity(-0.5) throw IllegalArgumentException! + assertFailsWith { + provider.setRadius(-5) + } + assertFailsWith { + provider.setOpacity(-0.5) + } + } +} diff --git a/heatmaps/src/test/resources/robolectric.properties b/heatmaps/src/test/resources/robolectric.properties index 89a6c8b4c..aafb05cf4 100644 --- a/heatmaps/src/test/resources/robolectric.properties +++ b/heatmaps/src/test/resources/robolectric.properties @@ -1 +1,17 @@ -sdk=28 \ No newline at end of file +sdk=28 +shadows=com.google.android.maps.robolectric.shadows.ShadowMapView,\ +com.google.android.maps.robolectric.shadows.ShadowGoogleMap,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdate,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdateFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMarker,\ +com.google.android.maps.robolectric.shadows.ShadowPolygon,\ +com.google.android.maps.robolectric.shadows.ShadowPolyline,\ +com.google.android.maps.robolectric.shadows.ShadowProjection,\ +com.google.android.maps.robolectric.shadows.ShadowCircle,\ +com.google.android.maps.robolectric.shadows.ShadowUiSettings,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptor,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptorFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMapsInitializer,\ +com.google.android.maps.robolectric.shadows.ShadowSupportMapFragment,\ +com.google.android.maps.robolectric.shadows.ShadowGroundOverlay,\ +com.google.android.maps.robolectric.shadows.ShadowTileOverlay \ No newline at end of file diff --git a/library/build.gradle.kts b/library/build.gradle.kts index a6631a17b..a6e49f9be 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -82,6 +82,12 @@ dependencies { implementation(libs.kotlin.stdlib.jdk8) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockito.kotlin) + testImplementation(libs.maps.robolectric.shadows) + testImplementation(libs.maps.testing.golden) + testImplementation(libs.maps.testing.visual) + testImplementation(platform(libs.compose.bom)) + testImplementation(libs.activity.compose) + testImplementation(libs.kotlinx.serialization.json) } tasks.register("instrumentTest") { diff --git a/library/src/test/java/com/google/maps/android/testing/MapsToolkitLibraryTest.kt b/library/src/test/java/com/google/maps/android/testing/MapsToolkitLibraryTest.kt new file mode 100644 index 000000000..29b125ed3 --- /dev/null +++ b/library/src/test/java/com/google/maps/android/testing/MapsToolkitLibraryTest.kt @@ -0,0 +1,850 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.maps.android.testing + +import android.app.Activity +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.Point +import android.location.Location +import android.os.Bundle +import android.view.View +import com.google.android.gms.maps.CameraUpdateFactory +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.MapView +import com.google.android.gms.maps.SupportMapFragment +import com.google.android.gms.maps.model.BitmapDescriptorFactory +import com.google.android.gms.maps.model.Circle +import com.google.android.gms.maps.model.GroundOverlay +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.LatLngBounds +import com.google.android.gms.maps.model.Marker +import com.google.android.gms.maps.model.MarkerOptions +import com.google.android.gms.maps.model.PointOfInterest +import com.google.android.gms.maps.model.Polygon +import com.google.android.gms.maps.model.Polyline +import com.google.android.gms.maps.model.TileOverlayOptions +import com.google.android.maps.robolectric.annotation.EnableMapsShadows +import com.google.android.maps.robolectric.shadows.ShadowGoogleMap +import com.google.android.maps.robolectric.shadows.ShadowMapView +import com.google.android.maps.robolectric.shadows.ShadowTileOverlay +import com.google.android.maps.robolectric.truth.LatLngSubject +import com.google.android.maps.robolectric.truth.isWithin +import com.google.android.maps.robolectric.truth.kilometers +import com.google.android.maps.robolectric.truth.meters +import com.google.android.maps.testing.golden.GoldenImageDiff +import com.google.android.maps.testing.golden.GoldenTileProvider +import com.google.android.maps.testing.golden.GoldenTileStrategy +import com.google.android.maps.testing.golden.awaitMapLoaded +import com.google.android.maps.testing.golden.captureSnapshot +import com.google.android.maps.testing.golden.enableGoldenTesting +import com.google.common.truth.Truth.assertThat +import com.google.maps.android.MarkerDragEndEvent +import com.google.maps.android.MarkerDragEvent +import com.google.maps.android.MarkerDragStartEvent +import com.google.maps.android.OnMarkerDragEvent +import com.google.maps.android.PolyUtil +import com.google.maps.android.SphericalUtil +import com.google.maps.android.addCircle +import com.google.maps.android.addGroundOverlay +import com.google.maps.android.addMarker +import com.google.maps.android.addPolygon +import com.google.maps.android.addPolyline +import com.google.maps.android.addTileOverlay +import com.google.maps.android.area +import com.google.maps.android.awaitAnimateCamera +import com.google.maps.android.awaitMap +import com.google.maps.android.awaitMapLoad +import com.google.maps.android.awaitSnapshot +import com.google.maps.android.buildGoogleMapOptions +import com.google.maps.android.cameraIdleEvents +import com.google.maps.android.cameraMoveCanceledEvents +import com.google.maps.android.cameraMoveEvents +import com.google.maps.android.cameraMoveStartedEvents +import com.google.maps.android.circleClickEvents +import com.google.maps.android.collections.CircleManager +import com.google.maps.android.collections.GroundOverlayManager +import com.google.maps.android.collections.MarkerManager +import com.google.maps.android.collections.PolygonManager +import com.google.maps.android.collections.PolylineManager +import com.google.maps.android.collections.addCircle +import com.google.maps.android.collections.addGroundOverlay +import com.google.maps.android.collections.addMarker +import com.google.maps.android.collections.addPolygon +import com.google.maps.android.collections.addPolyline +import com.google.maps.android.collections.clickEvents +import com.google.maps.android.collections.infoWindowClickEvents +import com.google.maps.android.collections.infoWindowLongClickEvents +import com.google.maps.android.component1 +import com.google.maps.android.component2 +import com.google.maps.android.computeSphericalOffsetOrigin +import com.google.maps.android.contains +import com.google.maps.android.containsLocation +import com.google.maps.android.groundOverlayClicks +import com.google.maps.android.infoWindowClickEvents +import com.google.maps.android.infoWindowCloseEvents +import com.google.maps.android.infoWindowLongClickEvents +import com.google.maps.android.isClosedPolygon +import com.google.maps.android.isLocationOnPath +import com.google.maps.android.isOnEdge +import com.google.maps.android.latLngListEncode +import com.google.maps.android.mapClickEvents +import com.google.maps.android.mapLongClickEvents +import com.google.maps.android.markerClickEvents +import com.google.maps.android.markerDragEvents +import com.google.maps.android.myLocationButtonClickEvents +import com.google.maps.android.myLocationClickEvents +import com.google.maps.android.poiClickEvents +import com.google.maps.android.polygonClickEvents +import com.google.maps.android.polylineClickEvents +import com.google.maps.android.signedArea +import com.google.maps.android.simplify +import com.google.maps.android.sphericalDistance +import com.google.maps.android.sphericalHeading +import com.google.maps.android.sphericalPathLength +import com.google.maps.android.sphericalPolygonArea +import com.google.maps.android.toLatLngList +import com.google.maps.android.withSphericalLinearInterpolation +import com.google.maps.android.withSphericalOffset +import java.util.concurrent.atomic.AtomicReference +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadow.api.Shadow +import org.robolectric.shadows.ShadowLooper + +/** + * End-to-end JVM integration and adversarial verification suite for the `:library` module + * powered by the Android Maps Testing Toolkit (`v1.2.0-rc01`). + * + * ## Architectural Scope + * Unlike traditional mock-based unit tests, this suite executes against stateful Robolectric + * shadows ([ShadowGoogleMap], [ShadowMapView], [ShadowSupportMapFragment], [ShadowProjection]), + * Truth geospatial subjects ([LatLngSubject]), and offline golden image rasterization + * ([GoldenImageDiff], [GoldenTileProvider], [GoldenTileStrategy]). + * + * We verify four major pillars of `:library`: + * 1. **KTX Coroutine & DSL Extensions**: Suspending lifecycle bridges (`awaitMap`, `awaitMapLoad`, + * `awaitAnimateCamera`, `awaitSnapshot`), builder DSLs (`addMarker`, `addPolygon`, `addPolyline`, + * `addCircle`, `addGroundOverlay`, `addTileOverlay`, `buildGoogleMapOptions`), and cold `callbackFlow` + * streams for camera, marker, info window, overlay, and location events. + * 2. **Multi-Collection Object Managers**: [MarkerManager], [PolygonManager], [PolylineManager], + * [CircleManager], and [GroundOverlayManager] collection isolation, visibility toggling, and + * Flow extensions. + * 3. **Adversarial Race Window Discovery (Bug #9)**: Exposes the asynchronous listener registration + * defect in `MapObjectManager` (`Handler(Looper.getMainLooper()).post { setListenersOnUiThread() }`), + * proving that events dispatched before main looper idling are dropped and that any KTX Flow + * collector subscribed immediately after manager instantiation gets clobbered when the posted + * runnable executes. + * 4. **Spherical Geometry, Polyline Simplification, Projection & Golden Snapshot Diffing**: + * Validates [SphericalUtil] and [PolyUtil] mathematics against [ShadowProjection] screen-space + * transforms, [LatLngSubject] distance tolerances, and [GoldenImageDiff] pixel-by-pixel diffs. + */ +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(sdk = [34]) +@EnableMapsShadows +class MapsToolkitLibraryTest { + + private lateinit var activity: Activity + private lateinit var mapView: MapView + private lateinit var googleMap: GoogleMap + private lateinit var shadowMap: ShadowGoogleMap + private lateinit var shadowMapView: ShadowMapView + + @Before + fun setUp() { + activity = Robolectric.buildActivity(Activity::class.java).create().get() + mapView = MapView(activity).apply { + onCreate(Bundle()) + onStart() + onResume() + // Measure and layout to a 400x400px viewport so ShadowProjection and + // ShadowMapView.renderMap(canvas) operate on realistic screen dimensions. + measure( + View.MeasureSpec.makeMeasureSpec(400, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(400, View.MeasureSpec.EXACTLY) + ) + layout(0, 0, 400, 400) + } + + val mapRef = AtomicReference() + mapView.getMapAsync { mapRef.set(it) } + ShadowLooper.idleMainLooper() + + googleMap = checkNotNull(mapRef.get()) + shadowMap = Shadow.extract(googleMap) as ShadowGoogleMap + shadowMapView = Shadow.extract(mapView) as ShadowMapView + } + + // ============================================================================================ + // Section 1: Coroutine Lifecycle Bridges & Builder DSLs + // ============================================================================================ + + /** + * Verifies that `MapView.awaitMap()` and `SupportMapFragment.awaitMap()` suspend until the + * main looper dispatches `OnMapReadyCallback` from [ShadowMapView] and [ShadowSupportMapFragment], + * and that `ShadowMapView` tracks lifecycle state transitions accurately. + */ + @Test + fun awaitMap_onMapViewAndSupportMapFragment_resolvesStatefulGoogleMap() = runTest { + assertThat(shadowMapView.isOnCreateCalled).isTrue() + assertThat(shadowMapView.isOnStartCalled).isTrue() + assertThat(shadowMapView.isOnResumeCalled).isTrue() + + val deferredMapFromView = async(UnconfinedTestDispatcher(testScheduler)) { + mapView.awaitMap() + } + ShadowLooper.idleMainLooper() + val resolvedFromView = deferredMapFromView.await() + assertThat(resolvedFromView).isSameInstanceAs(googleMap) + + val options = buildGoogleMapOptions { + mapType(GoogleMap.MAP_TYPE_HYBRID) + compassEnabled(true) + } + val fragment = SupportMapFragment.newInstance(options) + val deferredMapFromFragment = async(UnconfinedTestDispatcher(testScheduler)) { + fragment.awaitMap() + } + ShadowLooper.idleMainLooper() + val resolvedFromFragment = deferredMapFromFragment.await() + assertThat(resolvedFromFragment).isNotNull() + } + + /** + * Verifies that all KTX overlay builder DSLs (`addMarker`, `addPolygon`, `addPolyline`, + * `addCircle`, `addGroundOverlay`, `addTileOverlay`) construct and register real stateful + * overlay objects in [ShadowGoogleMap], and that [LatLngSubject] validates their geographic + * coordinates within metric tolerances. + */ + @Test + fun overlayBuilderDsls_populateShadowGoogleMapAndSatisfyLatLngTruthMatchers() = runTest { + val boulder = LatLng(40.0150, -105.2705) + googleMap.awaitAnimateCamera(CameraUpdateFactory.newLatLngZoom(boulder, 14f), durationMs = 250) + + LatLngSubject.assertThat(googleMap.cameraPosition.target) + .isWithin(1.meters) + .of(boulder) + assertThat(googleMap.cameraPosition.zoom).isEqualTo(14f) + + // 1. Marker DSL + val marker = checkNotNull( + googleMap.addMarker { + position(boulder) + title("Boulder") + snippet("Colorado") + alpha(0.85f) + rotation(45f) + zIndex(2f) + draggable(true) + flat(true) + } + ) + assertThat(shadowMap.markers).containsExactly(marker) + assertThat(marker.title).isEqualTo("Boulder") + assertThat(marker.snippet).isEqualTo("Colorado") + assertThat(marker.alpha).isWithin(0.001f).of(0.85f) + assertThat(marker.rotation).isWithin(0.001f).of(45f) + assertThat(marker.zIndex).isWithin(0.001f).of(2f) + assertThat(marker.isDraggable).isTrue() + assertThat(marker.isFlat).isTrue() + LatLngSubject.assertThat(marker.position).isWithin(1.meters).of(boulder) + + // 2. Polygon DSL + val triangle = listOf( + boulder, + boulder.withSphericalOffset(500.0, 90.0), + boulder.withSphericalOffset(500.0, 180.0) + ) + val polygon = googleMap.addPolygon { + addAll(triangle) + fillColor(Color.argb(128, 0, 255, 0)) + strokeColor(Color.GREEN) + strokeWidth(6f) + clickable(true) + geodesic(true) + } + assertThat(shadowMap.polygons).containsExactly(polygon) + assertThat(polygon.isClickable).isTrue() + assertThat(polygon.isGeodesic).isTrue() + assertThat(polygon.area).isGreaterThan(100_000.0) + + // 3. Polyline DSL + val polyline = googleMap.addPolyline { + addAll(triangle) + color(Color.BLUE) + width(8f) + clickable(true) + geodesic(true) + } + assertThat(shadowMap.polylines).containsExactly(polyline) + assertThat(polyline.isClickable).isTrue() + assertThat(polyline.sphericalPathLength).isWithin(15.0).of(1207.11) + + // 4. Circle DSL + val circle = googleMap.addCircle { + center(boulder) + radius(250.0) + fillColor(Color.YELLOW) + strokeColor(Color.BLACK) + clickable(true) + } + assertThat(shadowMap.circles).containsExactly(circle) + assertThat(circle.radius).isWithin(0.01).of(250.0) + LatLngSubject.assertThat(circle.center).isWithin(1.meters).of(boulder) + + // 5. GroundOverlay DSL + val overlayBitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888).apply { + eraseColor(Color.MAGENTA) + } + val groundOverlay = checkNotNull( + googleMap.addGroundOverlay { + image(BitmapDescriptorFactory.fromBitmap(overlayBitmap)) + position(boulder, 100f, 100f) + clickable(true) + transparency(0.2f) + } + ) + assertThat(shadowMap.groundOverlays).containsExactly(groundOverlay) + assertThat(groundOverlay.isClickable).isTrue() + assertThat(groundOverlay.transparency).isWithin(0.001f).of(0.2f) + + // 6. TileOverlay DSL with GoldenTileProvider + val goldenTileProvider = GoldenTileProvider(strategy = GoldenTileStrategy.Checkerboard()) + val tileOverlay = checkNotNull( + googleMap.addTileOverlay { + tileProvider(goldenTileProvider) + fadeIn(false) + zIndex(10f) + transparency(0.1f) + } + ) + assertThat(shadowMap.tileOverlays).containsExactly(tileOverlay) + assertThat(tileOverlay.fadeIn).isFalse() + assertThat(tileOverlay.zIndex).isWithin(0.001f).of(10f) + tileOverlay.clearTileCache() + val shadowTileOverlay = Shadow.extract(tileOverlay) as ShadowTileOverlay + assertThat(shadowTileOverlay.isTileCacheCleared).isTrue() + } + + // ============================================================================================ + // Section 2: Reactive Event Flows on GoogleMap + // ============================================================================================ + + /** + * Verifies all `GoogleMap` KTX cold `callbackFlow` streams (`cameraMoveStartedEvents`, + * `cameraMoveEvents`, `cameraIdleEvents`, `cameraMoveCanceledEvents`, `mapClickEvents`, + * `mapLongClickEvents`, `markerClickEvents`, `markerDragEvents`, `infoWindowClickEvents`, + * `infoWindowLongClickEvents`, `infoWindowCloseEvents`, `polygonClickEvents`, + * `polylineClickEvents`, `circleClickEvents`, `groundOverlayClicks`, `poiClickEvents`, + * `myLocationButtonClickEvents`, `myLocationClickEvents`, and `awaitMapLoad`) against + * synthetic user interactions dispatched by [ShadowGoogleMap]. + */ + @Test + fun googleMapEventFlows_emitDeterministicallyAndUnregisterOnCancellation() = runTest { + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val origin = LatLng(37.4220, -122.0841) + + // Collect camera lifecycle events + val moveStartedReasons = mutableListOf() + val moveEvents = mutableListOf() + val idleEvents = mutableListOf() + val canceledEvents = mutableListOf() + + val cameraJobs = listOf( + launch(dispatcher) { googleMap.cameraMoveStartedEvents().toList(moveStartedReasons) }, + launch(dispatcher) { googleMap.cameraMoveEvents().toList(moveEvents) }, + launch(dispatcher) { googleMap.cameraIdleEvents().toList(idleEvents) }, + launch(dispatcher) { googleMap.cameraMoveCanceledEvents().toList(canceledEvents) } + ) + + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(origin, 15f)) + shadowMap.cancelCameraMove() + + assertThat(moveStartedReasons).containsExactly( + GoogleMap.OnCameraMoveStartedListener.REASON_DEVELOPER_ANIMATION + ) + assertThat(moveEvents).hasSize(1) + assertThat(idleEvents).hasSize(1) + assertThat(canceledEvents).hasSize(1) + cameraJobs.forEach { it.cancel() } + + // Collect map click & long-click events + val mapClicks = mutableListOf() + val mapLongClicks = mutableListOf() + val clickJob = launch(dispatcher) { googleMap.mapClickEvents().toList(mapClicks) } + val longClickJob = launch(dispatcher) { googleMap.mapLongClickEvents().toList(mapLongClicks) } + + shadowMap.clickMap(origin) + shadowMap.longClickMap(origin) + assertThat(mapClicks).containsExactly(origin) + assertThat(mapLongClicks).containsExactly(origin) + + // Verify cancellation unregisters listeners so subsequent clicks are ignored + clickJob.cancel() + longClickJob.cancel() + shadowMap.clickMap(origin) + shadowMap.longClickMap(origin) + assertThat(mapClicks).hasSize(1) + assertThat(mapLongClicks).hasSize(1) + + // Marker click, drag, and info window lifecycle flows + val marker = checkNotNull(googleMap.addMarker { position(origin).title("Googleplex") }) + val markerClicks = mutableListOf() + val dragEvents = mutableListOf() + val infoClicks = mutableListOf() + val infoLongClicks = mutableListOf() + val infoCloses = mutableListOf() + + val markerJobs = listOf( + launch(dispatcher) { googleMap.markerClickEvents().toList(markerClicks) }, + launch(dispatcher) { googleMap.markerDragEvents().toList(dragEvents) }, + launch(dispatcher) { googleMap.infoWindowClickEvents().toList(infoClicks) }, + launch(dispatcher) { googleMap.infoWindowLongClickEvents().toList(infoLongClicks) }, + launch(dispatcher) { googleMap.infoWindowCloseEvents().toList(infoCloses) } + ) + + shadowMap.clickMarker(marker) + val draggedMid = origin.withSphericalOffset(50.0, 45.0) + val draggedEnd = origin.withSphericalOffset(100.0, 45.0) + shadowMap.dragMarkerStart(marker) + shadowMap.dragMarker(marker, draggedMid) + shadowMap.dragMarkerEnd(marker, draggedEnd) + + marker.showInfoWindow() + assertThat(marker.isInfoWindowShown).isTrue() + shadowMap.clickInfoWindow(marker) + shadowMap.longClickInfoWindow(marker) + marker.hideInfoWindow() + assertThat(marker.isInfoWindowShown).isFalse() + + assertThat(markerClicks).containsExactly(marker) + assertThat(dragEvents).containsExactly( + MarkerDragStartEvent(marker), + MarkerDragEvent(marker), + MarkerDragEndEvent(marker) + ).inOrder() + LatLngSubject.assertThat(marker.position).isWithin(1.meters).of(draggedEnd) + assertThat(infoClicks).containsExactly(marker) + assertThat(infoLongClicks).containsExactly(marker) + assertThat(infoCloses).containsExactly(marker) + markerJobs.forEach { it.cancel() } + + // Shape, POI, and MyLocation flows + val polygon = googleMap.addPolygon { + add(origin, draggedMid, draggedEnd) + clickable(true) + } + val polyline = googleMap.addPolyline { + add(origin, draggedEnd) + clickable(true) + } + val circle = googleMap.addCircle { + center(origin) + radius(100.0) + clickable(true) + } + val groundOverlay = checkNotNull( + googleMap.addGroundOverlay { + position(origin, 50f) + clickable(true) + } + ) + + val clickedPolygons = mutableListOf() + val clickedPolylines = mutableListOf() + val clickedCircles = mutableListOf() + val clickedOverlays = mutableListOf() + val clickedPois = mutableListOf() + val myLocationBtnClicks = mutableListOf() + val myLocationClicks = mutableListOf() + + val overlayJobs = listOf( + launch(dispatcher) { googleMap.polygonClickEvents().toList(clickedPolygons) }, + launch(dispatcher) { googleMap.polylineClickEvents().toList(clickedPolylines) }, + launch(dispatcher) { googleMap.circleClickEvents().toList(clickedCircles) }, + launch(dispatcher) { googleMap.groundOverlayClicks().toList(clickedOverlays) }, + launch(dispatcher) { googleMap.poiClickEvents().toList(clickedPois) }, + launch(dispatcher) { googleMap.myLocationButtonClickEvents().toList(myLocationBtnClicks) }, + launch(dispatcher) { googleMap.myLocationClickEvents().toList(myLocationClicks) } + ) + + shadowMap.clickPolygon(polygon) + shadowMap.clickPolyline(polyline) + shadowMap.clickCircle(circle) + shadowMap.clickGroundOverlay(groundOverlay) + val poi = PointOfInterest(origin, "place_id_123", "Amphitheatre") + shadowMap.clickPoi(poi) + assertThat(shadowMap.clickMyLocationButton()).isTrue() + val location = Location("gps").apply { + latitude = origin.latitude + longitude = origin.longitude + } + shadowMap.clickMyLocation(location) + + assertThat(clickedPolygons).containsExactly(polygon) + assertThat(clickedPolylines).containsExactly(polyline) + assertThat(clickedCircles).containsExactly(circle) + assertThat(clickedOverlays).containsExactly(groundOverlay) + assertThat(clickedPois).containsExactly(poi) + assertThat(myLocationBtnClicks).hasSize(1) + assertThat(myLocationClicks).containsExactly(location) + overlayJobs.forEach { it.cancel() } + + // Both KTX awaitMapLoad() and golden-testing awaitMapLoaded() + val mapLoadJob1 = async(dispatcher) { googleMap.awaitMapLoad() } + shadowMap.notifyMapLoaded() + mapLoadJob1.await() + + val mapLoadJob2 = async(dispatcher) { googleMap.awaitMapLoaded() } + shadowMap.notifyMapLoaded() + mapLoadJob2.await() + } + + // ============================================================================================ + // Section 3: Multi-Collection Object Managers & Bug #9 Race Window Proof + // ============================================================================================ + + /** + * Verifies multi-collection isolation, visibility toggling (`hideAll`/`showAll`), removal, + * and Flow collection across [MarkerManager], [PolygonManager], [PolylineManager], + * [CircleManager], and [GroundOverlayManager] after the main looper has processed their + * listener registration. + */ + @Test + fun mapObjectManagers_isolateCollectionsAndRouteClickFlowsCorrectly() = runTest { + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val center = LatLng(47.6062, -122.3321) + + val markerManager = MarkerManager(googleMap) + val polygonManager = PolygonManager(googleMap) + val polylineManager = PolylineManager(googleMap) + val circleManager = CircleManager(googleMap) + val groundOverlayManager = GroundOverlayManager(googleMap) + + // Idle main looper so MapObjectManager's posted setListenersOnUiThread() completes + ShadowLooper.idleMainLooper() + + val markerColA = markerManager.newCollection("hotels") + val markerColB = markerManager.newCollection("restaurants") + + val hotelMarker = markerColA.addMarker { + position(center) + title("Hotel") + } + val restaurantMarker = markerColB.addMarker { + position(center.withSphericalOffset(100.0, 90.0)) + title("Restaurant") + } + + val hotelClicks = mutableListOf() + val restaurantClicks = mutableListOf() + val hotelInfoClicks = mutableListOf() + val hotelInfoLongClicks = mutableListOf() + + val jobs = mutableListOf( + launch(dispatcher) { markerColA.clickEvents().toList(hotelClicks) }, + launch(dispatcher) { markerColB.clickEvents().toList(restaurantClicks) }, + launch(dispatcher) { markerColA.infoWindowClickEvents().toList(hotelInfoClicks) }, + launch(dispatcher) { markerColA.infoWindowLongClickEvents().toList(hotelInfoLongClicks) } + ) + + // Clicking hotelMarker only emits to markerColA, never markerColB + shadowMap.clickMarker(hotelMarker) + shadowMap.clickInfoWindow(hotelMarker) + shadowMap.longClickInfoWindow(hotelMarker) + assertThat(hotelClicks).containsExactly(hotelMarker) + assertThat(restaurantClicks).isEmpty() + assertThat(hotelInfoClicks).containsExactly(hotelMarker) + assertThat(hotelInfoLongClicks).containsExactly(hotelMarker) + + shadowMap.clickMarker(restaurantMarker) + assertThat(restaurantClicks).containsExactly(restaurantMarker) + + // Visibility & clear isolation + markerColA.hideAll() + assertThat(hotelMarker.isVisible).isFalse() + assertThat(restaurantMarker.isVisible).isTrue() + markerColA.showAll() + assertThat(hotelMarker.isVisible).isTrue() + + // Polygon, Polyline, Circle, and GroundOverlay manager flows + val polyCol = polygonManager.newCollection() + val lineCol = polylineManager.newCollection() + val circleCol = circleManager.newCollection() + val groundCol = groundOverlayManager.newCollection() + + val managedPolygon = polyCol.addPolygon { + add(center, center.withSphericalOffset(50.0, 0.0), center.withSphericalOffset(50.0, 90.0)) + clickable(true) + } + val managedPolyline = lineCol.addPolyline { + add(center, center.withSphericalOffset(100.0, 180.0)) + clickable(true) + } + val managedCircle = circleCol.addCircle { + center(center) + radius(75.0) + clickable(true) + } + val managedGroundOverlay = groundCol.addGroundOverlay { + position(center, 60f) + clickable(true) + } + + val polyClicks = mutableListOf() + val lineClicks = mutableListOf() + val circleClicks = mutableListOf() + val groundClicks = mutableListOf() + + jobs += launch(dispatcher) { polyCol.clickEvents().toList(polyClicks) } + jobs += launch(dispatcher) { lineCol.clickEvents().toList(lineClicks) } + jobs += launch(dispatcher) { circleCol.clickEvents().toList(circleClicks) } + jobs += launch(dispatcher) { groundCol.clickEvents().toList(groundClicks) } + + shadowMap.clickPolygon(managedPolygon) + shadowMap.clickPolyline(managedPolyline) + shadowMap.clickCircle(managedCircle) + shadowMap.clickGroundOverlay(managedGroundOverlay) + + assertThat(polyClicks).containsExactly(managedPolygon) + assertThat(lineClicks).containsExactly(managedPolyline) + assertThat(circleClicks).containsExactly(managedCircle) + assertThat(groundClicks).containsExactly(managedGroundOverlay) + + markerColA.clear() + assertThat(shadowMap.markers).containsExactly(restaurantMarker) + jobs.forEach { it.cancel() } + } + + /** + * **BUG #9 REGRESSION VERIFICATION (`MapObjectManager` Synchronous Listener Registration on UI Thread)**: + * + * Verifies that constructing [MarkerManager] on the main thread registers its listeners + * synchronously without waiting for a posted main-looper runnable: + * 1. Any marker click dispatched immediately after constructing `MarkerManager` (before the + * main looper drains) is routed immediately to the collection's listener. + * 2. If a caller subsequently registers a direct `googleMap.markerClickEvents()` collector, + * draining the main looper later does NOT overwrite that collector with a delayed init runnable. + */ + @Test + fun bug9_mapObjectManager_registersListenersSynchronouslyOnUiThread() = runTest { + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val point = LatLng(37.7749, -122.4194) + + // Instantiate MarkerManager on the main thread WITHOUT idling the looper + val markerManager = MarkerManager(googleMap) + val collection = markerManager.newCollection() + val marker = collection.addMarker(MarkerOptions().position(point).title("San Francisco")) + + val managerClicks = mutableListOf() + collection.setOnMarkerClickListener { + managerClicks.add(it) + true + } + + // 1. Click marker immediately -> routed synchronously to MarkerManager! + shadowMap.clickMarker(marker) + assertThat(managerClicks).containsExactly(marker) + + // 2. Suppose a caller subscribes to googleMap.markerClickEvents() after creating MarkerManager + val directFlowClicks = mutableListOf() + val flowJob = launch(dispatcher) { + googleMap.markerClickEvents().toList(directFlowClicks) + } + + shadowMap.clickMarker(marker) + assertThat(directFlowClicks).containsExactly(marker) + + // 3. Draining the main looper does NOT clobber the active `googleMap.markerClickEvents()` listener! + ShadowLooper.idleMainLooper() + shadowMap.clickMarker(marker) + assertThat(directFlowClicks).hasSize(2) + + flowJob.cancel() + } + + // ============================================================================================ + // Section 4: Spherical Geometry, Projection Alignment & Golden Visual Diffing + // ============================================================================================ + + /** + * Integrates [SphericalUtil], [PolyUtil], and KTX geometry extensions (`component1`, `component2`, + * `withSphericalOffset`, `computeSphericalOffsetOrigin`, `withSphericalLinearInterpolation`, + * `sphericalDistance`, `sphericalHeading`, `simplify`, `latLngListEncode`, `toLatLngList`) with + * [ShadowProjection] and [LatLngSubject] to verify round-trip geographic-to-screen accuracy. + */ + @Test + fun sphericalAndPolyUtils_alignWithShadowProjectionAndLatLngSubject() { + val origin = LatLng(37.7749, -122.4194) + val (lat, lng) = origin + assertThat(lat).isEqualTo(37.7749) + assertThat(lng).isEqualTo(-122.4194) + + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(origin, 15f)) + val projection = googleMap.projection + + // Center of 400x400 MapView must project to (200, 200) + val centerPoint = projection.toScreenLocation(origin) + assertThat(centerPoint).isEqualTo(Point(200, 200)) + + // Offset 500 meters East (heading 90 degrees) + val east500m = origin.withSphericalOffset(500.0, 90.0) + LatLngSubject.assertThat(east500m).isWithin(501.meters).of(origin) + assertThat(origin.sphericalDistance(east500m)).isWithin(0.5).of(500.0) + assertThat(origin.sphericalHeading(east500m)).isWithin(0.01).of(90.0) + + // Recover origin via computeSphericalOffsetOrigin + val recoveredOrigin = checkNotNull(east500m.computeSphericalOffsetOrigin(500.0, 90.0)) + LatLngSubject.assertThat(recoveredOrigin).isWithin(1.meters).of(origin) + + // Midpoint via Slerp (fraction = 0.5) + val midpoint = origin.withSphericalLinearInterpolation(east500m, 0.5) + LatLngSubject.assertThat(midpoint).isWithin(251.meters).of(origin) + LatLngSubject.assertThat(midpoint).isWithin(251.meters).of(east500m) + assertThat( listOf(origin, east500m).isLocationOnPath(midpoint, geodesic = true, tolerance = 1.0) ).isTrue() + + // Project east500m to screen coordinates and back via ShadowProjection + val eastScreen = projection.toScreenLocation(east500m) + assertThat(eastScreen.x).isGreaterThan(centerPoint.x) + assertThat(eastScreen.y).isEqualTo(centerPoint.y) + val roundTripLatLng = projection.fromScreenLocation(eastScreen) + LatLngSubject.assertThat(roundTripLatLng).isWithin(10.meters).of(east500m) + + // VisibleRegion bounds must contain origin and east500m at zoom 15 on a 400x400 viewport + val visibleRegion = projection.visibleRegion + assertThat(visibleRegion.latLngBounds.contains(origin)).isTrue() + assertThat(visibleRegion.latLngBounds.contains(east500m)).isTrue() + + // Polyline encoding/decoding and Douglas-Peucker simplification + val noisyPath = listOf( + origin, + origin.withSphericalLinearInterpolation(east500m, 0.25).withSphericalOffset(0.2, 0.0), + midpoint, + origin.withSphericalLinearInterpolation(east500m, 0.75).withSphericalOffset(0.2, 180.0), + east500m + ) + val simplified = noisyPath.simplify(tolerance = 2.0) + assertThat(simplified).containsExactly(origin, east500m).inOrder() + + val encoded = simplified.latLngListEncode() + val decoded = encoded.toLatLngList() + assertThat(decoded).hasSize(2) + LatLngSubject.assertThat(decoded[0]).isWithin(2.meters).of(origin) + LatLngSubject.assertThat(decoded[1]).isWithin(2.meters).of(east500m) + } + + /** + * Exercises `GoogleMap.enableGoldenTesting()`, `GoogleMap.awaitSnapshot()`, + * `GoogleMap.captureSnapshot()`, and [GoldenImageDiff] to verify pixel-accurate rendering + * of polygons, polylines, circles, markers, and ground overlays on [ShadowMapView]. + */ + @Test + fun goldenTestingAndSnapshots_detectVisualOverlayChangesAndMatchIdenticalScenes() = runTest { + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val center = LatLng(37.7749, -122.4194) + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 14f)) + + // Enable GoldenTesting with CoordinateGrid synthetic tiles + val goldenTileOverlay = googleMap.enableGoldenTesting( + strategy = GoldenTileStrategy.CoordinateGrid(), + tileSize = 256, + fadeIn = false + ) + assertThat(googleMap.mapType).isEqualTo(GoogleMap.MAP_TYPE_NONE) + assertThat(shadowMap.tileOverlays).contains(goldenTileOverlay) + + // Capture baseline empty scene snapshot via KTX awaitSnapshot() + val baselineJob = async(dispatcher) { googleMap.awaitSnapshot() } + ShadowLooper.idleMainLooper() + val baselineBitmap = checkNotNull(baselineJob.await()) + assertThat(baselineBitmap.width).isEqualTo(400) + assertThat(baselineBitmap.height).isEqualTo(400) + + // Capture a second snapshot of the identical scene via golden-testing captureSnapshot() + val secondJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val identicalBitmap = secondJob.await() + + // Pixel-by-pixel comparison must match with 0 differing pixels + GoldenImageDiff.assertMatches( + expected = baselineBitmap, + actual = identicalBitmap, + maxDiffPercentage = 0.0 + ) + + // Now render a closed polygon, polyline, circle, and marker onto the map + val square = listOf( + center.withSphericalOffset(600.0, 45.0), + center.withSphericalOffset(600.0, 135.0), + center.withSphericalOffset(600.0, 225.0), + center.withSphericalOffset(600.0, 315.0), + center.withSphericalOffset(600.0, 45.0) + ) + assertThat(square.isClosedPolygon()).isTrue() + assertThat(square.containsLocation(center, geodesic = true)).isTrue() + + val polygon = googleMap.addPolygon { + addAll(square) + fillColor(Color.argb(180, 255, 0, 0)) + strokeColor(Color.BLUE) + strokeWidth(5f) + } + assertThat(polygon.contains(center)).isTrue() + assertThat(polygon.isOnEdge(square[0], tolerance = 5.0)).isTrue() + assertThat( kotlin.math.abs(polygon.signedArea) ).isWithin(1.0).of(polygon.area) + + googleMap.addCircle { + center(center) + radius(200.0) + fillColor(Color.GREEN) + strokeColor(Color.BLACK) + strokeWidth(3f) + } + googleMap.addMarker { + position(center) + title("Center Pin") + } + + val renderedJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val renderedBitmap = renderedJob.await() + + // Compare baseline vs rendered scene: must diverge significantly and generate a diff bitmap + val diffResult = GoldenImageDiff.compare( + expected = baselineBitmap, + actual = renderedBitmap, + maxDiffPercentage = 0.001, + generateDiffBitmap = true + ) + assertThat(diffResult.matches).isFalse() + assertThat(diffResult.diffPixelCount).isGreaterThan(500) + assertThat(diffResult.diffPercentage).isGreaterThan(0.003) + assertThat(diffResult.diffBitmap).isNotNull() + } +} diff --git a/library/src/test/resources/robolectric.properties b/library/src/test/resources/robolectric.properties index 89a6c8b4c..aafb05cf4 100644 --- a/library/src/test/resources/robolectric.properties +++ b/library/src/test/resources/robolectric.properties @@ -1 +1,17 @@ -sdk=28 \ No newline at end of file +sdk=28 +shadows=com.google.android.maps.robolectric.shadows.ShadowMapView,\ +com.google.android.maps.robolectric.shadows.ShadowGoogleMap,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdate,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdateFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMarker,\ +com.google.android.maps.robolectric.shadows.ShadowPolygon,\ +com.google.android.maps.robolectric.shadows.ShadowPolyline,\ +com.google.android.maps.robolectric.shadows.ShadowProjection,\ +com.google.android.maps.robolectric.shadows.ShadowCircle,\ +com.google.android.maps.robolectric.shadows.ShadowUiSettings,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptor,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptorFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMapsInitializer,\ +com.google.android.maps.robolectric.shadows.ShadowSupportMapFragment,\ +com.google.android.maps.robolectric.shadows.ShadowGroundOverlay,\ +com.google.android.maps.robolectric.shadows.ShadowTileOverlay \ No newline at end of file diff --git a/repo/com/google/android/maps/robolectric/shadows/1.2.0-rc01/shadows-1.2.0-rc01.aar b/repo/com/google/android/maps/robolectric/shadows/1.2.0-rc01/shadows-1.2.0-rc01.aar new file mode 100644 index 0000000000000000000000000000000000000000..72f0cfbf6259785e1fe13ff1d7eb2d804ebb9e1e GIT binary patch literal 52137 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%0A&L>=sT4h;@rLF~8WyRqS6OYT8`D+S}ZM$2YK! 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

t|5EmiuNC4dV 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 + + + + + + + 4.0.0 + com.google.android.maps.robolectric + shadows + 1.2.0-rc01 + aar + Android Maps Robolectric Shadows + Robolectric shadows and Truth testing utilities for Google Maps SDK on Android. + https://github.com/googlemaps/android-maps-robolectric + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + Google Maps Platform Developer Relations + + + + scm:git:github.com/googlemaps/android-maps-robolectric.git + scm:git:ssh://github.com/googlemaps/android-maps-robolectric.git + https://github.com/googlemaps/android-maps-robolectric/tree/main + + + + org.jetbrains.kotlin + kotlin-stdlib + 2.4.20 + compile + + + diff --git a/repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.aar b/repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.aar new file mode 100644 index 0000000000000000000000000000000000000000..ad149c128df39f27634067037ae7b9f2ae7425e7 GIT binary patch literal 35141 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 DE$KCQ literal 0 HcmV?d00001 diff --git a/repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.module b/repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.module new file mode 100644 index 000000000..5c8292a64 --- /dev/null +++ b/repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.module @@ -0,0 +1,165 @@ +{ + "formatVersion": "1.1", + "component": { + "group": "com.google.android.maps.testing", + "module": "golden", + "version": "1.2.0-rc01", + "attributes": { + "org.gradle.status": "release" + } + }, + "createdBy": { + "gradle": { + "version": "9.8.0" + } + }, + "variants": [ + { + "name": "releaseVariantReleaseApiPublication", + "attributes": { + "org.gradle.category": "library", + "org.gradle.dependency.bundling": "external", + "org.gradle.libraryelements": "aar", + "org.gradle.usage": "java-api" + }, + "dependencies": [ + { + "group": "com.google.android.gms", + "module": "play-services-maps", + "version": { + "requires": "20.0.0" + } + }, + { + "group": "com.google.android.maps.robolectric", + "module": "shadows", + "version": { + "requires": "1.2.0-rc01" + } + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib", + "version": { + "requires": "2.4.20" + } + } + ], + "files": [ + { + "name": "golden-1.2.0-rc01.aar", + "url": "golden-1.2.0-rc01.aar", + "size": 35141, + "sha512": "61761d8d38d78e4fa0664ec8bacc2b4489b43adc0a198eb1d242ace6e537b8676665a8541b2d5c688acbdeb132061b41753805ebe63b47cc749fa0e0c433ffc9", + "sha256": "facebac421f9a92db7f9107eb57890068dd893311163667b9574b7ff0ab7f1c1", + "sha1": "44d13a70059b3a757e665376902784be93e2cbb4", + "md5": "ddb57a94c96754165fbb185bfa3756fd" + } + ] + }, + { + "name": "releaseVariantReleaseRuntimePublication", + "attributes": { + "org.gradle.category": "library", + "org.gradle.dependency.bundling": "external", + "org.gradle.libraryelements": "aar", + "org.gradle.usage": "java-runtime" + }, + "dependencies": [ + { + "group": "androidx.core", + "module": "core-ktx", + "version": { + "requires": "1.18.0" + } + }, + { + "group": "androidx.compose", + "module": "compose-bom", + "version": { + "requires": "2026.03.01" + }, + "attributes": { + "org.gradle.category": "platform" + } + }, + { + "group": "androidx.compose.ui", + "module": "ui" + }, + { + "group": "com.google.android.gms", + "module": "play-services-maps", + "version": { + "requires": "20.0.0" + } + }, + { + "group": "com.google.android.maps.robolectric", + "module": "shadows", + "version": { + "requires": "1.2.0-rc01" + } + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib", + "version": { + "requires": "2.4.20" + } + } + ], + "files": [ + { + "name": "golden-1.2.0-rc01.aar", + "url": "golden-1.2.0-rc01.aar", + "size": 35141, + "sha512": "61761d8d38d78e4fa0664ec8bacc2b4489b43adc0a198eb1d242ace6e537b8676665a8541b2d5c688acbdeb132061b41753805ebe63b47cc749fa0e0c433ffc9", + "sha256": "facebac421f9a92db7f9107eb57890068dd893311163667b9574b7ff0ab7f1c1", + "sha1": "44d13a70059b3a757e665376902784be93e2cbb4", + "md5": "ddb57a94c96754165fbb185bfa3756fd" + } + ] + }, + { + "name": "releaseVariantReleaseSourcePublication", + "attributes": { + "org.gradle.category": "documentation", + "org.gradle.dependency.bundling": "external", + "org.gradle.docstype": "sources", + "org.gradle.usage": "java-runtime" + }, + "files": [ + { + "name": "golden-1.2.0-rc01-sources.jar", + "url": "golden-1.2.0-rc01-sources.jar", + "size": 11874, + "sha512": "c33cba31cdb13557e58927a17ee98d67d7ce25ffcfdf2542d4ab47849670b24764e2e5f819e1a0e53137f70225a950564df27d3e12f79362dcd110b543a5efdc", + "sha256": "22cb8d3947ae4608dfc661aa902270b7d3490e544ae2b326059383316fdbf9cc", + "sha1": "b0de209b204f2ccaeed341e5a4e88ef44ce7c7bd", + "md5": "97eeb9eaaead0c944a21c6e8468e0a1c" + } + ] + }, + { + "name": "releaseVariantReleaseJavaDocPublication", + "attributes": { + "org.gradle.category": "documentation", + "org.gradle.dependency.bundling": "external", + "org.gradle.docstype": "javadoc", + "org.gradle.usage": "java-runtime" + }, + "files": [ + { + "name": "golden-1.2.0-rc01-javadoc.jar", + "url": "golden-1.2.0-rc01-javadoc.jar", + "size": 460055, + "sha512": "7fa09baaa311ba31e42368242022ca99d13a4821e71ecfa20eda804b81b461c3a96317929e782d0d3b6d2ba7fe85e43e3df3d9861a2d1e891aeda517e6f25aa4", + "sha256": "231d804a14463a383a7b0ab4710df5ac39fe3ab9249067709cbc207d1aa7f150", + "sha1": "e0b7b50335e80ad57ef4276d0c1547372099c358", + "md5": "7e5e9f3a6fe38f66df8d3ca2805e1419" + } + ] + } + ] +} diff --git a/repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.pom b/repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.pom new file mode 100644 index 000000000..eaea2e6b8 --- /dev/null +++ b/repo/com/google/android/maps/testing/golden/1.2.0-rc01/golden-1.2.0-rc01.pom @@ -0,0 +1,75 @@ + + + + + + + + 4.0.0 + com.google.android.maps.testing + golden + 1.2.0-rc01 + aar + Android Maps Golden Testing + Deterministic golden image and screenshot testing library for Google Maps SDK on Android. + https://github.com/googlemaps/android-maps-robolectric + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + Google Maps Platform Developer Relations + + + + scm:git:github.com/googlemaps/android-maps-robolectric.git + scm:git:ssh://github.com/googlemaps/android-maps-robolectric.git + https://github.com/googlemaps/android-maps-robolectric/tree/main + + + + + androidx.compose + compose-bom + 2026.03.01 + pom + import + + + + + + com.google.android.gms + play-services-maps + 20.0.0 + compile + + + com.google.android.maps.robolectric + shadows + 1.2.0-rc01 + compile + + + org.jetbrains.kotlin + kotlin-stdlib + 2.4.20 + compile + + + androidx.core + core-ktx + 1.18.0 + runtime + + + androidx.compose.ui + ui + runtime + + + diff --git a/repo/com/google/android/maps/testing/visual/1.2.0-rc01/visual-1.2.0-rc01.aar b/repo/com/google/android/maps/testing/visual/1.2.0-rc01/visual-1.2.0-rc01.aar new file mode 100644 index 0000000000000000000000000000000000000000..961e6671099b9c79f330542106e465658daa4032 GIT binary patch literal 114399 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 j?+yFE$i^ + + + + + + + 4.0.0 + com.google.android.maps.testing + visual + 1.2.0-rc01 + aar + Android Maps Visual Testing + AI-powered visual verification and screenshot testing library for Google Maps SDK on Android. + https://github.com/googlemaps/android-maps-robolectric + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + Google Maps Platform Developer Relations + + + + scm:git:github.com/googlemaps/android-maps-robolectric.git + scm:git:ssh://github.com/googlemaps/android-maps-robolectric.git + https://github.com/googlemaps/android-maps-robolectric/tree/main + + + + com.google.android.gms + play-services-maps + 20.0.0 + compile + + + com.google.android.maps.testing + golden + 1.2.0-rc01 + compile + + + androidx.test + core + 1.7.0 + compile + + + junit + junit + 4.13.2 + compile + + + androidx.test.ext + junit + 1.3.0 + compile + + + androidx.test.uiautomator + uiautomator + 2.3.0 + compile + + + org.jetbrains.kotlin + kotlin-stdlib + 2.4.20 + compile + + + androidx.core + core-ktx + 1.18.0 + runtime + + + org.jetbrains.kotlinx + kotlinx-serialization-json + 1.11.0 + runtime + + + io.ktor + ktor-client-core + 3.6.0 + runtime + + + io.ktor + ktor-client-cio + 3.6.0 + runtime + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index cb268f75b..52de35c61 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,6 +16,13 @@ dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { + maven { + url = uri(rootDir.resolve("repo")) + } + if (providers.gradleProperty("useMavenLocal").orNull == "true" || + providers.environmentVariable("USE_MAVEN_LOCAL").orNull == "true") { + mavenLocal() + } google() mavenCentral() } @@ -30,3 +37,20 @@ pluginManagement { } include("demo", "clustering", "heatmaps", "ui", "data", "lint-checks", "library", "visual-testing", "maps-utils") + +listOf( + file("../../android-maps-robolectric/main"), + file("../android-maps-robolectric/main"), + file("../android-maps-robolectric"), +).firstOrNull { it.resolve("settings.gradle.kts").exists() }?.let { mapsRobolectricDir -> + includeBuild(mapsRobolectricDir) { + dependencySubstitution { + substitute(module("com.google.android.maps.testing:golden")) + .using(project(":golden-testing")) + substitute(module("com.google.android.maps.robolectric:shadows")) + .using(project(":robolectric-testing")) + substitute(module("com.google.android.maps.testing:visual")) + .using(project(":visual-testing")) + } + } +} diff --git a/ui/build.gradle.kts b/ui/build.gradle.kts index 0ebd79a15..2927abc81 100644 --- a/ui/build.gradle.kts +++ b/ui/build.gradle.kts @@ -79,6 +79,12 @@ dependencies { testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockito.kotlin) + testImplementation(libs.maps.robolectric.shadows) + testImplementation(libs.maps.testing.golden) + testImplementation(libs.maps.testing.visual) + testImplementation(platform(libs.compose.bom)) + testImplementation(libs.activity.compose) + testImplementation(libs.kotlinx.serialization.json) } tasks.register("instrumentTest") { diff --git a/ui/src/test/java/com/google/maps/android/ui/testing/MapsToolkitUiTest.kt b/ui/src/test/java/com/google/maps/android/ui/testing/MapsToolkitUiTest.kt new file mode 100644 index 000000000..517d0af20 --- /dev/null +++ b/ui/src/test/java/com/google/maps/android/ui/testing/MapsToolkitUiTest.kt @@ -0,0 +1,358 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.maps.android.ui.testing + +import android.app.Activity +import android.os.Bundle +import android.os.SystemClock +import android.view.View +import android.widget.TextView +import com.google.android.gms.maps.CameraUpdateFactory +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.MapView +import com.google.android.gms.maps.model.BitmapDescriptorFactory +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.MarkerOptions +import com.google.android.maps.robolectric.annotation.EnableMapsShadows +import com.google.android.maps.robolectric.shadows.ShadowGoogleMap +import com.google.android.maps.robolectric.shadows.ShadowMarker +import com.google.android.maps.robolectric.truth.LatLngSubject +import com.google.android.maps.robolectric.truth.isWithin +import com.google.android.maps.robolectric.truth.kilometers +import com.google.android.maps.robolectric.truth.meters +import com.google.android.maps.testing.golden.GoldenImageDiff +import com.google.android.maps.testing.golden.GoldenTileStrategy +import com.google.android.maps.testing.golden.captureSnapshot +import com.google.android.maps.testing.golden.enableGoldenTesting +import com.google.android.maps.testing.visual.GeminiVisualTestHelper +import com.google.common.truth.Truth.assertThat +import com.google.maps.android.ui.AnimationUtil +import com.google.maps.android.ui.IconGenerator +import com.google.maps.android.ui.RotationLayout +import com.google.maps.android.ui.SquareTextView +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference +import kotlin.test.assertFailsWith +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.shadow.api.Shadow +import org.robolectric.shadows.ShadowLooper + +/** + * End-to-end JVM integration and adversarial verification suite for the `:ui` module + * ([IconGenerator], [RotationLayout], [SquareTextView], [AnimationUtil]) powered by the + * Android Maps Testing Toolkit (`v1.2.0-rc01`). + * + * ## Architectural Scope + * 1. **IconGenerator & Marker Snapshot Verification**: Generates bubble icons across styles + * (`STYLE_DEFAULT`, `STYLE_RED`, `STYLE_BLUE`, `STYLE_GREEN`, `STYLE_PURPLE`, `STYLE_ORANGE`), + * rotations (`0°`, `90°`, `180°`, `270°`), and content rotations, attaches them via + * [BitmapDescriptorFactory.fromBitmap] to markers on [ShadowGoogleMap], and verifies visual + * rendering on [MapView] via [GoldenImageDiff]. + * 2. **Visual Testing Toolkit (`com.google.android.maps.testing.visual`) Integration**: + * Exercises [GeminiVisualTestHelper] model resolution hierarchy (`preferredModel`, + * `gemini.model` system property, and `resetSessionModel()`). + * 3. **Marker Animation Stepping Across the Antimeridian**: Steps [AnimationUtil.animateMarkerTo] + * frame-by-frame via [ShadowLooper] across the 180th meridian (`+179.0°` to `-179.0°`), + * verifying intermediate and final coordinates with [LatLngSubject]. + * 4. **Adversarial Defect Reproductions (Bug #10)**: + * - **Bug #10A**: `AnimationUtil.animateMarkerTo(marker, finalPosition, durationInMs = 0)` + * computes `t = 0 / 0.0f = Float.NaN` on the initial frame and corrupts `marker.position` + * to `LatLng(Double.NaN, Double.NaN)`. + * - **Bug #10B**: `IconGenerator.setRotation(degrees)` uses `(degrees + 360) % 360 / 90`, + * which produces a negative index for negative angles `<= -450°` (e.g. `-450°`), causing + * `getAnchorU()` / `getAnchorV()` to crash with `IllegalStateException`. + */ +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(sdk = [34]) +@EnableMapsShadows +class MapsToolkitUiTest { + + private lateinit var activity: Activity + private lateinit var mapView: MapView + private lateinit var googleMap: GoogleMap + private lateinit var shadowMap: ShadowGoogleMap + + @Before + fun setUp() { + activity = Robolectric.buildActivity(Activity::class.java).create().get() + mapView = MapView(activity).apply { + onCreate(Bundle()) + onStart() + onResume() + measure( + View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY) + ) + layout(0, 0, 300, 300) + } + + val mapRef = AtomicReference() + mapView.getMapAsync { mapRef.set(it) } + ShadowLooper.idleMainLooper() + + googleMap = checkNotNull(mapRef.get()) + shadowMap = Shadow.extract(googleMap) as ShadowGoogleMap + } + + // ============================================================================================ + // Section 1: IconGenerator Styles, Rotations, GoldenImageDiff & Visual Helper + // ============================================================================================ + + /** + * Verifies that [IconGenerator] produces distinct, deterministic bitmaps across styles and + * 90-degree rotations, swaps width/height at 90° and 270°, computes rotated anchor coordinates, + * and renders visibly onto a [MapView] snapshot verified by [GoldenImageDiff]. + */ + @Test + fun iconGenerator_rendersDistinctStylesAndRotationsVerifiedByGoldenImageDiff() = runTest { + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val center = LatLng(37.7749, -122.4194) + googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 15f)) + googleMap.enableGoldenTesting(strategy = GoldenTileStrategy.SolidColorHash()) + + val iconGen = IconGenerator(activity) + val defaultBmp1 = iconGen.makeIcon("Coffee") + val defaultBmp2 = iconGen.makeIcon("Coffee") + + // Deterministic rendering: two calls with same state produce 0.0% pixel diff + GoldenImageDiff.assertMatches( + expected = defaultBmp1, + actual = defaultBmp2, + maxDiffPercentage = 0.0 + ) + + // Changing style from STYLE_DEFAULT (white) to STYLE_RED alters bubble pixels + iconGen.setStyle(IconGenerator.STYLE_RED) + val redBmp = iconGen.makeIcon("Coffee") + val styleDiff = GoldenImageDiff.compare(expected = defaultBmp1, actual = redBmp) + assertThat(styleDiff.matches).isFalse() + assertThat(styleDiff.diffPixelCount).isGreaterThan(100) + + // Verify all other built-in styles render non-empty bitmaps + for (style in listOf( + IconGenerator.STYLE_WHITE, + IconGenerator.STYLE_BLUE, + IconGenerator.STYLE_GREEN, + IconGenerator.STYLE_PURPLE, + IconGenerator.STYLE_ORANGE + )) { + iconGen.setStyle(style) + val bmp = iconGen.makeIcon("Tag") + assertThat(bmp.width).isGreaterThan(0) + assertThat(bmp.height).isGreaterThan(0) + } + + // Verify 0°, 90°, 180°, 270° rotations swap dimensions and rotate anchors accurately + iconGen.setRotation(0) + assertThat(iconGen.getAnchorU()).isEqualTo(0.5f) + assertThat(iconGen.getAnchorV()).isEqualTo(1.0f) + val unrotated = iconGen.makeIcon("Wide Banner Text") + + iconGen.setRotation(90) + assertThat(iconGen.getAnchorU()).isEqualTo(0.0f) + assertThat(iconGen.getAnchorV()).isEqualTo(0.5f) + val rotated90 = iconGen.makeIcon("Wide Banner Text") + assertThat(rotated90.width).isEqualTo(unrotated.height) + assertThat(rotated90.height).isEqualTo(unrotated.width) + + iconGen.setRotation(180) + assertThat(iconGen.getAnchorU()).isEqualTo(0.5f) + assertThat(iconGen.getAnchorV()).isEqualTo(0.0f) + + iconGen.setRotation(270) + assertThat(iconGen.getAnchorU()).isEqualTo(1.0f) + assertThat(iconGen.getAnchorV()).isEqualTo(0.5f) + + // Attach the generated IconGenerator bitmap to a Marker on GoogleMap and verify via snapshot + iconGen.setRotation(0) + val emptyMapJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val emptyMapSnapshot = emptyMapJob.await() + + val marker = checkNotNull( + googleMap.addMarker( + MarkerOptions() + .position(center) + .icon(BitmapDescriptorFactory.fromBitmap(redBmp)) + .anchor(iconGen.getAnchorU(), iconGen.getAnchorV()) + ) + ) + val shadowMarker = Shadow.extract(marker) as ShadowMarker + assertThat(shadowMarker.iconBitmap).isSameInstanceAs(redBmp) + + val mapWithIconJob = async(dispatcher) { googleMap.captureSnapshot() } + ShadowLooper.idleMainLooper() + val mapWithIconSnapshot = mapWithIconJob.await() + + val mapDiff = GoldenImageDiff.compare( + expected = emptyMapSnapshot, + actual = mapWithIconSnapshot + ) + assertThat(mapDiff.matches).isFalse() + assertThat(mapDiff.diffPixelCount).isGreaterThan(100) + } + + /** + * Verifies [SquareTextView] square dimension enforcement, [RotationLayout] dimension swapping, + * and [GeminiVisualTestHelper] model resolution hierarchy from `maps-testing-visual`. + */ + @Test + fun squareTextViewRotationLayoutAndGeminiVisualTestHelper_behaveDeterministically() = runTest { + // 1. SquareTextView always measures to max(width, height) x max(width, height) + val squareTextView = SquareTextView(activity).apply { + text = "12345" + } + squareTextView.measure( + View.MeasureSpec.makeMeasureSpec(120, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(40, View.MeasureSpec.EXACTLY) + ) + assertThat(squareTextView.measuredWidth).isEqualTo(120) + assertThat(squareTextView.measuredHeight).isEqualTo(120) + + // 2. RotationLayout swaps width and height when rotated by 90 or 270 degrees + val rotationLayout = RotationLayout(activity) + val child = TextView(activity) + rotationLayout.addView(child) + rotationLayout.setViewRotation(90) + rotationLayout.measure( + View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(80, View.MeasureSpec.EXACTLY) + ) + assertThat(rotationLayout.measuredWidth).isEqualTo(80) + assertThat(rotationLayout.measuredHeight).isEqualTo(200) + + // 3. GeminiVisualTestHelper model resolution priority + GeminiVisualTestHelper.resetSessionModel() + val explicitHelper = GeminiVisualTestHelper(preferredModel = "gemini-2.5-flash") + assertThat(explicitHelper.getOrResolveModel("dummy-key")).isEqualTo("gemini-2.5-flash") + + System.setProperty("gemini.model", "gemini-2.5-pro-preview") + try { + val propHelper = GeminiVisualTestHelper() + assertThat(propHelper.getOrResolveModel("dummy-key")).isEqualTo("gemini-2.5-pro-preview") + } finally { + System.clearProperty("gemini.model") + GeminiVisualTestHelper.resetSessionModel() + } + } + + // ============================================================================================ + // Section 2: Marker Animation Across Antimeridian & Bug #10 Reproductions + // ============================================================================================ + + /** + * Verifies that [AnimationUtil.animateMarkerTo] smoothly interpolates a [com.google.android.gms.maps.model.Marker] + * across the 180th meridian (International Date Line) using shortest-path longitude wrapping + * (`+179.0°` to `-179.0°` via `±180.0°` rather than traversing 358° the wrong way around the globe). + */ + @Test + fun animationUtil_animateMarkerTo_interpolatesShortestPathAcrossAntimeridian() { + val start = LatLng(0.0, 179.0) + val end = LatLng(2.0, -179.0) + val marker = checkNotNull(googleMap.addMarker(MarkerOptions().position(start))) + + // Default durationInMs = 2000L (125 * 16ms = 2000ms) + AnimationUtil.animateMarkerTo(marker, end, durationInMs = 2000L) + + // Advance clock to midpoint (1000ms, 62 frames * 16ms = 992ms): marker crosses the antimeridian + // near (lat = 1.0, lng = ±180.0), within 2km of (1.0, 180.0) (vs ~20,000km if it went the wrong way!) + ShadowLooper.idleMainLooper(1000, TimeUnit.MILLISECONDS) + LatLngSubject.assertThat(marker.position) + .isWithin(2.kilometers) + .of(LatLng(1.0, 180.0)) + + // Advance clock to completion (2000ms): marker reaches final destination (2.0, -179.0) + ShadowLooper.idleMainLooper(1100, TimeUnit.MILLISECONDS) + LatLngSubject.assertThat(marker.position) + .isWithin(1.meters) + .of(end) + } + + /** + * **BUG #10A REPRODUCTION (`AnimationUtil.animateMarkerTo` With `durationInMs = 0` Corrupts + * `Marker.position` to `LatLng(NaN, NaN)`)**: + * + * - When a caller invokes `AnimationUtil.animateMarkerTo(marker, finalPosition, durationInMs = 0)` + * (for example, when user preferences or test settings disable animation duration by passing `0L`), + * `AnimationUtil.kt:54-57` executes on the first posted frame (`elapsed = 0L`): + * ```kotlin + * val elapsed = SystemClock.uptimeMillis() - start + * val t = elapsed / durationInMs.toFloat() // 0 / 0.0f == Float.NaN! + * val v = interpolator.getInterpolation(t) // Float.NaN! + * marker.position = latLngInterpolator.interpolate(v, startPosition, finalPosition) + * ``` + * - Because `0 / 0.0f` is `Float.NaN` (and `t < 1` is `false` for `NaN`), the animation loop + * terminates immediately after setting `marker.position = LatLng(Double.NaN, Double.NaN)`! + * + * **BUG #10B REPRODUCTION (`AnimationUtil.animateMarkerTo` Does Not Clamp `t` to `1.0f`, Causing + * `AccelerateDecelerateInterpolator` to Rebound Backward on Non-Multiple-of-16ms Durations or Frame Drops)**: + * + * - `AnimationUtil.kt:55-63` computes `val t = elapsed / durationInMs.toFloat()` without clamping + * `t.coerceAtMost(1.0f)`. + * - Because `AccelerateDecelerateInterpolator.getInterpolation(t)` evaluates + * `cos((t + 1) * PI) / 2 + 0.5`, whenever `elapsed > durationInMs` on the final tick (for example, + * `durationInMs = 1000L` where `63 * 16ms = 1008ms`, or when the main thread drops frames), + * `t > 1.0f` causes the cosine curve to **rebound backward** (`v < 1.0f`) and terminate without + * ever snapping `marker.position` to `finalPosition`! + * + * **BUG #10C REPRODUCTION (`IconGenerator.setRotation` Crashes `getAnchorU()` / `getAnchorV()` + * on Negative Angles `<= -450°`)**: + * + * - `IconGenerator.kt:135-137` computes `rotation = (degrees + 360) % 360 / 90`. + * - Because Kotlin's `%` operator is remainder (preserving the dividend's sign) rather than + * `Math.floorMod`, passing `degrees = -450` yields `(-450 + 360) % 360 / 90 == -1`, which + * subsequently crashes `rotateAnchor` with `IllegalStateException`! + */ + @Test + fun bug10_animationUtilZeroDurationAndClampedOvershootAndIconGeneratorNegativeRotation() { + val start = LatLng(0.0, 179.0) + val destination = LatLng(2.0, -179.0) + val marker = checkNotNull(googleMap.addMarker(MarkerOptions().position(start))) + + // BUG #10A VERIFIED FIXED: durationInMs = 0L snaps directly to destination without NaN! + AnimationUtil.animateMarkerTo(marker, destination, durationInMs = 0L) + ShadowLooper.idleMainLooper() + LatLngSubject.assertThat(marker.position) + .isWithin(1.meters) + .of(destination) + + // BUG #10B VERIFIED FIXED: With durationInMs = 1000L, clamping t to 1.0f lands on exact destination! + marker.position = start + AnimationUtil.animateMarkerTo(marker, destination, durationInMs = 1000L) + ShadowLooper.idleMainLooper(1100, TimeUnit.MILLISECONDS) + LatLngSubject.assertThat(marker.position) + .isWithin(1.meters) + .of(destination) + + // BUG #10C VERIFIED FIXED: IconGenerator.setRotation(-450) normalizes via floorMod to 270° (rotation = 3)! + val iconGenerator = IconGenerator(activity) + iconGenerator.setRotation(-450) + assertThat(iconGenerator.getAnchorU()).isWithin(0.001f).of(1.0f) + assertThat(iconGenerator.getAnchorV()).isWithin(0.001f).of(0.5f) + } +} diff --git a/ui/src/test/resources/robolectric.properties b/ui/src/test/resources/robolectric.properties index 89a6c8b4c..aafb05cf4 100644 --- a/ui/src/test/resources/robolectric.properties +++ b/ui/src/test/resources/robolectric.properties @@ -1 +1,17 @@ -sdk=28 \ No newline at end of file +sdk=28 +shadows=com.google.android.maps.robolectric.shadows.ShadowMapView,\ +com.google.android.maps.robolectric.shadows.ShadowGoogleMap,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdate,\ +com.google.android.maps.robolectric.shadows.ShadowCameraUpdateFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMarker,\ +com.google.android.maps.robolectric.shadows.ShadowPolygon,\ +com.google.android.maps.robolectric.shadows.ShadowPolyline,\ +com.google.android.maps.robolectric.shadows.ShadowProjection,\ +com.google.android.maps.robolectric.shadows.ShadowCircle,\ +com.google.android.maps.robolectric.shadows.ShadowUiSettings,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptor,\ +com.google.android.maps.robolectric.shadows.ShadowBitmapDescriptorFactory,\ +com.google.android.maps.robolectric.shadows.ShadowMapsInitializer,\ +com.google.android.maps.robolectric.shadows.ShadowSupportMapFragment,\ +com.google.android.maps.robolectric.shadows.ShadowGroundOverlay,\ +com.google.android.maps.robolectric.shadows.ShadowTileOverlay \ No newline at end of file