@@ -27,10 +27,12 @@ import io.sentry.android.replay.ReplayCache.Companion.SEGMENT_KEY_REPLAY_RECORDI
2727import io.sentry.android.replay.ReplayCache.Companion.SEGMENT_KEY_REPLAY_TYPE
2828import io.sentry.android.replay.ReplayCache.Companion.SEGMENT_KEY_TIMESTAMP
2929import io.sentry.android.replay.ReplayCache.Companion.SEGMENT_KEY_WIDTH
30+ import io.sentry.android.replay.capture.BufferCaptureStrategy
3031import io.sentry.android.replay.capture.CaptureStrategy
3132import io.sentry.android.replay.capture.SessionCaptureStrategy
3233import io.sentry.android.replay.capture.SessionCaptureStrategyTest.Fixture.Companion.VIDEO_DURATION
3334import io.sentry.android.replay.gestures.GestureRecorder
35+ import io.sentry.android.replay.util.ReplayShadowMediaCodec
3436import io.sentry.cache.PersistingScopeObserver
3537import io.sentry.cache.tape.QueueFile
3638import io.sentry.protocol.SentryException
@@ -43,6 +45,7 @@ import io.sentry.rrweb.RRWebVideoEvent
4345import io.sentry.transport.CurrentDateProvider
4446import io.sentry.transport.ICurrentDateProvider
4547import io.sentry.transport.RateLimiter
48+ import io.sentry.util.Random
4649import java.io.ByteArrayOutputStream
4750import java.io.File
4851import kotlin.test.BeforeTest
@@ -63,13 +66,14 @@ import org.mockito.kotlin.doAnswer
6366import org.mockito.kotlin.eq
6467import org.mockito.kotlin.mock
6568import org.mockito.kotlin.never
69+ import org.mockito.kotlin.reset
6670import org.mockito.kotlin.times
6771import org.mockito.kotlin.verify
6872import org.mockito.kotlin.whenever
6973import org.robolectric.annotation.Config
7074
7175@RunWith(AndroidJUnit4 ::class )
72- @Config(sdk = [26 ])
76+ @Config(sdk = [26 ], shadows = [ ReplayShadowMediaCodec :: class ] )
7377class ReplayIntegrationTest {
7478 @get:Rule val tmpDir = TemporaryFolder ()
7579
@@ -726,6 +730,58 @@ class ReplayIntegrationTest {
726730 verify(recorder).resume()
727731 }
728732
733+ @Test
734+ fun `continues recording after converting to session strategy without extra config change` () {
735+ // Force buffer mode at start, but enable onError sample so captureReplay triggers
736+ val recorder = mock<Recorder >()
737+ val replay =
738+ fixture.getSut(
739+ context,
740+ recorderProvider = { recorder },
741+ replayCaptureStrategyProvider = { isFullSession ->
742+ // Always start with buffer strategy regardless of sampling
743+ BufferCaptureStrategy (
744+ fixture.options,
745+ fixture.scopes,
746+ // make time jump so session strategy will immediately cut a segment on next frame
747+ ICurrentDateProvider {
748+ System .currentTimeMillis() + fixture.options.sessionReplay.sessionSegmentDuration
749+ },
750+ Random (),
751+ // run tasks synchronously in tests
752+ mock {
753+ doAnswer { (it.arguments[0 ] as Runnable ).run () }
754+ .whenever(mock)
755+ .submit(any<Runnable >())
756+ },
757+ ) { _ ->
758+ fixture.replayCache
759+ }
760+ },
761+ )
762+
763+ fixture.options.sessionReplay.sessionSampleRate = 0.0 // ensure buffer mode initially
764+ fixture.options.sessionReplay.onErrorSampleRate = 1.0
765+ fixture.options.cacheDirPath = tmpDir.newFolder().absolutePath
766+
767+ replay.register(fixture.scopes, fixture.options)
768+ replay.start()
769+
770+ val config = ScreenshotRecorderConfig (100 , 200 , 1f , 1f , 1 , 20_000 )
771+ replay.onConfigurationChanged(config)
772+
773+ // Trigger convert() via captureReplay
774+ replay.captureReplay(false )
775+
776+ // Now, without invoking another config change, record a frame
777+ // Reset interactions to assert only post-convert capture
778+ reset(fixture.scopes)
779+ replay.onScreenshotRecorded(mock())
780+
781+ // Should capture a session segment after conversion without additional config changes
782+ verify(fixture.scopes).captureReplay(any(), any())
783+ }
784+
729785 @Test
730786 fun `closed replay cannot be started` () {
731787 val replay = fixture.getSut(context)
0 commit comments