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