توفّر Media3 عناصر اختبار مخصّصة (media3-test-utils وmedia3-test-utils-robolectric) لمساعدتك في اختبار تشغيل الوسائط وعمليات انتقال حالة المشغّل ومكوّنات واجهة المستخدم ومصادر الوسائط المخصّصة في كلّ من اختبارات آلة جافا الافتراضية (JVM) المحلية واختبارات لقياس حالة التطبيق على الأجهزة.
توضّح هذه الصفحة الممارسات المقترَحة لاختبار التطبيقات التي تستخدم ExoPlayer
وتوضّح كيفية استخدام أدوات الاختبار في Media3 في كل بيئة اختبار.
اقتراحات حول الاختبار
عند كتابة اختبارات مبرمَجة لتشغيل الوسائط، اتّبِع المبادئ الأساسية التالية:
- تفضيل المكوّنات الحقيقية أو المكوّنات الزائفة في Media3 على المكوّنات الوهمية: تجنَّب استخدام المكوّنات الوهمية
PlayerأوExoPlayerمع أُطر عمل مثل Mockito. يحتوي "Player" على ثوابت الحالة وعقود المستمعين التي لا تفرضها العناصر الوهمية، ما قد يجعل الاختبارات غير مستقرة أو يخفي الأخطاء. بدلاً من ذلك، استخدِم مثيلExoPlayerحقيقي أوFakePlayerأو مثيلات مزيّفة مثلFakeMediaSourceوFakeDataSource. احتفِظ بالمحاكاة للتحقّق من عمليات استدعاء عمليات الاستدعاء المخصّصة عند الحاجة. - الانتظار بشكل حتمي بدلاً من النوم: ينفّذ
ExoPlayerتحميل الوسائط وتشغيلها بشكل غير متزامن في سلسلة محادثات داخلية. تجنَّب طلباتThread.sleepالعشوائية التي تجعل الاختبارات بطيئة وغير مستقرة. استخدِمTestPlayerRunHelperفي اختبارات Robolectric وعلِّق إضافاتPlayerأوPlayerFenceفي اختبارات لقياس حالة التطبيق. تنتظر هذه الدوال المساعدة حالات المشغّل الصريحة وتتعطّل على الفور في حال حدوث خطأ في التشغيل. - الوصول إلى المشغّل من خلال سلسلة مهام التطبيق: يجب استدعاء مثيلات
ExoPlayerومساعدي الانتظار من خلال سلسلة مهام تطبيق المشغّلLooper(عادةً ما تكون سلسلة المهام الرئيسية). لمزيد من المعلومات، يُرجى الاطّلاع على متطلبات سلاسل المحادثات في ExoPlayer.
اختيار بيئة الاختبار المناسبة
تستفيد الطبقات المختلفة من عملية دمج الوسائط من بيئات الاختبار المختلفة:
| هدف الاختبار | البيئة المقترَحة | أدوات Media3 الرئيسية |
|---|---|---|
مكوّنات واجهة المستخدم (PlayerView وCompose UI) أو ViewModels أو عمليات استدعاء MediaSession |
اختبارات Robolectric المحلية (src/test) أو اختبارات واجهة المستخدم المزوّدة بأدوات (src/androidTest) |
FakePlayer |
دمج ExoPlayer، والانتقالات في قوائم التشغيل، والإحصاءات، ومنطق MediaSource أو DataSource المخصّص |
اختبارات Robolectric المحلية (src/test) |
TestExoPlayerBuilder، TestPlayerRunHelper، FakeMediaSource |
فك ترميز MediaCodec وعرض Surface وإخراج الصوت وإدارة الحقوق الرقمية على الأجهزة الحقيقية |
اختبارات قياس حالة التطبيق على الأجهزة الفعلية (src/androidTest) |
ExoPlayer.Builder، Player.await* إضافة، PlayerFence |
ننصحك بإجراء اختبارات تشغيل الأدوات على الأجهزة الفعلية بدلاً من المحاكيات، إن أمكن، لأنّ عمليات تنفيذ حزمة الوسائط في المحاكي تختلف من جهاز لآخر. لمزيد من المعلومات حول توافق المحاكي، يُرجى الاطّلاع على دليل الأجهزة المتوافقة.
إضافة تبعيات الاختبار
أضِف عناصر اختبار Media3 إلى حزمة dependencies في ملف build.gradle.kts أو build.gradle الخاص بوحدتك باستخدام الإصدار نفسه من وحدات 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")
أنيق
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 على جهاز JVM المحلي، بما في ذلك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 المحلية على جهاز Java الافتراضي (JVM) الخاص بجهاز التطوير
بدون جهاز فعلي أو محاكي. لا يوفّر Robolectric عمليات تنفيذ MediaCodec، ويُبقي Looper الرئيسي متوقفًا مؤقتًا إلى أن يتم تشغيله بشكل صريح، لذا لا يمكن لمثيل ExoPlayer التلقائي فك ترميز الوسائط أو تقديم تحديثات الحالة بمفرده.
إنشاء مشغّل تجريبي وتحسين التشغيل
استخدِم TestExoPlayerBuilder لإنشاء ExoPlayer تم إعداده لاختبارات Robolectric. يضبط TestExoPlayerBuilder تلقائيًا
المكوّنات التالية:
-
FakeClock(isAutoAdvancing = true) يتقدّم تلقائيًا وينسّق تقدّم الوقت بين سلسلة عمليات التشغيل في الخلفية في "ExoPlayer" وLooperالرئيسي المتوقف مؤقتًا في Robolectric. - أدوات عرض الفيديو والصوت الزائفة (
FakeVideoRendererوFakeAudioRenderer) التي تستهلك عيّنات الوسائط وفقًا لطوابعها الزمنية بدون الحاجة إلى برامج فك ترميزMediaCodec
لتقدّم التشغيل وتأكيد النتائج في سلسلة التعليمات الرئيسية (باستخدام مشغّل يستخدم
Looper.getMainLooper، وهو الإعداد التلقائي في Robolectric)، استخدِم الطريقتَين الثابتتَين
advance(player) أو play(player) في
TestPlayerRunHelper (تستدعي play(player)
player.play قبل الانتظار). في معظم الحالات، يضخّ المساعد
Looper الرئيسي في Robolectric إلى أن يتم استيفاء الشرط المستهدَف، ويُظهر
استثناءً في حال حدوث خطأ فادح أو غير فادح في التشغيل:
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 (أو
PlayerRunResult عند تمرير Player). توفّر أنواع النتائج هذه
طرقًا للانتظار في حالات التشغيل والمواضع وعمليات نقل
قائمة التشغيل والإطارات المعروضة والأوامر في انتظار المراجعة وأخطاء التشغيل المتوقعة أو
تجاهل الأخطاء غير الفادحة. لمزيد من المعلومات حول شروط الانتظار، يُرجى الاطّلاع على مرجع واجهة برمجة التطبيقات TestPlayerRunHelper.
الاختبار باستخدام مصادر مزيفة وبرامج ترميز محاكاة
تتحقّق العديد من اختبارات الدمج من التنقّل في قوائم التشغيل أو اختيار الأغاني أو تعديلات المخطط الزمني أو سلوك إعادة المحاولة، ولا تحتاج إلى ملفات وسائط حقيقية. يمكنك إدخال
المخططات الزمنية والبيانات الاصطناعية في مشغّل TestExoPlayerBuilder باستخدام
FakeMediaSource أو FakeTimeline أو
FakeDataSource أو
WebServerDispatcher (مع OkHttp MockWebServer):
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 بدلاً من ذلك، يمكنك ضبط برامج الترميز التي تمت محاكاتها والتقاط
الناتج على النحو التالي:
- أضِف
withAllDefaultSupportedCodecsمنShadowMediaCodecConfigكـ@Ruleفي JUnit لتسجيل برامج الترميز التي يحاكيها Robolectric. - مرِّر
CapturingRenderersFactoryتم إنشاؤه باستخدامClock(CapturingRenderersFactory(context, builder.getClock())) الخاص بأداة الإنشاء إلىbuilder.setRenderersFactory. - عند اختبار مقاطع الفيديو، أرفِق
Surface(مثلSurface(SurfaceTexture(1))) معplayer.setVideoSurfaceلكي يعرضMediaCodecVideoRendererالإطارات التي تم فك ترميزها بدلاً من تخطّيها. - للتحقّق من الناتج المعروض، يمكنك استدعاء
register(player, capturingRenderersFactory)علىPlaybackOutputقبل بدء التشغيل. - بعد انتهاء التشغيل، حرِّر المشغّل والسطح، ثم افحص الناتج المنسَّق من
Dumper(Dumper().add(playbackOutput).toString()).
اختبارات قياس حالة التطبيق على الأجهزة
لا تنفّذ اختبارات Robolectric المحلية برامج فك الترميز الحقيقية للمنصة أو المقاطع الصوتية أو جلسات إدارة الحقوق الرقمية أو عرض Surface. للتحقّق من سلوك التشغيل الشامل على
الأجهزة التي تعمل بنظام التشغيل Android، اكتب اختبارات لقياس حالة التطبيق في
src/androidTest باستخدام ExoPlayer حقيقي تم إنشاؤه باستخدام ExoPlayer.Builder.
في اختبارات لقياس حالة التطبيق في JUnit، يتم تشغيل إجراءات @Test في سلسلة تعليمات لقياس حالة التطبيق بدلاً من سلسلة التعليمات الرئيسية للتطبيق. يجب دائمًا إنشاء ExoPlayer والتحكّم فيه وإصداره على سلسلة محادثات تطبيق Looper، على سبيل المثال باستخدام runBlocking(Dispatchers.Main) في Kotlin أو InstrumentationRegistry.getInstrumentation().runOnMainSync في Java.
انتظار حالات اللاعبين باستخدام PlayerFence و"كوروتين"
في اختبار لقياس حالة التطبيق، يتم تشغيل Looper الرئيسي في الوقت الفعلي على الجهاز.
لا تُدرِج Robolectric أو media3-test-utils-robolectric في
اعتماديات androidTestImplementation. لانتظار حالات التشغيل والأحداث، يوفّر media3-test-utils المساعدين التاليين:
- Kotlin: دوال الإضافة المعلّقة في
Player(مثلawaitPlaybackStateوawaitIsPlayingوawaitFirstFrameRenderedوawaitContentPositionAtLeast). - Java: الإجراء الثابت
futureWhen(player)فيPlayerFence، والذي يعرض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)); } }
عند إنشاء ExoPlayer باستخدام ApplicationProvider.getApplicationContext في وحدة تطبيق (com.android.application)، يتم تحليل معرّفات الموارد المنتظمة (URI) لـ asset:/// وفقًا لمواد عرض التطبيق المستهدَف (مثل src/debug/assets/ أو src/main/assets/، بدلاً من src/androidTest/assets/).
الأحداث العابرة والإشراف المخصّص
بالإضافة إلى التحقّق من حالة المشغّل الحالية (حيث يتم عرض حالات الانتظار على الفور إذا
تم استيفاء الشرط)، تتيح أدوات المساعدة في media3-test-utils
الأحداث التي تحدث لمرة واحدة وشروط المستمع المخصّصة:
- الأحداث العابرة مثل أول إطار يتم عرضه: يمكنك إرفاق مخرج فيديو
(مثل
SurfaceأوPlayerView) بالمشغّل، والبدء في الانتظار باستخدامawaitFirstFrameRenderedأوrendersFirstFrameقبل استدعاءplayer.prepareحتى لا يفوتك حدث إطار مبكر لمرة واحدة. - الشروط المخصّصة: يمكنك تضمين عمليات انتظار المستمع المخصّص أو الكوروتين داخل
awaitSupervisedفيplayerفي Kotlin، أو تمريرListenableFutureمخصّص إلىfutureWhen(player).withSupervision(future)في Java. عند اختبار الاسترداد من الأخطاء غير الفادحة، يمكنك تمرير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 كامل غير ضروري. FakePlayer في
media3-test-utils يوسّع SimpleBasePlayer
لمحاكاة قوائم التشغيل وحالات التشغيل والمواضع وأحجام الفيديوهات والإشارات والأخطاء في
Looper لسلسلة الاتصال. يتوفّر هذا Looper تلقائيًا في اختبارات Robolectric مع @RunWith(AndroidJUnit4::class) وعلى سلسلة التعليمات الرئيسية في اختبارات قياس حالة التطبيق.
بشكل تلقائي، يحاكي FakePlayer تأخيرًا في التخزين المؤقت لمدة 100 ملي ثانية في prepare وseekTo. يمكنك تمرير bufferingDelayMs = 0 في Kotlin أو ضبط الحالات مباشرةً باستخدام 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 أدوات مساعدة متخصّصة لسيناريوهات الاختبار الأخرى:
- علامات الميزات ووضع Canary: استخدِم
Media3FlagsRuleمع@BindFlagو@BindCanaryModeلإعادة ضبط عمليات إلغاء العلامات بعد كل اختبار وإجراء اختبارات ذات معلَمات على مستوى حالات العلامات. لمزيد من المعلومات، يُرجى الاطّلاع على الاختبار باستخدام علامات الميزات. - اختبارات العقود المخصّصة لـ
DataSource: فئة فرعيةDataSourceContractTestللتحقّق من أنّ عمليات التنفيذ المخصّصة لـDataSourceأوHttpDataSourceتتوافق مع توقّعات العقود في Media3 في ما يتعلّق بطلبات النطاق ومعالجة الأخطاء ومستمعي عمليات النقل. لمزيد من المعلومات، يُرجى الاطّلاع على دمج حزم الشبكات الأخرى.