Chromium Code Reviews| Index: media/filters/video_frame_stream_unittest.cc |
| diff --git a/media/filters/video_frame_stream_unittest.cc b/media/filters/video_frame_stream_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..581eee98d74760b1d2562d801fd42408417acc03 |
| --- /dev/null |
| +++ b/media/filters/video_frame_stream_unittest.cc |
| @@ -0,0 +1,380 @@ |
| +// Copyright (c) 2012 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/callback_helpers.h" |
| +#include "base/message_loop.h" |
| +#include "media/base/gmock_callback_support.h" |
| +#include "media/base/mock_filters.h" |
| +#include "media/base/test_helpers.h" |
| +#include "media/filters/video_frame_stream.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +using ::testing::_; |
| +using ::testing::Assign; |
| +using ::testing::AtMost; |
| +using ::testing::NiceMock; |
| +using ::testing::NotNull; |
| +using ::testing::Return; |
| +using ::testing::ReturnRef; |
| +using ::testing::SaveArg; |
| +using ::testing::StrictMock; |
| + |
| +namespace media { |
| + |
| +static const VideoFrame::Format kVideoFormat = VideoFrame::YV12; |
| +static const gfx::Size kCodedSize(320, 240); |
| +static const gfx::Rect kVisibleRect(320, 240); |
| +static const gfx::Size kNaturalSize(320, 240); |
| + |
| +class VideoFrameStreamTest : public testing::TestWithParam<bool> { |
| + public: |
| + VideoFrameStreamTest() |
| + : video_frame_stream_( |
| + message_loop_.message_loop_proxy(), |
| + base::Bind(&VideoFrameStreamTest::SetDecryptorReadyCallback, |
| + base::Unretained(this))), |
| + video_config_(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, kVideoFormat, |
| + kCodedSize, kVisibleRect, kNaturalSize, NULL, 0, |
| + GetParam()), |
| + demuxer_stream_(new StrictMock<MockDemuxerStream>()), |
| + decryptor_(new NiceMock<MockDecryptor>()), |
| + decoder_(new StrictMock<MockVideoDecoder>()), |
| + is_initialized_(false) { |
| + decoders_.push_back(decoder_); |
| + |
| + EXPECT_CALL(*demuxer_stream_, type()) |
| + .WillRepeatedly(Return(DemuxerStream::VIDEO)); |
| + EXPECT_CALL(*demuxer_stream_, video_decoder_config()) |
| + .WillRepeatedly(ReturnRef(video_config_)); |
| + |
| + EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) |
| + .WillRepeatedly(RunCallback<0>(decryptor_.get())); |
| + |
| + // Decryptor can only decrypt (not decrypt-and-decode) so that |
| + // DecryptingDemuxerStream will be used. |
| + EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) |
| + .WillRepeatedly(RunCallback<1>(false)); |
| + EXPECT_CALL(*decryptor_, Decrypt(_, _, _)) |
| + .WillRepeatedly(RunCallback<2>(Decryptor::kSuccess, |
| + scoped_refptr<DecoderBuffer>())); |
| + } |
| + |
| + ~VideoFrameStreamTest() { |
| + if (is_initialized_) |
| + Stop(); |
| + EXPECT_FALSE(is_initialized_); |
| + } |
|
scherkus (not reviewing)
2013/03/22 01:02:40
add EXPECT_FALSE() for all the pending cbs?
xhwang
2013/03/22 07:23:28
Done.
|
| + |
| + MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&)); |
| + MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&)); |
| + MOCK_METHOD2(OnInitialized, void(bool, bool)); |
| + MOCK_METHOD2(OnFrameRead, void(VideoDecoder::Status, |
| + const scoped_refptr<VideoFrame>&)); |
| + MOCK_METHOD0(OnReset, void()); |
| + MOCK_METHOD0(OnStopped, void()); |
| + |
| + void InitializeAndExpectStatus(bool success) { |
| + EXPECT_CALL(*decoder_, Initialize(_, _, _)) |
| + .WillRepeatedly(RunCallback<1>( |
| + success ? PIPELINE_OK : DECODER_ERROR_NOT_SUPPORTED)); |
| + EXPECT_CALL(*this, OnInitialized(success, false)) |
| + .WillOnce(SaveArg<0>(&is_initialized_)); |
| + |
| + video_frame_stream_.Initialize( |
| + demuxer_stream_, |
| + decoders_, |
| + base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)), |
| + base::Bind(&VideoFrameStreamTest::OnInitialized, |
| + base::Unretained(this))); |
| + |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void Initialize() { |
| + InitializeAndExpectStatus(true); |
| + } |
| + |
| + void InitializeAndFail() { |
| + InitializeAndExpectStatus(false); |
| + } |
| + |
| + void ReadFrame() { |
| + EXPECT_CALL(*decoder_, Read(_)) |
|
scherkus (not reviewing)
2013/03/22 01:02:40
nit: perhaps ReadFrame, Reset, Stop, etc.. can be
xhwang
2013/03/22 07:23:28
Yes, I thought about that too. Done.
|
| + .WillRepeatedly(RunCallback<0>(VideoDecoder::kOk, |
| + scoped_refptr<VideoFrame>())); |
| + EXPECT_CALL(*this, OnFrameRead(VideoDecoder::kOk, _)); |
| + video_frame_stream_.ReadFrame(base::Bind(&VideoFrameStreamTest::OnFrameRead, |
| + base::Unretained(this))); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void Reset() { |
| + EXPECT_CALL(*decoder_, Reset(_)) |
| + .WillRepeatedly(RunCallback<0>()); |
| + EXPECT_CALL(*this, OnReset()); |
| + video_frame_stream_.Reset(base::Bind(&VideoFrameStreamTest::OnReset, |
| + base::Unretained(this))); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void Stop() { |
| + EXPECT_CALL(*decoder_, Stop(_)) |
| + .WillRepeatedly(RunCallback<0>()); |
| + EXPECT_CALL(*this, OnStopped()) |
| + .WillOnce(Assign(&is_initialized_, false)); |
| + video_frame_stream_.Stop(base::Bind(&VideoFrameStreamTest::OnStopped, |
| + base::Unretained(this))); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void EnterPendingInitializationState() { |
| + EXPECT_CALL(*decoder_, Initialize(_, _, _)) |
| + .WillOnce(SaveArg<1>(&decoder_init_cb_)); |
| + video_frame_stream_.Initialize( |
| + demuxer_stream_, |
| + decoders_, |
| + base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)), |
| + base::Bind(&VideoFrameStreamTest::OnInitialized, |
| + base::Unretained(this))); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void SatisfyPendingInitialization(bool success) { |
| + EXPECT_CALL(*this, OnInitialized(success, false)); |
| + base::ResetAndReturn(&decoder_init_cb_).Run( |
| + success ? PIPELINE_OK : DECODER_ERROR_NOT_SUPPORTED); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void EnterPendingReadFrameState() { |
| + EXPECT_CALL(*decoder_, Read(_)) |
| + .WillOnce(SaveArg<0>(&decoder_read_cb_)); |
| + EXPECT_CALL(*this, OnFrameRead(VideoDecoder::kOk, _)); |
| + video_frame_stream_.ReadFrame(base::Bind(&VideoFrameStreamTest::OnFrameRead, |
| + base::Unretained(this))); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void SatisfyReadFrameCallback() { |
| + base::ResetAndReturn(&decoder_read_cb_).Run(VideoDecoder::kOk, |
| + scoped_refptr<VideoFrame>()); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void EnterPendingResetState() { |
| + EXPECT_CALL(*decoder_, Reset(_)) |
| + .WillOnce(SaveArg<0>(&decoder_reset_cb_)); |
| + EXPECT_CALL(*this, OnReset()); |
| + video_frame_stream_.Reset(base::Bind(&VideoFrameStreamTest::OnReset, |
| + base::Unretained(this))); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void SatisfyResetCallback() { |
| + base::ResetAndReturn(&decoder_reset_cb_).Run(); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void EnterPendingStopState() { |
| + // If initialization failed, we won't call VideoDecoder::Stop() during |
| + // the stopping process. |
| + EXPECT_CALL(*decoder_, Stop(_)) |
| + .Times(AtMost(1)) |
| + .WillRepeatedly(SaveArg<0>(&decoder_stop_cb_)); |
| + EXPECT_CALL(*this, OnStopped()) |
| + .WillOnce(Assign(&is_initialized_, false)); |
| + video_frame_stream_.Stop(base::Bind(&VideoFrameStreamTest::OnStopped, |
| + base::Unretained(this))); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + void SatisfyStopCallback() { |
| + base::ResetAndReturn(&decoder_stop_cb_).Run(); |
| + message_loop_.RunUntilIdle(); |
| + } |
| + |
| + // Satisfy all pending callback in order. Note that VideoDecoder must return |
| + // callbacks in init -> read -> reset -> stop order (if any applicable). |
| + void SatisfyAllPendingCallbacksWithInitResult(bool init_result) { |
| + if (!decoder_init_cb_.is_null()) |
| + SatisfyPendingInitialization(init_result); |
| + |
| + if (!decoder_read_cb_.is_null()) |
| + SatisfyReadFrameCallback(); |
| + |
| + if (!decoder_reset_cb_.is_null()) |
| + SatisfyResetCallback(); |
| + |
| + if (!decoder_stop_cb_.is_null()) |
| + SatisfyStopCallback(); |
| + } |
| + |
| + void SatisfyAllPendingCallbacks() { |
| + SatisfyAllPendingCallbacksWithInitResult(true); |
| + } |
| + |
| + MessageLoop message_loop_; |
|
scherkus (not reviewing)
2013/03/22 01:02:40
I believe all of these can be private, right?
xhwang
2013/03/22 07:23:28
Done.
|
| + |
| + VideoFrameStream video_frame_stream_; |
| + VideoDecoderConfig video_config_; |
| + scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_stream_; |
| + // Use NiceMock since we don't care about most of calls on the decryptor, e.g. |
| + // RegisterNewKeyCB(). |
| + scoped_ptr<NiceMock<MockDecryptor> > decryptor_; |
| + scoped_refptr<StrictMock<MockVideoDecoder> > decoder_; |
| + VideoFrameStream::VideoDecoderList decoders_; |
| + |
| + // Callbacks to simulate pending decoder operations. |
| + PipelineStatusCB decoder_init_cb_; |
| + VideoDecoder::ReadCB decoder_read_cb_; |
| + base::Closure decoder_reset_cb_; |
| + base::Closure decoder_stop_cb_; |
| + |
| + bool is_initialized_; |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(VideoFrameStreamTest); |
| +}; |
| + |
| +INSTANTIATE_TEST_CASE_P(, VideoFrameStreamTest, testing::Bool()); |
|
scherkus (not reviewing)
2013/03/22 01:02:40
huh? what's the difference?
xhwang
2013/03/22 07:23:28
We test both encrypted/clear demuxer stream cases.
|
| + |
| +TEST_P(VideoFrameStreamTest, Initialization) { |
| + Initialize(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Initialization_Failed) { |
| + InitializeAndFail(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, ReadFrame) { |
| + Initialize(); |
| + ReadFrame(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, ReadFrame_Multiple) { |
| + Initialize(); |
| + for (int i = 0; i < 10; ++i) |
| + ReadFrame(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, ReadFrame_AfterReset) { |
| + Initialize(); |
| + Reset(); |
| + ReadFrame(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Reset_AfterInitialization) { |
| + Initialize(); |
| + Reset(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Reset_DuringReadFrame) { |
| + Initialize(); |
| + EnterPendingReadFrameState(); |
| + EnterPendingResetState(); |
| + SatisfyAllPendingCallbacks(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Reset_AfterReadFrame) { |
| + Initialize(); |
| + ReadFrame(); |
| + Reset(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, MultipleReadFramesAndResets) { |
|
scherkus (not reviewing)
2013/03/22 01:02:40
what is this testing?
xhwang
2013/03/22 07:23:28
I want to be a little bit crazy here. But probably
|
| + Initialize(); |
| + for (int i = 0; i < 2; ++i) |
| + ReadFrame(); |
| + for (int i = 0; i < 3; ++i) |
| + Reset(); |
| + for (int i = 0; i < 4; ++i) |
| + ReadFrame(); |
| + for (int i = 0; i < 5; ++i) |
| + Reset(); |
| + EnterPendingReadFrameState(); |
| + EnterPendingResetState(); |
| + SatisfyAllPendingCallbacks(); |
| + ReadFrame(); |
| + Reset(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_BeforeInitialization) { |
| + Stop(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_DuringInitialization) { |
| + EnterPendingInitializationState(); |
| + EnterPendingStopState(); |
| + SatisfyAllPendingCallbacks(); |
|
scherkus (not reviewing)
2013/03/22 01:02:40
I'm not so sure I like this "satisfy everything" h
xhwang
2013/03/22 07:23:28
Done.
|
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_DuringFailedInitialization) { |
| + EnterPendingInitializationState(); |
| + EnterPendingStopState(); |
| + SatisfyAllPendingCallbacksWithInitResult(false); |
|
scherkus (not reviewing)
2013/03/22 01:02:40
I think this would be clearer if you had:
Satisfy
xhwang
2013/03/22 07:23:28
Done.
|
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_AfterInitialization) { |
| + Initialize(); |
| + Stop(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_AfterFailedInitialization) { |
| + InitializeAndFail(); |
| + Stop(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_AfterReadFrame) { |
| + Initialize(); |
| + ReadFrame(); |
| + Stop(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_DuringReadFrame) { |
| + Initialize(); |
| + EnterPendingReadFrameState(); |
| + EnterPendingStopState(); |
| + SatisfyAllPendingCallbacks(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, StopAfterReset) { |
| + Initialize(); |
| + Reset(); |
| + Stop(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_DuringReset) { |
| + Initialize(); |
| + EnterPendingResetState(); |
| + EnterPendingStopState(); |
| + SatisfyAllPendingCallbacks(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_AfterReadFrame_AfterReset) { |
| + Initialize(); |
| + ReadFrame(); |
| + Reset(); |
| + Stop(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_DuringReadFrame_DuringReset) { |
| + Initialize(); |
| + EnterPendingReadFrameState(); |
| + EnterPendingResetState(); |
| + EnterPendingStopState(); |
| + SatisfyAllPendingCallbacks(); |
| +} |
| + |
| +TEST_P(VideoFrameStreamTest, Stop_AfterReadFrame_DuringReset) { |
| + Initialize(); |
| + EnterPendingReadFrameState(); |
| + EnterPendingResetState(); |
| + SatisfyReadFrameCallback(); |
| + EnterPendingStopState(); |
| + SatisfyResetCallback(); |
| + SatisfyStopCallback(); |
| +} |
| + |
| +} // namespace media |