1// Copyright (c) 2011 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. 4 5#include "chrome/browser/net/pref_proxy_config_tracker_impl.h" 6 7#include "base/command_line.h" 8#include "base/files/file_path.h" 9#include "base/message_loop/message_loop.h" 10#include "base/prefs/pref_registry_simple.h" 11#include "base/prefs/testing_pref_service.h" 12#include "chrome/browser/prefs/pref_service_mock_factory.h" 13#include "chrome/browser/prefs/proxy_config_dictionary.h" 14#include "chrome/common/chrome_switches.h" 15#include "chrome/common/pref_names.h" 16#include "content/public/test/test_browser_thread.h" 17#include "net/proxy/proxy_config_service_common_unittest.h" 18#include "testing/gmock/include/gmock/gmock.h" 19#include "testing/gtest/include/gtest/gtest.h" 20 21using content::BrowserThread; 22using testing::_; 23using testing::Mock; 24 25namespace { 26 27const char kFixedPacUrl[] = "http://chromium.org/fixed_pac_url"; 28 29// Testing proxy config service that allows us to fire notifications at will. 30class TestProxyConfigService : public net::ProxyConfigService { 31 public: 32 TestProxyConfigService(const net::ProxyConfig& config, 33 ConfigAvailability availability) 34 : config_(config), 35 availability_(availability) {} 36 37 void SetProxyConfig(const net::ProxyConfig config, 38 ConfigAvailability availability) { 39 config_ = config; 40 availability_ = availability; 41 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, 42 OnProxyConfigChanged(config, availability)); 43 } 44 45 private: 46 virtual void AddObserver( 47 net::ProxyConfigService::Observer* observer) OVERRIDE { 48 observers_.AddObserver(observer); 49 } 50 51 virtual void RemoveObserver( 52 net::ProxyConfigService::Observer* observer) OVERRIDE { 53 observers_.RemoveObserver(observer); 54 } 55 56 virtual net::ProxyConfigService::ConfigAvailability GetLatestProxyConfig( 57 net::ProxyConfig* config) OVERRIDE { 58 *config = config_; 59 return availability_; 60 } 61 62 net::ProxyConfig config_; 63 ConfigAvailability availability_; 64 ObserverList<net::ProxyConfigService::Observer, true> observers_; 65}; 66 67// A mock observer for capturing callbacks. 68class MockObserver : public net::ProxyConfigService::Observer { 69 public: 70 MOCK_METHOD2(OnProxyConfigChanged, 71 void(const net::ProxyConfig&, 72 net::ProxyConfigService::ConfigAvailability)); 73}; 74 75template<typename TESTBASE> 76class PrefProxyConfigTrackerImplTestBase : public TESTBASE { 77 protected: 78 PrefProxyConfigTrackerImplTestBase() 79 : ui_thread_(BrowserThread::UI, &loop_), 80 io_thread_(BrowserThread::IO, &loop_) {} 81 82 virtual void Init(PrefService* pref_service, PrefRegistrySimple* registry) { 83 ASSERT_TRUE(pref_service); 84 PrefProxyConfigTrackerImpl::RegisterPrefs(registry); 85 fixed_config_.set_pac_url(GURL(kFixedPacUrl)); 86 delegate_service_ = 87 new TestProxyConfigService(fixed_config_, 88 net::ProxyConfigService::CONFIG_VALID); 89 proxy_config_tracker_.reset(new PrefProxyConfigTrackerImpl(pref_service)); 90 proxy_config_service_ = 91 proxy_config_tracker_->CreateTrackingProxyConfigService( 92 scoped_ptr<net::ProxyConfigService>(delegate_service_)); 93 // SetChromeProxyConfigService triggers update of initial prefs proxy 94 // config by tracker to chrome proxy config service, so flush all pending 95 // tasks so that tests start fresh. 96 loop_.RunUntilIdle(); 97 } 98 99 virtual void TearDown() { 100 proxy_config_tracker_->DetachFromPrefService(); 101 loop_.RunUntilIdle(); 102 proxy_config_tracker_.reset(); 103 proxy_config_service_.reset(); 104 } 105 106 base::MessageLoop loop_; 107 TestProxyConfigService* delegate_service_; // weak 108 scoped_ptr<net::ProxyConfigService> proxy_config_service_; 109 net::ProxyConfig fixed_config_; 110 111 private: 112 scoped_ptr<PrefProxyConfigTrackerImpl> proxy_config_tracker_; 113 content::TestBrowserThread ui_thread_; 114 content::TestBrowserThread io_thread_; 115}; 116 117class PrefProxyConfigTrackerImplTest 118 : public PrefProxyConfigTrackerImplTestBase<testing::Test> { 119 protected: 120 virtual void SetUp() { 121 pref_service_.reset(new TestingPrefServiceSimple()); 122 Init(pref_service_.get(), pref_service_->registry()); 123 } 124 125 scoped_ptr<TestingPrefServiceSimple> pref_service_; 126}; 127 128TEST_F(PrefProxyConfigTrackerImplTest, BaseConfiguration) { 129 net::ProxyConfig actual_config; 130 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 131 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 132 EXPECT_EQ(GURL(kFixedPacUrl), actual_config.pac_url()); 133} 134 135TEST_F(PrefProxyConfigTrackerImplTest, DynamicPrefOverrides) { 136 pref_service_->SetManagedPref(prefs::kProxy, 137 ProxyConfigDictionary::CreateFixedServers( 138 "http://example.com:3128", std::string())); 139 loop_.RunUntilIdle(); 140 141 net::ProxyConfig actual_config; 142 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 143 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 144 EXPECT_FALSE(actual_config.auto_detect()); 145 EXPECT_EQ(net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY, 146 actual_config.proxy_rules().type); 147 EXPECT_EQ(actual_config.proxy_rules().single_proxies.Get(), 148 net::ProxyServer::FromURI("http://example.com:3128", 149 net::ProxyServer::SCHEME_HTTP)); 150 151 pref_service_->SetManagedPref(prefs::kProxy, 152 ProxyConfigDictionary::CreateAutoDetect()); 153 loop_.RunUntilIdle(); 154 155 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 156 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 157 EXPECT_TRUE(actual_config.auto_detect()); 158} 159 160// Compares proxy configurations, but allows different identifiers. 161MATCHER_P(ProxyConfigMatches, config, "") { 162 net::ProxyConfig reference(config); 163 reference.set_id(arg.id()); 164 return reference.Equals(arg); 165} 166 167TEST_F(PrefProxyConfigTrackerImplTest, Observers) { 168 const net::ProxyConfigService::ConfigAvailability CONFIG_VALID = 169 net::ProxyConfigService::CONFIG_VALID; 170 MockObserver observer; 171 proxy_config_service_->AddObserver(&observer); 172 173 // Firing the observers in the delegate should trigger a notification. 174 net::ProxyConfig config2; 175 config2.set_auto_detect(true); 176 EXPECT_CALL(observer, OnProxyConfigChanged(ProxyConfigMatches(config2), 177 CONFIG_VALID)).Times(1); 178 delegate_service_->SetProxyConfig(config2, CONFIG_VALID); 179 loop_.RunUntilIdle(); 180 Mock::VerifyAndClearExpectations(&observer); 181 182 // Override configuration, this should trigger a notification. 183 net::ProxyConfig pref_config; 184 pref_config.set_pac_url(GURL(kFixedPacUrl)); 185 186 EXPECT_CALL(observer, OnProxyConfigChanged(ProxyConfigMatches(pref_config), 187 CONFIG_VALID)).Times(1); 188 pref_service_->SetManagedPref( 189 prefs::kProxy, 190 ProxyConfigDictionary::CreatePacScript(kFixedPacUrl, false)); 191 loop_.RunUntilIdle(); 192 Mock::VerifyAndClearExpectations(&observer); 193 194 // Since there are pref overrides, delegate changes should be ignored. 195 net::ProxyConfig config3; 196 config3.proxy_rules().ParseFromString("http=config3:80"); 197 EXPECT_CALL(observer, OnProxyConfigChanged(_, _)).Times(0); 198 fixed_config_.set_auto_detect(true); 199 delegate_service_->SetProxyConfig(config3, CONFIG_VALID); 200 loop_.RunUntilIdle(); 201 Mock::VerifyAndClearExpectations(&observer); 202 203 // Clear the override should switch back to the fixed configuration. 204 EXPECT_CALL(observer, OnProxyConfigChanged(ProxyConfigMatches(config3), 205 CONFIG_VALID)).Times(1); 206 pref_service_->RemoveManagedPref(prefs::kProxy); 207 loop_.RunUntilIdle(); 208 Mock::VerifyAndClearExpectations(&observer); 209 210 // Delegate service notifications should show up again. 211 net::ProxyConfig config4; 212 config4.proxy_rules().ParseFromString("socks:config4"); 213 EXPECT_CALL(observer, OnProxyConfigChanged(ProxyConfigMatches(config4), 214 CONFIG_VALID)).Times(1); 215 delegate_service_->SetProxyConfig(config4, CONFIG_VALID); 216 loop_.RunUntilIdle(); 217 Mock::VerifyAndClearExpectations(&observer); 218 219 proxy_config_service_->RemoveObserver(&observer); 220} 221 222TEST_F(PrefProxyConfigTrackerImplTest, Fallback) { 223 const net::ProxyConfigService::ConfigAvailability CONFIG_VALID = 224 net::ProxyConfigService::CONFIG_VALID; 225 MockObserver observer; 226 net::ProxyConfig actual_config; 227 delegate_service_->SetProxyConfig(net::ProxyConfig::CreateDirect(), 228 net::ProxyConfigService::CONFIG_UNSET); 229 proxy_config_service_->AddObserver(&observer); 230 231 // Prepare test data. 232 net::ProxyConfig recommended_config = net::ProxyConfig::CreateAutoDetect(); 233 net::ProxyConfig user_config = 234 net::ProxyConfig::CreateFromCustomPacURL(GURL(kFixedPacUrl)); 235 236 // Set a recommended pref. 237 EXPECT_CALL(observer, 238 OnProxyConfigChanged(ProxyConfigMatches(recommended_config), 239 CONFIG_VALID)).Times(1); 240 pref_service_->SetRecommendedPref( 241 prefs::kProxy, 242 ProxyConfigDictionary::CreateAutoDetect()); 243 loop_.RunUntilIdle(); 244 Mock::VerifyAndClearExpectations(&observer); 245 EXPECT_EQ(CONFIG_VALID, 246 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 247 EXPECT_TRUE(actual_config.Equals(recommended_config)); 248 249 // Override in user prefs. 250 EXPECT_CALL(observer, 251 OnProxyConfigChanged(ProxyConfigMatches(user_config), 252 CONFIG_VALID)).Times(1); 253 pref_service_->SetManagedPref( 254 prefs::kProxy, 255 ProxyConfigDictionary::CreatePacScript(kFixedPacUrl, false)); 256 loop_.RunUntilIdle(); 257 Mock::VerifyAndClearExpectations(&observer); 258 EXPECT_EQ(CONFIG_VALID, 259 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 260 EXPECT_TRUE(actual_config.Equals(user_config)); 261 262 // Go back to recommended pref. 263 EXPECT_CALL(observer, 264 OnProxyConfigChanged(ProxyConfigMatches(recommended_config), 265 CONFIG_VALID)).Times(1); 266 pref_service_->RemoveManagedPref(prefs::kProxy); 267 loop_.RunUntilIdle(); 268 Mock::VerifyAndClearExpectations(&observer); 269 EXPECT_EQ(CONFIG_VALID, 270 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 271 EXPECT_TRUE(actual_config.Equals(recommended_config)); 272 273 proxy_config_service_->RemoveObserver(&observer); 274} 275 276TEST_F(PrefProxyConfigTrackerImplTest, ExplicitSystemSettings) { 277 pref_service_->SetRecommendedPref( 278 prefs::kProxy, 279 ProxyConfigDictionary::CreateAutoDetect()); 280 pref_service_->SetUserPref( 281 prefs::kProxy, 282 ProxyConfigDictionary::CreateSystem()); 283 loop_.RunUntilIdle(); 284 285 // Test if we actually use the system setting, which is |kFixedPacUrl|. 286 net::ProxyConfig actual_config; 287 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 288 proxy_config_service_->GetLatestProxyConfig(&actual_config)); 289 EXPECT_EQ(GURL(kFixedPacUrl), actual_config.pac_url()); 290} 291 292// Test parameter object for testing command line proxy configuration. 293struct CommandLineTestParams { 294 // Explicit assignment operator, so testing::TestWithParam works with MSVC. 295 CommandLineTestParams& operator=(const CommandLineTestParams& other) { 296 description = other.description; 297 for (unsigned int i = 0; i < arraysize(switches); i++) 298 switches[i] = other.switches[i]; 299 is_null = other.is_null; 300 auto_detect = other.auto_detect; 301 pac_url = other.pac_url; 302 proxy_rules = other.proxy_rules; 303 return *this; 304 } 305 306 // Short description to identify the test. 307 const char* description; 308 309 // The command line to build a ProxyConfig from. 310 struct SwitchValue { 311 const char* name; 312 const char* value; 313 } switches[2]; 314 315 // Expected outputs (fields of the ProxyConfig). 316 bool is_null; 317 bool auto_detect; 318 GURL pac_url; 319 net::ProxyRulesExpectation proxy_rules; 320}; 321 322void PrintTo(const CommandLineTestParams& params, std::ostream* os) { 323 *os << params.description; 324} 325 326class PrefProxyConfigTrackerImplCommandLineTest 327 : public PrefProxyConfigTrackerImplTestBase< 328 testing::TestWithParam<CommandLineTestParams> > { 329 protected: 330 PrefProxyConfigTrackerImplCommandLineTest() 331 : command_line_(CommandLine::NO_PROGRAM) {} 332 333 virtual void SetUp() { 334 for (size_t i = 0; i < arraysize(GetParam().switches); i++) { 335 const char* name = GetParam().switches[i].name; 336 const char* value = GetParam().switches[i].value; 337 if (name && value) 338 command_line_.AppendSwitchASCII(name, value); 339 else if (name) 340 command_line_.AppendSwitch(name); 341 } 342 scoped_refptr<PrefRegistrySimple> registry = new PrefRegistrySimple; 343 PrefServiceMockFactory factory; 344 factory.SetCommandLine(&command_line_); 345 pref_service_ = factory.Create(registry.get()).Pass(); 346 Init(pref_service_.get(), registry.get()); 347 } 348 349 private: 350 CommandLine command_line_; 351 scoped_ptr<PrefService> pref_service_; 352}; 353 354TEST_P(PrefProxyConfigTrackerImplCommandLineTest, CommandLine) { 355 net::ProxyConfig config; 356 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, 357 proxy_config_service_->GetLatestProxyConfig(&config)); 358 359 if (GetParam().is_null) { 360 EXPECT_EQ(GURL(kFixedPacUrl), config.pac_url()); 361 } else { 362 EXPECT_NE(GURL(kFixedPacUrl), config.pac_url()); 363 EXPECT_EQ(GetParam().auto_detect, config.auto_detect()); 364 EXPECT_EQ(GetParam().pac_url, config.pac_url()); 365 EXPECT_TRUE(GetParam().proxy_rules.Matches(config.proxy_rules())); 366 } 367} 368 369static const CommandLineTestParams kCommandLineTestParams[] = { 370 { 371 "Empty command line", 372 // Input 373 { }, 374 // Expected result 375 true, // is_null 376 false, // auto_detect 377 GURL(), // pac_url 378 net::ProxyRulesExpectation::Empty(), 379 }, 380 { 381 "No proxy", 382 // Input 383 { 384 { switches::kNoProxyServer, NULL }, 385 }, 386 // Expected result 387 false, // is_null 388 false, // auto_detect 389 GURL(), // pac_url 390 net::ProxyRulesExpectation::Empty(), 391 }, 392 { 393 "No proxy with extra parameters.", 394 // Input 395 { 396 { switches::kNoProxyServer, NULL }, 397 { switches::kProxyServer, "http://proxy:8888" }, 398 }, 399 // Expected result 400 false, // is_null 401 false, // auto_detect 402 GURL(), // pac_url 403 net::ProxyRulesExpectation::Empty(), 404 }, 405 { 406 "Single proxy.", 407 // Input 408 { 409 { switches::kProxyServer, "http://proxy:8888" }, 410 }, 411 // Expected result 412 false, // is_null 413 false, // auto_detect 414 GURL(), // pac_url 415 net::ProxyRulesExpectation::Single( 416 "proxy:8888", // single proxy 417 ""), // bypass rules 418 }, 419 { 420 "Per scheme proxy.", 421 // Input 422 { 423 { switches::kProxyServer, "http=httpproxy:8888;ftp=ftpproxy:8889" }, 424 }, 425 // Expected result 426 false, // is_null 427 false, // auto_detect 428 GURL(), // pac_url 429 net::ProxyRulesExpectation::PerScheme( 430 "httpproxy:8888", // http 431 "", // https 432 "ftpproxy:8889", // ftp 433 ""), // bypass rules 434 }, 435 { 436 "Per scheme proxy with bypass URLs.", 437 // Input 438 { 439 { switches::kProxyServer, "http=httpproxy:8888;ftp=ftpproxy:8889" }, 440 { switches::kProxyBypassList, 441 ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8" }, 442 }, 443 // Expected result 444 false, // is_null 445 false, // auto_detect 446 GURL(), // pac_url 447 net::ProxyRulesExpectation::PerScheme( 448 "httpproxy:8888", // http 449 "", // https 450 "ftpproxy:8889", // ftp 451 "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"), 452 }, 453 { 454 "Pac URL", 455 // Input 456 { 457 { switches::kProxyPacUrl, "http://wpad/wpad.dat" }, 458 }, 459 // Expected result 460 false, // is_null 461 false, // auto_detect 462 GURL("http://wpad/wpad.dat"), // pac_url 463 net::ProxyRulesExpectation::Empty(), 464 }, 465 { 466 "Autodetect", 467 // Input 468 { 469 { switches::kProxyAutoDetect, NULL }, 470 }, 471 // Expected result 472 false, // is_null 473 true, // auto_detect 474 GURL(), // pac_url 475 net::ProxyRulesExpectation::Empty(), 476 }, 477}; 478 479INSTANTIATE_TEST_CASE_P( 480 PrefProxyConfigTrackerImplCommandLineTestInstance, 481 PrefProxyConfigTrackerImplCommandLineTest, 482 testing::ValuesIn(kCommandLineTestParams)); 483 484} // namespace 485