summaryrefslogtreecommitdiff
path: root/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc')
-rw-r--r--chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc1912
1 files changed, 1912 insertions, 0 deletions
diff --git a/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc b/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc
new file mode 100644
index 00000000000..7aec908af44
--- /dev/null
+++ b/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc
@@ -0,0 +1,1912 @@
+// 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 "net/proxy_resolution/proxy_config_service_linux.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "net/proxy_resolution/proxy_config.h"
+#include "net/proxy_resolution/proxy_config_service_common_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+// TODO(eroman): Convert these to parameterized tests using TEST_P().
+
+namespace net {
+namespace {
+
+// Set of values for all environment variables that we might
+// query. NULL represents an unset variable.
+struct EnvVarValues {
+ // The strange capitalization is so that the field matches the
+ // environment variable name exactly.
+ const char* DESKTOP_SESSION;
+ const char* HOME;
+ const char* KDEHOME;
+ const char* KDE_SESSION_VERSION;
+ const char* XDG_CURRENT_DESKTOP;
+ const char* auto_proxy;
+ const char* all_proxy;
+ const char* http_proxy;
+ const char* https_proxy;
+ const char* ftp_proxy;
+ const char* SOCKS_SERVER;
+ const char* SOCKS_VERSION;
+ const char* no_proxy;
+};
+
+// Undo macro pollution from GDK includes (from message_loop.h).
+#undef TRUE
+#undef FALSE
+
+// So as to distinguish between an unset boolean variable and
+// one that is false.
+enum BoolSettingValue { UNSET = 0, TRUE, FALSE };
+
+// Set of values for all gsettings settings that we might query.
+struct GSettingsValues {
+ // strings
+ const char* mode;
+ const char* autoconfig_url;
+ const char* http_host;
+ const char* secure_host;
+ const char* ftp_host;
+ const char* socks_host;
+ // integers
+ int http_port;
+ int secure_port;
+ int ftp_port;
+ int socks_port;
+ // booleans
+ BoolSettingValue use_proxy;
+ BoolSettingValue same_proxy;
+ BoolSettingValue use_auth;
+ // string list
+ std::vector<std::string> ignore_hosts;
+};
+
+// Mapping from a setting name to the location of the corresponding
+// value (inside a EnvVarValues or GSettingsValues struct).
+template <typename key_type, typename value_type>
+struct SettingsTable {
+ typedef std::map<key_type, value_type*> map_type;
+
+ // Gets the value from its location
+ value_type Get(key_type key) {
+ auto it = settings.find(key);
+ // In case there's a typo or the unittest becomes out of sync.
+ CHECK(it != settings.end()) << "key " << key << " not found";
+ value_type* value_ptr = it->second;
+ return *value_ptr;
+ }
+
+ map_type settings;
+};
+
+class MockEnvironment : public base::Environment {
+ public:
+ MockEnvironment() {
+#define ENTRY(x) table_[#x] = &values.x
+ ENTRY(DESKTOP_SESSION);
+ ENTRY(HOME);
+ ENTRY(KDEHOME);
+ ENTRY(KDE_SESSION_VERSION);
+ ENTRY(XDG_CURRENT_DESKTOP);
+ ENTRY(auto_proxy);
+ ENTRY(all_proxy);
+ ENTRY(http_proxy);
+ ENTRY(https_proxy);
+ ENTRY(ftp_proxy);
+ ENTRY(no_proxy);
+ ENTRY(SOCKS_SERVER);
+ ENTRY(SOCKS_VERSION);
+#undef ENTRY
+ Reset();
+ }
+
+ // Zeroes all environment values.
+ void Reset() {
+ EnvVarValues zero_values = {0};
+ values = zero_values;
+ }
+
+ // Begin base::Environment implementation.
+ bool GetVar(base::StringPiece variable_name, std::string* result) override {
+ auto it = table_.find(variable_name);
+ if (it == table_.end() || !*it->second)
+ return false;
+
+ // Note that the variable may be defined but empty.
+ *result = *(it->second);
+ return true;
+ }
+
+ bool SetVar(base::StringPiece variable_name,
+ const std::string& new_value) override {
+ ADD_FAILURE();
+ return false;
+ }
+
+ bool UnSetVar(base::StringPiece variable_name) override {
+ ADD_FAILURE();
+ return false;
+ }
+ // End base::Environment implementation.
+
+ // Intentionally public, for convenience when setting up a test.
+ EnvVarValues values;
+
+ private:
+ std::map<base::StringPiece, const char**> table_;
+};
+
+class MockSettingGetter : public ProxyConfigServiceLinux::SettingGetter {
+ public:
+ typedef ProxyConfigServiceLinux::SettingGetter SettingGetter;
+ MockSettingGetter() {
+#define ENTRY(key, field) \
+ strings_table.settings[SettingGetter::key] = &values.field
+ ENTRY(PROXY_MODE, mode);
+ ENTRY(PROXY_AUTOCONF_URL, autoconfig_url);
+ ENTRY(PROXY_HTTP_HOST, http_host);
+ ENTRY(PROXY_HTTPS_HOST, secure_host);
+ ENTRY(PROXY_FTP_HOST, ftp_host);
+ ENTRY(PROXY_SOCKS_HOST, socks_host);
+#undef ENTRY
+#define ENTRY(key, field) \
+ ints_table.settings[SettingGetter::key] = &values.field
+ ENTRY(PROXY_HTTP_PORT, http_port);
+ ENTRY(PROXY_HTTPS_PORT, secure_port);
+ ENTRY(PROXY_FTP_PORT, ftp_port);
+ ENTRY(PROXY_SOCKS_PORT, socks_port);
+#undef ENTRY
+#define ENTRY(key, field) \
+ bools_table.settings[SettingGetter::key] = &values.field
+ ENTRY(PROXY_USE_HTTP_PROXY, use_proxy);
+ ENTRY(PROXY_USE_SAME_PROXY, same_proxy);
+ ENTRY(PROXY_USE_AUTHENTICATION, use_auth);
+#undef ENTRY
+ string_lists_table.settings[SettingGetter::PROXY_IGNORE_HOSTS] =
+ &values.ignore_hosts;
+ Reset();
+ }
+
+ // Zeros all environment values.
+ void Reset() {
+ GSettingsValues zero_values = {0};
+ values = zero_values;
+ }
+
+ bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner)
+ override {
+ task_runner_ = glib_task_runner;
+ return true;
+ }
+
+ void ShutDown() override {}
+
+ bool SetUpNotifications(
+ ProxyConfigServiceLinux::Delegate* delegate) override {
+ return true;
+ }
+
+ const scoped_refptr<base::SequencedTaskRunner>& GetNotificationTaskRunner()
+ override {
+ return task_runner_;
+ }
+
+ ProxyConfigSource GetConfigSource() override {
+ return PROXY_CONFIG_SOURCE_TEST;
+ }
+
+ bool GetString(StringSetting key, std::string* result) override {
+ const char* value = strings_table.Get(key);
+ if (value) {
+ *result = value;
+ return true;
+ }
+ return false;
+ }
+
+ bool GetBool(BoolSetting key, bool* result) override {
+ BoolSettingValue value = bools_table.Get(key);
+ switch (value) {
+ case UNSET:
+ return false;
+ case TRUE:
+ *result = true;
+ break;
+ case FALSE:
+ *result = false;
+ }
+ return true;
+ }
+
+ bool GetInt(IntSetting key, int* result) override {
+ // We don't bother to distinguish unset keys from 0 values.
+ *result = ints_table.Get(key);
+ return true;
+ }
+
+ bool GetStringList(StringListSetting key,
+ std::vector<std::string>* result) override {
+ *result = string_lists_table.Get(key);
+ // We don't bother to distinguish unset keys from empty lists.
+ return !result->empty();
+ }
+
+ bool BypassListIsReversed() override { return false; }
+
+ bool MatchHostsUsingSuffixMatching() override { return false; }
+
+ // Intentionally public, for convenience when setting up a test.
+ GSettingsValues values;
+
+ private:
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ SettingsTable<StringSetting, const char*> strings_table;
+ SettingsTable<BoolSetting, BoolSettingValue> bools_table;
+ SettingsTable<IntSetting, int> ints_table;
+ SettingsTable<StringListSetting, std::vector<std::string>> string_lists_table;
+};
+
+// This helper class runs ProxyConfigServiceLinux::GetLatestProxyConfig() on
+// the main TaskRunner and synchronously waits for the result.
+// Some code duplicated from proxy_script_fetcher_unittest.cc.
+class SyncConfigGetter : public ProxyConfigService::Observer {
+ public:
+ // Takes ownership of |config_service|.
+ explicit SyncConfigGetter(ProxyConfigServiceLinux* config_service)
+ : event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED),
+ main_thread_("Main_Thread"),
+ config_service_(config_service),
+ matches_pac_url_event_(
+ base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED) {
+ // Start the main IO thread.
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+ main_thread_.StartWithOptions(options);
+
+ // Make sure the thread started.
+ main_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&SyncConfigGetter::Init, base::Unretained(this)));
+ Wait();
+ }
+
+ ~SyncConfigGetter() override {
+ // Clean up the main thread.
+ main_thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&SyncConfigGetter::CleanUp, base::Unretained(this)));
+ Wait();
+ }
+
+ // Does gsettings setup and initial fetch of the proxy config,
+ // all on the calling thread (meant to be the thread with the
+ // default glib main loop, which is the glib thread).
+ void SetupAndInitialFetch() {
+ config_service_->SetupAndFetchInitialConfig(
+ base::ThreadTaskRunnerHandle::Get(), main_thread_.task_runner());
+ }
+ // Synchronously gets the proxy config.
+ ProxyConfigService::ConfigAvailability SyncGetLatestProxyConfig(
+ ProxyConfig* config) {
+ main_thread_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&SyncConfigGetter::GetLatestConfigOnIOThread,
+ base::Unretained(this)));
+ Wait();
+ *config = proxy_config_;
+ return get_latest_config_result_;
+ }
+
+ // Instructs |matches_pac_url_event_| to be signalled once the configuration
+ // changes to |pac_url|. The way to use this function is:
+ //
+ // SetExpectedPacUrl(..);
+ // WriteFile(...)
+ // WaitUntilPacUrlMatchesExpectation();
+ //
+ // The expectation must be set *before* any file-level mutation is done,
+ // otherwise the change may be received before
+ // WaitUntilPacUrlMatchesExpectation(), and subsequently be lost.
+ void SetExpectedPacUrl(const std::string& pac_url) {
+ base::AutoLock lock(lock_);
+ expected_pac_url_ = GURL(pac_url);
+ }
+
+ // Blocks until the proxy config service has received a configuration
+ // matching the value previously passed to SetExpectedPacUrl().
+ void WaitUntilPacUrlMatchesExpectation() {
+ matches_pac_url_event_.Wait();
+ matches_pac_url_event_.Reset();
+ }
+
+ private:
+ void OnProxyConfigChanged(
+ const ProxyConfig& config,
+ ProxyConfigService::ConfigAvailability availability) override {
+ // If the configuration changed to |expected_pac_url_| signal the event.
+ base::AutoLock lock(lock_);
+ if (config.has_pac_url() && config.pac_url() == expected_pac_url_) {
+ expected_pac_url_ = GURL();
+ matches_pac_url_event_.Signal();
+ }
+ }
+
+ // [Runs on |main_thread_|]
+ void Init() {
+ config_service_->AddObserver(this);
+ event_.Signal();
+ }
+
+ // Calls GetLatestProxyConfig, running on |main_thread_| Signals |event_|
+ // on completion.
+ void GetLatestConfigOnIOThread() {
+ get_latest_config_result_ =
+ config_service_->GetLatestProxyConfig(&proxy_config_);
+ event_.Signal();
+ }
+
+ // [Runs on |main_thread_|] Signals |event_| on cleanup completion.
+ void CleanUp() {
+ config_service_->RemoveObserver(this);
+ delete config_service_;
+ base::RunLoop().RunUntilIdle();
+ event_.Signal();
+ }
+
+ void Wait() {
+ event_.Wait();
+ event_.Reset();
+ }
+
+ base::WaitableEvent event_;
+ base::Thread main_thread_;
+
+ ProxyConfigServiceLinux* config_service_;
+
+ // The config obtained by |main_thread_| and read back by the main
+ // thread.
+ ProxyConfig proxy_config_;
+
+ // Return value from GetLatestProxyConfig().
+ ProxyConfigService::ConfigAvailability get_latest_config_result_;
+
+ // If valid, |expected_pac_url_| is the URL that is being waited for in
+ // the proxy configuration. The URL should only be accessed while |lock_|
+ // is held. Once a configuration arrives for |expected_pac_url_| then the
+ // event |matches_pac_url_event_| will be signalled.
+ base::Lock lock_;
+ GURL expected_pac_url_;
+ base::WaitableEvent matches_pac_url_event_;
+};
+
+// This test fixture is only really needed for the KDEConfigParser test case,
+// but all the test cases with the same prefix ("ProxyConfigServiceLinuxTest")
+// must use the same test fixture class (also "ProxyConfigServiceLinuxTest").
+class ProxyConfigServiceLinuxTest : public PlatformTest {
+ protected:
+ void SetUp() override {
+ PlatformTest::SetUp();
+ // Set up a temporary KDE home directory.
+ std::string prefix("ProxyConfigServiceLinuxTest_user_home");
+ base::CreateNewTempDirectory(prefix, &user_home_);
+ config_home_ = user_home_.Append(FILE_PATH_LITERAL(".config"));
+ kde_home_ = user_home_.Append(FILE_PATH_LITERAL(".kde"));
+ base::FilePath path = kde_home_.Append(FILE_PATH_LITERAL("share"));
+ path = path.Append(FILE_PATH_LITERAL("config"));
+ base::CreateDirectory(path);
+ kioslaverc_ = path.Append(FILE_PATH_LITERAL("kioslaverc"));
+ // Set up paths but do not create the directory for .kde4.
+ kde4_home_ = user_home_.Append(FILE_PATH_LITERAL(".kde4"));
+ path = kde4_home_.Append(FILE_PATH_LITERAL("share"));
+ kde4_config_ = path.Append(FILE_PATH_LITERAL("config"));
+ kioslaverc4_ = kde4_config_.Append(FILE_PATH_LITERAL("kioslaverc"));
+ // Set up paths for KDE 5
+ kioslaverc5_ = config_home_.Append(FILE_PATH_LITERAL("kioslaverc"));
+ }
+
+ void TearDown() override {
+ // Delete the temporary KDE home directory.
+ base::DeleteFile(user_home_, true);
+ PlatformTest::TearDown();
+ }
+
+ base::FilePath user_home_;
+ base::FilePath config_home_;
+ // KDE3 paths.
+ base::FilePath kde_home_;
+ base::FilePath kioslaverc_;
+ // KDE4 paths.
+ base::FilePath kde4_home_;
+ base::FilePath kde4_config_;
+ base::FilePath kioslaverc4_;
+ // KDE5 paths.
+ base::FilePath kioslaverc5_;
+};
+
+// Builds an identifier for each test in an array.
+#define TEST_DESC(desc) base::StringPrintf("at line %d <%s>", __LINE__, desc)
+
+TEST_F(ProxyConfigServiceLinuxTest, BasicGSettingsTest) {
+ std::vector<std::string> empty_ignores;
+
+ std::vector<std::string> google_ignores;
+ google_ignores.push_back("*.google.com");
+
+ // Inspired from proxy_config_service_win_unittest.cc.
+ // Very neat, but harder to track down failures though.
+ const struct {
+ // Short description to identify the test
+ std::string description;
+
+ // Input.
+ GSettingsValues values;
+
+ // Expected outputs (availability and fields of ProxyConfig).
+ ProxyConfigService::ConfigAvailability availability;
+ bool auto_detect;
+ GURL pac_url;
+ ProxyRulesExpectation proxy_rules;
+ } tests[] = {
+ {
+ TEST_DESC("No proxying"),
+ {
+ // Input.
+ "none", // mode
+ "", // autoconfig_url
+ "", "", "", "", // hosts
+ 0, 0, 0, 0, // ports
+ FALSE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Auto detect"),
+ {
+ // Input.
+ "auto", // mode
+ "", // autoconfig_url
+ "", "", "", "", // hosts
+ 0, 0, 0, 0, // ports
+ FALSE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ true, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Valid PAC URL"),
+ {
+ // Input.
+ "auto", // mode
+ "http://wpad/wpad.dat", // autoconfig_url
+ "", "", "", "", // hosts
+ 0, 0, 0, 0, // ports
+ FALSE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL("http://wpad/wpad.dat"), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Invalid PAC URL"),
+ {
+ // Input.
+ "auto", // mode
+ "wpad.dat", // autoconfig_url
+ "", "", "", "", // hosts
+ 0, 0, 0, 0, // ports
+ FALSE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Single-host in proxy list"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 80, 0, 0, 0, // ports
+ TRUE, TRUE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single("www.google.com:80", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("use_http_proxy is honored"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 80, 0, 0, 0, // ports
+ FALSE, TRUE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("use_http_proxy and use_same_proxy are optional"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 80, 0, 0, 0, // ports
+ UNSET, UNSET, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Single-host, different port"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 88, 0, 0, 0, // ports
+ TRUE, TRUE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single("www.google.com:88", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Per-scheme proxy rules"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", // http_host
+ "www.foo.com", // secure_host
+ "ftp.foo.com", // ftp
+ "", // socks
+ 88, 110, 121, 0, // ports
+ TRUE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:88", // http
+ "www.foo.com:110", // https
+ "ftp.foo.com:121", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("socks"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "", "", "", "socks.com", // hosts
+ 0, 0, 0, 99, // ports
+ TRUE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single(
+ "socks5://socks.com:99", // single proxy
+ "") // bypass rules
+ },
+
+ {
+ TEST_DESC("Per-scheme proxy rules with fallback to SOCKS"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", // http_host
+ "www.foo.com", // secure_host
+ "ftp.foo.com", // ftp
+ "foobar.net", // socks
+ 88, 110, 121, 99, // ports
+ TRUE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerSchemeWithSocks(
+ "www.google.com:88", // http
+ "www.foo.com:110", // https
+ "ftp.foo.com:121", // ftp
+ "socks5://foobar.net:99", // socks
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC(
+ "Per-scheme proxy rules (just HTTP) with fallback to SOCKS"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", // http_host
+ "", // secure_host
+ "", // ftp
+ "foobar.net", // socks
+ 88, 0, 0, 99, // ports
+ TRUE, FALSE, FALSE, // use, same, auth
+ empty_ignores, // ignore_hosts
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerSchemeWithSocks(
+ "www.google.com:88", // http
+ "", // https
+ "", // ftp
+ "socks5://foobar.net:99", // socks
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Bypass *.google.com"),
+ {
+ // Input.
+ "manual", // mode
+ "", // autoconfig_url
+ "www.google.com", "", "", "", // hosts
+ 80, 0, 0, 0, // ports
+ TRUE, TRUE, FALSE, // use, same, auth
+ google_ignores, // ignore_hosts
+ },
+
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single("www.google.com:80", // single proxy
+ "*.google.com"), // bypass rules
+ },
+ };
+
+ for (size_t i = 0; i < arraysize(tests); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
+ tests[i].description.c_str()));
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ MockSettingGetter* setting_getter = new MockSettingGetter;
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env), setting_getter));
+ ProxyConfig config;
+ setting_getter->values = tests[i].values;
+ sync_config_getter.SetupAndInitialFetch();
+ ProxyConfigService::ConfigAvailability availability =
+ sync_config_getter.SyncGetLatestProxyConfig(&config);
+ EXPECT_EQ(tests[i].availability, availability);
+
+ if (availability == ProxyConfigService::CONFIG_VALID) {
+ EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
+ EXPECT_EQ(tests[i].pac_url, config.pac_url());
+ EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
+ }
+ }
+}
+
+TEST_F(ProxyConfigServiceLinuxTest, BasicEnvTest) {
+ // Inspired from proxy_config_service_win_unittest.cc.
+ const struct {
+ // Short description to identify the test
+ std::string description;
+
+ // Input.
+ EnvVarValues values;
+
+ // Expected outputs (availability and fields of ProxyConfig).
+ ProxyConfigService::ConfigAvailability availability;
+ bool auto_detect;
+ GURL pac_url;
+ ProxyRulesExpectation proxy_rules;
+ } tests[] = {
+ {
+ TEST_DESC("No proxying"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ nullptr, // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ nullptr, nullptr, // SOCKS
+ "*", // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Auto detect"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ "", // auto_proxy
+ nullptr, // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ nullptr, nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ true, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Valid PAC URL"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ "http://wpad/wpad.dat", // auto_proxy
+ nullptr, // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ nullptr, nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL("http://wpad/wpad.dat"), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Invalid PAC URL"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ "wpad.dat", // auto_proxy
+ nullptr, // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ nullptr, nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Single-host in proxy list"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ "www.google.com", // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ nullptr, nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single("www.google.com:80", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Single-host, different port"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ "www.google.com:99", // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ nullptr, nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single("www.google.com:99", // single
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Tolerate a scheme"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ "http://www.google.com:99", // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ nullptr, nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single("www.google.com:99", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Per-scheme proxy rules"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ nullptr, // all_proxy
+ "www.google.com:80", "www.foo.com:110",
+ "ftp.foo.com:121", // per-proto
+ nullptr, nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "www.foo.com:110", // https
+ "ftp.foo.com:121", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("socks"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ "", // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ "socks.com:888", nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single(
+ "socks5://socks.com:888", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("socks4"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ "", // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ "socks.com:888", "4", // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single(
+ "socks4://socks.com:888", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("socks default port"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ "", // all_proxy
+ nullptr, nullptr, nullptr, // per-proto proxies
+ "socks.com", nullptr, // SOCKS
+ nullptr, // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single(
+ "socks5://socks.com:1080", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("bypass"),
+ {
+ // Input.
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ "www.google.com", // all_proxy
+ nullptr, nullptr, nullptr, // per-proto
+ nullptr, nullptr, // SOCKS
+ ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8", // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single(
+ "www.google.com:80",
+ "*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8"),
+ },
+ };
+
+ for (size_t i = 0; i < arraysize(tests); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
+ tests[i].description.c_str()));
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values = tests[i].values;
+ MockSettingGetter* setting_getter = new MockSettingGetter;
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env), setting_getter));
+ ProxyConfig config;
+ sync_config_getter.SetupAndInitialFetch();
+ ProxyConfigService::ConfigAvailability availability =
+ sync_config_getter.SyncGetLatestProxyConfig(&config);
+ EXPECT_EQ(tests[i].availability, availability);
+
+ if (availability == ProxyConfigService::CONFIG_VALID) {
+ EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
+ EXPECT_EQ(tests[i].pac_url, config.pac_url());
+ EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
+ }
+ }
+}
+
+TEST_F(ProxyConfigServiceLinuxTest, GSettingsNotification) {
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ MockSettingGetter* setting_getter = new MockSettingGetter;
+ ProxyConfigServiceLinux* service =
+ new ProxyConfigServiceLinux(std::move(env), setting_getter);
+ SyncConfigGetter sync_config_getter(service);
+ ProxyConfig config;
+
+ // Start with no proxy.
+ setting_getter->values.mode = "none";
+ sync_config_getter.SetupAndInitialFetch();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_FALSE(config.auto_detect());
+
+ // Now set to auto-detect.
+ setting_getter->values.mode = "auto";
+ // Simulate setting change notification callback.
+ service->OnCheckProxyConfigSettings();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_TRUE(config.auto_detect());
+}
+
+TEST_F(ProxyConfigServiceLinuxTest, KDEConfigParser) {
+ // One of the tests below needs a worst-case long line prefix. We build it
+ // programmatically so that it will always be the right size.
+ std::string long_line;
+ size_t limit = ProxyConfigServiceLinux::SettingGetter::BUFFER_SIZE - 1;
+ for (size_t i = 0; i < limit; ++i)
+ long_line += "-";
+
+ // Inspired from proxy_config_service_win_unittest.cc.
+ const struct {
+ // Short description to identify the test
+ std::string description;
+
+ // Input.
+ std::string kioslaverc;
+ EnvVarValues env_values;
+
+ // Expected outputs (availability and fields of ProxyConfig).
+ ProxyConfigService::ConfigAvailability availability;
+ bool auto_detect;
+ GURL pac_url;
+ ProxyRulesExpectation proxy_rules;
+ } tests[] = {
+ {
+ TEST_DESC("No proxying"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=0\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+ {
+ TEST_DESC("Invalid proxy type (ProxyType=-3)"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=-3\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Invalid proxy type (ProxyType=AB-)"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=AB-\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Auto detect"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=3\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ true, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Valid PAC URL"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=2\n"
+ "Proxy Config Script=http://wpad/wpad.dat\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL("http://wpad/wpad.dat"), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Valid PAC file without file://"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=2\n"
+ "Proxy Config Script=/wpad/wpad.dat\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL("file:///wpad/wpad.dat"), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Per-scheme proxy rules"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "httpsProxy=www.foo.com\nftpProxy=ftp.foo.com\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "www.foo.com:80", // https
+ "ftp.foo.com:80", // http
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Only HTTP proxy specified"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\n"
+ "httpProxy=www.google.com\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Only HTTP proxy specified, different port"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\n"
+ "httpProxy=www.google.com:88\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:88", // http
+ "", // https
+ "", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC(
+ "Only HTTP proxy specified, different port, space-delimited"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\n"
+ "httpProxy=www.google.com 88\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:88", // http
+ "", // https
+ "", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Bypass *.google.com"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=.google.com\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*.google.com"), // bypass rules
+ },
+
+ {
+ TEST_DESC("Bypass *.google.com and *.kde.org"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=.google.com,.kde.org\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme(
+ "www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*.google.com,*.kde.org"), // bypass rules
+ },
+
+ {
+ TEST_DESC("Correctly parse bypass list with ReversedException=true"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=.google.com\nReversedException=true\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerSchemeWithBypassReversed(
+ "www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*.google.com"), // bypass rules
+ },
+
+ {
+ TEST_DESC("Correctly parse bypass list with ReversedException=false"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=.google.com\nReversedException=false\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*.google.com"), // bypass rules
+ },
+
+ {
+ TEST_DESC("Correctly parse bypass list with ReversedException=1"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=.google.com\nReversedException=1\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerSchemeWithBypassReversed(
+ "www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*.google.com"), // bypass rules
+ },
+
+ {
+ TEST_DESC("Overflow: ReversedException=18446744073709551617"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=.google.com\nReversedException=18446744073709551617\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*.google.com"), // bypass rules
+ },
+
+ {
+ TEST_DESC("Not a number: ReversedException=noitpecxE"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=.google.com\nReversedException=noitpecxE\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*.google.com"), // bypass rules
+ },
+
+ {
+ TEST_DESC("socks"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nsocksProxy=socks.com 888\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single(
+ "socks5://socks.com:888", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("socks4"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nsocksProxy=socks4://socks.com 888\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Single(
+ "socks4://socks.com:888", // single proxy
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Treat all hostname patterns as wildcard patterns"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=google.com,kde.org,<local>\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme(
+ "www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*google.com,*kde.org,<local>"), // bypass rules
+ },
+
+ {
+ TEST_DESC("Allow trailing whitespace after boolean value"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "NoProxyFor=.google.com\nReversedException=true \n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerSchemeWithBypassReversed(
+ "www.google.com:80", // http
+ "", // https
+ "", // ftp
+ "*.google.com"), // bypass rules
+ },
+
+ {
+ TEST_DESC("Ignore settings outside [Proxy Settings]"),
+
+ // Input.
+ "httpsProxy=www.foo.com\n[Proxy Settings]\nProxyType=1\n"
+ "httpProxy=www.google.com\n[Other Section]\nftpProxy=ftp.foo.com\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Handle CRLF line endings"),
+
+ // Input.
+ "[Proxy Settings]\r\nProxyType=1\r\nhttpProxy=www.google.com\r\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Handle blank lines and mixed line endings"),
+
+ // Input.
+ "[Proxy Settings]\r\n\nProxyType=1\n\r\nhttpProxy=www.google.com\n\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Handle localized settings"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType[$e]=1\nhttpProxy[$e]=www.google.com\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Ignore malformed localized settings"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+ "httpsProxy$e]=www.foo.com\nftpProxy=ftp.foo.com\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "ftp.foo.com:80", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Handle strange whitespace"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType [$e] =2\n"
+ " Proxy Config Script = http:// foo\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL("http:// foo"), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Ignore all of a line which is too long"),
+
+ // Input.
+ std::string("[Proxy Settings]\nProxyType=1\nftpProxy=ftp.foo.com\n") +
+ long_line + "httpsProxy=www.foo.com\nhttpProxy=www.google.com\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "ftp.foo.com:80", // ftp
+ ""), // bypass rules
+ },
+
+ {
+ TEST_DESC("Indirect Proxy - no env vars set"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=4\nhttpProxy=http_proxy\n"
+ "httpsProxy=https_proxy\nftpProxy=ftp_proxy\nNoProxyFor=no_proxy\n",
+ {}, // env_values
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::Empty(),
+ },
+
+ {
+ TEST_DESC("Indirect Proxy - with env vars set"),
+
+ // Input.
+ "[Proxy Settings]\nProxyType=4\nhttpProxy=http_proxy\n"
+ "httpsProxy=https_proxy\nftpProxy=ftp_proxy\nNoProxyFor=no_proxy\n",
+ {
+ // env_values
+ nullptr, // DESKTOP_SESSION
+ nullptr, // HOME
+ nullptr, // KDEHOME
+ nullptr, // KDE_SESSION_VERSION
+ nullptr, // XDG_CURRENT_DESKTOP
+ nullptr, // auto_proxy
+ nullptr, // all_proxy
+ "www.normal.com", // http_proxy
+ "www.secure.com", // https_proxy
+ "ftp.foo.com", // ftp_proxy
+ nullptr, nullptr, // SOCKS
+ ".google.com, .kde.org", // no_proxy
+ },
+
+ // Expected result.
+ ProxyConfigService::CONFIG_VALID,
+ false, // auto_detect
+ GURL(), // pac_url
+ ProxyRulesExpectation::PerScheme(
+ "www.normal.com:80", // http
+ "www.secure.com:80", // https
+ "ftp.foo.com:80", // ftp
+ "*.google.com,*.kde.org"), // bypass rules
+ },
+ };
+
+ for (size_t i = 0; i < arraysize(tests); ++i) {
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
+ tests[i].description.c_str()));
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values = tests[i].env_values;
+ // Force the KDE getter to be used and tell it where the test is.
+ env->values.DESKTOP_SESSION = "kde4";
+ env->values.KDEHOME = kde_home_.value().c_str();
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env)));
+ ProxyConfig config;
+ // Overwrite the kioslaverc file.
+ base::WriteFile(kioslaverc_, tests[i].kioslaverc.c_str(),
+ tests[i].kioslaverc.length());
+ sync_config_getter.SetupAndInitialFetch();
+ ProxyConfigService::ConfigAvailability availability =
+ sync_config_getter.SyncGetLatestProxyConfig(&config);
+ EXPECT_EQ(tests[i].availability, availability);
+
+ if (availability == ProxyConfigService::CONFIG_VALID) {
+ EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
+ EXPECT_EQ(tests[i].pac_url, config.pac_url());
+ EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
+ }
+ }
+}
+
+TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) {
+ // Auto detect proxy settings.
+ std::string slaverc3 = "[Proxy Settings]\nProxyType=3\n";
+ // Valid PAC URL.
+ std::string slaverc4 =
+ "[Proxy Settings]\nProxyType=2\n"
+ "Proxy Config Script=http://wpad/wpad.dat\n";
+ GURL slaverc4_pac_url("http://wpad/wpad.dat");
+ // Basic HTTP proxy setting.
+ std::string slaverc5 =
+ "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com 80\n";
+ ProxyRulesExpectation slaverc5_rules =
+ ProxyRulesExpectation::PerScheme("www.google.com:80", // http
+ "", // https
+ "", // ftp
+ ""); // bypass rules
+
+ // Overwrite the .kde kioslaverc file.
+ base::WriteFile(kioslaverc_, slaverc3.c_str(), slaverc3.length());
+
+ // If .kde4 exists it will mess up the first test. It should not, as
+ // we created the directory for $HOME in the test setup.
+ CHECK(!base::DirectoryExists(kde4_home_));
+
+ {
+ SCOPED_TRACE("KDE4, no .kde4 directory, verify fallback");
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values.DESKTOP_SESSION = "kde4";
+ env->values.HOME = user_home_.value().c_str();
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env)));
+ ProxyConfig config;
+ sync_config_getter.SetupAndInitialFetch();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_TRUE(config.auto_detect());
+ EXPECT_EQ(GURL(), config.pac_url());
+ }
+
+ // Now create .kde4 and put a kioslaverc in the config directory.
+ // Note that its timestamp will be at least as new as the .kde one.
+ base::CreateDirectory(kde4_config_);
+ base::WriteFile(kioslaverc4_, slaverc4.c_str(), slaverc4.length());
+ CHECK(base::PathExists(kioslaverc4_));
+
+ {
+ SCOPED_TRACE("KDE4, .kde4 directory present, use it");
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values.DESKTOP_SESSION = "kde4";
+ env->values.HOME = user_home_.value().c_str();
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env)));
+ ProxyConfig config;
+ sync_config_getter.SetupAndInitialFetch();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_FALSE(config.auto_detect());
+ EXPECT_EQ(slaverc4_pac_url, config.pac_url());
+ }
+
+ {
+ SCOPED_TRACE("KDE3, .kde4 directory present, ignore it");
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values.DESKTOP_SESSION = "kde";
+ env->values.HOME = user_home_.value().c_str();
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env)));
+ ProxyConfig config;
+ sync_config_getter.SetupAndInitialFetch();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_TRUE(config.auto_detect());
+ EXPECT_EQ(GURL(), config.pac_url());
+ }
+
+ {
+ SCOPED_TRACE("KDE4, .kde4 directory present, KDEHOME set to .kde");
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values.DESKTOP_SESSION = "kde4";
+ env->values.HOME = user_home_.value().c_str();
+ env->values.KDEHOME = kde_home_.value().c_str();
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env)));
+ ProxyConfig config;
+ sync_config_getter.SetupAndInitialFetch();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_TRUE(config.auto_detect());
+ EXPECT_EQ(GURL(), config.pac_url());
+ }
+
+ // Finally, make the .kde4 config directory older than the .kde directory
+ // and make sure we then use .kde instead of .kde4 since it's newer.
+ base::TouchFile(kde4_config_, base::Time(), base::Time());
+
+ {
+ SCOPED_TRACE("KDE4, very old .kde4 directory present, use .kde");
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values.DESKTOP_SESSION = "kde4";
+ env->values.HOME = user_home_.value().c_str();
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env)));
+ ProxyConfig config;
+ sync_config_getter.SetupAndInitialFetch();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_TRUE(config.auto_detect());
+ EXPECT_EQ(GURL(), config.pac_url());
+ }
+
+ // For KDE 5 create ${HOME}/.config and put a kioslaverc in the directory.
+ base::CreateDirectory(config_home_);
+ base::WriteFile(kioslaverc5_, slaverc5.c_str(), slaverc5.length());
+ CHECK(base::PathExists(kioslaverc5_));
+
+ {
+ SCOPED_TRACE("KDE5, .kde and .kde4 present, use .config");
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values.XDG_CURRENT_DESKTOP = "KDE";
+ env->values.KDE_SESSION_VERSION = "5";
+ env->values.HOME = user_home_.value().c_str();
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env)));
+ ProxyConfig config;
+ sync_config_getter.SetupAndInitialFetch();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_FALSE(config.auto_detect());
+ EXPECT_TRUE(slaverc5_rules.Matches(config.proxy_rules()));
+ }
+}
+
+void WriteFile(const base::FilePath& path, base::StringPiece data) {
+ EXPECT_TRUE(base::WriteFile(path, data.data(), data.size()));
+}
+
+// Tests that the KDE proxy config service watches for file and directory
+// changes.
+TEST_F(ProxyConfigServiceLinuxTest, KDEFileChanged) {
+ // Set up the initial .kde kioslaverc file.
+ WriteFile(kioslaverc_,
+ "[Proxy Settings]\nProxyType=2\n"
+ "Proxy Config Script=http://version1/wpad.dat\n");
+
+ // Initialize the config service using kioslaverc.
+ std::unique_ptr<MockEnvironment> env(new MockEnvironment);
+ env->values.DESKTOP_SESSION = "kde4";
+ env->values.HOME = user_home_.value().c_str();
+ SyncConfigGetter sync_config_getter(
+ new ProxyConfigServiceLinux(std::move(env)));
+ ProxyConfig config;
+ sync_config_getter.SetupAndInitialFetch();
+ EXPECT_EQ(ProxyConfigService::CONFIG_VALID,
+ sync_config_getter.SyncGetLatestProxyConfig(&config));
+ EXPECT_TRUE(config.has_pac_url());
+ EXPECT_EQ(GURL("http://version1/wpad.dat"), config.pac_url());
+
+ //-----------------------------------------------------
+
+ // Change the kioslaverc file by overwriting it. Verify that the change was
+ // observed.
+ sync_config_getter.SetExpectedPacUrl("http://version2/wpad.dat");
+
+ WriteFile(kioslaverc_,
+ "[Proxy Settings]\nProxyType=2\n"
+ "Proxy Config Script=http://version2/wpad.dat\n");
+
+ // Wait for change to be noticed.
+ sync_config_getter.WaitUntilPacUrlMatchesExpectation();
+
+ //-----------------------------------------------------
+
+ // Change the kioslaverc file by renaming it. If only the file's inode
+ // were being watched (rather than directory) this will not result in
+ // an observable change. Note that KDE when re-writing proxy settings does
+ // so by renaming a new file, so the inode will change.
+ sync_config_getter.SetExpectedPacUrl("http://version3/wpad.dat");
+
+ // Create a new file, and rename it into place.
+ WriteFile(kioslaverc_.AddExtension("new"),
+ "[Proxy Settings]\nProxyType=2\n"
+ "Proxy Config Script=http://version3/wpad.dat\n");
+ base::Move(kioslaverc_, kioslaverc_.AddExtension("old"));
+ base::Move(kioslaverc_.AddExtension("new"), kioslaverc_);
+
+ // Wait for change to be noticed.
+ sync_config_getter.WaitUntilPacUrlMatchesExpectation();
+
+ //-----------------------------------------------------
+
+ // Change the kioslaverc file once more by ovewriting it. This is really
+ // just another test to make sure things still work after the directory
+ // change was observed (this final test probably isn't very useful).
+ sync_config_getter.SetExpectedPacUrl("http://version4/wpad.dat");
+
+ WriteFile(kioslaverc_,
+ "[Proxy Settings]\nProxyType=2\n"
+ "Proxy Config Script=http://version4/wpad.dat\n");
+
+ // Wait for change to be noticed.
+ sync_config_getter.WaitUntilPacUrlMatchesExpectation();
+
+ //-----------------------------------------------------
+
+ // TODO(eroman): Add a test where kioslaverc is deleted next. Currently this
+ // doesn't trigger any notifications, but it probably should.
+}
+
+} // namespace
+
+} // namespace net