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

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

Powered by Google App Engine
This is Rietveld 408576698