diff options
-rw-r--r-- | tools/valgrind/memcheck/suppressions.txt | 10 | ||||
-rw-r--r-- | webkit/media/buffered_data_source.h | 2 | ||||
-rw-r--r-- | webkit/media/buffered_data_source_unittest.cc | 876 | ||||
-rw-r--r-- | webkit/media/buffered_resource_loader.h | 2 | ||||
-rw-r--r-- | webkit/media/test_response_generator.cc | 84 | ||||
-rw-r--r-- | webkit/media/test_response_generator.h | 59 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gypi | 2 |
7 files changed, 419 insertions, 616 deletions
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt index 499dafc..763a6f6 100644 --- a/tools/valgrind/memcheck/suppressions.txt +++ b/tools/valgrind/memcheck/suppressions.txt @@ -4546,18 +4546,10 @@ fun:_ZN16CrxUpdateService19ProcessPendingItemsEv } { - bug_100914a + bug_100914 Memcheck:Leak fun:_Znw* fun:_ZN12webkit_media18BufferedDataSource20CreateResourceLoaderEll - fun:_ZN12webkit_media18BufferedDataSource24HttpInitialStartCallbackEi -} -{ - bug_100914b - Memcheck:Leak - fun:_Znw* - fun:_ZN12webkit_media18BufferedDataSource20CreateResourceLoaderEll - fun:_ZN12webkit_media18BufferedDataSource14InitializeTaskEv } { bug_100916 diff --git a/webkit/media/buffered_data_source.h b/webkit/media/buffered_data_source.h index 7987d09..d7dc37f 100644 --- a/webkit/media/buffered_data_source.h +++ b/webkit/media/buffered_data_source.h @@ -69,7 +69,7 @@ class BufferedDataSource : public WebDataSource { int64 first_byte_position, int64 last_byte_position); private: - friend class BufferedDataSourceTest2; + friend class BufferedDataSourceTest; // Posted to perform initialization on render thread and start resource // loading. diff --git a/webkit/media/buffered_data_source_unittest.cc b/webkit/media/buffered_data_source_unittest.cc index ce14c7a..2bcae64 100644 --- a/webkit/media/buffered_data_source_unittest.cc +++ b/webkit/media/buffered_data_source_unittest.cc @@ -2,41 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <algorithm> - #include "base/bind.h" -#include "base/test/test_timeouts.h" #include "media/base/media_log.h" #include "media/base/mock_callback.h" #include "media/base/mock_filter_host.h" #include "media/base/mock_filters.h" -#include "net/base/net_errors.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "webkit/media/buffered_data_source.h" #include "webkit/mocks/mock_webframeclient.h" #include "webkit/mocks/mock_weburlloader.h" +#include "webkit/media/test_response_generator.h" using ::testing::_; using ::testing::Assign; -using ::testing::AtLeast; -using ::testing::DeleteArg; -using ::testing::DoAll; -using ::testing::InSequence; using ::testing::Invoke; -using ::testing::InvokeWithoutArgs; -using ::testing::NotNull; -using ::testing::Return; -using ::testing::ReturnRef; -using ::testing::SetArgumentPointee; +using ::testing::Mock; using ::testing::StrictMock; using ::testing::NiceMock; -using ::testing::WithArgs; using WebKit::WebFrame; -using WebKit::WebString; -using WebKit::WebURLError; +using WebKit::WebURLLoader; using WebKit::WebURLResponse; using WebKit::WebView; @@ -45,487 +31,325 @@ using webkit_glue::MockWebURLLoader; namespace webkit_media { -static const char* kHttpUrl = "http://test"; -static const char* kFileUrl = "file://test"; -static const int kDataSize = 1024; -static const int kMaxCacheMissesBeforeFailTest = 20; - -enum NetworkState { - NONE, - LOADED, - LOADING -}; - -// A mock BufferedDataSource to inject mock BufferedResourceLoader through -// CreateResourceLoader() method. +// Overrides CreateResourceLoader() to permit injecting a MockWebURLLoader. +// Also keeps track of whether said MockWebURLLoader is actively loading. class MockBufferedDataSource : public BufferedDataSource { public: MockBufferedDataSource(MessageLoop* message_loop, WebFrame* frame) - : BufferedDataSource(message_loop, frame, new media::MediaLog()) { + : BufferedDataSource(message_loop, frame, new media::MediaLog()), + loading_(false) { } - virtual base::TimeDelta GetTimeoutMilliseconds() { - return base::TimeDelta::FromMilliseconds( - TestTimeouts::tiny_timeout_ms()); + MOCK_METHOD2(CreateResourceLoader, BufferedResourceLoader*(int64, int64)); + BufferedResourceLoader* CreateMockResourceLoader(int64 first_byte_position, + int64 last_byte_position) { + CHECK(!loading_) << "Previous resource load wasn't cancelled"; + + BufferedResourceLoader* loader = + BufferedDataSource::CreateResourceLoader(first_byte_position, + last_byte_position); + + // Keep track of active loading state via loadAsynchronously() and cancel(). + NiceMock<MockWebURLLoader>* url_loader = new NiceMock<MockWebURLLoader>(); + ON_CALL(*url_loader, loadAsynchronously(_, _)) + .WillByDefault(Assign(&loading_, true)); + ON_CALL(*url_loader, cancel()) + .WillByDefault(Assign(&loading_, false)); + + // TODO(scherkus): this is a real leak detected by http://crbug.com/100914 + // but the fix will have to wait for a more invasive follow up patch. + // + // If you're curious what the fix is, we no longer need the reference + // counting added to BufferedResourceLoader in r23274 since we started + // using WebURLLoader in r69429. + Mock::AllowLeak(url_loader); + + loader->SetURLLoaderForTest(url_loader); + return loader; } - MOCK_METHOD2(CreateResourceLoader, - BufferedResourceLoader*(int64 first_position, - int64 last_position)); + bool loading() { return loading_; } + void set_loading(bool loading) { loading_ = loading; } private: + // Whether the resource load has starting loading but yet to been cancelled. + bool loading_; + DISALLOW_COPY_AND_ASSIGN(MockBufferedDataSource); }; -class MockBufferedResourceLoader : public BufferedResourceLoader { - public: - MockBufferedResourceLoader() - : BufferedResourceLoader(GURL(), 0, 0, kThresholdDefer, - 0, 0, new media::MediaLog()) { - } - - MOCK_METHOD3(Start, void(net::OldCompletionCallback* read_callback, - const base::Closure& network_callback, - WebFrame* frame)); - MOCK_METHOD0(Stop, void()); - MOCK_METHOD4(Read, void(int64 position, int read_size, uint8* buffer, - net::OldCompletionCallback* callback)); - MOCK_METHOD0(content_length, int64()); - MOCK_METHOD0(instance_size, int64()); - MOCK_METHOD0(range_supported, bool()); - MOCK_METHOD0(network_activity, bool()); - MOCK_METHOD0(url, const GURL&()); - MOCK_METHOD0(GetBufferedFirstBytePosition, int64()); - MOCK_METHOD0(GetBufferedLastBytePosition, int64()); - - protected: - ~MockBufferedResourceLoader() {} - - DISALLOW_COPY_AND_ASSIGN(MockBufferedResourceLoader); -}; +static const int64 kFileSize = 5000000; +static const int64 kFarReadPosition = 4000000; +static const size_t kDataSize = 1024; class BufferedDataSourceTest : public testing::Test { public: BufferedDataSourceTest() - : view_(WebView::create(NULL)) { + : response_generator_(GURL("http://localhost/foo.webm"), kFileSize), + view_(WebView::create(NULL)), + message_loop_(MessageLoop::current()) { view_->initializeMainFrame(&client_); - message_loop_ = MessageLoop::current(); - for (size_t i = 0; i < sizeof(data_); ++i) { - data_[i] = i; - } + data_source_ = new MockBufferedDataSource(message_loop_, + view_->mainFrame()); + data_source_->set_host(&host_); } virtual ~BufferedDataSourceTest() { view_->close(); } - void ExpectCreateAndStartResourceLoader(int start_error) { - EXPECT_CALL(*data_source_, CreateResourceLoader(_, _)) - .WillOnce(Return(loader_.get())); - - EXPECT_CALL(*loader_, Start(NotNull(), _, NotNull())) - .WillOnce( - DoAll(Assign(&error_, start_error), - Invoke(this, - &BufferedDataSourceTest::InvokeStartCallback))); + void Initialize(media::PipelineStatus expected) { + ExpectCreateResourceLoader(); + data_source_->Initialize(response_generator_.gurl().spec(), + media::NewExpectedStatusCB(expected)); + message_loop_->RunAllPending(); } - void InitializeDataSource(const char* url, int error, - bool partial_response, int64 instance_size, - NetworkState networkState) { - // Saves the url first. - gurl_ = GURL(url); - - data_source_ = new MockBufferedDataSource(MessageLoop::current(), - view_->mainFrame()); - data_source_->set_host(&host_); - - scoped_refptr<NiceMock<MockBufferedResourceLoader> > first_loader( - new NiceMock<MockBufferedResourceLoader>()); - - // Creates the mock loader to be injected. - loader_ = first_loader; - - bool initialized_ok = (error == net::OK); - bool loaded = networkState == LOADED; - { - InSequence s; - ExpectCreateAndStartResourceLoader(error); - - // In the case of an invalid partial response we expect a second loader - // to be created. - if (partial_response && (error == net::ERR_INVALID_RESPONSE)) { - // Verify that the initial loader is stopped. - EXPECT_CALL(*loader_, url()) - .WillRepeatedly(ReturnRef(gurl_)); - EXPECT_CALL(*loader_, Stop()); + // Helper to initialize tests with a valid 206 response. + void InitializeWith206Response() { + Initialize(media::PIPELINE_OK); - // Replace loader_ with a new instance. - loader_ = new NiceMock<MockBufferedResourceLoader>(); - - // Create and start. Make sure Start() is called on the new loader. - ExpectCreateAndStartResourceLoader(net::OK); - - // Update initialization variable since we know the second loader will - // return OK. - initialized_ok = true; - } - } - - // Attach a static function that deletes the memory referred by the - // "callback" parameter. - ON_CALL(*loader_, Read(_, _, _ , _)) - .WillByDefault(DeleteArg<3>()); - - ON_CALL(*loader_, instance_size()) - .WillByDefault(Return(instance_size)); - - // range_supported() return true if we expect to get a partial response. - ON_CALL(*loader_, range_supported()) - .WillByDefault(Return(partial_response)); - - ON_CALL(*loader_, url()) - .WillByDefault(ReturnRef(gurl_)); - media::PipelineStatus expected_init_status = media::PIPELINE_OK; - if (initialized_ok) { - // Expected loaded or not. - EXPECT_CALL(host_, SetLoaded(loaded)); - - if (instance_size != -1) - EXPECT_CALL(host_, SetTotalBytes(instance_size)); - - if (loaded) - EXPECT_CALL(host_, SetBufferedBytes(instance_size)); - else - EXPECT_CALL(host_, SetBufferedBytes(0)); - - if (!partial_response || instance_size == -1) - EXPECT_CALL(host_, SetStreaming(true)); - - } else { - expected_init_status = media::PIPELINE_ERROR_NETWORK; - EXPECT_CALL(*loader_, Stop()); - } - - // Actual initialization of the data source. - data_source_->Initialize(url, - media::NewExpectedStatusCB(expected_init_status)); - message_loop_->RunAllPending(); - - if (initialized_ok) { - // Verify the size of the data source. - int64 size; - if (instance_size != -1 && (loaded || partial_response)) { - EXPECT_TRUE(data_source_->GetSize(&size)); - EXPECT_EQ(instance_size, size); - } else { - EXPECT_TRUE(data_source_->IsStreaming()); - } - } + EXPECT_CALL(host_, SetLoaded(false)); + EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); + EXPECT_CALL(host_, SetBufferedBytes(0)); + Respond(response_generator_.Generate206(0)); } - void StopDataSource() { - if (loader_) { - InSequence s; - EXPECT_CALL(*loader_, Stop()); + // Stops any active loaders and shuts down the data source. + // + // This typically happens when the page is closed and for our purposes is + // appropriate to do when tearing down a test. + void Stop() { + if (data_source_->loading()) { + loader()->didFail(url_loader(), response_generator_.GenerateError()); + message_loop_->RunAllPending(); } data_source_->Stop(media::NewExpectedClosure()); message_loop_->RunAllPending(); } - void InvokeStartCallback( - net::OldCompletionCallback* callback, - const base::Closure& network_callback, - WebFrame* frame) { - callback->RunWithParams(Tuple1<int>(error_)); - delete callback; - // TODO(hclam): Save network_callback. + void ExpectCreateResourceLoader() { + EXPECT_CALL(*data_source_, CreateResourceLoader(_, _)) + .WillOnce(Invoke(data_source_.get(), + &MockBufferedDataSource::CreateMockResourceLoader)); + message_loop_->RunAllPending(); } - void InvokeReadCallback(int64 position, int size, uint8* buffer, - net::OldCompletionCallback* callback) { - if (error_ > 0) - memcpy(buffer, data_ + static_cast<int>(position), error_); - callback->RunWithParams(Tuple1<int>(error_)); - delete callback; + void Respond(const WebURLResponse& response) { + loader()->didReceiveResponse(url_loader(), response); + message_loop_->RunAllPending(); } - void ReadDataSourceHit(int64 position, int size, int read_size) { - EXPECT_TRUE(loader_); - - InSequence s; - // Expect the read is delegated to the resource loader. - EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull())) - .WillOnce(DoAll(Assign(&error_, read_size), - Invoke(this, - &BufferedDataSourceTest::InvokeReadCallback))); - - // The read has succeeded, so read callback will be called. - EXPECT_CALL(*this, ReadCallback(read_size)); - - data_source_->Read( - position, size, buffer_, - base::Bind(&BufferedDataSourceTest::ReadCallback, - base::Unretained(this))); + void FinishRead() { + loader()->didReceiveData(url_loader(), data_, kDataSize, kDataSize); message_loop_->RunAllPending(); + } - // Make sure data is correct. - EXPECT_EQ(0, - memcmp(buffer_, data_ + static_cast<int>(position), read_size)); + void FinishLoading() { + data_source_->set_loading(false); + loader()->didFinishLoading(url_loader(), 0); + message_loop_->RunAllPending(); } - void ReadDataSourceHang(int64 position, int size) { - EXPECT_TRUE(loader_); + MOCK_METHOD1(ReadCallback, void(size_t size)); - // Expect a call to read, but the call never returns. - EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull())); - data_source_->Read( - position, size, buffer_, - base::Bind(&BufferedDataSourceTest::ReadCallback, - base::Unretained(this))); + void ReadAt(int64 position) { + data_source_->Read(position, kDataSize, buffer_, + base::Bind(&BufferedDataSourceTest::ReadCallback, + base::Unretained(this))); message_loop_->RunAllPending(); + } - // Now expect the read to return after aborting the data source. - EXPECT_CALL(*this, ReadCallback(_)); - EXPECT_CALL(*loader_, Stop()); - data_source_->Abort(); - message_loop_->RunAllPending(); + // Accessors for private variables on |data_source_|. + BufferedResourceLoader* loader() { return data_source_->loader_.get(); } + WebURLLoader* url_loader() { return loader()->url_loader_.get(); } - // The loader has now been stopped. Set this to null so that when the - // DataSource is stopped, it does not expect a call to stop the loader. - loader_ = NULL; + media::Preload preload() { return data_source_->preload_; } + BufferedResourceLoader::DeferStrategy defer_strategy() { + return loader()->defer_strategy_; } + int data_source_bitrate() { return data_source_->bitrate_; } + int data_source_playback_rate() { return data_source_->playback_rate_; } + int loader_bitrate() { return loader()->bitrate_; } + int loader_playback_rate() { return loader()->playback_rate_; } - void ReadDataSourceMiss(int64 position, int size, int start_error) { - EXPECT_TRUE(loader_); - - // 1. Reply with a cache miss for the read. - { - InSequence s; - EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull())) - .WillOnce(DoAll(Assign(&error_, net::ERR_CACHE_MISS), - Invoke(this, - &BufferedDataSourceTest::InvokeReadCallback))); - EXPECT_CALL(*loader_, Stop()); - } - // 2. Then the current loader will be stop and destroyed. - NiceMock<MockBufferedResourceLoader> *new_loader = - new NiceMock<MockBufferedResourceLoader>(); - EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1)) - .WillOnce(Return(new_loader)); - - // 3. Then the new loader will be started. - EXPECT_CALL(*new_loader, Start(NotNull(), _, NotNull())) - .WillOnce(DoAll(Assign(&error_, start_error), - Invoke(this, - &BufferedDataSourceTest::InvokeStartCallback))); - - if (start_error == net::OK) { - EXPECT_CALL(*new_loader, range_supported()) - .WillRepeatedly(Return(loader_->range_supported())); - - // 4a. Then again a read request is made to the new loader. - EXPECT_CALL(*new_loader, Read(position, size, NotNull(), NotNull())) - .WillOnce(DoAll(Assign(&error_, size), - Invoke(this, - &BufferedDataSourceTest::InvokeReadCallback))); - - EXPECT_CALL(*this, ReadCallback(size)); - } else { - // 4b. The read callback is called with an error because Start() on the - // new loader returned an error. - EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); - } + scoped_refptr<MockBufferedDataSource> data_source_; - data_source_->Read( - position, size, buffer_, - base::Bind(&BufferedDataSourceTest::ReadCallback, - base::Unretained(this))); - message_loop_->RunAllPending(); + TestResponseGenerator response_generator_; + MockWebFrameClient client_; + WebView* view_; - // Make sure data is correct. - if (start_error == net::OK) - EXPECT_EQ(0, memcmp(buffer_, data_ + static_cast<int>(position), size)); + StrictMock<media::MockFilterHost> host_; + MessageLoop* message_loop_; - loader_ = new_loader; - } + private: + // Used for calling BufferedDataSource::Read(). + uint8 buffer_[kDataSize]; - void ReadDataSourceFailed(int64 position, int size, int error) { - EXPECT_TRUE(loader_); + // Used for calling BufferedResourceLoader::didReceiveData(). + char data_[kDataSize]; - // 1. Expect the read is delegated to the resource loader. - EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull())) - .WillOnce(DoAll(Assign(&error_, error), - Invoke(this, - &BufferedDataSourceTest::InvokeReadCallback))); + DISALLOW_COPY_AND_ASSIGN(BufferedDataSourceTest); +}; - // 2. Host will then receive an error. - EXPECT_CALL(*loader_, Stop()); +TEST_F(BufferedDataSourceTest, Range_Supported) { + Initialize(media::PIPELINE_OK); - // 3. The read has failed, so read callback will be called. - EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + EXPECT_CALL(host_, SetLoaded(false)); + EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); + EXPECT_CALL(host_, SetBufferedBytes(0)); + Respond(response_generator_.Generate206(0)); - data_source_->Read( - position, size, buffer_, - base::Bind(&BufferedDataSourceTest::ReadCallback, - base::Unretained(this))); + EXPECT_TRUE(data_source_->loading()); + Stop(); +} - message_loop_->RunAllPending(); - } +TEST_F(BufferedDataSourceTest, Range_NotFound) { + Initialize(media::PIPELINE_ERROR_NETWORK); - BufferedResourceLoader* InvokeCacheMissCreateResourceLoader(int64 start, - int64 end) { - NiceMock<MockBufferedResourceLoader>* new_loader = - new NiceMock<MockBufferedResourceLoader>(); + // It'll try again. + // + // TODO(scherkus): don't try again on errors http://crbug.com/105230 + ExpectCreateResourceLoader(); + Respond(response_generator_.Generate404()); - EXPECT_CALL(*new_loader, Start(NotNull(), _, NotNull())) - .WillOnce(DoAll(Assign(&error_, net::OK), - Invoke(this, - &BufferedDataSourceTest::InvokeStartCallback))); + // Now it's done and will fail. + Respond(response_generator_.Generate404()); - EXPECT_CALL(*new_loader, range_supported()) - .WillRepeatedly(Return(loader_->range_supported())); + EXPECT_FALSE(data_source_->loading()); + Stop(); +} - int error = net::ERR_FAILED; - if (cache_miss_count_ < kMaxCacheMissesBeforeFailTest) { - cache_miss_count_++; - error = net::ERR_CACHE_MISS; - } +TEST_F(BufferedDataSourceTest, Range_NotSupported) { + Initialize(media::PIPELINE_OK); - EXPECT_CALL(*new_loader, Read(start, _, NotNull(), NotNull())) - .WillOnce(DoAll(Assign(&error_, error), - Invoke(this, - &BufferedDataSourceTest::InvokeReadCallback))); + // It'll try again. + // + // TODO(scherkus): try to reuse existing connection http://crbug.com/105231 + ExpectCreateResourceLoader(); + Respond(response_generator_.Generate200()); - loader_ = new_loader; - return new_loader; - } + // Now it'll succeed. + EXPECT_CALL(host_, SetLoaded(false)); + EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); + EXPECT_CALL(host_, SetBufferedBytes(0)); + EXPECT_CALL(host_, SetStreaming(true)); + Respond(response_generator_.Generate200()); - void ReadDataSourceAlwaysCacheMiss(int64 position, int size) { - cache_miss_count_ = 0; + EXPECT_TRUE(data_source_->loading()); + Stop(); +} - EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1)) - .WillRepeatedly(Invoke( - this, - &BufferedDataSourceTest::InvokeCacheMissCreateResourceLoader)); +TEST_F(BufferedDataSourceTest, Range_MissingContentRange) { + Initialize(media::PIPELINE_ERROR_NETWORK); - EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull())) - .WillOnce(DoAll(Assign(&error_, net::ERR_CACHE_MISS), - Invoke(this, - &BufferedDataSourceTest::InvokeReadCallback))); + // It'll try again. + // + // TODO(scherkus): don't try again on errors http://crbug.com/105230 + ExpectCreateResourceLoader(); + Respond(response_generator_.Generate206( + 0, TestResponseGenerator::kNoContentRange)); - EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + // Now it's done and will fail. + Respond(response_generator_.Generate206( + 0, TestResponseGenerator::kNoContentRange)); - data_source_->Read( - position, size, buffer_, - base::Bind(&BufferedDataSourceTest::ReadCallback, - base::Unretained(this))); + EXPECT_FALSE(data_source_->loading()); + Stop(); +} - message_loop_->RunAllPending(); +TEST_F(BufferedDataSourceTest, Range_MissingContentLength) { + Initialize(media::PIPELINE_OK); - EXPECT_LT(cache_miss_count_, kMaxCacheMissesBeforeFailTest); - } + // It'll manage without a Content-Length response. + EXPECT_CALL(host_, SetLoaded(false)); + EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); + EXPECT_CALL(host_, SetBufferedBytes(0)); + Respond(response_generator_.Generate206( + 0, TestResponseGenerator::kNoContentLength)); - MOCK_METHOD1(ReadCallback, void(size_t size)); + EXPECT_TRUE(data_source_->loading()); + Stop(); +} - scoped_refptr<NiceMock<MockBufferedResourceLoader> > loader_; - scoped_refptr<MockBufferedDataSource> data_source_; +TEST_F(BufferedDataSourceTest, Range_WrongContentRange) { + Initialize(media::PIPELINE_ERROR_NETWORK); - MockWebFrameClient client_; - WebView* view_; + // It'll try again. + // + // TODO(scherkus): don't try again on errors http://crbug.com/105230 + ExpectCreateResourceLoader(); + Respond(response_generator_.Generate206(1337)); - StrictMock<media::MockFilterHost> host_; - GURL gurl_; - MessageLoop* message_loop_; + // Now it's done and will fail. + Respond(response_generator_.Generate206(1337)); - int error_; - uint8 buffer_[1024]; - uint8 data_[1024]; + EXPECT_FALSE(data_source_->loading()); + Stop(); +} - int cache_miss_count_; +// Test the case where the initial response from the server indicates that +// Range requests are supported, but a later request prove otherwise. +TEST_F(BufferedDataSourceTest, Range_ServerLied) { + InitializeWith206Response(); - private: - DISALLOW_COPY_AND_ASSIGN(BufferedDataSourceTest); -}; + // Read causing a new request to be made -- we'll expect it to error. + ExpectCreateResourceLoader(); + ReadAt(kFarReadPosition); -TEST_F(BufferedDataSourceTest, InitializationSuccess) { - InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING); - StopDataSource(); -} + // Return a 200 in response to a range request. + EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + Respond(response_generator_.Generate200()); -TEST_F(BufferedDataSourceTest, InitiailizationFailed) { - InitializeDataSource(kHttpUrl, net::ERR_FILE_NOT_FOUND, false, 0, NONE); - StopDataSource(); + EXPECT_FALSE(data_source_->loading()); + Stop(); } -TEST_F(BufferedDataSourceTest, MissingContentLength) { - InitializeDataSource(kHttpUrl, net::OK, true, -1, LOADING); - StopDataSource(); -} +TEST_F(BufferedDataSourceTest, Range_AbortWhileReading) { + InitializeWith206Response(); -TEST_F(BufferedDataSourceTest, RangeRequestNotSupported) { - InitializeDataSource(kHttpUrl, net::OK, false, 1024, LOADING); - StopDataSource(); -} + // Make sure there's a pending read -- we'll expect it to error. + ReadAt(0); -// Test the case where we get a 206 response, but no Content-Range header. -TEST_F(BufferedDataSourceTest, MissingContentRange) { - InitializeDataSource(kHttpUrl, net::ERR_INVALID_RESPONSE, true, 1024, - LOADING); - StopDataSource(); -} + // Abort!!! + EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + data_source_->Abort(); + message_loop_->RunAllPending(); -TEST_F(BufferedDataSourceTest, - MissingContentLengthAndRangeRequestNotSupported) { - InitializeDataSource(kHttpUrl, net::OK, false, -1, LOADING); - StopDataSource(); + EXPECT_FALSE(data_source_->loading()); + Stop(); } -TEST_F(BufferedDataSourceTest, ReadCacheHit) { - InitializeDataSource(kHttpUrl, net::OK, true, 25, LOADING); - - // Performs read with cache hit. - ReadDataSourceHit(10, 10, 10); +TEST_F(BufferedDataSourceTest, Range_TooManyRetries) { + InitializeWith206Response(); - // Performs read with cache hit but partially filled. - ReadDataSourceHit(20, 10, 5); + // Make sure there's a pending read -- we'll expect it to error. + ReadAt(0); - StopDataSource(); -} + // It'll try three times. + ExpectCreateResourceLoader(); + FinishLoading(); + Respond(response_generator_.Generate206(0)); -TEST_F(BufferedDataSourceTest, ReadCacheMiss) { - InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING); - ReadDataSourceMiss(1000, 10, net::OK); - ReadDataSourceMiss(20, 10, net::OK); - StopDataSource(); -} + ExpectCreateResourceLoader(); + FinishLoading(); + Respond(response_generator_.Generate206(0)); -// Test the case where the initial response from the server indicates that -// Range requests are supported, but a later request prove otherwise. -TEST_F(BufferedDataSourceTest, ServerLiesAboutRangeSupport) { - InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING); - ReadDataSourceHit(10, 10, 10); - ReadDataSourceMiss(1000, 10, net::ERR_INVALID_RESPONSE); - StopDataSource(); -} + ExpectCreateResourceLoader(); + FinishLoading(); + Respond(response_generator_.Generate206(0)); -TEST_F(BufferedDataSourceTest, ReadHang) { - InitializeDataSource(kHttpUrl, net::OK, true, 25, LOADING); - ReadDataSourceHang(10, 10); - StopDataSource(); -} + // It'll error after this. + EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + FinishLoading(); -TEST_F(BufferedDataSourceTest, ReadFailed) { - InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING); - ReadDataSourceHit(10, 10, 10); - ReadDataSourceFailed(10, 10, net::ERR_CONNECTION_RESET); - StopDataSource(); + EXPECT_FALSE(data_source_->loading()); + Stop(); } -// Helper that sets |*value| to true. Useful for binding into a Closure. static void SetTrue(bool* value) { *value = true; } @@ -537,199 +361,22 @@ static void SetTrue(bool* value) { // object runs on the render message loop, Stop() will not complete if it // requires a task to run on the the message loop that is being blocked. TEST_F(BufferedDataSourceTest, StopDoesNotUseMessageLoopForCallback) { - InitializeDataSource(kFileUrl, net::OK, true, 1024, LOADED); + InitializeWith206Response(); // Stop() the data source, using a callback that lets us verify that it was // called before Stop() returns. This is to make sure that the callback does // not require |message_loop_| to execute tasks before being called. bool stop_done_called = false; + EXPECT_TRUE(data_source_->loading()); data_source_->Stop(base::Bind(&SetTrue, &stop_done_called)); // Verify that the callback was called inside the Stop() call. EXPECT_TRUE(stop_done_called); - message_loop_->RunAllPending(); } -TEST_F(BufferedDataSourceTest, AbortDuringPendingRead) { - InitializeDataSource(kFileUrl, net::OK, true, 1024, LOADED); - - // Setup a way to verify that Read() is not called on the loader. - // We are doing this to make sure that the ReadTask() is still on - // the message loop queue when Abort() is called. - bool read_called = false; - ON_CALL(*loader_, Read(_, _, _ , _)) - .WillByDefault(DoAll(Assign(&read_called, true), - DeleteArg<3>())); - - // Initiate a Read() on the data source, but don't allow the - // message loop to run. - data_source_->Read( - 0, 10, buffer_, - base::Bind(&BufferedDataSourceTest::ReadCallback, - base::Unretained(static_cast<BufferedDataSourceTest*>(this)))); - - // Call Abort() with the read pending. - EXPECT_CALL(*this, ReadCallback(-1)); - EXPECT_CALL(*loader_, Stop()); - data_source_->Abort(); - - // Verify that Read()'s after the Abort() issue callback with an error. - EXPECT_CALL(*this, ReadCallback(-1)); - data_source_->Read( - 0, 10, buffer_, - base::Bind(&BufferedDataSourceTest::ReadCallback, - base::Unretained(static_cast<BufferedDataSourceTest*>(this)))); - - // Stop() the data source like normal. - data_source_->Stop(media::NewExpectedClosure()); - - // Allow cleanup task to run. - message_loop_->RunAllPending(); - - // Verify that Read() was not called on the loader. - EXPECT_FALSE(read_called); -} - -// Test that we only allow a limited number of cache misses for a -// single Read() request. -TEST_F(BufferedDataSourceTest, BoundedCacheMisses) { - InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING); - - ReadDataSourceAlwaysCacheMiss(0, 10); - - StopDataSource(); -} - -// TODO(scherkus): de-dupe from buffered_resource_loader_unittest.cc -ACTION_P(RequestCanceled, loader) { - WebURLError error; - error.reason = net::ERR_ABORTED; - error.domain = WebString::fromUTF8(net::kErrorDomain); - loader->didFail(NULL, error); -} - -// A more realistic BufferedDataSource that uses BufferedResourceLoader instead -// of a mocked version but injects a MockWebURLLoader. -// -// TODO(scherkus): re-write these tests to use this class then drop the "2" -// suffix. -class MockBufferedDataSource2 : public BufferedDataSource { - public: - MockBufferedDataSource2(MessageLoop* message_loop, WebFrame* frame) - : BufferedDataSource(message_loop, frame, new media::MediaLog()), - url_loader_(NULL) { - } - - virtual base::TimeDelta GetTimeoutMilliseconds() { - return base::TimeDelta::FromMilliseconds( - TestTimeouts::tiny_timeout_ms()); - } - - virtual BufferedResourceLoader* CreateResourceLoader(int64 first_position, - int64 last_position) { - loader_ = BufferedDataSource::CreateResourceLoader(first_position, - last_position); - - url_loader_ = new NiceMock<MockWebURLLoader>(); - ON_CALL(*url_loader_, cancel()) - .WillByDefault(RequestCanceled(loader_)); - - loader_->SetURLLoaderForTest(url_loader_); - return loader_; - } - - const scoped_refptr<BufferedResourceLoader>& loader() { return loader_; } - NiceMock<MockWebURLLoader>* url_loader() { return url_loader_; } - - private: - scoped_refptr<BufferedResourceLoader> loader_; - NiceMock<MockWebURLLoader>* url_loader_; - - DISALLOW_COPY_AND_ASSIGN(MockBufferedDataSource2); -}; - -class BufferedDataSourceTest2 : public testing::Test { - public: - BufferedDataSourceTest2() - : view_(WebView::create(NULL)), - message_loop_(MessageLoop::current()) { - view_->initializeMainFrame(&client_); - } - - virtual ~BufferedDataSourceTest2() { - view_->close(); - } - - void InitializeDataSource(const char* url) { - gurl_ = GURL(url); - - data_source_ = new MockBufferedDataSource2(message_loop_, - view_->mainFrame()); - data_source_->set_host(&host_); - data_source_->Initialize(url, - media::NewExpectedStatusCB(media::PIPELINE_OK)); - message_loop_->RunAllPending(); - - // Simulate 206 response for a 5,000,000 byte length file. - WebURLResponse response(gurl_); - response.setHTTPHeaderField(WebString::fromUTF8("Accept-Ranges"), - WebString::fromUTF8("bytes")); - response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"), - WebString::fromUTF8("bytes 0-4999999/5000000")); - response.setHTTPHeaderField(WebString::fromUTF8("Content-Length"), - WebString::fromUTF8("5000000")); - response.setExpectedContentLength(5000000); - response.setHTTPStatusCode(206); - - // We should receive corresponding information about the media resource. - EXPECT_CALL(host_, SetLoaded(false)); - EXPECT_CALL(host_, SetTotalBytes(5000000)); - EXPECT_CALL(host_, SetBufferedBytes(0)); - - data_source_->loader()->didReceiveResponse(data_source_->url_loader(), - response); - - message_loop_->RunAllPending(); - } - - void StopDataSource() { - data_source_->Stop(media::NewExpectedClosure()); - message_loop_->RunAllPending(); - } - - MOCK_METHOD1(ReadCallback, void(size_t size)); - media::DataSource::ReadCallback NewReadCallback(size_t size) { - EXPECT_CALL(*this, ReadCallback(size)); - return base::Bind(&BufferedDataSourceTest2::ReadCallback, - base::Unretained(this)); - } - - // Accessors for private variables on |data_source_|. - media::Preload preload() { return data_source_->preload_; } - BufferedResourceLoader::DeferStrategy defer_strategy() { - return data_source_->loader()->defer_strategy_; - } - int data_source_bitrate() { return data_source_->bitrate_; } - int data_source_playback_rate() { return data_source_->playback_rate_; } - int loader_bitrate() { return data_source_->loader()->bitrate_; } - int loader_playback_rate() { return data_source_->loader()->playback_rate_; } - - scoped_refptr<MockBufferedDataSource2> data_source_; - - GURL gurl_; - MockWebFrameClient client_; - WebView* view_; - - StrictMock<media::MockFilterHost> host_; - MessageLoop* message_loop_; - - private: - DISALLOW_COPY_AND_ASSIGN(BufferedDataSourceTest2); -}; - -TEST_F(BufferedDataSourceTest2, Default) { - InitializeDataSource("http://localhost/foo.webm"); +TEST_F(BufferedDataSourceTest, DefaultValues) { + InitializeWith206Response(); // Ensure we have sane values for default loading scenario. EXPECT_EQ(media::AUTO, preload()); @@ -740,11 +387,12 @@ TEST_F(BufferedDataSourceTest2, Default) { EXPECT_EQ(0, loader_bitrate()); EXPECT_EQ(0.0f, loader_playback_rate()); - StopDataSource(); + EXPECT_TRUE(data_source_->loading()); + Stop(); } -TEST_F(BufferedDataSourceTest2, SetBitrate) { - InitializeDataSource("http://localhost/foo.webm"); +TEST_F(BufferedDataSourceTest, SetBitrate) { + InitializeWith206Response(); data_source_->SetBitrate(1234); message_loop_->RunAllPending(); @@ -752,22 +400,22 @@ TEST_F(BufferedDataSourceTest2, SetBitrate) { EXPECT_EQ(1234, loader_bitrate()); // Read so far ahead to cause the loader to get recreated. - BufferedResourceLoader* old_loader = data_source_->loader(); - - uint8 buffer[1024]; - data_source_->Read(4000000, 1024, buffer, - NewReadCallback(media::DataSource::kReadError)); - message_loop_->RunAllPending(); + BufferedResourceLoader* old_loader = loader(); + ExpectCreateResourceLoader(); + ReadAt(kFarReadPosition); + Respond(response_generator_.Generate206(kFarReadPosition)); // Verify loader changed but still has same bitrate. - EXPECT_NE(old_loader, data_source_->loader().get()); + EXPECT_NE(old_loader, loader()); EXPECT_EQ(1234, loader_bitrate()); - StopDataSource(); + EXPECT_TRUE(data_source_->loading()); + EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + Stop(); } -TEST_F(BufferedDataSourceTest2, SetPlaybackRate) { - InitializeDataSource("http://localhost/foo.webm"); +TEST_F(BufferedDataSourceTest, SetPlaybackRate) { + InitializeWith206Response(); data_source_->SetPlaybackRate(2.0f); message_loop_->RunAllPending(); @@ -775,18 +423,36 @@ TEST_F(BufferedDataSourceTest2, SetPlaybackRate) { EXPECT_EQ(2.0f, loader_playback_rate()); // Read so far ahead to cause the loader to get recreated. - BufferedResourceLoader* old_loader = data_source_->loader(); + BufferedResourceLoader* old_loader = loader(); + ExpectCreateResourceLoader(); + ReadAt(kFarReadPosition); + Respond(response_generator_.Generate206(kFarReadPosition)); - uint8 buffer[1024]; - data_source_->Read(4000000, 1024, buffer, - NewReadCallback(media::DataSource::kReadError)); - message_loop_->RunAllPending(); + // Verify loader changed but still has same playback rate. + EXPECT_NE(old_loader, loader()); - // Verify loader changed but still has same bitrate. - EXPECT_NE(old_loader, data_source_->loader().get()); - EXPECT_EQ(2.0f, loader_playback_rate()); + EXPECT_TRUE(data_source_->loading()); + EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + Stop(); +} + +TEST_F(BufferedDataSourceTest, Read) { + InitializeWith206Response(); + + ReadAt(0); + + // When the read completes we'll update our network status. + EXPECT_CALL(host_, SetBufferedBytes(kDataSize)); + EXPECT_CALL(host_, SetNetworkActivity(true)); + EXPECT_CALL(*this, ReadCallback(kDataSize)); + FinishRead(); + + // During teardown we'll also report our final network status. + EXPECT_CALL(host_, SetBufferedBytes(kDataSize)); + EXPECT_CALL(host_, SetNetworkActivity(false)); - StopDataSource(); + EXPECT_TRUE(data_source_->loading()); + Stop(); } } // namespace webkit_media diff --git a/webkit/media/buffered_resource_loader.h b/webkit/media/buffered_resource_loader.h index e92eb37..4399d00 100644 --- a/webkit/media/buffered_resource_loader.h +++ b/webkit/media/buffered_resource_loader.h @@ -173,7 +173,7 @@ class BufferedResourceLoader virtual ~BufferedResourceLoader(); private: - friend class BufferedDataSourceTest2; + friend class BufferedDataSourceTest; friend class BufferedResourceLoaderTest; // Updates the |buffer_|'s forward and backward capacities. diff --git a/webkit/media/test_response_generator.cc b/webkit/media/test_response_generator.cc new file mode 100644 index 0000000..b3138c0 --- /dev/null +++ b/webkit/media/test_response_generator.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2011 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 "webkit/media/test_response_generator.h" + +#include "base/format_macros.h" +#include "base/string_number_conversions.h" +#include "base/stringprintf.h" +#include "net/base/net_errors.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" + +using WebKit::WebString; +using WebKit::WebURLError; +using WebKit::WebURLResponse; + +namespace webkit_media { + +TestResponseGenerator::TestResponseGenerator(const GURL& gurl, + int64 content_length) + : gurl_(gurl), + content_length_(content_length) { +} + +WebURLError TestResponseGenerator::GenerateError() { + WebURLError error; + error.reason = net::ERR_ABORTED; + error.domain = WebString::fromUTF8(net::kErrorDomain); + return error; +} + +WebURLResponse TestResponseGenerator::Generate200() { + WebURLResponse response(gurl_); + response.setHTTPStatusCode(200); + + response.setHTTPHeaderField( + WebString::fromUTF8("Content-Length"), + WebString::fromUTF8(base::Int64ToString(content_length_))); + response.setExpectedContentLength(content_length_); + return response; +} + +WebURLResponse TestResponseGenerator::Generate206(int64 first_byte_offset) { + return Generate206(first_byte_offset, kNormal); +} + +WebURLResponse TestResponseGenerator::Generate206(int64 first_byte_offset, + Flags flags) { + int64 range_content_length = content_length_ - first_byte_offset; + int64 last_byte_offset = content_length_ - 1; + + WebURLResponse response(gurl_); + response.setHTTPStatusCode(206); + + if ((flags & kNoAcceptRanges) == 0) { + response.setHTTPHeaderField(WebString::fromUTF8("Accept-Ranges"), + WebString::fromUTF8("bytes")); + } + + if ((flags & kNoContentRange) == 0) { + std::string content_range = base::StringPrintf( + "bytes %" PRId64 "-%" PRId64 "/%" PRId64, + first_byte_offset, last_byte_offset, content_length_); + response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"), + WebString::fromUTF8(content_range)); + } + + if ((flags & kNoContentLength) == 0) { + response.setHTTPHeaderField( + WebString::fromUTF8("Content-Length"), + WebString::fromUTF8(base::Int64ToString(range_content_length))); + response.setExpectedContentLength(range_content_length); + } + return response; +} + +WebURLResponse TestResponseGenerator::Generate404() { + WebURLResponse response(gurl_); + response.setHTTPStatusCode(404); + return response; +} + +} // namespace webkit_media diff --git a/webkit/media/test_response_generator.h b/webkit/media/test_response_generator.h new file mode 100644 index 0000000..fbdf5a8 --- /dev/null +++ b/webkit/media/test_response_generator.h @@ -0,0 +1,59 @@ +// Copyright (c) 2011 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. + +#ifndef WEBKIT_MEDIA_TEST_RESPONSE_GENERATOR_H_ +#define WEBKIT_MEDIA_TEST_RESPONSE_GENERATOR_H_ + +#include "base/basictypes.h" +#include "googleurl/src/gurl.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" + +namespace webkit_media { + +// Generates WebURLErrors and WebURLResponses suitable for testing purposes. +class TestResponseGenerator { + public: + enum Flags { + kNormal = 0, + kNoAcceptRanges = 1 << 0, // Don't include Accept-Ranges in 206 response. + kNoContentRange = 1 << 1, // Don't include Content-Range in 206 response. + kNoContentLength = 1 << 2, // Don't include Content-Length in 206 response. + }; + + // Build an HTTP response generator for the given URL. |content_length| is + // used to generate Content-Length and Content-Range headers. + TestResponseGenerator(const GURL& gurl, int64 content_length); + + // Generates a WebURLError object. + WebKit::WebURLError GenerateError(); + + // Generates a regular HTTP 200 response. + WebKit::WebURLResponse Generate200(); + + // Generates a regular HTTP 206 response starting from |first_byte_offset| + // until the end of the resource. + WebKit::WebURLResponse Generate206(int64 first_byte_offset); + + // Generates a custom HTTP 206 response starting from |first_byte_offset| + // until the end of the resource. You can tweak what gets included in the + // headers via |flags|. + WebKit::WebURLResponse Generate206(int64 first_byte_offset, Flags flags); + + // Generates a regular HTTP 404 response. + WebKit::WebURLResponse Generate404(); + + const GURL& gurl() { return gurl_; } + int64 content_length() { return content_length_; } + + private: + GURL gurl_; + int64 content_length_; + + DISALLOW_COPY_AND_ASSIGN(TestResponseGenerator); +}; + +} // namespace webkit_media + +#endif // WEBKIT_MEDIA_TEST_RESPONSE_GENERATOR_H_ diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index 01f5b10..21e1314 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -427,6 +427,8 @@ '../../media/buffered_data_source_unittest.cc', '../../media/buffered_resource_loader_unittest.cc', '../../media/simple_data_source_unittest.cc', + '../../media/test_response_generator.cc', + '../../media/test_response_generator.h', '../../mocks/mock_resource_loader_bridge.h', '../../mocks/mock_webframeclient.h', '../../mocks/mock_weburlloader.cc', |