blob: 5730262976df041a2f03a97c07f804f3f3a0ac5a [file] [log] [blame]
// 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 "chrome/browser/chromeos/proxy_config_service_impl.h"
#include <map>
#include <string>
#include <vector>
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_pref_service.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "content/public/test/test_browser_thread.h"
#include "net/proxy/proxy_config_service_common_unittest.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::BrowserThread;
namespace chromeos {
namespace {
struct Input { // Fields of chromeos::ProxyConfigServiceImpl::ProxyConfig.
ProxyConfigServiceImpl::ProxyConfig::Mode mode;
const char* pac_url;
const char* single_uri;
const char* http_uri;
const char* https_uri;
const char* ftp_uri;
const char* socks_uri;
const char* bypass_rules;
};
// Builds an identifier for each test in an array.
#define TEST_DESC(desc) base::StringPrintf("at line %d <%s>", __LINE__, desc)
// Shortcuts to declare enums within chromeos's ProxyConfig.
#define MK_MODE(mode) ProxyConfigServiceImpl::ProxyConfig::MODE_##mode
#define MK_SCHM(scheme) net::ProxyServer::SCHEME_##scheme
#define MK_AVAIL(avail) net::ProxyConfigService::CONFIG_##avail
// Inspired from net/proxy/proxy_config_service_linux_unittest.cc.
const struct TestParams {
// Short description to identify the test
std::string description;
bool is_valid;
Input input;
// Expected outputs from fields of net::ProxyConfig (via IO).
bool auto_detect;
GURL pac_url;
net::ProxyRulesExpectation proxy_rules;
} tests[] = {
{ // 0
TEST_DESC("No proxying"),
true, // is_valid
{ // Input.
MK_MODE(DIRECT), // mode
},
// Expected result.
false, // auto_detect
GURL(), // pac_url
net::ProxyRulesExpectation::Empty(), // proxy_rules
},
{ // 1
TEST_DESC("Auto detect"),
true, // is_valid
{ // Input.
MK_MODE(AUTO_DETECT), // mode
},
// Expected result.
true, // auto_detect
GURL(), // pac_url
net::ProxyRulesExpectation::Empty(), // proxy_rules
},
{ // 2
TEST_DESC("Valid PAC URL"),
true, // is_valid
{ // Input.
MK_MODE(PAC_SCRIPT), // mode
"http://wpad/wpad.dat", // pac_url
},
// Expected result.
false, // auto_detect
GURL("http://wpad/wpad.dat"), // pac_url
net::ProxyRulesExpectation::Empty(), // proxy_rules
},
{ // 3
TEST_DESC("Invalid PAC URL"),
false, // is_valid
{ // Input.
MK_MODE(PAC_SCRIPT), // mode
"wpad.dat", // pac_url
},
// Expected result.
false, // auto_detect
GURL(), // pac_url
net::ProxyRulesExpectation::Empty(), // proxy_rules
},
{ // 4
TEST_DESC("Single-host in proxy list"),
true, // is_valid
{ // Input.
MK_MODE(SINGLE_PROXY), // mode
NULL, // pac_url
"www.google.com", // single_uri
},
// Expected result.
false, // auto_detect
GURL(), // pac_url
net::ProxyRulesExpectation::Single( // proxy_rules
"www.google.com:80", // single proxy
"<local>"), // bypass rules
},
{ // 5
TEST_DESC("Single-host, different port"),
true, // is_valid
{ // Input.
MK_MODE(SINGLE_PROXY), // mode
NULL, // pac_url
"www.google.com:99", // single_uri
},
// Expected result.
false, // auto_detect
GURL(), // pac_url
net::ProxyRulesExpectation::Single( // proxy_rules
"www.google.com:99", // single
"<local>"), // bypass rules
},
{ // 6
TEST_DESC("Tolerate a scheme"),
true, // is_valid
{ // Input.
MK_MODE(SINGLE_PROXY), // mode
NULL, // pac_url
"http://www.google.com:99", // single_uri
},
// Expected result.
false, // auto_detect
GURL(), // pac_url
net::ProxyRulesExpectation::Single( // proxy_rules
"www.google.com:99", // single proxy
"<local>"), // bypass rules
},
{ // 7
TEST_DESC("Per-scheme proxy rules"),
true, // is_valid
{ // Input.
MK_MODE(PROXY_PER_SCHEME), // mode
NULL, // pac_url
NULL, // single_uri
"www.google.com:80", // http_uri
"www.foo.com:110", // https_uri
"ftp.foo.com:121", // ftp_uri
"socks.com:888", // socks_uri
},
// Expected result.
false, // auto_detect
GURL(), // pac_url
net::ProxyRulesExpectation::PerSchemeWithSocks( // proxy_rules
"www.google.com:80", // http
"https://www.foo.com:110", // https
"ftp.foo.com:121", // ftp
"socks5://socks.com:888", // fallback proxy
"<local>"), // bypass rules
},
{ // 8
TEST_DESC("Bypass rules"),
true, // is_valid
{ // Input.
MK_MODE(SINGLE_PROXY), // mode
NULL, // pac_url
"www.google.com", // single_uri
NULL, NULL, NULL, NULL, // per-proto
"*.google.com, *foo.com:99, 1.2.3.4:22, 127.0.0.1/8", // bypass_rules
},
// Expected result.
false, // auto_detect
GURL(), // pac_url
net::ProxyRulesExpectation::Single( // proxy_rules
"www.google.com:80", // single proxy
// bypass_rules
"*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8,<local>"),
},
}; // tests
template<typename TESTBASE>
class ProxyConfigServiceImplTestBase : public TESTBASE {
protected:
ProxyConfigServiceImplTestBase()
: ui_thread_(BrowserThread::UI, &loop_),
io_thread_(BrowserThread::IO, &loop_) {}
virtual void Init(PrefService* pref_service) {
ASSERT_TRUE(pref_service);
DBusThreadManager::Initialize();
PrefProxyConfigTrackerImpl::RegisterPrefs(pref_service);
ProxyConfigServiceImpl::RegisterPrefs(pref_service);
proxy_config_service_.reset(new ChromeProxyConfigService(NULL, true));
config_service_impl_.reset(new ProxyConfigServiceImpl(pref_service));
config_service_impl_->SetChromeProxyConfigService(
proxy_config_service_.get());
// SetChromeProxyConfigService triggers update of initial prefs proxy
// config by tracker to chrome proxy config service, so flush all pending
// tasks so that tests start fresh.
loop_.RunAllPending();
}
virtual void TearDown() {
config_service_impl_->DetachFromPrefService();
loop_.RunAllPending();
config_service_impl_.reset();
proxy_config_service_.reset();
DBusThreadManager::Shutdown();
}
void SetAutomaticProxy(
ProxyConfigServiceImpl::ProxyConfig::Mode mode,
const char* pac_url,
ProxyConfigServiceImpl::ProxyConfig* config,
ProxyConfigServiceImpl::ProxyConfig::AutomaticProxy* automatic_proxy) {
config->mode = mode;
config->state = ProxyPrefs::CONFIG_SYSTEM;
if (pac_url)
automatic_proxy->pac_url = GURL(pac_url);
}
void SetManualProxy(
ProxyConfigServiceImpl::ProxyConfig::Mode mode,
const char* server_uri,
net::ProxyServer::Scheme scheme,
ProxyConfigServiceImpl::ProxyConfig* config,
ProxyConfigServiceImpl::ProxyConfig::ManualProxy* manual_proxy) {
if (!server_uri)
return;
config->mode = mode;
config->state = ProxyPrefs::CONFIG_SYSTEM;
manual_proxy->server = net::ProxyServer::FromURI(server_uri, scheme);
}
void InitConfigWithTestInput(
const Input& input, ProxyConfigServiceImpl::ProxyConfig* test_config) {
switch (input.mode) {
case MK_MODE(DIRECT):
case MK_MODE(AUTO_DETECT):
case MK_MODE(PAC_SCRIPT):
SetAutomaticProxy(input.mode, input.pac_url, test_config,
&test_config->automatic_proxy);
return;
case MK_MODE(SINGLE_PROXY):
SetManualProxy(input.mode, input.single_uri, MK_SCHM(HTTP),
test_config, &test_config->single_proxy);
break;
case MK_MODE(PROXY_PER_SCHEME):
SetManualProxy(input.mode, input.http_uri, MK_SCHM(HTTP),
test_config, &test_config->http_proxy);
SetManualProxy(input.mode, input.https_uri, MK_SCHM(HTTPS),
test_config, &test_config->https_proxy);
SetManualProxy(input.mode, input.ftp_uri, MK_SCHM(HTTP),
test_config, &test_config->ftp_proxy);
SetManualProxy(input.mode, input.socks_uri, MK_SCHM(SOCKS5),
test_config, &test_config->socks_proxy);
break;
}
if (input.bypass_rules)
test_config->bypass_rules.ParseFromString(input.bypass_rules);
}
// Synchronously gets the latest proxy config.
net::ProxyConfigService::ConfigAvailability SyncGetLatestProxyConfig(
net::ProxyConfig* config) {
*config = net::ProxyConfig();
// Let message loop process all messages.
loop_.RunAllPending();
// Calls ChromeProIOGetProxyConfig (which is called from
// ProxyConfigService::GetLatestProxyConfig), running on faked IO thread.
return proxy_config_service_->GetLatestProxyConfig(config);
}
MessageLoop loop_;
scoped_ptr<ChromeProxyConfigService> proxy_config_service_;
scoped_ptr<ProxyConfigServiceImpl> config_service_impl_;
private:
// Default stub state has ethernet as the active connected network and
// PROFILE_SHARED as profile type, which this unittest expects.
ScopedStubCrosEnabler stub_cros_enabler_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread io_thread_;
};
class ProxyConfigServiceImplTest
: public ProxyConfigServiceImplTestBase<testing::Test> {
protected:
virtual void SetUp() {
Init(&pref_service_);
}
TestingPrefService pref_service_;
};
TEST_F(ProxyConfigServiceImplTest, NetworkProxy) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
SCOPED_TRACE(StringPrintf("Test[%" PRIuS "] %s", i,
tests[i].description.c_str()));
ProxyConfigServiceImpl::ProxyConfig test_config;
InitConfigWithTestInput(tests[i].input, &test_config);
config_service_impl_->SetTesting(&test_config);
net::ProxyConfig config;
EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&config));
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(ProxyConfigServiceImplTest, ModifyFromUI) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
SCOPED_TRACE(StringPrintf("Test[%" PRIuS "] %s", i,
tests[i].description.c_str()));
// Init with direct.
ProxyConfigServiceImpl::ProxyConfig test_config;
SetAutomaticProxy(MK_MODE(DIRECT), NULL, &test_config,
&test_config.automatic_proxy);
config_service_impl_->SetTesting(&test_config);
// Set config to tests[i].input via UI.
net::ProxyBypassRules bypass_rules;
const Input& input = tests[i].input;
switch (input.mode) {
case MK_MODE(DIRECT) :
config_service_impl_->UISetProxyConfigToDirect();
break;
case MK_MODE(AUTO_DETECT) :
config_service_impl_->UISetProxyConfigToAutoDetect();
break;
case MK_MODE(PAC_SCRIPT) :
config_service_impl_->UISetProxyConfigToPACScript(GURL(input.pac_url));
break;
case MK_MODE(SINGLE_PROXY) :
config_service_impl_->UISetProxyConfigToSingleProxy(
net::ProxyServer::FromURI(input.single_uri, MK_SCHM(HTTP)));
if (input.bypass_rules) {
bypass_rules.ParseFromString(input.bypass_rules);
config_service_impl_->UISetProxyConfigBypassRules(bypass_rules);
}
break;
case MK_MODE(PROXY_PER_SCHEME) :
if (input.http_uri) {
config_service_impl_->UISetProxyConfigToProxyPerScheme("http",
net::ProxyServer::FromURI(input.http_uri, MK_SCHM(HTTP)));
}
if (input.https_uri) {
config_service_impl_->UISetProxyConfigToProxyPerScheme("https",
net::ProxyServer::FromURI(input.https_uri, MK_SCHM(HTTPS)));
}
if (input.ftp_uri) {
config_service_impl_->UISetProxyConfigToProxyPerScheme("ftp",
net::ProxyServer::FromURI(input.ftp_uri, MK_SCHM(HTTP)));
}
if (input.socks_uri) {
config_service_impl_->UISetProxyConfigToProxyPerScheme("socks",
net::ProxyServer::FromURI(input.socks_uri, MK_SCHM(SOCKS5)));
}
if (input.bypass_rules) {
bypass_rules.ParseFromString(input.bypass_rules);
config_service_impl_->UISetProxyConfigBypassRules(bypass_rules);
}
break;
}
// Retrieve config from IO thread.
net::ProxyConfig io_config;
EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&io_config));
EXPECT_EQ(tests[i].auto_detect, io_config.auto_detect());
EXPECT_EQ(tests[i].pac_url, io_config.pac_url());
EXPECT_TRUE(tests[i].proxy_rules.Matches(io_config.proxy_rules()));
// Retrieve config from UI thread.
ProxyConfigServiceImpl::ProxyConfig ui_config;
config_service_impl_->UIGetProxyConfig(&ui_config);
EXPECT_EQ(input.mode, ui_config.mode);
if (tests[i].is_valid) {
if (input.pac_url)
EXPECT_EQ(GURL(input.pac_url), ui_config.automatic_proxy.pac_url);
const net::ProxyRulesExpectation& proxy_rules = tests[i].proxy_rules;
if (input.single_uri)
EXPECT_EQ(proxy_rules.single_proxy,
ui_config.single_proxy.server.ToURI());
if (input.http_uri)
EXPECT_EQ(proxy_rules.proxy_for_http,
ui_config.http_proxy.server.ToURI());
if (input.https_uri)
EXPECT_EQ(proxy_rules.proxy_for_https,
ui_config.https_proxy.server.ToURI());
if (input.ftp_uri)
EXPECT_EQ(proxy_rules.proxy_for_ftp,
ui_config.ftp_proxy.server.ToURI());
if (input.socks_uri) {
EXPECT_EQ(proxy_rules.fallback_proxy,
ui_config.socks_proxy.server.ToURI());
}
if (input.bypass_rules)
EXPECT_TRUE(bypass_rules.Equals(ui_config.bypass_rules));
}
}
}
TEST_F(ProxyConfigServiceImplTest, DynamicPrefsOverride) {
// Groupings of 3 test inputs to use for managed, recommended and network
// proxies respectively. Only valid and non-direct test inputs are used.
const size_t proxies[][3] = {
{ 1, 2, 4, },
{ 1, 4, 2, },
{ 4, 2, 1, },
{ 2, 1, 4, },
{ 2, 4, 5, },
{ 2, 5, 4, },
{ 5, 4, 2, },
{ 4, 2, 5, },
{ 4, 5, 6, },
{ 4, 6, 5, },
{ 6, 5, 4, },
{ 5, 4, 6, },
{ 5, 6, 7, },
{ 5, 7, 6, },
{ 7, 6, 5, },
{ 6, 5, 7, },
{ 6, 7, 8, },
{ 6, 8, 7, },
{ 8, 7, 6, },
{ 7, 6, 8, },
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(proxies); ++i) {
const TestParams& managed_params = tests[proxies[i][0]];
const TestParams& recommended_params = tests[proxies[i][1]];
const TestParams& network_params = tests[proxies[i][2]];
SCOPED_TRACE(StringPrintf(
"Test[%" PRIuS "] managed=[%s], recommended=[%s], network=[%s]", i,
managed_params.description.c_str(),
recommended_params.description.c_str(),
network_params.description.c_str()));
ProxyConfigServiceImpl::ProxyConfig managed_config;
InitConfigWithTestInput(managed_params.input, &managed_config);
ProxyConfigServiceImpl::ProxyConfig recommended_config;
InitConfigWithTestInput(recommended_params.input, &recommended_config);
ProxyConfigServiceImpl::ProxyConfig network_config;
InitConfigWithTestInput(network_params.input, &network_config);
// Managed proxy pref should take effect over recommended proxy and
// non-existent network proxy.
config_service_impl_->SetTesting(NULL);
pref_service_.SetManagedPref(prefs::kProxy,
managed_config.ToPrefProxyConfig());
pref_service_.SetRecommendedPref(prefs::kProxy,
recommended_config.ToPrefProxyConfig());
net::ProxyConfig actual_config;
EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&actual_config));
EXPECT_EQ(managed_params.auto_detect, actual_config.auto_detect());
EXPECT_EQ(managed_params.pac_url, actual_config.pac_url());
EXPECT_TRUE(managed_params.proxy_rules.Matches(
actual_config.proxy_rules()));
// Recommended proxy pref should take effect when managed proxy pref is
// removed.
pref_service_.RemoveManagedPref(prefs::kProxy);
EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&actual_config));
EXPECT_EQ(recommended_params.auto_detect, actual_config.auto_detect());
EXPECT_EQ(recommended_params.pac_url, actual_config.pac_url());
EXPECT_TRUE(recommended_params.proxy_rules.Matches(
actual_config.proxy_rules()));
// Network proxy should take take effect over recommended proxy pref.
config_service_impl_->SetTesting(&network_config);
EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&actual_config));
EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
EXPECT_TRUE(network_params.proxy_rules.Matches(
actual_config.proxy_rules()));
// Managed proxy pref should take effect over network proxy.
pref_service_.SetManagedPref(prefs::kProxy,
managed_config.ToPrefProxyConfig());
EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&actual_config));
EXPECT_EQ(managed_params.auto_detect, actual_config.auto_detect());
EXPECT_EQ(managed_params.pac_url, actual_config.pac_url());
EXPECT_TRUE(managed_params.proxy_rules.Matches(
actual_config.proxy_rules()));
// Network proxy should take effect over recommended proxy pref when managed
// proxy pref is removed.
pref_service_.RemoveManagedPref(prefs::kProxy);
EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&actual_config));
EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
EXPECT_TRUE(network_params.proxy_rules.Matches(
actual_config.proxy_rules()));
// Removing recommended proxy pref should have no effect on network proxy.
pref_service_.RemoveRecommendedPref(prefs::kProxy);
EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&actual_config));
EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
EXPECT_TRUE(network_params.proxy_rules.Matches(
actual_config.proxy_rules()));
}
}
} // namespace
} // namespace chromeos