Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1618)

Unified Diff: media/mojo/clients/mojo_renderer_unittest.cc

Issue 2070753002: media: Add unittest for MojoRenderer{Impl|Service} (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/mojo/clients/mojo_renderer_unittest.cc
diff --git a/media/mojo/clients/mojo_renderer_unittest.cc b/media/mojo/clients/mojo_renderer_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8f90d3351e0fb31affdb01590881694470c00285
--- /dev/null
+++ b/media/mojo/clients/mojo_renderer_unittest.cc
@@ -0,0 +1,382 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/platform_thread.h"
+#include "media/base/cdm_config.h"
+#include "media/base/cdm_context.h"
+#include "media/base/gmock_callback_support.h"
+#include "media/base/mock_filters.h"
+#include "media/base/test_helpers.h"
+#include "media/cdm/default_cdm_factory.h"
+#include "media/mojo/clients/mojo_renderer_impl.h"
+#include "media/mojo/common/media_type_converters.h"
+#include "media/mojo/interfaces/content_decryption_module.mojom.h"
+#include "media/mojo/interfaces/renderer.mojom.h"
+#include "media/mojo/services/mojo_cdm_service.h"
+#include "media/mojo/services/mojo_cdm_service_context.h"
+#include "media/mojo/services/mojo_renderer_service.h"
+#include "media/renderers/video_overlay_factory.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+
+namespace media {
+
+const int64_t kStartPlayingTimeInMs = 100;
+const char kClearKeyKeySystem[] = "org.w3.clearkey";
+
+ACTION_P2(SetError, renderer_client, error) {
+ renderer_client->OnError(error);
+}
+
+class MojoRendererTest : public ::testing::Test {
+ public:
+ MojoRendererTest() {
+ std::unique_ptr<StrictMock<MockRenderer>> mock_renderer(
+ new StrictMock<MockRenderer>());
+ mock_renderer_ = mock_renderer.get();
+
+ mojom::RendererPtr remote_renderer;
+
+ mojo_renderer_service_ = new MojoRendererService(
+ mojo_cdm_service_context_.GetWeakPtr(), std::move(mock_renderer),
+ mojo::GetProxy(&remote_renderer));
+
+ mojo_renderer_.reset(
+ new MojoRendererImpl(message_loop_.task_runner(),
+ std::unique_ptr<VideoOverlayFactory>(nullptr),
+ nullptr, std::move(remote_renderer)));
+
+ // CreateAudioStream() and CreateVideoStream() overrides expectations for
+ // expected non-NULL streams.
+ EXPECT_CALL(demuxer_, GetStream(_)).WillRepeatedly(Return(nullptr));
+
+ EXPECT_CALL(*mock_renderer_, GetMediaTime())
+ .WillRepeatedly(Return(base::TimeDelta()));
+ }
+
+ virtual ~MojoRendererTest() {}
+
+ void Destroy() {
+ mojo_renderer_.reset();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ // Completion callbacks.
+ MOCK_METHOD1(OnInitialized, void(PipelineStatus));
+ MOCK_METHOD0(OnFlushed, void());
+ MOCK_METHOD1(OnCdmAttached, void(bool));
+
+ std::unique_ptr<StrictMock<MockDemuxerStream>> CreateStream(
+ DemuxerStream::Type type) {
+ std::unique_ptr<StrictMock<MockDemuxerStream>> stream(
+ new StrictMock<MockDemuxerStream>(type));
+ return stream;
+ }
+
+ void CreateAudioStream() {
+ audio_stream_ = CreateStream(DemuxerStream::AUDIO);
+ EXPECT_CALL(demuxer_, GetStream(DemuxerStream::AUDIO))
+ .WillRepeatedly(Return(audio_stream_.get()));
+ }
+
+ void CreateVideoStream(bool is_encrypted = false) {
+ video_stream_ = CreateStream(DemuxerStream::VIDEO);
+ video_stream_->set_video_decoder_config(
+ is_encrypted ? TestVideoConfig::NormalEncrypted()
+ : TestVideoConfig::Normal());
+ EXPECT_CALL(demuxer_, GetStream(DemuxerStream::VIDEO))
+ .WillRepeatedly(Return(video_stream_.get()));
+ }
+
+ void InitializeAndExpect(PipelineStatus status) {
+ DVLOG(1) << __FUNCTION__ << ": " << status;
+ EXPECT_CALL(*this, OnInitialized(status));
+ mojo_renderer_->Initialize(
+ &demuxer_, &renderer_client_,
+ base::Bind(&MojoRendererTest::OnInitialized, base::Unretained(this)));
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void Initialize() {
+ CreateAudioStream();
+ EXPECT_CALL(*mock_renderer_, Initialize(_, _, _))
+ .WillOnce(DoAll(SaveArg<1>(&remote_renderer_client_),
+ RunCallback<2>(PIPELINE_OK)));
+ InitializeAndExpect(PIPELINE_OK);
+ }
+
+ void Flush() {
+ DVLOG(1) << __FUNCTION__;
+ // Flush callback should always be fired.
+ EXPECT_CALL(*this, OnFlushed());
+ mojo_renderer_->Flush(
+ base::Bind(&MojoRendererTest::OnFlushed, base::Unretained(this)));
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void SetCdmAndExpect(bool success) {
+ DVLOG(1) << __FUNCTION__;
+ // Set CDM callback should always be fired.
+ EXPECT_CALL(*this, OnCdmAttached(success));
+ mojo_renderer_->SetCdm(
+ &cdm_context_,
+ base::Bind(&MojoRendererTest::OnCdmAttached, base::Unretained(this)));
+ base::RunLoop().RunUntilIdle();
+ }
+
+ // Simulates a connection error at the client side by killing the service.
+ // Note that |mock_renderer_| will also be destroyed, do NOT expect anything
+ // on it. Otherwise the test will crash.
+ void ConnectionError() {
+ DVLOG(1) << __FUNCTION__;
+ delete mojo_renderer_service_;
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void OnCdmCreated(mojom::CdmPromiseResultPtr result,
+ int cdm_id,
+ mojom::DecryptorPtr decryptor) {
+ EXPECT_TRUE(result->success);
+ EXPECT_NE(CdmContext::kInvalidCdmId, cdm_id);
+ cdm_context_.set_cdm_id(cdm_id);
+ }
+
+ void CreateCdm() {
+ new MojoCdmService(mojo_cdm_service_context_.GetWeakPtr(), &cdm_factory_,
+ mojo::GetProxy(&remote_cdm_));
+ remote_cdm_->Initialize(
+ kClearKeyKeySystem, "https://www.test.com",
+ mojom::CdmConfig::From(CdmConfig()),
+ base::Bind(&MojoRendererTest::OnCdmCreated, base::Unretained(this)));
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void StartPlayingFrom(base::TimeDelta start_time) {
+ EXPECT_CALL(*mock_renderer_, StartPlayingFrom(start_time));
+ mojo_renderer_->StartPlayingFrom(start_time);
+ EXPECT_EQ(start_time, mojo_renderer_->GetMediaTime());
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void Play() {
+ StartPlayingFrom(base::TimeDelta::FromMilliseconds(kStartPlayingTimeInMs));
+ }
+
+ // Fixture members.
+ base::MessageLoop message_loop_;
+
+ // The MojoRendererImpl that we are testing.
+ std::unique_ptr<MojoRendererImpl> mojo_renderer_;
+
+ // Client side mocks and helpers.
+ StrictMock<MockRendererClient> renderer_client_;
+ StrictMock<MockCdmContext> cdm_context_;
+ mojom::ContentDecryptionModulePtr remote_cdm_;
+
+ // Client side mock demuxer and demuxer streams.
+ StrictMock<MockDemuxer> demuxer_;
+ std::unique_ptr<StrictMock<MockDemuxerStream>> audio_stream_;
+ std::unique_ptr<StrictMock<MockDemuxerStream>> video_stream_;
+
+ // Service side mocks and helpers.
+ StrictMock<MockRenderer>* mock_renderer_;
+ MojoCdmServiceContext mojo_cdm_service_context_;
+ RendererClient* remote_renderer_client_;
+ DefaultCdmFactory cdm_factory_;
+
+ // Owned by the connection. But we can delete it manually to trigger a
+ // connection error at the client side. See ConnectionError();
+ MojoRendererService* mojo_renderer_service_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MojoRendererTest);
+};
+
+TEST_F(MojoRendererTest, Initialize_Success) {
+ Initialize();
+}
+
+TEST_F(MojoRendererTest, Initialize_Failure) {
+ CreateAudioStream();
+ // Mojo Renderer only expects a boolean result, which will be translated
+ // to PIPELINE_OK or PIPELINE_ERROR_INITIALIZATION_FAILED.
+ EXPECT_CALL(*mock_renderer_, Initialize(_, _, _))
+ .WillOnce(RunCallback<2>(PIPELINE_ERROR_ABORT));
+ InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, Initialize_BeforeConnectionError) {
+ CreateAudioStream();
+ EXPECT_CALL(*mock_renderer_, Initialize(_, _, _))
+ .WillOnce(InvokeWithoutArgs(this, &MojoRendererTest::ConnectionError));
+ InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, Initialize_AfterConnectionError) {
+ ConnectionError();
+ CreateAudioStream();
+ InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, Flush_Success) {
+ Initialize();
+
+ EXPECT_CALL(*mock_renderer_, Flush(_)).WillOnce(RunClosure<0>());
+ Flush();
+}
+
+TEST_F(MojoRendererTest, Flush_ConnectionError) {
+ Initialize();
+
+ // Upon connection error, OnError() should be called once and only once.
+ EXPECT_CALL(renderer_client_, OnError(PIPELINE_ERROR_DECODE)).Times(1);
+ EXPECT_CALL(*mock_renderer_, Flush(_))
+ .WillOnce(InvokeWithoutArgs(this, &MojoRendererTest::ConnectionError));
+ Flush();
+}
+
+TEST_F(MojoRendererTest, Flush_AfterConnectionError) {
+ Initialize();
+
+ // Upon connection error, OnError() should be called once and only once.
+ EXPECT_CALL(renderer_client_, OnError(PIPELINE_ERROR_DECODE)).Times(1);
+ ConnectionError();
+
+ Flush();
+}
+
+TEST_F(MojoRendererTest, SetCdm_Success) {
+ Initialize();
+ CreateCdm();
+ EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+ SetCdmAndExpect(true);
+}
+
+TEST_F(MojoRendererTest, SetCdm_Failure) {
+ Initialize();
+ CreateCdm();
+ EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(false));
+ SetCdmAndExpect(false);
+}
+
+TEST_F(MojoRendererTest, SetCdm_InvalidCdmId) {
+ Initialize();
+ SetCdmAndExpect(false);
+}
+
+TEST_F(MojoRendererTest, SetCdm_NonExistCdmId) {
+ Initialize();
+ cdm_context_.set_cdm_id(1);
+ SetCdmAndExpect(false);
+}
+
+TEST_F(MojoRendererTest, SetCdm_BeforeInitialize) {
+ CreateCdm();
+ EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+ SetCdmAndExpect(true);
+}
+
+TEST_F(MojoRendererTest, SetCdm_AfterInitializeAndConnectionError) {
+ CreateCdm();
+ Initialize();
+ EXPECT_CALL(renderer_client_, OnError(PIPELINE_ERROR_DECODE)).Times(1);
+ ConnectionError();
+ SetCdmAndExpect(false);
+}
+
+TEST_F(MojoRendererTest, SetCdm_AfterConnectionErrorAndBeforeInitialize) {
+ CreateCdm();
+ // Initialize() is not called so RendererClient::OnError() is not expected.
+ ConnectionError();
+ SetCdmAndExpect(false);
+ InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, SetCdm_BeforeInitializeAndConnectionError) {
+ CreateCdm();
+ EXPECT_CALL(*mock_renderer_, SetCdm(_, _)).WillOnce(RunCallback<1>(true));
+ SetCdmAndExpect(true);
+ // Initialize() is not called so RendererClient::OnError() is not expected.
+ ConnectionError();
+ CreateAudioStream();
+ InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED);
+}
+
+TEST_F(MojoRendererTest, StartPlayingFrom) {
+ Initialize();
+ Play();
+}
+
+TEST_F(MojoRendererTest, GetMediaTime) {
+ Initialize();
+ EXPECT_EQ(base::TimeDelta(), mojo_renderer_->GetMediaTime());
+
+ Play();
+
+ // Time is updated periodically with a short delay.
+ const base::TimeDelta kUpdatedTime = base::TimeDelta::FromMilliseconds(500);
+ EXPECT_CALL(*mock_renderer_, GetMediaTime())
+ .WillRepeatedly(Return(kUpdatedTime));
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(kUpdatedTime, mojo_renderer_->GetMediaTime());
+}
+
+TEST_F(MojoRendererTest, OnEnded) {
+ Initialize();
+ Play();
+
+ EXPECT_CALL(renderer_client_, OnEnded()).Times(1);
+ remote_renderer_client_->OnEnded();
+ base::RunLoop().RunUntilIdle();
+}
+
+// TODO(xhwang): Add tests for all RendererClient methods.
+
+TEST_F(MojoRendererTest, Destroy_PendingInitialize) {
+ CreateAudioStream();
+ EXPECT_CALL(*mock_renderer_, Initialize(_, _, _))
+ .WillRepeatedly(RunCallback<2>(PIPELINE_ERROR_ABORT));
+ EXPECT_CALL(*this, OnInitialized(PIPELINE_ERROR_INITIALIZATION_FAILED));
+ mojo_renderer_->Initialize(
+ &demuxer_, &renderer_client_,
+ base::Bind(&MojoRendererTest::OnInitialized, base::Unretained(this)));
+ Destroy();
+}
+
+TEST_F(MojoRendererTest, Destroy_PendingFlush) {
+ EXPECT_CALL(*mock_renderer_, SetCdm(_, _))
+ .WillRepeatedly(RunCallback<1>(true));
+ EXPECT_CALL(*this, OnCdmAttached(false));
+ mojo_renderer_->SetCdm(
+ &cdm_context_,
+ base::Bind(&MojoRendererTest::OnCdmAttached, base::Unretained(this)));
+ Destroy();
+}
+
+TEST_F(MojoRendererTest, Destroy_PendingSetCdm) {
+ Initialize();
+ EXPECT_CALL(*mock_renderer_, Flush(_)).WillRepeatedly(RunClosure<0>());
+ EXPECT_CALL(*this, OnFlushed());
+ mojo_renderer_->Flush(
+ base::Bind(&MojoRendererTest::OnFlushed, base::Unretained(this)));
+ Destroy();
+}
+
+// TODO(xhwang): Add more tests on OnError. For example, ErrorDuringFlush,
+// ErrorAfterFlush etc.
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698