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..e3c0f90ab19c5b11cb0d1a191658450d3846cc79 |
| --- /dev/null |
| +++ b/net/proxy/proxy_config_service_android_unittest.cc |
| @@ -0,0 +1,457 @@ |
| +// 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/compiler_specific.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/message_loop.h" |
| +#include "base/synchronization/waitable_event.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 { |
| + |
| +scoped_ptr<base::WaitableEvent> MakeWaitableEvent() { |
| + return scoped_ptr<base::WaitableEvent>( |
| + new base::WaitableEvent( |
| + false /* automatic reset */, false /* not signaled initialy */)); |
| +} |
| + |
| +class TestObserver : public ProxyConfigService::Observer { |
| + public: |
| + TestObserver(base::WaitableEvent* proxy_config_changed_event) |
| + : main_thread_loop_(MessageLoop::current()), |
| + proxy_config_changed_event_(proxy_config_changed_event) {} |
| + |
| + // ProxyConfigService::Observer: |
| + virtual void OnProxyConfigChanged( |
| + const ProxyConfig& config, |
| + ProxyConfigService::ConfigAvailability availability) OVERRIDE { |
| + // Called from network thread. |
| + main_thread_loop_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&TestObserver::UpdateConfig, |
| + base::Unretained(this), config, availability)); |
| + proxy_config_changed_event_->Signal(); |
| + } |
| + |
| + // Called from main thread. |
| + 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_; |
| + base::WaitableEvent* const proxy_config_changed_event_; |
| + // Accessed only from the main thread. |
| + ProxyConfig config_; |
| + ProxyConfigService::ConfigAvailability availability_; |
| +}; |
| + |
| +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; |
|
Ryan Sleevi
2012/05/25 17:42:14
Use ProxyInfo.ToPacString() - this whole loop is a
Philippe
2012/05/29 10:05:30
Done.
|
| +} |
| + |
| +} // namespace |
| + |
| +class ProxyConfigServiceAndroidTest : public testing::Test { |
| + protected: |
| + ProxyConfigServiceAndroidTest() : service_(NULL) {} |
| + |
| + typedef std::map<std::string, std::string> StringMap; |
| + |
| + // testing::Test: |
| + virtual void SetUp() OVERRIDE { |
| + proxy_config_changed_event_ = MakeWaitableEvent(); |
| + observer_.reset(new TestObserver(proxy_config_changed_event_.get())); |
| + |
| + jni_thread_.reset(new base::Thread("jni_thread")); |
| + network_thread_.reset(new base::Thread("network_thread")); |
| + |
| + jni_thread_->Start(); |
| + network_thread_->Start(); |
| + |
| + ClearConfiguration(); |
| + service_.reset(new ProxyConfigServiceAndroid( |
| + network_thread_->message_loop_proxy(), |
| + jni_thread_->message_loop_proxy(), |
| + base::Bind(&ProxyConfigServiceAndroidTest::GetPropertyOnJNIThread, |
| + base::Unretained(this)))); |
| + |
| + AddObserver(); |
| + } |
| + |
| + virtual void TearDown() OVERRIDE { |
| + service_.reset(); |
| + network_thread_.reset(); |
| + jni_thread_.reset(); |
| + proxy_config_changed_event_.reset(); |
| + } |
| + |
| + void AddProperty(const std::string& key, const std::string& value) { |
| + jni_thread_->message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&ProxyConfigServiceAndroidTest::AddPropertyOnJNIThread, |
| + base::Unretained(this), key, value)); |
| + } |
| + |
| + void ClearConfiguration() { |
| + jni_thread_->message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&StringMap::clear, base::Unretained(&configuration_))); |
| + } |
| + |
| + void ProxySettingsChanged() { |
| + jni_thread_->message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&ProxyConfigServiceAndroid::ProxySettingsChanged, |
| + base::Unretained(service_.get()), static_cast<JNIEnv*>(NULL), |
| + static_cast<jobject>(NULL))); |
| + proxy_config_changed_event_->Wait(); |
| + MessageLoop::current()->Run(); |
| + } |
| + |
| + ProxyConfigService::ConfigAvailability GetLatestProxyConfig( |
| + ProxyConfig* proxy_config) { |
| + // Post ProxyConfigServiceAndroid::GetLatestProxyConfig() on the network |
| + // thread and wait for its completion. |
| + scoped_ptr<base::WaitableEvent> completion_event(MakeWaitableEvent()); |
| + ProxyConfigService::ConfigAvailability availability; |
| + network_thread_->message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind( |
| + &ProxyConfigServiceAndroidTest::GetLatestProxyConfigOnNetworkThread, |
| + base::Unretained(this), proxy_config, &availability, |
| + completion_event.get())); |
| + completion_event->Wait(); |
| + return availability; |
| + } |
| + |
| + void TestMapping(const std::string& url, const std::string& expected) { |
| + ProxyConfig proxy_config = observer_->config(); |
| + TestMappingForProxyConfig( |
| + url, expected, observer_->availability(), &proxy_config); |
| + } |
| + |
| + static void TestMappingForProxyConfig( |
| + const std::string& url, |
| + const std::string& expected, |
| + ProxyConfigService::ConfigAvailability availability, |
| + ProxyConfig* proxy_config) { |
| + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, availability); |
| + ProxyInfo proxy_info; |
| + proxy_config->proxy_rules().Apply(GURL(url), &proxy_info); |
| + EXPECT_EQ(expected, ToString(proxy_info)); |
| + } |
| + |
| + // Accessed only from the JNI thread. |
| + StringMap configuration_; |
| + |
| + scoped_ptr<TestObserver> observer_; |
| + |
| + private: |
| + std::string GetPropertyOnJNIThread(const std::string& property) { |
| + DCHECK(OnJNIThread()); |
| + StringMap::const_iterator it = configuration_.find(property); |
| + if (it == configuration_.end()) |
| + return ""; |
| + return it->second; |
| + } |
| + |
| + void AddPropertyOnJNIThread(const std::string& key, |
| + const std::string& value) { |
| + DCHECK(OnJNIThread()); |
| + configuration_[key] = value; |
| + } |
| + |
| + void AddObserver() { |
| + jni_thread_->message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind( |
| + &ProxyConfigServiceAndroidTest::AddObserverAfterInitialConfigIsSet, |
| + base::Unretained(this))); |
| + } |
| + |
| + void AddObserverAfterInitialConfigIsSet() { |
| + DCHECK(OnJNIThread()); |
| + network_thread_->message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&ProxyConfigServiceAndroid::AddObserver, |
| + base::Unretained(service_.get()), observer_.get())); |
| + } |
| + |
| + void GetLatestProxyConfigOnNetworkThread( |
| + ProxyConfig* proxy_config, |
| + ProxyConfigService::ConfigAvailability* availability, |
| + base::WaitableEvent* completion_event) { |
| + DCHECK(OnNetworkThread()); |
| + *availability = service_->GetLatestProxyConfig(proxy_config); |
| + completion_event->Signal(); |
| + } |
| + |
| + bool OnJNIThread() const { |
| + return jni_thread_->message_loop_proxy()->BelongsToCurrentThread(); |
| + } |
| + |
| + bool OnNetworkThread() const { |
| + return network_thread_->message_loop_proxy()->BelongsToCurrentThread(); |
| + } |
| + |
| + scoped_ptr<base::WaitableEvent> proxy_config_changed_event_; |
| + scoped_ptr<base::Thread> jni_thread_; |
| + scoped_ptr<base::Thread> network_thread_; |
| + scoped_ptr<ProxyConfigServiceAndroid> service_; |
| +}; |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, TestChangePropertiesNotification) { |
| + // Set up a non-empty configuration |
| + AddProperty("http.proxyHost", "localhost"); |
| + ProxySettingsChanged(); |
| + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, observer_->availability()); |
| + EXPECT_FALSE(observer_->config().proxy_rules().empty()); |
| + |
| + // Set up an empty configuration |
| + ClearConfiguration(); |
| + ProxySettingsChanged(); |
| + EXPECT_EQ(ProxyConfigService::CONFIG_VALID, observer_->availability()); |
| + EXPECT_TRUE(observer_->config().proxy_rules().empty()); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, GetLatestConfigWithoutUsingObserver) { |
| + AddProperty("http.proxyHost", "httpproxy.com"); |
| + AddProperty("http.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + ProxyConfig proxy_config; |
| + ProxyConfigService::ConfigAvailability availability = |
| + GetLatestProxyConfig(&proxy_config); |
| + TestMappingForProxyConfig( |
| + "http://example.com/", "httpproxy.com:8080", availability, &proxy_config); |
| +} |
| + |
| +// !! 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. |
| + ProxySettingsChanged(); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("https://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndPort) { |
| + // Test http.proxyHost and http.proxyPort works. |
| + AddProperty("http.proxyHost", "httpproxy.com"); |
| + AddProperty("http.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + 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. |
| + AddProperty("http.proxyHost", "httpproxy.com"); |
| + ProxySettingsChanged(); |
| + 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. |
| + AddProperty("http.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + 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 |
| + AddProperty("http.nonProxyHosts", "slashdot.org"); |
| + AddProperty("http.proxyHost", "httpproxy.com"); |
| + AddProperty("http.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + TestMapping("http://example.com/", "httpproxy.com:8080"); |
| + TestMapping("http://slashdot.org/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts2) { |
| + // Test that | pattern works. |
| + AddProperty("http.nonProxyHosts", "slashdot.org|freecode.net"); |
| + AddProperty("http.proxyHost", "httpproxy.com"); |
| + AddProperty("http.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + TestMapping("http://example.com/", "httpproxy.com:8080"); |
| + TestMapping("http://freecode.net/", ""); |
| + TestMapping("http://slashdot.org/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpNonProxyHosts3) { |
| + // Test that * pattern works. |
| + AddProperty("http.nonProxyHosts", "*example.com"); |
| + AddProperty("http.proxyHost", "httpproxy.com"); |
| + AddProperty("http.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + 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 |
| + AddProperty("ftp.nonProxyHosts", "slashdot.org"); |
| + AddProperty("ftp.proxyHost", "httpproxy.com"); |
| + AddProperty("ftp.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + TestMapping("ftp://example.com/", "httpproxy.com:8080"); |
| + TestMapping("http://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, FtpProxyHostAndPort) { |
| + // Test ftp.proxyHost and ftp.proxyPort works. |
| + AddProperty("ftp.proxyHost", "httpproxy.com"); |
| + AddProperty("ftp.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + 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. |
| + AddProperty("ftp.proxyHost", "httpproxy.com"); |
| + ProxySettingsChanged(); |
| + 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. |
| + AddProperty("https.proxyHost", "httpproxy.com"); |
| + AddProperty("https.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + 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. |
| + AddProperty("https.proxyHost", "httpproxy.com"); |
| + ProxySettingsChanged(); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", ""); |
| + TestMapping("https://example.com/", "httpproxy.com:443"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostIPv6) { |
| + // Test IPv6 https.proxyHost and default port. |
| + AddProperty("http.proxyHost", "a:b:c::d:1"); |
| + ProxySettingsChanged(); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", "[a:b:c::d:1]:80"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndPortIPv6) { |
| + // Test IPv6 http.proxyHost and http.proxyPort works. |
| + AddProperty("http.proxyHost", "a:b:c::d:1"); |
| + AddProperty("http.proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", "[a:b:c::d:1]:8080"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxyHostAndInvalidPort) { |
| + // Test invalid http.proxyPort does not crash. |
| + AddProperty("http.proxyHost", "a:b:c::d:1"); |
| + AddProperty("http.proxyPort", "65536"); |
| + ProxySettingsChanged(); |
| + TestMapping("ftp://example.com/", ""); |
| + TestMapping("http://example.com/", ""); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, DefaultProxyExplictPort) { |
| + // Default http proxy is used if a scheme-specific one is not found. |
| + AddProperty("ftp.proxyHost", "httpproxy.com"); |
| + AddProperty("ftp.proxyPort", "8080"); |
| + AddProperty("proxyHost", "defaultproxy.com"); |
| + AddProperty("proxyPort", "8080"); |
| + ProxySettingsChanged(); |
| + 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. |
| + AddProperty("proxyHost", "defaultproxy.com"); |
| + ProxySettingsChanged(); |
| + 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. |
| + AddProperty("http.proxyHost", "defaultproxy.com"); |
| + AddProperty("socksProxyHost", "socksproxy.com"); |
| + ProxySettingsChanged(); |
| + 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 |
| + AddProperty("socksProxyHost", "socksproxy.com"); |
| + AddProperty("socksProxyPort", "9000"); |
| + ProxySettingsChanged(); |
| + TestMapping("http://example.com/", "socksproxy.com:9000"); |
| +} |
| + |
| +TEST_F(ProxyConfigServiceAndroidTest, HttpProxySupercedesSocks) { |
| + // SOCKS proxy is ignored if default HTTP proxy defined. |
| + AddProperty("proxyHost", "defaultproxy.com"); |
| + AddProperty("socksProxyHost", "socksproxy.com"); |
| + AddProperty("socksProxyPort", "9000"); |
| + ProxySettingsChanged(); |
| + TestMapping("http://example.com/", "defaultproxy.com:80"); |
| +} |
| + |
| +} // namespace net |