Skip to content

Commit b95b8af

Browse files
Use appLaunchedInForeground to determine invalid app start data on Android (#4146)
* Check if appLaunchedInForeground and launch duration for native app start --------- Co-authored-by: LucasZF <[email protected]>
1 parent f31859f commit b95b8af

File tree

3 files changed

+107
-2
lines changed

3 files changed

+107
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixes
66

7+
- Use `appLaunchedInForeground` to determine invalid app start data on Android ([#4146](https://github.com/getsentry/sentry-react-native/pull/4146))
78
- Upload source maps for all release variants on Android (not only the last found) ([#4125](https://github.com/getsentry/sentry-react-native/pull/4125))
89

910
### Dependencies
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package io.sentry.react
2+
3+
import android.content.pm.PackageInfo
4+
import android.content.pm.PackageManager
5+
import com.facebook.react.bridge.Arguments
6+
import com.facebook.react.bridge.Promise
7+
import com.facebook.react.bridge.ReactApplicationContext
8+
import com.facebook.react.bridge.WritableMap
9+
import io.sentry.ILogger
10+
import io.sentry.SentryLevel
11+
import org.junit.After
12+
import org.junit.Assert.assertEquals
13+
import org.junit.Before
14+
import org.junit.Test
15+
import org.junit.runner.RunWith
16+
import org.junit.runners.JUnit4
17+
import org.mockito.ArgumentCaptor
18+
import org.mockito.Captor
19+
import org.mockito.Mockito.*
20+
import org.mockito.MockitoAnnotations
21+
import org.mockito.MockedStatic
22+
import org.mockito.Mockito.mockStatic
23+
import org.mockito.kotlin.whenever
24+
25+
@RunWith(JUnit4::class)
26+
class RNSentryModuleImplTest {
27+
28+
private lateinit var module: RNSentryModuleImpl
29+
private lateinit var promise: Promise
30+
private lateinit var logger: ILogger
31+
private var argumentsMock: MockedStatic<Arguments>? = null
32+
33+
@Captor
34+
private lateinit var writableMapCaptor: ArgumentCaptor<WritableMap>
35+
36+
@Before
37+
fun setUp() {
38+
MockitoAnnotations.openMocks(this)
39+
val reactContext = mock(ReactApplicationContext::class.java)
40+
promise = mock(Promise::class.java)
41+
logger = mock(ILogger::class.java)
42+
val packageManager = mock(PackageManager::class.java)
43+
val packageInfo = mock(PackageInfo::class.java)
44+
45+
whenever(reactContext.packageManager).thenReturn(packageManager)
46+
whenever(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo)
47+
48+
module = RNSentryModuleImpl(reactContext)
49+
50+
// Mock the Arguments class
51+
argumentsMock = mockStatic(Arguments::class.java)
52+
val writableMap = mock(WritableMap::class.java)
53+
whenever(Arguments.createMap()).thenReturn(writableMap)
54+
}
55+
56+
@After
57+
fun tearDown() {
58+
argumentsMock?.close()
59+
}
60+
61+
@Test
62+
fun `fetchNativeAppStart resolves promise with null when app is not launched in the foreground`() {
63+
// Mock the app start measurement
64+
val appStartMeasurement = mapOf<String, Any>()
65+
66+
// Call the method
67+
module.fetchNativeAppStart(promise, appStartMeasurement, logger, false)
68+
69+
// Verify a warning log is emitted
70+
verify(logger, org.mockito.kotlin.times(1)).log(
71+
SentryLevel.WARNING,
72+
"Invalid app start data: app not launched in foreground."
73+
)
74+
75+
// Verify the promise is resolved with null
76+
verify(promise).resolve(null)
77+
}
78+
79+
@Test
80+
fun `fetchNativeAppStart resolves promise with app start data when app is launched in the foreground`() {
81+
// Mock the app start measurement
82+
val appStartMeasurement = mapOf<String, Any>()
83+
84+
// Call the method
85+
module.fetchNativeAppStart(promise, appStartMeasurement, logger, true)
86+
87+
// Verify no logs are emitted
88+
verify(logger, org.mockito.kotlin.times(0)).log(any(), any())
89+
90+
// Verify the promise is resolved with the expected data
91+
verify(promise).resolve(any(WritableMap::class.java))
92+
verify(promise).resolve(writableMapCaptor.capture())
93+
val capturedMap = writableMapCaptor.value
94+
assertEquals(false, capturedMap.getBoolean("has_fetched"))
95+
}
96+
}

android/src/main/java/io/sentry/react/RNSentryModuleImpl.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -380,9 +380,17 @@ public void fetchNativeRelease(Promise promise) {
380380
}
381381

382382
public void fetchNativeAppStart(Promise promise) {
383-
final Map<String, Object> measurement = InternalSentrySdk.getAppStartMeasurement();
383+
fetchNativeAppStart(promise, InternalSentrySdk.getAppStartMeasurement(), logger, AppStartMetrics.getInstance().isAppLaunchedInForeground());
384+
}
385+
386+
protected void fetchNativeAppStart(Promise promise, final Map<String, Object> appStartMeasurement, ILogger logger, boolean isAppLaunchedInForeground) {
387+
if (!isAppLaunchedInForeground) {
388+
logger.log(SentryLevel.WARNING, "Invalid app start data: app not launched in foreground.");
389+
promise.resolve(null);
390+
return;
391+
}
384392

385-
WritableMap mutableMeasurement = (WritableMap) RNSentryMapConverter.convertToWritable(measurement);
393+
WritableMap mutableMeasurement = (WritableMap) RNSentryMapConverter.convertToWritable(appStartMeasurement);
386394
mutableMeasurement.putBoolean("has_fetched", hasFetchedAppStart);
387395

388396
// This is always set to true, as we would only allow an app start fetch to only

0 commit comments

Comments
 (0)