Index: media/filters/pipeline_controller_unittest.cc |
diff --git a/media/filters/pipeline_controller_unittest.cc b/media/filters/pipeline_controller_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..561c7b08d6abf83f33629d7c43cca41b35e73c51 |
--- /dev/null |
+++ b/media/filters/pipeline_controller_unittest.cc |
@@ -0,0 +1,212 @@ |
+// 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 "base/bind.h" |
+#include "base/bind_helpers.h" |
+#include "base/logging.h" |
+#include "base/macros.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/time/time.h" |
+#include "media/base/mock_filters.h" |
+#include "media/base/pipeline.h" |
+#include "media/filters/pipeline_controller.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using ::testing::_; |
+using ::testing::DoAll; |
+using ::testing::Mock; |
+using ::testing::Return; |
+using ::testing::SaveArg; |
+using ::testing::StrictMock; |
+ |
+namespace media { |
+ |
+class PipelineControllerTest : public ::testing::Test { |
+ public: |
+ PipelineControllerTest() |
+ : pipeline_controller_(&pipeline_, |
+ base::Bind(&PipelineControllerTest::CreateRenderer, |
+ base::Unretained(this)), |
+ base::Bind(&PipelineControllerTest::OnSeeked, |
+ base::Unretained(this)), |
+ base::Bind(&PipelineControllerTest::OnSuspended, |
+ base::Unretained(this)), |
+ base::Bind(&PipelineControllerTest::OnResumed, |
+ base::Unretained(this)), |
+ base::Bind(&PipelineControllerTest::OnError, |
+ base::Unretained(this))) {} |
+ |
+ ~PipelineControllerTest() override {} |
+ |
+ PipelineStatusCB StartPipeline() { |
+ EXPECT_FALSE(pipeline_controller_.IsStable()); |
+ PipelineStatusCB start_cb; |
+ EXPECT_CALL(pipeline_, Start(_, _, _, _, _, _, _, _, _, _)) |
+ .WillOnce(SaveArg<4>(&start_cb)); |
+ pipeline_controller_.Start( |
+ nullptr, nullptr, false, base::Closure(), PipelineMetadataCB(), |
+ BufferingStateCB(), base::Closure(), AddTextTrackCB(), base::Closure()); |
+ Mock::VerifyAndClear(&pipeline_); |
+ EXPECT_FALSE(pipeline_controller_.IsStable()); |
+ return start_cb; |
+ } |
+ |
+ PipelineStatusCB SeekPipeline(base::TimeDelta time) { |
+ EXPECT_TRUE(pipeline_controller_.IsStable()); |
+ PipelineStatusCB seek_cb; |
+ EXPECT_CALL(pipeline_, Seek(time, _)).WillOnce(SaveArg<1>(&seek_cb)); |
+ pipeline_controller_.Seek(time, true); |
+ Mock::VerifyAndClear(&pipeline_); |
+ EXPECT_FALSE(pipeline_controller_.IsStable()); |
+ return seek_cb; |
+ } |
+ |
+ PipelineStatusCB SuspendPipeline() { |
+ EXPECT_TRUE(pipeline_controller_.IsStable()); |
+ PipelineStatusCB suspend_cb; |
+ EXPECT_CALL(pipeline_, Suspend(_)).WillOnce(SaveArg<0>(&suspend_cb)); |
+ pipeline_controller_.Suspend(); |
+ Mock::VerifyAndClear(&pipeline_); |
+ EXPECT_FALSE(pipeline_controller_.IsStable()); |
+ EXPECT_FALSE(pipeline_controller_.IsSuspended()); |
+ return suspend_cb; |
+ } |
+ |
+ PipelineStatusCB ResumePipeline() { |
+ EXPECT_TRUE(pipeline_controller_.IsSuspended()); |
+ PipelineStatusCB resume_cb; |
+ EXPECT_CALL(pipeline_, Resume(_, _, _)) |
+ .WillOnce( |
+ DoAll(SaveArg<1>(&last_resume_time_), SaveArg<2>(&resume_cb))); |
+ EXPECT_CALL(pipeline_, GetMediaTime()) |
+ .WillRepeatedly(Return(base::TimeDelta())); |
+ pipeline_controller_.Resume(); |
+ Mock::VerifyAndClear(&pipeline_); |
+ EXPECT_FALSE(pipeline_controller_.IsStable()); |
+ EXPECT_FALSE(pipeline_controller_.IsSuspended()); |
+ return resume_cb; |
+ } |
+ |
+ void Complete(const PipelineStatusCB& cb) { |
+ cb.Run(PIPELINE_OK); |
+ message_loop_.RunUntilIdle(); |
+ } |
+ |
+ protected: |
+ scoped_ptr<Renderer> CreateRenderer() { return scoped_ptr<Renderer>(); } |
+ |
+ void OnSeeked(bool time_updated) { |
+ was_seeked_ = true; |
+ last_seeked_time_updated_ = time_updated; |
+ } |
+ |
+ void OnSuspended() { was_suspended_ = true; } |
+ |
+ void OnResumed() { was_resumed_ = true; } |
+ |
+ void OnError(PipelineStatus status) { NOTREACHED(); } |
+ |
+ base::MessageLoop message_loop_; |
+ |
+ StrictMock<MockPipeline> pipeline_; |
+ PipelineController pipeline_controller_; |
+ |
+ bool was_seeked_ = false; |
+ bool last_seeked_time_updated_ = false; |
+ bool was_suspended_ = false; |
+ bool was_resumed_ = false; |
+ base::TimeDelta last_resume_time_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PipelineControllerTest); |
+}; |
+ |
+TEST_F(PipelineControllerTest, Startup) { |
+ PipelineStatusCB start_cb = StartPipeline(); |
+ EXPECT_FALSE(was_seeked_); |
+ |
+ Complete(start_cb); |
+ EXPECT_TRUE(was_seeked_); |
+ EXPECT_FALSE(last_seeked_time_updated_); |
+ EXPECT_FALSE(was_suspended_); |
+ EXPECT_FALSE(was_resumed_); |
+ EXPECT_TRUE(pipeline_controller_.IsStable()); |
+} |
+ |
+TEST_F(PipelineControllerTest, SuspendResume) { |
+ Complete(StartPipeline()); |
+ EXPECT_TRUE(was_seeked_); |
+ was_seeked_ = false; |
+ |
+ Complete(SuspendPipeline()); |
+ EXPECT_TRUE(was_suspended_); |
+ EXPECT_FALSE(was_resumed_); |
+ EXPECT_FALSE(pipeline_controller_.IsStable()); |
+ |
+ Complete(ResumePipeline()); |
+ EXPECT_TRUE(was_resumed_); |
+ EXPECT_TRUE(pipeline_controller_.IsStable()); |
+ |
+ // |was_seeked_| should not be affected by Suspend()/Resume() at all. |
+ EXPECT_FALSE(was_seeked_); |
+} |
+ |
+TEST_F(PipelineControllerTest, PendingSuspend) { |
+ Complete(StartPipeline()); |
+ |
+ base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); |
+ PipelineStatusCB seek_cb = SeekPipeline(seek_time); |
+ message_loop_.RunUntilIdle(); |
+ |
+ // While the seek is ongoing, request a suspend. |
+ // It will be a mock failure if pipeline_.Suspend() is called. |
+ pipeline_controller_.Suspend(); |
+ message_loop_.RunUntilIdle(); |
+ |
+ // Expect the suspend to trigger when the seek is completed. |
+ EXPECT_CALL(pipeline_, Suspend(_)); |
+ Complete(seek_cb); |
+} |
+ |
+TEST_F(PipelineControllerTest, SeekMergesWithResume) { |
+ Complete(StartPipeline()); |
+ Complete(SuspendPipeline()); |
+ |
+ // Request a seek while suspended. |
+ // It will be a mock failure if pipeline_.Seek() is called. |
+ base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); |
+ pipeline_controller_.Seek(seek_time, true); |
+ message_loop_.RunUntilIdle(); |
+ |
+ // Resume and verify the resume time includes the seek. |
+ Complete(ResumePipeline()); |
+ EXPECT_EQ(seek_time, last_resume_time_); |
+ EXPECT_TRUE(last_seeked_time_updated_); |
+} |
+ |
+TEST_F(PipelineControllerTest, SeekMergesWithSeek) { |
+ Complete(StartPipeline()); |
+ |
+ base::TimeDelta seek_time_1 = base::TimeDelta::FromSeconds(5); |
+ PipelineStatusCB seek_cb_1 = SeekPipeline(seek_time_1); |
+ message_loop_.RunUntilIdle(); |
+ |
+ // Request another seek while the first is ongoing. |
+ base::TimeDelta seek_time_2 = base::TimeDelta::FromSeconds(10); |
+ pipeline_controller_.Seek(seek_time_2, true); |
+ message_loop_.RunUntilIdle(); |
+ |
+ // Request a third seek. (It should replace the second.) |
+ base::TimeDelta seek_time_3 = base::TimeDelta::FromSeconds(15); |
+ pipeline_controller_.Seek(seek_time_3, true); |
+ message_loop_.RunUntilIdle(); |
+ |
+ // Expect the third seek to trigger when the first seek completes. |
+ EXPECT_CALL(pipeline_, Seek(seek_time_3, _)); |
+ Complete(seek_cb_1); |
+} |
+ |
+} // namespace media |