blob: 36869e9c6d83dabc1eb2ae7ac115bf25483e3e2e [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/callback.h"
#include "base/stl_util-inl.h"
#include "media/base/pipeline_impl.h"
#include "media/base/media_format.h"
#include "media/base/filters.h"
#include "media/base/factory.h"
#include "media/base/filter_host.h"
#include "media/base/mock_filters.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::StrictMock;
namespace {
// Total bytes of the data source.
const int kTotalBytes = 1024;
// Buffered bytes of the data source.
const int kBufferedBytes = 1024;
} // namespace
namespace media {
// Used for setting expectations on pipeline callbacks. Using a StrictMock
// also lets us test for missing callbacks.
class CallbackHelper {
public:
CallbackHelper() {}
virtual ~CallbackHelper() {}
MOCK_METHOD0(OnStart, void());
MOCK_METHOD0(OnSeek, void());
MOCK_METHOD0(OnStop, void());
MOCK_METHOD0(OnEnded, void());
MOCK_METHOD0(OnError, void());
private:
DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
};
// TODO(scherkus): even though some filters are initialized on separate
// threads these test aren't flaky... why? It's because filters' Initialize()
// is executed on |message_loop_| and the mock filters instantly call
// InitializationComplete(), which keeps the pipeline humming along. If
// either filters don't call InitializationComplete() immediately or filter
// initialization is moved to a separate thread this test will become flaky.
class PipelineImplTest : public ::testing::Test {
public:
PipelineImplTest()
: pipeline_(new PipelineImpl(&message_loop_)),
mocks_(new MockFilterFactory()) {
pipeline_->SetPipelineErrorCallback(NewCallback(
reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnError));
}
virtual ~PipelineImplTest() {
if (!pipeline_->IsRunning()) {
return;
}
// Expect a stop callback if we were started.
EXPECT_CALL(callbacks_, OnStop());
pipeline_->Stop(NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnStop));
message_loop_.RunAllPending();
// Free allocated media formats (if any).
STLDeleteElements(&stream_media_formats_);
// Release the filter factory to workaround a gMock bug.
// See: http://code.google.com/p/googlemock/issues/detail?id=79
mocks_ = NULL;
}
protected:
// Sets up expectations to allow the data source to initialize.
void InitializeDataSource() {
EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
.WillOnce(DoAll(SetTotalBytes(mocks_->data_source(), kTotalBytes),
SetBufferedBytes(mocks_->data_source(), kBufferedBytes),
Invoke(&RunFilterCallback)));
EXPECT_CALL(*mocks_->data_source(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->data_source(), Seek(base::TimeDelta(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->data_source(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
EXPECT_CALL(*mocks_->data_source(), media_format())
.WillOnce(ReturnRef(data_source_media_format_));
}
// Sets up expectations to allow the demuxer to initialize.
typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector;
void InitializeDemuxer(MockDemuxerStreamVector* streams,
const base::TimeDelta& duration) {
EXPECT_CALL(*mocks_->demuxer(),
Initialize(mocks_->data_source(), NotNull()))
.WillOnce(DoAll(SetDuration(mocks_->data_source(), duration),
Invoke(&RunFilterCallback)));
EXPECT_CALL(*mocks_->demuxer(), GetNumberOfStreams())
.WillRepeatedly(Return(streams->size()));
EXPECT_CALL(*mocks_->demuxer(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->demuxer(), Seek(base::TimeDelta(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->demuxer(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
// Configure the demuxer to return the streams.
for (size_t i = 0; i < streams->size(); ++i) {
scoped_refptr<DemuxerStream> stream = (*streams)[i];
EXPECT_CALL(*mocks_->demuxer(), GetStream(i))
.WillRepeatedly(Return(stream));
}
}
// Create a stream with an associated media format.
StrictMock<MockDemuxerStream>* CreateStream(const std::string& mime_type) {
StrictMock<MockDemuxerStream>* stream =
new StrictMock<MockDemuxerStream>();
// Sets the mime type of this stream's media format, which is usually
// checked to determine the type of decoder to create.
MediaFormat* media_format = new MediaFormat();
media_format->SetAsString(MediaFormat::kMimeType, mime_type);
EXPECT_CALL(*stream, media_format())
.WillRepeatedly(ReturnRef(*media_format));
stream_media_formats_.push_back(media_format);
return stream;
}
// Sets up expectations to allow the video decoder to initialize.
void InitializeVideoDecoder(MockDemuxerStream* stream) {
EXPECT_CALL(*mocks_->video_decoder(), Initialize(stream, NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->video_decoder(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->video_decoder(), Seek(base::TimeDelta(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->video_decoder(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
EXPECT_CALL(*mocks_->video_decoder(), media_format())
.WillOnce(ReturnRef(video_decoder_media_format_));
}
// Sets up expectations to allow the audio decoder to initialize.
void InitializeAudioDecoder(MockDemuxerStream* stream) {
EXPECT_CALL(*mocks_->audio_decoder(), Initialize(stream, NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->audio_decoder(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->audio_decoder(), Seek(base::TimeDelta(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->audio_decoder(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
EXPECT_CALL(*mocks_->audio_decoder(), media_format())
.WillOnce(ReturnRef(audio_decoder_media_format_));
}
// Sets up expectations to allow the video renderer to initialize.
void InitializeVideoRenderer() {
EXPECT_CALL(*mocks_->video_renderer(),
Initialize(mocks_->video_decoder(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->video_renderer(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->video_renderer(), Seek(base::TimeDelta(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->video_renderer(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
}
// Sets up expectations to allow the audio renderer to initialize.
void InitializeAudioRenderer() {
EXPECT_CALL(*mocks_->audio_renderer(),
Initialize(mocks_->audio_decoder(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->audio_renderer(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->audio_renderer(), SetVolume(1.0f));
EXPECT_CALL(*mocks_->audio_renderer(), Seek(base::TimeDelta(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->audio_renderer(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
}
// Sets up expectations on the callback and initializes the pipeline. Called
// after tests have set expectations any filters they wish to use.
void InitializePipeline() {
// Expect an initialization callback.
EXPECT_CALL(callbacks_, OnStart());
pipeline_->Start(mocks_, "",
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnStart));
message_loop_.RunAllPending();
}
void CreateAudioStream() {
audio_stream_ = CreateStream("audio/x-foo");
}
void CreateVideoStream() {
video_stream_ = CreateStream("video/x-foo");
}
MockDemuxerStream* audio_stream() {
return audio_stream_;
}
MockDemuxerStream* video_stream() {
return video_stream_;
}
// Fixture members.
StrictMock<CallbackHelper> callbacks_;
MessageLoop message_loop_;
scoped_refptr<PipelineImpl> pipeline_;
scoped_refptr<media::MockFilterFactory> mocks_;
scoped_refptr<StrictMock<MockDemuxerStream> > audio_stream_;
scoped_refptr<StrictMock<MockDemuxerStream> > video_stream_;
MediaFormat data_source_media_format_;
MediaFormat audio_decoder_media_format_;
MediaFormat video_decoder_media_format_;
typedef std::vector<MediaFormat*> MediaFormatVector;
MediaFormatVector stream_media_formats_;
private:
DISALLOW_COPY_AND_ASSIGN(PipelineImplTest);
};
// Test that playback controls methods no-op when the pipeline hasn't been
// started.
TEST_F(PipelineImplTest, NotStarted) {
const base::TimeDelta kZero;
// StrictMock<> will ensure these never get called, and valgrind/purify will
// make sure the callbacks are instantly deleted.
pipeline_->Stop(NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnStop));
pipeline_->Seek(kZero,
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnSeek));
EXPECT_FALSE(pipeline_->IsRunning());
EXPECT_FALSE(pipeline_->IsInitialized());
EXPECT_FALSE(pipeline_->IsRendered(""));
EXPECT_FALSE(pipeline_->IsRendered(AudioDecoder::major_mime_type()));
EXPECT_FALSE(pipeline_->IsRendered(VideoDecoder::major_mime_type()));
// Setting should still work.
EXPECT_EQ(0.0f, pipeline_->GetPlaybackRate());
pipeline_->SetPlaybackRate(-1.0f);
EXPECT_EQ(0.0f, pipeline_->GetPlaybackRate());
pipeline_->SetPlaybackRate(1.0f);
EXPECT_EQ(1.0f, pipeline_->GetPlaybackRate());
// Setting should still work.
EXPECT_EQ(1.0f, pipeline_->GetVolume());
pipeline_->SetVolume(-1.0f);
EXPECT_EQ(1.0f, pipeline_->GetVolume());
pipeline_->SetVolume(0.0f);
EXPECT_EQ(0.0f, pipeline_->GetVolume());
EXPECT_TRUE(kZero == pipeline_->GetCurrentTime());
EXPECT_TRUE(kZero == pipeline_->GetBufferedTime());
EXPECT_TRUE(kZero == pipeline_->GetMediaDuration());
EXPECT_EQ(0, pipeline_->GetBufferedBytes());
EXPECT_EQ(0, pipeline_->GetTotalBytes());
// Should always get set to zero.
size_t width = 1u;
size_t height = 1u;
pipeline_->GetVideoSize(&width, &height);
EXPECT_EQ(0u, width);
EXPECT_EQ(0u, height);
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
}
TEST_F(PipelineImplTest, NeverInitializes) {
EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
.WillOnce(Invoke(&DestroyFilterCallback));
EXPECT_CALL(*mocks_->data_source(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
// This test hangs during initialization by never calling
// InitializationComplete(). StrictMock<> will ensure that the callback is
// never executed.
pipeline_->Start(mocks_, "",
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnStart));
message_loop_.RunAllPending();
EXPECT_FALSE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
// Because our callback will get executed when the test tears down, we'll
// verify that nothing has been called, then set our expectation for the call
// made during tear down.
Mock::VerifyAndClear(&callbacks_);
EXPECT_CALL(callbacks_, OnStart());
}
TEST_F(PipelineImplTest, RequiredFilterMissing) {
EXPECT_CALL(callbacks_, OnError());
mocks_->set_creation_successful(false);
InitializePipeline();
EXPECT_FALSE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_ERROR_REQUIRED_FILTER_MISSING,
pipeline_->GetError());
}
TEST_F(PipelineImplTest, URLNotFound) {
EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
.WillOnce(DoAll(SetError(mocks_->data_source(),
PIPELINE_ERROR_URL_NOT_FOUND),
Invoke(&RunFilterCallback)));
EXPECT_CALL(callbacks_, OnError());
EXPECT_CALL(*mocks_->data_source(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
InitializePipeline();
EXPECT_FALSE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_ERROR_URL_NOT_FOUND, pipeline_->GetError());
}
TEST_F(PipelineImplTest, NoStreams) {
// Manually set these expectations because SetPlaybackRate() is not called if
// we cannot fully initialize the pipeline.
EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->data_source(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
EXPECT_CALL(*mocks_->data_source(), media_format())
.WillOnce(ReturnRef(data_source_media_format_));
EXPECT_CALL(*mocks_->demuxer(), Initialize(mocks_->data_source(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->demuxer(), GetNumberOfStreams())
.WillRepeatedly(Return(0));
EXPECT_CALL(*mocks_->demuxer(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
EXPECT_CALL(callbacks_, OnError());
InitializePipeline();
EXPECT_FALSE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_ERROR_COULD_NOT_RENDER, pipeline_->GetError());
}
TEST_F(PipelineImplTest, AudioStream) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
InitializeDataSource();
InitializeDemuxer(&streams, base::TimeDelta());
InitializeAudioDecoder(audio_stream());
InitializeAudioRenderer();
InitializePipeline();
EXPECT_TRUE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
EXPECT_TRUE(pipeline_->IsRendered(media::mime_type::kMajorTypeAudio));
EXPECT_FALSE(pipeline_->IsRendered(media::mime_type::kMajorTypeVideo));
}
TEST_F(PipelineImplTest, VideoStream) {
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(video_stream());
InitializeDataSource();
InitializeDemuxer(&streams, base::TimeDelta());
InitializeVideoDecoder(video_stream());
InitializeVideoRenderer();
InitializePipeline();
EXPECT_TRUE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
EXPECT_FALSE(pipeline_->IsRendered(media::mime_type::kMajorTypeAudio));
EXPECT_TRUE(pipeline_->IsRendered(media::mime_type::kMajorTypeVideo));
}
TEST_F(PipelineImplTest, AudioVideoStream) {
CreateAudioStream();
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
streams.push_back(video_stream());
InitializeDataSource();
InitializeDemuxer(&streams, base::TimeDelta());
InitializeAudioDecoder(audio_stream());
InitializeAudioRenderer();
InitializeVideoDecoder(video_stream());
InitializeVideoRenderer();
InitializePipeline();
EXPECT_TRUE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
EXPECT_TRUE(pipeline_->IsRendered(media::mime_type::kMajorTypeAudio));
EXPECT_TRUE(pipeline_->IsRendered(media::mime_type::kMajorTypeVideo));
}
TEST_F(PipelineImplTest, Seek) {
CreateAudioStream();
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
streams.push_back(video_stream());
InitializeDataSource();
InitializeDemuxer(&streams, base::TimeDelta::FromSeconds(3000));
InitializeAudioDecoder(audio_stream());
InitializeAudioRenderer();
InitializeVideoDecoder(video_stream());
InitializeVideoRenderer();
// Every filter should receive a call to Seek().
base::TimeDelta expected = base::TimeDelta::FromSeconds(2000);
EXPECT_CALL(*mocks_->data_source(), Seek(expected, NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->demuxer(), Seek(expected, NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->audio_decoder(), Seek(expected, NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->audio_renderer(), Seek(expected, NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->video_decoder(), Seek(expected, NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->video_renderer(), Seek(expected, NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
// We expect a successful seek callback.
EXPECT_CALL(callbacks_, OnSeek());
// Initialize then seek!
InitializePipeline();
pipeline_->Seek(expected,
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnSeek));
// We expect the time to be updated only after the seek has completed.
EXPECT_TRUE(expected != pipeline_->GetCurrentTime());
message_loop_.RunAllPending();
EXPECT_TRUE(expected == pipeline_->GetCurrentTime());
}
TEST_F(PipelineImplTest, SetVolume) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
InitializeDataSource();
InitializeDemuxer(&streams, base::TimeDelta());
InitializeAudioDecoder(audio_stream());
InitializeAudioRenderer();
// The audio renderer should receive a call to SetVolume().
float expected = 0.5f;
EXPECT_CALL(*mocks_->audio_renderer(), SetVolume(expected));
// Initialize then set volume!
InitializePipeline();
pipeline_->SetVolume(expected);
}
TEST_F(PipelineImplTest, Properties) {
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(video_stream());
InitializeDataSource();
const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
InitializeDemuxer(&streams, kDuration);
InitializeVideoDecoder(video_stream());
InitializeVideoRenderer();
InitializePipeline();
EXPECT_TRUE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
EXPECT_EQ(kDuration.ToInternalValue(),
pipeline_->GetMediaDuration().ToInternalValue());
EXPECT_EQ(kTotalBytes, pipeline_->GetTotalBytes());
EXPECT_EQ(kBufferedBytes, pipeline_->GetBufferedBytes());
// Because kTotalBytes and kBufferedBytes are equal to each other,
// the entire video should be buffered.
EXPECT_EQ(kDuration.ToInternalValue(),
pipeline_->GetBufferedTime().ToInternalValue());
}
TEST_F(PipelineImplTest, GetBufferedTime) {
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(video_stream());
InitializeDataSource();
const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
InitializeDemuxer(&streams, kDuration);
InitializeVideoDecoder(video_stream());
InitializeVideoRenderer();
InitializePipeline();
EXPECT_TRUE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
// TODO(vrk): The following mini-test cases are order-dependent, and should
// probably be separated into independent test cases.
// Buffered time is 0 if no bytes are buffered.
pipeline_->SetBufferedBytes(0);
EXPECT_EQ(0, pipeline_->GetBufferedTime().ToInternalValue());
// We should return buffered_time_ if it is set and valid.
const base::TimeDelta buffered = base::TimeDelta::FromSeconds(10);
pipeline_->SetBufferedTime(buffered);
EXPECT_EQ(buffered.ToInternalValue(),
pipeline_->GetBufferedTime().ToInternalValue());
// If media has been fully received, we should return the duration
// of the media.
pipeline_->SetBufferedBytes(kTotalBytes);
EXPECT_EQ(kDuration.ToInternalValue(),
pipeline_->GetBufferedTime().ToInternalValue());
// If media is loaded, we should return duration of media.
pipeline_->SetLoaded(true);
EXPECT_EQ(kDuration.ToInternalValue(),
pipeline_->GetBufferedTime().ToInternalValue());
}
TEST_F(PipelineImplTest, DisableAudioRenderer) {
CreateAudioStream();
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
streams.push_back(video_stream());
pipeline_->SetPipelineEndedCallback(
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnEnded));
InitializeDataSource();
InitializeDemuxer(&streams, base::TimeDelta());
InitializeAudioDecoder(audio_stream());
InitializeAudioRenderer();
InitializeVideoDecoder(video_stream());
InitializeVideoRenderer();
InitializePipeline();
EXPECT_TRUE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_OK, pipeline_->GetError());
EXPECT_TRUE(pipeline_->IsRendered(mime_type::kMajorTypeAudio));
EXPECT_TRUE(pipeline_->IsRendered(mime_type::kMajorTypeVideo));
EXPECT_CALL(*mocks_->audio_renderer(), SetPlaybackRate(1.0f))
.WillOnce(DisableAudioRenderer(mocks_->audio_renderer()));
EXPECT_CALL(*mocks_->data_source(),
OnAudioRendererDisabled());
EXPECT_CALL(*mocks_->demuxer(),
OnAudioRendererDisabled());
EXPECT_CALL(*mocks_->audio_decoder(),
OnAudioRendererDisabled());
EXPECT_CALL(*mocks_->audio_renderer(),
OnAudioRendererDisabled());
EXPECT_CALL(*mocks_->video_decoder(),
OnAudioRendererDisabled());
EXPECT_CALL(*mocks_->video_renderer(),
OnAudioRendererDisabled());
mocks_->audio_renderer()->SetPlaybackRate(1.0f);
// Verify that ended event is fired when video ends.
EXPECT_CALL(*mocks_->audio_renderer(), HasEnded())
.WillOnce(Return(false));
EXPECT_CALL(*mocks_->video_renderer(), HasEnded())
.WillOnce(Return(true));
EXPECT_CALL(callbacks_, OnEnded());
FilterHost* host = pipeline_;
host->NotifyEnded();
}
TEST_F(PipelineImplTest, EndedCallback) {
CreateAudioStream();
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
streams.push_back(video_stream());
// Set our ended callback.
pipeline_->SetPipelineEndedCallback(
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnEnded));
InitializeDataSource();
InitializeDemuxer(&streams, base::TimeDelta());
InitializeAudioDecoder(audio_stream());
InitializeAudioRenderer();
InitializeVideoDecoder(video_stream());
InitializeVideoRenderer();
InitializePipeline();
// For convenience to simulate filters calling the methods.
FilterHost* host = pipeline_;
// Due to short circuit evaluation we only need to test a subset of cases.
InSequence s;
EXPECT_CALL(*mocks_->audio_renderer(), HasEnded())
.WillOnce(Return(false));
host->NotifyEnded();
EXPECT_CALL(*mocks_->audio_renderer(), HasEnded())
.WillOnce(Return(true));
EXPECT_CALL(*mocks_->video_renderer(), HasEnded())
.WillOnce(Return(false));
host->NotifyEnded();
EXPECT_CALL(*mocks_->audio_renderer(), HasEnded())
.WillOnce(Return(true));
EXPECT_CALL(*mocks_->video_renderer(), HasEnded())
.WillOnce(Return(true));
EXPECT_CALL(callbacks_, OnEnded());
host->NotifyEnded();
}
} // namespace media