OLD | NEW |
---|---|
(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 | |
OLD | NEW |