| Index: media/renderers/renderer_impl_unittest.cc
 | 
| diff --git a/media/renderers/renderer_impl_unittest.cc b/media/renderers/renderer_impl_unittest.cc
 | 
| index bbbaa2c27b3410558ac34600c65d297ee3769ed9..e513e69b59dc1cd60c649d7f3d29b86bbfdce493 100644
 | 
| --- a/media/renderers/renderer_impl_unittest.cc
 | 
| +++ b/media/renderers/renderer_impl_unittest.cc
 | 
| @@ -32,6 +32,10 @@ namespace media {
 | 
|  
 | 
|  const int64_t kStartPlayingTimeInMs = 100;
 | 
|  
 | 
| +ACTION_P2(SetBool, var, value) {
 | 
| +  *var = value;
 | 
| +}
 | 
| +
 | 
|  ACTION_P2(SetBufferingState, renderer_client, buffering_state) {
 | 
|    (*renderer_client)->OnBufferingStateChange(buffering_state);
 | 
|  }
 | 
| @@ -226,10 +230,7 @@ class RendererImplTest : public ::testing::Test {
 | 
|      base::RunLoop().RunUntilIdle();
 | 
|    }
 | 
|  
 | 
| -  void Flush(bool underflowed) {
 | 
| -    if (!underflowed)
 | 
| -      EXPECT_CALL(time_source_, StopTicking());
 | 
| -
 | 
| +  void SetFlushExpectationsForAVRenderers() {
 | 
|      if (audio_stream_) {
 | 
|        EXPECT_CALL(*audio_renderer_, Flush(_))
 | 
|            .WillOnce(DoAll(SetBufferingState(&audio_renderer_client_,
 | 
| @@ -243,7 +244,13 @@ class RendererImplTest : public ::testing::Test {
 | 
|                                              BUFFERING_HAVE_NOTHING),
 | 
|                            RunClosure<0>()));
 | 
|      }
 | 
| +  }
 | 
|  
 | 
| +  void Flush(bool underflowed) {
 | 
| +    if (!underflowed)
 | 
| +      EXPECT_CALL(time_source_, StopTicking());
 | 
| +
 | 
| +    SetFlushExpectationsForAVRenderers();
 | 
|      EXPECT_CALL(callbacks_, OnFlushed());
 | 
|  
 | 
|      renderer_impl_->Flush(
 | 
| @@ -851,4 +858,209 @@ TEST_F(RendererImplTest, PostponedStreamStatusNotificationHandling) {
 | 
|    base::RunLoop().Run();
 | 
|  }
 | 
|  
 | 
| +// Verify that a RendererImpl::Flush gets postponed until after stream status
 | 
| +// change handling is completed.
 | 
| +TEST_F(RendererImplTest, FlushDuringAudioReinit) {
 | 
| +  CreateAudioAndVideoStream();
 | 
| +
 | 
| +  StreamStatusChangeCB stream_status_change_cb;
 | 
| +  EXPECT_CALL(*demuxer_, SetStreamStatusChangeCB(_))
 | 
| +      .WillOnce(SaveArg<0>(&stream_status_change_cb));
 | 
| +  SetAudioRendererInitializeExpectations(PIPELINE_OK);
 | 
| +  SetVideoRendererInitializeExpectations(PIPELINE_OK);
 | 
| +  InitializeAndExpect(PIPELINE_OK);
 | 
| +  Play();
 | 
| +
 | 
| +  EXPECT_CALL(time_source_, StopTicking()).Times(testing::AnyNumber());
 | 
| +  base::Closure audio_renderer_flush_cb;
 | 
| +  EXPECT_CALL(*audio_renderer_, Flush(_))
 | 
| +      .WillOnce(SaveArg<0>(&audio_renderer_flush_cb));
 | 
| +  EXPECT_CALL(*audio_renderer_, StartPlaying())
 | 
| +      .Times(1)
 | 
| +      .WillOnce(
 | 
| +          SetBufferingState(&audio_renderer_client_, BUFFERING_HAVE_ENOUGH));
 | 
| +
 | 
| +  // This should start flushing the audio renderer (due to audio stream status
 | 
| +  // change) and should populate the |audio_renderer_flush_cb|.
 | 
| +  stream_status_change_cb.Run(audio_stream_.get(), false, base::TimeDelta());
 | 
| +  EXPECT_TRUE(audio_renderer_flush_cb);
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  bool flush_done = false;
 | 
| +
 | 
| +  // Now that audio stream change is being handled the RendererImpl::Flush
 | 
| +  // should be postponed, instead of being executed immediately.
 | 
| +  EXPECT_CALL(callbacks_, OnFlushed()).WillOnce(SetBool(&flush_done, true));
 | 
| +  renderer_impl_->Flush(
 | 
| +      base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +  EXPECT_FALSE(flush_done);
 | 
| +
 | 
| +  // The renderer_impl_->Flush invoked above should proceed after the first
 | 
| +  // audio renderer flush (initiated by the stream status change) completes.
 | 
| +  SetFlushExpectationsForAVRenderers();
 | 
| +  audio_renderer_flush_cb.Run();
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +  EXPECT_TRUE(flush_done);
 | 
| +}
 | 
| +
 | 
| +TEST_F(RendererImplTest, FlushDuringVideoReinit) {
 | 
| +  CreateAudioAndVideoStream();
 | 
| +
 | 
| +  StreamStatusChangeCB stream_status_change_cb;
 | 
| +  EXPECT_CALL(*demuxer_, SetStreamStatusChangeCB(_))
 | 
| +      .WillOnce(SaveArg<0>(&stream_status_change_cb));
 | 
| +  SetAudioRendererInitializeExpectations(PIPELINE_OK);
 | 
| +  SetVideoRendererInitializeExpectations(PIPELINE_OK);
 | 
| +  InitializeAndExpect(PIPELINE_OK);
 | 
| +  Play();
 | 
| +
 | 
| +  EXPECT_CALL(time_source_, StopTicking()).Times(testing::AnyNumber());
 | 
| +  base::Closure video_renderer_flush_cb;
 | 
| +  EXPECT_CALL(*video_renderer_, Flush(_))
 | 
| +      .WillOnce(SaveArg<0>(&video_renderer_flush_cb));
 | 
| +  EXPECT_CALL(*video_renderer_, StartPlayingFrom(_))
 | 
| +      .Times(1)
 | 
| +      .WillOnce(
 | 
| +          SetBufferingState(&video_renderer_client_, BUFFERING_HAVE_ENOUGH));
 | 
| +
 | 
| +  // This should start flushing the video renderer (due to video stream status
 | 
| +  // change) and should populate the |video_renderer_flush_cb|.
 | 
| +  stream_status_change_cb.Run(video_stream_.get(), false, base::TimeDelta());
 | 
| +  EXPECT_TRUE(video_renderer_flush_cb);
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +
 | 
| +  bool flush_done = false;
 | 
| +
 | 
| +  // Now that video stream change is being handled the RendererImpl::Flush
 | 
| +  // should be postponed, instead of being executed immediately.
 | 
| +  EXPECT_CALL(callbacks_, OnFlushed()).WillOnce(SetBool(&flush_done, true));
 | 
| +  renderer_impl_->Flush(
 | 
| +      base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +  EXPECT_FALSE(flush_done);
 | 
| +
 | 
| +  // The renderer_impl_->Flush invoked above should proceed after the first
 | 
| +  // video renderer flush (initiated by the stream status change) completes.
 | 
| +  SetFlushExpectationsForAVRenderers();
 | 
| +  video_renderer_flush_cb.Run();
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +  EXPECT_TRUE(flush_done);
 | 
| +}
 | 
| +
 | 
| +// Test audio track switching when the RendererImpl is in STATE_FLUSHING/FLUSHED
 | 
| +TEST_F(RendererImplTest, AudioTrackSwitchDuringFlush) {
 | 
| +  CreateAudioAndVideoStream();
 | 
| +  std::unique_ptr<StrictMock<MockDemuxerStream>> primary_audio_stream =
 | 
| +      std::move(audio_stream_);
 | 
| +  CreateAudioStream();
 | 
| +  std::unique_ptr<StrictMock<MockDemuxerStream>> secondary_audio_stream =
 | 
| +      std::move(audio_stream_);
 | 
| +  audio_stream_ = std::move(primary_audio_stream);
 | 
| +
 | 
| +  StreamStatusChangeCB stream_status_change_cb;
 | 
| +  EXPECT_CALL(*demuxer_, SetStreamStatusChangeCB(_))
 | 
| +      .WillOnce(SaveArg<0>(&stream_status_change_cb));
 | 
| +  SetAudioRendererInitializeExpectations(PIPELINE_OK);
 | 
| +  SetVideoRendererInitializeExpectations(PIPELINE_OK);
 | 
| +  InitializeAndExpect(PIPELINE_OK);
 | 
| +  Play();
 | 
| +
 | 
| +  EXPECT_CALL(time_source_, StopTicking()).Times(testing::AnyNumber());
 | 
| +  EXPECT_CALL(*video_renderer_, Flush(_))
 | 
| +      .WillRepeatedly(DoAll(
 | 
| +          SetBufferingState(&video_renderer_client_, BUFFERING_HAVE_NOTHING),
 | 
| +          RunClosure<0>()));
 | 
| +
 | 
| +  // Initiate RendererImpl::Flush, but postpone its completion by not calling
 | 
| +  // audio renderer flush callback right away, i.e. pretending audio renderer
 | 
| +  // flush takes a while.
 | 
| +  base::Closure audio_renderer_flush_cb;
 | 
| +  EXPECT_CALL(*audio_renderer_, Flush(_))
 | 
| +      .WillOnce(SaveArg<0>(&audio_renderer_flush_cb));
 | 
| +  EXPECT_CALL(callbacks_, OnFlushed());
 | 
| +  renderer_impl_->Flush(
 | 
| +      base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +  EXPECT_TRUE(audio_renderer_flush_cb);
 | 
| +
 | 
| +  // Now, while the RendererImpl::Flush is pending, perform an audio track
 | 
| +  // switch. The handling of the track switch will be postponed until after
 | 
| +  // RendererImpl::Flush completes.
 | 
| +  stream_status_change_cb.Run(audio_stream_.get(), false, base::TimeDelta());
 | 
| +  stream_status_change_cb.Run(secondary_audio_stream.get(), true,
 | 
| +                              base::TimeDelta());
 | 
| +
 | 
| +  // Ensure that audio track switch occurs after Flush by verifying that the
 | 
| +  // audio renderer is reinitialized with the secondary audio stream.
 | 
| +  EXPECT_CALL(*audio_renderer_,
 | 
| +              Initialize(secondary_audio_stream.get(), _, _, _));
 | 
| +
 | 
| +  // Complete the audio renderer flush, thus completing the renderer_impl_ Flush
 | 
| +  // initiated above. This will transition the RendererImpl into the FLUSHED
 | 
| +  // state and will process pending track switch, which should result in the
 | 
| +  // reinitialization of the audio renderer for the secondary audio stream.
 | 
| +  audio_renderer_client_->OnBufferingStateChange(BUFFERING_HAVE_NOTHING);
 | 
| +  audio_renderer_flush_cb.Run();
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +}
 | 
| +
 | 
| +// Test video track switching when the RendererImpl is in STATE_FLUSHING/FLUSHED
 | 
| +TEST_F(RendererImplTest, VideoTrackSwitchDuringFlush) {
 | 
| +  CreateAudioAndVideoStream();
 | 
| +  std::unique_ptr<StrictMock<MockDemuxerStream>> primary_video_stream =
 | 
| +      std::move(video_stream_);
 | 
| +  CreateVideoStream();
 | 
| +  std::unique_ptr<StrictMock<MockDemuxerStream>> secondary_video_stream =
 | 
| +      std::move(video_stream_);
 | 
| +  video_stream_ = std::move(primary_video_stream);
 | 
| +
 | 
| +  StreamStatusChangeCB stream_status_change_cb;
 | 
| +  EXPECT_CALL(*demuxer_, SetStreamStatusChangeCB(_))
 | 
| +      .WillOnce(SaveArg<0>(&stream_status_change_cb));
 | 
| +  SetAudioRendererInitializeExpectations(PIPELINE_OK);
 | 
| +  SetVideoRendererInitializeExpectations(PIPELINE_OK);
 | 
| +  InitializeAndExpect(PIPELINE_OK);
 | 
| +  Play();
 | 
| +
 | 
| +  EXPECT_CALL(time_source_, StopTicking()).Times(testing::AnyNumber());
 | 
| +  EXPECT_CALL(*video_renderer_, OnTimeStopped()).Times(testing::AnyNumber());
 | 
| +  EXPECT_CALL(*audio_renderer_, Flush(_))
 | 
| +      .WillRepeatedly(DoAll(
 | 
| +          SetBufferingState(&audio_renderer_client_, BUFFERING_HAVE_NOTHING),
 | 
| +          RunClosure<0>()));
 | 
| +
 | 
| +  // Initiate RendererImpl::Flush, but postpone its completion by not calling
 | 
| +  // video renderer flush callback right away, i.e. pretending video renderer
 | 
| +  // flush takes a while.
 | 
| +  base::Closure video_renderer_flush_cb;
 | 
| +  EXPECT_CALL(*video_renderer_, Flush(_))
 | 
| +      .WillOnce(SaveArg<0>(&video_renderer_flush_cb));
 | 
| +  EXPECT_CALL(callbacks_, OnFlushed());
 | 
| +  renderer_impl_->Flush(
 | 
| +      base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +  EXPECT_TRUE(video_renderer_flush_cb);
 | 
| +
 | 
| +  // Now, while the RendererImpl::Flush is pending, perform a video track
 | 
| +  // switch. The handling of the track switch will be postponed until after
 | 
| +  // RendererImpl::Flush completes.
 | 
| +  stream_status_change_cb.Run(video_stream_.get(), false, base::TimeDelta());
 | 
| +  stream_status_change_cb.Run(secondary_video_stream.get(), true,
 | 
| +                              base::TimeDelta());
 | 
| +
 | 
| +  // Ensure that video track switch occurs after Flush by verifying that the
 | 
| +  // video renderer is reinitialized with the secondary video stream.
 | 
| +  EXPECT_CALL(*video_renderer_,
 | 
| +              Initialize(secondary_video_stream.get(), _, _, _, _));
 | 
| +
 | 
| +  // Complete the video renderer flush, thus completing the renderer_impl_ Flush
 | 
| +  // initiated above. This will transition the RendererImpl into the FLUSHED
 | 
| +  // state and will process pending track switch, which should result in the
 | 
| +  // reinitialization of the video renderer for the secondary video stream.
 | 
| +  video_renderer_client_->OnBufferingStateChange(BUFFERING_HAVE_NOTHING);
 | 
| +  video_renderer_flush_cb.Run();
 | 
| +  base::RunLoop().RunUntilIdle();
 | 
| +}
 | 
| +
 | 
|  }  // namespace media
 | 
| 
 |