| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/base/pipeline.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <utility> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/bind.h" | |
| 12 #include "base/macros.h" | |
| 13 #include "base/message_loop/message_loop.h" | |
| 14 #include "base/stl_util.h" | |
| 15 #include "base/test/simple_test_tick_clock.h" | |
| 16 #include "base/threading/simple_thread.h" | |
| 17 #include "base/time/clock.h" | |
| 18 #include "media/base/fake_text_track_stream.h" | |
| 19 #include "media/base/gmock_callback_support.h" | |
| 20 #include "media/base/media_log.h" | |
| 21 #include "media/base/mock_filters.h" | |
| 22 #include "media/base/test_helpers.h" | |
| 23 #include "media/base/text_renderer.h" | |
| 24 #include "media/base/text_track_config.h" | |
| 25 #include "media/base/time_delta_interpolator.h" | |
| 26 #include "testing/gtest/include/gtest/gtest.h" | |
| 27 #include "ui/gfx/geometry/size.h" | |
| 28 | |
| 29 using ::testing::_; | |
| 30 using ::testing::AnyNumber; | |
| 31 using ::testing::DeleteArg; | |
| 32 using ::testing::DoAll; | |
| 33 // TODO(scherkus): Remove InSequence after refactoring Pipeline. | |
| 34 using ::testing::InSequence; | |
| 35 using ::testing::Invoke; | |
| 36 using ::testing::InvokeWithoutArgs; | |
| 37 using ::testing::Mock; | |
| 38 using ::testing::NotNull; | |
| 39 using ::testing::Return; | |
| 40 using ::testing::SaveArg; | |
| 41 using ::testing::StrictMock; | |
| 42 using ::testing::WithArg; | |
| 43 | |
| 44 namespace media { | |
| 45 | |
| 46 ACTION_P(SetDemuxerProperties, duration) { | |
| 47 arg0->SetDuration(duration); | |
| 48 } | |
| 49 | |
| 50 ACTION_P2(Stop, pipeline, stop_cb) { | |
| 51 pipeline->Stop(stop_cb); | |
| 52 } | |
| 53 | |
| 54 ACTION_P2(SetError, pipeline, status) { | |
| 55 pipeline->SetErrorForTesting(status); | |
| 56 } | |
| 57 | |
| 58 ACTION_P2(SetBufferingState, cb, buffering_state) { | |
| 59 cb->Run(buffering_state); | |
| 60 } | |
| 61 | |
| 62 ACTION_TEMPLATE(PostCallback, | |
| 63 HAS_1_TEMPLATE_PARAMS(int, k), | |
| 64 AND_1_VALUE_PARAMS(p0)) { | |
| 65 return base::MessageLoop::current()->PostTask( | |
| 66 FROM_HERE, base::Bind(::std::tr1::get<k>(args), p0)); | |
| 67 } | |
| 68 | |
| 69 // TODO(scherkus): even though some filters are initialized on separate | |
| 70 // threads these test aren't flaky... why? It's because filters' Initialize() | |
| 71 // is executed on |message_loop_| and the mock filters instantly call | |
| 72 // InitializationComplete(), which keeps the pipeline humming along. If | |
| 73 // either filters don't call InitializationComplete() immediately or filter | |
| 74 // initialization is moved to a separate thread this test will become flaky. | |
| 75 class PipelineTest : public ::testing::Test { | |
| 76 public: | |
| 77 // Used for setting expectations on pipeline callbacks. Using a StrictMock | |
| 78 // also lets us test for missing callbacks. | |
| 79 class CallbackHelper { | |
| 80 public: | |
| 81 CallbackHelper() {} | |
| 82 virtual ~CallbackHelper() {} | |
| 83 | |
| 84 MOCK_METHOD1(OnStart, void(PipelineStatus)); | |
| 85 MOCK_METHOD1(OnSeek, void(PipelineStatus)); | |
| 86 MOCK_METHOD1(OnSuspend, void(PipelineStatus)); | |
| 87 MOCK_METHOD1(OnResume, void(PipelineStatus)); | |
| 88 MOCK_METHOD0(OnStop, void()); | |
| 89 MOCK_METHOD0(OnEnded, void()); | |
| 90 MOCK_METHOD1(OnError, void(PipelineStatus)); | |
| 91 MOCK_METHOD1(OnMetadata, void(PipelineMetadata)); | |
| 92 MOCK_METHOD1(OnBufferingStateChange, void(BufferingState)); | |
| 93 MOCK_METHOD0(OnDurationChange, void()); | |
| 94 | |
| 95 private: | |
| 96 DISALLOW_COPY_AND_ASSIGN(CallbackHelper); | |
| 97 }; | |
| 98 | |
| 99 PipelineTest() | |
| 100 : pipeline_(new Pipeline(message_loop_.task_runner(), | |
| 101 new MediaLog())), | |
| 102 demuxer_(new StrictMock<MockDemuxer>()), | |
| 103 scoped_renderer_(new StrictMock<MockRenderer>()), | |
| 104 renderer_(scoped_renderer_.get()) { | |
| 105 // SetDemuxerExpectations() adds overriding expectations for expected | |
| 106 // non-NULL streams. | |
| 107 DemuxerStream* null_pointer = NULL; | |
| 108 EXPECT_CALL(*demuxer_, GetStream(_)) | |
| 109 .WillRepeatedly(Return(null_pointer)); | |
| 110 | |
| 111 EXPECT_CALL(*demuxer_, GetTimelineOffset()) | |
| 112 .WillRepeatedly(Return(base::Time())); | |
| 113 | |
| 114 EXPECT_CALL(*renderer_, GetMediaTime()) | |
| 115 .WillRepeatedly(Return(base::TimeDelta())); | |
| 116 | |
| 117 EXPECT_CALL(*demuxer_, GetStartTime()).WillRepeatedly(Return(start_time_)); | |
| 118 } | |
| 119 | |
| 120 virtual ~PipelineTest() { | |
| 121 if (!pipeline_ || !pipeline_->IsRunning()) | |
| 122 return; | |
| 123 | |
| 124 ExpectDemuxerStop(); | |
| 125 | |
| 126 // The mock demuxer doesn't stop the fake text track stream, | |
| 127 // so just stop it manually. | |
| 128 if (text_stream_) { | |
| 129 text_stream_->Stop(); | |
| 130 message_loop_.RunUntilIdle(); | |
| 131 } | |
| 132 | |
| 133 // Expect a stop callback if we were started. | |
| 134 ExpectPipelineStopAndDestroyPipeline(); | |
| 135 pipeline_->Stop(base::Bind(&CallbackHelper::OnStop, | |
| 136 base::Unretained(&callbacks_))); | |
| 137 message_loop_.RunUntilIdle(); | |
| 138 } | |
| 139 | |
| 140 void OnDemuxerError() { | |
| 141 // Cast because OnDemuxerError is private in Pipeline. | |
| 142 static_cast<DemuxerHost*>(pipeline_.get()) | |
| 143 ->OnDemuxerError(PIPELINE_ERROR_ABORT); | |
| 144 } | |
| 145 | |
| 146 protected: | |
| 147 // Sets up expectations to allow the demuxer to initialize. | |
| 148 typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector; | |
| 149 void SetDemuxerExpectations(MockDemuxerStreamVector* streams, | |
| 150 const base::TimeDelta& duration) { | |
| 151 EXPECT_CALL(callbacks_, OnDurationChange()); | |
| 152 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | |
| 153 .WillOnce(DoAll(SetDemuxerProperties(duration), | |
| 154 PostCallback<1>(PIPELINE_OK))); | |
| 155 | |
| 156 // Configure the demuxer to return the streams. | |
| 157 for (size_t i = 0; i < streams->size(); ++i) { | |
| 158 DemuxerStream* stream = (*streams)[i]; | |
| 159 EXPECT_CALL(*demuxer_, GetStream(stream->type())) | |
| 160 .WillRepeatedly(Return(stream)); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 void SetDemuxerExpectations(MockDemuxerStreamVector* streams) { | |
| 165 // Initialize with a default non-zero duration. | |
| 166 SetDemuxerExpectations(streams, base::TimeDelta::FromSeconds(10)); | |
| 167 } | |
| 168 | |
| 169 scoped_ptr<StrictMock<MockDemuxerStream> > CreateStream( | |
| 170 DemuxerStream::Type type) { | |
| 171 scoped_ptr<StrictMock<MockDemuxerStream> > stream( | |
| 172 new StrictMock<MockDemuxerStream>(type)); | |
| 173 return stream; | |
| 174 } | |
| 175 | |
| 176 // Sets up expectations to allow the video renderer to initialize. | |
| 177 void SetRendererExpectations() { | |
| 178 EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _)) | |
| 179 .WillOnce(DoAll(SaveArg<3>(&buffering_state_cb_), | |
| 180 SaveArg<4>(&ended_cb_), | |
| 181 PostCallback<1>(PIPELINE_OK))); | |
| 182 EXPECT_CALL(*renderer_, HasAudio()).WillRepeatedly(Return(audio_stream())); | |
| 183 EXPECT_CALL(*renderer_, HasVideo()).WillRepeatedly(Return(video_stream())); | |
| 184 } | |
| 185 | |
| 186 void AddTextStream() { | |
| 187 EXPECT_CALL(*this, OnAddTextTrack(_,_)) | |
| 188 .WillOnce(Invoke(this, &PipelineTest::DoOnAddTextTrack)); | |
| 189 static_cast<DemuxerHost*>(pipeline_.get())->AddTextStream(text_stream(), | |
| 190 TextTrackConfig(kTextSubtitles, "", "", "")); | |
| 191 message_loop_.RunUntilIdle(); | |
| 192 } | |
| 193 | |
| 194 void StartPipeline() { | |
| 195 EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0); | |
| 196 pipeline_->Start( | |
| 197 demuxer_.get(), std::move(scoped_renderer_), | |
| 198 base::Bind(&CallbackHelper::OnEnded, base::Unretained(&callbacks_)), | |
| 199 base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)), | |
| 200 base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)), | |
| 201 base::Bind(&CallbackHelper::OnMetadata, base::Unretained(&callbacks_)), | |
| 202 base::Bind(&CallbackHelper::OnBufferingStateChange, | |
| 203 base::Unretained(&callbacks_)), | |
| 204 base::Bind(&CallbackHelper::OnDurationChange, | |
| 205 base::Unretained(&callbacks_)), | |
| 206 base::Bind(&PipelineTest::OnAddTextTrack, base::Unretained(this)), | |
| 207 base::Bind(&PipelineTest::OnWaitingForDecryptionKey, | |
| 208 base::Unretained(this))); | |
| 209 } | |
| 210 | |
| 211 // Sets up expectations on the callback and initializes the pipeline. Called | |
| 212 // after tests have set expectations any filters they wish to use. | |
| 213 void StartPipelineAndExpect(PipelineStatus start_status) { | |
| 214 EXPECT_CALL(callbacks_, OnStart(start_status)); | |
| 215 | |
| 216 if (start_status == PIPELINE_OK) { | |
| 217 EXPECT_CALL(callbacks_, OnMetadata(_)).WillOnce(SaveArg<0>(&metadata_)); | |
| 218 EXPECT_CALL(*renderer_, SetPlaybackRate(0.0)); | |
| 219 EXPECT_CALL(*renderer_, SetVolume(1.0f)); | |
| 220 EXPECT_CALL(*renderer_, StartPlayingFrom(start_time_)) | |
| 221 .WillOnce(SetBufferingState(&buffering_state_cb_, | |
| 222 BUFFERING_HAVE_ENOUGH)); | |
| 223 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); | |
| 224 } | |
| 225 | |
| 226 StartPipeline(); | |
| 227 message_loop_.RunUntilIdle(); | |
| 228 } | |
| 229 | |
| 230 void CreateAudioStream() { | |
| 231 audio_stream_ = CreateStream(DemuxerStream::AUDIO); | |
| 232 } | |
| 233 | |
| 234 void CreateVideoStream() { | |
| 235 video_stream_ = CreateStream(DemuxerStream::VIDEO); | |
| 236 video_stream_->set_video_decoder_config(video_decoder_config_); | |
| 237 } | |
| 238 | |
| 239 void CreateTextStream() { | |
| 240 scoped_ptr<FakeTextTrackStream> text_stream(new FakeTextTrackStream()); | |
| 241 EXPECT_CALL(*text_stream, OnRead()).Times(AnyNumber()); | |
| 242 text_stream_ = std::move(text_stream); | |
| 243 } | |
| 244 | |
| 245 MockDemuxerStream* audio_stream() { | |
| 246 return audio_stream_.get(); | |
| 247 } | |
| 248 | |
| 249 MockDemuxerStream* video_stream() { | |
| 250 return video_stream_.get(); | |
| 251 } | |
| 252 | |
| 253 FakeTextTrackStream* text_stream() { | |
| 254 return text_stream_.get(); | |
| 255 } | |
| 256 | |
| 257 void ExpectSeek(const base::TimeDelta& seek_time, bool underflowed) { | |
| 258 EXPECT_CALL(*demuxer_, Seek(seek_time, _)) | |
| 259 .WillOnce(RunCallback<1>(PIPELINE_OK)); | |
| 260 | |
| 261 EXPECT_CALL(*renderer_, Flush(_)) | |
| 262 .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_, | |
| 263 BUFFERING_HAVE_NOTHING), | |
| 264 RunClosure<0>())); | |
| 265 EXPECT_CALL(*renderer_, SetPlaybackRate(_)); | |
| 266 EXPECT_CALL(*renderer_, SetVolume(_)); | |
| 267 EXPECT_CALL(*renderer_, StartPlayingFrom(seek_time)) | |
| 268 .WillOnce(SetBufferingState(&buffering_state_cb_, | |
| 269 BUFFERING_HAVE_ENOUGH)); | |
| 270 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 271 | |
| 272 // We expect a successful seek callback followed by a buffering update. | |
| 273 EXPECT_CALL(callbacks_, OnSeek(PIPELINE_OK)); | |
| 274 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); | |
| 275 } | |
| 276 | |
| 277 void DoSeek(const base::TimeDelta& seek_time) { | |
| 278 pipeline_->Seek(seek_time, | |
| 279 base::Bind(&CallbackHelper::OnSeek, | |
| 280 base::Unretained(&callbacks_))); | |
| 281 message_loop_.RunUntilIdle(); | |
| 282 } | |
| 283 | |
| 284 void ExpectSuspend() { | |
| 285 EXPECT_CALL(*renderer_, SetPlaybackRate(0)); | |
| 286 EXPECT_CALL(*renderer_, Flush(_)) | |
| 287 .WillOnce(DoAll( | |
| 288 SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING), | |
| 289 RunClosure<0>())); | |
| 290 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 291 EXPECT_CALL(callbacks_, OnSuspend(PIPELINE_OK)); | |
| 292 } | |
| 293 | |
| 294 void DoSuspend() { | |
| 295 pipeline_->Suspend( | |
| 296 base::Bind(&CallbackHelper::OnSuspend, base::Unretained(&callbacks_))); | |
| 297 message_loop_.RunUntilIdle(); | |
| 298 | |
| 299 // |renderer_| has been deleted, replace it. | |
| 300 scoped_renderer_.reset(new StrictMock<MockRenderer>()), | |
| 301 renderer_ = scoped_renderer_.get(); | |
| 302 } | |
| 303 | |
| 304 void ExpectResume(const base::TimeDelta& seek_time) { | |
| 305 SetRendererExpectations(); | |
| 306 EXPECT_CALL(*demuxer_, Seek(seek_time, _)) | |
| 307 .WillOnce(RunCallback<1>(PIPELINE_OK)); | |
| 308 EXPECT_CALL(*renderer_, SetPlaybackRate(_)); | |
| 309 EXPECT_CALL(*renderer_, SetVolume(_)); | |
| 310 EXPECT_CALL(*renderer_, StartPlayingFrom(seek_time)) | |
| 311 .WillOnce( | |
| 312 SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_ENOUGH)); | |
| 313 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); | |
| 314 EXPECT_CALL(callbacks_, OnResume(PIPELINE_OK)); | |
| 315 } | |
| 316 | |
| 317 void DoResume(const base::TimeDelta& seek_time) { | |
| 318 pipeline_->Resume( | |
| 319 std::move(scoped_renderer_), seek_time, | |
| 320 base::Bind(&CallbackHelper::OnResume, base::Unretained(&callbacks_))); | |
| 321 message_loop_.RunUntilIdle(); | |
| 322 } | |
| 323 | |
| 324 void DestroyPipeline() { | |
| 325 // In real code Pipeline could be destroyed on a different thread. All weak | |
| 326 // pointers must have been invalidated before the stop callback returns. | |
| 327 DCHECK(!pipeline_->HasWeakPtrsForTesting()); | |
| 328 pipeline_.reset(); | |
| 329 } | |
| 330 | |
| 331 void ExpectDemuxerStop() { | |
| 332 if (demuxer_) | |
| 333 EXPECT_CALL(*demuxer_, Stop()); | |
| 334 } | |
| 335 | |
| 336 void ExpectPipelineStopAndDestroyPipeline() { | |
| 337 // After the Pipeline is stopped, it could be destroyed any time. Always | |
| 338 // destroy the pipeline immediately after OnStop() to test this. | |
| 339 EXPECT_CALL(callbacks_, OnStop()) | |
| 340 .WillOnce(Invoke(this, &PipelineTest::DestroyPipeline)); | |
| 341 } | |
| 342 | |
| 343 MOCK_METHOD2(OnAddTextTrack, void(const TextTrackConfig&, | |
| 344 const AddTextTrackDoneCB&)); | |
| 345 MOCK_METHOD0(OnWaitingForDecryptionKey, void(void)); | |
| 346 | |
| 347 void DoOnAddTextTrack(const TextTrackConfig& config, | |
| 348 const AddTextTrackDoneCB& done_cb) { | |
| 349 scoped_ptr<TextTrack> text_track(new MockTextTrack); | |
| 350 done_cb.Run(std::move(text_track)); | |
| 351 } | |
| 352 | |
| 353 // Fixture members. | |
| 354 StrictMock<CallbackHelper> callbacks_; | |
| 355 base::SimpleTestTickClock test_tick_clock_; | |
| 356 base::MessageLoop message_loop_; | |
| 357 scoped_ptr<Pipeline> pipeline_; | |
| 358 | |
| 359 scoped_ptr<StrictMock<MockDemuxer> > demuxer_; | |
| 360 scoped_ptr<StrictMock<MockRenderer> > scoped_renderer_; | |
| 361 StrictMock<MockRenderer>* renderer_; | |
| 362 StrictMock<CallbackHelper> text_renderer_callbacks_; | |
| 363 TextRenderer* text_renderer_; | |
| 364 scoped_ptr<StrictMock<MockDemuxerStream> > audio_stream_; | |
| 365 scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_; | |
| 366 scoped_ptr<FakeTextTrackStream> text_stream_; | |
| 367 BufferingStateCB buffering_state_cb_; | |
| 368 base::Closure ended_cb_; | |
| 369 VideoDecoderConfig video_decoder_config_; | |
| 370 PipelineMetadata metadata_; | |
| 371 base::TimeDelta start_time_; | |
| 372 | |
| 373 private: | |
| 374 DISALLOW_COPY_AND_ASSIGN(PipelineTest); | |
| 375 }; | |
| 376 | |
| 377 // Test that playback controls methods no-op when the pipeline hasn't been | |
| 378 // started. | |
| 379 TEST_F(PipelineTest, NotStarted) { | |
| 380 const base::TimeDelta kZero; | |
| 381 | |
| 382 EXPECT_FALSE(pipeline_->IsRunning()); | |
| 383 | |
| 384 // Setting should still work. | |
| 385 EXPECT_EQ(0.0f, pipeline_->GetPlaybackRate()); | |
| 386 pipeline_->SetPlaybackRate(-1.0); | |
| 387 EXPECT_EQ(0.0f, pipeline_->GetPlaybackRate()); | |
| 388 pipeline_->SetPlaybackRate(1.0); | |
| 389 EXPECT_EQ(1.0f, pipeline_->GetPlaybackRate()); | |
| 390 | |
| 391 // Setting should still work. | |
| 392 EXPECT_EQ(1.0f, pipeline_->GetVolume()); | |
| 393 pipeline_->SetVolume(-1.0f); | |
| 394 EXPECT_EQ(1.0f, pipeline_->GetVolume()); | |
| 395 pipeline_->SetVolume(0.0f); | |
| 396 EXPECT_EQ(0.0f, pipeline_->GetVolume()); | |
| 397 | |
| 398 EXPECT_TRUE(kZero == pipeline_->GetMediaTime()); | |
| 399 EXPECT_EQ(0u, pipeline_->GetBufferedTimeRanges().size()); | |
| 400 EXPECT_TRUE(kZero == pipeline_->GetMediaDuration()); | |
| 401 } | |
| 402 | |
| 403 TEST_F(PipelineTest, NeverInitializes) { | |
| 404 // Don't execute the callback passed into Initialize(). | |
| 405 EXPECT_CALL(*demuxer_, Initialize(_, _, _)); | |
| 406 | |
| 407 // This test hangs during initialization by never calling | |
| 408 // InitializationComplete(). StrictMock<> will ensure that the callback is | |
| 409 // never executed. | |
| 410 StartPipeline(); | |
| 411 message_loop_.RunUntilIdle(); | |
| 412 | |
| 413 // Because our callback will get executed when the test tears down, we'll | |
| 414 // verify that nothing has been called, then set our expectation for the call | |
| 415 // made during tear down. | |
| 416 Mock::VerifyAndClear(&callbacks_); | |
| 417 EXPECT_CALL(callbacks_, OnStart(PIPELINE_OK)); | |
| 418 } | |
| 419 | |
| 420 TEST_F(PipelineTest, StopWithoutStart) { | |
| 421 ExpectPipelineStopAndDestroyPipeline(); | |
| 422 pipeline_->Stop( | |
| 423 base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_))); | |
| 424 message_loop_.RunUntilIdle(); | |
| 425 } | |
| 426 | |
| 427 TEST_F(PipelineTest, StartThenStopImmediately) { | |
| 428 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | |
| 429 .WillOnce(PostCallback<1>(PIPELINE_OK)); | |
| 430 EXPECT_CALL(*demuxer_, Stop()); | |
| 431 | |
| 432 EXPECT_CALL(callbacks_, OnStart(_)); | |
| 433 StartPipeline(); | |
| 434 | |
| 435 // Expect a stop callback if we were started. | |
| 436 ExpectPipelineStopAndDestroyPipeline(); | |
| 437 pipeline_->Stop( | |
| 438 base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_))); | |
| 439 message_loop_.RunUntilIdle(); | |
| 440 } | |
| 441 | |
| 442 TEST_F(PipelineTest, DemuxerErrorDuringStop) { | |
| 443 CreateAudioStream(); | |
| 444 MockDemuxerStreamVector streams; | |
| 445 streams.push_back(audio_stream()); | |
| 446 | |
| 447 SetDemuxerExpectations(&streams); | |
| 448 SetRendererExpectations(); | |
| 449 | |
| 450 StartPipelineAndExpect(PIPELINE_OK); | |
| 451 | |
| 452 EXPECT_CALL(*demuxer_, Stop()) | |
| 453 .WillOnce(InvokeWithoutArgs(this, &PipelineTest::OnDemuxerError)); | |
| 454 ExpectPipelineStopAndDestroyPipeline(); | |
| 455 | |
| 456 pipeline_->Stop( | |
| 457 base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_))); | |
| 458 message_loop_.RunUntilIdle(); | |
| 459 } | |
| 460 | |
| 461 TEST_F(PipelineTest, URLNotFound) { | |
| 462 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | |
| 463 .WillOnce(PostCallback<1>(PIPELINE_ERROR_URL_NOT_FOUND)); | |
| 464 EXPECT_CALL(*demuxer_, Stop()); | |
| 465 | |
| 466 StartPipelineAndExpect(PIPELINE_ERROR_URL_NOT_FOUND); | |
| 467 } | |
| 468 | |
| 469 TEST_F(PipelineTest, NoStreams) { | |
| 470 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | |
| 471 .WillOnce(PostCallback<1>(PIPELINE_OK)); | |
| 472 EXPECT_CALL(*demuxer_, Stop()); | |
| 473 EXPECT_CALL(callbacks_, OnMetadata(_)); | |
| 474 | |
| 475 StartPipelineAndExpect(PIPELINE_ERROR_COULD_NOT_RENDER); | |
| 476 } | |
| 477 | |
| 478 TEST_F(PipelineTest, AudioStream) { | |
| 479 CreateAudioStream(); | |
| 480 MockDemuxerStreamVector streams; | |
| 481 streams.push_back(audio_stream()); | |
| 482 | |
| 483 SetDemuxerExpectations(&streams); | |
| 484 SetRendererExpectations(); | |
| 485 | |
| 486 StartPipelineAndExpect(PIPELINE_OK); | |
| 487 EXPECT_TRUE(metadata_.has_audio); | |
| 488 EXPECT_FALSE(metadata_.has_video); | |
| 489 } | |
| 490 | |
| 491 TEST_F(PipelineTest, VideoStream) { | |
| 492 CreateVideoStream(); | |
| 493 MockDemuxerStreamVector streams; | |
| 494 streams.push_back(video_stream()); | |
| 495 | |
| 496 SetDemuxerExpectations(&streams); | |
| 497 SetRendererExpectations(); | |
| 498 | |
| 499 StartPipelineAndExpect(PIPELINE_OK); | |
| 500 EXPECT_FALSE(metadata_.has_audio); | |
| 501 EXPECT_TRUE(metadata_.has_video); | |
| 502 } | |
| 503 | |
| 504 TEST_F(PipelineTest, AudioVideoStream) { | |
| 505 CreateAudioStream(); | |
| 506 CreateVideoStream(); | |
| 507 MockDemuxerStreamVector streams; | |
| 508 streams.push_back(audio_stream()); | |
| 509 streams.push_back(video_stream()); | |
| 510 | |
| 511 SetDemuxerExpectations(&streams); | |
| 512 SetRendererExpectations(); | |
| 513 | |
| 514 StartPipelineAndExpect(PIPELINE_OK); | |
| 515 EXPECT_TRUE(metadata_.has_audio); | |
| 516 EXPECT_TRUE(metadata_.has_video); | |
| 517 } | |
| 518 | |
| 519 TEST_F(PipelineTest, VideoTextStream) { | |
| 520 CreateVideoStream(); | |
| 521 CreateTextStream(); | |
| 522 MockDemuxerStreamVector streams; | |
| 523 streams.push_back(video_stream()); | |
| 524 | |
| 525 SetDemuxerExpectations(&streams); | |
| 526 SetRendererExpectations(); | |
| 527 | |
| 528 StartPipelineAndExpect(PIPELINE_OK); | |
| 529 EXPECT_FALSE(metadata_.has_audio); | |
| 530 EXPECT_TRUE(metadata_.has_video); | |
| 531 | |
| 532 AddTextStream(); | |
| 533 } | |
| 534 | |
| 535 TEST_F(PipelineTest, VideoAudioTextStream) { | |
| 536 CreateVideoStream(); | |
| 537 CreateAudioStream(); | |
| 538 CreateTextStream(); | |
| 539 MockDemuxerStreamVector streams; | |
| 540 streams.push_back(video_stream()); | |
| 541 streams.push_back(audio_stream()); | |
| 542 | |
| 543 SetDemuxerExpectations(&streams); | |
| 544 SetRendererExpectations(); | |
| 545 | |
| 546 StartPipelineAndExpect(PIPELINE_OK); | |
| 547 EXPECT_TRUE(metadata_.has_audio); | |
| 548 EXPECT_TRUE(metadata_.has_video); | |
| 549 | |
| 550 AddTextStream(); | |
| 551 } | |
| 552 | |
| 553 TEST_F(PipelineTest, Seek) { | |
| 554 CreateAudioStream(); | |
| 555 CreateVideoStream(); | |
| 556 CreateTextStream(); | |
| 557 MockDemuxerStreamVector streams; | |
| 558 streams.push_back(audio_stream()); | |
| 559 streams.push_back(video_stream()); | |
| 560 | |
| 561 SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000)); | |
| 562 SetRendererExpectations(); | |
| 563 | |
| 564 // Initialize then seek! | |
| 565 StartPipelineAndExpect(PIPELINE_OK); | |
| 566 | |
| 567 // Every filter should receive a call to Seek(). | |
| 568 base::TimeDelta expected = base::TimeDelta::FromSeconds(2000); | |
| 569 ExpectSeek(expected, false); | |
| 570 DoSeek(expected); | |
| 571 } | |
| 572 | |
| 573 TEST_F(PipelineTest, SeekAfterError) { | |
| 574 CreateAudioStream(); | |
| 575 MockDemuxerStreamVector streams; | |
| 576 streams.push_back(audio_stream()); | |
| 577 | |
| 578 SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000)); | |
| 579 SetRendererExpectations(); | |
| 580 | |
| 581 // Initialize then seek! | |
| 582 StartPipelineAndExpect(PIPELINE_OK); | |
| 583 | |
| 584 EXPECT_CALL(*demuxer_, Stop()); | |
| 585 EXPECT_CALL(callbacks_, OnError(_)); | |
| 586 | |
| 587 static_cast<DemuxerHost*>(pipeline_.get()) | |
| 588 ->OnDemuxerError(PIPELINE_ERROR_ABORT); | |
| 589 message_loop_.RunUntilIdle(); | |
| 590 | |
| 591 pipeline_->Seek( | |
| 592 base::TimeDelta::FromMilliseconds(100), | |
| 593 base::Bind(&CallbackHelper::OnSeek, base::Unretained(&callbacks_))); | |
| 594 message_loop_.RunUntilIdle(); | |
| 595 } | |
| 596 | |
| 597 TEST_F(PipelineTest, SuspendResume) { | |
| 598 CreateAudioStream(); | |
| 599 CreateVideoStream(); | |
| 600 CreateTextStream(); | |
| 601 MockDemuxerStreamVector streams; | |
| 602 streams.push_back(audio_stream()); | |
| 603 streams.push_back(video_stream()); | |
| 604 | |
| 605 SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000)); | |
| 606 SetRendererExpectations(); | |
| 607 | |
| 608 StartPipelineAndExpect(PIPELINE_OK); | |
| 609 | |
| 610 ExpectSuspend(); | |
| 611 DoSuspend(); | |
| 612 | |
| 613 base::TimeDelta expected = base::TimeDelta::FromSeconds(2000); | |
| 614 ExpectResume(expected); | |
| 615 DoResume(expected); | |
| 616 } | |
| 617 | |
| 618 TEST_F(PipelineTest, SetVolume) { | |
| 619 CreateAudioStream(); | |
| 620 MockDemuxerStreamVector streams; | |
| 621 streams.push_back(audio_stream()); | |
| 622 | |
| 623 SetDemuxerExpectations(&streams); | |
| 624 SetRendererExpectations(); | |
| 625 | |
| 626 // The audio renderer should receive a call to SetVolume(). | |
| 627 float expected = 0.5f; | |
| 628 EXPECT_CALL(*renderer_, SetVolume(expected)); | |
| 629 | |
| 630 // Initialize then set volume! | |
| 631 StartPipelineAndExpect(PIPELINE_OK); | |
| 632 pipeline_->SetVolume(expected); | |
| 633 } | |
| 634 | |
| 635 TEST_F(PipelineTest, Properties) { | |
| 636 CreateVideoStream(); | |
| 637 MockDemuxerStreamVector streams; | |
| 638 streams.push_back(video_stream()); | |
| 639 | |
| 640 const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100); | |
| 641 SetDemuxerExpectations(&streams, kDuration); | |
| 642 SetRendererExpectations(); | |
| 643 | |
| 644 StartPipelineAndExpect(PIPELINE_OK); | |
| 645 EXPECT_EQ(kDuration.ToInternalValue(), | |
| 646 pipeline_->GetMediaDuration().ToInternalValue()); | |
| 647 EXPECT_FALSE(pipeline_->DidLoadingProgress()); | |
| 648 } | |
| 649 | |
| 650 TEST_F(PipelineTest, GetBufferedTimeRanges) { | |
| 651 CreateVideoStream(); | |
| 652 MockDemuxerStreamVector streams; | |
| 653 streams.push_back(video_stream()); | |
| 654 | |
| 655 const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100); | |
| 656 SetDemuxerExpectations(&streams, kDuration); | |
| 657 SetRendererExpectations(); | |
| 658 | |
| 659 StartPipelineAndExpect(PIPELINE_OK); | |
| 660 | |
| 661 EXPECT_EQ(0u, pipeline_->GetBufferedTimeRanges().size()); | |
| 662 | |
| 663 EXPECT_FALSE(pipeline_->DidLoadingProgress()); | |
| 664 Ranges<base::TimeDelta> ranges; | |
| 665 ranges.Add(base::TimeDelta(), kDuration / 8); | |
| 666 pipeline_->OnBufferedTimeRangesChanged(ranges); | |
| 667 EXPECT_TRUE(pipeline_->DidLoadingProgress()); | |
| 668 EXPECT_FALSE(pipeline_->DidLoadingProgress()); | |
| 669 EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size()); | |
| 670 EXPECT_EQ(base::TimeDelta(), pipeline_->GetBufferedTimeRanges().start(0)); | |
| 671 EXPECT_EQ(kDuration / 8, pipeline_->GetBufferedTimeRanges().end(0)); | |
| 672 | |
| 673 base::TimeDelta kSeekTime = kDuration / 2; | |
| 674 ExpectSeek(kSeekTime, false); | |
| 675 DoSeek(kSeekTime); | |
| 676 | |
| 677 EXPECT_FALSE(pipeline_->DidLoadingProgress()); | |
| 678 } | |
| 679 | |
| 680 TEST_F(PipelineTest, EndedCallback) { | |
| 681 CreateAudioStream(); | |
| 682 CreateVideoStream(); | |
| 683 CreateTextStream(); | |
| 684 MockDemuxerStreamVector streams; | |
| 685 streams.push_back(audio_stream()); | |
| 686 streams.push_back(video_stream()); | |
| 687 | |
| 688 SetDemuxerExpectations(&streams); | |
| 689 SetRendererExpectations(); | |
| 690 StartPipelineAndExpect(PIPELINE_OK); | |
| 691 | |
| 692 AddTextStream(); | |
| 693 | |
| 694 // The ended callback shouldn't run until all renderers have ended. | |
| 695 ended_cb_.Run(); | |
| 696 message_loop_.RunUntilIdle(); | |
| 697 | |
| 698 EXPECT_CALL(callbacks_, OnEnded()); | |
| 699 text_stream()->SendEosNotification(); | |
| 700 message_loop_.RunUntilIdle(); | |
| 701 } | |
| 702 | |
| 703 TEST_F(PipelineTest, ErrorDuringSeek) { | |
| 704 CreateAudioStream(); | |
| 705 MockDemuxerStreamVector streams; | |
| 706 streams.push_back(audio_stream()); | |
| 707 | |
| 708 SetDemuxerExpectations(&streams); | |
| 709 SetRendererExpectations(); | |
| 710 StartPipelineAndExpect(PIPELINE_OK); | |
| 711 | |
| 712 double playback_rate = 1.0; | |
| 713 EXPECT_CALL(*renderer_, SetPlaybackRate(playback_rate)); | |
| 714 pipeline_->SetPlaybackRate(playback_rate); | |
| 715 message_loop_.RunUntilIdle(); | |
| 716 | |
| 717 base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); | |
| 718 | |
| 719 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 720 EXPECT_CALL(*renderer_, Flush(_)) | |
| 721 .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_, | |
| 722 BUFFERING_HAVE_NOTHING), | |
| 723 RunClosure<0>())); | |
| 724 | |
| 725 EXPECT_CALL(*demuxer_, Seek(seek_time, _)) | |
| 726 .WillOnce(RunCallback<1>(PIPELINE_ERROR_READ)); | |
| 727 EXPECT_CALL(*demuxer_, Stop()); | |
| 728 | |
| 729 pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek, | |
| 730 base::Unretained(&callbacks_))); | |
| 731 EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ)); | |
| 732 message_loop_.RunUntilIdle(); | |
| 733 } | |
| 734 | |
| 735 // Invoked function OnError. This asserts that the pipeline does not enqueue | |
| 736 // non-teardown related tasks while tearing down. | |
| 737 static void TestNoCallsAfterError( | |
| 738 Pipeline* pipeline, base::MessageLoop* message_loop, | |
| 739 PipelineStatus /* status */) { | |
| 740 CHECK(pipeline); | |
| 741 CHECK(message_loop); | |
| 742 | |
| 743 // When we get to this stage, the message loop should be empty. | |
| 744 EXPECT_TRUE(message_loop->IsIdleForTesting()); | |
| 745 | |
| 746 // Make calls on pipeline after error has occurred. | |
| 747 pipeline->SetPlaybackRate(0.5); | |
| 748 pipeline->SetVolume(0.5f); | |
| 749 | |
| 750 // No additional tasks should be queued as a result of these calls. | |
| 751 EXPECT_TRUE(message_loop->IsIdleForTesting()); | |
| 752 } | |
| 753 | |
| 754 TEST_F(PipelineTest, NoMessageDuringTearDownFromError) { | |
| 755 CreateAudioStream(); | |
| 756 MockDemuxerStreamVector streams; | |
| 757 streams.push_back(audio_stream()); | |
| 758 | |
| 759 SetDemuxerExpectations(&streams); | |
| 760 SetRendererExpectations(); | |
| 761 StartPipelineAndExpect(PIPELINE_OK); | |
| 762 | |
| 763 // Trigger additional requests on the pipeline during tear down from error. | |
| 764 base::Callback<void(PipelineStatus)> cb = base::Bind( | |
| 765 &TestNoCallsAfterError, pipeline_.get(), &message_loop_); | |
| 766 ON_CALL(callbacks_, OnError(_)) | |
| 767 .WillByDefault(Invoke(&cb, &base::Callback<void(PipelineStatus)>::Run)); | |
| 768 | |
| 769 base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); | |
| 770 | |
| 771 // Seek() isn't called as the demuxer errors out first. | |
| 772 EXPECT_CALL(*renderer_, Flush(_)) | |
| 773 .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_, | |
| 774 BUFFERING_HAVE_NOTHING), | |
| 775 RunClosure<0>())); | |
| 776 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 777 | |
| 778 EXPECT_CALL(*demuxer_, Seek(seek_time, _)) | |
| 779 .WillOnce(RunCallback<1>(PIPELINE_ERROR_READ)); | |
| 780 EXPECT_CALL(*demuxer_, Stop()); | |
| 781 | |
| 782 pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek, | |
| 783 base::Unretained(&callbacks_))); | |
| 784 EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ)); | |
| 785 message_loop_.RunUntilIdle(); | |
| 786 } | |
| 787 | |
| 788 TEST_F(PipelineTest, DestroyAfterStop) { | |
| 789 CreateAudioStream(); | |
| 790 MockDemuxerStreamVector streams; | |
| 791 streams.push_back(audio_stream()); | |
| 792 SetDemuxerExpectations(&streams); | |
| 793 SetRendererExpectations(); | |
| 794 StartPipelineAndExpect(PIPELINE_OK); | |
| 795 | |
| 796 ExpectDemuxerStop(); | |
| 797 | |
| 798 ExpectPipelineStopAndDestroyPipeline(); | |
| 799 pipeline_->Stop( | |
| 800 base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_))); | |
| 801 message_loop_.RunUntilIdle(); | |
| 802 } | |
| 803 | |
| 804 TEST_F(PipelineTest, Underflow) { | |
| 805 CreateAudioStream(); | |
| 806 CreateVideoStream(); | |
| 807 MockDemuxerStreamVector streams; | |
| 808 streams.push_back(audio_stream()); | |
| 809 streams.push_back(video_stream()); | |
| 810 | |
| 811 SetDemuxerExpectations(&streams); | |
| 812 SetRendererExpectations(); | |
| 813 StartPipelineAndExpect(PIPELINE_OK); | |
| 814 | |
| 815 // Simulate underflow. | |
| 816 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 817 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); | |
| 818 | |
| 819 // Seek while underflowed. | |
| 820 base::TimeDelta expected = base::TimeDelta::FromSeconds(5); | |
| 821 ExpectSeek(expected, true); | |
| 822 DoSeek(expected); | |
| 823 } | |
| 824 | |
| 825 TEST_F(PipelineTest, PositiveStartTime) { | |
| 826 start_time_ = base::TimeDelta::FromSeconds(1); | |
| 827 EXPECT_CALL(*demuxer_, GetStartTime()).WillRepeatedly(Return(start_time_)); | |
| 828 CreateAudioStream(); | |
| 829 MockDemuxerStreamVector streams; | |
| 830 streams.push_back(audio_stream()); | |
| 831 SetDemuxerExpectations(&streams); | |
| 832 SetRendererExpectations(); | |
| 833 StartPipelineAndExpect(PIPELINE_OK); | |
| 834 ExpectDemuxerStop(); | |
| 835 ExpectPipelineStopAndDestroyPipeline(); | |
| 836 pipeline_->Stop( | |
| 837 base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_))); | |
| 838 message_loop_.RunUntilIdle(); | |
| 839 } | |
| 840 | |
| 841 class PipelineTeardownTest : public PipelineTest { | |
| 842 public: | |
| 843 enum TeardownState { | |
| 844 kInitDemuxer, | |
| 845 kInitRenderer, | |
| 846 kFlushing, | |
| 847 kSeeking, | |
| 848 kPlaying, | |
| 849 kSuspending, | |
| 850 kSuspended, | |
| 851 kResuming, | |
| 852 }; | |
| 853 | |
| 854 enum StopOrError { | |
| 855 kStop, | |
| 856 kError, | |
| 857 kErrorAndStop, | |
| 858 }; | |
| 859 | |
| 860 PipelineTeardownTest() {} | |
| 861 ~PipelineTeardownTest() override {} | |
| 862 | |
| 863 void RunTest(TeardownState state, StopOrError stop_or_error) { | |
| 864 switch (state) { | |
| 865 case kInitDemuxer: | |
| 866 case kInitRenderer: | |
| 867 DoInitialize(state, stop_or_error); | |
| 868 break; | |
| 869 | |
| 870 case kFlushing: | |
| 871 case kSeeking: | |
| 872 DoInitialize(state, stop_or_error); | |
| 873 DoSeek(state, stop_or_error); | |
| 874 break; | |
| 875 | |
| 876 case kPlaying: | |
| 877 DoInitialize(state, stop_or_error); | |
| 878 DoStopOrError(stop_or_error); | |
| 879 break; | |
| 880 | |
| 881 case kSuspending: | |
| 882 case kSuspended: | |
| 883 case kResuming: | |
| 884 DoInitialize(state, stop_or_error); | |
| 885 DoSuspend(state, stop_or_error); | |
| 886 break; | |
| 887 } | |
| 888 } | |
| 889 | |
| 890 private: | |
| 891 // TODO(scherkus): We do radically different things whether teardown is | |
| 892 // invoked via stop vs error. The teardown path should be the same, | |
| 893 // see http://crbug.com/110228 | |
| 894 void DoInitialize(TeardownState state, StopOrError stop_or_error) { | |
| 895 PipelineStatus expected_status = | |
| 896 SetInitializeExpectations(state, stop_or_error); | |
| 897 | |
| 898 EXPECT_CALL(callbacks_, OnStart(expected_status)); | |
| 899 StartPipeline(); | |
| 900 message_loop_.RunUntilIdle(); | |
| 901 } | |
| 902 | |
| 903 PipelineStatus SetInitializeExpectations(TeardownState state, | |
| 904 StopOrError stop_or_error) { | |
| 905 PipelineStatus status = PIPELINE_OK; | |
| 906 base::Closure stop_cb = base::Bind( | |
| 907 &CallbackHelper::OnStop, base::Unretained(&callbacks_)); | |
| 908 | |
| 909 if (state == kInitDemuxer) { | |
| 910 if (stop_or_error == kStop) { | |
| 911 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | |
| 912 .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), | |
| 913 PostCallback<1>(PIPELINE_OK))); | |
| 914 ExpectPipelineStopAndDestroyPipeline(); | |
| 915 } else { | |
| 916 status = DEMUXER_ERROR_COULD_NOT_OPEN; | |
| 917 EXPECT_CALL(*demuxer_, Initialize(_, _, _)) | |
| 918 .WillOnce(PostCallback<1>(status)); | |
| 919 } | |
| 920 | |
| 921 EXPECT_CALL(*demuxer_, Stop()); | |
| 922 return status; | |
| 923 } | |
| 924 | |
| 925 CreateAudioStream(); | |
| 926 CreateVideoStream(); | |
| 927 MockDemuxerStreamVector streams; | |
| 928 streams.push_back(audio_stream()); | |
| 929 streams.push_back(video_stream()); | |
| 930 SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000)); | |
| 931 | |
| 932 EXPECT_CALL(*renderer_, HasAudio()).WillRepeatedly(Return(true)); | |
| 933 EXPECT_CALL(*renderer_, HasVideo()).WillRepeatedly(Return(true)); | |
| 934 | |
| 935 if (state == kInitRenderer) { | |
| 936 if (stop_or_error == kStop) { | |
| 937 EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _)) | |
| 938 .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), | |
| 939 PostCallback<1>(PIPELINE_OK))); | |
| 940 ExpectPipelineStopAndDestroyPipeline(); | |
| 941 } else { | |
| 942 status = PIPELINE_ERROR_INITIALIZATION_FAILED; | |
| 943 EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _)) | |
| 944 .WillOnce(PostCallback<1>(status)); | |
| 945 } | |
| 946 | |
| 947 EXPECT_CALL(*demuxer_, Stop()); | |
| 948 EXPECT_CALL(callbacks_, OnMetadata(_)); | |
| 949 return status; | |
| 950 } | |
| 951 | |
| 952 EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _)) | |
| 953 .WillOnce(DoAll(SaveArg<3>(&buffering_state_cb_), | |
| 954 PostCallback<1>(PIPELINE_OK))); | |
| 955 | |
| 956 EXPECT_CALL(callbacks_, OnMetadata(_)); | |
| 957 | |
| 958 // If we get here it's a successful initialization. | |
| 959 EXPECT_CALL(*renderer_, SetPlaybackRate(0.0)); | |
| 960 EXPECT_CALL(*renderer_, SetVolume(1.0f)); | |
| 961 EXPECT_CALL(*renderer_, StartPlayingFrom(base::TimeDelta())) | |
| 962 .WillOnce(SetBufferingState(&buffering_state_cb_, | |
| 963 BUFFERING_HAVE_ENOUGH)); | |
| 964 | |
| 965 if (status == PIPELINE_OK) | |
| 966 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); | |
| 967 | |
| 968 return status; | |
| 969 } | |
| 970 | |
| 971 void DoSeek(TeardownState state, StopOrError stop_or_error) { | |
| 972 InSequence s; | |
| 973 PipelineStatus status = SetSeekExpectations(state, stop_or_error); | |
| 974 | |
| 975 EXPECT_CALL(*demuxer_, Stop()); | |
| 976 EXPECT_CALL(callbacks_, OnSeek(status)); | |
| 977 | |
| 978 if (status == PIPELINE_OK) { | |
| 979 ExpectPipelineStopAndDestroyPipeline(); | |
| 980 } | |
| 981 | |
| 982 pipeline_->Seek(base::TimeDelta::FromSeconds(10), base::Bind( | |
| 983 &CallbackHelper::OnSeek, base::Unretained(&callbacks_))); | |
| 984 message_loop_.RunUntilIdle(); | |
| 985 } | |
| 986 | |
| 987 PipelineStatus SetSeekExpectations(TeardownState state, | |
| 988 StopOrError stop_or_error) { | |
| 989 PipelineStatus status = PIPELINE_OK; | |
| 990 base::Closure stop_cb = base::Bind( | |
| 991 &CallbackHelper::OnStop, base::Unretained(&callbacks_)); | |
| 992 | |
| 993 if (state == kFlushing) { | |
| 994 if (stop_or_error == kStop) { | |
| 995 EXPECT_CALL(*renderer_, Flush(_)) | |
| 996 .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), | |
| 997 SetBufferingState(&buffering_state_cb_, | |
| 998 BUFFERING_HAVE_NOTHING), | |
| 999 RunClosure<0>())); | |
| 1000 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 1001 } else { | |
| 1002 status = PIPELINE_ERROR_READ; | |
| 1003 EXPECT_CALL(*renderer_, Flush(_)) | |
| 1004 .WillOnce(DoAll(SetError(pipeline_.get(), status), | |
| 1005 SetBufferingState(&buffering_state_cb_, | |
| 1006 BUFFERING_HAVE_NOTHING), | |
| 1007 RunClosure<0>())); | |
| 1008 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 1009 } | |
| 1010 | |
| 1011 return status; | |
| 1012 } | |
| 1013 | |
| 1014 EXPECT_CALL(*renderer_, Flush(_)) | |
| 1015 .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_, | |
| 1016 BUFFERING_HAVE_NOTHING), | |
| 1017 RunClosure<0>())); | |
| 1018 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 1019 | |
| 1020 if (state == kSeeking) { | |
| 1021 if (stop_or_error == kStop) { | |
| 1022 EXPECT_CALL(*demuxer_, Seek(_, _)) | |
| 1023 .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), | |
| 1024 RunCallback<1>(PIPELINE_OK))); | |
| 1025 } else { | |
| 1026 status = PIPELINE_ERROR_READ; | |
| 1027 EXPECT_CALL(*demuxer_, Seek(_, _)) | |
| 1028 .WillOnce(RunCallback<1>(status)); | |
| 1029 } | |
| 1030 | |
| 1031 return status; | |
| 1032 } | |
| 1033 | |
| 1034 NOTREACHED() << "State not supported: " << state; | |
| 1035 return status; | |
| 1036 } | |
| 1037 | |
| 1038 void DoSuspend(TeardownState state, StopOrError stop_or_error) { | |
| 1039 PipelineStatus status = SetSuspendExpectations(state, stop_or_error); | |
| 1040 | |
| 1041 if (state != kSuspended) { | |
| 1042 // DoStopOrError() handles these for kSuspended. | |
| 1043 EXPECT_CALL(*demuxer_, Stop()); | |
| 1044 if (status == PIPELINE_OK) { | |
| 1045 ExpectPipelineStopAndDestroyPipeline(); | |
| 1046 } | |
| 1047 } | |
| 1048 | |
| 1049 PipelineTest::DoSuspend(); | |
| 1050 | |
| 1051 if (state == kSuspended) { | |
| 1052 DoStopOrError(stop_or_error); | |
| 1053 } else if (state == kResuming) { | |
| 1054 PipelineTest::DoResume(base::TimeDelta()); | |
| 1055 } | |
| 1056 } | |
| 1057 | |
| 1058 PipelineStatus SetSuspendExpectations(TeardownState state, | |
| 1059 StopOrError stop_or_error) { | |
| 1060 PipelineStatus status = PIPELINE_OK; | |
| 1061 base::Closure stop_cb = | |
| 1062 base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)); | |
| 1063 | |
| 1064 EXPECT_CALL(*renderer_, SetPlaybackRate(0)); | |
| 1065 if (state == kSuspended || state == kResuming) { | |
| 1066 EXPECT_CALL(*renderer_, Flush(_)) | |
| 1067 .WillOnce(DoAll( | |
| 1068 SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING), | |
| 1069 RunClosure<0>())); | |
| 1070 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 1071 EXPECT_CALL(callbacks_, OnSuspend(PIPELINE_OK)); | |
| 1072 if (state == kResuming) { | |
| 1073 if (stop_or_error == kStop) { | |
| 1074 EXPECT_CALL(*demuxer_, Seek(_, _)) | |
| 1075 .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), | |
| 1076 RunCallback<1>(PIPELINE_OK))); | |
| 1077 EXPECT_CALL(callbacks_, OnResume(PIPELINE_OK)); | |
| 1078 } else { | |
| 1079 status = PIPELINE_ERROR_READ; | |
| 1080 EXPECT_CALL(*demuxer_, Seek(_, _)).WillOnce(RunCallback<1>(status)); | |
| 1081 EXPECT_CALL(callbacks_, OnResume(status)); | |
| 1082 } | |
| 1083 } | |
| 1084 return status; | |
| 1085 } else if (state == kSuspending) { | |
| 1086 if (stop_or_error == kStop) { | |
| 1087 EXPECT_CALL(*renderer_, Flush(_)) | |
| 1088 .WillOnce(DoAll( | |
| 1089 Stop(pipeline_.get(), stop_cb), | |
| 1090 SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING), | |
| 1091 RunClosure<0>())); | |
| 1092 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); | |
| 1093 EXPECT_CALL(callbacks_, OnSuspend(PIPELINE_OK)); | |
| 1094 } else { | |
| 1095 status = PIPELINE_ERROR_READ; | |
| 1096 EXPECT_CALL(*renderer_, Flush(_)) | |
| 1097 .WillOnce(SetError(pipeline_.get(), status)); | |
| 1098 EXPECT_CALL(callbacks_, OnSuspend(status)); | |
| 1099 } | |
| 1100 return status; | |
| 1101 } | |
| 1102 | |
| 1103 NOTREACHED() << "State not supported: " << state; | |
| 1104 return status; | |
| 1105 } | |
| 1106 | |
| 1107 void DoStopOrError(StopOrError stop_or_error) { | |
| 1108 InSequence s; | |
| 1109 | |
| 1110 EXPECT_CALL(*demuxer_, Stop()); | |
| 1111 | |
| 1112 switch (stop_or_error) { | |
| 1113 case kStop: | |
| 1114 ExpectPipelineStopAndDestroyPipeline(); | |
| 1115 pipeline_->Stop(base::Bind( | |
| 1116 &CallbackHelper::OnStop, base::Unretained(&callbacks_))); | |
| 1117 break; | |
| 1118 | |
| 1119 case kError: | |
| 1120 EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ)); | |
| 1121 pipeline_->SetErrorForTesting(PIPELINE_ERROR_READ); | |
| 1122 break; | |
| 1123 | |
| 1124 case kErrorAndStop: | |
| 1125 EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ)); | |
| 1126 ExpectPipelineStopAndDestroyPipeline(); | |
| 1127 pipeline_->SetErrorForTesting(PIPELINE_ERROR_READ); | |
| 1128 message_loop_.RunUntilIdle(); | |
| 1129 pipeline_->Stop(base::Bind( | |
| 1130 &CallbackHelper::OnStop, base::Unretained(&callbacks_))); | |
| 1131 break; | |
| 1132 } | |
| 1133 | |
| 1134 message_loop_.RunUntilIdle(); | |
| 1135 } | |
| 1136 | |
| 1137 DISALLOW_COPY_AND_ASSIGN(PipelineTeardownTest); | |
| 1138 }; | |
| 1139 | |
| 1140 #define INSTANTIATE_TEARDOWN_TEST(stop_or_error, state) \ | |
| 1141 TEST_F(PipelineTeardownTest, stop_or_error##_##state) { \ | |
| 1142 RunTest(k##state, k##stop_or_error); \ | |
| 1143 } | |
| 1144 | |
| 1145 INSTANTIATE_TEARDOWN_TEST(Stop, InitDemuxer); | |
| 1146 INSTANTIATE_TEARDOWN_TEST(Stop, InitRenderer); | |
| 1147 INSTANTIATE_TEARDOWN_TEST(Stop, Flushing); | |
| 1148 INSTANTIATE_TEARDOWN_TEST(Stop, Seeking); | |
| 1149 INSTANTIATE_TEARDOWN_TEST(Stop, Playing); | |
| 1150 INSTANTIATE_TEARDOWN_TEST(Stop, Suspending); | |
| 1151 INSTANTIATE_TEARDOWN_TEST(Stop, Suspended); | |
| 1152 INSTANTIATE_TEARDOWN_TEST(Stop, Resuming); | |
| 1153 | |
| 1154 INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer); | |
| 1155 INSTANTIATE_TEARDOWN_TEST(Error, InitRenderer); | |
| 1156 INSTANTIATE_TEARDOWN_TEST(Error, Flushing); | |
| 1157 INSTANTIATE_TEARDOWN_TEST(Error, Seeking); | |
| 1158 INSTANTIATE_TEARDOWN_TEST(Error, Playing); | |
| 1159 INSTANTIATE_TEARDOWN_TEST(Error, Suspending); | |
| 1160 INSTANTIATE_TEARDOWN_TEST(Error, Suspended); | |
| 1161 INSTANTIATE_TEARDOWN_TEST(Error, Resuming); | |
| 1162 | |
| 1163 INSTANTIATE_TEARDOWN_TEST(ErrorAndStop, Playing); | |
| 1164 INSTANTIATE_TEARDOWN_TEST(ErrorAndStop, Suspended); | |
| 1165 | |
| 1166 } // namespace media | |
| OLD | NEW |