Media3, टेस्टिंग के लिए खास आर्टफ़ैक्ट (media3-test-utils और media3-test-utils-robolectric) उपलब्ध कराता है. इनकी मदद से, मीडिया प्लेबैक, प्लेयर की स्थिति में होने वाले बदलावों, यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट, और कस्टम मीडिया सोर्स को टेस्ट किया जा सकता है. इन्हें लोकल जेवीएम टेस्ट और डिवाइस इंस्ट्रुमेंटेशन टेस्ट, दोनों में टेस्ट किया जा सकता है.
इस पेज पर, ExoPlayer का इस्तेमाल करने वाले ऐप्लिकेशन की जांच करने के लिए सुझाए गए तरीके बताए गए हैं. साथ ही, यह भी बताया गया है कि हर टेस्ट एनवायरमेंट में Media3 की टेस्टिंग यूटिलिटी का इस्तेमाल कैसे किया जाता है.
टेस्टिंग के लिए सुझाव
मीडिया प्लेबैक के लिए ऑटोमेटेड टेस्ट लिखते समय, इन मुख्य सिद्धांतों का पालन करें:
- मॉक के बजाय, असली कॉम्पोनेंट या Media3 के नकली कॉम्पोनेंट को प्राथमिकता दें: Mockito जैसे फ़्रेमवर्क का इस्तेमाल करके,
PlayerयाExoPlayerको मॉक करने से बचें.Playerमें स्टेट इनवेरियंट और लिसनर कॉन्ट्रैक्ट होते हैं, जिन्हें मॉक लागू नहीं करते. इससे टेस्ट कमज़ोर हो सकते हैं या गड़बड़ियां छिप सकती हैं. इसके बजाय, असलExoPlayerइंस्टेंस,FakePlayerया नकली इंस्टेंस इस्तेमाल करें. जैसे,FakeMediaSourceऔरFakeDataSource. ज़रूरत पड़ने पर, कस्टम कॉलबैक को शुरू करने की पुष्टि करने के लिए, मॉक रिज़र्व करें. - स्लीप मोड के बजाय डिटरमिनिस्टिक तरीके से इंतज़ार करना:
ExoPlayer, इंटरनल थ्रेड पर मीडिया को एसिंक्रोनस तरीके से लोड और प्ले करता है.Thread.sleepके लिए मनमाने कॉल करने से बचें. इनसे टेस्ट धीमे और अस्थिर हो जाते हैं. Robolectric टेस्ट मेंTestPlayerRunHelperका इस्तेमाल करें. साथ ही, इंस्ट्रुमेंटेशन टेस्ट मेंPlayerएक्सटेंशन याPlayerFenceको निलंबित करें. ये हेल्पर, प्लेयर की खास स्थितियों का इंतज़ार करते हैं. साथ ही, वीडियो चलाने में कोई गड़बड़ी होने पर, तुरंत काम करना बंद कर देते हैं. - प्लेयर को उसके ऐप्लिकेशन थ्रेड पर ऐक्सेस करें:
ExoPlayerइंस्टेंस और वेटिंग हेल्पर को प्लेयर के ऐप्लिकेशनLooperथ्रेड (आम तौर पर मुख्य थ्रेड) पर कॉल किया जाना चाहिए. ज़्यादा जानकारी के लिए, ExoPlayer थ्रेडिंग से जुड़ी ज़रूरी शर्तें देखें.
सही टेस्ट एनवायरमेंट चुनना
मीडिया इंटिग्रेशन की अलग-अलग लेयर को, अलग-अलग टेस्ट एनवायरमेंट से फ़ायदा मिलता है:
| टेस्ट टारगेट | सुझाया गया एनवायरमेंट | Media3 की मुख्य सुविधाएं |
|---|---|---|
यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट (PlayerView, Compose यूज़र इंटरफ़ेस), ViewModels या MediaSession कॉलबैक |
लोकल Robolectric टेस्ट (src/test) या इंस्ट्रूमेंटेड यूज़र इंटरफ़ेस (यूआई) टेस्ट (src/androidTest) |
FakePlayer |
ExoPlayer इंटिग्रेशन, प्लेलिस्ट ट्रांज़िशन, आंकड़े, और कस्टम MediaSource या DataSource लॉजिक |
लोकल रोबोलेक्ट्रिक टेस्ट (src/test) |
TestExoPlayerBuilder, TestPlayerRunHelper, FakeMediaSource |
असली हार्डवेयर MediaCodec डिकोडिंग, Surface रेंडरिंग, ऑडियो आउटपुट, और डीआरएम |
फ़िज़िकल डिवाइसों पर इंस्ट्रुमेंटेशन टेस्ट (src/androidTest) |
ExoPlayer.Builder, Player.await* एक्सटेंशन, PlayerFence |
जहां तक हो सके, एम्युलेटर के बजाय फ़िज़िकल डिवाइसों पर इंस्ट्रुमेंटेशन प्लेबैक टेस्ट चलाएं. ऐसा इसलिए, क्योंकि एम्युलेटर मीडिया स्टैक को अलग-अलग तरीके से लागू किया जाता है. एम्युलेटर के साथ काम करने वाले डिवाइसों के बारे में ज़्यादा जानकारी के लिए, ऐसे डिवाइसों की गाइड देखें जिन पर Google Play काम करता है.
टेस्टिंग डिपेंडेंसी जोड़ना
Media3 के टेस्ट आर्टफ़ैक्ट को अपने मॉड्यूल की build.gradle.kts या build.gradle फ़ाइल के dependencies ब्लॉक में जोड़ें. इसके लिए, वही वर्शन इस्तेमाल करें जो आपके अन्य Media3 मॉड्यूल के लिए इस्तेमाल किया गया है:
Kotlin
testImplementation("androidx.media3:media3-test-utils:1.11.1")
testImplementation("androidx.media3:media3-test-utils-robolectric:1.11.1")
androidTestImplementation("androidx.media3:media3-test-utils:1.11.1")
Groovy
testImplementation "androidx.media3:media3-test-utils:1.11.1"
testImplementation "androidx.media3:media3-test-utils-robolectric:1.11.1"
androidTestImplementation "androidx.media3:media3-test-utils:1.11.1"
दोनों मॉड्यूल, रनटाइम एनवायरमेंट के हिसाब से ज़िम्मेदारियों को बांटते हैं:
media3-test-utils: लोकल और इंस्ट्रुमेंटेड टेस्ट, दोनों के लिए क्रॉस-एनवायरमेंट यूटिलिटी. इनमें ये शामिल हैं:TestExoPlayerBuilder,PlayerFence(और KotlinPlayer.await*एक्सटेंशन),FakePlayer,FakeClock,FakeMediaSource,FakeDataSource,DataSourceContractTest, औरMedia3FlagsRule.media3-test-utils-robolectric: लोकल जेवीएम पर Robolectric टेस्ट के लिए यूटिलिटी. इनमेंTestPlayerRunHelper,RobolectricUtil,ShadowMediaCodecConfig,CapturingRenderersFactory, औरPlaybackOutputशामिल हैं.
पक्का करें कि आपके मॉड्यूल में, टेस्ट एनवायरमेंट के लिए टेस्ट फ़्रेमवर्क डिपेंडेंसी भी शामिल हों:
- Robolectric:
org.robolectric:robolectric. लोकल ऐसेट लोड करते समय,android.testOptions.unitTests.isIncludeAndroidResources = trueको सेट करें. - AndroidX Test:
androidx.test:runner,androidx.test:core, औरandroidx.test.ext:junit. - मुख्य थ्रेड पर Kotlin कोरूटीन:
org.jetbrains.kotlinx:kotlinx-coroutines-android.
टेस्टिंग के लिए उपलब्ध ऐसे एपीआई के लिए ऑप्ट इन करना जो पूरी तरह से काम नहीं करते
media3-test-utils और media3-test-utils-robolectric में मौजूद क्लास और तरीकों को @UnstableApi के साथ एनोटेट किया गया है. Media3 का मकसद, सभी रिलीज़ में ज़्यादा इस्तेमाल होने वाले टेस्ट हेल्पर को आसान बनाना है. हालांकि, टेस्ट यूटिलिटी, बाइनरी फ़ाइल के साथ काम करने की स्थिरता के उन नियमों का पालन नहीं करती हैं जो प्रोडक्शन प्लेबैक एपीआई के लिए लागू होते हैं.
अपने टेस्ट सोर्स सेट में, @androidx.annotation.OptIn का इस्तेमाल करके टेस्ट क्लास या तरीकों के बारे में बताएं. इसके अलावा, अपने टेस्ट पैकेज के लिए package-info शामिल होने का विकल्प जोड़ें या टेस्ट सोर्स के लिए lint.xml को कॉन्फ़िगर करें. ज़्यादा जानकारी के लिए, अनस्टेबल एपीआई लिंट से जुड़ी समस्या हल करने के नोट देखें.
Robolectric की मदद से लोकल टेस्ट
लोकल Robolectric टेस्ट रन, आपकी डेवलपमेंट मशीन के जेवीएम पर चलते हैं. इनके लिए, किसी फ़िज़िकल डिवाइस या एम्युलेटर की ज़रूरत नहीं होती. Robolectric, MediaCodec को लागू करने की सुविधा नहीं देता. साथ ही, यह मुख्य Looper को तब तक रोके रखता है, जब तक कि उसे साफ़ तौर पर पंप न किया जाए. इसलिए, डिफ़ॉल्ट ExoPlayer इंस्टेंस, मीडिया को डिकोड नहीं कर सकता या स्थिति के अपडेट खुद डिलीवर नहीं कर सकता.
टेस्ट प्लेयर बनाना और वीडियो चलाने की सुविधा को बेहतर बनाना
Robolectric टेस्ट के लिए कॉन्फ़िगर किए गए ExoPlayer को बनाने के लिए, TestExoPlayerBuilder का इस्तेमाल करें. डिफ़ॉल्ट रूप से, TestExoPlayerBuilder इन कॉम्पोनेंट को कॉन्फ़िगर करता है:
- अपने-आप आगे बढ़ने वाला
FakeClock(isAutoAdvancing = true) जोExoPlayerके बैकग्राउंड प्लेबैक थ्रेड और Robolectric के रोके गए मुख्यLooperके बीच समय की प्रोग्रेस को कोऑर्डिनेट करता है. - नकली वीडियो और ऑडियो रेंडरर (
FakeVideoRendererऔरFakeAudioRenderer), जोMediaCodecडिकोडर की ज़रूरत के बिना, अपने टाइमस्टैंप के हिसाब से मीडिया सैंपल का इस्तेमाल करते हैं.
प्लेबैक को आगे बढ़ाने और मुख्य थ्रेड पर नतीजों की पुष्टि करने के लिए, TestPlayerRunHelper पर स्टैटिक advance(player) या play(player) तरीकों का इस्तेमाल करें. इसके लिए, Looper.getMainLooper का इस्तेमाल करने वाले प्लेयर का इस्तेमाल करें. यह Robolectric में डिफ़ॉल्ट रूप से मौजूद होता है. play(player), इंतज़ार करने से पहले player.play को कॉल करता है. ज़्यादातर मामलों में, हेल्पर Robolectric के मुख्य Looper को तब तक पंप करता है, जब तक कि टारगेट कंडीशन पूरी नहीं हो जाती. साथ ही, वीडियो चलाने में कोई गंभीर या सामान्य गड़बड़ी होने पर, अपवाद दिखाता है:
Kotlin
@RunWith(AndroidJUnit4::class) @OptIn(UnstableApi::class) class PlaybackRobolectricTest { private lateinit var player: ExoPlayer @After fun tearDown() { if (::player.isInitialized) { player.release() } } @Test fun play_sampleMediaItem_reachesEndedState() { val context = ApplicationProvider.getApplicationContext<Context>() player = TestExoPlayerBuilder(context).build() player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4")) player.prepare() play(player).untilState(Player.STATE_ENDED) assertThat(player.playbackState).isEqualTo(Player.STATE_ENDED) } }
Java
@RunWith(AndroidJUnit4.class) @OptIn(markerClass = UnstableApi.class) public static class PlaybackRobolectricTest { private @Nullable ExoPlayer player; @After public void tearDown() { if (player != null) { player.release(); } } @Test public void play_sampleMediaItem_reachesEndedState() throws Exception { Context context = ApplicationProvider.getApplicationContext(); player = new TestExoPlayerBuilder(context).build(); player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4")); player.prepare(); play(player).untilState(Player.STATE_ENDED); assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_ENDED); } }
इस उदाहरण में, asset:///media/mp4/sample.mp4 का मतलब है कि सैंपल मीडिया फ़ाइल को आपके मॉड्यूल की src/test/assets/ (या src/main/assets/) डायरेक्ट्री में रखा गया है.
ExoPlayer रेफ़रंस पास करने पर, advance(player) और play(player), ExoPlayerRunResult (या Player पास करने पर PlayerRunResult) दिखाते हैं. इन नतीजों के टाइप से, वीडियो चलाने की स्थितियों, पोज़िशन, प्लेलिस्ट ट्रांज़िशन, रेंडर किए गए फ़्रेम, बाकी बचे निर्देशों, और वीडियो चलाने से जुड़ी संभावित गड़बड़ियों का इंतज़ार करने या सामान्य गड़बड़ियों को अनदेखा करने के तरीके मिलते हैं. इंतज़ार करने की शर्तों के बारे में ज़्यादा जानने के लिए, TestPlayerRunHelper एपीआई का संदर्भ देखें.
नकली सोर्स और सिम्युलेट किए गए कोडेक के साथ टेस्ट करना
इंटिग्रेशन के कई टेस्ट, प्लेलिस्ट नेविगेशन, ट्रैक चुनने, टाइमलाइन अपडेट करने या फिर से कोशिश करने के तरीके की पुष्टि करते हैं. इनके लिए, असल मीडिया फ़ाइलों की ज़रूरत नहीं होती. FakeMediaSource, FakeTimeline, FakeDataSource या WebServerDispatcher (OkHttp MockWebServer के साथ) का इस्तेमाल करके, TestExoPlayerBuilder प्लेयर में सिंथेटिक टाइमलाइन और डेटा फ़ीड किया जा सकता है:
Kotlin
val player = TestExoPlayerBuilder(context).build() val fakeMediaSource = FakeMediaSource(FakeTimeline(/* windowCount= */ 2)) player.setMediaSource(fakeMediaSource) player.prepare() play(player).untilMediaItemIndex(/* mediaItemIndex= */ 1) assertThat(player.currentMediaItemIndex).isEqualTo(1) player.release()
Java
ExoPlayer player = new TestExoPlayerBuilder(context).build(); FakeMediaSource fakeMediaSource = new FakeMediaSource(new FakeTimeline(/* windowCount= */ 2)); player.setMediaSource(fakeMediaSource); player.prepare(); play(player).untilMediaItemIndex(/* mediaItemIndex= */ 1); assertThat(player.getCurrentMediaItemIndex()).isEqualTo(1); player.release();
TestExoPlayerBuilder, डिफ़ॉल्ट रूप से नकली रेंडरर का इस्तेमाल करता है. अगर आपके Robolectric टेस्ट को MediaCodec इंटरैक्शन की ज़रूरत है, तो सिम्युलेट किए गए कोडेक और आउटपुट कैप्चर को इस तरह कॉन्फ़िगर करें:
- Robolectric के सिम्युलेट किए गए कोडेक को रजिस्टर करने के लिए,
ShadowMediaCodecConfigसेwithAllDefaultSupportedCodecsको JUnit@Ruleके तौर पर जोड़ें. - बिल्डर के
Clock(CapturingRenderersFactory(context, builder.getClock())) से बनाए गएCapturingRenderersFactoryकोbuilder.setRenderersFactoryको पास करें. - वीडियो ट्रैक की जांच करते समय,
player.setVideoSurfaceके साथSurface(जैसे किSurface(SurfaceTexture(1))) अटैच करें, ताकिMediaCodecVideoRendererडिकोड किए गए फ़्रेम को स्किप करने के बजाय रेंडर करे. - रेंडर किए गए आउटपुट की पुष्टि करने के लिए, वीडियो चलाना शुरू करने से पहले
PlaybackOutputपरregister(player, capturingRenderersFactory)को कॉल करें. - वीडियो चलने के बाद, प्लेयर और सरफ़ेस को रिलीज़ करें. इसके बाद,
Dumper(Dumper().add(playbackOutput).toString()) से फ़ॉर्मैट किए गए आउटपुट की जांच करें.
डिवाइसों पर इंस्ट्रुमेंटेशन टेस्ट
लोकल Robolectric टेस्ट, असल प्लैटफ़ॉर्म डिकोडर, ऑडियो ट्रैक, डीआरएम सेशन या Surface रेंडरिंग को लागू नहीं करते. Android पर चलने वाले डिवाइसों पर, वीडियो चलाने की प्रोसेस की पूरी जांच करने के लिए, src/androidTest में इंस्ट्रुमेंटेड टेस्ट लिखें. इसके लिए, ExoPlayer.Builder की मदद से बनाए गए असली ExoPlayer का इस्तेमाल करें.
JUnit इंस्ट्रुमेंटेशन टेस्ट में, @Test तरीके ऐप्लिकेशन के मुख्य थ्रेड के बजाय इंस्ट्रुमेंटेशन थ्रेड पर चलते हैं. ExoPlayer को हमेशा उसके ऐप्लिकेशन Looper थ्रेड पर बनाएं, कंट्रोल करें, और रिलीज़ करें. उदाहरण के लिए, Kotlin में runBlocking(Dispatchers.Main) या Java में InstrumentationRegistry.getInstrumentation().runOnMainSync का इस्तेमाल करके.
PlayerFence और कोरूटीन की मदद से, प्लेयर की स्थितियों का इंतज़ार करना
इंस्ट्रुमेंटेशन टेस्ट में, मुख्य Looper डिवाइस पर रीयल टाइम में चलता है.
androidTestImplementation डिपेंडेंसी में Robolectric या media3-test-utils-robolectric को शामिल न करें. वीडियो चलाने की स्थितियों और इवेंट के लिए, media3-test-utils ये हेल्पर उपलब्ध कराता है:
- Kotlin:
Playerपर एक्सटेंशन फ़ंक्शन निलंबित करना (जैसे किawaitPlaybackState,awaitIsPlaying,awaitFirstFrameRendered, औरawaitContentPositionAtLeast). - Java:
PlayerFenceपर स्टैटिकfutureWhen(player)तरीका, जोPlayerFenceदिखाता है. इसके शर्त वाले तरीकों (जैसे,entersPlaybackStateऔरisPlaying) को प्लेयर केLooperथ्रेड पर कॉल किया जाना चाहिए. साथ ही,ListenableFutureदिखाना चाहिए, जिसे टेस्ट थ्रेडgetके साथ ब्लॉक कर सकता है.
दोनों एपीआई, इंतज़ार के दौरान प्लेयर की निगरानी करते हैं. अगर वीडियो चलाने में कोई गंभीर गड़बड़ी (या डिफ़ॉल्ट रूप से, नुकसान न पहुंचाने वाली गड़बड़ी) होती है या इंतज़ार का समय, टाइम आउट से ज़्यादा हो जाता है (डिफ़ॉल्ट रूप से 10 सेकंड या एम्युलेटर पर 30 सेकंड), तो कॉल हैंग होने के बजाय तुरंत बंद हो जाता है:
Kotlin
@RunWith(AndroidJUnit4::class) @OptIn(UnstableApi::class) class PlaybackInstrumentationTest { private lateinit var player: ExoPlayer @After fun tearDown() = runBlocking(Dispatchers.Main) { if (::player.isInitialized) { player.release() } } @Test fun play_sampleMediaItem_advancesAndEnds() = runBlocking(Dispatchers.Main) { val context = ApplicationProvider.getApplicationContext<Context>() player = ExoPlayer.Builder(context).build() player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4")) player.prepare() player.play() player.awaitIsPlaying(true) player.awaitContentPositionAtLeast(500) player.awaitPlaybackState(Player.STATE_ENDED) assertThat(player.playbackState).isEqualTo(Player.STATE_ENDED) } }
Java
@RunWith(AndroidJUnit4.class) @OptIn(markerClass = UnstableApi.class) public static class PlaybackInstrumentationTest { private @Nullable ExoPlayer player; @After public void tearDown() { InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> { if (player != null) { player.release(); } }); } @Test public void play_sampleMediaItem_advancesAndEnds() throws Exception { SettableFuture<Void> playbackStarted = SettableFuture.create(); SettableFuture<Void> positionPassed = SettableFuture.create(); SettableFuture<Void> playbackEnded = SettableFuture.create(); InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> { Context context = ApplicationProvider.getApplicationContext(); player = new ExoPlayer.Builder(context).build(); player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/sample.mp4")); playbackStarted.setFuture(futureWhen(player).isPlaying(true)); positionPassed.setFuture(futureWhen(player).passesContentPosition(500)); playbackEnded.setFuture(futureWhen(player).entersPlaybackState(Player.STATE_ENDED)); player.prepare(); player.play(); }); playbackStarted.get(); positionPassed.get(); playbackEnded.get(); InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> assertThat(player.getPlaybackState()).isEqualTo(Player.STATE_ENDED)); } }
ऐप्लिकेशन मॉड्यूल (com.android.application) में ApplicationProvider.getApplicationContext की मदद से ExoPlayer बनाते समय, asset:/// यूआरआई, टारगेट ऐप्लिकेशन की ऐसेट (जैसे, src/androidTest/assets/ के बजाय src/debug/assets/ या src/main/assets/) के हिसाब से रिज़ॉल्व होते हैं.
ट्रांज़िएंट इवेंट और कस्टम सुपरविज़न
प्लेयर की मौजूदा स्थिति की जांच करने के अलावा, media3-test-utils में मौजूद हेल्पर, एक बार होने वाले इवेंट और कस्टम लिसनर की शर्तों के साथ काम करते हैं. अगर शर्त पहले ही पूरी हो चुकी है, तो इंतज़ार तुरंत खत्म हो जाता है:
- ट्रांज़िएंट इवेंट, जैसे कि रेंडर किया गया पहला फ़्रेम: प्लेयर में वीडियो आउटपुट (जैसे कि
SurfaceयाPlayerView) अटैच करें. इसके बाद,player.prepareको कॉल करने से पहले,awaitFirstFrameRenderedयाrendersFirstFrameके साथ इंतज़ार करें, ताकि शुरुआती फ़्रेम इवेंट न छूटे. - कस्टम शर्तें: Kotlin में
playerपरawaitSupervisedके अंदर, कस्टम लिसनर या कोरूटीन वेट को रैप करें या Java मेंfutureWhen(player).withSupervision(future)को कस्टमListenableFutureपास करें. नॉन-फ़ेटल गड़बड़ियों से रिकवरी की जांच करते समय,failOnNonFatalErrors = false(Kotlin) पास करें याignoringNonFatalErrors(Java) को कॉल करें. साथ ही,timeoutयाwithTimeoutMs(timeoutMs)का इस्तेमाल करके, टाइम आउट को अपनी ज़रूरत के मुताबिक बनाएं.
यहां दिए गए उदाहरण में, पहले फ़्रेम के ट्रांज़िएंट इवेंट का इंतज़ार किया जाता है और कस्टम मेटाडेटा लिसनर की निगरानी की जाती है:
Kotlin
// Start awaiting transient events (with a video surface attached) before prepare(). coroutineScope { val firstFrameDeferred = async(start = CoroutineStart.UNDISPATCHED) { player.awaitFirstFrameRendered() } player.setMediaItem(mediaItem) player.prepare() player.play() firstFrameDeferred.await() } // Supervise a custom listener condition while still failing fast on player errors. val metadataTitle = player.awaitSupervised { player.mediaMetadata.title?.let { return@awaitSupervised it } val titleDeferred = CompletableDeferred<CharSequence>() val listener = object : Player.Listener { override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) { mediaMetadata.title?.let { titleDeferred.complete(it) } } } player.addListener(listener) try { titleDeferred.await() } finally { player.removeListener(listener) } }
Java
SettableFuture<Void> firstFrameFuture = SettableFuture.create(); SettableFuture<CharSequence> metadataTitleFuture = SettableFuture.create(); SettableFuture<CharSequence> supervisedTitleFuture = SettableFuture.create(); Player.Listener metadataListener = new Player.Listener() { @Override public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { if (mediaMetadata.title != null) { metadataTitleFuture.set(mediaMetadata.title); } } }; try { InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> { // Register transient event fences (with a video surface attached) before prepare(). firstFrameFuture.setFuture(futureWhen(player).rendersFirstFrame()); // Supervise a custom ListenableFuture so player errors fail fast. player.addListener(metadataListener); supervisedTitleFuture.setFuture( futureWhen(player).withSupervision(metadataTitleFuture)); player.setMediaItem(mediaItem); if (player.getMediaMetadata().title != null) { metadataTitleFuture.set(player.getMediaMetadata().title); } player.prepare(); player.play(); }); firstFrameFuture.get(); CharSequence title = supervisedTitleFuture.get(); } finally { InstrumentationRegistry.getInstrumentation() .runOnMainSync(() -> { player.removeListener(metadataListener); firstFrameFuture.cancel(/* mayInterruptIfRunning= */ true); supervisedTitleFuture.cancel(/* mayInterruptIfRunning= */ true); }); }
FakePlayer की मदद से, यूज़र इंटरफ़ेस (यूआई) और ऐप्लिकेशन के कॉम्पोनेंट की जांच करना
PlayerView या media3-ui-compose कंपोज़ेबल, ViewModels या MediaSession कॉलबैक जैसे यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट की जांच करते समय, पूरा ExoPlayer बनाने की ज़रूरत नहीं होती. media3-test-utils में FakePlayer, SimpleBasePlayer को एक्सटेंड करता है, ताकि कॉलिंग थ्रेड के Looper पर प्लेलिस्ट, वीडियो चलाने की स्थितियां, पोज़िशन, वीडियो के साइज़, क्यू, और गड़बड़ियों को सिम्युलेट किया जा सके. यह Looper, @RunWith(AndroidJUnit4::class) के साथ Robolectric टेस्ट में और इंस्ट्रुमेंटेड टेस्ट में मुख्य थ्रेड पर अपने-आप उपलब्ध हो जाता है.
डिफ़ॉल्ट रूप से, FakePlayer prepare और seekTo पर 100 मि॰से॰ की बफ़रिंग देरी को सिम्युलेट करता है; Kotlin में bufferingDelayMs = 0 पास करें या setPlaybackState की मदद से सीधे तौर पर स्थितियां सेट करें:
Kotlin
val fakePlayer = FakePlayer(bufferingDelayMs = 0) fakePlayer.setMediaItem(MediaItem.fromUri("https://example.com/media.mp4")) fakePlayer.prepare() fakePlayer.play() assertThat(fakePlayer.isPlaying).isTrue() // Drive state transitions directly to test UI or session listeners. fakePlayer.setPosition(30_000) fakePlayer.setPlaybackState(Player.STATE_ENDED) fakePlayer.release()
Java
FakePlayer fakePlayer = new FakePlayer(); fakePlayer.setMediaItem(MediaItem.fromUri("https://example.com/media.mp4")); // Drive state transitions directly (prepare() simulates a 100 ms buffering delay by default). fakePlayer.setPlaybackState(Player.STATE_READY); fakePlayer.play(); assertThat(fakePlayer.isPlaying()).isTrue(); fakePlayer.setPosition(30_000); fakePlayer.setPlaybackState(Player.STATE_ENDED); fakePlayer.release();
टेस्टिंग के लिए अन्य सुविधाएं
media3-test-utils में, अन्य टेस्टिंग स्थितियों के लिए खास हेल्पर शामिल हैं:
- फ़ीचर फ़्लैग और कैनरी मोड: हर टेस्ट के बाद फ़्लैग ओवरराइड को रीसेट करने और फ़्लैग की स्थितियों के हिसाब से पैरामीटर वाले टेस्ट चलाने के लिए,
Media3FlagsRuleको@BindFlagऔर@BindCanaryModeके साथ इस्तेमाल करें. ज़्यादा जानकारी के लिए, फ़ीचर फ़्लैग की मदद से टेस्टिंग करना लेख पढ़ें. - कस्टम
DataSourceकॉन्ट्रैक्ट टेस्ट: सबक्लासDataSourceContractTest, ताकि यह पुष्टि की जा सके कि कस्टमDataSourceयाHttpDataSourceके लागू होने पर, Media3 के कॉन्ट्रैक्ट की शर्तों का पालन किया जा रहा है. इसमें रेंज के अनुरोध, गड़बड़ी को ठीक करना, और ट्रांसफ़र लिसनर शामिल हैं. ज़्यादा जानकारी के लिए, अन्य नेटवर्क स्टैक को इंटिग्रेट करना लेख पढ़ें.