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..ac77920ec2b2b7f46155140e26ebd7e8b57c8f7a |
--- /dev/null |
+++ b/media/filters/pipeline_controller_unittest.cc |
@@ -0,0 +1,168 @@ |
+// Copyright (c) 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/time/time.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::Mock; |
+using ::testing::NiceMock; |
+using ::testing::SaveArg; |
+ |
+namespace media { |
+ |
+ACTION_TEMPLATE(RunCallback, |
+ HAS_1_TEMPLATE_PARAMS(int, k), |
+ AND_1_VALUE_PARAMS(p0)) { |
+ return ::std::tr1::get<k>(args).Run(p0); |
+} |
+ |
+class MockPipeline : public Pipeline { |
+ public: |
+ MockPipeline() {} |
+ |
+ virtual ~MockPipeline() {} |
+ |
+ // Start() cannot be mocked due to scoped_ptr. Instead it forwards to a |
+ // version that can. |
+ MOCK_METHOD10(Start, |
+ void(Demuxer*, |
+ scoped_ptr<Renderer>*, |
+ const base::Closure&, |
+ const PipelineStatusCB&, |
+ const PipelineStatusCB&, |
+ const PipelineMetadataCB&, |
+ const BufferingStateCB&, |
+ const base::Closure&, |
+ const AddTextTrackCB&, |
+ const base::Closure&)); |
+ |
+ void Start(Demuxer* demuxer, |
+ scoped_ptr<Renderer> renderer, |
+ const base::Closure& ended_cb, |
+ const PipelineStatusCB& error_cb, |
+ const PipelineStatusCB& seek_cb, |
+ const PipelineMetadataCB& metadata_cb, |
+ const BufferingStateCB& buffering_state_cb, |
+ const base::Closure& duration_change_cb, |
+ const AddTextTrackCB& add_text_track_cb, |
+ const base::Closure& waiting_for_decryption_key_cb) override { |
+ Start(demuxer, &renderer, ended_cb, error_cb, seek_cb, metadata_cb, |
+ buffering_state_cb, duration_change_cb, add_text_track_cb, |
+ waiting_for_decryption_key_cb); |
+ } |
+ |
+ MOCK_METHOD1(Stop, void(const base::Closure&)); |
+ MOCK_METHOD2(Seek, void(base::TimeDelta, const PipelineStatusCB&)); |
+ MOCK_METHOD1(Suspend, void(const PipelineStatusCB&)); |
+ |
+ // Resume() cannot be mocked due to scoped_ptr. Instead it forwards to a |
+ // version that can. |
+ MOCK_METHOD3(Resume, |
+ void(scoped_ptr<Renderer>*, |
+ base::TimeDelta, |
+ const PipelineStatusCB&)); |
+ |
+ void Resume(scoped_ptr<Renderer> renderer, |
+ base::TimeDelta timestamp, |
+ const PipelineStatusCB& seek_cb) override { |
+ Resume(&renderer, timestamp, seek_cb); |
+ } |
+ |
+ // TODO(sandersd): It would be helpful if this always returned true once the |
+ // start callback was called (until Stop()). |
+ MOCK_CONST_METHOD0(IsRunning, bool()); |
+ |
+ // TODO(sandersd): These should be regular get/set implementations. |
+ MOCK_CONST_METHOD0(GetPlaybackRate, double()); |
+ MOCK_METHOD1(SetPlaybackRate, void(double)); |
+ MOCK_CONST_METHOD0(GetVolume, float()); |
+ MOCK_METHOD1(SetVolume, void(float)); |
+ |
+ // TODO(sandersd): Some sort of help would be useful for configuring these. |
+ MOCK_CONST_METHOD0(GetMediaTime, base::TimeDelta()); |
+ MOCK_CONST_METHOD0(GetBufferedTimeRanges, Ranges<base::TimeDelta>()); |
+ MOCK_CONST_METHOD0(GetMediaDuration, base::TimeDelta()); |
+ MOCK_METHOD0(DidLoadingProgress, bool()); |
+ MOCK_CONST_METHOD0(GetStatistics, PipelineStatistics()); |
+ |
+ MOCK_METHOD2(SetCdm, void(CdmContext*, const CdmAttachedCB&)); |
+}; |
+ |
+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::OnError, |
+ base::Unretained(this))) {} |
+ |
+ ~PipelineControllerTest() override {} |
+ |
+ void StartPipeline(ChunkDemuxer* chunk_demuxer) { |
+ // Everything other than |chunk_demuxer| is null since there is no reason |
+ // for MockPipeline to call them. |
+ pipeline_controller_.Start( |
+ chunk_demuxer, nullptr, base::Closure(), PipelineMetadataCB(), |
+ BufferingStateCB(), base::Closure(), AddTextTrackCB(), base::Closure()); |
+ } |
+ |
+ void StopPipeline() {} |
+ |
+ protected: |
+ scoped_ptr<Renderer> CreateRenderer() { return scoped_ptr<Renderer>(); } |
+ |
+ void OnSeeked(bool time_updated) {} |
+ |
+ void OnSuspended() {} |
+ |
+ void OnError(PipelineStatus status) { NOTREACHED(); } |
+ |
+ MockPipeline pipeline_; |
+ PipelineController pipeline_controller_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PipelineControllerTest); |
+}; |
+ |
+TEST_F(PipelineControllerTest, PendingSuspend) { |
+ // Immediately complete Pipline startup. |
+ EXPECT_CALL(pipeline_, Start(_, _, _, _, _, _, _, _, _, _)) |
+ .WillOnce(RunCallback<4>(PIPELINE_OK)); |
+ StartPipeline(nullptr); |
+ |
+ Mock::VerifyAndClear(&pipeline_); |
+ EXPECT_TRUE(pipeline_controller_.IsStable()); |
+ |
+ // Start a seek, but do not complete it immediately. |
+ base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); |
+ PipelineStatusCB seek_cb; |
+ EXPECT_CALL(pipeline_, Seek(seek_time, _)).WillOnce(SaveArg<1>(&seek_cb)); |
+ pipeline_controller_.Seek(seek_time, true); |
+ |
+ Mock::VerifyAndClear(&pipeline_); |
+ EXPECT_FALSE(pipeline_controller_.IsStable()); |
+ |
+ // Schedule a suspend. (It shouldn't happen yet.) |
+ pipeline_controller_.Suspend(); |
+ |
+ // Complete the seek; the suspend should trigger. |
+ EXPECT_CALL(pipeline_, Suspend(_)); |
+ seek_cb.Run(PIPELINE_OK); |
+} |
+ |
+} // namespace media |