diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-29 16:48:19 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-29 16:48:19 +0000 |
commit | 91c06e5e57d38760631ed240b1161a2af613b974 (patch) | |
tree | c24a23ab720050396179a46ddc4c64a4426dcbaa /content/test | |
parent | c7fd73291df44ad9c03c04d8aba6e5d694bf2864 (diff) | |
download | chromium_src-91c06e5e57d38760631ed240b1161a2af613b974.zip chromium_src-91c06e5e57d38760631ed240b1161a2af613b974.tar.gz chromium_src-91c06e5e57d38760631ed240b1161a2af613b974.tar.bz2 |
Add a scoper object for URLFetcher::Factory
This should make it much safer to use (i.e. memory-safe).
BUG=90585
Review URL: http://codereview.chromium.org/7524033
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@94694 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/test')
-rw-r--r-- | content/test/test_url_fetcher_factory.cc | 213 | ||||
-rw-r--r-- | content/test/test_url_fetcher_factory.h | 237 |
2 files changed, 450 insertions, 0 deletions
diff --git a/content/test/test_url_fetcher_factory.cc b/content/test/test_url_fetcher_factory.cc new file mode 100644 index 0000000..ad6e512 --- /dev/null +++ b/content/test/test_url_fetcher_factory.cc @@ -0,0 +1,213 @@ +// 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 "content/test/test_url_fetcher_factory.h" + +#include <string> + +#include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "net/url_request/url_request_status.h" + +ScopedURLFetcherFactory::ScopedURLFetcherFactory(URLFetcher::Factory* factory) { + DCHECK(!URLFetcher::factory()); + URLFetcher::set_factory(factory); +} + +ScopedURLFetcherFactory::~ScopedURLFetcherFactory() { + DCHECK(URLFetcher::factory()); + URLFetcher::set_factory(NULL); +} + +TestURLFetcher::TestURLFetcher(int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d) + : URLFetcher(url, request_type, d), + id_(id), + original_url_(url), + did_receive_last_chunk_(false) { +} + +TestURLFetcher::~TestURLFetcher() { +} + +void TestURLFetcher::AppendChunkToUpload(const std::string& data, + bool is_last_chunk) { + DCHECK(!did_receive_last_chunk_); + did_receive_last_chunk_ = is_last_chunk; + chunks_.push_back(data); +} + +void TestURLFetcher::set_status(const net::URLRequestStatus& status) { + fake_status_ = status; +} + +void TestURLFetcher::SetResponseString(const std::string& response) { + SetResponseDestinationForTesting(STRING); + fake_response_string_ = response; +} + +void TestURLFetcher::SetResponseFilePath(const FilePath& path) { + SetResponseDestinationForTesting(TEMP_FILE); + fake_response_file_path_ = path; +} + +bool TestURLFetcher::GetResponseAsString( + std::string* out_response_string) const { + if (GetResponseDestinationForTesting() != STRING) + return false; + + *out_response_string = fake_response_string_; + return true; +} + +bool TestURLFetcher::GetResponseAsFilePath( + bool take_ownership, FilePath* out_response_path) const { + if (GetResponseDestinationForTesting() != TEMP_FILE) + return false; + + *out_response_path = fake_response_file_path_; + return true; +} + +TestURLFetcherFactory::TestURLFetcherFactory() + : ScopedURLFetcherFactory(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +TestURLFetcherFactory::~TestURLFetcherFactory() {} + +URLFetcher* TestURLFetcherFactory::CreateURLFetcher( + int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d) { + TestURLFetcher* fetcher = new TestURLFetcher(id, url, request_type, d); + fetchers_[id] = fetcher; + return fetcher; +} + +TestURLFetcher* TestURLFetcherFactory::GetFetcherByID(int id) const { + Fetchers::const_iterator i = fetchers_.find(id); + return i == fetchers_.end() ? NULL : i->second; +} + +void TestURLFetcherFactory::RemoveFetcherFromMap(int id) { + Fetchers::iterator i = fetchers_.find(id); + DCHECK(i != fetchers_.end()); + fetchers_.erase(i); +} + +const GURL& TestURLFetcher::url() const { + return fake_url_; +} + +const net::URLRequestStatus& TestURLFetcher::status() const { + return fake_status_; +} + +int TestURLFetcher::response_code() const { + return fake_response_code_; +} + +// This class is used by the FakeURLFetcherFactory below. +class FakeURLFetcher : public URLFetcher { + public: + // Normal URL fetcher constructor but also takes in a pre-baked response. + FakeURLFetcher(const GURL& url, RequestType request_type, Delegate* d, + const std::string& response_data, bool success) + : URLFetcher(url, request_type, d), + url_(url), + response_data_(response_data), + success_(success), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { + } + + // Start the request. This will call the given delegate asynchronously + // with the pre-baked response as parameter. + virtual void Start() { + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod(&FakeURLFetcher::RunDelegate)); + } + + private: + virtual ~FakeURLFetcher() { + } + + // This is the method which actually calls the delegate that is passed in the + // constructor. + void RunDelegate() { + net::URLRequestStatus status; + status.set_status(success_ ? net::URLRequestStatus::SUCCESS : + net::URLRequestStatus::FAILED); + delegate()->OnURLFetchComplete(this, url_, status, success_ ? 200 : 500, + net::ResponseCookies(), response_data_); + } + + // Pre-baked response data and flag which indicates whether the request should + // be successful or not. + GURL url_; + std::string response_data_; + bool success_; + + // Method factory used to run the delegate. + ScopedRunnableMethodFactory<FakeURLFetcher> method_factory_; + + DISALLOW_COPY_AND_ASSIGN(FakeURLFetcher); +}; + +FakeURLFetcherFactory::FakeURLFetcherFactory() + : ScopedURLFetcherFactory(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +FakeURLFetcherFactory::FakeURLFetcherFactory( + URLFetcher::Factory* default_factory) + : ScopedURLFetcherFactory(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + default_factory_(default_factory) { +} + +FakeURLFetcherFactory::~FakeURLFetcherFactory() {} + +URLFetcher* FakeURLFetcherFactory::CreateURLFetcher( + int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d) { + FakeResponseMap::const_iterator it = fake_responses_.find(url); + if (it == fake_responses_.end()) { + if (default_factory_ == NULL) { + // If we don't have a baked response for that URL we return NULL. + DLOG(ERROR) << "No baked response for URL: " << url.spec(); + return NULL; + } else { + return default_factory_->CreateURLFetcher(id, url, request_type, d); + } + } + return new FakeURLFetcher(url, request_type, d, + it->second.first, it->second.second); +} + +void FakeURLFetcherFactory::SetFakeResponse(const std::string& url, + const std::string& response_data, + bool success) { + // Overwrite existing URL if it already exists. + fake_responses_[GURL(url)] = std::make_pair(response_data, success); +} + +void FakeURLFetcherFactory::ClearFakeReponses() { + fake_responses_.clear(); +} + +URLFetcherFactory::URLFetcherFactory() {} + +URLFetcherFactory::~URLFetcherFactory() {} + +URLFetcher* URLFetcherFactory::CreateURLFetcher( + int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d) { + return new URLFetcher(url, request_type, d); +} diff --git a/content/test/test_url_fetcher_factory.h b/content/test/test_url_fetcher_factory.h new file mode 100644 index 0000000..f402875 --- /dev/null +++ b/content/test/test_url_fetcher_factory.h @@ -0,0 +1,237 @@ +// 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 CONTENT_TEST_TEST_URL_FETCHER_FACTORY_H_ +#define CONTENT_TEST_TEST_URL_FETCHER_FACTORY_H_ +#pragma once + +#include <list> +#include <map> +#include <string> +#include <utility> + +#include "base/threading/non_thread_safe.h" +#include "content/common/url_fetcher.h" +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request_status.h" + +// Changes URLFetcher's Factory for the lifetime of the object. +// Note that this scoper cannot be nested (to make it even harder to misuse). +class ScopedURLFetcherFactory : public base::NonThreadSafe { + public: + explicit ScopedURLFetcherFactory(URLFetcher::Factory* factory); + virtual ~ScopedURLFetcherFactory(); + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedURLFetcherFactory); +}; + +// TestURLFetcher and TestURLFetcherFactory are used for testing consumers of +// URLFetcher. TestURLFetcherFactory is a URLFetcher::Factory that creates +// TestURLFetchers. TestURLFetcher::Start is overriden to do nothing. It is +// expected that you'll grab the delegate from the TestURLFetcher and invoke +// the callback method when appropriate. In this way it's easy to mock a +// URLFetcher. +// Typical usage: +// // TestURLFetcher requires a MessageLoop: +// MessageLoopForUI message_loop; +// // And io_thread to release URLRequestContextGetter in URLFetcher::Core. +// BrowserThread io_thread(BrowserThread::IO, &message_loop); +// // Create factory (it automatically sets itself as URLFetcher's factory). +// TestURLFetcherFactory factory; +// // Do something that triggers creation of a URLFetcher. +// TestURLFetcher* fetcher = factory.GetFetcherByID(expected_id); +// DCHECK(fetcher); +// // Notify delegate with whatever data you want. +// fetcher->delegate()->OnURLFetchComplete(...); +// // Make sure consumer of URLFetcher does the right thing. +// ... +// +// Note: if you don't know when your request objects will be created you +// might want to use the FakeUrlFetcher and FakeUrlFetcherFactory classes +// below. + +class TestURLFetcher : public URLFetcher { + public: + TestURLFetcher(int id, + const GURL& url, + RequestType request_type, + Delegate* d); + virtual ~TestURLFetcher(); + + // Overriden to do nothing. It is assumed the caller will notify the delegate. + virtual void Start() {} + + // Overriden to cache the chunks uploaded. Caller can read back the uploaded + // chunks with the upload_data() accessor. + virtual void AppendChunkToUpload(const std::string& data, bool is_last_chunk); + + // Unique ID in our factory. + int id() const { return id_; } + + // URL we were created with. Because of how we're using URLFetcher url() + // always returns an empty URL. Chances are you'll want to use original_url() + // in your tests. + const GURL& original_url() const { return original_url_; } + + // Returns the data uploaded on this URLFetcher. + const std::string& upload_data() const { return URLFetcher::upload_data(); } + + // Returns the chunks of data uploaded on this URLFetcher. + const std::list<std::string>& upload_chunks() const { return chunks_; } + + // Returns the delegate installed on the URLFetcher. + Delegate* delegate() const { return URLFetcher::delegate(); } + + void set_url(const GURL& url) { fake_url_ = url; } + virtual const GURL& url() const; + + void set_status(const net::URLRequestStatus& status); + virtual const net::URLRequestStatus& status() const; + + void set_response_code(int response_code) { + fake_response_code_ = response_code; + } + virtual int response_code() const; + + // Set string data. + void SetResponseString(const std::string& response); + + // Set File data. + void SetResponseFilePath(const FilePath& path); + + // Override response access functions to return fake data. + virtual bool GetResponseAsString(std::string* out_response_string) const; + virtual bool GetResponseAsFilePath(bool take_ownership, + FilePath* out_response_path) const; + + private: + const int id_; + const GURL original_url_; + std::list<std::string> chunks_; + bool did_receive_last_chunk_; + + // User can use set_* methods to provide values returned by getters. + // Setting the real values is not possible, because the real class + // has no setters. The data is a private member of a class defined + // in a .cc file, so we can't get at it with friendship. + GURL fake_url_; + net::URLRequestStatus fake_status_; + int fake_response_code_; + std::string fake_response_string_; + FilePath fake_response_file_path_; + + DISALLOW_COPY_AND_ASSIGN(TestURLFetcher); +}; + +// Simple URLFetcher::Factory method that creates TestURLFetchers. All fetchers +// are registered in a map by the id passed to the create method. +class TestURLFetcherFactory : public URLFetcher::Factory, + public ScopedURLFetcherFactory { + public: + TestURLFetcherFactory(); + virtual ~TestURLFetcherFactory(); + + virtual URLFetcher* CreateURLFetcher(int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d); + TestURLFetcher* GetFetcherByID(int id) const; + void RemoveFetcherFromMap(int id); + + private: + // Maps from id passed to create to the returned URLFetcher. + typedef std::map<int, TestURLFetcher*> Fetchers; + Fetchers fetchers_; + + DISALLOW_COPY_AND_ASSIGN(TestURLFetcherFactory); +}; + +// The FakeUrlFetcher and FakeUrlFetcherFactory classes are similar to the +// ones above but don't require you to know when exactly the URLFetcher objects +// will be created. +// +// These classes let you set pre-baked HTTP responses for particular URLs. +// E.g., if the user requests http://a.com/ then respond with an HTTP/500. +// +// We assume that the thread that is calling Start() on the URLFetcher object +// has a message loop running. +// +// This class is not thread-safe. You should not call SetFakeResponse or +// ClearFakeResponse at the same time you call CreateURLFetcher. However, it is +// OK to start URLFetcher objects while setting or clearning fake responses +// since already created URLFetcher objects will not be affected by any changes +// made to the fake responses (once a URLFetcher object is created you cannot +// change its fake response). +// +// Example usage: +// FakeURLFetcherFactory factory; +// +// // You know that class SomeService will request url http://a.com/ and you +// // want to test the service class by returning an error. +// factory.SetFakeResponse("http://a.com/", "", false); +// // But if the service requests http://b.com/asdf you want to respond with +// // a simple html page and an HTTP/200 code. +// factory.SetFakeResponse("http://b.com/asdf", +// "<html><body>hello world</body></html>", +// true); +// +// SomeService service; +// service.Run(); // Will eventually request these two URLs. + +class FakeURLFetcherFactory : public URLFetcher::Factory, + public ScopedURLFetcherFactory { + public: + FakeURLFetcherFactory(); + // FakeURLFetcherFactory that will delegate creating URLFetcher for unknown + // url to the given factory. + explicit FakeURLFetcherFactory(URLFetcher::Factory* default_factory); + virtual ~FakeURLFetcherFactory(); + + // If no fake response is set for the given URL this method will delegate the + // call to |default_factory_| if it is not NULL, or return NULL if it is + // NULL. + // Otherwise, it will return a URLFetcher object which will respond with the + // pre-baked response that the client has set by calling SetFakeResponse(). + virtual URLFetcher* CreateURLFetcher(int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d); + + // Sets the fake response for a given URL. If success is true we will serve + // an HTTP/200 and an HTTP/500 otherwise. The |response_data| may be empty. + void SetFakeResponse(const std::string& url, + const std::string& response_data, + bool success); + + // Clear all the fake responses that were previously set via + // SetFakeResponse(). + void ClearFakeReponses(); + + private: + typedef std::map<GURL, std::pair<std::string, bool> > FakeResponseMap; + FakeResponseMap fake_responses_; + URLFetcher::Factory* default_factory_; + + DISALLOW_COPY_AND_ASSIGN(FakeURLFetcherFactory); +}; + +// This is an implementation of URLFetcher::Factory that will create a real +// URLFetcher. It can be use in conjunction with a FakeURLFetcherFactory in +// integration tests to control the behavior of some requests but execute +// all the other ones. +class URLFetcherFactory : public URLFetcher::Factory { + public: + URLFetcherFactory(); + virtual ~URLFetcherFactory(); + + // This method will create a real URLFetcher. + virtual URLFetcher* CreateURLFetcher(int id, + const GURL& url, + URLFetcher::RequestType request_type, + URLFetcher::Delegate* d); + +}; + +#endif // CONTENT_TEST_TEST_URL_FETCHER_FACTORY_H_ |