Chromium Code Reviews| Index: net/proxy/proxy_config_service_android_unittest.cc |
| diff --git a/net/proxy/proxy_config_service_android_unittest.cc b/net/proxy/proxy_config_service_android_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d986beff1924eeb25d751a725b0849f1c801b158 |
| --- /dev/null |
| +++ b/net/proxy/proxy_config_service_android_unittest.cc |
| @@ -0,0 +1,384 @@ |
| +// Copyright (c) 2012 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 <map> |
| +#include <string> |
| + |
| +#include "base/bind.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/synchronization/lock.h" |
| +#include "base/threading/platform_thread.h" |
| +#include "base/threading/thread.h" |
| +#include "net/proxy/proxy_config.h" |
| +#include "net/proxy/proxy_config_service_android.h" |
| +#include "net/proxy/proxy_info.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace net { |
| + |
| +namespace { |
| + |
| +typedef std::map<std::string, std::string> StringMap; |
| + |
| +class TestDelegate : public ProxyConfigServiceAndroid::Delegate { |
| + public: |
| + TestDelegate() : service_(NULL) {} |
| + |
| + // ProxyConfigServiceAndroid::Delegate: |
| + virtual void Start(ProxyConfigServiceAndroid* service) OVERRIDE { |
| + base::AutoLock lock(lock_); |
| + service_ = service; |
| + } |
| + |
| + virtual void Stop() OVERRIDE {} |
| + |
| + virtual std::string GetProperty(const std::string& property) OVERRIDE { |
| + base::AutoLock lock(lock_); |
| + StringMap::iterator it = properties_.find(property); |
| + if (it == properties_.end()) |
| + return ""; |
| + return it->second; |
| + } |
| + |
| + // For testing: |
| + void SetProperties(const StringMap& properties) { |
| + { |
| + base::AutoLock lock(lock_); |
| + properties_ = properties; |
| + } |
| + DCHECK(service_); |
| + service_->ProxySettingsChanged(); |
| + } |
| + |
| + // Waits until Start() is called by the observer thread. |
| + void WaitUntilReady() { |
| + while (true) { |
| + { |
| + base::AutoLock lock(lock_); |
| + if (service_) |
| + return; |
| + } |
| + base::PlatformThread::YieldCurrentThread(); |
| + } |
| + } |
| + |
| + private: |
| + base::Lock lock_; |
| + StringMap properties_; |
| + ProxyConfigServiceAndroid* service_; |
| +}; |
| + |
| +class TestObserver : public ProxyConfigService::Observer { |
| + public: |
| + TestObserver() : main_thread_loop_(MessageLoop::current()) {} |
| + |
| + // ProxyConfigService::Observer: |
| + virtual void OnProxyConfigChanged( |
| + const ProxyConfig& config, |
| + ProxyConfigService::ConfigAvailability availability) OVERRIDE { |
| + // Called from observer thread. |
| + main_thread_loop_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&TestObserver::UpdateConfig, |
| + base::Unretained(this), config, availability)); |
| + } |
| + |
| + ProxyConfigService::ConfigAvailability availability() const { |
| + return availability_; |
| + } |
| + |
| + const ProxyConfig& config() const { |
| + return config_; |
| + } |
| + |
| + private: |
| + void UpdateConfig(const ProxyConfig& config, |
| + ProxyConfigService::ConfigAvailability availability) { |
| + config_ = config; |
| + availability_ = availability; |
| + main_thread_loop_->Quit(); |
| + } |
| + |
| + MessageLoop* const main_thread_loop_; |
| + ProxyConfig config_; |
| + ProxyConfigService::ConfigAvailability availability_; |
| +}; |
| + |
| +class TestObserverThread : public base::Thread { |
| + public: |
| + TestObserverThread(ProxyConfigService::Observer* observer) |
| + : base::Thread("observer_thread"), |
| + observer_(observer) { |
| + } |
| + |
| + virtual ~TestObserverThread() { |
| + // This is needed to make sure that CleanUp() is called. See |
| + // base::Thread::~Thread() for details. |
| + base::Thread::Stop(); |
|
Ryan Sleevi
2012/05/07 17:12:20
This means you're calling a virtual function (Clea
Philippe
2012/05/09 11:48:40
There is a non-trivial comment in thread.h for Thr
|
| + } |
| + |
| + virtual void CleanUp() OVERRIDE { |
| + DCHECK(service_.get()); |
| + service_->RemoveObserver(observer_); |
| + service_.reset(); |
| + } |
| + |
| + // Takes ownership of |service|. |
| + void SetService(ProxyConfigServiceAndroid* service) { |
| + service_.reset(service); |
| + // Call AddObserver() on the observer thread now that the service is set. |
| + message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&TestObserverThread::AddObserver, base::Unretained(this))); |
| + } |
| + |
| + private: |
| + void AddObserver() { |
| + service_->AddObserver(observer_); |
| + } |
| + |
| + scoped_ptr<ProxyConfigServiceAndroid> service_; |
| + ProxyConfigService::Observer* const observer_; |
| +}; |
| + |
| +std::string ToString(const ProxyInfo& proxy_info) { |
| + ProxyInfo proxy_info_copy(proxy_info); |
| + BoundNetLog net_log; |
| + std::string result; |
| + while (!proxy_info_copy.is_empty()) { |
| + if (!result.empty()) |
| + result += ";"; |
| + if (!proxy_info_copy.is_direct()) |
| + result += proxy_info_copy.proxy_server().host_port_pair().ToString(); |
| + proxy_info_copy.Fallback(net_log); |
| + } |
| + return result; |
| +} |
| + |
| +} // namespace |
| + |
| +class ProxyConfigServiceAndroidTest : public testing::Test { |
| + protected: |
| + ProxyConfigServiceAndroidTest() : service_(NULL) {} |
| + |
| + // testing::Test |
| + virtual void SetUp() OVERRIDE { |
| + configuration_.clear(); |
| + // Note that |service_| takes ownership of |delegate_|. |
| + delegate_ = new TestDelegate(); |
| + observer_.reset(new TestObserver()); |
| + observer_thread_.reset(new TestObserverThread(observer_.get())); |
| + // Start the observer thread now so that its message loop (used below) is |
| + // initialized. |
| + observer_thread_->Start(); |
| + service_ = new ProxyConfigServiceAndroid( |
| + observer_thread_->message_loop_proxy(), delegate_); |
| + // Note that |observer_thread_| takes ownership of |service_|. |
| + observer_thread_->SetService(service_); |
| + // Block until the observer thread calls TestDelegate::Start(). |
| + delegate_->WaitUntilReady(); |
| + } |
| + |
| + void TestMapping(const std::string& url, const std::string& expected) { |
| + ProxyConfig config; |
| + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, |
| + service_->GetLatestProxyConfig(&config)); |
| + ProxyInfo proxy_info; |
| + config.proxy_rules().Apply(GURL(url), &proxy_info); |
| + EXPECT_EQ(expected, ToString(proxy_info)); |
| + } |
| + |
| + scoped_ptr<TestObserver> observer_; |
| + TestDelegate* delegate_; |
| + ProxyConfigServiceAndroid* service_; |
| + scoped_ptr<TestObserverThread> observer_thread_; |
| + StringMap configuration_; |
| +}; |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, TestChangePropertiesNotification) { |
| + // Set up a non-empty configuration |
| + StringMap properties; |
| + properties["http.proxyHost"] = "localhost"; |
| + delegate_->SetProperties(properties); |
| + MessageLoop::current()->Run(); |
| + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, observer_->availability()); |
| + EXPECT_FALSE(observer_->config().proxy_rules().empty()); |
| + |
| + // Set up an empty configuration |
| + delegate_->SetProperties(StringMap()); |
| + MessageLoop::current()->Run(); |
| + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, observer_->availability()); |
| + EXPECT_TRUE(observer_->config().proxy_rules().empty()); |
| +} |
| + |
| +// !! The following test cases are automatically generated from |
| +// !! net/android/tools/proxy_test_cases.py. |
| +// !! Please edit that file instead of editing the test cases below and |
| +// !! update also the corresponding Java unit tests in |
| +// !! AndroidProxySelectorTest.java |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, NoProxy) { |
| + // Test direct mapping when no proxy defined. |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("https://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndPort) { |
| + // Test http.proxyHost and http.proxyPort works. |
| + configuration_["http.proxyHost"] = "httpproxy.com"; |
| + configuration_["http.proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", "httpproxy.com:8080"); |
| + TestMapping("https://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostOnly) { |
| + // We should get the default port (80) for proxied hosts. |
| + configuration_["http.proxyHost"] = "httpproxy.com"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", "httpproxy.com:80"); |
| + TestMapping("https://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyPortOnly) { |
| + // http.proxyPort only should not result in any hosts being proxied. |
| + configuration_["http.proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("https://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts1) { |
| + // Test that HTTP non proxy hosts are mapped correctly |
| + configuration_["http.nonProxyHosts"] = "slashdot.org"; |
| + configuration_["http.proxyHost"] = "httpproxy.com"; |
| + configuration_["http.proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("http://example.com/", "httpproxy.com:8080"); |
| + TestMapping("http://slashdot.org/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts2) { |
| + // Test that | pattern works. |
| + configuration_["http.nonProxyHosts"] = "slashdot.org|freecode.net"; |
| + configuration_["http.proxyHost"] = "httpproxy.com"; |
| + configuration_["http.proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("http://example.com/", "httpproxy.com:8080"); |
| + TestMapping("http://freecode.net/", ""); |
| + TestMapping("http://slashdot.org/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts3) { |
| + // Test that * pattern works. |
| + configuration_["http.nonProxyHosts"] = "*example.com"; |
| + configuration_["http.proxyHost"] = "httpproxy.com"; |
| + configuration_["http.proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("http://slashdot.org/", "httpproxy.com:8080"); |
| + TestMapping("http://www.example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, FtpNonProxyHosts) { |
| + // Test that FTP non proxy hosts are mapped correctly |
| + configuration_["ftp.nonProxyHosts"] = "slashdot.org"; |
| + configuration_["ftp.proxyHost"] = "httpproxy.com"; |
| + configuration_["ftp.proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", "httpproxy.com:8080"); |
| + TestMapping("http://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, FtpProxyHostAndPort) { |
| + // Test ftp.proxyHost and ftp.proxyPort works. |
| + configuration_["ftp.proxyHost"] = "httpproxy.com"; |
| + configuration_["ftp.proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", "httpproxy.com:8080"); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("https://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, FtpProxyHostOnly) { |
| + // Test ftp.proxyHost and default port. |
| + configuration_["ftp.proxyHost"] = "httpproxy.com"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", "httpproxy.com:80"); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("https://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpsProxyHostAndPort) { |
| + // Test https.proxyHost and https.proxyPort works. |
| + configuration_["https.proxyHost"] = "httpproxy.com"; |
| + configuration_["https.proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("https://example.com/", "httpproxy.com:8080"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpsProxyHostOnly) { |
| + // Test https.proxyHost and default port. |
| + configuration_["https.proxyHost"] = "httpproxy.com"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("https://example.com/", "httpproxy.com:443"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, DefaultProxyExplictPort) { |
| + // Default http proxy is used if a scheme-specific one is not found. |
| + configuration_["ftp.proxyHost"] = "httpproxy.com"; |
| + configuration_["ftp.proxyPort"] = "8080"; |
| + configuration_["proxyHost"] = "defaultproxy.com"; |
| + configuration_["proxyPort"] = "8080"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com/", "httpproxy.com:8080"); |
| + TestMapping("http://example.com/", "defaultproxy.com:8080"); |
| + TestMapping("https://example.com/", "defaultproxy.com:8080"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, DefaultProxyDefaultPort) { |
| + // Check that the default proxy port is as expected. |
| + configuration_["proxyHost"] = "defaultproxy.com"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("http://example.com/", "defaultproxy.com:80"); |
| + TestMapping("https://example.com/", "defaultproxy.com:443"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, FallbackToSocks) { |
| + // SOCKS proxy is used if scheme-specific one is not found. |
| + configuration_["http.proxyHost"] = "defaultproxy.com"; |
| + configuration_["socksProxyHost"] = "socksproxy.com"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("ftp://example.com", "socksproxy.com:1080"); |
| + TestMapping("http://example.com/", "defaultproxy.com:80"); |
| + TestMapping("https://example.com/", "socksproxy.com:1080"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, SocksExplicitPort) { |
| + // SOCKS proxy port is used if specified |
| + configuration_["socksProxyHost"] = "socksproxy.com"; |
| + configuration_["socksProxyPort"] = "9000"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("http://example.com/", "socksproxy.com:9000"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxySupercedesSocks) { |
| + // SOCKS proxy is ignored if default HTTP proxy defined. |
| + configuration_["proxyHost"] = "defaultproxy.com"; |
| + configuration_["socksProxyHost"] = "socksproxy.com"; |
| + configuration_["socksProxyPort"] = "9000"; |
| + delegate_->SetProperties(configuration_); |
| + TestMapping("http://example.com/", "defaultproxy.com:80"); |
| +} |
| + |
| +} // namespace net |