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 "net/proxy_resolution/win/dhcp_pac_file_fetcher_win.h"
6
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/rand_util.h"
12 #include "base/run_loop.h"
13 #include "base/test/task_environment.h"
14 #include "base/test/test_timeouts.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/timer/elapsed_timer.h"
17 #include "base/timer/timer.h"
18 #include "net/proxy_resolution/win/dhcp_pac_file_adapter_fetcher_win.h"
19 #include "net/test/gtest_util.h"
20 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
21 #include "net/url_request/url_request_test_util.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 using net::test::IsError;
26 using net::test::IsOk;
27
28 namespace net {
29
30 namespace {
31
TEST(DhcpPacFileFetcherWin,AdapterNamesAndPacURLFromDhcp)32 TEST(DhcpPacFileFetcherWin, AdapterNamesAndPacURLFromDhcp) {
33 // This tests our core Win32 implementation without any of the wrappers
34 // we layer on top to achieve asynchronous and parallel operations.
35 //
36 // We don't make assumptions about the environment this unit test is
37 // running in, so it just exercises the code to make sure there
38 // is no crash and no error returned, but does not assert on the number
39 // of interfaces or the information returned via DHCP.
40 std::set<std::string> adapter_names;
41 DhcpPacFileFetcherWin::GetCandidateAdapterNames(&adapter_names, nullptr);
42 for (const std::string& adapter_name : adapter_names) {
43 DhcpPacFileAdapterFetcher::GetPacURLFromDhcp(adapter_name);
44 }
45 }
46
47 // Helper for RealFetch* tests below.
48 class RealFetchTester {
49 public:
RealFetchTester()50 RealFetchTester()
51 : context_(new TestURLRequestContext),
52 fetcher_(new DhcpPacFileFetcherWin(context_.get())),
53 finished_(false),
54 on_completion_is_error_(false) {
55 // Make sure the test ends.
56 timeout_.Start(FROM_HERE,
57 base::TimeDelta::FromSeconds(5), this, &RealFetchTester::OnTimeout);
58 }
59
RunTest()60 void RunTest() {
61 int result = fetcher_->Fetch(
62 &pac_text_,
63 base::BindOnce(&RealFetchTester::OnCompletion, base::Unretained(this)),
64 NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS);
65 if (result != ERR_IO_PENDING)
66 finished_ = true;
67 }
68
RunTestWithCancel()69 void RunTestWithCancel() {
70 RunTest();
71 fetcher_->Cancel();
72 }
73
RunTestWithDeferredCancel()74 void RunTestWithDeferredCancel() {
75 // Put the cancellation into the queue before even running the
76 // test to avoid the chance of one of the adapter fetcher worker
77 // threads completing before cancellation. See http://crbug.com/86756.
78 cancel_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(0),
79 this, &RealFetchTester::OnCancelTimer);
80 RunTest();
81 }
82
OnCompletion(int result)83 void OnCompletion(int result) {
84 if (on_completion_is_error_) {
85 FAIL() << "Received completion for test in which this is error.";
86 }
87 finished_ = true;
88 }
89
OnTimeout()90 void OnTimeout() {
91 OnCompletion(0);
92 }
93
OnCancelTimer()94 void OnCancelTimer() {
95 fetcher_->Cancel();
96 finished_ = true;
97 }
98
WaitUntilDone()99 void WaitUntilDone() {
100 while (!finished_) {
101 base::RunLoop().RunUntilIdle();
102 }
103 base::RunLoop().RunUntilIdle();
104 }
105
106 // Attempts to give worker threads time to finish. This is currently
107 // very simplistic as completion (via completion callback or cancellation)
108 // immediately "detaches" any worker threads, so the best we can do is give
109 // them a little time. If we start running into memory leaks, we can
110 // do something a bit more clever to track worker threads even when the
111 // DhcpPacFileFetcherWin state machine has finished.
FinishTestAllowCleanup()112 void FinishTestAllowCleanup() {
113 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30));
114 }
115
116 std::unique_ptr<URLRequestContext> context_;
117 std::unique_ptr<DhcpPacFileFetcherWin> fetcher_;
118 bool finished_;
119 base::string16 pac_text_;
120 base::OneShotTimer timeout_;
121 base::OneShotTimer cancel_timer_;
122 bool on_completion_is_error_;
123 };
124
TEST(DhcpPacFileFetcherWin,RealFetch)125 TEST(DhcpPacFileFetcherWin, RealFetch) {
126 base::test::TaskEnvironment task_environment;
127
128 // This tests a call to Fetch() with no stubbing out of dependencies.
129 //
130 // We don't make assumptions about the environment this unit test is
131 // running in, so it just exercises the code to make sure there
132 // is no crash and no unexpected error returned, but does not assert on
133 // results beyond that.
134 RealFetchTester fetcher;
135 fetcher.RunTest();
136
137 fetcher.WaitUntilDone();
138 fetcher.fetcher_->GetPacURL().possibly_invalid_spec();
139
140 fetcher.FinishTestAllowCleanup();
141 }
142
TEST(DhcpPacFileFetcherWin,RealFetchWithCancel)143 TEST(DhcpPacFileFetcherWin, RealFetchWithCancel) {
144 base::test::TaskEnvironment task_environment;
145
146 // Does a Fetch() with an immediate cancel. As before, just
147 // exercises the code without stubbing out dependencies.
148 RealFetchTester fetcher;
149 fetcher.RunTestWithCancel();
150 base::RunLoop().RunUntilIdle();
151
152 // Attempt to avoid memory leak reports in case worker thread is
153 // still running.
154 fetcher.FinishTestAllowCleanup();
155 }
156
157 // For RealFetchWithDeferredCancel, below.
158 class DelayingDhcpPacFileAdapterFetcher : public DhcpPacFileAdapterFetcher {
159 public:
DelayingDhcpPacFileAdapterFetcher(URLRequestContext * url_request_context,scoped_refptr<base::TaskRunner> task_runner)160 DelayingDhcpPacFileAdapterFetcher(URLRequestContext* url_request_context,
161 scoped_refptr<base::TaskRunner> task_runner)
162 : DhcpPacFileAdapterFetcher(url_request_context, task_runner) {}
163
164 class DelayingDhcpQuery : public DhcpQuery {
165 public:
DelayingDhcpQuery()166 explicit DelayingDhcpQuery() : DhcpQuery() {}
167
ImplGetPacURLFromDhcp(const std::string & adapter_name)168 std::string ImplGetPacURLFromDhcp(
169 const std::string& adapter_name) override {
170 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
171 return DhcpQuery::ImplGetPacURLFromDhcp(adapter_name);
172 }
173
174 private:
~DelayingDhcpQuery()175 ~DelayingDhcpQuery() override {}
176 };
177
ImplCreateDhcpQuery()178 DhcpQuery* ImplCreateDhcpQuery() override {
179 return new DelayingDhcpQuery();
180 }
181 };
182
183 // For RealFetchWithDeferredCancel, below.
184 class DelayingDhcpPacFileFetcherWin : public DhcpPacFileFetcherWin {
185 public:
DelayingDhcpPacFileFetcherWin(URLRequestContext * context)186 explicit DelayingDhcpPacFileFetcherWin(URLRequestContext* context)
187 : DhcpPacFileFetcherWin(context) {}
188
ImplCreateAdapterFetcher()189 DhcpPacFileAdapterFetcher* ImplCreateAdapterFetcher() override {
190 return new DelayingDhcpPacFileAdapterFetcher(url_request_context(),
191 GetTaskRunner());
192 }
193 };
194
TEST(DhcpPacFileFetcherWin,RealFetchWithDeferredCancel)195 TEST(DhcpPacFileFetcherWin, RealFetchWithDeferredCancel) {
196 base::test::TaskEnvironment task_environment;
197
198 // Does a Fetch() with a slightly delayed cancel. As before, just
199 // exercises the code without stubbing out dependencies, but
200 // introduces a guaranteed 20 ms delay on the worker threads so that
201 // the cancel is called before they complete.
202 RealFetchTester fetcher;
203 fetcher.fetcher_.reset(
204 new DelayingDhcpPacFileFetcherWin(fetcher.context_.get()));
205 fetcher.on_completion_is_error_ = true;
206 fetcher.RunTestWithDeferredCancel();
207 fetcher.WaitUntilDone();
208 }
209
210 // The remaining tests are to exercise our state machine in various
211 // situations, with actual network access fully stubbed out.
212
213 class DummyDhcpPacFileAdapterFetcher : public DhcpPacFileAdapterFetcher {
214 public:
DummyDhcpPacFileAdapterFetcher(URLRequestContext * context,scoped_refptr<base::TaskRunner> runner)215 DummyDhcpPacFileAdapterFetcher(URLRequestContext* context,
216 scoped_refptr<base::TaskRunner> runner)
217 : DhcpPacFileAdapterFetcher(context, runner),
218 did_finish_(false),
219 result_(OK),
220 pac_script_(STRING16_LITERAL("bingo")),
221 fetch_delay_ms_(1) {}
222
Fetch(const std::string & adapter_name,CompletionOnceCallback callback,const NetworkTrafficAnnotationTag traffic_annotation)223 void Fetch(const std::string& adapter_name,
224 CompletionOnceCallback callback,
225 const NetworkTrafficAnnotationTag traffic_annotation) override {
226 callback_ = std::move(callback);
227 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(fetch_delay_ms_),
228 this, &DummyDhcpPacFileAdapterFetcher::OnTimer);
229 }
230
Cancel()231 void Cancel() override {
232 timer_.Stop();
233 }
234
DidFinish() const235 bool DidFinish() const override {
236 return did_finish_;
237 }
238
GetResult() const239 int GetResult() const override {
240 return result_;
241 }
242
GetPacScript() const243 base::string16 GetPacScript() const override {
244 return pac_script_;
245 }
246
OnTimer()247 void OnTimer() { std::move(callback_).Run(result_); }
248
Configure(bool did_finish,int result,base::string16 pac_script,int fetch_delay_ms)249 void Configure(bool did_finish,
250 int result,
251 base::string16 pac_script,
252 int fetch_delay_ms) {
253 did_finish_ = did_finish;
254 result_ = result;
255 pac_script_ = pac_script;
256 fetch_delay_ms_ = fetch_delay_ms;
257 }
258
259 private:
260 bool did_finish_;
261 int result_;
262 base::string16 pac_script_;
263 int fetch_delay_ms_;
264 CompletionOnceCallback callback_;
265 base::OneShotTimer timer_;
266 };
267
268 class MockDhcpPacFileFetcherWin : public DhcpPacFileFetcherWin {
269 public:
270 class MockAdapterQuery : public AdapterQuery {
271 public:
MockAdapterQuery()272 MockAdapterQuery() {
273 }
274
ImplGetCandidateAdapterNames(std::set<std::string> * adapter_names,DhcpAdapterNamesLoggingInfo * logging)275 bool ImplGetCandidateAdapterNames(
276 std::set<std::string>* adapter_names,
277 DhcpAdapterNamesLoggingInfo* logging) override {
278 adapter_names->insert(mock_adapter_names_.begin(),
279 mock_adapter_names_.end());
280 return true;
281 }
282
283 std::vector<std::string> mock_adapter_names_;
284
285 private:
~MockAdapterQuery()286 ~MockAdapterQuery() override {}
287 };
288
MockDhcpPacFileFetcherWin(URLRequestContext * context)289 MockDhcpPacFileFetcherWin(URLRequestContext* context)
290 : DhcpPacFileFetcherWin(context),
291 num_fetchers_created_(0),
292 worker_finished_event_(
293 base::WaitableEvent::ResetPolicy::MANUAL,
294 base::WaitableEvent::InitialState::NOT_SIGNALED) {
295 ResetTestState();
296 }
297
~MockDhcpPacFileFetcherWin()298 ~MockDhcpPacFileFetcherWin() override { ResetTestState(); }
299
300 using DhcpPacFileFetcherWin::GetTaskRunner;
301
302 // Adds a fetcher object to the queue of fetchers used by
303 // |ImplCreateAdapterFetcher()|, and its name to the list of adapters
304 // returned by ImplGetCandidateAdapterNames.
PushBackAdapter(const std::string & adapter_name,DhcpPacFileAdapterFetcher * fetcher)305 void PushBackAdapter(const std::string& adapter_name,
306 DhcpPacFileAdapterFetcher* fetcher) {
307 adapter_query_->mock_adapter_names_.push_back(adapter_name);
308 adapter_fetchers_.push_back(fetcher);
309 }
310
ConfigureAndPushBackAdapter(const std::string & adapter_name,bool did_finish,int result,base::string16 pac_script,base::TimeDelta fetch_delay)311 void ConfigureAndPushBackAdapter(const std::string& adapter_name,
312 bool did_finish,
313 int result,
314 base::string16 pac_script,
315 base::TimeDelta fetch_delay) {
316 std::unique_ptr<DummyDhcpPacFileAdapterFetcher> adapter_fetcher(
317 new DummyDhcpPacFileAdapterFetcher(url_request_context(),
318 GetTaskRunner()));
319 adapter_fetcher->Configure(
320 did_finish, result, pac_script, fetch_delay.InMilliseconds());
321 PushBackAdapter(adapter_name, adapter_fetcher.release());
322 }
323
ImplCreateAdapterFetcher()324 DhcpPacFileAdapterFetcher* ImplCreateAdapterFetcher() override {
325 ++num_fetchers_created_;
326 return adapter_fetchers_[next_adapter_fetcher_index_++];
327 }
328
ImplCreateAdapterQuery()329 AdapterQuery* ImplCreateAdapterQuery() override {
330 DCHECK(adapter_query_.get());
331 return adapter_query_.get();
332 }
333
ImplGetMaxWait()334 base::TimeDelta ImplGetMaxWait() override {
335 return max_wait_;
336 }
337
ImplOnGetCandidateAdapterNamesDone()338 void ImplOnGetCandidateAdapterNamesDone() override {
339 worker_finished_event_.Signal();
340 }
341
ResetTestState()342 void ResetTestState() {
343 // Delete any adapter fetcher objects we didn't hand out.
344 std::vector<DhcpPacFileAdapterFetcher*>::const_iterator it =
345 adapter_fetchers_.begin();
346 for (; it != adapter_fetchers_.end(); ++it) {
347 if (num_fetchers_created_-- <= 0) {
348 delete (*it);
349 }
350 }
351
352 next_adapter_fetcher_index_ = 0;
353 num_fetchers_created_ = 0;
354 adapter_fetchers_.clear();
355 adapter_query_ = new MockAdapterQuery();
356 max_wait_ = TestTimeouts::tiny_timeout();
357 }
358
HasPendingFetchers()359 bool HasPendingFetchers() {
360 return num_pending_fetchers() > 0;
361 }
362
363 int next_adapter_fetcher_index_;
364
365 // Ownership gets transferred to the implementation class via
366 // ImplCreateAdapterFetcher, but any objects not handed out are
367 // deleted on destruction.
368 std::vector<DhcpPacFileAdapterFetcher*> adapter_fetchers_;
369
370 scoped_refptr<MockAdapterQuery> adapter_query_;
371
372 base::TimeDelta max_wait_;
373 int num_fetchers_created_;
374 base::WaitableEvent worker_finished_event_;
375 };
376
377 class FetcherClient {
378 public:
FetcherClient()379 FetcherClient()
380 : context_(new TestURLRequestContext),
381 fetcher_(context_.get()),
382 finished_(false),
383 result_(ERR_UNEXPECTED) {
384 }
385
RunTest()386 void RunTest() {
387 int result = fetcher_.Fetch(
388 &pac_text_,
389 base::BindOnce(&FetcherClient::OnCompletion, base::Unretained(this)),
390 NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS);
391 ASSERT_THAT(result, IsError(ERR_IO_PENDING));
392 }
393
RunTestThatMayFailSync()394 int RunTestThatMayFailSync() {
395 int result = fetcher_.Fetch(
396 &pac_text_,
397 base::BindOnce(&FetcherClient::OnCompletion, base::Unretained(this)),
398 NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS);
399 if (result != ERR_IO_PENDING)
400 result_ = result;
401 return result;
402 }
403
RunMessageLoopUntilComplete()404 void RunMessageLoopUntilComplete() {
405 while (!finished_) {
406 base::RunLoop().RunUntilIdle();
407 }
408 base::RunLoop().RunUntilIdle();
409 }
410
RunMessageLoopUntilWorkerDone()411 void RunMessageLoopUntilWorkerDone() {
412 DCHECK(fetcher_.adapter_query_.get());
413 while (!fetcher_.worker_finished_event_.TimedWait(
414 base::TimeDelta::FromMilliseconds(10))) {
415 base::RunLoop().RunUntilIdle();
416 }
417 }
418
OnCompletion(int result)419 void OnCompletion(int result) {
420 finished_ = true;
421 result_ = result;
422 }
423
ResetTestState()424 void ResetTestState() {
425 finished_ = false;
426 result_ = ERR_UNEXPECTED;
427 pac_text_.clear();
428 fetcher_.ResetTestState();
429 }
430
GetTaskRunner()431 scoped_refptr<base::TaskRunner> GetTaskRunner() {
432 return fetcher_.GetTaskRunner();
433 }
434
435 std::unique_ptr<URLRequestContext> context_;
436 MockDhcpPacFileFetcherWin fetcher_;
437 bool finished_;
438 int result_;
439 base::string16 pac_text_;
440 };
441
442 // We separate out each test's logic so that we can easily implement
443 // the ReuseFetcher test at the bottom.
TestNormalCaseURLConfiguredOneAdapter(FetcherClient * client)444 void TestNormalCaseURLConfiguredOneAdapter(FetcherClient* client) {
445 TestURLRequestContext context;
446 std::unique_ptr<DummyDhcpPacFileAdapterFetcher> adapter_fetcher(
447 new DummyDhcpPacFileAdapterFetcher(&context, client->GetTaskRunner()));
448 adapter_fetcher->Configure(true, OK, STRING16_LITERAL("bingo"), 1);
449 client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
450 client->RunTest();
451 client->RunMessageLoopUntilComplete();
452 ASSERT_THAT(client->result_, IsOk());
453 ASSERT_EQ(STRING16_LITERAL("bingo"), client->pac_text_);
454 }
455
TEST(DhcpPacFileFetcherWin,NormalCaseURLConfiguredOneAdapter)456 TEST(DhcpPacFileFetcherWin, NormalCaseURLConfiguredOneAdapter) {
457 base::test::TaskEnvironment task_environment;
458
459 FetcherClient client;
460 TestNormalCaseURLConfiguredOneAdapter(&client);
461 }
462
TestNormalCaseURLConfiguredMultipleAdapters(FetcherClient * client)463 void TestNormalCaseURLConfiguredMultipleAdapters(FetcherClient* client) {
464 client->fetcher_.ConfigureAndPushBackAdapter(
465 "most_preferred", true, ERR_PAC_NOT_IN_DHCP, base::string16(),
466 base::TimeDelta::FromMilliseconds(1));
467 client->fetcher_.ConfigureAndPushBackAdapter(
468 "second", true, OK, STRING16_LITERAL("bingo"),
469 base::TimeDelta::FromMilliseconds(50));
470 client->fetcher_.ConfigureAndPushBackAdapter(
471 "third", true, OK, STRING16_LITERAL("rocko"),
472 base::TimeDelta::FromMilliseconds(1));
473 client->RunTest();
474 client->RunMessageLoopUntilComplete();
475 ASSERT_THAT(client->result_, IsOk());
476 ASSERT_EQ(STRING16_LITERAL("bingo"), client->pac_text_);
477 }
478
TEST(DhcpPacFileFetcherWin,NormalCaseURLConfiguredMultipleAdapters)479 TEST(DhcpPacFileFetcherWin, NormalCaseURLConfiguredMultipleAdapters) {
480 base::test::TaskEnvironment task_environment;
481
482 FetcherClient client;
483 TestNormalCaseURLConfiguredMultipleAdapters(&client);
484 }
485
TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(FetcherClient * client)486 void TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(
487 FetcherClient* client) {
488 client->fetcher_.ConfigureAndPushBackAdapter(
489 "most_preferred", true, ERR_PAC_NOT_IN_DHCP, base::string16(),
490 base::TimeDelta::FromMilliseconds(1));
491 // This will time out.
492 client->fetcher_.ConfigureAndPushBackAdapter("second", false, ERR_IO_PENDING,
493 STRING16_LITERAL("bingo"),
494 TestTimeouts::action_timeout());
495 client->fetcher_.ConfigureAndPushBackAdapter(
496 "third", true, OK, STRING16_LITERAL("rocko"),
497 base::TimeDelta::FromMilliseconds(1));
498 client->RunTest();
499 client->RunMessageLoopUntilComplete();
500 ASSERT_THAT(client->result_, IsOk());
501 ASSERT_EQ(STRING16_LITERAL("rocko"), client->pac_text_);
502 }
503
TEST(DhcpPacFileFetcherWin,NormalCaseURLConfiguredMultipleAdaptersWithTimeout)504 TEST(DhcpPacFileFetcherWin,
505 NormalCaseURLConfiguredMultipleAdaptersWithTimeout) {
506 base::test::TaskEnvironment task_environment;
507
508 FetcherClient client;
509 TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
510 }
511
TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(FetcherClient * client)512 void TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(
513 FetcherClient* client) {
514 client->fetcher_.ConfigureAndPushBackAdapter(
515 "most_preferred", true, ERR_PAC_NOT_IN_DHCP, base::string16(),
516 base::TimeDelta::FromMilliseconds(1));
517 // This will time out.
518 client->fetcher_.ConfigureAndPushBackAdapter("second", false, ERR_IO_PENDING,
519 STRING16_LITERAL("bingo"),
520 TestTimeouts::action_timeout());
521 // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
522 // should be chosen.
523 client->fetcher_.ConfigureAndPushBackAdapter(
524 "third", true, ERR_HTTP_RESPONSE_CODE_FAILURE, base::string16(),
525 base::TimeDelta::FromMilliseconds(1));
526 client->fetcher_.ConfigureAndPushBackAdapter(
527 "fourth", true, ERR_NOT_IMPLEMENTED, base::string16(),
528 base::TimeDelta::FromMilliseconds(1));
529 client->RunTest();
530 client->RunMessageLoopUntilComplete();
531 ASSERT_THAT(client->result_, IsError(ERR_HTTP_RESPONSE_CODE_FAILURE));
532 ASSERT_EQ(base::string16(), client->pac_text_);
533 }
534
TEST(DhcpPacFileFetcherWin,FailureCaseURLConfiguredMultipleAdaptersWithTimeout)535 TEST(DhcpPacFileFetcherWin,
536 FailureCaseURLConfiguredMultipleAdaptersWithTimeout) {
537 base::test::TaskEnvironment task_environment;
538
539 FetcherClient client;
540 TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(&client);
541 }
542
TestFailureCaseNoURLConfigured(FetcherClient * client)543 void TestFailureCaseNoURLConfigured(FetcherClient* client) {
544 client->fetcher_.ConfigureAndPushBackAdapter(
545 "most_preferred", true, ERR_PAC_NOT_IN_DHCP, base::string16(),
546 base::TimeDelta::FromMilliseconds(1));
547 // This will time out.
548 client->fetcher_.ConfigureAndPushBackAdapter("second", false, ERR_IO_PENDING,
549 STRING16_LITERAL("bingo"),
550 TestTimeouts::action_timeout());
551 // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such
552 // should be chosen.
553 client->fetcher_.ConfigureAndPushBackAdapter(
554 "third", true, ERR_PAC_NOT_IN_DHCP, base::string16(),
555 base::TimeDelta::FromMilliseconds(1));
556 client->RunTest();
557 client->RunMessageLoopUntilComplete();
558 ASSERT_THAT(client->result_, IsError(ERR_PAC_NOT_IN_DHCP));
559 ASSERT_EQ(base::string16(), client->pac_text_);
560 }
561
TEST(DhcpPacFileFetcherWin,FailureCaseNoURLConfigured)562 TEST(DhcpPacFileFetcherWin, FailureCaseNoURLConfigured) {
563 base::test::TaskEnvironment task_environment;
564
565 FetcherClient client;
566 TestFailureCaseNoURLConfigured(&client);
567 }
568
TestFailureCaseNoDhcpAdapters(FetcherClient * client)569 void TestFailureCaseNoDhcpAdapters(FetcherClient* client) {
570 client->RunTest();
571 client->RunMessageLoopUntilComplete();
572 ASSERT_THAT(client->result_, IsError(ERR_PAC_NOT_IN_DHCP));
573 ASSERT_EQ(base::string16(), client->pac_text_);
574 ASSERT_EQ(0, client->fetcher_.num_fetchers_created_);
575 }
576
TEST(DhcpPacFileFetcherWin,FailureCaseNoDhcpAdapters)577 TEST(DhcpPacFileFetcherWin, FailureCaseNoDhcpAdapters) {
578 base::test::TaskEnvironment task_environment;
579
580 FetcherClient client;
581 TestFailureCaseNoDhcpAdapters(&client);
582 }
583
TestShortCircuitLessPreferredAdapters(FetcherClient * client)584 void TestShortCircuitLessPreferredAdapters(FetcherClient* client) {
585 // Here we have a bunch of adapters; the first reports no PAC in DHCP,
586 // the second responds quickly with a PAC file, the rest take a long
587 // time. Verify that we complete quickly and do not wait for the slow
588 // adapters, i.e. we finish before timeout.
589 client->fetcher_.ConfigureAndPushBackAdapter(
590 "1", true, ERR_PAC_NOT_IN_DHCP, base::string16(),
591 base::TimeDelta::FromMilliseconds(1));
592 client->fetcher_.ConfigureAndPushBackAdapter(
593 "2", true, OK, STRING16_LITERAL("bingo"),
594 base::TimeDelta::FromMilliseconds(1));
595 client->fetcher_.ConfigureAndPushBackAdapter(
596 "3", true, OK, STRING16_LITERAL("wrongo"),
597 TestTimeouts::action_max_timeout());
598
599 // Increase the timeout to ensure the short circuit mechanism has
600 // time to kick in before the timeout waiting for more adapters kicks in.
601 client->fetcher_.max_wait_ = TestTimeouts::action_timeout();
602
603 base::ElapsedTimer timer;
604 client->RunTest();
605 client->RunMessageLoopUntilComplete();
606 ASSERT_TRUE(client->fetcher_.HasPendingFetchers());
607 // Assert that the time passed is definitely less than the wait timer
608 // timeout, to get a second signal that it was the shortcut mechanism
609 // (in OnFetcherDone) that kicked in, and not the timeout waiting for
610 // more adapters.
611 ASSERT_GT(client->fetcher_.max_wait_ - (client->fetcher_.max_wait_ / 10),
612 timer.Elapsed());
613 }
614
TEST(DhcpPacFileFetcherWin,ShortCircuitLessPreferredAdapters)615 TEST(DhcpPacFileFetcherWin, ShortCircuitLessPreferredAdapters) {
616 base::test::TaskEnvironment task_environment;
617
618 FetcherClient client;
619 TestShortCircuitLessPreferredAdapters(&client);
620 }
621
TestImmediateCancel(FetcherClient * client)622 void TestImmediateCancel(FetcherClient* client) {
623 TestURLRequestContext context;
624 std::unique_ptr<DummyDhcpPacFileAdapterFetcher> adapter_fetcher(
625 new DummyDhcpPacFileAdapterFetcher(&context, client->GetTaskRunner()));
626 adapter_fetcher->Configure(true, OK, STRING16_LITERAL("bingo"), 1);
627 client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
628 client->RunTest();
629 client->fetcher_.Cancel();
630 client->RunMessageLoopUntilWorkerDone();
631 ASSERT_EQ(0, client->fetcher_.num_fetchers_created_);
632 }
633
634 // Regression test to check that when we cancel immediately, no
635 // adapter fetchers get created.
TEST(DhcpPacFileFetcherWin,ImmediateCancel)636 TEST(DhcpPacFileFetcherWin, ImmediateCancel) {
637 base::test::TaskEnvironment task_environment;
638
639 FetcherClient client;
640 TestImmediateCancel(&client);
641 }
642
TEST(DhcpPacFileFetcherWin,ReuseFetcher)643 TEST(DhcpPacFileFetcherWin, ReuseFetcher) {
644 base::test::TaskEnvironment task_environment;
645
646 FetcherClient client;
647
648 // The PacFileFetcher interface stipulates that only a single
649 // |Fetch()| may be in flight at once, but allows reuse, so test
650 // that the state transitions correctly from done to start in all
651 // cases we're testing.
652
653 typedef void (*FetcherClientTestFunction)(FetcherClient*);
654 typedef std::vector<FetcherClientTestFunction> TestVector;
655 TestVector test_functions;
656 test_functions.push_back(TestNormalCaseURLConfiguredOneAdapter);
657 test_functions.push_back(TestNormalCaseURLConfiguredMultipleAdapters);
658 test_functions.push_back(
659 TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout);
660 test_functions.push_back(
661 TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout);
662 test_functions.push_back(TestFailureCaseNoURLConfigured);
663 test_functions.push_back(TestFailureCaseNoDhcpAdapters);
664 test_functions.push_back(TestShortCircuitLessPreferredAdapters);
665 test_functions.push_back(TestImmediateCancel);
666
667 base::RandomShuffle(test_functions.begin(), test_functions.end());
668 for (TestVector::const_iterator it = test_functions.begin();
669 it != test_functions.end();
670 ++it) {
671 (*it)(&client);
672 client.ResetTestState();
673 }
674
675 // Re-do the first test to make sure the last test that was run did
676 // not leave things in a bad state.
677 (*test_functions.begin())(&client);
678 }
679
TEST(DhcpPacFileFetcherWin,OnShutdown)680 TEST(DhcpPacFileFetcherWin, OnShutdown) {
681 base::test::TaskEnvironment task_environment;
682
683 FetcherClient client;
684 TestURLRequestContext context;
685 std::unique_ptr<DummyDhcpPacFileAdapterFetcher> adapter_fetcher(
686 new DummyDhcpPacFileAdapterFetcher(&context, client.GetTaskRunner()));
687 adapter_fetcher->Configure(true, OK, STRING16_LITERAL("bingo"), 1);
688 client.fetcher_.PushBackAdapter("a", adapter_fetcher.release());
689 client.RunTest();
690
691 client.fetcher_.OnShutdown();
692 base::RunLoop().RunUntilIdle();
693 EXPECT_FALSE(client.finished_);
694
695 client.ResetTestState();
696 EXPECT_THAT(client.RunTestThatMayFailSync(), IsError(ERR_CONTEXT_SHUT_DOWN));
697 EXPECT_EQ(0u, context.url_requests()->size());
698 }
699
700 } // namespace
701
702 } // namespace net
703