diff --git a/CHANGELOG.md b/CHANGELOG.md index ca613369..d5561cb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ # Changelog +## Unreleased + ## Features - Add `proguardUuid` option to `SentryOptions` ([#436](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/436)) - This will propagate the `proguardUuid` value to Sentry Android +### Fixes + +- Fix stack trace crash on Apple targets ([#434](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/434)) + ## 0.17.1 ### Fixes diff --git a/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/Throwable.kt b/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/Throwable.kt index 8fec98f7..27d8f8ec 100644 --- a/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/Throwable.kt +++ b/sentry-kotlin-multiplatform/src/appleMain/kotlin/io/sentry/kotlin/multiplatform/nsexception/Throwable.kt @@ -77,7 +77,8 @@ internal fun List.dropCommonAddresses( ): List { var i = commonAddresses.size if (i == 0) return this + return dropLastWhile { - i-- >= 0 && commonAddresses[i] == it + i-- > 0 && commonAddresses[i] == it } } diff --git a/sentry-kotlin-multiplatform/src/appleTest/kotlin/io/sentry/kotlin/multiplatform/nsexception/CommonAddressesTests.kt b/sentry-kotlin-multiplatform/src/appleTest/kotlin/io/sentry/kotlin/multiplatform/nsexception/CommonAddressesTests.kt index c157eb30..9fb7813e 100644 --- a/sentry-kotlin-multiplatform/src/appleTest/kotlin/io/sentry/kotlin/multiplatform/nsexception/CommonAddressesTests.kt +++ b/sentry-kotlin-multiplatform/src/appleTest/kotlin/io/sentry/kotlin/multiplatform/nsexception/CommonAddressesTests.kt @@ -19,7 +19,6 @@ import kotlin.test.assertEquals import kotlin.test.assertSame class CommonAddressesTests { - @Test fun testDropCommon() { val commonAddresses = listOf(5, 4, 3, 2, 1, 0) @@ -28,6 +27,30 @@ class CommonAddressesTests { assertEquals(listOf(8, 7, 6), withoutCommonAddresses) } + @Test + fun testDropCommonPartialMatch() { + val commonAddresses = listOf(3, 2, 1) + val addresses = listOf(9, 8, 7, 2, 1) + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(9, 8, 7), withoutCommonAddresses) + } + + @Test + fun testDropCommonNoMatch() { + val commonAddresses = listOf(5, 4, 3) + val addresses = listOf(9, 8, 7, 6) + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(addresses, withoutCommonAddresses) + } + + @Test + fun testDropCommonSingleElementMatch() { + val commonAddresses = listOf(1) + val addresses = listOf(5, 4, 3, 1) + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(5, 4, 3), withoutCommonAddresses) + } + @Test fun testDropCommonEmptyCommon() { val addresses = listOf(0, 1, 2) @@ -35,10 +58,111 @@ class CommonAddressesTests { assertSame(addresses, withoutCommonAddresses) } + @Test + fun testDropCommonEmptyAddresses() { + val commonAddresses = listOf(1, 2, 3) + val addresses = emptyList() + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertSame(addresses, withoutCommonAddresses) + } + @Test fun testDropCommonSameAddresses() { val addresses = listOf(0, 1, 2) val withoutCommonAddresses = addresses.dropCommonAddresses(addresses) assertEquals(emptyList(), withoutCommonAddresses) } + + @Test + fun testDropCommonLargerCommonList() { + val commonAddresses = listOf(5, 4, 3, 2, 1, 0) + val addresses = listOf(2, 1, 0) + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(emptyList(), withoutCommonAddresses) + } + + @Test + fun testDropCommonLargerAddressList() { + val commonAddresses = listOf(2, 1, 0) + val addresses = listOf(9, 8, 7, 6, 5, 4, 2, 1, 0) + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(9, 8, 7, 6, 5, 4), withoutCommonAddresses) + } + + @Test + fun testDropCommonNoIndexOutOfBounds_LargeCommonList() { + // This test specifically targets the original bug where i-- could become -1 + val commonAddresses = (0L..26L).toList().reversed() // 27 elements: [26, 25, ..., 1, 0] + val addresses = listOf(30, 29, 28, 2, 1, 0) + + // This should not throw IndexOutOfBoundsException + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(30, 29, 28), withoutCommonAddresses) + } + + @Test + fun testDropCommonNoIndexOutOfBounds_ExactSizeMatch() { + // Test when commonAddresses.size equals the number of matching elements + val commonAddresses = listOf(4, 3, 2, 1, 0) + val addresses = listOf(9, 8, 7, 4, 3, 2, 1, 0) + + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(9, 8, 7), withoutCommonAddresses) + } + + @Test + fun testDropCommonNoIndexOutOfBounds_OffByOneScenario() { + // Test the exact scenario that caused the original crash + val commonAddresses = (1L..27L).toList() // size: 27 + val addresses = listOf(100L, 99L, 98L) + commonAddresses.takeLast(3) + + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(100L, 99L, 98L), withoutCommonAddresses) + } + + @Test + fun testDropCommonRepeatedElements() { + val commonAddresses = listOf(1, 1, 1, 0, 0) + val addresses = listOf(5, 4, 1, 1, 0) + + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(5, 4, 1, 1), withoutCommonAddresses) + } + + @Test + fun testDropCommonMaintainsOrder() { + val commonAddresses = listOf(3, 2, 1) + val addresses = listOf(9, 8, 7, 6, 5, 3, 2, 1) + + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(9, 8, 7, 6, 5), withoutCommonAddresses) + } + + @Test + fun testDropCommonOutOfSequenceMatch() { + // Common addresses are in different order than in the target list + val commonAddresses = listOf(1, 3, 2) // Note: 3 and 2 are swapped + val addresses = listOf(9, 8, 7, 6, 2) + + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(9, 8, 7, 6), withoutCommonAddresses) // Should drop 2 + } + + @Test + fun testDropCommonMixedPositiveNegative() { + val commonAddresses = listOf(1, 0, -1) + val addresses = listOf(5, 4, 3, 1, 0, -1) + + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(5, 4, 3), withoutCommonAddresses) + } + + @Test + fun testDropCommonLargeNumbers() { + val commonAddresses = listOf(Long.MAX_VALUE - 1, Long.MAX_VALUE - 2) + val addresses = listOf(Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2) + + val withoutCommonAddresses = addresses.dropCommonAddresses(commonAddresses) + assertEquals(listOf(Long.MAX_VALUE), withoutCommonAddresses) + } }