Index: media/base/pipeline_unittest.cc |
diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc |
index c949ef1b82faa71bea59f4acfa26a17de9600609..36b42578d877bcceb3f2be7f11a92331cf5ec5f1 100644 |
--- a/media/base/pipeline_unittest.cc |
+++ b/media/base/pipeline_unittest.cc |
@@ -19,6 +19,7 @@ |
using ::testing::_; |
using ::testing::DeleteArg; |
using ::testing::DoAll; |
+// TODO(scherkus): Remove InSequence after refactoring Pipeline. |
using ::testing::InSequence; |
using ::testing::Invoke; |
using ::testing::InvokeWithoutArgs; |
@@ -41,11 +42,19 @@ ACTION_P(SetDemuxerProperties, duration) { |
arg0->SetDuration(duration); |
} |
-ACTION(RunPipelineStatusCB1) { |
+ACTION_P2(Stop, pipeline, stop_cb) { |
+ pipeline->Stop(stop_cb); |
+} |
+ |
+ACTION_P2(SetError, pipeline, status) { |
+ pipeline->SetErrorForTesting(status); |
+} |
+ |
+ACTION(RunPipelineStatusCB) { |
arg1.Run(PIPELINE_OK); |
} |
-ACTION_P(RunPipelineStatusCB1WithStatus, status) { |
+ACTION_P(RunPipelineStatusCBWithStatus, status) { |
arg1.Run(status); |
} |
@@ -98,6 +107,8 @@ class PipelineTest : public ::testing::Test { |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
+ // TODO(scherkus): Don't pause+flush on shutdown, |
+ // see http://crbug.com/110228 |
if (audio_stream_) { |
EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)) |
.WillOnce(RunClosure()); |
@@ -134,7 +145,7 @@ class PipelineTest : public ::testing::Test { |
const base::TimeDelta& duration) { |
EXPECT_CALL(*mocks_->demuxer(), Initialize(_, _)) |
.WillOnce(DoAll(SetDemuxerProperties(duration), |
- RunPipelineStatusCB1())); |
+ RunPipelineStatusCB())); |
// Configure the demuxer to return the streams. |
for (size_t i = 0; i < streams->size(); ++i) { |
@@ -161,13 +172,13 @@ class PipelineTest : public ::testing::Test { |
void InitializeVideoDecoder(const scoped_refptr<DemuxerStream>& stream) { |
EXPECT_CALL(*mocks_->video_decoder(), |
Initialize(stream, _, _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
} |
// Sets up expectations to allow the audio decoder to initialize. |
void InitializeAudioDecoder(const scoped_refptr<DemuxerStream>& stream) { |
EXPECT_CALL(*mocks_->audio_decoder(), Initialize(stream, _, _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
} |
// Sets up expectations to allow the video renderer to initialize. |
@@ -175,13 +186,13 @@ class PipelineTest : public ::testing::Test { |
EXPECT_CALL(*mocks_->video_renderer(), Initialize( |
scoped_refptr<VideoDecoder>(mocks_->video_decoder()), |
_, _, _, _, _, _, _, _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
EXPECT_CALL(*mocks_->video_renderer(), SetPlaybackRate(0.0f)); |
// Startup sequence. |
EXPECT_CALL(*mocks_->video_renderer(), |
Preroll(mocks_->demuxer()->GetStartTime(), _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
EXPECT_CALL(*mocks_->video_renderer(), Play(_)) |
.WillOnce(RunClosure()); |
} |
@@ -192,14 +203,14 @@ class PipelineTest : public ::testing::Test { |
EXPECT_CALL(*mocks_->audio_renderer(), Initialize( |
scoped_refptr<AudioDecoder>(mocks_->audio_decoder()), |
_, _, _, _, _, _)) |
- .WillOnce(DoAll(RunPipelineStatusCB1(), |
+ .WillOnce(DoAll(RunPipelineStatusCB(), |
WithArg<5>(RunClosure()))); // |disabled_cb|. |
} else { |
EXPECT_CALL(*mocks_->audio_renderer(), Initialize( |
scoped_refptr<AudioDecoder>(mocks_->audio_decoder()), |
_, _, _, _, _, _)) |
.WillOnce(DoAll(SaveArg<3>(&audio_time_cb_), |
- RunPipelineStatusCB1())); |
+ RunPipelineStatusCB())); |
} |
} |
@@ -217,7 +228,7 @@ class PipelineTest : public ::testing::Test { |
// Startup sequence. |
EXPECT_CALL(*mocks_->audio_renderer(), Preroll(base::TimeDelta(), _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
EXPECT_CALL(*mocks_->audio_renderer(), Play(_)) |
.WillOnce(RunClosure()); |
} |
@@ -250,7 +261,7 @@ class PipelineTest : public ::testing::Test { |
void ExpectSeek(const base::TimeDelta& seek_time) { |
// Every filter should receive a call to Seek(). |
EXPECT_CALL(*mocks_->demuxer(), Seek(seek_time, _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
if (audio_stream_) { |
EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)) |
@@ -258,7 +269,7 @@ class PipelineTest : public ::testing::Test { |
EXPECT_CALL(*mocks_->audio_renderer(), Flush(_)) |
.WillOnce(RunClosure()); |
EXPECT_CALL(*mocks_->audio_renderer(), Preroll(seek_time, _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
EXPECT_CALL(*mocks_->audio_renderer(), Play(_)) |
.WillOnce(RunClosure()); |
} |
@@ -269,7 +280,7 @@ class PipelineTest : public ::testing::Test { |
EXPECT_CALL(*mocks_->video_renderer(), Flush(_)) |
.WillOnce(RunClosure()); |
EXPECT_CALL(*mocks_->video_renderer(), Preroll(seek_time, _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
EXPECT_CALL(*mocks_->video_renderer(), Play(_)) |
.WillOnce(RunClosure()); |
} |
@@ -381,7 +392,7 @@ TEST_F(PipelineTest, RequiredFilterMissing) { |
TEST_F(PipelineTest, URLNotFound) { |
EXPECT_CALL(*mocks_->demuxer(), Initialize(_, _)) |
- .WillOnce(RunPipelineStatusCB1WithStatus(PIPELINE_ERROR_URL_NOT_FOUND)); |
+ .WillOnce(RunPipelineStatusCBWithStatus(PIPELINE_ERROR_URL_NOT_FOUND)); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
@@ -391,7 +402,7 @@ TEST_F(PipelineTest, URLNotFound) { |
TEST_F(PipelineTest, NoStreams) { |
EXPECT_CALL(*mocks_->demuxer(), Initialize(_, _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
@@ -720,7 +731,7 @@ TEST_F(PipelineTest, ErrorDuringSeek) { |
.WillOnce(RunClosure()); |
EXPECT_CALL(*mocks_->demuxer(), Seek(seek_time, _)) |
- .WillOnce(RunPipelineStatusCB1WithStatus(PIPELINE_ERROR_READ)); |
+ .WillOnce(RunPipelineStatusCBWithStatus(PIPELINE_ERROR_READ)); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
@@ -776,7 +787,7 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) { |
.WillOnce(RunClosure()); |
EXPECT_CALL(*mocks_->demuxer(), Seek(seek_time, _)) |
- .WillOnce(RunPipelineStatusCB1WithStatus(PIPELINE_ERROR_READ)); |
+ .WillOnce(RunPipelineStatusCBWithStatus(PIPELINE_ERROR_READ)); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
@@ -863,14 +874,14 @@ TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) { |
base::Closure closure = base::Bind(&RunTimeCB, audio_time_cb_, 300, 700); |
EXPECT_CALL(*mocks_->demuxer(), Seek(seek_time, _)) |
.WillOnce(DoAll(InvokeWithoutArgs(&closure, &base::Closure::Run), |
- RunPipelineStatusCB1())); |
+ RunPipelineStatusCB())); |
EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)) |
.WillOnce(RunClosure()); |
EXPECT_CALL(*mocks_->audio_renderer(), Flush(_)) |
.WillOnce(RunClosure()); |
EXPECT_CALL(*mocks_->audio_renderer(), Preroll(seek_time, _)) |
- .WillOnce(RunPipelineStatusCB1()); |
+ .WillOnce(RunPipelineStatusCB()); |
EXPECT_CALL(*mocks_->audio_renderer(), Play(_)) |
.WillOnce(RunClosure()); |
@@ -890,7 +901,7 @@ TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) { |
TEST_F(PipelineTest, InitFailure_Demuxer) { |
PipelineStatus expected_status = DEMUXER_ERROR_COULD_NOT_OPEN; |
EXPECT_CALL(*mocks_->demuxer(), Initialize(_, _)) |
- .WillOnce(RunPipelineStatusCB1WithStatus(expected_status)); |
+ .WillOnce(RunPipelineStatusCBWithStatus(expected_status)); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
InitializePipeline(expected_status); |
@@ -907,7 +918,7 @@ TEST_F(PipelineTest, InitFailure_AudioDecoder) { |
PipelineStatus expected_status = PIPELINE_ERROR_DECODE; |
scoped_refptr<DemuxerStream> stream = streams[0]; |
EXPECT_CALL(*mocks_->audio_decoder(), Initialize(stream, _, _)) |
- .WillOnce(RunPipelineStatusCB1WithStatus(expected_status)); |
+ .WillOnce(RunPipelineStatusCBWithStatus(expected_status)); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
@@ -929,7 +940,7 @@ TEST_F(PipelineTest, InitFailure_AudioRenderer) { |
EXPECT_CALL(*mocks_->audio_renderer(), Initialize( |
scoped_refptr<AudioDecoder>(mocks_->audio_decoder()), |
_, _, _, _, _, _)) |
- .WillOnce(RunPipelineStatusCB1WithStatus(expected_status)); |
+ .WillOnce(RunPipelineStatusCBWithStatus(expected_status)); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
@@ -956,7 +967,7 @@ TEST_F(PipelineTest, InitFailure_VideoDecoder) { |
scoped_refptr<DemuxerStream> stream = streams[1]; |
EXPECT_CALL(*mocks_->video_decoder(), |
Initialize(stream, _, _)) |
- .WillOnce(RunPipelineStatusCB1WithStatus(expected_status)); |
+ .WillOnce(RunPipelineStatusCBWithStatus(expected_status)); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
@@ -985,7 +996,7 @@ TEST_F(PipelineTest, InitFailure_VideoRenderer) { |
EXPECT_CALL(*mocks_->video_renderer(), Initialize( |
scoped_refptr<VideoDecoder>(mocks_->video_decoder()), |
_, _, _, _, _, _, _, _)) |
- .WillOnce(RunPipelineStatusCB1WithStatus(expected_status)); |
+ .WillOnce(RunPipelineStatusCBWithStatus(expected_status)); |
EXPECT_CALL(*mocks_->demuxer(), Stop(_)) |
.WillOnce(RunClosure()); |
@@ -1053,4 +1064,223 @@ TEST(PipelineStatusNotificationTest, DelayedCallback) { |
TestPipelineStatusNotification(base::TimeDelta::FromMilliseconds(20)); |
} |
+class PipelineTeardownTest : public PipelineTest { |
+ public: |
+ enum TeardownState { |
+ kPausing, |
+ kFlushing, |
+ kSeeking, |
+ kPrerolling, |
+ kStarting, |
+ kPlaying, |
+ }; |
+ |
+ enum StopOrError { |
+ kStop, |
+ kError, |
+ }; |
+ |
+ PipelineTeardownTest() { |
+ CreateAudioStream(); |
+ MockDemuxerStreamVector streams; |
+ streams.push_back(audio_stream()); |
+ |
+ InitializeDemuxer(&streams, base::TimeDelta::FromSeconds(3000)); |
+ InitializeAudioDecoder(audio_stream()); |
+ InitializeAudioRenderer(); |
+ InitializePipeline(PIPELINE_OK); |
+ } |
+ |
+ void RunTest(TeardownState state, StopOrError stop_or_error) { |
+ InSequence s; |
+ switch (state) { |
+ case kPausing: |
+ case kFlushing: |
+ case kSeeking: |
+ case kPrerolling: |
+ case kStarting: |
+ if (stop_or_error == kStop) { |
+ ExpectSeekStop(state); |
+ } else { |
+ ExpectSeekError(state); |
+ } |
+ DoSeek(); |
+ break; |
+ |
+ case kPlaying: |
+ if (stop_or_error == kStop) { |
+ ExpectStop(); |
+ DoStop(); |
+ } else { |
+ ExpectPlaybackError(); |
+ DoPlaybackError(); |
+ } |
+ break; |
+ } |
+ } |
+ |
+ private: |
+ // TODO(scherkus): We do radically different things whether teardown is |
+ // invoked via stop vs error. The teardown path should be the same, |
+ // see http://crbug.com/110228 |
+ void ExpectSeekStop(TeardownState state) { |
+ base::Closure stop_cb = base::Bind( |
+ &CallbackHelper::OnStop, base::Unretained(&callbacks_)); |
+ |
+ if (state == kPausing) { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)) |
+ .WillOnce(DoAll(Stop(pipeline_, stop_cb), RunClosure())); |
+ } else { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)).WillOnce(RunClosure()); |
+ } |
+ |
+ if (state == kFlushing) { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Flush(_)) |
+ .WillOnce(DoAll(Stop(pipeline_, stop_cb), RunClosure())); |
+ } else { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Flush(_)).WillOnce(RunClosure()); |
+ } |
+ |
+ if (state == kSeeking) { |
+ EXPECT_CALL(*mocks_->demuxer(), Seek(_, _)) |
+ .WillOnce(DoAll(Stop(pipeline_, stop_cb), RunPipelineStatusCB())); |
+ } else { |
+ EXPECT_CALL(*mocks_->demuxer(), Seek(_, _)) |
+ .WillOnce(RunPipelineStatusCB()); |
+ } |
+ |
+ if (state == kPrerolling) { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Preroll(_, _)) |
+ .WillOnce(DoAll(Stop(pipeline_, stop_cb), RunPipelineStatusCB())); |
+ } else { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Preroll(_, _)) |
+ .WillOnce(RunPipelineStatusCB()); |
+ } |
+ |
+ if (state == kStarting) { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Play(_)) |
+ .WillOnce(DoAll(Stop(pipeline_, stop_cb), RunClosure())); |
+ } else { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Play(_)).WillOnce(RunClosure()); |
+ } |
+ |
+ EXPECT_CALL(callbacks_, OnSeek(PIPELINE_OK)); |
+ ExpectStop(); |
+ } |
+ |
+ void ExpectSeekError(TeardownState state) { |
+ SetSeekErrorExpectations(state); |
+ |
+ // Executed after the error is raised. |
+ EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ)); |
+ EXPECT_CALL(*mocks_->demuxer(), Stop(_)).WillOnce(RunClosure()); |
+ EXPECT_CALL(*mocks_->audio_renderer(), Stop(_)).WillOnce(RunClosure()); |
+ } |
+ |
+ void SetSeekErrorExpectations(TeardownState state) { |
+ if (state == kPausing) { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)) |
+ .WillOnce(DoAll(SetError(pipeline_, PIPELINE_ERROR_READ), |
+ RunClosure())); |
+ return; |
+ } |
+ |
+ EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)).WillOnce(RunClosure()); |
+ |
+ if (state == kFlushing) { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Flush(_)) |
+ .WillOnce(DoAll(SetError(pipeline_, PIPELINE_ERROR_READ), |
+ RunClosure())); |
+ return; |
+ } |
+ |
+ EXPECT_CALL(*mocks_->audio_renderer(), Flush(_)).WillOnce(RunClosure()); |
+ |
+ if (state == kSeeking) { |
+ EXPECT_CALL(*mocks_->demuxer(), Seek(_, _)) |
+ .WillOnce(RunPipelineStatusCBWithStatus(PIPELINE_ERROR_READ)); |
+ return; |
+ } |
+ |
+ EXPECT_CALL(*mocks_->demuxer(), Seek(_, _)) |
+ .WillOnce(RunPipelineStatusCB()); |
+ |
+ if (state == kPrerolling) { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Preroll(_, _)) |
+ .WillOnce(RunPipelineStatusCBWithStatus(PIPELINE_ERROR_READ)); |
+ return; |
+ } |
+ |
+ EXPECT_CALL(*mocks_->audio_renderer(), Preroll(_, _)) |
+ .WillOnce(RunPipelineStatusCB()); |
+ |
+ if (state == kStarting) { |
+ EXPECT_CALL(*mocks_->audio_renderer(), Play(_)) |
+ .WillOnce(DoAll(SetError(pipeline_, PIPELINE_ERROR_READ), |
+ RunClosure())); |
+ return; |
+ } |
+ |
+ NOTREACHED() << "Unexpected TeardownState: " << state; |
+ } |
+ |
+ void ExpectStop() { |
+ // TODO(scherkus): Don't pause+flush, see http://crbug.com/110228 |
+ EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)).WillOnce(RunClosure()); |
+ EXPECT_CALL(*mocks_->audio_renderer(), Flush(_)).WillOnce(RunClosure()); |
+ EXPECT_CALL(*mocks_->demuxer(), Stop(_)).WillOnce(RunClosure()); |
+ EXPECT_CALL(*mocks_->audio_renderer(), Stop(_)).WillOnce(RunClosure()); |
+ |
+ EXPECT_CALL(callbacks_, OnStop()); |
+ } |
+ |
+ void ExpectPlaybackError() { |
+ // TODO(scherkus): Don't pause+flush, see http://crbug.com/110228 |
+ EXPECT_CALL(*mocks_->audio_renderer(), Pause(_)).WillOnce(RunClosure()); |
+ EXPECT_CALL(*mocks_->audio_renderer(), Flush(_)).WillOnce(RunClosure()); |
+ EXPECT_CALL(*mocks_->demuxer(), Stop(_)).WillOnce(RunClosure()); |
+ EXPECT_CALL(*mocks_->audio_renderer(), Stop(_)).WillOnce(RunClosure()); |
+ |
+ EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ)); |
+ } |
+ |
+ void DoSeek() { |
+ pipeline_->Seek(base::TimeDelta::FromSeconds(10), base::Bind( |
+ &CallbackHelper::OnSeek, base::Unretained(&callbacks_))); |
+ message_loop_.RunAllPending(); |
+ } |
+ |
+ void DoStop() { |
+ pipeline_->Stop(base::Bind( |
+ &CallbackHelper::OnStop, base::Unretained(&callbacks_))); |
+ message_loop_.RunAllPending(); |
+ } |
+ |
+ void DoPlaybackError() { |
+ pipeline_->SetErrorForTesting(PIPELINE_ERROR_READ); |
+ message_loop_.RunAllPending(); |
+ } |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PipelineTeardownTest); |
+}; |
+ |
+#define INSTANTIATE_TEARDOWN_TEST(stop_or_error, state) \ |
+ TEST_F(PipelineTeardownTest, stop_or_error##_##state) { \ |
+ RunTest(k##state, k##stop_or_error); \ |
+ } |
+ |
+INSTANTIATE_TEARDOWN_TEST(Stop, Pausing); |
+INSTANTIATE_TEARDOWN_TEST(Stop, Flushing); |
+INSTANTIATE_TEARDOWN_TEST(Stop, Seeking); |
+INSTANTIATE_TEARDOWN_TEST(Stop, Prerolling); |
+INSTANTIATE_TEARDOWN_TEST(Stop, Starting); |
+INSTANTIATE_TEARDOWN_TEST(Stop, Playing); |
+ |
+INSTANTIATE_TEARDOWN_TEST(Error, Pausing); |
+INSTANTIATE_TEARDOWN_TEST(Error, Flushing); |
+INSTANTIATE_TEARDOWN_TEST(Error, Seeking); |
+INSTANTIATE_TEARDOWN_TEST(Error, Prerolling); |
+INSTANTIATE_TEARDOWN_TEST(Error, Starting); |
+INSTANTIATE_TEARDOWN_TEST(Error, Playing); |
+ |
} // namespace media |