Chromium Code Reviews| Index: media/base/composite_filter_unittest.cc | 
| diff --git a/media/base/composite_filter_unittest.cc b/media/base/composite_filter_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..7e5e0546a182c59806d884317c9aff47997e47af | 
| --- /dev/null | 
| +++ b/media/base/composite_filter_unittest.cc | 
| @@ -0,0 +1,845 @@ | 
| +// 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 "media/base/composite_filter.h" | 
| +#include "media/base/mock_filter_host.h" | 
| +#include "media/base/mock_filters.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +using ::testing::_; | 
| +using ::testing::InSequence; | 
| +using ::testing::Return; | 
| +using ::testing::SaveArg; | 
| +using ::testing::StrictMock; | 
| + | 
| +namespace media { | 
| + | 
| +class CompositeFilterTest : public testing::Test { | 
| + public: | 
| + CompositeFilterTest(); | 
| + virtual ~CompositeFilterTest(); | 
| + | 
| + // Sets up a new CompositeFilter in |composite_|, creates |filter_1_| and | 
| + // |filter_2_|, and adds these filters to |composite_|. | 
| + void SetupAndAdd2Filters(); | 
| + | 
| + // Helper enum that indicates what filter method to call. | 
| + enum MethodToCall { | 
| + PLAY, | 
| + PAUSE, | 
| + FLUSH, | 
| + STOP, | 
| + SEEK, | 
| + }; | 
| + | 
| + // Helper method that adds a filter method call expectation based on the value | 
| + // of |method_to_call|. | 
| + // | 
| + // |method_to_call| - Indicates which method we expect a call for. | 
| + // |filter| - The MockFilter to add the expectation to. | 
| + // |seek_time| - The time to pass to the Seek() call if |method_to_call| | 
| + // equals SEEK. | 
| + void ExpectFilterCall(MethodToCall method_to_call, MockFilter* filter, | 
| + base::TimeDelta seek_time); | 
| + | 
| + // Helper method that calls a filter method based on the value of | 
| + // |method_to_call|. | 
| + // | 
| + // |method_to_call| - Indicates which method to call. | 
| + // |filter| - The Filter to make the method call on. | 
| + // |seek_time| - The time to pass to the Seek() call if |method_to_call| | 
| + // equals SEEK. | 
| + // |callback| - The callback object to pass to the method. | 
| + void DoFilterCall(MethodToCall method_to_call, Filter* filter, | 
| + base::TimeDelta seek_time, | 
| + FilterCallback* callback); | 
| + | 
| + // Creates an expectation sequence based on the value of method_to_call. | 
| + // | 
| + // |method_to_call| - Indicates which method we want a success sequence for. | 
| + // |seek_time| - The time to pass in the Seek() call if |method_to_call| | 
| + // equals SEEK. | 
| + void ExpectSuccess(MethodToCall method_to_call, | 
| + base::TimeDelta seek_time = base::TimeDelta()); | 
| + | 
| + // Issue a Play(), Pause(), Flush(), Stop(), or Seek() on the composite and | 
| + // verify all the expected calls on the filters. | 
| + void DoPlay(); | 
| + void DoPause(); | 
| + void DoFlush(); | 
| + void DoStop(); | 
| + void DoSeek(base::TimeDelta time); | 
| + | 
| + // Issue a Play(), Pause(), Flush(), or Seek() and expect the calls to fail | 
| + // with a PIPELINE_ERROR_INVALID_STATE error. | 
| + // | 
| + // |method_to_call| - Indicates whick method to call. | 
| + // |seek_time| - The time to pass to the Seek() call if |method_to_call| | 
| + // equals SEEK. | 
| + void ExpectInvalidStateFail(MethodToCall method_to_call, | 
| + base::TimeDelta seek_time = base::TimeDelta()); | 
| + | 
| + // Run the callback stored in |filter_1_callback_|, | 
| 
 
scherkus (not reviewing)
2010/12/16 18:43:39
nit: , -> .
 
acolwell GONE FROM CHROMIUM
2010/12/20 17:43:22
Done.
 
 | 
| + void RunFilter1Callback(); | 
| + | 
| + // Run the callback stored in |filter_2_callback_|, | 
| 
 
scherkus (not reviewing)
2010/12/16 18:43:39
nit: , -> .
 
acolwell GONE FROM CHROMIUM
2010/12/20 17:43:22
Done.
 
 | 
| + void RunFilter2Callback(); | 
| + | 
| + protected: | 
| + MessageLoop message_loop_; | 
| + | 
| + // The composite object being tested. | 
| + scoped_refptr<CompositeFilter> composite_; | 
| + | 
| + // First filter added to the composite. | 
| + scoped_refptr<StrictMock<MockFilter> > filter_1_; | 
| + | 
| + // Callback passed to |filter_1_| during last Play(), Pause(), Flush(), | 
| + // Stop(), or Seek() call. | 
| + FilterCallback* filter_1_callback_; | 
| + | 
| + // Second filter added to the composite. | 
| + scoped_refptr<StrictMock<MockFilter> > filter_2_; | 
| + | 
| + // Callback passed to |filter_2_| during last Play(), Pause(), Flush(), | 
| + // Stop(), or Seek() call. | 
| + FilterCallback* filter_2_callback_; | 
| + | 
| + // FilterHost implementation passed to |composite_| via set_host(). | 
| + scoped_ptr<StrictMock<MockFilterHost> > mock_filter_host_; | 
| + | 
| + DISALLOW_COPY_AND_ASSIGN(CompositeFilterTest); | 
| +}; | 
| + | 
| +CompositeFilterTest::CompositeFilterTest() : | 
| + composite_(new CompositeFilter(&message_loop_)), | 
| + filter_1_callback_(NULL), | 
| + filter_2_callback_(NULL), | 
| + mock_filter_host_(new StrictMock<MockFilterHost>()){ | 
| 
 
scherkus (not reviewing)
2010/12/16 18:43:39
nit: space between ) and {
 
acolwell GONE FROM CHROMIUM
2010/12/20 17:43:22
Done.
 
 | 
| +} | 
| + | 
| +CompositeFilterTest::~CompositeFilterTest() {} | 
| + | 
| +void CompositeFilterTest::SetupAndAdd2Filters() { | 
| + mock_filter_host_.reset(new StrictMock<MockFilterHost>()); | 
| + composite_ = new CompositeFilter(&message_loop_); | 
| + composite_->set_host(mock_filter_host_.get()); | 
| + | 
| + // Setup |filter_1_| and arrange for methods to set | 
| + // |filter_1_callback_| when they are called. | 
| + filter_1_ = new StrictMock<MockFilter>(); | 
| + filter_1_callback_ = NULL; | 
| + ON_CALL(*filter_1_, Play(_)) | 
| + .WillByDefault(SaveArg<0>(&filter_1_callback_)); | 
| + ON_CALL(*filter_1_, Pause(_)) | 
| + .WillByDefault(SaveArg<0>(&filter_1_callback_)); | 
| + ON_CALL(*filter_1_, Flush(_)) | 
| + .WillByDefault(SaveArg<0>(&filter_1_callback_)); | 
| + ON_CALL(*filter_1_, Stop(_)) | 
| + .WillByDefault(SaveArg<0>(&filter_1_callback_)); | 
| + ON_CALL(*filter_1_, Seek(_,_)) | 
| + .WillByDefault(SaveArg<1>(&filter_1_callback_)); | 
| + | 
| + // Setup |filter_2_| and arrange for methods to set | 
| + // |filter_2_callback_| when they are called. | 
| + filter_2_ = new StrictMock<MockFilter>(); | 
| + filter_2_callback_ = NULL; | 
| + ON_CALL(*filter_2_, Play(_)) | 
| + .WillByDefault(SaveArg<0>(&filter_2_callback_)); | 
| + ON_CALL(*filter_2_, Pause(_)) | 
| + .WillByDefault(SaveArg<0>(&filter_2_callback_)); | 
| + ON_CALL(*filter_2_, Flush(_)) | 
| + .WillByDefault(SaveArg<0>(&filter_2_callback_)); | 
| + ON_CALL(*filter_2_, Stop(_)) | 
| + .WillByDefault(SaveArg<0>(&filter_2_callback_)); | 
| + ON_CALL(*filter_2_, Seek(_,_)) | 
| + .WillByDefault(SaveArg<1>(&filter_2_callback_)); | 
| + | 
| + composite_->AddFilter(filter_1_); | 
| + composite_->AddFilter(filter_2_); | 
| +} | 
| + | 
| +void CompositeFilterTest::ExpectFilterCall(MethodToCall method_to_call, | 
| + MockFilter* filter, | 
| + base::TimeDelta seek_time) { | 
| + switch(method_to_call) { | 
| + case PLAY: | 
| + EXPECT_CALL(*filter, Play(_)); | 
| + break; | 
| + case PAUSE: | 
| + EXPECT_CALL(*filter, Pause(_)); | 
| + break; | 
| + case FLUSH: | 
| + EXPECT_CALL(*filter, Flush(_)); | 
| + break; | 
| + case STOP: | 
| + EXPECT_CALL(*filter, Stop(_)); | 
| + break; | 
| + case SEEK: | 
| + EXPECT_CALL(*filter, Seek(seek_time, _)); | 
| + break; | 
| + }; | 
| +} | 
| + | 
| +void CompositeFilterTest::DoFilterCall(MethodToCall method_to_call, | 
| + Filter* filter, | 
| + base::TimeDelta seek_time, | 
| + FilterCallback* callback) { | 
| + switch(method_to_call) { | 
| + case PLAY: | 
| + filter->Play(callback); | 
| + break; | 
| + case PAUSE: | 
| + filter->Pause(callback); | 
| + break; | 
| + case FLUSH: | 
| + filter->Flush(callback); | 
| + break; | 
| + case STOP: | 
| + filter->Stop(callback); | 
| + break; | 
| + case SEEK: | 
| + filter->Seek(seek_time, callback); | 
| + break; | 
| + }; | 
| +} | 
| + | 
| +void CompositeFilterTest::ExpectSuccess(MethodToCall method_to_call, | 
| + base::TimeDelta seek_time) { | 
| + InSequence seq; | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + bool is_parallel_call = (method_to_call == FLUSH); | 
| + | 
| + ExpectFilterCall(method_to_call, filter_1_.get(), seek_time); | 
| + | 
| + if (is_parallel_call) { | 
| + ExpectFilterCall(method_to_call, filter_2_.get(), seek_time); | 
| + } | 
| + | 
| + // Make method call on the composite. | 
| + DoFilterCall(method_to_call, composite_.get(), seek_time, | 
| + mock_callback->NewCallback()); | 
| + | 
| + if (is_parallel_call) { | 
| + // Make sure both filters have their callbacks set. | 
| + EXPECT_NE((FilterCallback*)NULL, filter_1_callback_); | 
| 
 
scherkus (not reviewing)
2010/12/16 18:43:39
ok I debugged this a bit because that's strange...
 
acolwell GONE FROM CHROMIUM
2010/12/20 17:43:22
Done. I just changed the EXPECT_NE()s to EXPECT_TR
 
 | 
| + EXPECT_NE((FilterCallback*)NULL, filter_2_callback_); | 
| + | 
| + RunFilter1Callback(); | 
| + } else { | 
| + // Make sure that only |filter_1_| has its callback set. | 
| + EXPECT_NE((FilterCallback*)NULL, filter_1_callback_); | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); | 
| + | 
| + ExpectFilterCall(method_to_call, filter_2_.get(), seek_time); | 
| + | 
| + RunFilter1Callback(); | 
| + | 
| + // Verify that |filter_2_| was called by checking the callback pointer. | 
| + EXPECT_NE((FilterCallback*)NULL, filter_2_callback_); | 
| + } | 
| + | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + RunFilter2Callback(); | 
| +} | 
| + | 
| +void CompositeFilterTest::DoPlay() { | 
| + ExpectSuccess(PLAY); | 
| +} | 
| + | 
| +void CompositeFilterTest::DoPause() { | 
| + ExpectSuccess(PAUSE); | 
| +} | 
| + | 
| +void CompositeFilterTest::DoFlush() { | 
| + ExpectSuccess(FLUSH); | 
| +} | 
| + | 
| +void CompositeFilterTest::DoStop() { | 
| + ExpectSuccess(STOP); | 
| +} | 
| + | 
| +void CompositeFilterTest::DoSeek(base::TimeDelta time) { | 
| + ExpectSuccess(SEEK, time); | 
| +} | 
| + | 
| +void CompositeFilterTest::ExpectInvalidStateFail(MethodToCall method_to_call, | 
| + base::TimeDelta seek_time) { | 
| + InSequence seq; | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_INVALID_STATE)) | 
| + .WillOnce(Return()); | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + DoFilterCall(method_to_call, composite_, seek_time, | 
| + mock_callback->NewCallback()); | 
| + | 
| + // Make sure that neither of the filters were called by | 
| + // verifying that the callback pointers weren't set. | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_1_callback_); | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); | 
| +} | 
| + | 
| +void CompositeFilterTest::RunFilter1Callback() { | 
| + EXPECT_NE((FilterCallback*)NULL, filter_1_callback_); | 
| + FilterCallback* callback = filter_1_callback_; | 
| + filter_1_callback_ = NULL; | 
| + callback->Run(); | 
| + delete callback; | 
| +} | 
| + | 
| +void CompositeFilterTest::RunFilter2Callback() { | 
| + EXPECT_NE((FilterCallback*)NULL, filter_2_callback_); | 
| + FilterCallback* callback = filter_2_callback_; | 
| + filter_2_callback_ = NULL; | 
| + callback->Run(); | 
| + delete callback; | 
| +} | 
| + | 
| +static base::Thread* NullThreadFactory(const char* thread_name) { | 
| + return NULL; | 
| +} | 
| + | 
| +// Test AddFilter() failure cases. | 
| +TEST_F(CompositeFilterTest, TestAddFilterFailCases) { | 
| + // Test adding a null pointer. | 
| + EXPECT_FALSE(composite_->AddFilter(NULL)); | 
| + | 
| + scoped_refptr<StrictMock<MockFilter> > filter = | 
| + new StrictMock<MockFilter>(true); | 
| + EXPECT_EQ(NULL, filter->host()); | 
| + EXPECT_EQ(NULL, filter->message_loop()); | 
| + | 
| + // Test failing because set_host() hasn't been called yet. | 
| + EXPECT_FALSE(composite_->AddFilter(filter)); | 
| + | 
| + // Test thread creation failure. | 
| + composite_ = new CompositeFilter(&message_loop_, &NullThreadFactory); | 
| + composite_->set_host(mock_filter_host_.get()); | 
| + EXPECT_FALSE(composite_->AddFilter(filter)); | 
| + EXPECT_EQ(NULL, filter->host()); | 
| + EXPECT_EQ(NULL, filter->message_loop()); | 
| +} | 
| + | 
| +// Test successful AddFilter() cases. | 
| +TEST_F(CompositeFilterTest, TestAddFilter) { | 
| + composite_->set_host(mock_filter_host_.get()); | 
| + | 
| + // Add a filter that doesn't require a message loop. | 
| + scoped_refptr<StrictMock<MockFilter> > filter = new StrictMock<MockFilter>(); | 
| + EXPECT_EQ(NULL, filter->host()); | 
| + EXPECT_EQ(NULL, filter->message_loop()); | 
| + | 
| + EXPECT_TRUE(composite_->AddFilter(filter)); | 
| + | 
| + EXPECT_NE((FilterHost*)NULL, filter->host()); | 
| + EXPECT_EQ(NULL, filter->message_loop()); | 
| + | 
| + // Add a filter that requires a message loop. | 
| + scoped_refptr<StrictMock<MockFilter> > filter_2 = | 
| + new StrictMock<MockFilter>(true); | 
| + | 
| + EXPECT_EQ(NULL, filter_2->host()); | 
| + EXPECT_EQ(NULL, filter_2->message_loop()); | 
| + | 
| + EXPECT_TRUE(composite_->AddFilter(filter_2)); | 
| + | 
| + EXPECT_NE((FilterHost*)NULL, filter_2->host()); | 
| + EXPECT_NE((MessageLoop*)NULL, filter_2->message_loop()); | 
| +} | 
| + | 
| +static bool g_thread_cleanup_called_ = false; | 
| +class CompositeFilterThread : public base::Thread { | 
| + public: | 
| + CompositeFilterThread(const char* name) : base::Thread(name) {} | 
| + virtual void CleanUp() { | 
| + g_thread_cleanup_called_ = true; | 
| + base::Thread::CleanUp(); | 
| + } | 
| +}; | 
| + | 
| +static base::Thread* g_thread_created_by_factory = NULL; | 
| +static base::Thread* TestableThreadFactory(const char* thread_name) { | 
| + g_thread_created_by_factory = new CompositeFilterThread(thread_name); | 
| + return g_thread_created_by_factory; | 
| +} | 
| + | 
| +// Verify that thread termination works as expected. | 
| +TEST_F(CompositeFilterTest, TestThreadTermination) { | 
| 
 
scherkus (not reviewing)
2010/12/16 18:43:39
I still don't think we need (most of) this test si
 
acolwell GONE FROM CHROMIUM
2010/12/20 17:43:22
Removed the test case.
Agree that thread managemen
 
 | 
| + composite_ = new CompositeFilter(&message_loop_, &TestableThreadFactory); | 
| + composite_->set_host(mock_filter_host_.get()); | 
| + | 
| + // Add a filter that requires a message loop. | 
| + scoped_refptr<StrictMock<MockFilter> > filter = | 
| + new StrictMock<MockFilter>(true); | 
| + | 
| + EXPECT_EQ(NULL, g_thread_created_by_factory); | 
| + EXPECT_TRUE(composite_->AddFilter(filter)); | 
| + EXPECT_NE((base::Thread*)NULL, g_thread_created_by_factory); | 
| + EXPECT_NE((MessageLoop*)NULL, filter->message_loop()); | 
| + | 
| + // Verify that the filter has a message loop and it matches the | 
| + // thread's message loop. | 
| + base::Thread* filter_thread = g_thread_created_by_factory; | 
| + EXPECT_NE((MessageLoop*)NULL, filter_thread->message_loop()); | 
| + EXPECT_EQ(filter_thread->message_loop(), filter->message_loop()); | 
| + | 
| + EXPECT_TRUE(filter_thread->IsRunning()); | 
| + EXPECT_FALSE(g_thread_cleanup_called_); | 
| + | 
| + composite_ = NULL; | 
| + | 
| + // Verify that the thread stops running when the composite is | 
| + // destroyed. | 
| + EXPECT_TRUE(g_thread_cleanup_called_); | 
| +} | 
| + | 
| +TEST_F(CompositeFilterTest, TestPlay) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + // Verify successful call to Play(). | 
| + DoPlay(); | 
| + | 
| + // At this point we are now in the kPlaying state. | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + // Try calling Play() again to make sure that we simply get a callback. | 
| + // We are already in the Play() state so there is no point calling the | 
| + // filters. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + composite_->Play(mock_callback->NewCallback()); | 
| + | 
| + // Verify that neither of the filter callbacks were set. | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_1_callback_); | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); | 
| + | 
| + // Stop playback. | 
| + DoStop(); | 
| + | 
| + // At this point we should be in the kStopped state. | 
| + | 
| + // Try calling Stop() again to make sure neither filter is called. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + composite_->Stop(mock_callback->NewCallback()); | 
| + | 
| + // Verify that neither of the filter callbacks were set. | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_1_callback_); | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); | 
| + | 
| + // Try calling Play() again to make sure we get an error. | 
| + ExpectInvalidStateFail(PLAY); | 
| +} | 
| + | 
| +// Test errors in the middle of a serial call sequence like Play(). | 
| +TEST_F(CompositeFilterTest, TestPlayErrors) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + EXPECT_CALL(*filter_1_, Play(_)); | 
| + | 
| + // Call Play() on the composite. | 
| + composite_->Play(mock_callback->NewCallback()); | 
| + | 
| + EXPECT_CALL(*filter_2_, Play(_)); | 
| + | 
| + // Run callback to indicate that |filter_1_|'s Play() has completed. | 
| + RunFilter1Callback(); | 
| + | 
| + // At this point Play() has been called on |filter_2_|. Simulate an | 
| + // error by calling SetError() on its FilterHost interface. | 
| + filter_2_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); | 
| + | 
| + // Expect error to be reported and "play done" callback to be called. | 
| + EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + // Run callback to indicate that |filter_2_|'s Play() has completed. | 
| + RunFilter2Callback(); | 
| + | 
| + // Verify that Play/Pause/Flush/Seek fail now that an error occured. | 
| + ExpectInvalidStateFail(PLAY); | 
| + ExpectInvalidStateFail(PAUSE); | 
| + ExpectInvalidStateFail(FLUSH); | 
| + ExpectInvalidStateFail(SEEK); | 
| + | 
| + // Make sure you can still Stop(). | 
| + DoStop(); | 
| +} | 
| + | 
| +TEST_F(CompositeFilterTest, TestPause) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + // Try calling Pause() to make sure we get an error because we aren't in | 
| + // the playing state. | 
| + ExpectInvalidStateFail(PAUSE); | 
| + | 
| + // Transition to playing state. | 
| + DoPlay(); | 
| + | 
| + // Issue a successful Pause(). | 
| + DoPause(); | 
| + | 
| + // At this point we are paused. | 
| + | 
| + // Try calling Pause() again to make sure that the filters aren't called | 
| + // because we are already in the paused state. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + composite_->Pause(mock_callback->NewCallback()); | 
| + | 
| + // Verify that neither of the filter callbacks were set. | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_1_callback_); | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); | 
| + | 
| + // Verify that we can transition pack to the play state. | 
| + DoPlay(); | 
| + | 
| + // Go back to the pause state. | 
| + DoPause(); | 
| + | 
| + // Transition to the stop state. | 
| + DoStop(); | 
| + | 
| + // Try calling Pause() to make sure we get an error because we aren't in | 
| + // the playing state. | 
| + ExpectInvalidStateFail(PAUSE); | 
| +} | 
| + | 
| +// Test errors in the middle of a serial call sequence like Pause(). | 
| +TEST_F(CompositeFilterTest, TestPauseErrors) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + DoPlay(); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + EXPECT_CALL(*filter_1_, Pause(_)); | 
| + | 
| + // Call Pause() on the composite. | 
| + composite_->Pause(mock_callback->NewCallback()); | 
| + | 
| + // Simulate an error by calling SetError() on |filter_1_|'s FilterHost | 
| + // interface. | 
| + filter_1_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); | 
| + | 
| + // Expect error to be reported and "pause done" callback to be called. | 
| + EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + RunFilter1Callback(); | 
| + | 
| + // Make sure |filter_2_callback_| was not set. | 
| + EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); | 
| + | 
| + // Verify that Play/Pause/Flush/Seek fail now that an error occured. | 
| + ExpectInvalidStateFail(PLAY); | 
| + ExpectInvalidStateFail(PAUSE); | 
| + ExpectInvalidStateFail(FLUSH); | 
| + ExpectInvalidStateFail(SEEK); | 
| + | 
| + // Make sure you can still Stop(). | 
| + DoStop(); | 
| +} | 
| + | 
| +TEST_F(CompositeFilterTest, TestFlush) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + // Make sure Flush() works before calling Play(). | 
| + DoFlush(); | 
| + | 
| + // Transition to playing state. | 
| + DoPlay(); | 
| + | 
| + // Call Flush() to make sure we get an error because we are in | 
| + // the playing state. | 
| + ExpectInvalidStateFail(FLUSH); | 
| + | 
| + // Issue a successful Pause(). | 
| + DoPause(); | 
| + | 
| + // Make sure Flush() works after pausing. | 
| + DoFlush(); | 
| + | 
| + // Verify that we can transition back to the play state. | 
| + DoPlay(); | 
| + | 
| + // Transition to the stop state. | 
| + DoStop(); | 
| + | 
| + // Try calling Flush() to make sure we get an error because we are stopped. | 
| + ExpectInvalidStateFail(FLUSH); | 
| +} | 
| + | 
| +// Test errors in the middle of a parallel call sequence like Flush(). | 
| +TEST_F(CompositeFilterTest, TestFlushErrors) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + EXPECT_CALL(*filter_1_, Flush(_)); | 
| + EXPECT_CALL(*filter_2_, Flush(_)); | 
| + | 
| + // Call Flush() on the composite. | 
| + composite_->Flush(mock_callback->NewCallback()); | 
| + | 
| + // Simulate an error by calling SetError() on |filter_1_|'s FilterHost | 
| + // interface. | 
| + filter_1_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); | 
| + | 
| + RunFilter1Callback(); | 
| + | 
| + // Expect error to be reported and "pause done" callback to be called. | 
| + EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + RunFilter2Callback(); | 
| + | 
| + // Verify that Play/Pause/Flush/Seek fail now that an error occured. | 
| + ExpectInvalidStateFail(PLAY); | 
| + ExpectInvalidStateFail(PAUSE); | 
| + ExpectInvalidStateFail(FLUSH); | 
| + ExpectInvalidStateFail(SEEK); | 
| + | 
| + // Make sure you can still Stop(). | 
| + DoStop(); | 
| +} | 
| + | 
| +TEST_F(CompositeFilterTest, TestSeek) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + // Verify that seek is allowed to be called before a Play() call. | 
| + DoSeek(base::TimeDelta::FromSeconds(5)); | 
| + | 
| + // Verify we can issue a Play() after the Seek(). | 
| + DoPlay(); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + // Try calling Seek() while playing to make sure we get an error. | 
| + ExpectInvalidStateFail(SEEK); | 
| + | 
| + // Transition to paused state. | 
| + DoPause(); | 
| + | 
| + // Verify that seek is allowed after pausing. | 
| + DoSeek(base::TimeDelta::FromSeconds(5)); | 
| + | 
| + // Verify we can still play again. | 
| + DoPlay(); | 
| + | 
| + // Stop playback. | 
| + DoStop(); | 
| + | 
| + // Try calling Seek() to make sure we get an error. | 
| + ExpectInvalidStateFail(SEEK); | 
| +} | 
| + | 
| +TEST_F(CompositeFilterTest, TestStop) { | 
| + InSequence sequence; | 
| + | 
| + // Test Stop() before any other call. | 
| + SetupAndAdd2Filters(); | 
| + DoStop(); | 
| + | 
| + // Test error during Stop() sequence. | 
| + SetupAndAdd2Filters(); | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + EXPECT_CALL(*filter_1_, Stop(_)); | 
| + | 
| + composite_->Stop(mock_callback->NewCallback()); | 
| + | 
| + // Have |filter_1_| signal an error. | 
| + filter_1_->host()->SetError(PIPELINE_ERROR_READ); | 
| + | 
| + EXPECT_CALL(*filter_2_, Stop(_)); | 
| + | 
| + RunFilter1Callback(); | 
| + | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + | 
| + RunFilter2Callback(); | 
| +} | 
| + | 
| +// Test stopping in the middle of a serial call sequence. | 
| +TEST_F(CompositeFilterTest, TestStopWhilePlayPending) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>()); | 
| + | 
| + EXPECT_CALL(*filter_1_, Play(_)); | 
| + | 
| + composite_->Play(mock_callback->NewCallback()); | 
| + | 
| + // Note: Play() is pending on |filter_1_| right now. | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback_2( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + EXPECT_CALL(*mock_callback, OnCallbackDestroyed()); | 
| + | 
| + composite_->Stop(mock_callback_2->NewCallback()); | 
| + | 
| + EXPECT_CALL(*filter_1_, Stop(_)); | 
| + | 
| + // Run |filter_1_|'s callback again to indicate Play() has completed. | 
| + RunFilter1Callback(); | 
| + | 
| + EXPECT_CALL(*filter_2_, Stop(_)); | 
| + | 
| + // Run |filter_1_|'s callback again to indicate Stop() has completed. | 
| + RunFilter1Callback(); | 
| + | 
| + EXPECT_CALL(*mock_callback_2, OnFilterCallback()); | 
| + | 
| + // Run |filter_2_|'s callback to indicate Stop() has completed. | 
| + RunFilter2Callback(); | 
| +} | 
| + | 
| +// Test stopping in the middle of a parallel call sequence. | 
| +TEST_F(CompositeFilterTest, TestStopWhileFlushPending) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>()); | 
| + | 
| + EXPECT_CALL(*filter_1_, Flush(_)); | 
| + EXPECT_CALL(*filter_2_, Flush(_)); | 
| + | 
| + composite_->Flush(mock_callback->NewCallback()); | 
| + | 
| + // Note: |filter_1_| and |filter_2_| have pending Flush() calls at this point. | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback_2( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + EXPECT_CALL(*mock_callback, OnCallbackDestroyed()); | 
| + | 
| + composite_->Stop(mock_callback_2->NewCallback()); | 
| + | 
| + // Run callback to indicate that |filter_1_|'s Flush() has completed. | 
| + RunFilter1Callback(); | 
| + | 
| + EXPECT_CALL(*filter_1_, Stop(_)); | 
| + | 
| + // Run callback to indicate that |filter_2_|'s Flush() has completed. | 
| + RunFilter2Callback(); | 
| + | 
| + EXPECT_CALL(*filter_2_, Stop(_)); | 
| + | 
| + // Run callback to indicate that |filter_1_|'s Stop() has completed. | 
| + RunFilter1Callback(); | 
| + | 
| + EXPECT_CALL(*mock_callback_2, OnFilterCallback()); | 
| + | 
| + // Run callback to indicate that |filter_2_|'s Stop() has completed. | 
| + RunFilter2Callback(); | 
| +} | 
| + | 
| +TEST_F(CompositeFilterTest, TestErrorWhilePlaying) { | 
| + InSequence sequence; | 
| + | 
| + SetupAndAdd2Filters(); | 
| + | 
| + // Simulate an error on |filter_2_| while in kCreated state. This | 
| + // can happen if an error occurs during filter initialization. | 
| + EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); | 
| + filter_2_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); | 
| + | 
| + DoPlay(); | 
| + | 
| + // Simulate an error on |filter_2_| while playing. | 
| + EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); | 
| + filter_2_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); | 
| + | 
| + DoPause(); | 
| + | 
| + // Simulate an error on |filter_2_| while paused. | 
| + EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_NETWORK)); | 
| + filter_2_->host()->SetError(PIPELINE_ERROR_NETWORK); | 
| + | 
| + DoStop(); | 
| + | 
| + // Verify that errors are not passed to |mock_filter_host_| | 
| + // after Stop() has been called. | 
| + filter_2_->host()->SetError(PIPELINE_ERROR_NETWORK); | 
| +} | 
| + | 
| +// Make sure that state transitions act as expected even | 
| +// if the composite doesn't contain any filters. | 
| +TEST_F(CompositeFilterTest, TestEmptyComposite) { | 
| + InSequence sequence; | 
| + | 
| + composite_->set_host(mock_filter_host_.get()); | 
| + | 
| + scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( | 
| + new StrictMock<MockFilterCallback>(false)); | 
| + | 
| + // Issue a Play() and expect no errors. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + composite_->Play(mock_callback->NewCallback()); | 
| + | 
| + // Issue a Pause() and expect no errors. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + composite_->Pause(mock_callback->NewCallback()); | 
| + | 
| + // Issue a Flush() and expect no errors. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + composite_->Flush(mock_callback->NewCallback()); | 
| + | 
| + // Issue a Seek() and expect no errors. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + composite_->Seek(base::TimeDelta::FromSeconds(5), | 
| + mock_callback->NewCallback()); | 
| + | 
| + // Issue a Play() and expect no errors. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + composite_->Play(mock_callback->NewCallback()); | 
| + | 
| + // Issue a Stop() and expect no errors. | 
| + EXPECT_CALL(*mock_callback, OnFilterCallback()); | 
| + composite_->Stop(mock_callback->NewCallback()); | 
| +} | 
| + | 
| +} // namespace media |