1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/proxy_config_service_impl.h"
11 #include "base/format_macros.h"
12 #include "base/logging.h"
13 #include "base/message_loop.h"
14 #include "base/string_util.h"
15 #include "base/stringprintf.h"
16 #include "chrome/browser/chromeos/cros/cros_library.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/test/base/testing_pref_service.h"
19 #include "chromeos/dbus/dbus_thread_manager.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "net/proxy/proxy_config_service_common_unittest.h"
22 #include "testing/gtest/include/gtest/gtest.h"
24 using content::BrowserThread
;
30 struct Input
{ // Fields of chromeos::ProxyConfigServiceImpl::ProxyConfig.
31 ProxyConfigServiceImpl::ProxyConfig::Mode mode
;
33 const char* single_uri
;
35 const char* https_uri
;
37 const char* socks_uri
;
38 const char* bypass_rules
;
41 // Builds an identifier for each test in an array.
42 #define TEST_DESC(desc) base::StringPrintf("at line %d <%s>", __LINE__, desc)
44 // Shortcuts to declare enums within chromeos's ProxyConfig.
45 #define MK_MODE(mode) ProxyConfigServiceImpl::ProxyConfig::MODE_##mode
46 #define MK_SCHM(scheme) net::ProxyServer::SCHEME_##scheme
47 #define MK_AVAIL(avail) net::ProxyConfigService::CONFIG_##avail
49 // Inspired from net/proxy/proxy_config_service_linux_unittest.cc.
50 const struct TestParams
{
51 // Short description to identify the test
52 std::string description
;
58 // Expected outputs from fields of net::ProxyConfig (via IO).
61 net::ProxyRulesExpectation proxy_rules
;
64 TEST_DESC("No proxying"),
69 MK_MODE(DIRECT
), // mode
75 net::ProxyRulesExpectation::Empty(), // proxy_rules
79 TEST_DESC("Auto detect"),
84 MK_MODE(AUTO_DETECT
), // mode
90 net::ProxyRulesExpectation::Empty(), // proxy_rules
94 TEST_DESC("Valid PAC URL"),
99 MK_MODE(PAC_SCRIPT
), // mode
100 "http://wpad/wpad.dat", // pac_url
104 false, // auto_detect
105 GURL("http://wpad/wpad.dat"), // pac_url
106 net::ProxyRulesExpectation::Empty(), // proxy_rules
110 TEST_DESC("Invalid PAC URL"),
115 MK_MODE(PAC_SCRIPT
), // mode
116 "wpad.dat", // pac_url
120 false, // auto_detect
122 net::ProxyRulesExpectation::Empty(), // proxy_rules
126 TEST_DESC("Single-host in proxy list"),
131 MK_MODE(SINGLE_PROXY
), // mode
133 "www.google.com", // single_uri
137 false, // auto_detect
139 net::ProxyRulesExpectation::Single( // proxy_rules
140 "www.google.com:80", // single proxy
141 "<local>"), // bypass rules
145 TEST_DESC("Single-host, different port"),
150 MK_MODE(SINGLE_PROXY
), // mode
152 "www.google.com:99", // single_uri
156 false, // auto_detect
158 net::ProxyRulesExpectation::Single( // proxy_rules
159 "www.google.com:99", // single
160 "<local>"), // bypass rules
164 TEST_DESC("Tolerate a scheme"),
169 MK_MODE(SINGLE_PROXY
), // mode
171 "http://www.google.com:99", // single_uri
175 false, // auto_detect
177 net::ProxyRulesExpectation::Single( // proxy_rules
178 "www.google.com:99", // single proxy
179 "<local>"), // bypass rules
183 TEST_DESC("Per-scheme proxy rules"),
188 MK_MODE(PROXY_PER_SCHEME
), // mode
191 "www.google.com:80", // http_uri
192 "www.foo.com:110", // https_uri
193 "ftp.foo.com:121", // ftp_uri
194 "socks.com:888", // socks_uri
198 false, // auto_detect
200 net::ProxyRulesExpectation::PerSchemeWithSocks( // proxy_rules
201 "www.google.com:80", // http
202 "https://www.foo.com:110", // https
203 "ftp.foo.com:121", // ftp
204 "socks5://socks.com:888", // fallback proxy
205 "<local>"), // bypass rules
209 TEST_DESC("Bypass rules"),
214 MK_MODE(SINGLE_PROXY
), // mode
216 "www.google.com", // single_uri
217 NULL
, NULL
, NULL
, NULL
, // per-proto
218 "*.google.com, *foo.com:99, 1.2.3.4:22, 127.0.0.1/8", // bypass_rules
222 false, // auto_detect
224 net::ProxyRulesExpectation::Single( // proxy_rules
225 "www.google.com:80", // single proxy
227 "*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8,<local>"),
231 template<typename TESTBASE
>
232 class ProxyConfigServiceImplTestBase
: public TESTBASE
{
234 ProxyConfigServiceImplTestBase()
235 : ui_thread_(BrowserThread::UI
, &loop_
),
236 io_thread_(BrowserThread::IO
, &loop_
) {}
238 virtual void Init(PrefService
* pref_service
) {
239 ASSERT_TRUE(pref_service
);
240 DBusThreadManager::Initialize();
241 PrefProxyConfigTrackerImpl::RegisterPrefs(pref_service
);
242 ProxyConfigServiceImpl::RegisterPrefs(pref_service
);
243 proxy_config_service_
.reset(new ChromeProxyConfigService(NULL
, true));
244 config_service_impl_
.reset(new ProxyConfigServiceImpl(pref_service
));
245 config_service_impl_
->SetChromeProxyConfigService(
246 proxy_config_service_
.get());
247 // SetChromeProxyConfigService triggers update of initial prefs proxy
248 // config by tracker to chrome proxy config service, so flush all pending
249 // tasks so that tests start fresh.
250 loop_
.RunAllPending();
253 virtual void TearDown() {
254 config_service_impl_
->DetachFromPrefService();
255 loop_
.RunAllPending();
256 config_service_impl_
.reset();
257 proxy_config_service_
.reset();
258 DBusThreadManager::Shutdown();
261 void SetAutomaticProxy(
262 ProxyConfigServiceImpl::ProxyConfig::Mode mode
,
264 ProxyConfigServiceImpl::ProxyConfig
* config
,
265 ProxyConfigServiceImpl::ProxyConfig::AutomaticProxy
* automatic_proxy
) {
267 config
->state
= ProxyPrefs::CONFIG_SYSTEM
;
269 automatic_proxy
->pac_url
= GURL(pac_url
);
273 ProxyConfigServiceImpl::ProxyConfig::Mode mode
,
274 const char* server_uri
,
275 net::ProxyServer::Scheme scheme
,
276 ProxyConfigServiceImpl::ProxyConfig
* config
,
277 ProxyConfigServiceImpl::ProxyConfig::ManualProxy
* manual_proxy
) {
281 config
->state
= ProxyPrefs::CONFIG_SYSTEM
;
282 manual_proxy
->server
= net::ProxyServer::FromURI(server_uri
, scheme
);
285 void InitConfigWithTestInput(
286 const Input
& input
, ProxyConfigServiceImpl::ProxyConfig
* test_config
) {
287 switch (input
.mode
) {
288 case MK_MODE(DIRECT
):
289 case MK_MODE(AUTO_DETECT
):
290 case MK_MODE(PAC_SCRIPT
):
291 SetAutomaticProxy(input
.mode
, input
.pac_url
, test_config
,
292 &test_config
->automatic_proxy
);
294 case MK_MODE(SINGLE_PROXY
):
295 SetManualProxy(input
.mode
, input
.single_uri
, MK_SCHM(HTTP
),
296 test_config
, &test_config
->single_proxy
);
298 case MK_MODE(PROXY_PER_SCHEME
):
299 SetManualProxy(input
.mode
, input
.http_uri
, MK_SCHM(HTTP
),
300 test_config
, &test_config
->http_proxy
);
301 SetManualProxy(input
.mode
, input
.https_uri
, MK_SCHM(HTTPS
),
302 test_config
, &test_config
->https_proxy
);
303 SetManualProxy(input
.mode
, input
.ftp_uri
, MK_SCHM(HTTP
),
304 test_config
, &test_config
->ftp_proxy
);
305 SetManualProxy(input
.mode
, input
.socks_uri
, MK_SCHM(SOCKS5
),
306 test_config
, &test_config
->socks_proxy
);
309 if (input
.bypass_rules
)
310 test_config
->bypass_rules
.ParseFromString(input
.bypass_rules
);
313 // Synchronously gets the latest proxy config.
314 net::ProxyConfigService::ConfigAvailability
SyncGetLatestProxyConfig(
315 net::ProxyConfig
* config
) {
316 *config
= net::ProxyConfig();
317 // Let message loop process all messages.
318 loop_
.RunAllPending();
319 // Calls ChromeProIOGetProxyConfig (which is called from
320 // ProxyConfigService::GetLatestProxyConfig), running on faked IO thread.
321 return proxy_config_service_
->GetLatestProxyConfig(config
);
325 scoped_ptr
<ChromeProxyConfigService
> proxy_config_service_
;
326 scoped_ptr
<ProxyConfigServiceImpl
> config_service_impl_
;
329 // Default stub state has ethernet as the active connected network and
330 // PROFILE_SHARED as profile type, which this unittest expects.
331 ScopedStubCrosEnabler stub_cros_enabler_
;
332 content::TestBrowserThread ui_thread_
;
333 content::TestBrowserThread io_thread_
;
336 class ProxyConfigServiceImplTest
337 : public ProxyConfigServiceImplTestBase
<testing::Test
> {
339 virtual void SetUp() {
340 Init(&pref_service_
);
343 TestingPrefService pref_service_
;
346 TEST_F(ProxyConfigServiceImplTest
, NetworkProxy
) {
347 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(tests
); ++i
) {
348 SCOPED_TRACE(StringPrintf("Test[%" PRIuS
"] %s", i
,
349 tests
[i
].description
.c_str()));
351 ProxyConfigServiceImpl::ProxyConfig test_config
;
352 InitConfigWithTestInput(tests
[i
].input
, &test_config
);
353 config_service_impl_
->SetTesting(&test_config
);
355 net::ProxyConfig config
;
356 EXPECT_EQ(MK_AVAIL(VALID
), SyncGetLatestProxyConfig(&config
));
358 EXPECT_EQ(tests
[i
].auto_detect
, config
.auto_detect());
359 EXPECT_EQ(tests
[i
].pac_url
, config
.pac_url());
360 EXPECT_TRUE(tests
[i
].proxy_rules
.Matches(config
.proxy_rules()));
364 TEST_F(ProxyConfigServiceImplTest
, ModifyFromUI
) {
365 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(tests
); ++i
) {
366 SCOPED_TRACE(StringPrintf("Test[%" PRIuS
"] %s", i
,
367 tests
[i
].description
.c_str()));
370 ProxyConfigServiceImpl::ProxyConfig test_config
;
371 SetAutomaticProxy(MK_MODE(DIRECT
), NULL
, &test_config
,
372 &test_config
.automatic_proxy
);
373 config_service_impl_
->SetTesting(&test_config
);
375 // Set config to tests[i].input via UI.
376 net::ProxyBypassRules bypass_rules
;
377 const Input
& input
= tests
[i
].input
;
378 switch (input
.mode
) {
379 case MK_MODE(DIRECT
) :
380 config_service_impl_
->UISetProxyConfigToDirect();
382 case MK_MODE(AUTO_DETECT
) :
383 config_service_impl_
->UISetProxyConfigToAutoDetect();
385 case MK_MODE(PAC_SCRIPT
) :
386 config_service_impl_
->UISetProxyConfigToPACScript(GURL(input
.pac_url
));
388 case MK_MODE(SINGLE_PROXY
) :
389 config_service_impl_
->UISetProxyConfigToSingleProxy(
390 net::ProxyServer::FromURI(input
.single_uri
, MK_SCHM(HTTP
)));
391 if (input
.bypass_rules
) {
392 bypass_rules
.ParseFromString(input
.bypass_rules
);
393 config_service_impl_
->UISetProxyConfigBypassRules(bypass_rules
);
396 case MK_MODE(PROXY_PER_SCHEME
) :
397 if (input
.http_uri
) {
398 config_service_impl_
->UISetProxyConfigToProxyPerScheme("http",
399 net::ProxyServer::FromURI(input
.http_uri
, MK_SCHM(HTTP
)));
401 if (input
.https_uri
) {
402 config_service_impl_
->UISetProxyConfigToProxyPerScheme("https",
403 net::ProxyServer::FromURI(input
.https_uri
, MK_SCHM(HTTPS
)));
406 config_service_impl_
->UISetProxyConfigToProxyPerScheme("ftp",
407 net::ProxyServer::FromURI(input
.ftp_uri
, MK_SCHM(HTTP
)));
409 if (input
.socks_uri
) {
410 config_service_impl_
->UISetProxyConfigToProxyPerScheme("socks",
411 net::ProxyServer::FromURI(input
.socks_uri
, MK_SCHM(SOCKS5
)));
413 if (input
.bypass_rules
) {
414 bypass_rules
.ParseFromString(input
.bypass_rules
);
415 config_service_impl_
->UISetProxyConfigBypassRules(bypass_rules
);
420 // Retrieve config from IO thread.
421 net::ProxyConfig io_config
;
422 EXPECT_EQ(MK_AVAIL(VALID
), SyncGetLatestProxyConfig(&io_config
));
423 EXPECT_EQ(tests
[i
].auto_detect
, io_config
.auto_detect());
424 EXPECT_EQ(tests
[i
].pac_url
, io_config
.pac_url());
425 EXPECT_TRUE(tests
[i
].proxy_rules
.Matches(io_config
.proxy_rules()));
427 // Retrieve config from UI thread.
428 ProxyConfigServiceImpl::ProxyConfig ui_config
;
429 config_service_impl_
->UIGetProxyConfig(&ui_config
);
430 EXPECT_EQ(input
.mode
, ui_config
.mode
);
431 if (tests
[i
].is_valid
) {
433 EXPECT_EQ(GURL(input
.pac_url
), ui_config
.automatic_proxy
.pac_url
);
434 const net::ProxyRulesExpectation
& proxy_rules
= tests
[i
].proxy_rules
;
435 if (input
.single_uri
)
436 EXPECT_EQ(proxy_rules
.single_proxy
,
437 ui_config
.single_proxy
.server
.ToURI());
439 EXPECT_EQ(proxy_rules
.proxy_for_http
,
440 ui_config
.http_proxy
.server
.ToURI());
442 EXPECT_EQ(proxy_rules
.proxy_for_https
,
443 ui_config
.https_proxy
.server
.ToURI());
445 EXPECT_EQ(proxy_rules
.proxy_for_ftp
,
446 ui_config
.ftp_proxy
.server
.ToURI());
447 if (input
.socks_uri
) {
448 EXPECT_EQ(proxy_rules
.fallback_proxy
,
449 ui_config
.socks_proxy
.server
.ToURI());
451 if (input
.bypass_rules
)
452 EXPECT_TRUE(bypass_rules
.Equals(ui_config
.bypass_rules
));
457 TEST_F(ProxyConfigServiceImplTest
, DynamicPrefsOverride
) {
458 // Groupings of 3 test inputs to use for managed, recommended and network
459 // proxies respectively. Only valid and non-direct test inputs are used.
460 const size_t proxies
[][3] = {
482 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(proxies
); ++i
) {
483 const TestParams
& managed_params
= tests
[proxies
[i
][0]];
484 const TestParams
& recommended_params
= tests
[proxies
[i
][1]];
485 const TestParams
& network_params
= tests
[proxies
[i
][2]];
487 SCOPED_TRACE(StringPrintf(
488 "Test[%" PRIuS
"] managed=[%s], recommended=[%s], network=[%s]", i
,
489 managed_params
.description
.c_str(),
490 recommended_params
.description
.c_str(),
491 network_params
.description
.c_str()));
493 ProxyConfigServiceImpl::ProxyConfig managed_config
;
494 InitConfigWithTestInput(managed_params
.input
, &managed_config
);
495 ProxyConfigServiceImpl::ProxyConfig recommended_config
;
496 InitConfigWithTestInput(recommended_params
.input
, &recommended_config
);
497 ProxyConfigServiceImpl::ProxyConfig network_config
;
498 InitConfigWithTestInput(network_params
.input
, &network_config
);
500 // Managed proxy pref should take effect over recommended proxy and
501 // non-existent network proxy.
502 config_service_impl_
->SetTesting(NULL
);
503 pref_service_
.SetManagedPref(prefs::kProxy
,
504 managed_config
.ToPrefProxyConfig());
505 pref_service_
.SetRecommendedPref(prefs::kProxy
,
506 recommended_config
.ToPrefProxyConfig());
507 net::ProxyConfig actual_config
;
508 EXPECT_EQ(MK_AVAIL(VALID
), SyncGetLatestProxyConfig(&actual_config
));
509 EXPECT_EQ(managed_params
.auto_detect
, actual_config
.auto_detect());
510 EXPECT_EQ(managed_params
.pac_url
, actual_config
.pac_url());
511 EXPECT_TRUE(managed_params
.proxy_rules
.Matches(
512 actual_config
.proxy_rules()));
514 // Recommended proxy pref should take effect when managed proxy pref is
516 pref_service_
.RemoveManagedPref(prefs::kProxy
);
517 EXPECT_EQ(MK_AVAIL(VALID
), SyncGetLatestProxyConfig(&actual_config
));
518 EXPECT_EQ(recommended_params
.auto_detect
, actual_config
.auto_detect());
519 EXPECT_EQ(recommended_params
.pac_url
, actual_config
.pac_url());
520 EXPECT_TRUE(recommended_params
.proxy_rules
.Matches(
521 actual_config
.proxy_rules()));
523 // Network proxy should take take effect over recommended proxy pref.
524 config_service_impl_
->SetTesting(&network_config
);
525 EXPECT_EQ(MK_AVAIL(VALID
), SyncGetLatestProxyConfig(&actual_config
));
526 EXPECT_EQ(network_params
.auto_detect
, actual_config
.auto_detect());
527 EXPECT_EQ(network_params
.pac_url
, actual_config
.pac_url());
528 EXPECT_TRUE(network_params
.proxy_rules
.Matches(
529 actual_config
.proxy_rules()));
531 // Managed proxy pref should take effect over network proxy.
532 pref_service_
.SetManagedPref(prefs::kProxy
,
533 managed_config
.ToPrefProxyConfig());
534 EXPECT_EQ(MK_AVAIL(VALID
), SyncGetLatestProxyConfig(&actual_config
));
535 EXPECT_EQ(managed_params
.auto_detect
, actual_config
.auto_detect());
536 EXPECT_EQ(managed_params
.pac_url
, actual_config
.pac_url());
537 EXPECT_TRUE(managed_params
.proxy_rules
.Matches(
538 actual_config
.proxy_rules()));
540 // Network proxy should take effect over recommended proxy pref when managed
541 // proxy pref is removed.
542 pref_service_
.RemoveManagedPref(prefs::kProxy
);
543 EXPECT_EQ(MK_AVAIL(VALID
), SyncGetLatestProxyConfig(&actual_config
));
544 EXPECT_EQ(network_params
.auto_detect
, actual_config
.auto_detect());
545 EXPECT_EQ(network_params
.pac_url
, actual_config
.pac_url());
546 EXPECT_TRUE(network_params
.proxy_rules
.Matches(
547 actual_config
.proxy_rules()));
549 // Removing recommended proxy pref should have no effect on network proxy.
550 pref_service_
.RemoveRecommendedPref(prefs::kProxy
);
551 EXPECT_EQ(MK_AVAIL(VALID
), SyncGetLatestProxyConfig(&actual_config
));
552 EXPECT_EQ(network_params
.auto_detect
, actual_config
.auto_detect());
553 EXPECT_EQ(network_params
.pac_url
, actual_config
.pac_url());
554 EXPECT_TRUE(network_params
.proxy_rules
.Matches(
555 actual_config
.proxy_rules()));
561 } // namespace chromeos