// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/renderers/win/media_foundation_renderer.h"

#include <windows.media.protection.h>

#include <memory>
#include <vector>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/strcat.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/win/scoped_com_initializer.h"
#include "media/base/demuxer_stream.h"
#include "media/base/media_switches.h"
#include "media/base/media_util.h"
#include "media/base/mock_filters.h"
#include "media/base/test_helpers.h"
#include "media/base/win/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"

using ::testing::_;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::StrictMock;

#include "media/base/win/mf_mocks.h"

namespace media {

using ABI::Windows::Media::Protection::IMediaProtectionPMPServer;
using Microsoft::WRL::ComPtr;

constexpr char kSubsequentErrorUmaName[] =
    "Media.MediaFoundation.MediaEngineError.SubsequentError";
constexpr char kSubsequentEventUmaName[] =
    "Media.MediaFoundation.MediaEngineError.SubsequentEvent";
constexpr char kSubsequentEventOrErrorReportedUmaName[] =
    "Media.MediaFoundation.MediaEngineError.SubsequentEventOrErrorReported";

class MockMediaProtectionPMPServer
    : public Microsoft::WRL::RuntimeClass<
          Microsoft::WRL::RuntimeClassFlags<
              Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
          IMediaProtectionPMPServer> {
 public:
  MockMediaProtectionPMPServer() = default;
  ~MockMediaProtectionPMPServer() override = default;

  static HRESULT MakeMockMediaProtectionPMPServer(
      IMediaProtectionPMPServer** pmp_server) {
    *pmp_server = Microsoft::WRL::Make<MockMediaProtectionPMPServer>().Detach();
    return S_OK;
  }

  // Return E_NOINTERFACE to avoid a crash when MFMediaEngine tries to use the
  // mocked IPropertySet from get_Properties().
  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
                                           void** object_result) override {
    return E_NOINTERFACE;
  }

  // ABI::Windows::Media::Protection::IMediaProtectionPMPServer.
  MOCK_STDCALL_METHOD1(
      get_Properties,
      HRESULT(
          ABI::Windows::Foundation::Collections::IPropertySet** properties));
};

class MediaFoundationRendererTest : public testing::Test {
 public:
  MediaFoundationRendererTest() {
    mf_cdm_proxy_ =
        base::MakeRefCounted<NiceMock<MockMediaFoundationCdmProxy>>();

    // MF MediaEngine holds IMFMediaSource (MediaFoundationSourceWrapper) even
    // after the test finishes, which holds a reference to the `mf_cdm_proxy_`.
    testing::Mock::AllowLeak(mf_cdm_proxy_.get());

    MockMediaProtectionPMPServer::MakeMockMediaProtectionPMPServer(
        &pmp_server_);

    LUID empty_luid{0, 0};
    mf_renderer_ = std::make_unique<MediaFoundationRenderer>(
        task_environment_.GetMainThreadTaskRunner(),
        std::make_unique<NullMediaLog>(), empty_luid);

    // Some default actions.
    ON_CALL(cdm_context_, GetMediaFoundationCdmProxy())
        .WillByDefault(Return(mf_cdm_proxy_));
    ON_CALL(*mf_cdm_proxy_, GetPMPServer(_, _))
        .WillByDefault(
            Invoke(this, &MediaFoundationRendererTest::GetPMPServer));

    // Some expected calls with return values.
    EXPECT_CALL(media_resource_, GetAllStreams())
        .WillRepeatedly(
            Invoke(this, &MediaFoundationRendererTest::GetAllStreams));
  }

  ~MediaFoundationRendererTest() override { mf_renderer_.reset(); }

  void AddStream(DemuxerStream::Type type, bool encrypted) {
    streams_.push_back(CreateMockDemuxerStream(type, encrypted));
  }

  std::vector<raw_ptr<DemuxerStream>> GetAllStreams() {
    std::vector<raw_ptr<DemuxerStream>> streams;

    for (auto& stream : streams_) {
      streams.push_back(stream.get());
    }

    return streams;
  }

  HRESULT GetPMPServer(REFIID riid, LPVOID* object_result) {
    ComPtr<IMediaProtectionPMPServer> pmp_server;
    if (riid != __uuidof(**(&pmp_server)) || !object_result) {
      return E_INVALIDARG;
    }

    return pmp_server_.CopyTo(
        reinterpret_cast<IMediaProtectionPMPServer**>(object_result));
  }

 protected:
  // IMF* interfaces (e.g. IMediaProtectionPMPServer or
  // IMFContentDecryptionModule) may require an MTA to run successfully.
  base::win::ScopedCOMInitializer com_initializer_{
      base::win::ScopedCOMInitializer::kMTA};

  base::test::TaskEnvironment task_environment_;
  base::MockOnceCallback<void(bool)> set_cdm_cb_;
  base::MockOnceCallback<void(PipelineStatus)> renderer_init_cb_;
  NiceMock<MockCdmContext> cdm_context_;
  NiceMock<MockMediaResource> media_resource_;
  NiceMock<MockRendererClient> renderer_client_;
  scoped_refptr<NiceMock<MockMediaFoundationCdmProxy>> mf_cdm_proxy_;
  ComPtr<IMediaProtectionPMPServer> pmp_server_;
  std::unique_ptr<MediaFoundationRenderer> mf_renderer_;
  std::vector<std::unique_ptr<StrictMock<MockDemuxerStream>>> streams_;
};

TEST_F(MediaFoundationRendererTest, VerifyInitWithoutSetCdm) {
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/false);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());

  task_environment_.RunUntilIdle();
}

TEST_F(MediaFoundationRendererTest, SetCdmThenInit) {
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(set_cdm_cb_, Run(true));
  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());

  task_environment_.RunUntilIdle();
}

TEST_F(MediaFoundationRendererTest, InitThenSetCdm) {
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(set_cdm_cb_, Run(true));
  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());
  mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());

  task_environment_.RunUntilIdle();
}

TEST_F(MediaFoundationRendererTest, ClearStreamWithCdmSetsUpProtectionManager) {
  LUID empty_luid{0, 0};
  mf_renderer_ = std::make_unique<MediaFoundationRenderer>(
      task_environment_.GetMainThreadTaskRunner(),
      std::make_unique<NullMediaLog>(), empty_luid,
      /*is_testing=*/false);

  AddStream(DemuxerStream::AUDIO, /*encrypted=*/false);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/false);

  EXPECT_CALL(set_cdm_cb_, Run(true));
  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  // Expect the CDM proxy to be queried for the PMP server, proving that
  // MediaFoundationProtectionManager is initialized despite the clear streams
  // because a CDM was explicitly attached prior to initialization.
  EXPECT_CALL(*mf_cdm_proxy_, GetPMPServer(_, _)).Times(1);

  // In production, SetCdm() is strictly guaranteed to be called before
  // Initialize(). Attach the CDM first.
  mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());

  // Initialize the renderer. Because has_cdm is true, it should setup the
  // protection manager rather than early-outing into clear playback.
  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());

  task_environment_.RunUntilIdle();
}

TEST_F(MediaFoundationRendererTest,
       ClearStreamInitThenSetCdmDoesNotSetUpProtectionManager) {
  LUID empty_luid{0, 0};
  mf_renderer_ = std::make_unique<MediaFoundationRenderer>(
      task_environment_.GetMainThreadTaskRunner(),
      std::make_unique<NullMediaLog>(), empty_luid,
      /*is_testing=*/false);

  AddStream(DemuxerStream::AUDIO, /*encrypted=*/false);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/false);

  EXPECT_CALL(set_cdm_cb_, Run(true));
  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  // Expect the CDM proxy to NOT be queried for the PMP server, proving that
  // MediaFoundationProtectionManager is NOT initialized because the CDM was
  // not attached prior to initialization and the streams are clear.
  EXPECT_CALL(*mf_cdm_proxy_, GetPMPServer(_, _)).Times(0);

  // Initialize the renderer first. Because has_cdm is false and streams are
  // clear, it should early-out to clear playback.
  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());

  // Attach the CDM second. This should still succeed, but won't trigger
  // protection manager setup.
  mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());

  task_environment_.RunUntilIdle();
}

TEST_F(MediaFoundationRendererTest, DirectCompositionHandle) {
  base::MockCallback<MediaFoundationRendererExtension::GetDCompSurfaceCB>
      get_dcomp_surface_cb;

  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(set_cdm_cb_, Run(true));
  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));
  // Ignore the DirectComposition handle value returned as our |pmp_server_|
  // has no real implementation.
  EXPECT_CALL(get_dcomp_surface_cb, Run(_, _));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());
  mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
  mf_renderer_->GetDCompSurface(get_dcomp_surface_cb.Get());

  task_environment_.RunUntilIdle();
}

TEST_F(MediaFoundationRendererTest, SetOutputRect_ErrorCases) {
  base::HistogramTester histogram_tester;
  base::MockCallback<MediaFoundationRendererExtension::SetOutputRectCB>
      set_output_rect_cb;

  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(set_cdm_cb_, Run(true));
  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));
  EXPECT_CALL(set_output_rect_cb, Run(true));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());
  mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
  mf_renderer_->SetOutputRect(gfx::Rect(0, 0, 100, 100),
                              set_output_rect_cb.Get());

  task_environment_.RunUntilIdle();

  histogram_tester.ExpectBucketCount(
      "Media.MediaFoundationRenderer.SetOutputRect.Hresult", S_OK, 1);
}

TEST_F(MediaFoundationRendererTest, IgnoreSubsequentErrorsAfterFirstError) {
  // Use encrypted streams to avoid SetSourceOnMediaEngine() being called
  // automatically during Initialize(). This prevents the MediaEngine from
  // generating events that could interfere with the test.
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());

  testing::InSequence s;

  // We only expect one error to be reported.
  EXPECT_CALL(renderer_client_, OnError(_)).Times(1);

  // After the error, no other event calls to the client should be made.
  EXPECT_CALL(renderer_client_, OnWaiting(_)).Times(0);

  MediaEngineNotifyImpl* media_engine_notify =
      mf_renderer_->GetMediaEngineNotifyForTesting();
  ASSERT_TRUE(media_engine_notify);

  // Simulate the first error event.
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_ERROR,
                                   MF_MEDIA_ENGINE_ERR_DECODE, E_FAIL);
  task_environment_.RunUntilIdle();

  // Simulate a subsequent error. This should be ignored because the renderer
  // is already in an error state.
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_ERROR,
                                   MF_MEDIA_ENGINE_ERR_DECODE, E_UNEXPECTED);
  task_environment_.RunUntilIdle();

  // Simulate a subsequent event. This should be ignored because the renderer
  // is already in an error state.
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_WAITING, 0L, 0L);
  task_environment_.RunUntilIdle();

  testing::Mock::VerifyAndClearExpectations(&renderer_client_);
}

// No error reported.
TEST_F(MediaFoundationRendererTest, SubsequentEventOrError_NoError) {
  base::HistogramTester histogram_tester;
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(set_cdm_cb_, Run(true));
  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());
  mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());

  task_environment_.RunUntilIdle();

  // Destroy the renderer to trigger MediaEngineNotifyImpl's destructor
  mf_renderer_.reset();

  // Process any pending tasks (like histogram recording)
  task_environment_.RunUntilIdle();

  histogram_tester.ExpectTotalCount(kSubsequentEventOrErrorReportedUmaName, 0);
  histogram_tester.ExpectTotalCount(kSubsequentErrorUmaName, 0);
  histogram_tester.ExpectTotalCount(kSubsequentEventUmaName, 0);
}

// One error but not subsequent event or error reported.
TEST_F(MediaFoundationRendererTest, SubsequentEventOrError_OneError) {
  base::HistogramTester histogram_tester;
  // Use encrypted streams to avoid SetSourceOnMediaEngine() being called
  // automatically during Initialize(). This prevents the MediaEngine from
  // generating events that could interfere with the test.
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());

  testing::InSequence s;

  // We only expect one error to be reported.
  EXPECT_CALL(renderer_client_, OnError(_)).Times(1);

  MediaEngineNotifyImpl* media_engine_notify =
      mf_renderer_->GetMediaEngineNotifyForTesting();
  ASSERT_TRUE(media_engine_notify);

  // Simulate the first error event.
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_ERROR,
                                   MF_MEDIA_ENGINE_ERR_DECODE, E_FAIL);
  task_environment_.RunUntilIdle();

  testing::Mock::VerifyAndClearExpectations(&renderer_client_);

  // Destroy the renderer to trigger MediaEngineNotifyImpl's destructor
  mf_renderer_.reset();

  // Process any pending tasks (like histogram recording)
  task_environment_.RunUntilIdle();

  histogram_tester.ExpectBucketCount(
      base::StrCat(
          {kSubsequentErrorUmaName, ".MF_MEDIA_ENGINE_ERR_DECODE.Hresult"}),
      E_FAIL, 0);
  histogram_tester.ExpectBucketCount(kSubsequentErrorUmaName,
                                     MF_MEDIA_ENGINE_ERR_DECODE, 0);
  histogram_tester.ExpectTotalCount(kSubsequentEventUmaName, 0);
  histogram_tester.ExpectBucketCount(kSubsequentEventOrErrorReportedUmaName,
                                     false, 1);
}

// One error and then a subsequent error reported.
TEST_F(MediaFoundationRendererTest, SubsequentEventOrError_TwoErrors) {
  base::HistogramTester histogram_tester;
  // Use encrypted streams to avoid SetSourceOnMediaEngine() being called
  // automatically during Initialize(). This prevents the MediaEngine from
  // generating events that could interfere with the test.
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());

  testing::InSequence s;

  // We only expect one error to be reported.
  EXPECT_CALL(renderer_client_, OnError(_)).Times(1);

  MediaEngineNotifyImpl* media_engine_notify =
      mf_renderer_->GetMediaEngineNotifyForTesting();
  ASSERT_TRUE(media_engine_notify);

  // Simulate the first error event.
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_ERROR,
                                   MF_MEDIA_ENGINE_ERR_DECODE, E_UNEXPECTED);
  task_environment_.RunUntilIdle();

  // Simulate a subsequent error. This should be ignored because the renderer
  // is already in an error state.
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_ERROR,
                                   MF_MEDIA_ENGINE_ERR_DECODE, E_FAIL);
  task_environment_.RunUntilIdle();

  testing::Mock::VerifyAndClearExpectations(&renderer_client_);

  // Destroy the renderer to trigger MediaEngineNotifyImpl's destructor
  mf_renderer_.reset();

  // Process any pending tasks (like histogram recording)
  task_environment_.RunUntilIdle();

  histogram_tester.ExpectBucketCount(
      base::StrCat(
          {kSubsequentErrorUmaName, ".MF_MEDIA_ENGINE_ERR_DECODE.Hresult"}),
      E_FAIL, 1);
  histogram_tester.ExpectBucketCount(kSubsequentErrorUmaName,
                                     MF_MEDIA_ENGINE_ERR_DECODE, 1);
  histogram_tester.ExpectTotalCount(kSubsequentEventUmaName, 0);
  histogram_tester.ExpectBucketCount(kSubsequentEventOrErrorReportedUmaName,
                                     true, 1);
}

// One error and then a subsequent event reported.
TEST_F(MediaFoundationRendererTest,
       SubsequentEventOrError_OneErrorThenOneEvent) {
  base::HistogramTester histogram_tester;
  // Use encrypted streams to avoid SetSourceOnMediaEngine() being called
  // automatically during Initialize(). This prevents the MediaEngine from
  // generating events that could interfere with the test.
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));

  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());

  testing::InSequence s;

  // We only expect one error to be reported.
  EXPECT_CALL(renderer_client_, OnError(_)).Times(1);

  MediaEngineNotifyImpl* media_engine_notify =
      mf_renderer_->GetMediaEngineNotifyForTesting();
  ASSERT_TRUE(media_engine_notify);

  // Simulate the first error event.
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_ERROR,
                                   MF_MEDIA_ENGINE_ERR_DECODE, E_UNEXPECTED);
  task_environment_.RunUntilIdle();

  // Simulate a subsequent event. This should be ignored because the renderer
  // is already in an error state.
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_ENDED, 0L, 0L);
  task_environment_.RunUntilIdle();

  testing::Mock::VerifyAndClearExpectations(&renderer_client_);

  // Destroy the renderer to trigger MediaEngineNotifyImpl's destructor
  mf_renderer_.reset();

  // Process any pending tasks (like histogram recording)
  task_environment_.RunUntilIdle();

  histogram_tester.ExpectBucketCount(
      base::StrCat(
          {kSubsequentErrorUmaName, ".MF_MEDIA_ENGINE_ERR_DECODE.Hresult"}),
      E_UNEXPECTED, 0);
  histogram_tester.ExpectBucketCount(kSubsequentErrorUmaName,
                                     MF_MEDIA_ENGINE_ERR_DECODE, 0);
  histogram_tester.ExpectBucketCount(kSubsequentEventUmaName,
                                     MF_MEDIA_ENGINE_EVENT_ENDED, 1);
  histogram_tester.ExpectBucketCount(kSubsequentEventOrErrorReportedUmaName,
                                     true, 1);
}

// A frame step completing during a seek must leave the Media Engine paused for
// StartPlayingFrom().
TEST_F(MediaFoundationRendererTest,
       FrameStepCompletedDuringFlushDoesNotResume) {
  // Use encrypted streams to avoid SetSourceOnMediaEngine() being called
  // automatically during Initialize(). This prevents the MediaEngine from
  // generating events that could interfere with the test.
  AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
  AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);

  EXPECT_CALL(renderer_init_cb_, Run(HasStatusCode(PIPELINE_OK)));
  mf_renderer_->Initialize(&media_resource_, &renderer_client_,
                           renderer_init_cb_.Get());
  task_environment_.RunUntilIdle();

  MediaEngineNotifyImpl* media_engine_notify =
      mf_renderer_->GetMediaEngineNotifyForTesting();
  ASSERT_TRUE(media_engine_notify);
  IMFMediaEngine* media_engine = mf_renderer_->GetMediaEngineForTesting();
  ASSERT_TRUE(media_engine);

  // Flush() pauses the Media Engine for the seek.
  base::MockOnceClosure flush_cb;
  EXPECT_CALL(flush_cb, Run());
  mf_renderer_->Flush(flush_cb.Get());
  task_environment_.RunUntilIdle();

  ASSERT_TRUE(media_engine->IsPaused());

  // The frame step completes before StartPlayingFrom().
  media_engine_notify->EventNotify(MF_MEDIA_ENGINE_EVENT_FRAMESTEPCOMPLETED, 0L,
                                   0L);
  task_environment_.RunUntilIdle();

  EXPECT_TRUE(media_engine->IsPaused());
}
}  // namespace media
