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