@@ -27,10 +27,12 @@ import io.sentry.android.replay.ReplayCache.Companion.SEGMENT_KEY_REPLAY_RECORDI
27
27
import io.sentry.android.replay.ReplayCache.Companion.SEGMENT_KEY_REPLAY_TYPE
28
28
import io.sentry.android.replay.ReplayCache.Companion.SEGMENT_KEY_TIMESTAMP
29
29
import io.sentry.android.replay.ReplayCache.Companion.SEGMENT_KEY_WIDTH
30
+ import io.sentry.android.replay.capture.BufferCaptureStrategy
30
31
import io.sentry.android.replay.capture.CaptureStrategy
31
32
import io.sentry.android.replay.capture.SessionCaptureStrategy
32
33
import io.sentry.android.replay.capture.SessionCaptureStrategyTest.Fixture.Companion.VIDEO_DURATION
33
34
import io.sentry.android.replay.gestures.GestureRecorder
35
+ import io.sentry.android.replay.util.ReplayShadowMediaCodec
34
36
import io.sentry.cache.PersistingScopeObserver
35
37
import io.sentry.cache.tape.QueueFile
36
38
import io.sentry.protocol.SentryException
@@ -43,6 +45,7 @@ import io.sentry.rrweb.RRWebVideoEvent
43
45
import io.sentry.transport.CurrentDateProvider
44
46
import io.sentry.transport.ICurrentDateProvider
45
47
import io.sentry.transport.RateLimiter
48
+ import io.sentry.util.Random
46
49
import java.io.ByteArrayOutputStream
47
50
import java.io.File
48
51
import kotlin.test.BeforeTest
@@ -63,13 +66,14 @@ import org.mockito.kotlin.doAnswer
63
66
import org.mockito.kotlin.eq
64
67
import org.mockito.kotlin.mock
65
68
import org.mockito.kotlin.never
69
+ import org.mockito.kotlin.reset
66
70
import org.mockito.kotlin.times
67
71
import org.mockito.kotlin.verify
68
72
import org.mockito.kotlin.whenever
69
73
import org.robolectric.annotation.Config
70
74
71
75
@RunWith(AndroidJUnit4 ::class )
72
- @Config(sdk = [26 ])
76
+ @Config(sdk = [26 ], shadows = [ ReplayShadowMediaCodec :: class ] )
73
77
class ReplayIntegrationTest {
74
78
@get:Rule val tmpDir = TemporaryFolder ()
75
79
@@ -726,6 +730,58 @@ class ReplayIntegrationTest {
726
730
verify(recorder).resume()
727
731
}
728
732
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
+
729
785
@Test
730
786
fun `closed replay cannot be started` () {
731
787
val replay = fixture.getSut(context)
0 commit comments