proxy_config_service_impl_unittest.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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.
4
5#include "chrome/browser/chromeos/proxy_config_service_impl.h"
6
7#include <map>
8#include <string>
9#include <vector>
10
11#include "base/format_macros.h"
12#include "base/logging.h"
13#include "base/message_loop.h"
14#include "base/prefs/testing_pref_service.h"
15#include "base/string_util.h"
16#include "base/stringprintf.h"
17#include "chrome/browser/chromeos/cros/cros_library.h"
18#include "chrome/common/pref_names.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"
23
24using content::BrowserThread;
25
26namespace chromeos {
27
28namespace {
29
30struct Input {  // Fields of chromeos::ProxyConfigServiceImpl::ProxyConfig.
31  ProxyConfigServiceImpl::ProxyConfig::Mode mode;
32  const char* pac_url;
33  const char* single_uri;
34  const char* http_uri;
35  const char* https_uri;
36  const char* ftp_uri;
37  const char* socks_uri;
38  const char* bypass_rules;
39};
40
41// Builds an identifier for each test in an array.
42#define TEST_DESC(desc) base::StringPrintf("at line %d <%s>", __LINE__, desc)
43
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
48
49// Inspired from net/proxy/proxy_config_service_linux_unittest.cc.
50const struct TestParams {
51  // Short description to identify the test
52  std::string description;
53
54  bool is_valid;
55
56  Input input;
57
58  // Expected outputs from fields of net::ProxyConfig (via IO).
59  bool auto_detect;
60  GURL pac_url;
61  net::ProxyRulesExpectation proxy_rules;
62} tests[] = {
63  {  // 0
64    TEST_DESC("No proxying"),
65
66    true,  // is_valid
67
68    { // Input.
69      MK_MODE(DIRECT),  // mode
70    },
71
72    // Expected result.
73    false,                                   // auto_detect
74    GURL(),                                  // pac_url
75    net::ProxyRulesExpectation::Empty(),     // proxy_rules
76  },
77
78  {  // 1
79    TEST_DESC("Auto detect"),
80
81    true,  // is_valid
82
83    { // Input.
84      MK_MODE(AUTO_DETECT),  // mode
85    },
86
87    // Expected result.
88    true,                                    // auto_detect
89    GURL(),                                  // pac_url
90    net::ProxyRulesExpectation::Empty(),     // proxy_rules
91  },
92
93  {  // 2
94    TEST_DESC("Valid PAC URL"),
95
96    true,  // is_valid
97
98    { // Input.
99      MK_MODE(PAC_SCRIPT),     // mode
100      "http://wpad/wpad.dat",  // pac_url
101    },
102
103    // Expected result.
104    false,                                   // auto_detect
105    GURL("http://wpad/wpad.dat"),            // pac_url
106    net::ProxyRulesExpectation::Empty(),     // proxy_rules
107  },
108
109  {  // 3
110    TEST_DESC("Invalid PAC URL"),
111
112    false,  // is_valid
113
114    { // Input.
115      MK_MODE(PAC_SCRIPT),  // mode
116      "wpad.dat",           // pac_url
117    },
118
119    // Expected result.
120    false,                                   // auto_detect
121    GURL(),                                  // pac_url
122    net::ProxyRulesExpectation::Empty(),     // proxy_rules
123  },
124
125  {  // 4
126    TEST_DESC("Single-host in proxy list"),
127
128    true,  // is_valid
129
130    { // Input.
131      MK_MODE(SINGLE_PROXY),  // mode
132      NULL,                   // pac_url
133      "www.google.com",       // single_uri
134    },
135
136    // Expected result.
137    false,                                   // auto_detect
138    GURL(),                                  // pac_url
139    net::ProxyRulesExpectation::Single(      // proxy_rules
140        "www.google.com:80",                 // single proxy
141        "<local>"),                          // bypass rules
142  },
143
144  {  // 5
145    TEST_DESC("Single-host, different port"),
146
147    true,   // is_valid
148
149    { // Input.
150      MK_MODE(SINGLE_PROXY),  // mode
151      NULL,                   // pac_url
152      "www.google.com:99",    // single_uri
153    },
154
155    // Expected result.
156    false,                                   // auto_detect
157    GURL(),                                  // pac_url
158    net::ProxyRulesExpectation::Single(      // proxy_rules
159        "www.google.com:99",                 // single
160        "<local>"),                          // bypass rules
161  },
162
163  {  // 6
164    TEST_DESC("Tolerate a scheme"),
165
166    true,   // is_valid
167
168    { // Input.
169      MK_MODE(SINGLE_PROXY),       // mode
170      NULL,                        // pac_url
171      "http://www.google.com:99",  // single_uri
172    },
173
174    // Expected result.
175    false,                                   // auto_detect
176    GURL(),                                  // pac_url
177    net::ProxyRulesExpectation::Single(      // proxy_rules
178        "www.google.com:99",                 // single proxy
179        "<local>"),                          // bypass rules
180  },
181
182  {  // 7
183    TEST_DESC("Per-scheme proxy rules"),
184
185    true,  // is_valid
186
187    { // Input.
188      MK_MODE(PROXY_PER_SCHEME),  // mode
189      NULL,                       // pac_url
190      NULL,                       // single_uri
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
195    },
196
197    // Expected result.
198    false,                          // auto_detect
199    GURL(),                         // pac_url
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
206  },
207
208  {  // 8
209    TEST_DESC("Bypass rules"),
210
211    true,  // is_valid
212
213    { // Input.
214      MK_MODE(SINGLE_PROXY),      // mode
215      NULL,                       // pac_url
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
219    },
220
221    // Expected result.
222    false,                          // auto_detect
223    GURL(),                         // pac_url
224    net::ProxyRulesExpectation::Single(  // proxy_rules
225        "www.google.com:80",             // single proxy
226        // bypass_rules
227        "*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8,<local>"),
228  },
229};  // tests
230
231template<typename TESTBASE>
232class ProxyConfigServiceImplTestBase : public TESTBASE {
233 protected:
234  ProxyConfigServiceImplTestBase()
235      : ui_thread_(BrowserThread::UI, &loop_),
236        io_thread_(BrowserThread::IO, &loop_) {}
237
238  virtual void Init(TestingPrefServiceSimple* pref_service) {
239    ASSERT_TRUE(pref_service);
240    DBusThreadManager::Initialize();
241    PrefProxyConfigTrackerImpl::RegisterPrefs(pref_service->registry());
242    ProxyConfigServiceImpl::RegisterPrefs(pref_service->registry());
243    proxy_config_service_.reset(new ChromeProxyConfigService(NULL));
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_.RunUntilIdle();
251  }
252
253  virtual void TearDown() {
254    config_service_impl_->DetachFromPrefService();
255    loop_.RunUntilIdle();
256    config_service_impl_.reset();
257    proxy_config_service_.reset();
258    DBusThreadManager::Shutdown();
259  }
260
261  void SetAutomaticProxy(
262      ProxyConfigServiceImpl::ProxyConfig::Mode mode,
263      const char* pac_url,
264      ProxyConfigServiceImpl::ProxyConfig* config,
265      ProxyConfigServiceImpl::ProxyConfig::AutomaticProxy* automatic_proxy) {
266    config->mode = mode;
267    config->state = ProxyPrefs::CONFIG_SYSTEM;
268    if (pac_url)
269      automatic_proxy->pac_url = GURL(pac_url);
270  }
271
272  void SetManualProxy(
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) {
278    if (!server_uri)
279      return;
280    config->mode = mode;
281    config->state = ProxyPrefs::CONFIG_SYSTEM;
282    manual_proxy->server = net::ProxyServer::FromURI(server_uri, scheme);
283  }
284
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);
293        return;
294      case MK_MODE(SINGLE_PROXY):
295        SetManualProxy(input.mode, input.single_uri, MK_SCHM(HTTP),
296                       test_config, &test_config->single_proxy);
297        break;
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);
307        break;
308    }
309    if (input.bypass_rules)
310      test_config->bypass_rules.ParseFromString(input.bypass_rules);
311  }
312
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_.RunUntilIdle();
319    // Calls ChromeProIOGetProxyConfig (which is called from
320    // ProxyConfigService::GetLatestProxyConfig), running on faked IO thread.
321    return proxy_config_service_->GetLatestProxyConfig(config);
322  }
323
324  MessageLoop loop_;
325  scoped_ptr<ChromeProxyConfigService> proxy_config_service_;
326  scoped_ptr<ProxyConfigServiceImpl> config_service_impl_;
327
328 private:
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_;
334};
335
336class ProxyConfigServiceImplTest
337    : public ProxyConfigServiceImplTestBase<testing::Test> {
338 protected:
339  virtual void SetUp() {
340    Init(&pref_service_);
341  }
342
343  TestingPrefServiceSimple pref_service_;
344};
345
346TEST_F(ProxyConfigServiceImplTest, NetworkProxy) {
347  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
348    SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
349                              tests[i].description.c_str()));
350
351    ProxyConfigServiceImpl::ProxyConfig test_config;
352    InitConfigWithTestInput(tests[i].input, &test_config);
353    config_service_impl_->SetTesting(&test_config);
354
355    net::ProxyConfig config;
356    EXPECT_EQ(MK_AVAIL(VALID), SyncGetLatestProxyConfig(&config));
357
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()));
361  }
362}
363
364TEST_F(ProxyConfigServiceImplTest, ModifyFromUI) {
365  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
366    SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
367                              tests[i].description.c_str()));
368
369    // Init with direct.
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);
374
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();
381        break;
382      case MK_MODE(AUTO_DETECT) :
383        config_service_impl_->UISetProxyConfigToAutoDetect();
384        break;
385      case MK_MODE(PAC_SCRIPT) :
386        config_service_impl_->UISetProxyConfigToPACScript(GURL(input.pac_url));
387        break;
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);
394        }
395        break;
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)));
400        }
401        if (input.https_uri) {
402          config_service_impl_->UISetProxyConfigToProxyPerScheme("https",
403              net::ProxyServer::FromURI(input.https_uri, MK_SCHM(HTTPS)));
404        }
405        if (input.ftp_uri) {
406          config_service_impl_->UISetProxyConfigToProxyPerScheme("ftp",
407              net::ProxyServer::FromURI(input.ftp_uri, MK_SCHM(HTTP)));
408        }
409        if (input.socks_uri) {
410          config_service_impl_->UISetProxyConfigToProxyPerScheme("socks",
411              net::ProxyServer::FromURI(input.socks_uri, MK_SCHM(SOCKS5)));
412        }
413        if (input.bypass_rules) {
414          bypass_rules.ParseFromString(input.bypass_rules);
415          config_service_impl_->UISetProxyConfigBypassRules(bypass_rules);
416        }
417        break;
418    }
419
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()));
426
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) {
432      if (input.pac_url)
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());
438      if (input.http_uri)
439        EXPECT_EQ(proxy_rules.proxy_for_http,
440                  ui_config.http_proxy.server.ToURI());
441      if (input.https_uri)
442        EXPECT_EQ(proxy_rules.proxy_for_https,
443                  ui_config.https_proxy.server.ToURI());
444      if (input.ftp_uri)
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());
450      }
451      if (input.bypass_rules)
452        EXPECT_TRUE(bypass_rules.Equals(ui_config.bypass_rules));
453    }
454  }
455}
456
457TEST_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] = {
461    { 1, 2, 4, },
462    { 1, 4, 2, },
463    { 4, 2, 1, },
464    { 2, 1, 4, },
465    { 2, 4, 5, },
466    { 2, 5, 4, },
467    { 5, 4, 2, },
468    { 4, 2, 5, },
469    { 4, 5, 6, },
470    { 4, 6, 5, },
471    { 6, 5, 4, },
472    { 5, 4, 6, },
473    { 5, 6, 7, },
474    { 5, 7, 6, },
475    { 7, 6, 5, },
476    { 6, 5, 7, },
477    { 6, 7, 8, },
478    { 6, 8, 7, },
479    { 8, 7, 6, },
480    { 7, 6, 8, },
481  };
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]];
486
487    SCOPED_TRACE(base::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()));
492
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);
499
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()));
513
514    // Recommended proxy pref should take effect when managed proxy pref is
515    // removed.
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()));
522
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()));
530
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()));
539
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()));
548
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()));
556  }
557}
558
559}  // namespace
560
561}  // namespace chromeos
562