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:
ProxyConfigServiceImplTest()213 ProxyConfigServiceImplTest()
214 : ui_thread_(BrowserThread::UI, &loop_),
215 io_thread_(BrowserThread::IO, &loop_) {}
216
SetUp()217 virtual void SetUp() {
218 DBusThreadManager::Initialize();
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
SetUpNetwork()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("/service/stub_wifi2",
253 "stub_wifi2" /* guid */,
254 "wifi2_PSK",
255 shill::kTypeWifi, shill::kStateOnline,
256 true /* visible */);
257 profile_test->AddService(kUserProfilePath, "/service/stub_wifi2");
258
259 loop_.RunUntilIdle();
260 }
261
TearDown()262 virtual void TearDown() {
263 config_service_impl_->DetachFromPrefService();
264 loop_.RunUntilIdle();
265 config_service_impl_.reset();
266 proxy_config_service_.reset();
267 NetworkHandler::Shutdown();
268 DBusThreadManager::Shutdown();
269 }
270
InitConfigWithTestInput(const Input & input,base::DictionaryValue * result)271 void InitConfigWithTestInput(const Input& input,
272 base::DictionaryValue* result) {
273 base::DictionaryValue* new_config = NULL;
274 switch (input.mode) {
275 case MK_MODE(DIRECT):
276 new_config = ProxyConfigDictionary::CreateDirect();
277 break;
278 case MK_MODE(AUTO_DETECT):
279 new_config = ProxyConfigDictionary::CreateAutoDetect();
280 break;
281 case MK_MODE(PAC_SCRIPT):
282 new_config =
283 ProxyConfigDictionary::CreatePacScript(input.pac_url, false);
284 break;
285 case MK_MODE(SINGLE_PROXY):
286 case MK_MODE(PROXY_PER_SCHEME):
287 new_config =
288 ProxyConfigDictionary::CreateFixedServers(input.server,
289 input.bypass_rules);
290 break;
291 }
292 result->Swap(new_config);
293 delete new_config;
294 }
295
SetConfig(base::DictionaryValue * pref_proxy_config_dict)296 void SetConfig(base::DictionaryValue* pref_proxy_config_dict) {
297 std::string proxy_config;
298 if (pref_proxy_config_dict)
299 base::JSONWriter::Write(pref_proxy_config_dict, &proxy_config);
300
301 NetworkStateHandler* network_state_handler =
302 NetworkHandler::Get()->network_state_handler();
303 const NetworkState* network = network_state_handler->DefaultNetwork();
304 ASSERT_TRUE(network);
305 DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface()->
306 SetServiceProperty(network->path(),
307 shill::kProxyConfigProperty,
308 base::StringValue(proxy_config));
309 }
310
311 // Synchronously gets the latest proxy config.
SyncGetLatestProxyConfig(net::ProxyConfig * config)312 void SyncGetLatestProxyConfig(net::ProxyConfig* config) {
313 *config = net::ProxyConfig();
314 // Let message loop process all messages. This will run
315 // ChromeProxyConfigService::UpdateProxyConfig, which is posted on IO from
316 // PrefProxyConfigTrackerImpl::OnProxyConfigChanged.
317 loop_.RunUntilIdle();
318 net::ProxyConfigService::ConfigAvailability availability =
319 proxy_config_service_->GetLatestProxyConfig(config);
320
321 EXPECT_EQ(net::ProxyConfigService::CONFIG_VALID, availability);
322 }
323
324 base::MessageLoop loop_;
325 scoped_ptr<net::ProxyConfigService> proxy_config_service_;
326 scoped_ptr<ProxyConfigServiceImpl> config_service_impl_;
327 TestingPrefServiceSimple pref_service_;
328
329 private:
330 ScopedTestDeviceSettingsService test_device_settings_service_;
331 ScopedTestCrosSettings test_cros_settings_;
332 content::TestBrowserThread ui_thread_;
333 content::TestBrowserThread io_thread_;
334 };
335
TEST_F(ProxyConfigServiceImplTest,NetworkProxy)336 TEST_F(ProxyConfigServiceImplTest, NetworkProxy) {
337 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
338 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i,
339 tests[i].description.c_str()));
340
341 base::DictionaryValue test_config;
342 InitConfigWithTestInput(tests[i].input, &test_config);
343 SetConfig(&test_config);
344
345 net::ProxyConfig config;
346 SyncGetLatestProxyConfig(&config);
347
348 EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
349 EXPECT_EQ(tests[i].pac_url, config.pac_url());
350 EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
351 }
352 }
353
TEST_F(ProxyConfigServiceImplTest,DynamicPrefsOverride)354 TEST_F(ProxyConfigServiceImplTest, DynamicPrefsOverride) {
355 // Groupings of 3 test inputs to use for managed, recommended and network
356 // proxies respectively. Only valid and non-direct test inputs are used.
357 const size_t proxies[][3] = {
358 { 1, 2, 4, },
359 { 1, 4, 2, },
360 { 4, 2, 1, },
361 { 2, 1, 4, },
362 { 2, 4, 5, },
363 { 2, 5, 4, },
364 { 5, 4, 2, },
365 { 4, 2, 5, },
366 { 4, 5, 6, },
367 { 4, 6, 5, },
368 { 6, 5, 4, },
369 { 5, 4, 6, },
370 { 5, 6, 7, },
371 { 5, 7, 6, },
372 { 7, 6, 5, },
373 { 6, 5, 7, },
374 { 6, 7, 8, },
375 { 6, 8, 7, },
376 { 8, 7, 6, },
377 { 7, 6, 8, },
378 };
379 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(proxies); ++i) {
380 const TestParams& managed_params = tests[proxies[i][0]];
381 const TestParams& recommended_params = tests[proxies[i][1]];
382 const TestParams& network_params = tests[proxies[i][2]];
383
384 SCOPED_TRACE(base::StringPrintf(
385 "Test[%" PRIuS "] managed=[%s], recommended=[%s], network=[%s]", i,
386 managed_params.description.c_str(),
387 recommended_params.description.c_str(),
388 network_params.description.c_str()));
389
390 base::DictionaryValue managed_config;
391 InitConfigWithTestInput(managed_params.input, &managed_config);
392 base::DictionaryValue recommended_config;
393 InitConfigWithTestInput(recommended_params.input, &recommended_config);
394 base::DictionaryValue network_config;
395 InitConfigWithTestInput(network_params.input, &network_config);
396
397 // Managed proxy pref should take effect over recommended proxy and
398 // non-existent network proxy.
399 SetConfig(NULL);
400 pref_service_.SetManagedPref(prefs::kProxy, managed_config.DeepCopy());
401 pref_service_.SetRecommendedPref(prefs::kProxy,
402 recommended_config.DeepCopy());
403 net::ProxyConfig actual_config;
404 SyncGetLatestProxyConfig(&actual_config);
405 EXPECT_EQ(managed_params.auto_detect, actual_config.auto_detect());
406 EXPECT_EQ(managed_params.pac_url, actual_config.pac_url());
407 EXPECT_TRUE(managed_params.proxy_rules.Matches(
408 actual_config.proxy_rules()));
409
410 // Recommended proxy pref should take effect when managed proxy pref is
411 // removed.
412 pref_service_.RemoveManagedPref(prefs::kProxy);
413 SyncGetLatestProxyConfig(&actual_config);
414 EXPECT_EQ(recommended_params.auto_detect, actual_config.auto_detect());
415 EXPECT_EQ(recommended_params.pac_url, actual_config.pac_url());
416 EXPECT_TRUE(recommended_params.proxy_rules.Matches(
417 actual_config.proxy_rules()));
418
419 // Network proxy should take take effect over recommended proxy pref.
420 SetConfig(&network_config);
421 SyncGetLatestProxyConfig(&actual_config);
422 EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
423 EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
424 EXPECT_TRUE(network_params.proxy_rules.Matches(
425 actual_config.proxy_rules()));
426
427 // Managed proxy pref should take effect over network proxy.
428 pref_service_.SetManagedPref(prefs::kProxy, managed_config.DeepCopy());
429 SyncGetLatestProxyConfig(&actual_config);
430 EXPECT_EQ(managed_params.auto_detect, actual_config.auto_detect());
431 EXPECT_EQ(managed_params.pac_url, actual_config.pac_url());
432 EXPECT_TRUE(managed_params.proxy_rules.Matches(
433 actual_config.proxy_rules()));
434
435 // Network proxy should take effect over recommended proxy pref when managed
436 // proxy pref is removed.
437 pref_service_.RemoveManagedPref(prefs::kProxy);
438 SyncGetLatestProxyConfig(&actual_config);
439 EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
440 EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
441 EXPECT_TRUE(network_params.proxy_rules.Matches(
442 actual_config.proxy_rules()));
443
444 // Removing recommended proxy pref should have no effect on network proxy.
445 pref_service_.RemoveRecommendedPref(prefs::kProxy);
446 SyncGetLatestProxyConfig(&actual_config);
447 EXPECT_EQ(network_params.auto_detect, actual_config.auto_detect());
448 EXPECT_EQ(network_params.pac_url, actual_config.pac_url());
449 EXPECT_TRUE(network_params.proxy_rules.Matches(
450 actual_config.proxy_rules()));
451 }
452 }
453
454 } // namespace chromeos
455