diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 172bccc9b..1fd8b9a40 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -30,6 +30,7 @@ materialIconsExtendedAndroid = "1.7.8"
# Google Maps Platform
mapsecrets = "2.0.1"
mapsutils = "6.0.0"
+mapsRobolectric = "1.0.0"
# Testing
androidCore = "1.7.0"
@@ -74,6 +75,7 @@ material = { group = "com.google.android.material", name = "material", version.r
maps-utils-core = { module = "com.google.maps.android:android-maps-utils-core", version.ref = "mapsutils" }
maps-utils = { module = "com.google.maps.android:android-maps-utils", version.ref = "mapsutils" }
maps-secrets-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "mapsecrets" }
+maps-robolectric-shadows = { module = "com.google.android.maps.robolectric:shadows", version.ref = "mapsRobolectric" }
# Testing
androidx-test-compose-ui = { module = "androidx.compose.ui:ui-test-junit4" }
diff --git a/maps-compose/build.gradle.kts b/maps-compose/build.gradle.kts
index d47910041..53dd3535f 100644
--- a/maps-compose/build.gradle.kts
+++ b/maps-compose/build.gradle.kts
@@ -56,6 +56,12 @@ android {
enableAndroidTestCoverage = true
}
}
+
+ testOptions {
+ unitTests {
+ isIncludeAndroidResources = true
+ }
+ }
}
kotlin {
@@ -92,6 +98,14 @@ dependencies {
api(libs.maps.utils.core)
testImplementation(libs.test.junit)
+ testImplementation(libs.robolectric)
+ testImplementation(libs.truth)
+ testImplementation(libs.kotlinx.coroutines.test)
+ testImplementation(libs.maps.robolectric.shadows)
+ testImplementation(libs.mockk)
+ testImplementation(libs.androidx.test.compose.ui)
+ testImplementation(libs.androidx.activity.compose)
+ testImplementation(libs.androidx.test.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.test.espresso)
diff --git a/maps-compose/src/debug/AndroidManifest.xml b/maps-compose/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..b106adacb
--- /dev/null
+++ b/maps-compose/src/debug/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/maps-compose/src/test/java/com/google/maps/android/compose/CameraPositionStateTest.kt b/maps-compose/src/test/java/com/google/maps/android/compose/CameraPositionStateTest.kt
new file mode 100644
index 000000000..9f5757fea
--- /dev/null
+++ b/maps-compose/src/test/java/com/google/maps/android/compose/CameraPositionStateTest.kt
@@ -0,0 +1,150 @@
+/*
+ * 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.compose
+
+import android.app.Activity
+import android.os.Bundle
+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.CameraPosition
+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.truth.LatLngSubject
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.atomic.AtomicReference
+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.shadow.api.Shadow
+import org.robolectric.shadows.ShadowLooper
+
+// [START maps_compose_camera_position_state_test]
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [34])
+@EnableMapsShadows
+internal class CameraPositionStateTest {
+
+ private lateinit var googleMap: GoogleMap
+ private lateinit var shadowMap: ShadowGoogleMap
+
+ private val sydney: LatLng = LatLng(-33.852, 151.211)
+ private val tokyo: LatLng = LatLng(35.6762, 139.6503)
+
+ @Before
+ internal fun setUp(): Unit {
+ val activity = Robolectric.buildActivity(Activity::class.java).create().get()
+ val mapView = MapView(activity)
+ mapView.onCreate(Bundle())
+
+ val mapRef = AtomicReference()
+ mapView.getMapAsync { mapRef.set(it) }
+ ShadowLooper.idleMainLooper()
+
+ googleMap = mapRef.get()
+ shadowMap = Shadow.extract(googleMap) as ShadowGoogleMap
+ }
+
+ @Test
+ internal fun testInitialPosition_withoutMapBound(): Unit {
+ val state = CameraPositionState(position = CameraPosition.fromLatLngZoom(sydney, 10f))
+ assertThat(state.position.target).isEqualTo(sydney)
+ assertThat(state.position.zoom).isEqualTo(10f)
+ assertThat(state.isMoving).isFalse()
+ assertThat(state.projection).isNull()
+ }
+
+ @Test
+ internal fun testSetMap_syncsPositionFromStateToMap(): Unit {
+ val initialPosition = CameraPosition.fromLatLngZoom(sydney, 12f)
+ val state = CameraPositionState(position = initialPosition)
+
+ state.setMap(googleMap)
+
+ assertThat(googleMap.cameraPosition.target).isEqualTo(sydney)
+ assertThat(googleMap.cameraPosition.zoom).isEqualTo(12f)
+ assertThat(state.projection).isNotNull()
+ }
+
+ private fun bindState(state: CameraPositionState) {
+ state.setMap(googleMap)
+ googleMap.setOnCameraMoveStartedListener { reason ->
+ state.cameraMoveStartedReason = CameraMoveStartedReason.fromInt(reason)
+ state.isMoving = true
+ }
+ googleMap.setOnCameraMoveListener {
+ state.rawPosition = googleMap.cameraPosition
+ }
+ googleMap.setOnCameraIdleListener {
+ state.isMoving = false
+ state.rawPosition = googleMap.cameraPosition
+ }
+ }
+
+ @Test
+ internal fun testMove_instantaneouslyUpdatesCameraPosition(): Unit {
+ val state = CameraPositionState()
+ bindState(state)
+
+ state.move(CameraUpdateFactory.newLatLngZoom(tokyo, 14f))
+
+ assertThat(googleMap.cameraPosition.target).isEqualTo(tokyo)
+ LatLngSubject.assertThat(state.position.target)
+ .isWithin(1.0)
+ .of(tokyo)
+ assertThat(state.position.zoom).isEqualTo(14f)
+ assertThat(state.isMoving).isFalse()
+ }
+
+ @Test
+ internal fun testAnimate_updatesCameraPosition(): Unit = runTest {
+ val state = CameraPositionState()
+ bindState(state)
+
+ state.animate(CameraUpdateFactory.newLatLngZoom(tokyo, 15f), durationMs = 1000)
+
+ assertThat(googleMap.cameraPosition.target).isEqualTo(tokyo)
+ LatLngSubject.assertThat(state.position.target)
+ .isWithin(1.0)
+ .of(tokyo)
+ assertThat(state.position.zoom).isEqualTo(15f)
+ assertThat(state.isMoving).isFalse()
+ }
+
+ @Test
+ internal fun testProjection_convertsCoordinates(): Unit {
+ val state = CameraPositionState()
+ state.setMap(googleMap)
+
+ state.move(CameraUpdateFactory.newLatLngZoom(sydney, 10f))
+
+ val projection = state.projection
+ assertThat(projection).isNotNull()
+ val screenPoint = projection?.toScreenLocation(sydney)
+ assertThat(screenPoint).isNotNull()
+ val roundTripLatLng = projection?.fromScreenLocation(screenPoint!!)
+ LatLngSubject.assertThat(roundTripLatLng)
+ .isWithin(1.0)
+ .of(sydney)
+ }
+}
+// [END maps_compose_camera_position_state_test]
diff --git a/maps-compose/src/test/java/com/google/maps/android/compose/GoogleMapTest.kt b/maps-compose/src/test/java/com/google/maps/android/compose/GoogleMapTest.kt
new file mode 100644
index 000000000..102c4faa2
--- /dev/null
+++ b/maps-compose/src/test/java/com/google/maps/android/compose/GoogleMapTest.kt
@@ -0,0 +1,188 @@
+/*
+ * 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.compose
+
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.AndroidComposeTestRule
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.model.CameraPosition
+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.truth.LatLngSubject
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.shadow.api.Shadow
+
+// [START maps_compose_google_map_composable_test]
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [34])
+@EnableMapsShadows
+@OptIn(MapsComposeExperimentalApi::class)
+internal class GoogleMapTest {
+
+ @get:Rule
+ internal val composeTestRule: AndroidComposeTestRule, ComponentActivity> =
+ createAndroidComposeRule()
+
+ private val sydney: LatLng = LatLng(-33.852, 151.211)
+
+ @Test
+ internal fun testGoogleMap_rendersWithMarkersAndCircles(): Unit {
+ lateinit var cameraState: CameraPositionState
+ var underlyingMap: GoogleMap? = null
+
+ composeTestRule.setContent {
+ cameraState = rememberCameraPositionState {
+ position = CameraPosition.fromLatLngZoom(sydney, 11f)
+ }
+ GoogleMap(
+ cameraPositionState = cameraState,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ MapEffect(Unit) { map ->
+ underlyingMap = map
+ }
+ Marker(
+ state = rememberMarkerState(position = sydney),
+ title = "Sydney Flagship"
+ )
+ Circle(
+ center = sydney,
+ radius = 1500.0
+ )
+ }
+ }
+
+ composeTestRule.waitForIdle()
+
+ assertThat(underlyingMap).isNotNull()
+ val shadowMap = Shadow.extract(underlyingMap) as ShadowGoogleMap
+
+ // Verify camera position
+ LatLngSubject.assertThat(cameraState.position.target)
+ .isWithin(1.0)
+ .of(sydney)
+ assertThat(cameraState.position.zoom).isEqualTo(11f)
+
+ // Verify Marker rendered via MapApplier
+ assertThat(shadowMap.markers).hasSize(1)
+ val marker = shadowMap.markers.first()
+ assertThat(marker.title).isEqualTo("Sydney Flagship")
+ LatLngSubject.assertThat(marker.position)
+ .isWithin(1.0)
+ .of(sydney)
+
+ // Verify Circle rendered via MapApplier
+ assertThat(shadowMap.circles).hasSize(1)
+ val circle = shadowMap.circles.first()
+ assertThat(circle.radius).isEqualTo(1500.0)
+ LatLngSubject.assertThat(circle.center)
+ .isWithin(1.0)
+ .of(sydney)
+ }
+
+ @Test
+ internal fun testGoogleMap_markerClickUpdatesComposeState(): Unit {
+ var underlyingMap: GoogleMap? = null
+ var markerClicked by mutableStateOf(false)
+
+ composeTestRule.setContent {
+ val cameraState = rememberCameraPositionState {
+ position = CameraPosition.fromLatLngZoom(sydney, 10f)
+ }
+ GoogleMap(
+ cameraPositionState = cameraState,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ MapEffect(Unit) { map ->
+ underlyingMap = map
+ }
+ Marker(
+ state = rememberMarkerState(position = sydney),
+ title = "Clickable Marker",
+ onClick = {
+ markerClicked = true
+ true
+ }
+ )
+ }
+ }
+
+ composeTestRule.waitForIdle()
+
+ assertThat(underlyingMap).isNotNull()
+ val shadowMap = Shadow.extract(underlyingMap) as ShadowGoogleMap
+ val marker = shadowMap.markers.first()
+
+ // Simulate user clicking on marker
+ shadowMap.clickMarker(marker)
+ composeTestRule.waitForIdle()
+
+ assertThat(markerClicked).isTrue()
+ }
+
+ @Test
+ internal fun testGoogleMap_dynamicRecompositionAddsAndRemovesMarker(): Unit {
+ var underlyingMap: GoogleMap? = null
+ var showMarker by mutableStateOf(true)
+
+ composeTestRule.setContent {
+ val cameraState = rememberCameraPositionState {
+ position = CameraPosition.fromLatLngZoom(sydney, 10f)
+ }
+ GoogleMap(
+ cameraPositionState = cameraState,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ MapEffect(Unit) { map ->
+ underlyingMap = map
+ }
+ if (showMarker) {
+ Marker(
+ state = rememberMarkerState(position = sydney),
+ title = "Conditional Marker"
+ )
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ assertThat(underlyingMap).isNotNull()
+ val shadowMap = Shadow.extract(underlyingMap) as ShadowGoogleMap
+ assertThat(shadowMap.markers).hasSize(1)
+
+ // Mutate Compose state to remove marker
+ showMarker = false
+ composeTestRule.waitForIdle()
+
+ // Assert that the marker was dynamically removed from the map
+ assertThat(shadowMap.markers).isEmpty()
+ }
+}
+// [END maps_compose_google_map_composable_test]
diff --git a/maps-compose/src/test/java/com/google/maps/android/compose/MapApplierTest.kt b/maps-compose/src/test/java/com/google/maps/android/compose/MapApplierTest.kt
new file mode 100644
index 000000000..3986fcf45
--- /dev/null
+++ b/maps-compose/src/test/java/com/google/maps/android/compose/MapApplierTest.kt
@@ -0,0 +1,198 @@
+/*
+ * 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.compose
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.compose.runtime.CompositionContext
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.MapView
+import com.google.android.gms.maps.model.CircleOptions
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.Marker
+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.truth.LatLngSubject
+import com.google.common.truth.Truth.assertThat
+import io.mockk.mockk
+import java.util.concurrent.atomic.AtomicReference
+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.shadow.api.Shadow
+import org.robolectric.shadows.ShadowLooper
+
+// [START maps_compose_map_applier_test]
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [34])
+@EnableMapsShadows
+internal class MapApplierTest {
+
+ private lateinit var mapView: MapView
+ private lateinit var googleMap: GoogleMap
+ private lateinit var shadowMap: ShadowGoogleMap
+ private lateinit var applier: MapApplier
+
+ private val sydney: LatLng = LatLng(-33.852, 151.211)
+ private val perth: LatLng = LatLng(-31.952, 115.857)
+
+ @Before
+ internal fun setUp(): Unit {
+ val activity = Robolectric.buildActivity(Activity::class.java).create().get()
+ mapView = MapView(activity)
+ mapView.onCreate(Bundle())
+
+ val mapRef = AtomicReference()
+ mapView.getMapAsync { mapRef.set(it) }
+ ShadowLooper.idleMainLooper()
+
+ googleMap = mapRef.get()
+ shadowMap = Shadow.extract(googleMap) as ShadowGoogleMap
+ applier = MapApplier(googleMap, mapView, MapClickListeners(), isLiteMode = false)
+ }
+
+ private fun createMarkerNode(
+ markerState: MarkerState,
+ onMarkerClick: (Marker) -> Boolean = { false },
+ position: LatLng = sydney
+ ): MarkerNode {
+ val marker = googleMap.addMarker(MarkerOptions().position(position).title("Test Marker"))!!
+ val dummyContext: CompositionContext = mockk(relaxed = true)
+ return MarkerNode(
+ compositionContext = dummyContext,
+ marker = marker,
+ markerState = markerState,
+ onMarkerClick = onMarkerClick,
+ onInfoWindowClick = {},
+ onInfoWindowClose = {},
+ onInfoWindowLongClick = {},
+ infoWindow = null,
+ infoContent = null,
+ )
+ }
+
+ @Test
+ internal fun testInsertMarkerNode_attachesAndSyncsMarkerState(): Unit {
+ val markerState = MarkerState(sydney)
+ val node = createMarkerNode(markerState)
+
+ applier.insertBottomUp(0, node)
+
+ assertThat(markerState.marker).isSameInstanceAs(node.marker)
+ assertThat(shadowMap.markers).contains(node.marker)
+ }
+
+ @Test
+ internal fun testRemoveMarkerNode_clearsMarkerStateAndRemovesFromMap(): Unit {
+ val markerState = MarkerState(sydney)
+ val node = createMarkerNode(markerState)
+
+ applier.insertBottomUp(0, node)
+ assertThat(shadowMap.markers).hasSize(1)
+
+ applier.remove(0, 1)
+
+ assertThat(markerState.marker).isNull()
+ assertThat(shadowMap.markers).isEmpty()
+ }
+
+ @Test
+ internal fun testMarkerClick_dispatchesToNodeCallback(): Unit {
+ val markerState = MarkerState(sydney)
+ var clickCount = 0
+ val node = createMarkerNode(
+ markerState = markerState,
+ onMarkerClick = {
+ clickCount++
+ true
+ }
+ )
+
+ applier.insertBottomUp(0, node)
+
+ // Simulate marker click via shadow
+ shadowMap.clickMarker(node.marker)
+
+ assertThat(clickCount).isEqualTo(1)
+ }
+
+ @Test
+ internal fun testMarkerDrag_updatesMarkerStatePosition(): Unit {
+ val markerState = MarkerState(sydney)
+ val node = createMarkerNode(markerState)
+ node.marker.isDraggable = true
+
+ applier.insertBottomUp(0, node)
+
+ // Drag marker to Perth via shadow
+ shadowMap.dragMarkerStart(node.marker)
+ assertThat(markerState.isDragging).isTrue()
+
+ shadowMap.dragMarker(node.marker, perth)
+ LatLngSubject.assertThat(markerState.position).isWithin(1.0).of(perth)
+
+ shadowMap.dragMarkerEnd(node.marker, perth)
+ assertThat(markerState.isDragging).isFalse()
+ LatLngSubject.assertThat(markerState.position).isWithin(1.0).of(perth)
+ }
+
+ @Test
+ internal fun testCircleNode_clickAndRemoval(): Unit {
+ val circle = googleMap.addCircle(CircleOptions().center(sydney).radius(500.0).clickable(true))
+ var circleClicked = false
+ val circleNode = CircleNode(
+ circle = circle,
+ onCircleClick = { circleClicked = true }
+ )
+
+ applier.insertBottomUp(0, circleNode)
+ assertThat(shadowMap.circles).contains(circle)
+
+ // Simulate click via shadow
+ shadowMap.clickCircle(circle)
+ assertThat(circleClicked).isTrue()
+
+ // Remove node
+ applier.remove(0, 1)
+ assertThat(shadowMap.circles).isEmpty()
+ }
+
+ @Test
+ internal fun testOnClear_removesAllDecorations(): Unit {
+ val markerState = MarkerState(sydney)
+ val markerNode = createMarkerNode(markerState)
+ val circle = googleMap.addCircle(CircleOptions().center(sydney).radius(500.0))
+ val circleNode = CircleNode(circle, onCircleClick = {})
+
+ applier.insertBottomUp(0, markerNode)
+ applier.insertBottomUp(1, circleNode)
+
+ assertThat(shadowMap.markers).hasSize(1)
+ assertThat(shadowMap.circles).hasSize(1)
+
+ applier.clear()
+
+ assertThat(shadowMap.markers).isEmpty()
+ assertThat(shadowMap.circles).isEmpty()
+ assertThat(markerState.marker).isNull()
+ }
+}
+// [END maps_compose_map_applier_test]
diff --git a/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.aar b/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.aar
new file mode 100644
index 000000000..637d97107
Binary files /dev/null and b/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.aar differ
diff --git a/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.module b/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.module
new file mode 100644
index 000000000..8c651d719
--- /dev/null
+++ b/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.module
@@ -0,0 +1,116 @@
+{
+ "formatVersion": "1.1",
+ "component": {
+ "group": "com.google.android.maps.robolectric",
+ "module": "shadows",
+ "version": "1.0.0",
+ "attributes": {
+ "org.gradle.status": "release"
+ }
+ },
+ "createdBy": {
+ "gradle": {
+ "version": "9.1.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": "org.jetbrains.kotlin",
+ "module": "kotlin-stdlib",
+ "version": {
+ "requires": "2.3.20"
+ }
+ }
+ ],
+ "files": [
+ {
+ "name": "shadows-1.0.0.aar",
+ "url": "shadows-1.0.0.aar",
+ "size": 50521,
+ "sha512": "be8db1c9b97ded5b3b7598ab58d223df837bcede822c89be78cbc1cd40a93e803f50555479e0c392820214c2ea8dd34864c4686bf3d9c02ed0a1bc77b11b824f",
+ "sha256": "17a2cdbf2948a18133acaaad74145e542b091efc85b154aae47e48829a4f2bb5",
+ "sha1": "379b67474c7045fa5c5bc1ede172390ce40fe075",
+ "md5": "665f37deca4f4c7eacd22e65291bca63"
+ }
+ ]
+ },
+ {
+ "name": "releaseVariantReleaseRuntimePublication",
+ "attributes": {
+ "org.gradle.category": "library",
+ "org.gradle.dependency.bundling": "external",
+ "org.gradle.libraryelements": "aar",
+ "org.gradle.usage": "java-runtime"
+ },
+ "dependencies": [
+ {
+ "group": "org.jetbrains.kotlin",
+ "module": "kotlin-stdlib",
+ "version": {
+ "requires": "2.3.20"
+ }
+ }
+ ],
+ "files": [
+ {
+ "name": "shadows-1.0.0.aar",
+ "url": "shadows-1.0.0.aar",
+ "size": 50521,
+ "sha512": "be8db1c9b97ded5b3b7598ab58d223df837bcede822c89be78cbc1cd40a93e803f50555479e0c392820214c2ea8dd34864c4686bf3d9c02ed0a1bc77b11b824f",
+ "sha256": "17a2cdbf2948a18133acaaad74145e542b091efc85b154aae47e48829a4f2bb5",
+ "sha1": "379b67474c7045fa5c5bc1ede172390ce40fe075",
+ "md5": "665f37deca4f4c7eacd22e65291bca63"
+ }
+ ]
+ },
+ {
+ "name": "releaseVariantReleaseSourcePublication",
+ "attributes": {
+ "org.gradle.category": "documentation",
+ "org.gradle.dependency.bundling": "external",
+ "org.gradle.docstype": "sources",
+ "org.gradle.usage": "java-runtime"
+ },
+ "files": [
+ {
+ "name": "shadows-1.0.0-sources.jar",
+ "url": "shadows-1.0.0-sources.jar",
+ "size": 31416,
+ "sha512": "3f1714d5f352dc5996d90c0ace16f38566253fe3b49f1b8d24e444571750b6e5039c0631463a92cb3cea7c5f9ac90e2096e4e66272899d627dea3a82542f6783",
+ "sha256": "b442bdd6c6ac88128058c5af4a28288b827432731a68624012764d15973972c0",
+ "sha1": "563b547330cd1701f1a1f15b39cde1fa73554aa0",
+ "md5": "cd96d340bf7af4bea40b4f9631959fc6"
+ }
+ ]
+ },
+ {
+ "name": "releaseVariantReleaseJavaDocPublication",
+ "attributes": {
+ "org.gradle.category": "documentation",
+ "org.gradle.dependency.bundling": "external",
+ "org.gradle.docstype": "javadoc",
+ "org.gradle.usage": "java-runtime"
+ },
+ "files": [
+ {
+ "name": "shadows-1.0.0-javadoc.jar",
+ "url": "shadows-1.0.0-javadoc.jar",
+ "size": 358915,
+ "sha512": "5ed9457edc6b3f24cdc7db19a7b3a5cb49d34322c18e81a47b7a21ba1587a053571a33a34ecda00950723fa5b700cbe775a828434499f1e5c7b93b32d3a9e54c",
+ "sha256": "b5ce9bfff24d3efab5eadc18d70ce3b9d61733e7b562aae078cbc9163435d4bd",
+ "sha1": "d0f4bcddae95c27f268fc77fc9e062b90e845448",
+ "md5": "1d822db43393004267bb9f11eb4d62dc"
+ }
+ ]
+ }
+ ]
+}
diff --git a/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.pom b/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.pom
new file mode 100644
index 000000000..e5068d5c6
--- /dev/null
+++ b/repo/com/google/android/maps/robolectric/shadows/1.0.0/shadows-1.0.0.pom
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+ 4.0.0
+ com.google.android.maps.robolectric
+ shadows
+ 1.0.0
+ 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.3.20
+ compile
+
+
+
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 6b9611bcc..c5728497e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -17,6 +17,9 @@
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()
@@ -39,4 +42,19 @@ include(":maps-app")
include(":maps-compose")
include(":maps-compose-widgets")
include(":maps-compose-utils")
-include(":docs")
\ No newline at end of file
+include(":docs")
+
+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"))
+ }
+ }
+}
\ No newline at end of file