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