Home | History | Annotate | Download | only in chromeos
      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 <vector>
      8 
      9 #include "base/format_macros.h"
     10 #include "base/json/json_writer.h"
     11 #include "base/logging.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/prefs/testing_pref_service.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "chrome/browser/chromeos/settings/cros_settings.h"
     16 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     17 #include "chrome/browser/chromeos/ui_proxy_config.h"
     18 #include "chrome/common/pref_names.h"
     19 #include "chromeos/dbus/dbus_thread_manager.h"
     20 #include "chromeos/dbus/shill_profile_client.h"
     21 #include "chromeos/dbus/shill_service_client.h"
     22 #include "chromeos/network/network_handler.h"
     23 #include "chromeos/network/network_state.h"
     24 #include "chromeos/network/network_state_handler.h"
     25 #include "content/public/test/test_browser_thread.h"
     26 #include "net/proxy/proxy_config_service_common_unittest.h"
     27 #include "testing/gtest/include/gtest/gtest.h"
     28 #include "third_party/cros_system_api/dbus/service_constants.h"
     29 
     30 using content::BrowserThread;
     31 
     32 namespace chromeos {
     33 
     34 namespace {
     35 
     36 struct Input {
     37   UIProxyConfig::Mode mode;
     38   std::string pac_url;
     39   std::string server;
     40   std::string bypass_rules;
     41 };
     42 
     43 // Builds an identifier for each test in an array.
     44 #define TEST_DESC(desc) base::StringPrintf("at line %d <%s>", __LINE__, desc)
     45 
     46 // Shortcuts to declare enums within chromeos's ProxyConfig.
     47 #define MK_MODE(mode) UIProxyConfig::MODE_##mode
     48 
     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;
     53 
     54   Input input;
     55 
     56   // Expected outputs from fields of net::ProxyConfig (via IO).
     57   bool auto_detect;
     58   GURL pac_url;
     59   net::ProxyRulesExpectation proxy_rules;
     60 } tests[] = {
     61   {  // 0
     62     TEST_DESC("No proxying"),
     63 
     64     { // Input.
     65       MK_MODE(DIRECT),  // mode
     66     },
     67 
     68     // Expected result.
     69     false,                                   // auto_detect
     70     GURL(),                                  // pac_url
     71     net::ProxyRulesExpectation::Empty(),     // proxy_rules
     72   },
     73 
     74   {  // 1
     75     TEST_DESC("Auto detect"),
     76 
     77     { // Input.
     78       MK_MODE(AUTO_DETECT),  // mode
     79     },
     80 
     81     // Expected result.
     82     true,                                    // auto_detect
     83     GURL(),                                  // pac_url
     84     net::ProxyRulesExpectation::Empty(),     // proxy_rules
     85   },
     86 
     87   {  // 2
     88     TEST_DESC("Valid PAC URL"),
     89 
     90     { // Input.
     91       MK_MODE(PAC_SCRIPT),     // mode
     92       "http://wpad/wpad.dat",  // pac_url
     93     },
     94 
     95     // Expected result.
     96     false,                                   // auto_detect
     97     GURL("http://wpad/wpad.dat"),            // pac_url
     98     net::ProxyRulesExpectation::Empty(),     // proxy_rules
     99   },
    100 
    101   {  // 3
    102     TEST_DESC("Invalid PAC URL"),
    103 
    104     { // Input.
    105       MK_MODE(PAC_SCRIPT),  // mode
    106       "wpad.dat",           // pac_url
    107     },
    108 
    109     // Expected result.
    110     false,                                   // auto_detect
    111     GURL(),                                  // pac_url
    112     net::ProxyRulesExpectation::Empty(),     // proxy_rules
    113   },
    114 
    115   {  // 4
    116     TEST_DESC("Single-host in proxy list"),
    117 
    118     { // Input.
    119       MK_MODE(SINGLE_PROXY),  // mode
    120       "",                     // pac_url
    121       "www.google.com",       // server
    122     },
    123 
    124     // Expected result.
    125     false,                                   // auto_detect
    126     GURL(),                                  // pac_url
    127     net::ProxyRulesExpectation::Single(      // proxy_rules
    128         "www.google.com:80",                 // single proxy
    129         "<local>"),                          // bypass rules
    130   },
    131 
    132   {  // 5
    133     TEST_DESC("Single-host, different port"),
    134 
    135     { // Input.
    136       MK_MODE(SINGLE_PROXY),  // mode
    137       "",                     // pac_url
    138       "www.google.com:99",    // server
    139     },
    140 
    141     // Expected result.
    142     false,                                   // auto_detect
    143     GURL(),                                  // pac_url
    144     net::ProxyRulesExpectation::Single(      // proxy_rules
    145         "www.google.com:99",                 // single
    146         "<local>"),                          // bypass rules
    147   },
    148 
    149   {  // 6
    150     TEST_DESC("Tolerate a scheme"),
    151 
    152     { // Input.
    153       MK_MODE(SINGLE_PROXY),       // mode
    154       "",                          // pac_url
    155       "http://www.google.com:99",  // server
    156     },
    157 
    158     // Expected result.
    159     false,                                   // auto_detect
    160     GURL(),                                  // pac_url
    161     net::ProxyRulesExpectation::Single(      // proxy_rules
    162         "www.google.com:99",                 // single proxy
    163         "<local>"),                          // bypass rules
    164   },
    165 
    166   {  // 7
    167     TEST_DESC("Per-scheme proxy rules"),
    168 
    169     { // Input.
    170       MK_MODE(PROXY_PER_SCHEME),  // mode
    171       "",                         // pac_url
    172       "http=www.google.com:80;https=https://www.foo.com:110;"
    173       "ftp=ftp.foo.com:121;socks=socks5://socks.com:888",  // server
    174     },
    175 
    176     // Expected result.
    177     false,                          // auto_detect
    178     GURL(),                         // pac_url
    179     net::ProxyRulesExpectation::PerSchemeWithSocks(  // proxy_rules
    180         "www.google.com:80",        // http
    181         "https://www.foo.com:110",  // https
    182         "ftp.foo.com:121",          // ftp
    183         "socks5://socks.com:888",   // fallback proxy
    184         "<local>"),                 // bypass rules
    185   },
    186 
    187   {  // 8
    188     TEST_DESC("Bypass rules"),
    189 
    190     { // Input.
    191       MK_MODE(SINGLE_PROXY),      // mode
    192       "",                         // pac_url
    193       "www.google.com",           // server
    194       "*.google.com, *foo.com:99, 1.2.3.4:22, 127.0.0.1/8",  // bypass_rules
    195     },
    196 
    197     // Expected result.
    198     false,                          // auto_detect
    199     GURL(),                         // pac_url
    200     net::ProxyRulesExpectation::Single(  // proxy_rules
    201         "www.google.com:80",             // single proxy
    202         // bypass_rules
    203         "*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8,<local>"),
    204   },
    205 };  // tests
    206 
    207 const char* kUserProfilePath = "user_profile";
    208 
    209 }  // namespace
    210 
    211 class ProxyConfigServiceImplTest : public testing::Test {
    212  protected:
    213   ProxyConfigServiceImplTest()
    214       : ui_thread_(BrowserThread::UI, &loop_),
    215         io_thread_(BrowserThread::IO, &loop_) {}
    216 
    217   virtual void SetUp() {
    218     DBusThreadManager::InitializeWithStub();
    219     NetworkHandler::Initialize();
    220 
    221     SetUpNetwork();
    222 
    223     PrefProxyConfigTrackerImpl::RegisterPrefs(pref_service_.registry());
    224 
    225     // Create a ProxyConfigServiceImpl like for the system request context.
    226     config_service_impl_.reset(
    227         new ProxyConfigServiceImpl(NULL,  // no profile prefs
    228                                    &pref_service_));
    229     proxy_config_service_ =
    230         config_service_impl_->CreateTrackingProxyConfigService(
    231             scoped_ptr<net::ProxyConfigService>());
    232 
    233     // CreateTrackingProxyConfigService triggers update of initial prefs proxy
    234     // config by tracker to chrome proxy config service, so flush all pending
    235     // tasks so that tests start fresh.
    236     loop_.RunUntilIdle();
    237   }
    238 
    239   void SetUpNetwork() {
    240     ShillProfileClient::TestInterface* profile_test =
    241         DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface();
    242     ShillServiceClient::TestInterface* service_test =
    243         DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
    244 
    245     // Process any pending notifications before clearing services.
    246     loop_.RunUntilIdle();
    247     service_test->ClearServices();
    248 
    249     // Sends a notification about the added profile.
    250     profile_test->AddProfile(kUserProfilePath, "user_hash");
    251 
    252     service_test->AddService("stub_wifi2", "wifi2_PSK",
    253                              shill::kTypeWifi, shill::kStateOnline,
    254                              true /* visible */);
    255     service_test->SetServiceProperty("stub_wifi2",
    256                                      shill::kGuidProperty,
    257                                      base::StringValue("stub_wifi2"));
    258     profile_test->AddService(kUserProfilePath, "stub_wifi2");
    259 
    260     loop_.RunUntilIdle();
    261   }
    262 
    263   virtual void TearDown() {
    264     config_service_impl_->DetachFromPrefService();
    265     loop_.RunUntilIdle();
    266     config_service_impl_.reset();
    267     proxy_config_service_.reset();
    268     NetworkHandler::Shutdown();
    269     DBusThreadManager::Shutdown();
    270   }
    271 
    272   void InitConfigWithTestInput(const Input& input,
    273                                base::DictionaryValue* result) {
    274     base::DictionaryValue* new_config = NULL;
    275     switch (input.mode) {
    276       case MK_MODE(DIRECT):
    277         new_config = ProxyConfigDictionary::CreateDirect();
    278         break;
    279       case MK_MODE(AUTO_DETECT):
    280         new_config = ProxyConfigDictionary::CreateAutoDetect();
    281         break;
    282       case MK_MODE(PAC_SCRIPT):
    283         new_config =
    284             ProxyConfigDictionary::CreatePacScript(input.pac_url, false);
    285         break;
    286       case MK_MODE(SINGLE_PROXY):
    287       case MK_MODE(PROXY_PER_SCHEME):
    288         new_config =
    289           ProxyConfigDictionary::CreateFixedServers(input.server,
    290                                                     input.bypass_rules);
    291         break;
    292     }
    293     result->Swap(new_config);
    294     delete new_config;
    295   }
    296 
    297   void SetConfig(base::DictionaryValue* pref_proxy_config_dict) {
    298     std::string proxy_config;
    299     if (pref_proxy_config_dict)
    300       base::JSONWriter::Write(pref_proxy_config_dict, &proxy_config);
    301 
    302     NetworkStateHandler* network_state_handler =
    303         NetworkHandler::Get()->network_state_handler();
    304     const NetworkState* network = network_state_handler->DefaultNetwork();
    305     ASSERT_TRUE(network);
    306     DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface()->
    307         SetServiceProperty(network->path(),
    308                            shill::kProxyConfigProperty,
    309                            base::StringValue(proxy_config));
    310   }
    311 
    312   // Synchronously gets the latest proxy config.
    313   void SyncGetLatestProxyConfig(net::ProxyConfig* config) {
    314     *config = net::ProxyConfig();
    315     // Let message loop process all messages. This will run
    316     // ChromeProxyConfigService::UpdateProxyConfig, which is posted on IO from
    317     // PrefProxyConfigTrackerImpl::OnProxyConfigChanged.
    318     loop_.RunUntilIdle();
    319     net::ProxyConfigService::ConfigAvailability availability =
    320         proxy_config_service_->GetLatestProxyConfig(config);
    321 
    322     EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, availability);
    323   }
    324 
    325   base::MessageLoop loop_;
    326   scoped_ptr<net::ProxyConfigService> proxy_config_service_;
    327   scoped_ptr<ProxyConfigServiceImpl> config_service_impl_;
    328   TestingPrefServiceSimple pref_service_;
    329 
    330  private:
    331   ScopedTestDeviceSettingsService test_device_settings_service_;
    332   ScopedTestCrosSettings test_cros_settings_;
    333   content::TestBrowserThread ui_thread_;
    334   content::TestBrowserThread io_thread_;
    335 };
    336 
    337 TEST_F(ProxyConfigServiceImplTest, NetworkProxy) {
    338   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    339     SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
    340                                     tests[i].description.c_str()));
    341 
    342     base::DictionaryValue test_config;
    343     InitConfigWithTestInput(tests[i].input, &test_config);
    344     SetConfig(&test_config);
    345 
    346     net::ProxyConfig config;
    347     SyncGetLatestProxyConfig(&config);
    348 
    349     EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
    350     EXPECT_EQ(tests[i].pac_url, config.pac_url());
    351     EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
    352   }
    353 }
    354 
    355 TEST_F(ProxyConfigServiceImplTest, DynamicPrefsOverride) {
    356   // Groupings of 3 test inputs to use for managed, recommended and network
    357   // proxies respectively.  Only valid and non-direct test inputs are used.
    358   const size_t proxies[][3] = {
    359     { 1, 2, 4, },
    360     { 1, 4, 2, },
    361     { 4, 2, 1, },
    362     { 2, 1, 4, },
    363     { 2, 4, 5, },
    364     { 2, 5, 4, },
    365     { 5, 4, 2, },
    366     { 4, 2, 5, },
    367     { 4, 5, 6, },
    368     { 4, 6, 5, },
    369     { 6, 5, 4, },
    370     { 5, 4, 6, },
    371     { 5, 6, 7, },
    372     { 5, 7, 6, },
    373     { 7, 6, 5, },
    374     { 6, 5, 7, },
    375     { 6, 7, 8, },
    376     { 6, 8, 7, },
    377     { 8, 7, 6, },
    378     { 7, 6, 8, },
    379   };
    380   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(proxies); ++i) {
    381     const TestParams& managed_params = tests[proxies[i][0]];
    382     const TestParams& recommended_params = tests[proxies[i][1]];
    383     const TestParams& network_params = tests[proxies[i][2]];
    384 
    385     SCOPED_TRACE(base::StringPrintf(
    386         "Test[%" PRIuS "] managed=[%s], recommended=[%s], network=[%s]", i,
    387         managed_params.description.c_str(),
    388         recommended_params.description.c_str(),
    389         network_params.description.c_str()));
    390 
    391     base::DictionaryValue managed_config;
    392     InitConfigWithTestInput(managed_params.input, &managed_config);
    393     base::DictionaryValue recommended_config;
    394     InitConfigWithTestInput(recommended_params.input, &recommended_config);
    395     base::DictionaryValue network_config;
    396     InitConfigWithTestInput(network_params.input, &network_config);
    397 
    398     // Managed proxy pref should take effect over recommended proxy and
    399     // non-existent network proxy.
    400     SetConfig(NULL);
    401     pref_service_.SetManagedPref(prefs::kProxy, managed_config.DeepCopy());
    402     pref_service_.SetRecommendedPref(prefs::kProxy,
    403                                      recommended_config.DeepCopy());
    404     net::ProxyConfig actual_config;
    405     SyncGetLatestProxyConfig(&actual_config);
    406     EXPECT_EQ(managed_params.auto_detect, actual_config.auto_detect());
    407     EXPECT_EQ(managed_params.pac_url, actual_config.pac_url());
    408     EXPECT_TRUE(managed_params.proxy_rules.Matches(
    409         actual_config.proxy_rules()));
    410 
    411     // Recommended proxy pref should take effect when managed proxy pref is
    412     // removed.
    413     pref_service_.RemoveManagedPref(prefs::kProxy);
    414     SyncGetLatestProxyConfig(&actual_config);
    415     EXPECT_EQ(recommended_params.auto_detect, actual_config.auto_detect());
    416     EXPECT_EQ(recommended_params.pac_url, actual_config.pac_url());
    417     EXPECT_TRUE(recommended_params.proxy_rules.Matches(
    418         actual_config.proxy_rules()));
    419 
    420     // Network proxy should take take effect over recommended proxy pref.
    421     SetConfig(&network_config);
    422     SyncGetLatestProxyConfig(&actual_config);
    423     EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
    424     EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
    425     EXPECT_TRUE(network_params.proxy_rules.Matches(
    426         actual_config.proxy_rules()));
    427 
    428     // Managed proxy pref should take effect over network proxy.
    429     pref_service_.SetManagedPref(prefs::kProxy, managed_config.DeepCopy());
    430     SyncGetLatestProxyConfig(&actual_config);
    431     EXPECT_EQ(managed_params.auto_detect, actual_config.auto_detect());
    432     EXPECT_EQ(managed_params.pac_url, actual_config.pac_url());
    433     EXPECT_TRUE(managed_params.proxy_rules.Matches(
    434         actual_config.proxy_rules()));
    435 
    436     // Network proxy should take effect over recommended proxy pref when managed
    437     // proxy pref is removed.
    438     pref_service_.RemoveManagedPref(prefs::kProxy);
    439     SyncGetLatestProxyConfig(&actual_config);
    440     EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
    441     EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
    442     EXPECT_TRUE(network_params.proxy_rules.Matches(
    443         actual_config.proxy_rules()));
    444 
    445     // Removing recommended proxy pref should have no effect on network proxy.
    446     pref_service_.RemoveRecommendedPref(prefs::kProxy);
    447     SyncGetLatestProxyConfig(&actual_config);
    448     EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
    449     EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
    450     EXPECT_TRUE(network_params.proxy_rules.Matches(
    451         actual_config.proxy_rules()));
    452   }
    453 }
    454 
    455 }  // namespace chromeos
    456