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 |