From 5f362d1b5140d2dc88eeaa970fd70a5486559ddf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Oct 2025 18:41:01 +0000 Subject: [PATCH 1/2] Initial plan From 9d39efb535618499ee67f223fe5613a1a56325aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Oct 2025 18:52:21 +0000 Subject: [PATCH 2/2] Fix infinite recursion in TileSet.kt setBounds() and add test coverage Co-authored-by: louwers <649392+louwers@users.noreply.github.com> --- .../maplibre/android/style/sources/TileSet.kt | 2 +- .../android/style/sources/TileSetTest.kt | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/style/sources/TileSetTest.kt diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/sources/TileSet.kt b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/sources/TileSet.kt index d2c9b45dfb2b..9c9f81c37983 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/sources/TileSet.kt +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/sources/TileSet.kt @@ -204,7 +204,7 @@ class TileSet(val tilejson: String, vararg tiles: String) { * @param top the Float top bound */ fun setBounds(left: Float, bottom: Float, right: Float, top: Float) { - setBounds(left, bottom, right, top) + this.bounds = arrayOf(left, bottom, right, top) } /** diff --git a/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/style/sources/TileSetTest.kt b/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/style/sources/TileSetTest.kt new file mode 100644 index 000000000000..63bebe0dad06 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/style/sources/TileSetTest.kt @@ -0,0 +1,105 @@ +package org.maplibre.android.style.sources + +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.maplibre.android.geometry.LatLngBounds +import org.robolectric.RobolectricTestRunner + +/** + * Test for TileSet class to validate bounds setting functionality + */ +@RunWith(RobolectricTestRunner::class) +class TileSetTest { + + @Test + fun testSetBoundsWithFourFloats() { + val tileSet = TileSet("2.1.0", "https://example.com/{z}/{x}/{y}.png") + + // Test setting bounds with four float parameters + tileSet.setBounds(-180f, -90f, 180f, 90f) + + assertNotNull("Bounds should be set", tileSet.bounds) + assertEquals("Bounds should have 4 elements", 4, tileSet.bounds?.size) + assertEquals("Left bound should be -180", -180f, tileSet.bounds?.get(0)) + assertEquals("Bottom bound should be -90", -90f, tileSet.bounds?.get(1)) + assertEquals("Right bound should be 180", 180f, tileSet.bounds?.get(2)) + assertEquals("Top bound should be 90", 90f, tileSet.bounds?.get(3)) + } + + @Test + fun testSetBoundsWithVarargFloats() { + val tileSet = TileSet("2.1.0", "https://example.com/{z}/{x}/{y}.png") + + // Test setting bounds with vararg floats + tileSet.setBounds(-10f, -20f, 30f, 40f) + + assertNotNull("Bounds should be set", tileSet.bounds) + assertEquals("Bounds should have 4 elements", 4, tileSet.bounds?.size) + assertEquals("Left bound should be -10", -10f, tileSet.bounds?.get(0)) + assertEquals("Bottom bound should be -20", -20f, tileSet.bounds?.get(1)) + assertEquals("Right bound should be 30", 30f, tileSet.bounds?.get(2)) + assertEquals("Top bound should be 40", 40f, tileSet.bounds?.get(3)) + } + + @Test + fun testSetBoundsWithArray() { + val tileSet = TileSet("2.1.0", "https://example.com/{z}/{x}/{y}.png") + + // Test setting bounds with array + val boundsArray = arrayOf(12f, 34f, 56f, 78f) + tileSet.setBounds(boundsArray) + + assertNotNull("Bounds should be set", tileSet.bounds) + assertEquals("Bounds should have 4 elements", 4, tileSet.bounds?.size) + assertArrayEquals("Bounds should match input array", boundsArray, tileSet.bounds) + } + + @Test + fun testSetBoundsWithLatLngBounds() { + val tileSet = TileSet("2.1.0", "https://example.com/{z}/{x}/{y}.png") + + // Test setting bounds with LatLngBounds + val latLngBounds = LatLngBounds.Builder() + .include(org.maplibre.android.geometry.LatLng(12.0, 34.0)) + .include(org.maplibre.android.geometry.LatLng(56.0, 78.0)) + .build() + + tileSet.setBounds(latLngBounds) + + assertNotNull("Bounds should be set", tileSet.bounds) + assertEquals("Bounds should have 4 elements", 4, tileSet.bounds?.size) + assertEquals("Left bound should match", latLngBounds.longitudeWest.toFloat(), tileSet.bounds?.get(0)) + assertEquals("Bottom bound should match", latLngBounds.latitudeSouth.toFloat(), tileSet.bounds?.get(1)) + assertEquals("Right bound should match", latLngBounds.longitudeEast.toFloat(), tileSet.bounds?.get(2)) + assertEquals("Top bound should match", latLngBounds.latitudeNorth.toFloat(), tileSet.bounds?.get(3)) + } + + @Test + fun testSetBoundsNoStackOverflow() { + val tileSet = TileSet("2.1.0", "https://example.com/{z}/{x}/{y}.png") + + // This test verifies the fix for the infinite recursion bug + // If the bug still exists, this will cause a StackOverflowError + tileSet.setBounds(1f, 2f, 3f, 4f) + + assertNotNull("Bounds should be set without stack overflow", tileSet.bounds) + assertEquals("Should have 4 elements", 4, tileSet.bounds?.size) + } + + @Test + fun testMultipleSetBoundsCalls() { + val tileSet = TileSet("2.1.0", "https://example.com/{z}/{x}/{y}.png") + + // Test multiple calls to setBounds + tileSet.setBounds(-180f, -90f, 180f, 90f) + assertNotNull("First bounds should be set", tileSet.bounds) + + tileSet.setBounds(-10f, -20f, 30f, 40f) + assertNotNull("Second bounds should be set", tileSet.bounds) + assertEquals("Left bound should be updated", -10f, tileSet.bounds?.get(0)) + assertEquals("Bottom bound should be updated", -20f, tileSet.bounds?.get(1)) + assertEquals("Right bound should be updated", 30f, tileSet.bounds?.get(2)) + assertEquals("Top bound should be updated", 40f, tileSet.bounds?.get(3)) + } +}