| Index: media/filters/fake_video_decoder_unittest.cc | 
| diff --git a/media/filters/fake_video_decoder_unittest.cc b/media/filters/fake_video_decoder_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..91a6c77c9a0be76fed866266ae4675663d2eee6a | 
| --- /dev/null | 
| +++ b/media/filters/fake_video_decoder_unittest.cc | 
| @@ -0,0 +1,434 @@ | 
| +// Copyright (c) 2013 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/basictypes.h" | 
| +#include "base/bind.h" | 
| +#include "base/message_loop.h" | 
| +#include "media/base/decoder_buffer.h" | 
| +#include "media/base/mock_filters.h" | 
| +#include "media/base/test_helpers.h" | 
| +#include "media/base/video_frame.h" | 
| +#include "media/filters/fake_demuxer_stream.h" | 
| +#include "media/filters/fake_video_decoder.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +namespace media { | 
| + | 
| +static const int kDecodingDelay = 9; | 
| +static const int kNumConfigs = 3; | 
| +static const int kNumBuffersInOneConfig = 9; | 
| +static const int kNumInputBuffers = kNumConfigs * kNumBuffersInOneConfig; | 
| + | 
| +class FakeVideoDecoderTest : public testing::Test { | 
| + public: | 
| +  FakeVideoDecoderTest() | 
| +      : decoder_(new FakeVideoDecoder(kDecodingDelay)), | 
| +        demuxer_stream_( | 
| +            new FakeDemuxerStream(kNumConfigs, kNumBuffersInOneConfig, false)), | 
| +        num_decoded_frames_(0), | 
| +        is_read_pending_(false), | 
| +        is_reset_pending_(false), | 
| +        is_stop_pending_(false) {} | 
| + | 
| +  virtual ~FakeVideoDecoderTest() { | 
| +    StopAndExpect(OK); | 
| +  } | 
| + | 
| +  void Initialize() { | 
| +    decoder_->Initialize(demuxer_stream_.get(), | 
| +                         NewExpectedStatusCB(PIPELINE_OK), | 
| +                         base::Bind(&MockStatisticsCB::OnStatistics, | 
| +                                    base::Unretained(&statistics_cb_))); | 
| +    message_loop_.RunUntilIdle(); | 
| +  } | 
| + | 
| +  // Callback for VideoDecoder::Read(). | 
| +  void FrameReady(VideoDecoder::Status status, | 
| +                  const scoped_refptr<VideoFrame>& frame) { | 
| +    DCHECK(is_read_pending_); | 
| +    ASSERT_EQ(VideoDecoder::kOk, status); | 
| + | 
| +    is_read_pending_ = false; | 
| +    frame_read_ = frame; | 
| + | 
| +    if (frame && !frame->IsEndOfStream()) | 
| +      num_decoded_frames_++; | 
| +  } | 
| + | 
| +  enum CallbackResult { | 
| +    PENDING, | 
| +    OK, | 
| +    ABROTED, | 
| +    EOS | 
| +  }; | 
| + | 
| +  void ExpectReadResult(CallbackResult result) { | 
| +    switch (result) { | 
| +      case PENDING: | 
| +        EXPECT_TRUE(is_read_pending_); | 
| +        ASSERT_FALSE(frame_read_); | 
| +        break; | 
| +      case OK: | 
| +        EXPECT_FALSE(is_read_pending_); | 
| +        ASSERT_TRUE(frame_read_); | 
| +        EXPECT_FALSE(frame_read_->IsEndOfStream()); | 
| +        break; | 
| +      case ABROTED: | 
| +        EXPECT_FALSE(is_read_pending_); | 
| +        EXPECT_FALSE(frame_read_); | 
| +        break; | 
| +      case EOS: | 
| +        EXPECT_FALSE(is_read_pending_); | 
| +        ASSERT_TRUE(frame_read_); | 
| +        EXPECT_TRUE(frame_read_->IsEndOfStream()); | 
| +        break; | 
| +    } | 
| +  } | 
| + | 
| +  void ReadOneFrame() { | 
| +    frame_read_ = NULL; | 
| +    is_read_pending_ = true; | 
| +    decoder_->Read( | 
| +        base::Bind(&FakeVideoDecoderTest::FrameReady, base::Unretained(this))); | 
| +    message_loop_.RunUntilIdle(); | 
| +  } | 
| + | 
| +  void ReadUntilEOS() { | 
| +    do { | 
| +      ReadOneFrame(); | 
| +    } while (frame_read_ && !frame_read_->IsEndOfStream()); | 
| +  } | 
| + | 
| +  void EnterPendingReadState() { | 
| +    decoder_->HoldNextRead(); | 
| +    ReadOneFrame(); | 
| +    ExpectReadResult(PENDING); | 
| +  } | 
| + | 
| +  void SatisfyRead() { | 
| +    decoder_->SatisfyRead(); | 
| +    message_loop_.RunUntilIdle(); | 
| +    ExpectReadResult(OK); | 
| +  } | 
| + | 
| +  // Callback for VideoDecoder::Reset(). | 
| +  void OnDecoderReset() { | 
| +    DCHECK(is_reset_pending_); | 
| +    is_reset_pending_ = false; | 
| +  } | 
| + | 
| +  void ExpectResetResult(CallbackResult result) { | 
| +    switch (result) { | 
| +      case PENDING: | 
| +        EXPECT_TRUE(is_reset_pending_); | 
| +        break; | 
| +      case OK: | 
| +        EXPECT_FALSE(is_reset_pending_); | 
| +        break; | 
| +      default: | 
| +        NOTREACHED(); | 
| +    } | 
| +  } | 
| + | 
| +  void ResetAndExpect(CallbackResult result) { | 
| +    is_reset_pending_ = true; | 
| +    decoder_->Reset(base::Bind(&FakeVideoDecoderTest::OnDecoderReset, | 
| +                               base::Unretained(this))); | 
| +    message_loop_.RunUntilIdle(); | 
| +    ExpectResetResult(result); | 
| +  } | 
| + | 
| +  void EnterPendingResetState() { | 
| +    decoder_->HoldNextReset(); | 
| +    ResetAndExpect(PENDING); | 
| +  } | 
| + | 
| +  void SatisfyReset() { | 
| +    decoder_->SatisfyReset(); | 
| +    message_loop_.RunUntilIdle(); | 
| +    ExpectResetResult(OK); | 
| +  } | 
| + | 
| +  // Callback for VideoDecoder::Stop(). | 
| +  void OnDecoderStopped() { | 
| +    DCHECK(is_stop_pending_); | 
| +    is_stop_pending_ = false; | 
| +  } | 
| + | 
| +  void ExpectStopResult(CallbackResult result) { | 
| +    switch (result) { | 
| +      case PENDING: | 
| +        EXPECT_TRUE(is_stop_pending_); | 
| +        break; | 
| +      case OK: | 
| +        EXPECT_FALSE(is_stop_pending_); | 
| +        break; | 
| +      default: | 
| +        NOTREACHED(); | 
| +    } | 
| +  } | 
| + | 
| +  void StopAndExpect(CallbackResult result) { | 
| +    is_stop_pending_ = true; | 
| +    decoder_->Stop(base::Bind(&FakeVideoDecoderTest::OnDecoderStopped, | 
| +                              base::Unretained(this))); | 
| +    message_loop_.RunUntilIdle(); | 
| +    ExpectStopResult(result); | 
| +  } | 
| + | 
| +  void EnterPendingStopState() { | 
| +    decoder_->HoldNextStop(); | 
| +    StopAndExpect(PENDING); | 
| +  } | 
| + | 
| +  void SatisfyStop() { | 
| +    decoder_->SatisfyStop(); | 
| +    message_loop_.RunUntilIdle(); | 
| +    ExpectStopResult(OK); | 
| +  } | 
| + | 
| +  // Callback for DemuxerStream::Read so that we can skip frames to trigger a | 
| +  // config change. | 
| +  void BufferReady(bool* config_changed, | 
| +                   DemuxerStream::Status status, | 
| +                   const scoped_refptr<DecoderBuffer>& buffer) { | 
| +    if (status == DemuxerStream::kConfigChanged) | 
| +      *config_changed = true; | 
| +  } | 
| + | 
| +  void ChangeConfig() { | 
| +    bool config_changed = false; | 
| +    while (!config_changed) { | 
| +      demuxer_stream_->Read(base::Bind(&FakeVideoDecoderTest::BufferReady, | 
| +                                       base::Unretained(this), | 
| +                                       &config_changed)); | 
| +      message_loop_.RunUntilIdle(); | 
| +    } | 
| +  } | 
| + | 
| +  void EnterPendingDemuxerReadState() { | 
| +    demuxer_stream_->HoldNextRead(); | 
| +    ReadOneFrame(); | 
| +  } | 
| + | 
| +  void SatisfyDemuxerRead() { | 
| +    demuxer_stream_->SatisfyRead(); | 
| +    message_loop_.RunUntilIdle(); | 
| +  } | 
| + | 
| +  void AbortDemuxerRead() { | 
| +    demuxer_stream_->Reset(); | 
| +    message_loop_.RunUntilIdle(); | 
| +  } | 
| + | 
| +  base::MessageLoop message_loop_; | 
| +  scoped_ptr<FakeVideoDecoder> decoder_; | 
| +  scoped_ptr<FakeDemuxerStream> demuxer_stream_; | 
| +  MockStatisticsCB statistics_cb_; | 
| +  int num_decoded_frames_; | 
| + | 
| +  // Callback result/status. | 
| +  scoped_refptr<VideoFrame> frame_read_; | 
| +  bool is_read_pending_; | 
| +  bool is_reset_pending_; | 
| +  bool is_stop_pending_; | 
| + | 
| + private: | 
| +  DISALLOW_COPY_AND_ASSIGN(FakeVideoDecoderTest); | 
| +}; | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Initialize) { | 
| +  Initialize(); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Read_AllFrames) { | 
| +  Initialize(); | 
| +  ReadUntilEOS(); | 
| +  EXPECT_EQ(kNumInputBuffers, num_decoded_frames_); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Read_AbortedDemuxerRead) { | 
| +  Initialize(); | 
| +  demuxer_stream_->HoldNextRead(); | 
| +  ReadOneFrame(); | 
| +  AbortDemuxerRead(); | 
| +  ExpectReadResult(ABROTED); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Read_DecodingDelay) { | 
| +  Initialize(); | 
| + | 
| +  while (demuxer_stream_->num_buffers_returned() < kNumInputBuffers) { | 
| +    ReadOneFrame(); | 
| +    EXPECT_EQ(demuxer_stream_->num_buffers_returned(), | 
| +              num_decoded_frames_ + kDecodingDelay); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Read_ZeroDelay) { | 
| +  decoder_.reset(new FakeVideoDecoder(0)); | 
| +  Initialize(); | 
| + | 
| +  while (demuxer_stream_->num_buffers_returned() < kNumInputBuffers) { | 
| +    ReadOneFrame(); | 
| +    EXPECT_EQ(demuxer_stream_->num_buffers_returned(), num_decoded_frames_); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Read_Pending) { | 
| +  Initialize(); | 
| +  EnterPendingReadState(); | 
| +  SatisfyRead(); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Reinitialize) { | 
| +  Initialize(); | 
| +  VideoDecoderConfig old_config = demuxer_stream_->video_decoder_config(); | 
| +  ChangeConfig(); | 
| +  VideoDecoderConfig new_config = demuxer_stream_->video_decoder_config(); | 
| +  EXPECT_FALSE(new_config.Matches(old_config)); | 
| +  Initialize(); | 
| +} | 
| + | 
| +// Reinitializing the decoder during the middle of the decoding process can | 
| +// cause dropped frames. | 
| +TEST_F(FakeVideoDecoderTest, Reinitialize_FrameDropped) { | 
| +  Initialize(); | 
| +  ReadOneFrame(); | 
| +  Initialize(); | 
| +  ReadUntilEOS(); | 
| +  EXPECT_LT(num_decoded_frames_, kNumInputBuffers); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Reset) { | 
| +  Initialize(); | 
| +  ReadOneFrame(); | 
| +  ResetAndExpect(OK); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Reset_DuringPendingDemuxerRead) { | 
| +  Initialize(); | 
| +  EnterPendingDemuxerReadState(); | 
| +  ResetAndExpect(PENDING); | 
| +  SatisfyDemuxerRead(); | 
| +  ExpectReadResult(ABROTED); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Reset_DuringPendingDemuxerRead_Aborted) { | 
| +  Initialize(); | 
| +  EnterPendingDemuxerReadState(); | 
| +  ResetAndExpect(PENDING); | 
| +  AbortDemuxerRead(); | 
| +  ExpectReadResult(ABROTED); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Reset_DuringPendingRead) { | 
| +  Initialize(); | 
| +  EnterPendingReadState(); | 
| +  ResetAndExpect(PENDING); | 
| +  SatisfyRead(); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Reset_Pending) { | 
| +  Initialize(); | 
| +  EnterPendingResetState(); | 
| +  SatisfyReset(); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Reset_PendingDuringPendingRead) { | 
| +  Initialize(); | 
| +  EnterPendingReadState(); | 
| +  EnterPendingResetState(); | 
| +  SatisfyRead(); | 
| +  SatisfyReset(); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop) { | 
| +  Initialize(); | 
| +  ReadOneFrame(); | 
| +  ExpectReadResult(OK); | 
| +  StopAndExpect(OK); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_DuringPendingDemuxerRead) { | 
| +  Initialize(); | 
| +  EnterPendingDemuxerReadState(); | 
| +  StopAndExpect(PENDING); | 
| +  SatisfyDemuxerRead(); | 
| +  ExpectReadResult(ABROTED); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_DuringPendingDemuxerRead_Aborted) { | 
| +  Initialize(); | 
| +  EnterPendingDemuxerReadState(); | 
| +  ResetAndExpect(PENDING); | 
| +  StopAndExpect(PENDING); | 
| +  SatisfyDemuxerRead(); | 
| +  ExpectReadResult(ABROTED); | 
| +  ExpectResetResult(OK); | 
| +  ExpectStopResult(OK); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_DuringPendingRead) { | 
| +  Initialize(); | 
| +  EnterPendingReadState(); | 
| +  StopAndExpect(PENDING); | 
| +  SatisfyRead(); | 
| +  ExpectStopResult(OK); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_DuringPendingReset) { | 
| +  Initialize(); | 
| +  EnterPendingResetState(); | 
| +  StopAndExpect(PENDING); | 
| +  SatisfyReset(); | 
| +  ExpectStopResult(OK); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_DuringPendingReadAndPendingReset) { | 
| +  Initialize(); | 
| +  EnterPendingReadState(); | 
| +  EnterPendingResetState(); | 
| +  StopAndExpect(PENDING); | 
| +  SatisfyRead(); | 
| +  SatisfyReset(); | 
| +  ExpectStopResult(OK); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_Pending) { | 
| +  Initialize(); | 
| +  decoder_->HoldNextStop(); | 
| +  StopAndExpect(PENDING); | 
| +  decoder_->SatisfyStop(); | 
| +  message_loop_.RunUntilIdle(); | 
| +  ExpectStopResult(OK); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_PendingDuringPendingRead) { | 
| +  Initialize(); | 
| +  EnterPendingReadState(); | 
| +  EnterPendingStopState(); | 
| +  SatisfyRead(); | 
| +  SatisfyStop(); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_PendingDuringPendingReset) { | 
| +  Initialize(); | 
| +  EnterPendingResetState(); | 
| +  EnterPendingStopState(); | 
| +  SatisfyReset(); | 
| +  SatisfyStop(); | 
| +} | 
| + | 
| +TEST_F(FakeVideoDecoderTest, Stop_PendingDuringPendingReadAndPendingReset) { | 
| +  Initialize(); | 
| +  EnterPendingReadState(); | 
| +  EnterPendingResetState(); | 
| +  EnterPendingStopState(); | 
| +  SatisfyRead(); | 
| +  SatisfyReset(); | 
| +  SatisfyStop(); | 
| +} | 
| + | 
| +}  // namespace media | 
|  |