পরীক্ষা করা

Media3 ডেডিকেটেড টেস্টিং আর্টিফ্যাক্ট (media3-test-utils এবং media3-test-utils-robolectric) প্রদান করে যা আপনাকে লোকাল JVM টেস্ট এবং ডিভাইস ইন্সট্রুমেন্টেশন টেস্ট, উভয় ক্ষেত্রেই মিডিয়া প্লেব্যাক, প্লেয়ার স্টেট ট্রানজিশন, UI কম্পোনেন্ট এবং কাস্টম মিডিয়া সোর্স টেস্ট করতে সাহায্য করে।

এই পৃষ্ঠায় ExoPlayer ব্যবহার করে এমন অ্যাপ পরীক্ষা করার জন্য সাজেস্ট করা পদ্ধতিগুলি উল্লেখ করা হয়েছে এবং প্রতিটি টেস্ট এনভায়রনমেন্টে Media3-এর টেস্টিং ইউটিলিটি কীভাবে ব্যবহার করতে হয় তা দেখানো হয়েছে।

সাজেশন পরীক্ষা করা হচ্ছে

মিডিয়া প্লেব্যাকের জন্য অটোমেটেড টেস্ট লেখার সময়, এইসব মূল নীতি অনুসরণ করুন:

  • মকের পরিবর্তে আসল কম্পোনেন্ট বা Media3 ফেক ব্যবহার করা: Mockito-র মতো ফ্রেমওয়ার্কের সাহায্যে Player বা ExoPlayer মক করা এড়িয়ে চলুন। Player-এ স্টেট ইনভেরিয়েন্ট ও লিসনার কন্ট্র্যাক্ট আছে যা মক এনফোর্স করে না, এর ফলে টেস্ট ভঙ্গুর হতে পারে বা বাগ লুকিয়ে থাকতে পারে। পরিবর্তে, আসল ExoPlayer ইনস্ট্যান্স, FakePlayer, অথবা নকল যেমন FakeMediaSource এবং FakeDataSource ব্যবহার করুন। প্রয়োজন হলে কাস্টম কলব্যাক ইনভোকেশন যাচাই করার জন্য মক রিজার্ভ করুন।
  • স্লিপিংয়ের পরিবর্তে নির্ধারকভাবে অপেক্ষা করুন: ExoPlayer ইন্টার্নাল থ্রেডে অ্যাসিঙ্ক্রোনাসভাবে মিডিয়া লোডিং ও প্লেব্যাক পারফর্ম করে। যথেচ্ছ Thread.sleep কল এড়িয়ে চলুন, যা টেস্টকে ধীর ও অবিশ্বস্ত করে তোলে। Robolectric টেস্টে TestPlayerRunHelper এবং ইনস্ট্রুমেন্টেশন টেস্টে Player এক্সটেনশন বা PlayerFence সাসপেন্ড করা ব্যবহার করুন। এইসব হেল্পার প্লেয়ারের নির্দিষ্ট অবস্থার জন্য অপেক্ষা করে এবং প্লেব্যাক সংক্রান্ত কোনও সমস্যা হলে সাথে সাথে ব্যর্থ হয়।
  • অ্যাপ্লিকেশন থ্রেডে প্লেয়ার অ্যাক্সেস করুন: ExoPlayer ইনস্ট্যান্স এবং অপেক্ষারত হেল্পারদের অবশ্যই প্লেয়ারের অ্যাপ্লিকেশন Looper থ্রেডে কল করতে হবে (সাধারণত প্রধান থ্রেড)। আরও তথ্যের জন্য, ExoPlayer থ্রেডিং সংক্রান্ত প্রয়োজনীয়তা দেখুন।

সঠিক টেস্ট এনভায়রনমেন্ট বেছে নিন

আপনার মিডিয়া ইন্টিগ্রেশনের বিভিন্ন লেয়ার বিভিন্ন টেস্ট এনভায়রনমেন্ট থেকে সুবিধা পায়:

টেস্ট টার্গেট সাজেস্ট করা এনভায়রনমেন্ট প্রধান Media3 ইউটিলিটি
UI কম্পোনেন্ট (PlayerView, Compose UI), ViewModels অথবা MediaSession কলব্যাক লোকাল Robolectric টেস্ট (src/test) বা ইনস্ট্রুমেন্টেড UI টেস্ট (src/androidTest) FakePlayer
ExoPlayer ইন্টিগ্রেশন, প্লেলিস্ট ট্রানজিশন, অ্যানালিটিক্স এবং কাস্টম MediaSource বা DataSource লজিক স্থানীয় রোবোলেকট্রিক টেস্ট (src/test) TestExoPlayerBuilder, TestPlayerRunHelper, FakeMediaSource
আসল হার্ডওয়্যার MediaCodec ডিকোডিং, Surface রেন্ডারিং, অডিও আউটপুট ও DRM ফিজিক্যাল ডিভাইসে ইন্সট্রুমেন্টেশন টেস্ট (src/androidTest) ExoPlayer.Builder, Player.await* এক্সটেনশন, PlayerFence

সম্ভব হলে, এমুলেটরের পরিবর্তে ফিজিক্যাল ডিভাইসে ইনস্ট্রুমেন্টেশন প্লেব্যাক টেস্ট চালান, কারণ এমুলেটর মিডিয়া স্ট্যাক প্রয়োগের পদ্ধতি আলাদা হয়। এমুলেটর সাপোর্ট সম্পর্কে আরও জানতে, কাজ করে এমন ডিভাইসের গাইড দেখুন।

টেস্টিং ডিপেন্ডেন্সি যোগ করুন

আপনার অন্যান্য 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")

গ্ৰুভি

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"

দুটি মডিউল রানটাইম এনভায়রনমেন্ট অনুযায়ী দায়িত্ব ভাগ করে নেয়:

আপনার মডিউলে টেস্ট এনভায়রনমেন্টের জন্য টেস্ট ফ্রেমওয়ার্ক ডিপেন্ডেন্সিও অন্তর্ভুক্ত আছে কিনা তা নিশ্চিত করুন:

  • Robolectric: org.robolectric:robolectric. লোকাল অ্যাসেট লোড করার সময় android.testOptions.unitTests.isIncludeAndroidResources = true সেট করুন।
  • AndroidX Test: androidx.test:runner, androidx.test:core এবং androidx.test.ext:junit।
  • মূল থ্রেডে Kotlin coroutines: org.jetbrains.kotlinx:kotlinx-coroutines-android.

অস্থির টেস্টিং API-তে অপ্ট-ইন করুন

media3-test-utils এবং media3-test-utils-robolectric-এ ক্লাস এবং পদ্ধতিগুলি @UnstableApi দিয়ে অ্যানোটেট করা হয়েছে। যদিও Media3-এর লক্ষ্য হল ব্যাপকভাবে ব্যবহৃত টেস্ট হেল্পারকে রিলিজ জুড়ে এরগোনমিক রাখা, টেস্ট ইউটিলিটিগুলি প্রোডাকশন প্লেব্যাক API-এর মতো একই বাইনারি কম্প্যাটিবিলিটি স্টেবিলিটি নিয়ম অনুসরণ করে না।

আপনার টেস্ট সোর্স সেটে, @androidx.annotation.OptIn-এর সাথে টেস্ট ক্লাস বা পদ্ধতি অ্যানোটেট করুন, আপনার টেস্ট প্যাকেজের জন্য package-info অপ্ট-ইন যোগ করুন অথবা টেস্ট সোর্সের জন্য lint.xml কনফিগার করুন। আরও তথ্যের জন্য, অস্থির API লিন্ট সমস্যার সমাধান সংক্রান্ত নোট দেখুন।

Robolectric-এর মাধ্যমে লোকাল টেস্ট

লোকাল Robolectric টেস্ট আপনার ডেভেলপমেন্ট মেশিনের JVM-এ রান করে, এতে কোনও ফিজিক্যাল ডিভাইস বা এমুলেটর থাকে না। Robolectric MediaCodec ইমপ্লিমেন্টেশন প্রদান করে না এবং এটি প্রধান Looper পজ করে রাখে যতক্ষণ না স্পষ্টভাবে পাম্প করা হয়, তাই একটি ডিফল্ট ExoPlayer ইনস্ট্যান্স মিডিয়া ডিকোড করতে পারে না বা নিজেই স্ট্যাটাস আপডেট প্রদান করতে পারে না।

একটি টেস্ট প্লেয়ার তৈরি করুন এবং প্লেব্যাক উন্নত করুন

Robolectric টেস্টের জন্য কনফিগার করা ExoPlayer তৈরি করতে TestExoPlayerBuilder ব্যবহার করুন। সাধারণত, TestExoPlayerBuilder নিম্নলিখিত কম্পোনেন্ট কনফিগার করে:

  • একটি অটো-অ্যাডভান্সিং FakeClock (isAutoAdvancing = true) যা ExoPlayer-এর ব্যাকগ্রাউন্ড প্লেব্যাক থ্রেড এবং Robolectric-এর পজ করা প্রধান Looper-এর মধ্যে সময়ের অগ্রগতিকে কোঅর্ডিনেট করে।
  • নকল ভিডিও এবং অডিও রেন্ডারার (FakeVideoRenderer এবং FakeAudioRenderer) যা MediaCodec ডিকোডার ছাড়াই তাদের টাইমস্ট্যাম্প অনুযায়ী মিডিয়া স্যাম্পেল ব্যবহার করে।

প্লেব্যাক প্রোগ্রেস করতে এবং মূল থ্রেডে ফলাফল অ্যাসার্ট করতে ( Looper.getMainLooper ব্যবহার করে প্লেয়ারের সাথে, Robolectric-এ ডিফল্ট), TestPlayerRunHelper-এ স্ট্যাটিক advance(player) বা play(player) পদ্ধতি ব্যবহার করুন (অপেক্ষা করার আগে 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)
  }
}

জাভা

@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 API রেফারেন্স দেখুন।

জাল সোর্স এবং সিমুলেটেড কোডেক দিয়ে পরীক্ষা করুন

অনেক ইন্টিগ্রেশন টেস্ট প্লেলিস্ট নেভিগেশন, ট্র্যাক বেছে নেওয়া, টাইমলাইন আপডেট বা আবার চেষ্টা করার আচরণ যাচাই করে এবং আসল মিডিয়া ফাইলের প্রয়োজন হয় না। আপনি 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()

জাভা

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 ইন্টার‍্যাকশন প্রয়োজন হলে, সিমুলেটেড কোডেক ও আউটপুট ক্যাপচার এইভাবে কনফিগার করুন:

  1. Robolectric-এর সিমুলেটেড কোডেক রেজিস্টার করতে ShadowMediaCodecConfig থেকে JUnit @Rule হিসেবে withAllDefaultSupportedCodecs যোগ করুন।
  2. বিল্ডারের Clock (CapturingRenderersFactory(context, builder.getClock())) দিয়ে তৈরি করা CapturingRenderersFactory builder.setRenderersFactory-এ পাস করুন।
  3. ভিডিও ট্র্যাক পরীক্ষা করার সময়, player.setVideoSurface-এর সাথে Surface (যেমন Surface(SurfaceTexture(1))) অ্যাটাচ করুন যাতে MediaCodecVideoRenderer ডিকোড করা ফ্রেমগুলি এড়িয়ে যাওয়ার পরিবর্তে রেন্ডার করে।
  4. রেন্ডার করা আউটপুট যাচাই করতে, প্লেব্যাক শুরু করার আগে PlaybackOutput-এ register(player, capturingRenderersFactory) কল করুন।
  5. প্লেব্যাক শেষ হওয়ার পরে, প্লেয়ার ও সারফেস রিলিজ করুন এবং তারপরে Dumper (Dumper().add(playbackOutput).toString()) থেকে ফর্ম্যাট করা আউটপুট পরীক্ষা করুন।

ডিভাইসে ইন্সট্রুমেন্টেশন টেস্ট

লোকাল Robolectric টেস্ট আসল প্ল্যাটফর্ম ডিকোডার, অডিও ট্র্যাক, DRM সেশন বা Surface রেন্ডারিং এক্সিকিউট করে না। Android ডিভাইসে এন্ড-টু-এন্ড প্লেব্যাক আচরণ যাচাই করতে, ExoPlayer.Builder দিয়ে তৈরি আসল ExoPlayer ব্যবহার করে src/androidTest-এ ইনস্ট্রুমেন্টেড টেস্ট লিখুন।

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-এর মাধ্যমে ব্লক করতে পারে।

অপেক্ষা করার সময় দুটি API-ই প্লেয়ারকে তত্ত্বাবধান করে। যদি কোনও মারাত্মক প্লেব্যাক ত্রুটি (বা, ডিফল্টরূপে, একটি অ-মারাত্মক ত্রুটি) ঘটে, বা যদি অপেক্ষা তার টাইমআউট অতিক্রম করে (ডিফল্টরূপে ১০ সেকেন্ড, বা এমুলেটরে ৩০ সেকেন্ড), কলটি হ্যাং হওয়ার পরিবর্তে অবিলম্বে ব্যর্থ হয়:

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)
    }
}

জাভা

@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:/// URI টার্গেট অ্যাপের অ্যাসেটের (যেমন src/debug/assets/ বা src/main/assets/, src/androidTest/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)
  }
}

জাভা

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-এর সাহায্যে UI ও অ্যাপ কম্পোনেন্ট টেস্ট করা

PlayerView বা media3-ui-compose কম্পোজেবল, ViewModels বা MediaSession কলব্যাকের মতো UI কম্পোনেন্ট টেস্ট করার সময়, সম্পূর্ণ ExoPlayer তৈরি করার প্রয়োজন হয় না। media3-test-utils-এ FakePlayer SimpleBasePlayer-কে এক্সটেন্ড করে যাতে কলিং থ্রেডের Looper-এ প্লেলিস্ট, প্লেব্যাক স্টেট, পজিশন, ভিডিও সাইজ, কিউ ও সমস্যা সিমুলেট করা যায়। এই Looper অটোমেটিক উপলভ্য হয় @RunWith(AndroidJUnit4::class) সহ Robolectric টেস্টে এবং ইনস্ট্রুমেন্টেড টেস্টে মেন থ্রেডে।

সাধারণত, FakePlayer, prepare এবং seekTo-এ ১০০ মিলিসেকেন্ডের বাফারিং বিলম্ব সিমুলেট করে; 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()

জাভা

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-এর কন্ট্র্যাক্ট প্রত্যাশা পূরণ করে কিনা তা যাচাই করা যায়। আরও তথ্যের জন্য, অন্য নেটওয়ার্ক স্ট্যাক ইন্টিগ্রেট করা দেখুন।