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

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

Powered by Google App Engine
This is Rietveld 408576698