Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(89)

Side by Side Diff: net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc

Issue 6831025: Adds support for the DHCP portion of the WPAD (proxy auto-discovery) protocol. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add timeout on Win32 DHCP API. Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "base/message_loop.h"
6 #include "base/perftimer.h"
7 #include "base/rand_util.h"
8 #include "base/threading/platform_thread.h"
9 #include "net/base/completion_callback.h"
10 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
11 #include "net/proxy/dhcp_proxy_script_fetcher_win.h"
12 #include "net/url_request/url_request_test_util.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 #include <vector>
eroman 2011/04/21 05:22:48 move up.
Jói 2011/05/03 21:20:59 Done.
16
17 namespace net {
18 namespace {
19
20 TEST(WindowsDhcpProxyScriptFetcher, AdapterNamesAndPacURLFromDhcp) {
21 // This tests our core Win32 implementation without any of the wrappers
22 // we layer on top to achieve asynchronous and parallel operations.
23 //
24 // We don't make assumptions about the environment this unit test is
25 // running in, so it just exercises the code to make sure there
26 // is no crash and no error returned, but does not assert on the number
27 // of interfaces or the information returned via DHCP.
28 std::set<std::string> adapter_names;
29 WindowsDhcpProxyScriptFetcher::GetCandidateAdapterNames(&adapter_names);
30 for (std::set<std::string>::iterator it = adapter_names.begin();
31 it != adapter_names.end();
32 ++it) {
33 const std::string& adapter_name = *it;
34 std::string pac_url =
35 DhcpProxyScriptAdapterFetcher::GetPacURLFromDhcp(adapter_name);
36 printf("Adapter '%s' has PAC URL '%s' configured in DHCP.\n",
37 adapter_name.c_str(),
38 pac_url.c_str());
39 }
40 }
41
42 // Helper for RealFetch* tests below.
43 class RealFetchTester {
44 public:
45 RealFetchTester()
46 : context_((new TestURLRequestContext())),
47 fetcher_(new WindowsDhcpProxyScriptFetcher(context_.get())),
48 finished_(false),
49 ALLOW_THIS_IN_INITIALIZER_LIST(
50 completion_callback_(this, &RealFetchTester::OnCompletion)),
51 on_completion_is_error_(false) {
52 // Make sure the test ends.
53 timeout_.Start(
54 base::TimeDelta::FromSeconds(5), this, &RealFetchTester::OnTimeout);
55 }
56
57 void RunTest() {
58 fetcher_->Fetch(&pac_text_, &completion_callback_);
59 }
60
61 void RunTestWithCancel() {
62 RunTest();
63 fetcher_->Cancel();
64 }
65
66 void RunTestWithDeferredCancel() {
67 RunTest();
68 cancel_timer_.Start(base::TimeDelta::FromMilliseconds(1),
69 this, &RealFetchTester::OnCancelTimer);
70 }
71
72 void OnCompletion(int result) {
73 if (on_completion_is_error_) {
74 FAIL() << "Received completion for test in which this is error.";
75 }
76 finished_ = true;
77 printf("Result code %d PAC data length %d\n", result, pac_text_.size());
78 }
79
80 void OnTimeout() {
81 printf("Timeout!");
82 OnCompletion(0);
83 }
84
85 void OnCancelTimer() {
86 fetcher_->Cancel();
87 finished_ = true;
88 }
89
90 scoped_refptr<URLRequestContext> context_;
91 scoped_ptr<WindowsDhcpProxyScriptFetcher> fetcher_;
92 bool finished_;
93 string16 pac_text_;
94 CompletionCallbackImpl<RealFetchTester> completion_callback_;
95 base::OneShotTimer<RealFetchTester> timeout_;
96 base::OneShotTimer<RealFetchTester> cancel_timer_;
97 bool on_completion_is_error_;
98 };
99
100 TEST(WindowsDhcpProxyScriptFetcher, RealFetch) {
101 // This tests a call to Fetch() with no stubbing out of dependencies.
102 //
103 // We don't make assumptions about the environment this unit test is
104 // running in, so it just exercises the code to make sure there
105 // is no crash and no unexpected error returned, but does not assert on
106 // results beyond that.
107 RealFetchTester fetcher;
108 fetcher.RunTest();
109
110 while (!fetcher.finished_) {
111 MessageLoop::current()->RunAllPending();
112 }
113 printf("PAC URL was %s\n",
114 fetcher.fetcher_->GetPacURL().possibly_invalid_spec().c_str());
115 }
116
117 TEST(WindowsDhcpProxyScriptFetcher, RealFetchWithCancel) {
118 // Does a Fetch() with an immediate cancel. As before, just
119 // exercises the code without stubbing out dependencies.
120 RealFetchTester fetcher;
121 fetcher.RunTestWithCancel();
122 MessageLoop::current()->RunAllPending();
123 }
124
125 // For RealFetchWithDeferredCancel, below.
126 class DelayingDhcpProxyScriptAdapterFetcher
127 : public DhcpProxyScriptAdapterFetcher {
128 public:
129 explicit DelayingDhcpProxyScriptAdapterFetcher(
130 URLRequestContext* url_request_context)
131 : DhcpProxyScriptAdapterFetcher(url_request_context) {
132 }
133
134 std::string ImplGetPacURLFromDhcp(const std::string& adapter_name) OVERRIDE {
135 base::PlatformThread::Sleep(20);
136 return DhcpProxyScriptAdapterFetcher::ImplGetPacURLFromDhcp(adapter_name);
137 }
138 };
139
140 // For RealFetchWithDeferredCancel, below.
141 class DelayingWindowsDhcpProxyScriptFetcher
142 : public WindowsDhcpProxyScriptFetcher {
143 public:
144 explicit DelayingWindowsDhcpProxyScriptFetcher(
145 URLRequestContext* context)
146 : WindowsDhcpProxyScriptFetcher(context) {
147 }
148
149 DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
150 return new DelayingDhcpProxyScriptAdapterFetcher(url_request_context_);
151 }
152 };
153
154 TEST(WindowsDhcpProxyScriptFetcher, RealFetchWithDeferredCancel) {
155 // Does a Fetch() with a slightly delayed cancel. As before, just
156 // exercises the code without stubbing out dependencies, but
157 // introduces a guaranteed 20 ms delay on the worker threads so that
158 // the cancel is called before they complete.
159 RealFetchTester fetcher;
160 fetcher.fetcher_.reset(
161 new DelayingWindowsDhcpProxyScriptFetcher(fetcher.context_));
162 fetcher.on_completion_is_error_ = true;
163 fetcher.RunTestWithDeferredCancel();
164 while (!fetcher.finished_) {
165 MessageLoop::current()->RunAllPending();
166 }
167 MessageLoop::current()->RunAllPending();
168 }
169
170 // The remaining tests are to exercise our state machine in various
171 // situations, with actual network access fully stubbed out.
172
173 class DummyDhcpProxyScriptAdapterFetcher
174 : public DhcpProxyScriptAdapterFetcher {
175 public:
176 DummyDhcpProxyScriptAdapterFetcher()
177 : DhcpProxyScriptAdapterFetcher(new TestURLRequestContext()),
178 did_finish_(false),
179 result_(OK),
180 pac_script_(L"bingo"),
181 fetch_delay_ms_(1),
182 client_callback_(NULL) {
183 }
184
185 void Fetch(const std::string& adapter_name,
186 CompletionCallback* callback) OVERRIDE {
187 client_callback_ = callback;
188 timer_.Start(base::TimeDelta::FromMilliseconds(fetch_delay_ms_),
189 this, &DummyDhcpProxyScriptAdapterFetcher::OnTimer);
190 }
191
192 void Cancel() OVERRIDE {
193 timer_.Stop();
194 }
195
196 bool DidFinish() const OVERRIDE {
197 return did_finish_;
198 }
199
200 int result() const OVERRIDE {
201 return result_;
202 }
203
204 string16 pac_script() const OVERRIDE {
205 return pac_script_;
206 }
207
208 void OnTimer() {
209 client_callback_->Run(result_);
210 }
211
212 void Configure(
213 bool did_finish, int result, string16 pac_script, int fetch_delay_ms) {
214 did_finish_ = did_finish;
215 result_ = result;
216 pac_script_ = pac_script;
217 fetch_delay_ms_ = fetch_delay_ms;
218 }
219
220 private:
221 bool did_finish_;
222 int result_;
223 string16 pac_script_;
224 int fetch_delay_ms_;
225 CompletionCallback* client_callback_;
226 base::OneShotTimer<DummyDhcpProxyScriptAdapterFetcher> timer_;
227 };
228
229 class MockWindowsDhcpProxyScriptFetcher : public WindowsDhcpProxyScriptFetcher {
230 public:
231 MockWindowsDhcpProxyScriptFetcher()
232 : WindowsDhcpProxyScriptFetcher(new TestURLRequestContext()),
233 next_adapter_fetcher_index_(0) {
234 }
235
236 // Adds a fetcher object to the queue of fetchers used by
237 // |ImplCreateAdapterFetcher()|, and its name to the list of adapters
238 // returned by ImplGetCandidateAdapterNames.
239 void PushBackAdapter(const std::string& adapter_name,
240 DhcpProxyScriptAdapterFetcher* fetcher) {
241 adapter_names_.push_back(adapter_name);
242 adapter_fetchers_.push_back(fetcher);
243 }
244
245 void ConfigureAndPushBackAdapter(const std::string& adapter_name,
246 bool did_finish,
247 int result,
248 string16 pac_script,
249 int fetch_delay_ms) {
250 scoped_refptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher =
251 new DummyDhcpProxyScriptAdapterFetcher();
252 adapter_fetcher->Configure(did_finish, result, pac_script, fetch_delay_ms);
253 PushBackAdapter(adapter_name, adapter_fetcher.get());
254 }
255
256 DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
257 return adapter_fetchers_[next_adapter_fetcher_index_++].get();
258 }
259
260 bool ImplGetCandidateAdapterNames(
261 std::set<std::string>* adapter_names) OVERRIDE {
262 adapter_names->insert(adapter_names_.begin(), adapter_names_.end());
263 return true;
264 }
265
266 int ImplGetMaxWaitMs() OVERRIDE {
267 return 25;
268 }
269
270 void ResetTestState() {
271 next_adapter_fetcher_index_ = 0;
272 adapter_fetchers_.clear();
273 // String pointers contained herein will have been freed during test.
274 adapter_names_.clear();
275 }
276
277 int next_adapter_fetcher_index_;
278 std::vector<scoped_refptr<DhcpProxyScriptAdapterFetcher>> adapter_fetchers_;
279 std::vector<std::string> adapter_names_;
280 };
281
282 class FetcherClient {
283 public:
284 FetcherClient()
285 : finished_(false),
286 result_(ERR_UNEXPECTED),
287 ALLOW_THIS_IN_INITIALIZER_LIST(
288 completion_callback_(this, &FetcherClient::OnCompletion)) {
289 }
290
291 void RunTest() {
292 int result = fetcher_.Fetch(&pac_text_, &completion_callback_);
293 ASSERT_EQ(ERR_IO_PENDING, result);
294 }
295
296 void RunImmediateReturnTest() {
297 int result = fetcher_.Fetch(&pac_text_, &completion_callback_);
298 ASSERT_EQ(ERR_PAC_NOT_IN_DHCP, result);
299 }
300
301 void RunMessageLoopUntilComplete() {
302 while (!finished_) {
303 MessageLoop::current()->RunAllPending();
304 }
305 MessageLoop::current()->RunAllPending();
306 }
307
308 void OnCompletion(int result) {
309 finished_ = true;
310 result_ = result;
311 }
312
313 void ResetTestState() {
314 finished_ = false;
315 result_ = ERR_UNEXPECTED;
316 pac_text_ = L"";
317 fetcher_.ResetTestState();
318 }
319
320 MockWindowsDhcpProxyScriptFetcher fetcher_;
321 bool finished_;
322 int result_;
323 string16 pac_text_;
324 CompletionCallbackImpl<FetcherClient> completion_callback_;
325 };
326
327 // We separate out each test's logic so that we can easily implement
328 // the ReuseFetcher test at the bottom.
329 void TestNormalCaseURLConfiguredOneAdapter(FetcherClient* client) {
330 scoped_refptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher =
331 new DummyDhcpProxyScriptAdapterFetcher();
332 adapter_fetcher->Configure(true, OK, L"bingo", 1);
333 client->fetcher_.PushBackAdapter("a", adapter_fetcher.get());
334 client->RunTest();
335 client->RunMessageLoopUntilComplete();
336 ASSERT_EQ(OK, client->result_);
337 ASSERT_EQ(L"bingo", client->pac_text_);
338 }
339
340 TEST(WindowsDhcpProxyScriptFetcher, NormalCaseURLConfiguredOneAdapter) {
341 FetcherClient client;
342 TestNormalCaseURLConfiguredOneAdapter(&client);
343 }
344
345 void TestNormalCaseURLConfiguredMultipleAdapters(FetcherClient* client) {
346 client->fetcher_.ConfigureAndPushBackAdapter(
347 "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
348 client->fetcher_.ConfigureAndPushBackAdapter(
349 "second", true, OK, L"bingo", 50);
350 client->fetcher_.ConfigureAndPushBackAdapter(
351 "third", true, OK, L"rocko", 1);
352 client->RunTest();
353 client->RunMessageLoopUntilComplete();
354 ASSERT_EQ(OK, client->result_);
355 ASSERT_EQ(L"bingo", client->pac_text_);
356 }
357
358 TEST(WindowsDhcpProxyScriptFetcher, NormalCaseURLConfiguredMultipleAdapters) {
359 FetcherClient client;
360 TestNormalCaseURLConfiguredMultipleAdapters(&client);
361 }
362
363 void TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(
364 FetcherClient* client) {
365 client->fetcher_.ConfigureAndPushBackAdapter(
366 "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
367 // This will time out.
368 client->fetcher_.ConfigureAndPushBackAdapter(
369 "second", false, ERR_IO_PENDING, L"bingo", 1000);
370 client->fetcher_.ConfigureAndPushBackAdapter(
371 "third", true, OK, L"rocko", 1);
372 client->RunTest();
373 client->RunMessageLoopUntilComplete();
374 ASSERT_EQ(OK, client->result_);
375 ASSERT_EQ(L"rocko", client->pac_text_);
376 }
377
378 TEST(WindowsDhcpProxyScriptFetcher,
379 NormalCaseURLConfiguredMultipleAdaptersWithTimeout) {
380 FetcherClient client;
381 TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
382 }
383
384 void TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(
385 FetcherClient* client) {
386 client->fetcher_.ConfigureAndPushBackAdapter(
387 "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
388 // This will time out.
389 client->fetcher_.ConfigureAndPushBackAdapter(
390 "second", false, ERR_IO_PENDING, L"bingo", 1000);
391 // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
392 // should be chosen.
393 client->fetcher_.ConfigureAndPushBackAdapter(
394 "third", true, ERR_PAC_STATUS_NOT_OK, L"", 1);
395 client->fetcher_.ConfigureAndPushBackAdapter(
396 "fourth", true, ERR_NOT_IMPLEMENTED, L"", 1);
397 client->RunTest();
398 client->RunMessageLoopUntilComplete();
399 ASSERT_EQ(ERR_PAC_STATUS_NOT_OK, client->result_);
400 ASSERT_EQ(L"", client->pac_text_);
401 }
402
403 TEST(WindowsDhcpProxyScriptFetcher,
404 FailureCaseURLConfiguredMultipleAdaptersWithTimeout) {
405 FetcherClient client;
406 TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
407 }
408
409 void TestFailureCaseNoURLConfigured(FetcherClient* client) {
410 client->fetcher_.ConfigureAndPushBackAdapter(
411 "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
412 // This will time out.
413 client->fetcher_.ConfigureAndPushBackAdapter(
414 "second", false, ERR_IO_PENDING, L"bingo", 1000);
415 // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
416 // should be chosen.
417 client->fetcher_.ConfigureAndPushBackAdapter(
418 "third", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
419 client->RunTest();
420 client->RunMessageLoopUntilComplete();
421 ASSERT_EQ(ERR_PAC_NOT_IN_DHCP, client->result_);
422 ASSERT_EQ(L"", client->pac_text_);
423 }
424
425 TEST(WindowsDhcpProxyScriptFetcher, FailureCaseNoURLConfigured) {
426 FetcherClient client;
427 TestFailureCaseNoURLConfigured(&client);
428 }
429
430 void TestFailureCaseNoDhcpAdapters(FetcherClient* client) {
431 client->RunImmediateReturnTest();
432 // In case there are any pending messages that get us in a bad state
433 // (there shouldn't be).
434 MessageLoop::current()->RunAllPending();
435 }
436
437 TEST(WindowsDhcpProxyScriptFetcher, FailureCaseNoDhcpAdapters) {
438 FetcherClient client;
439 TestFailureCaseNoDhcpAdapters(&client);
440 }
441
442 void TestShortCircuitLessPreferredAdapters(FetcherClient* client) {
443 // Here we have a bunch of adapters; the first reports no PAC in DHCP,
444 // the second responds quickly with a PAC file, the rest take a long
445 // time. Verify that we complete quickly and do not wait for the slow
446 // adapters, i.e. we finish before timeout.
447 client->fetcher_.ConfigureAndPushBackAdapter(
448 "1", true, ERR_PAC_NOT_IN_DHCP, L"", 1);
449 client->fetcher_.ConfigureAndPushBackAdapter(
450 "2", true, OK, L"bingo", 1);
451 client->fetcher_.ConfigureAndPushBackAdapter(
452 "3", true, OK, L"wrongo", 1000);
453
454 PerfTimer timer;
455 client->RunTest();
456 client->RunMessageLoopUntilComplete();
457 // Assert that the time passed is just less than the wait timer
458 // timeout (which we have mocked out above to be 25 ms), to avoid
459 // flakiness but still get a strong signal that it was the shortcut
460 // mechanism (in OnFetcherDone) that kicked in.
461 ASSERT_GT(TimeDelta::FromMilliseconds(23), timer.Elapsed());
462 }
463
464 TEST(WindowsDhcpProxyScriptFetcher, ShortCircuitLessPreferredAdapters) {
465 FetcherClient client;
466 TestShortCircuitLessPreferredAdapters(&client);
467 }
468
469 TEST(WindowsDhcpProxyScriptFetcher, ReuseFetcher) {
470 FetcherClient client;
471
472 // The ProxyScriptFetcher interface stipulates that only a single
473 // |Fetch()| may be in flight at once, but allows reuse, so test
474 // that the state transitions correctly from done to start in all
475 // cases we're testing.
476
477 typedef void (*FetcherClientTestFunction)(FetcherClient*);
478 typedef std::vector<FetcherClientTestFunction> TestVector;
479 TestVector test_functions;
480 test_functions.push_back(TestNormalCaseURLConfiguredOneAdapter);
481 test_functions.push_back(TestNormalCaseURLConfiguredMultipleAdapters);
482 test_functions.push_back(
483 TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout);
484 test_functions.push_back(
485 TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout);
486 test_functions.push_back(TestFailureCaseNoURLConfigured);
487 test_functions.push_back(TestFailureCaseNoDhcpAdapters);
488 test_functions.push_back(TestShortCircuitLessPreferredAdapters);
489
490 std::random_shuffle(test_functions.begin(),
491 test_functions.end(),
492 base::RandGenerator);
493 for (TestVector::iterator it = test_functions.begin();
494 it != test_functions.end();
495 ++it) {
496 (*it)(&client);
497 client.ResetTestState();
498 }
499
500 // Re-do the first test to make sure the last test that was run did
501 // not leave things in a bad state.
502 (*test_functions.begin())(&client);
503 }
504
505 } // namespace
506 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698