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