diff options
Diffstat (limited to 'media/base')
-rw-r--r-- | media/base/mock_ffmpeg.cc | 41 | ||||
-rw-r--r-- | media/base/mock_ffmpeg.h | 8 | ||||
-rw-r--r-- | media/base/mock_filters.h | 51 | ||||
-rw-r--r-- | media/base/mock_media_filters.h | 656 | ||||
-rw-r--r-- | media/base/pipeline_impl_unittest.cc | 248 | ||||
-rw-r--r-- | media/base/video_frame_impl_unittest.cc | 50 |
6 files changed, 298 insertions, 756 deletions
diff --git a/media/base/mock_ffmpeg.cc b/media/base/mock_ffmpeg.cc index 054f996..562aff2 100644 --- a/media/base/mock_ffmpeg.cc +++ b/media/base/mock_ffmpeg.cc @@ -7,12 +7,36 @@ #include "base/logging.h" #include "media/filters/ffmpeg_common.h" +using ::testing::_; +using ::testing::AtMost; +using ::testing::DoAll; +using ::testing::Return; +using ::testing::SaveArg; + namespace media { MockFFmpeg* MockFFmpeg::instance_ = NULL; +URLProtocol* MockFFmpeg::protocol_ = NULL; MockFFmpeg::MockFFmpeg() : outstanding_packets_(0) { + // If we haven't assigned our static copy of URLProtocol, set up expectations + // to catch the URLProtocol registered when the singleton instance of + // FFmpegGlue is created. + // + // TODO(scherkus): this feels gross and I need to think of a way to better + // inject/mock singletons. + if (!protocol_) { + EXPECT_CALL(*this, AVCodecInit()) + .Times(AtMost(1)) + .WillOnce(Return()); + EXPECT_CALL(*this, AVRegisterProtocol(_)) + .Times(AtMost(1)) + .WillOnce(DoAll(SaveArg<0>(&protocol_), Return(0))); + EXPECT_CALL(*this, AVRegisterAll()) + .Times(AtMost(1)) + .WillOnce(Return()); + } } MockFFmpeg::~MockFFmpeg() { @@ -40,6 +64,11 @@ MockFFmpeg* MockFFmpeg::get() { } // static +URLProtocol* MockFFmpeg::protocol() { + return protocol_; +} + +// static void MockFFmpeg::DestructPacket(AVPacket* packet) { delete [] packet->data; packet->data = NULL; @@ -49,6 +78,18 @@ void MockFFmpeg::DestructPacket(AVPacket* packet) { // FFmpeg stubs that delegate to the FFmpegMock instance. extern "C" { +void avcodec_init() { + media::MockFFmpeg::get()->AVCodecInit(); +} + +int av_register_protocol(URLProtocol* protocol) { + return media::MockFFmpeg::get()->AVRegisterProtocol(protocol); +} + +void av_register_all() { + media::MockFFmpeg::get()->AVRegisterAll(); +} + AVCodec* avcodec_find_decoder(enum CodecID id) { return media::MockFFmpeg::get()->AVCodecFindDecoder(id); } diff --git a/media/base/mock_ffmpeg.h b/media/base/mock_ffmpeg.h index 3afdef9..4ba38fc 100644 --- a/media/base/mock_ffmpeg.h +++ b/media/base/mock_ffmpeg.h @@ -16,6 +16,10 @@ class MockFFmpeg { MockFFmpeg(); virtual ~MockFFmpeg(); + MOCK_METHOD0(AVCodecInit, void()); + MOCK_METHOD1(AVRegisterProtocol, int(URLProtocol* protocol)); + MOCK_METHOD0(AVRegisterAll, void()); + MOCK_METHOD1(AVCodecFindDecoder, AVCodec*(enum CodecID id)); MOCK_METHOD2(AVCodecOpen, int(AVCodecContext* avctx, AVCodec* codec)); MOCK_METHOD1(AVCodecClose, int(AVCodecContext* avctx)); @@ -51,6 +55,9 @@ class MockFFmpeg { static void set(MockFFmpeg* instance); static MockFFmpeg* get(); + // Returns the URLProtocol registered by the FFmpegGlue singleton. + static URLProtocol* protocol(); + // AVPacket destructor for packets allocated by av_new_packet(). static void DestructPacket(AVPacket* packet); @@ -60,6 +67,7 @@ class MockFFmpeg { private: static MockFFmpeg* instance_; + static URLProtocol* protocol_; // Tracks the number of packets allocated by calls to av_read_frame() and // av_free_packet(). We crash the unit test if this is not zero at time of diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h index bef46a7..bffba6f 100644 --- a/media/base/mock_filters.h +++ b/media/base/mock_filters.h @@ -19,6 +19,27 @@ namespace media { +// Use this template to test for object destruction by setting expectations on +// the method OnDestroy(). +// +// TODO(scherkus): not sure about the naming... perhaps contribute this back +// to gmock itself! +template<class MockClass> +class Destroyable : public MockClass { + public: + Destroyable() {} + + MOCK_METHOD0(OnDestroy, void()); + + protected: + virtual ~Destroyable() { + OnDestroy(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(Destroyable); +}; + class MockDataSource : public DataSource { public: MockDataSource() {} @@ -71,6 +92,12 @@ class MockDemuxerStream : public DemuxerStream { public: MockDemuxerStream() {} + // Sets the mime type of this object's media format, which is usually checked + // to determine the type of decoder to create. + explicit MockDemuxerStream(const std::string& mime_type) { + media_format_.SetAsString(MediaFormat::kMimeType, mime_type); + } + // DemuxerStream implementation. const MediaFormat& media_format() { return media_format_; } MOCK_METHOD1(Read, void(Callback1<Buffer*>::Type* read_callback)); @@ -175,7 +202,8 @@ class MockAudioRenderer : public AudioRenderer { class MockFilterFactory : public FilterFactory { public: MockFilterFactory() - : data_source_(new MockDataSource()), + : creation_successful_(true), + data_source_(new MockDataSource()), demuxer_(new MockDemuxer()), video_decoder_(new MockVideoDecoder()), audio_decoder_(new MockAudioDecoder()), @@ -185,6 +213,11 @@ class MockFilterFactory : public FilterFactory { virtual ~MockFilterFactory() {} + // Controls whether the Create() method is successful or not. + void set_creation_successful(bool creation_successful) { + creation_successful_ = creation_successful; + } + // Mock accessors. MockDataSource* data_source() const { return data_source_; } MockDemuxer* demuxer() const { return demuxer_; } @@ -195,6 +228,10 @@ class MockFilterFactory : public FilterFactory { protected: MediaFilter* Create(FilterType filter_type, const MediaFormat& media_format) { + if (!creation_successful_) { + return NULL; + } + switch (filter_type) { case FILTER_DATA_SOURCE: return data_source_; @@ -215,6 +252,7 @@ class MockFilterFactory : public FilterFactory { } private: + bool creation_successful_; scoped_refptr<MockDataSource> data_source_; scoped_refptr<MockDemuxer> demuxer_; scoped_refptr<MockVideoDecoder> video_decoder_; @@ -225,6 +263,17 @@ class MockFilterFactory : public FilterFactory { DISALLOW_COPY_AND_ASSIGN(MockFilterFactory); }; +// Helper gmock action that calls InitializationComplete() on behalf of the +// provided filter. +ACTION_P(InitializationComplete, filter) { + filter->host()->InitializationComplete(); +} + +// Helper gmock action that calls Error() on behalf of the provided filter. +ACTION_P2(Error, filter, error) { + filter->host()->Error(error); +} + } // namespace media #endif // MEDIA_BASE_MOCK_FILTERS_H_ diff --git a/media/base/mock_media_filters.h b/media/base/mock_media_filters.h deleted file mode 100644 index 698815f..0000000 --- a/media/base/mock_media_filters.h +++ /dev/null @@ -1,656 +0,0 @@ -// Copyright (c) 2009 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. -// -// TODO(ajwong): This whole file is deprecated in favor or gmock style mocks. -// The deprecated classes have been moved into the old_mocks to avoid colliding -// with the newer mock classes. Once all the unittests have been migrated, this -// should be deleted. - -#ifndef MEDIA_BASE_MOCK_MEDIA_FILTERS_H_ -#define MEDIA_BASE_MOCK_MEDIA_FILTERS_H_ - -#include <string> - -#include "base/scoped_ptr.h" -#include "base/waitable_event.h" -#include "media/base/buffers.h" -#include "media/base/factory.h" -#include "media/base/filter_host.h" -#include "media/base/filters.h" -#include "media/base/media_format.h" -#include "media/base/pipeline.h" -#include "media/base/video_frame_impl.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace media { - -namespace old_mocks { - -// Behaviors for MockDataSource filter. -enum MockDataSourceBehavior { - MOCK_DATA_SOURCE_NORMAL_INIT, - MOCK_DATA_SOURCE_NEVER_INIT, - MOCK_DATA_SOURCE_URL_ERROR_IN_INIT, - MOCK_DATA_SOURCE_INIT_RETURN_FALSE, -}; - - -// This class is used by all of the mock filters to change the configuration -// of the desired pipeline. The test using this must ensure that the lifetime -// of the object is at least as long as the lifetime of the filters, as this -// is typically allocated on the stack. -struct MockFilterConfig { - MockFilterConfig() - : create_filter(true), - data_source_behavior(MOCK_DATA_SOURCE_NORMAL_INIT), - data_source_value('!'), - has_video(true), - video_width(1280u), - video_height(720u), - video_surface_format(VideoSurface::YV12), - has_audio(true), - compressed_audio_mime_type(mime_type::kAACAudio), - uncompressed_audio_mime_type(mime_type::kUncompressedAudio), - compressed_video_mime_type(mime_type::kH264AnnexB), - uncompressed_video_mime_type(mime_type::kUncompressedVideo), - frame_duration(base::TimeDelta::FromMicroseconds(33333)), - media_duration(base::TimeDelta::FromSeconds(5)), - media_total_bytes(media_duration.InMilliseconds() * 250) { - } - - bool create_filter; - MockDataSourceBehavior data_source_behavior; - char data_source_value; - bool has_video; - size_t video_width; - size_t video_height; - VideoSurface::Format video_surface_format; - bool has_audio; - std::string compressed_audio_mime_type; - std::string uncompressed_audio_mime_type; - std::string compressed_video_mime_type; - std::string uncompressed_video_mime_type; - base::TimeDelta frame_duration; - base::TimeDelta media_duration; - int64 media_total_bytes; -}; - - -class MockDataSource : public DataSource { - public: - explicit MockDataSource(const MockFilterConfig* config) - : config_(config), - position_(0), - deleted_(NULL) { - } - - MockDataSource(const MockFilterConfig* config, bool* deleted) - : config_(config), - position_(0), - deleted_(deleted) { - EXPECT_TRUE(deleted); - EXPECT_FALSE(*deleted); - } - - // Implementation of MediaFilter. - virtual void Stop() {} - - virtual void Seek(base::TimeDelta time) { - seek_time_ = time; - } - - // Implementation of DataSource. - virtual bool Initialize(const std::string& url) { - media_format_.SetAsString(MediaFormat::kMimeType, - mime_type::kApplicationOctetStream); - media_format_.SetAsString(MediaFormat::kURL, url); - host()->SetTotalBytes(config_->media_total_bytes); - switch (config_->data_source_behavior) { - case MOCK_DATA_SOURCE_NORMAL_INIT: - host()->InitializationComplete(); - return true; - case MOCK_DATA_SOURCE_NEVER_INIT: - return true; - case MOCK_DATA_SOURCE_URL_ERROR_IN_INIT: - host()->Error(PIPELINE_ERROR_URL_NOT_FOUND); - return false; - case MOCK_DATA_SOURCE_INIT_RETURN_FALSE: - return false; - default: - NOTREACHED(); - return false; - } - } - - virtual const MediaFormat& media_format() { - return media_format_; - } - - virtual size_t Read(uint8* data, size_t size) { - size_t read = static_cast<size_t>(config_->media_total_bytes - position_); - if (size < read) { - read = size; - } - memset(data, config_->data_source_value, read); - return read; - } - - virtual bool GetPosition(int64* position_out) { - *position_out = position_; - return true; - } - - virtual bool SetPosition(int64 position) { - if (position < 0u || position > config_->media_total_bytes) { - return false; - } - position_ = position; - return true; - } - - virtual bool GetSize(int64* size_out) { - if (config_->media_total_bytes >= 0) { - *size_out = config_->media_total_bytes; - return true; - } - return false; - } - - virtual bool IsSeekable() { - return true; - } - - // Mock accessors. - int64 position() const { return position_; } - const base::TimeDelta& seek_time() const { return seek_time_; } - - private: - virtual ~MockDataSource() { - if (deleted_) { - *deleted_ = true; - } - } - - const MockFilterConfig* config_; - int64 position_; - MediaFormat media_format_; - base::TimeDelta seek_time_; - - // Set to true inside the destructor. Used in FFmpegGlue unit tests for - // testing proper reference counting. - bool* deleted_; - - DISALLOW_COPY_AND_ASSIGN(MockDataSource); -}; - - -class MockDemuxerStream : public DemuxerStream { - public: - MockDemuxerStream(const MockFilterConfig* config, bool is_audio) { - if (is_audio) { - media_format_.SetAsString(MediaFormat::kMimeType, - config->compressed_audio_mime_type); - } else { - media_format_.SetAsString(MediaFormat::kMimeType, - config->compressed_video_mime_type); - media_format_.SetAsInteger(MediaFormat::kWidth, config->video_width); - media_format_.SetAsInteger(MediaFormat::kHeight, config->video_height); - } - } - - // Implementation of DemuxerStream. - virtual const MediaFormat& media_format() { - return media_format_; - } - - virtual void Read(Callback1<Buffer*>::Type* read_callback) { - NOTREACHED(); // TODO(ralphl): fix me!! - } - - private: - virtual ~MockDemuxerStream() {} - - MediaFormat media_format_; - - DISALLOW_COPY_AND_ASSIGN(MockDemuxerStream); -}; - - -class MockDemuxer : public Demuxer { - public: - explicit MockDemuxer(const MockFilterConfig* config) - : config_(config), - mock_audio_stream_(new MockDemuxerStream(config, true)), - mock_video_stream_(new MockDemuxerStream(config, false)) { - } - - // Implementation of MediaFilter. - virtual void Stop() {} - - virtual void Seek(base::TimeDelta time) { - seek_time_ = time; - } - - // Implementation of Demuxer. - virtual bool Initialize(DataSource* data_source) { - host()->InitializationComplete(); - return true; - } - - virtual size_t GetNumberOfStreams() { - size_t num_streams = 0; - if (config_->has_audio) { - ++num_streams; - } - if (config_->has_video) { - ++num_streams; - } - return num_streams; - } - - virtual scoped_refptr<DemuxerStream> GetStream(int stream_id) { - switch (stream_id) { - case 0: - if (config_->has_audio) { - return mock_audio_stream_; - } else if (config_->has_video) { - return mock_video_stream_; - } - break; - case 1: - if (config_->has_audio && config_->has_video) { - return mock_video_stream_; - } - break; - } - ADD_FAILURE(); - return NULL; - } - - // Mock accessors. - const base::TimeDelta& seek_time() const { return seek_time_; } - - private: - virtual ~MockDemuxer() {} - - const MockFilterConfig* config_; - scoped_refptr<DemuxerStream> mock_audio_stream_; - scoped_refptr<DemuxerStream> mock_video_stream_; - base::TimeDelta seek_time_; - - DISALLOW_COPY_AND_ASSIGN(MockDemuxer); -}; - - -class MockAudioDecoder : public AudioDecoder { - public: - explicit MockAudioDecoder(const MockFilterConfig* config) { - media_format_.SetAsString(MediaFormat::kMimeType, - config->uncompressed_audio_mime_type); - } - - // Implementation of MediaFilter. - virtual void Stop() {} - - virtual void Seek(base::TimeDelta time) { - seek_time_ = time; - } - - // Implementation of AudioDecoder. - virtual bool Initialize(DemuxerStream* stream) { - host()->InitializationComplete(); - return true; - } - - virtual const MediaFormat& media_format() { - return media_format_; - } - - virtual void Read(Callback1<Buffer*>::Type* callback) { - // TODO(ralphl): implement mock read. - NOTREACHED(); - } - - // Mock accessors. - const base::TimeDelta& seek_time() const { return seek_time_; } - - private: - virtual ~MockAudioDecoder() {} - - MediaFormat media_format_; - base::TimeDelta seek_time_; - - DISALLOW_COPY_AND_ASSIGN(MockAudioDecoder); -}; - - -class MockAudioRenderer : public AudioRenderer { - public: - explicit MockAudioRenderer(const MockFilterConfig* config) {} - - // Implementation of MediaFilter. - virtual void Stop() {} - - virtual void Seek(base::TimeDelta time) { - seek_time_ = time; - } - - // Implementation of AudioRenderer. - virtual bool Initialize(AudioDecoder* decoder) { - host()->InitializationComplete(); - return true; - } - - virtual void SetVolume(float volume) {} - - // Mock accessors. - const base::TimeDelta& seek_time() const { return seek_time_; } - - private: - virtual ~MockAudioRenderer() {} - - base::TimeDelta seek_time_; - - DISALLOW_COPY_AND_ASSIGN(MockAudioRenderer); -}; - - -class MockVideoDecoder : public VideoDecoder { - public: - // Helper function that initializes a YV12 frame with white and black scan - // lines based on the |white_to_black| parameter. If 0, then the entire - // frame will be black, if 1 then the entire frame will be white. - static void InitializeYV12Frame(VideoFrame* frame, double white_to_black) { - VideoSurface surface; - if (!frame->Lock(&surface)) { - ADD_FAILURE(); - } else { - EXPECT_EQ(surface.format, VideoSurface::YV12); - size_t first_black_row = static_cast<size_t>(surface.height * - white_to_black); - uint8* y_plane = surface.data[VideoSurface::kYPlane]; - for (size_t row = 0; row < surface.height; ++row) { - int color = (row < first_black_row) ? 0xFF : 0x00; - memset(y_plane, color, surface.width); - y_plane += surface.strides[VideoSurface::kYPlane]; - } - uint8* u_plane = surface.data[VideoSurface::kUPlane]; - uint8* v_plane = surface.data[VideoSurface::kVPlane]; - for (size_t row = 0; row < surface.height; row += 2) { - memset(u_plane, 0x80, surface.width / 2); - memset(v_plane, 0x80, surface.width / 2); - u_plane += surface.strides[VideoSurface::kUPlane]; - v_plane += surface.strides[VideoSurface::kVPlane]; - } - frame->Unlock(); - } - } - - explicit MockVideoDecoder(const MockFilterConfig* config) - : config_(config) { - media_format_.SetAsString(MediaFormat::kMimeType, - config->uncompressed_video_mime_type); - media_format_.SetAsInteger(MediaFormat::kWidth, config->video_width); - media_format_.SetAsInteger(MediaFormat::kHeight, config->video_height); - } - - // Implementation of MediaFilter. - virtual void Stop() {} - - virtual void Seek(base::TimeDelta time) { - seek_time_ = time; - } - - // Implementation of VideoDecoder. - virtual bool Initialize(DemuxerStream* stream) { - host()->InitializationComplete(); - return true; - } - - virtual const MediaFormat& media_format() { - return media_format_; - } - - virtual void Read(Callback1<VideoFrame*>::Type* callback) { - DoRead(callback); - } - - // Mock accessors. - const base::TimeDelta& seek_time() const { return seek_time_; } - - private: - virtual ~MockVideoDecoder() {} - - void DoRead(Callback1<VideoFrame*>::Type* callback) { - scoped_ptr<Callback1<VideoFrame*>::Type> scoped_callback(callback); - if (mock_frame_time_ < config_->media_duration) { - // TODO(ralphl): Mock video decoder only works with YV12. Implement other - // formats as needed. - EXPECT_EQ(config_->video_surface_format, VideoSurface::YV12); - scoped_refptr<VideoFrame> frame; - VideoFrameImpl::CreateFrame(config_->video_surface_format, - config_->video_width, - config_->video_height, - mock_frame_time_, - config_->frame_duration, - &frame); - if (!frame) { - host()->Error(PIPELINE_ERROR_OUT_OF_MEMORY); - ADD_FAILURE(); - } else { - mock_frame_time_ += config_->frame_duration; - if (mock_frame_time_ >= config_->media_duration) { - VideoFrameImpl::CreateEmptyFrame(&frame); - } else { - InitializeYV12Frame(frame, (mock_frame_time_.InSecondsF() / - config_->media_duration.InSecondsF())); - } - callback->Run(frame); - } - } - } - - MediaFormat media_format_; - base::TimeDelta mock_frame_time_; - base::TimeDelta seek_time_; - const MockFilterConfig* config_; - - DISALLOW_COPY_AND_ASSIGN(MockVideoDecoder); -}; - - -class MockVideoRenderer : public VideoRenderer { - public: - explicit MockVideoRenderer(const MockFilterConfig* config) - : config_(config) { - } - - // Implementation of MediaFilter. - virtual void Stop() {} - - virtual void Seek(base::TimeDelta time) { - seek_time_ = time; - } - - // Implementation of VideoRenderer. - virtual bool Initialize(VideoDecoder* decoder) { - host()->SetVideoSize(config_->video_width, config_->video_height); - host()->InitializationComplete(); - return true; - } - - // Mock accessors. - const base::TimeDelta& seek_time() const { return seek_time_; } - - private: - virtual ~MockVideoRenderer() {} - - base::TimeDelta seek_time_; - const MockFilterConfig* config_; - - DISALLOW_COPY_AND_ASSIGN(MockVideoRenderer); -}; - - -// FilterFactory capable of creating each mock filter type. Only one instance -// of each filter type can exist at any time. Filters can be inspected for -// expectations using the accessors, which may return NULL if the filter was -// never created (i.e., streams containing no video). -class MockFilterFactory : public FilterFactory { - public: - explicit MockFilterFactory(const MockFilterConfig* config) - : config_(config) { - } - - // Mock accessors. - MockDataSource* data_source() const { return data_source_; } - MockDemuxer* demuxer() const { return demuxer_; } - MockAudioDecoder* audio_decoder() const { return audio_decoder_; } - MockVideoDecoder* video_decoder() const { return video_decoder_; } - MockAudioRenderer* audio_renderer() const { return audio_renderer_; } - MockVideoRenderer* video_renderer() const { return video_renderer_; } - - protected: - MediaFilter* Create(FilterType filter_type, const MediaFormat& media_format) { - if (!config_->create_filter) - return NULL; - - switch (filter_type) { - case FILTER_DATA_SOURCE: - DCHECK(!data_source_); - data_source_ = new MockDataSource(config_); - return data_source_; - - case FILTER_DEMUXER: - DCHECK(!demuxer_); - demuxer_ = new MockDemuxer(config_); - return demuxer_; - - case FILTER_AUDIO_DECODER: - DCHECK(!audio_decoder_); - audio_decoder_ = new MockAudioDecoder(config_); - return audio_decoder_; - - case FILTER_VIDEO_DECODER: - DCHECK(!video_decoder_); - video_decoder_ = new MockVideoDecoder(config_); - return video_decoder_; - - case FILTER_AUDIO_RENDERER: - DCHECK(!audio_renderer_); - audio_renderer_ = new MockAudioRenderer(config_); - return audio_renderer_; - - case FILTER_VIDEO_RENDERER: - DCHECK(!video_renderer_); - video_renderer_ = new MockVideoRenderer(config_); - return video_renderer_; - - default: - NOTREACHED(); - } - return NULL; - } - - private: - const MockFilterConfig* config_; - scoped_refptr<MockDataSource> data_source_; - scoped_refptr<MockDemuxer> demuxer_; - scoped_refptr<MockAudioDecoder> audio_decoder_; - scoped_refptr<MockVideoDecoder> video_decoder_; - scoped_refptr<MockAudioRenderer> audio_renderer_; - scoped_refptr<MockVideoRenderer> video_renderer_; - - DISALLOW_COPY_AND_ASSIGN(MockFilterFactory); -}; - -// A simple class that waits for a pipeline to be started and checks some -// basic initialization values. The Start() method will not return until -// either a pre-determined amount of time has passed or the pipeline calls the -// InitCallback() callback. A typical use would be: -// Pipeline p; -// FilterFactoryCollection f; -// f->AddFactory(a); -// f->AddFactory(b); -// ... -// InitializationHelper h; -// h.Start(&p, f, uri); -// -// If the test is expecting to produce an error use would be: -// h.Start(&p, f, uri, PIPELINE_ERROR_REQUIRED_FILTER_MISSING) -// -// If the test expects the pipeline to hang during initialization (a filter -// never calls FilterHost::InitializationComplete()) then the use would be: -// h.Start(&p, f, uri, PIPELINE_OK, true); -// -// TODO(scherkus): Keep refactoring tests until we can remove this entirely. -class InitializationHelper { - public: - InitializationHelper() - : event_(true, false), - callback_success_status_(false), - waiting_for_callback_(false) {} - - // If callback has been called, then returns the boolean passed by the - // pipeline to the callback. - bool callback_success_status() { return callback_success_status_; } - - // Returns true if Start has been called, but the pipeline has not yet - // called the initialization complete callback. - bool waiting_for_callback() { return waiting_for_callback_; } - - // Starts the pipeline, providing an initialization callback that points - // to this object. - void Start(Pipeline* pipeline, - FilterFactory* filter_factory, - const std::string& uri, - PipelineError expect_error = PIPELINE_OK, - bool expect_hang = false) { - // For tests that we expect to hang in initialization, we want to - // wait a short time. If a hang is not expected, then wait long enough - // to make sure that the filters have time to initalize. 1/2 second if - // we expect to hang, and 3 seconds if we expect success. - base::TimeDelta max_wait = base::TimeDelta::FromMilliseconds(expect_hang ? - 500 : 3000); - EXPECT_FALSE(waiting_for_callback_); - waiting_for_callback_ = true; - callback_success_status_ = false; - event_.Reset(); - pipeline->Start(filter_factory, uri, - NewCallback(this, &InitializationHelper::InitCallback)); - bool signaled = event_.TimedWait(max_wait); - if (expect_hang) { - EXPECT_FALSE(signaled); - EXPECT_FALSE(pipeline->IsInitialized()); - EXPECT_TRUE(waiting_for_callback_); - } else { - EXPECT_TRUE(signaled); - EXPECT_FALSE(waiting_for_callback_); - EXPECT_EQ(pipeline->GetError(), expect_error); - EXPECT_EQ(callback_success_status_, (expect_error == PIPELINE_OK)); - EXPECT_EQ(pipeline->IsInitialized(), (expect_error == PIPELINE_OK)); - } - } - - private: - void InitCallback(bool success) { - EXPECT_TRUE(waiting_for_callback_); - EXPECT_FALSE(event_.IsSignaled()); - waiting_for_callback_ = false; - callback_success_status_ = success; - event_.Signal(); - } - - base::WaitableEvent event_; - bool callback_success_status_; - bool waiting_for_callback_; - - DISALLOW_COPY_AND_ASSIGN(InitializationHelper); -}; - -} // namespace old_mocks - -} // namespace media - -#endif // MEDIA_BASE_MOCK_MEDIA_FILTERS_H_ diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc index 56aa246..4183126 100644 --- a/media/base/pipeline_impl_unittest.cc +++ b/media/base/pipeline_impl_unittest.cc @@ -10,34 +10,38 @@ #include "media/base/filters.h" #include "media/base/factory.h" #include "media/base/filter_host.h" -#include "media/base/mock_media_filters.h" +#include "media/base/mock_filters.h" #include "testing/gtest/include/gtest/gtest.h" -namespace { +using ::testing::DoAll; +using ::testing::Return; +using ::testing::StrictMock; -class PipelineImplTest : public testing::Test { - protected: +namespace media { + +typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector; + +class PipelineImplTest : public ::testing::Test { + public: PipelineImplTest() - : initialize_result_(false), + : mocks_(new MockFilterFactory()), + initialize_result_(false), seek_result_(false), initialize_event_(false, false), seek_event_(false, false) { } - virtual ~PipelineImplTest() {} - - virtual void TearDown() { + virtual ~PipelineImplTest() { // Force the pipeline to shut down its thread. pipeline_.Stop(); } + protected: // Called by tests after they have finished setting up MockFilterConfig. // Initializes the pipeline and returns true if the initialization callback // was executed, false otherwise. bool InitializeAndWait() { - DCHECK(!filters_); - filters_ = new media::old_mocks::MockFilterFactory(&config_); - pipeline_.Start(filters_, "", + pipeline_.Start(mocks_, "", NewCallback(this, &PipelineImplTest::OnInitialize)); return initialize_event_.TimedWait(base::TimeDelta::FromMilliseconds(500)); } @@ -49,10 +53,66 @@ class PipelineImplTest : public testing::Test { return seek_event_.TimedWait(base::TimeDelta::FromMilliseconds(500)); } + // Sets up expectations to allow the data source to initialize. + void InitializeDataSource() { + EXPECT_CALL(*mocks_->data_source(), Initialize("")) + .WillOnce(DoAll(InitializationComplete(mocks_->data_source()), + Return(true))); + EXPECT_CALL(*mocks_->data_source(), Stop()); + } + + // Sets up expectations to allow the demuxer to initialize. + void InitializeDemuxer(MockDemuxerStreamVector* streams) { + EXPECT_CALL(*mocks_->demuxer(), Initialize(mocks_->data_source())) + .WillOnce(DoAll(InitializationComplete(mocks_->demuxer()), + Return(true))); + EXPECT_CALL(*mocks_->demuxer(), GetNumberOfStreams()) + .WillRepeatedly(Return(streams->size())); + EXPECT_CALL(*mocks_->demuxer(), Stop()); + + // 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)); + } + } + + // Sets up expectations to allow the video decoder to initialize. + void InitializeVideoDecoder(MockDemuxerStream* stream) { + EXPECT_CALL(*mocks_->video_decoder(), Initialize(stream)) + .WillOnce(DoAll(InitializationComplete(mocks_->video_decoder()), + Return(true))); + EXPECT_CALL(*mocks_->video_decoder(), Stop()); + } + + // Sets up expectations to allow the audio decoder to initialize. + void InitializeAudioDecoder(MockDemuxerStream* stream) { + EXPECT_CALL(*mocks_->audio_decoder(), Initialize(stream)) + .WillOnce(DoAll(InitializationComplete(mocks_->audio_decoder()), + Return(true))); + EXPECT_CALL(*mocks_->audio_decoder(), Stop()); + } + + // Sets up expectations to allow the video renderer to initialize. + void InitializeVideoRenderer() { + EXPECT_CALL(*mocks_->video_renderer(), Initialize(mocks_->video_decoder())) + .WillOnce(DoAll(InitializationComplete(mocks_->video_renderer()), + Return(true))); + EXPECT_CALL(*mocks_->video_renderer(), Stop()); + } + + // Sets up expectations to allow the audio renderer to initialize. + void InitializeAudioRenderer() { + EXPECT_CALL(*mocks_->audio_renderer(), Initialize(mocks_->audio_decoder())) + .WillOnce(DoAll(InitializationComplete(mocks_->audio_renderer()), + Return(true))); + EXPECT_CALL(*mocks_->audio_renderer(), Stop()); + } + // Fixture members. media::PipelineImpl pipeline_; - scoped_refptr<media::old_mocks::MockFilterFactory> filters_; - media::old_mocks::MockFilterConfig config_; + scoped_refptr<media::MockFilterFactory> mocks_; bool initialize_result_; bool seek_result_; @@ -75,7 +135,9 @@ class PipelineImplTest : public testing::Test { }; TEST_F(PipelineImplTest, NeverInitializes) { - config_.data_source_behavior = media::old_mocks::MOCK_DATA_SOURCE_NEVER_INIT; + EXPECT_CALL(*mocks_->data_source(), Initialize("")) + .WillOnce(Return(true)); + EXPECT_CALL(*mocks_->data_source(), Stop()); // This test hangs during initialization by never calling // InitializationComplete(). Make sure we tear down the pipeline properly. @@ -86,7 +148,7 @@ TEST_F(PipelineImplTest, NeverInitializes) { } TEST_F(PipelineImplTest, RequiredFilterMissing) { - config_.create_filter = false; + mocks_->set_creation_successful(false); ASSERT_TRUE(InitializeAndWait()); EXPECT_FALSE(initialize_result_); @@ -96,8 +158,11 @@ TEST_F(PipelineImplTest, RequiredFilterMissing) { } TEST_F(PipelineImplTest, URLNotFound) { - config_.data_source_behavior = - media::old_mocks::MOCK_DATA_SOURCE_URL_ERROR_IN_INIT; + EXPECT_CALL(*mocks_->data_source(), Initialize("")) + .WillOnce(DoAll(Error(mocks_->data_source(), + PIPELINE_ERROR_URL_NOT_FOUND), + Return(false))); + EXPECT_CALL(*mocks_->data_source(), Stop()); ASSERT_TRUE(InitializeAndWait()); EXPECT_FALSE(initialize_result_); @@ -106,117 +171,128 @@ TEST_F(PipelineImplTest, URLNotFound) { } TEST_F(PipelineImplTest, NoStreams) { - config_.has_audio = false; - config_.has_video = false; + MockDemuxerStreamVector streams; + InitializeDataSource(); + InitializeDemuxer(&streams); ASSERT_TRUE(InitializeAndWait()); EXPECT_FALSE(initialize_result_); EXPECT_FALSE(pipeline_.IsInitialized()); EXPECT_EQ(media::PIPELINE_ERROR_COULD_NOT_RENDER, pipeline_.GetError()); - - EXPECT_FALSE(filters_->audio_decoder()); - EXPECT_FALSE(filters_->audio_renderer()); - EXPECT_FALSE(filters_->video_decoder()); - EXPECT_FALSE(filters_->video_renderer()); } TEST_F(PipelineImplTest, AudioStream) { - config_.has_video = false; + scoped_refptr<StrictMock<MockDemuxerStream> > stream = + new StrictMock<MockDemuxerStream>("audio/x-foo"); + MockDemuxerStreamVector streams; + streams.push_back(stream); + + InitializeDataSource(); + InitializeDemuxer(&streams); + InitializeAudioDecoder(stream); + InitializeAudioRenderer(); ASSERT_TRUE(InitializeAndWait()); EXPECT_TRUE(initialize_result_); EXPECT_TRUE(pipeline_.IsInitialized()); EXPECT_EQ(media::PIPELINE_OK, pipeline_.GetError()); - - size_t width, height; - pipeline_.GetVideoSize(&width, &height); - EXPECT_EQ(0u, width); - EXPECT_EQ(0u, height); EXPECT_TRUE(pipeline_.IsRendered(media::mime_type::kMajorTypeAudio)); EXPECT_FALSE(pipeline_.IsRendered(media::mime_type::kMajorTypeVideo)); - - EXPECT_TRUE(filters_->audio_decoder()); - EXPECT_TRUE(filters_->audio_renderer()); - EXPECT_FALSE(filters_->video_decoder()); - EXPECT_FALSE(filters_->video_renderer()); } TEST_F(PipelineImplTest, VideoStream) { - config_.has_audio = false; + scoped_refptr<StrictMock<MockDemuxerStream> > stream = + new StrictMock<MockDemuxerStream>("video/x-foo"); + MockDemuxerStreamVector streams; + streams.push_back(stream); + + InitializeDataSource(); + InitializeDemuxer(&streams); + InitializeVideoDecoder(stream); + InitializeVideoRenderer(); ASSERT_TRUE(InitializeAndWait()); EXPECT_TRUE(initialize_result_); EXPECT_TRUE(pipeline_.IsInitialized()); EXPECT_EQ(media::PIPELINE_OK, pipeline_.GetError()); - - size_t width, height; - pipeline_.GetVideoSize(&width, &height); - EXPECT_EQ(config_.video_width, width); - EXPECT_EQ(config_.video_height, height); EXPECT_FALSE(pipeline_.IsRendered(media::mime_type::kMajorTypeAudio)); EXPECT_TRUE(pipeline_.IsRendered(media::mime_type::kMajorTypeVideo)); - - EXPECT_FALSE(filters_->audio_decoder()); - EXPECT_FALSE(filters_->audio_renderer()); - EXPECT_TRUE(filters_->video_decoder()); - EXPECT_TRUE(filters_->video_renderer()); } TEST_F(PipelineImplTest, AudioVideoStream) { + scoped_refptr<StrictMock<MockDemuxerStream> > audio_stream = + new StrictMock<MockDemuxerStream>("audio/x-foo"); + scoped_refptr<StrictMock<MockDemuxerStream> > video_stream = + new StrictMock<MockDemuxerStream>("video/x-foo"); + MockDemuxerStreamVector streams; + streams.push_back(audio_stream); + streams.push_back(video_stream); + + InitializeDataSource(); + InitializeDemuxer(&streams); + InitializeAudioDecoder(audio_stream); + InitializeAudioRenderer(); + InitializeVideoDecoder(video_stream); + InitializeVideoRenderer(); + ASSERT_TRUE(InitializeAndWait()); EXPECT_TRUE(initialize_result_); EXPECT_TRUE(pipeline_.IsInitialized()); EXPECT_EQ(media::PIPELINE_OK, pipeline_.GetError()); - - size_t width, height; - pipeline_.GetVideoSize(&width, &height); - EXPECT_EQ(config_.video_width, width); - EXPECT_EQ(config_.video_height, height); EXPECT_TRUE(pipeline_.IsRendered(media::mime_type::kMajorTypeAudio)); EXPECT_TRUE(pipeline_.IsRendered(media::mime_type::kMajorTypeVideo)); - - EXPECT_TRUE(filters_->audio_decoder()); - EXPECT_TRUE(filters_->audio_renderer()); - EXPECT_TRUE(filters_->video_decoder()); - EXPECT_TRUE(filters_->video_renderer()); } TEST_F(PipelineImplTest, Seek) { - ASSERT_TRUE(InitializeAndWait()); - - // Seek and verify callback returned true. + scoped_refptr<StrictMock<MockDemuxerStream> > audio_stream = + new StrictMock<MockDemuxerStream>("audio/x-foo"); + scoped_refptr<StrictMock<MockDemuxerStream> > video_stream = + new StrictMock<MockDemuxerStream>("video/x-foo"); + MockDemuxerStreamVector streams; + streams.push_back(audio_stream); + streams.push_back(video_stream); + + InitializeDataSource(); + InitializeDemuxer(&streams); + 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)); + EXPECT_CALL(*mocks_->demuxer(), Seek(expected)); + EXPECT_CALL(*mocks_->audio_decoder(), Seek(expected)); + EXPECT_CALL(*mocks_->audio_renderer(), Seek(expected)); + EXPECT_CALL(*mocks_->video_decoder(), Seek(expected)); + EXPECT_CALL(*mocks_->video_renderer(), Seek(expected)); + + // Initialize then seek! + ASSERT_TRUE(InitializeAndWait()); EXPECT_TRUE(SeekAndWait(expected)); EXPECT_TRUE(seek_result_); - - // Verify every filter received the seek. - // TODO(scherkus): implement whatever it takes so I can use EXPECT_EQ with - // base::TimeDelta. - EXPECT_TRUE(expected == filters_->data_source()->seek_time()); - EXPECT_TRUE(expected == filters_->demuxer()->seek_time()); - EXPECT_TRUE(expected == filters_->audio_decoder()->seek_time()); - EXPECT_TRUE(expected == filters_->audio_renderer()->seek_time()); - EXPECT_TRUE(expected == filters_->video_decoder()->seek_time()); - EXPECT_TRUE(expected == filters_->video_renderer()->seek_time()); } -// Try to execute Start()/Stop() on the Pipeline many times and very fast. This -// test is trying to simulate the situation where the pipeline can get dead -// locked very easily by quickly calling Start()/Stop(). -TEST_F(PipelineImplTest, StressTestPipelineStartStop) { - media::old_mocks::MockFilterConfig config; - const int kTimes = 1000; - for (int i = 0; i < kTimes; ++i) { - scoped_refptr<media::old_mocks::MockFilterFactory> factory = - new media::old_mocks::MockFilterFactory(&config); - media::PipelineImpl pipeline; - pipeline.Start(factory.get(), "", NULL); - pipeline.Stop(); - } +TEST_F(PipelineImplTest, SetVolume) { + scoped_refptr<StrictMock<MockDemuxerStream> > audio_stream = + new StrictMock<MockDemuxerStream>("audio/x-foo"); + MockDemuxerStreamVector streams; + streams.push_back(audio_stream); + + InitializeDataSource(); + InitializeDemuxer(&streams); + 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! + ASSERT_TRUE(InitializeAndWait()); + pipeline_.SetVolume(expected); } -// TODO(ralphl): Add a unit test that makes sure that the mock audio filter -// is actually called on a SetVolume() call to the pipeline. I almost checked -// in code that broke this, but all unit tests were passing. +} // namespace media -} // namespace diff --git a/media/base/video_frame_impl_unittest.cc b/media/base/video_frame_impl_unittest.cc index a6a1a6a..eeec716 100644 --- a/media/base/video_frame_impl_unittest.cc +++ b/media/base/video_frame_impl_unittest.cc @@ -3,15 +3,40 @@ // found in the LICENSE file. #include "media/base/buffers.h" -#include "media/base/mock_media_filters.h" +#include "media/base/mock_filters.h" #include "media/base/video_frame_impl.h" #include "media/base/yuv_convert.h" #include "testing/gtest/include/gtest/gtest.h" -using media::VideoFrameImpl; -using media::VideoSurface; +namespace media { -namespace { +// Helper function that initializes a YV12 frame with white and black scan +// lines based on the |white_to_black| parameter. If 0, then the entire +// frame will be black, if 1 then the entire frame will be white. +void InitializeYV12Frame(VideoFrame* frame, double white_to_black) { + VideoSurface surface; + if (!frame->Lock(&surface)) { + ADD_FAILURE(); + return; + } + EXPECT_EQ(surface.format, VideoSurface::YV12); + size_t first_black_row = static_cast<size_t>(surface.height * white_to_black); + uint8* y_plane = surface.data[VideoSurface::kYPlane]; + for (size_t row = 0; row < surface.height; ++row) { + int color = (row < first_black_row) ? 0xFF : 0x00; + memset(y_plane, color, surface.width); + y_plane += surface.strides[VideoSurface::kYPlane]; + } + uint8* u_plane = surface.data[VideoSurface::kUPlane]; + uint8* v_plane = surface.data[VideoSurface::kVPlane]; + for (size_t row = 0; row < surface.height; row += 2) { + memset(u_plane, 0x80, surface.width / 2); + memset(v_plane, 0x80, surface.width / 2); + u_plane += surface.strides[VideoSurface::kUPlane]; + v_plane += surface.strides[VideoSurface::kVPlane]; + } + frame->Unlock(); +} // Given a |yv12_frame| this method converts the YV12 frame to RGBA and // makes sure that all the pixels of the RBG frame equal |expect_rgb_color|. @@ -65,10 +90,7 @@ void ExpectFrameColor(media::VideoFrame* yv12_frame, uint32 expect_rgb_color) { yv12_frame->Unlock(); } -} // namespace - - -TEST(VideoFrameImpl, Basic) { +TEST(VideoFrameImpl, CreateFrame) { const size_t kWidth = 64; const size_t kHeight = 48; const base::TimeDelta kTimestampA = base::TimeDelta::FromMicroseconds(1337); @@ -78,8 +100,8 @@ TEST(VideoFrameImpl, Basic) { // Create a YV12 Video Frame. scoped_refptr<media::VideoFrame> frame; - media::VideoFrameImpl::CreateFrame(media::VideoSurface::YV12, kWidth, kHeight, - kTimestampA, kDurationA, &frame); + VideoFrameImpl::CreateFrame(media::VideoSurface::YV12, kWidth, kHeight, + kTimestampA, kDurationA, &frame); ASSERT_TRUE(frame); // Test StreamSample implementation. @@ -98,12 +120,14 @@ TEST(VideoFrameImpl, Basic) { EXPECT_FALSE(frame->IsDiscontinuous()); // Test VideoFrame implementation. - media::old_mocks::MockVideoDecoder::InitializeYV12Frame(frame, 0.0f); + InitializeYV12Frame(frame, 0.0f); ExpectFrameColor(frame, 0xFF000000); - media::old_mocks::MockVideoDecoder::InitializeYV12Frame(frame, 1.0f); + InitializeYV12Frame(frame, 1.0f); ExpectFrameColor(frame, 0xFFFFFFFF); // Test an empty frame. - media::VideoFrameImpl::CreateEmptyFrame(&frame); + VideoFrameImpl::CreateEmptyFrame(&frame); EXPECT_TRUE(frame->IsEndOfStream()); } + +} // namespace media |