Skip to content

Commit 7c4b52e

Browse files
committed
test(profiling): Add unit test for PerfettoContinuousProfiler rate limiting
Verify that onRateLimitChanged stops the profiler, resets profiler/chunk IDs, and logs the expected warning. Run with: ./gradlew :sentry-android-core:testDebugUnitTest --tests "io.sentry.android.core.PerfettoContinuousProfilerTest"
1 parent 02fd189 commit 7c4b52e

1 file changed

Lines changed: 110 additions & 0 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package io.sentry.android.core
2+
3+
import android.content.Context
4+
import android.os.Build
5+
import androidx.test.core.app.ApplicationProvider
6+
import androidx.test.ext.junit.runners.AndroidJUnit4
7+
import io.sentry.DataCategory
8+
import io.sentry.ILogger
9+
import io.sentry.IScopes
10+
import io.sentry.ProfileLifecycle
11+
import io.sentry.Sentry
12+
import io.sentry.SentryLevel
13+
import io.sentry.TracesSampler
14+
import io.sentry.protocol.SentryId
15+
import io.sentry.test.DeferredExecutorService
16+
import io.sentry.transport.RateLimiter
17+
import kotlin.test.AfterTest
18+
import kotlin.test.BeforeTest
19+
import kotlin.test.Test
20+
import kotlin.test.assertEquals
21+
import kotlin.test.assertFalse
22+
import kotlin.test.assertTrue
23+
import org.junit.runner.RunWith
24+
import org.mockito.Mockito.mockStatic
25+
import org.mockito.kotlin.any
26+
import org.mockito.kotlin.eq
27+
import org.mockito.kotlin.mock
28+
import org.mockito.kotlin.spy
29+
import org.mockito.kotlin.verify
30+
import org.mockito.kotlin.whenever
31+
32+
@RunWith(AndroidJUnit4::class)
33+
class PerfettoContinuousProfilerTest {
34+
private lateinit var context: Context
35+
private val fixture = Fixture()
36+
37+
private class Fixture {
38+
private val mockDsn = "http://key@localhost/proj"
39+
val buildInfo =
40+
mock<BuildInfoProvider> {
41+
whenever(it.sdkInfoVersion).thenReturn(Build.VERSION_CODES.VANILLA_ICE_CREAM)
42+
}
43+
val executor = DeferredExecutorService()
44+
val mockedSentry = mockStatic(Sentry::class.java)
45+
val mockLogger = mock<ILogger>()
46+
val mockTracesSampler = mock<TracesSampler>()
47+
val mockPerfettoProfiler = mock<PerfettoProfiler>()
48+
49+
val scopes: IScopes = mock()
50+
51+
val options =
52+
spy(SentryAndroidOptions()).apply {
53+
dsn = mockDsn
54+
profilesSampleRate = 1.0
55+
isDebug = true
56+
setLogger(mockLogger)
57+
}
58+
59+
init {
60+
whenever(mockTracesSampler.sampleSessionProfile(any())).thenReturn(true)
61+
whenever(mockPerfettoProfiler.start(any())).thenReturn(
62+
AndroidProfiler.ProfileStartData(
63+
System.nanoTime(),
64+
0L,
65+
io.sentry.DateUtils.getCurrentDateTime(),
66+
),
67+
)
68+
}
69+
70+
fun getSut(): PerfettoContinuousProfiler {
71+
options.executorService = executor
72+
whenever(scopes.options).thenReturn(options)
73+
return PerfettoContinuousProfiler(
74+
buildInfo,
75+
mockLogger,
76+
{ options.executorService },
77+
{ mockPerfettoProfiler },
78+
)
79+
}
80+
}
81+
82+
@BeforeTest
83+
fun `set up`() {
84+
context = ApplicationProvider.getApplicationContext()
85+
Sentry.setCurrentScopes(fixture.scopes)
86+
fixture.mockedSentry.`when`<Any> { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes)
87+
}
88+
89+
@AfterTest
90+
fun clear() {
91+
fixture.mockedSentry.close()
92+
}
93+
94+
@Test
95+
fun `profiler stops when rate limited`() {
96+
val profiler = fixture.getSut()
97+
val rateLimiter = mock<RateLimiter>()
98+
whenever(rateLimiter.isActiveForCategory(DataCategory.ProfileChunkUi)).thenReturn(true)
99+
100+
profiler.startProfiler(ProfileLifecycle.MANUAL, fixture.mockTracesSampler)
101+
assertTrue(profiler.isRunning)
102+
103+
profiler.onRateLimitChanged(rateLimiter)
104+
assertFalse(profiler.isRunning)
105+
assertEquals(SentryId.EMPTY_ID, profiler.profilerId)
106+
assertEquals(SentryId.EMPTY_ID, profiler.chunkId)
107+
verify(fixture.mockLogger)
108+
.log(eq(SentryLevel.WARNING), eq("SDK is rate limited. Stopping profiler."))
109+
}
110+
}

0 commit comments

Comments
 (0)