| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/proxy_resolution/dhcp_pac_file_fetcher_win.h" |
| |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/rand_util.h" |
| #include "base/run_loop.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/test/test_timeouts.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/timer/elapsed_timer.h" |
| #include "net/proxy_resolution/dhcp_pac_file_adapter_fetcher_win.h" |
| #include "net/test/gtest_util.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using net::test::IsError; |
| using net::test::IsOk; |
| |
| namespace net { |
| |
| namespace { |
| |
| TEST(DhcpPacFileFetcherWin, AdapterNamesAndPacURLFromDhcp) { |
| // This tests our core Win32 implementation without any of the wrappers |
| // we layer on top to achieve asynchronous and parallel operations. |
| // |
| // We don't make assumptions about the environment this unit test is |
| // running in, so it just exercises the code to make sure there |
| // is no crash and no error returned, but does not assert on the number |
| // of interfaces or the information returned via DHCP. |
| std::set<std::string> adapter_names; |
| DhcpPacFileFetcherWin::GetCandidateAdapterNames(&adapter_names, nullptr); |
| for (const std::string& adapter_name : adapter_names) { |
| DhcpPacFileAdapterFetcher::GetPacURLFromDhcp(adapter_name); |
| } |
| } |
| |
| // Helper for RealFetch* tests below. |
| class RealFetchTester { |
| public: |
| RealFetchTester() |
| : context_(new TestURLRequestContext), |
| fetcher_(new DhcpPacFileFetcherWin(context_.get())), |
| finished_(false), |
| on_completion_is_error_(false) { |
| // Make sure the test ends. |
| timeout_.Start(FROM_HERE, |
| base::TimeDelta::FromSeconds(5), this, &RealFetchTester::OnTimeout); |
| } |
| |
| void RunTest() { |
| int result = fetcher_->Fetch( |
| &pac_text_, |
| base::Bind(&RealFetchTester::OnCompletion, base::Unretained(this)), |
| NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS); |
| if (result != ERR_IO_PENDING) |
| finished_ = true; |
| } |
| |
| void RunTestWithCancel() { |
| RunTest(); |
| fetcher_->Cancel(); |
| } |
| |
| void RunTestWithDeferredCancel() { |
| // Put the cancellation into the queue before even running the |
| // test to avoid the chance of one of the adapter fetcher worker |
| // threads completing before cancellation. See http://crbug.com/86756. |
| cancel_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(0), |
| this, &RealFetchTester::OnCancelTimer); |
| RunTest(); |
| } |
| |
| void OnCompletion(int result) { |
| if (on_completion_is_error_) { |
| FAIL() << "Received completion for test in which this is error."; |
| } |
| finished_ = true; |
| } |
| |
| void OnTimeout() { |
| OnCompletion(0); |
| } |
| |
| void OnCancelTimer() { |
| fetcher_->Cancel(); |
| finished_ = true; |
| } |
| |
| void WaitUntilDone() { |
| while (!finished_) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Attempts to give worker threads time to finish. This is currently |
| // very simplistic as completion (via completion callback or cancellation) |
| // immediately "detaches" any worker threads, so the best we can do is give |
| // them a little time. If we start running into memory leaks, we can |
| // do something a bit more clever to track worker threads even when the |
| // DhcpPacFileFetcherWin state machine has finished. |
| void FinishTestAllowCleanup() { |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(30)); |
| } |
| |
| std::unique_ptr<URLRequestContext> context_; |
| std::unique_ptr<DhcpPacFileFetcherWin> fetcher_; |
| bool finished_; |
| base::string16 pac_text_; |
| base::OneShotTimer timeout_; |
| base::OneShotTimer cancel_timer_; |
| bool on_completion_is_error_; |
| }; |
| |
| TEST(DhcpPacFileFetcherWin, RealFetch) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| // This tests a call to Fetch() with no stubbing out of dependencies. |
| // |
| // We don't make assumptions about the environment this unit test is |
| // running in, so it just exercises the code to make sure there |
| // is no crash and no unexpected error returned, but does not assert on |
| // results beyond that. |
| RealFetchTester fetcher; |
| fetcher.RunTest(); |
| |
| fetcher.WaitUntilDone(); |
| fetcher.fetcher_->GetPacURL().possibly_invalid_spec(); |
| |
| fetcher.FinishTestAllowCleanup(); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, RealFetchWithCancel) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| // Does a Fetch() with an immediate cancel. As before, just |
| // exercises the code without stubbing out dependencies. |
| RealFetchTester fetcher; |
| fetcher.RunTestWithCancel(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Attempt to avoid memory leak reports in case worker thread is |
| // still running. |
| fetcher.FinishTestAllowCleanup(); |
| } |
| |
| // For RealFetchWithDeferredCancel, below. |
| class DelayingDhcpPacFileAdapterFetcher : public DhcpPacFileAdapterFetcher { |
| public: |
| DelayingDhcpPacFileAdapterFetcher(URLRequestContext* url_request_context, |
| scoped_refptr<base::TaskRunner> task_runner) |
| : DhcpPacFileAdapterFetcher(url_request_context, task_runner) {} |
| |
| class DelayingDhcpQuery : public DhcpQuery { |
| public: |
| explicit DelayingDhcpQuery() : DhcpQuery() {} |
| |
| std::string ImplGetPacURLFromDhcp( |
| const std::string& adapter_name) override { |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); |
| return DhcpQuery::ImplGetPacURLFromDhcp(adapter_name); |
| } |
| |
| private: |
| ~DelayingDhcpQuery() override {} |
| }; |
| |
| DhcpQuery* ImplCreateDhcpQuery() override { |
| return new DelayingDhcpQuery(); |
| } |
| }; |
| |
| // For RealFetchWithDeferredCancel, below. |
| class DelayingDhcpPacFileFetcherWin : public DhcpPacFileFetcherWin { |
| public: |
| explicit DelayingDhcpPacFileFetcherWin(URLRequestContext* context) |
| : DhcpPacFileFetcherWin(context) {} |
| |
| DhcpPacFileAdapterFetcher* ImplCreateAdapterFetcher() override { |
| return new DelayingDhcpPacFileAdapterFetcher(url_request_context(), |
| GetTaskRunner()); |
| } |
| }; |
| |
| TEST(DhcpPacFileFetcherWin, RealFetchWithDeferredCancel) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| // Does a Fetch() with a slightly delayed cancel. As before, just |
| // exercises the code without stubbing out dependencies, but |
| // introduces a guaranteed 20 ms delay on the worker threads so that |
| // the cancel is called before they complete. |
| RealFetchTester fetcher; |
| fetcher.fetcher_.reset( |
| new DelayingDhcpPacFileFetcherWin(fetcher.context_.get())); |
| fetcher.on_completion_is_error_ = true; |
| fetcher.RunTestWithDeferredCancel(); |
| fetcher.WaitUntilDone(); |
| } |
| |
| // The remaining tests are to exercise our state machine in various |
| // situations, with actual network access fully stubbed out. |
| |
| class DummyDhcpPacFileAdapterFetcher : public DhcpPacFileAdapterFetcher { |
| public: |
| DummyDhcpPacFileAdapterFetcher(URLRequestContext* context, |
| scoped_refptr<base::TaskRunner> runner) |
| : DhcpPacFileAdapterFetcher(context, runner), |
| did_finish_(false), |
| result_(OK), |
| pac_script_(L"bingo"), |
| fetch_delay_ms_(1) {} |
| |
| void Fetch(const std::string& adapter_name, |
| CompletionOnceCallback callback, |
| const NetworkTrafficAnnotationTag traffic_annotation) override { |
| callback_ = std::move(callback); |
| timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(fetch_delay_ms_), |
| this, &DummyDhcpPacFileAdapterFetcher::OnTimer); |
| } |
| |
| void Cancel() override { |
| timer_.Stop(); |
| } |
| |
| bool DidFinish() const override { |
| return did_finish_; |
| } |
| |
| int GetResult() const override { |
| return result_; |
| } |
| |
| base::string16 GetPacScript() const override { |
| return pac_script_; |
| } |
| |
| void OnTimer() { std::move(callback_).Run(result_); } |
| |
| void Configure(bool did_finish, |
| int result, |
| base::string16 pac_script, |
| int fetch_delay_ms) { |
| did_finish_ = did_finish; |
| result_ = result; |
| pac_script_ = pac_script; |
| fetch_delay_ms_ = fetch_delay_ms; |
| } |
| |
| private: |
| bool did_finish_; |
| int result_; |
| base::string16 pac_script_; |
| int fetch_delay_ms_; |
| CompletionOnceCallback callback_; |
| base::OneShotTimer timer_; |
| }; |
| |
| class MockDhcpPacFileFetcherWin : public DhcpPacFileFetcherWin { |
| public: |
| class MockAdapterQuery : public AdapterQuery { |
| public: |
| MockAdapterQuery() { |
| } |
| |
| bool ImplGetCandidateAdapterNames( |
| std::set<std::string>* adapter_names, |
| DhcpAdapterNamesLoggingInfo* logging) override { |
| adapter_names->insert(mock_adapter_names_.begin(), |
| mock_adapter_names_.end()); |
| return true; |
| } |
| |
| std::vector<std::string> mock_adapter_names_; |
| |
| private: |
| ~MockAdapterQuery() override {} |
| }; |
| |
| MockDhcpPacFileFetcherWin(URLRequestContext* context) |
| : DhcpPacFileFetcherWin(context), |
| num_fetchers_created_(0), |
| worker_finished_event_( |
| base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED) { |
| ResetTestState(); |
| } |
| |
| ~MockDhcpPacFileFetcherWin() override { ResetTestState(); } |
| |
| using DhcpPacFileFetcherWin::GetTaskRunner; |
| |
| // Adds a fetcher object to the queue of fetchers used by |
| // |ImplCreateAdapterFetcher()|, and its name to the list of adapters |
| // returned by ImplGetCandidateAdapterNames. |
| void PushBackAdapter(const std::string& adapter_name, |
| DhcpPacFileAdapterFetcher* fetcher) { |
| adapter_query_->mock_adapter_names_.push_back(adapter_name); |
| adapter_fetchers_.push_back(fetcher); |
| } |
| |
| void ConfigureAndPushBackAdapter(const std::string& adapter_name, |
| bool did_finish, |
| int result, |
| base::string16 pac_script, |
| base::TimeDelta fetch_delay) { |
| std::unique_ptr<DummyDhcpPacFileAdapterFetcher> adapter_fetcher( |
| new DummyDhcpPacFileAdapterFetcher(url_request_context(), |
| GetTaskRunner())); |
| adapter_fetcher->Configure( |
| did_finish, result, pac_script, fetch_delay.InMilliseconds()); |
| PushBackAdapter(adapter_name, adapter_fetcher.release()); |
| } |
| |
| DhcpPacFileAdapterFetcher* ImplCreateAdapterFetcher() override { |
| ++num_fetchers_created_; |
| return adapter_fetchers_[next_adapter_fetcher_index_++]; |
| } |
| |
| AdapterQuery* ImplCreateAdapterQuery() override { |
| DCHECK(adapter_query_.get()); |
| return adapter_query_.get(); |
| } |
| |
| base::TimeDelta ImplGetMaxWait() override { |
| return max_wait_; |
| } |
| |
| void ImplOnGetCandidateAdapterNamesDone() override { |
| worker_finished_event_.Signal(); |
| } |
| |
| void ResetTestState() { |
| // Delete any adapter fetcher objects we didn't hand out. |
| std::vector<DhcpPacFileAdapterFetcher*>::const_iterator it = |
| adapter_fetchers_.begin(); |
| for (; it != adapter_fetchers_.end(); ++it) { |
| if (num_fetchers_created_-- <= 0) { |
| delete (*it); |
| } |
| } |
| |
| next_adapter_fetcher_index_ = 0; |
| num_fetchers_created_ = 0; |
| adapter_fetchers_.clear(); |
| adapter_query_ = new MockAdapterQuery(); |
| max_wait_ = TestTimeouts::tiny_timeout(); |
| } |
| |
| bool HasPendingFetchers() { |
| return num_pending_fetchers() > 0; |
| } |
| |
| int next_adapter_fetcher_index_; |
| |
| // Ownership gets transferred to the implementation class via |
| // ImplCreateAdapterFetcher, but any objects not handed out are |
| // deleted on destruction. |
| std::vector<DhcpPacFileAdapterFetcher*> adapter_fetchers_; |
| |
| scoped_refptr<MockAdapterQuery> adapter_query_; |
| |
| base::TimeDelta max_wait_; |
| int num_fetchers_created_; |
| base::WaitableEvent worker_finished_event_; |
| }; |
| |
| class FetcherClient { |
| public: |
| FetcherClient() |
| : context_(new TestURLRequestContext), |
| fetcher_(context_.get()), |
| finished_(false), |
| result_(ERR_UNEXPECTED) { |
| } |
| |
| void RunTest() { |
| int result = fetcher_.Fetch( |
| &pac_text_, |
| base::Bind(&FetcherClient::OnCompletion, base::Unretained(this)), |
| NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS); |
| ASSERT_THAT(result, IsError(ERR_IO_PENDING)); |
| } |
| |
| int RunTestThatMayFailSync() { |
| int result = fetcher_.Fetch( |
| &pac_text_, |
| base::Bind(&FetcherClient::OnCompletion, base::Unretained(this)), |
| NetLogWithSource(), TRAFFIC_ANNOTATION_FOR_TESTS); |
| if (result != ERR_IO_PENDING) |
| result_ = result; |
| return result; |
| } |
| |
| void RunMessageLoopUntilComplete() { |
| while (!finished_) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void RunMessageLoopUntilWorkerDone() { |
| DCHECK(fetcher_.adapter_query_.get()); |
| while (!fetcher_.worker_finished_event_.TimedWait( |
| base::TimeDelta::FromMilliseconds(10))) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| } |
| |
| void OnCompletion(int result) { |
| finished_ = true; |
| result_ = result; |
| } |
| |
| void ResetTestState() { |
| finished_ = false; |
| result_ = ERR_UNEXPECTED; |
| pac_text_ = L""; |
| fetcher_.ResetTestState(); |
| } |
| |
| scoped_refptr<base::TaskRunner> GetTaskRunner() { |
| return fetcher_.GetTaskRunner(); |
| } |
| |
| std::unique_ptr<URLRequestContext> context_; |
| MockDhcpPacFileFetcherWin fetcher_; |
| bool finished_; |
| int result_; |
| base::string16 pac_text_; |
| }; |
| |
| // We separate out each test's logic so that we can easily implement |
| // the ReuseFetcher test at the bottom. |
| void TestNormalCaseURLConfiguredOneAdapter(FetcherClient* client) { |
| TestURLRequestContext context; |
| std::unique_ptr<DummyDhcpPacFileAdapterFetcher> adapter_fetcher( |
| new DummyDhcpPacFileAdapterFetcher(&context, client->GetTaskRunner())); |
| adapter_fetcher->Configure(true, OK, L"bingo", 1); |
| client->fetcher_.PushBackAdapter("a", adapter_fetcher.release()); |
| client->RunTest(); |
| client->RunMessageLoopUntilComplete(); |
| ASSERT_THAT(client->result_, IsOk()); |
| ASSERT_EQ(L"bingo", client->pac_text_); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, NormalCaseURLConfiguredOneAdapter) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestNormalCaseURLConfiguredOneAdapter(&client); |
| } |
| |
| void TestNormalCaseURLConfiguredMultipleAdapters(FetcherClient* client) { |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", |
| base::TimeDelta::FromMilliseconds(1)); |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "second", true, OK, L"bingo", base::TimeDelta::FromMilliseconds(50)); |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "third", true, OK, L"rocko", base::TimeDelta::FromMilliseconds(1)); |
| client->RunTest(); |
| client->RunMessageLoopUntilComplete(); |
| ASSERT_THAT(client->result_, IsOk()); |
| ASSERT_EQ(L"bingo", client->pac_text_); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, NormalCaseURLConfiguredMultipleAdapters) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestNormalCaseURLConfiguredMultipleAdapters(&client); |
| } |
| |
| void TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout( |
| FetcherClient* client) { |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", |
| base::TimeDelta::FromMilliseconds(1)); |
| // This will time out. |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "second", false, ERR_IO_PENDING, L"bingo", |
| TestTimeouts::action_timeout()); |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "third", true, OK, L"rocko", base::TimeDelta::FromMilliseconds(1)); |
| client->RunTest(); |
| client->RunMessageLoopUntilComplete(); |
| ASSERT_THAT(client->result_, IsOk()); |
| ASSERT_EQ(L"rocko", client->pac_text_); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, |
| NormalCaseURLConfiguredMultipleAdaptersWithTimeout) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout(&client); |
| } |
| |
| void TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout( |
| FetcherClient* client) { |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", |
| base::TimeDelta::FromMilliseconds(1)); |
| // This will time out. |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "second", false, ERR_IO_PENDING, L"bingo", |
| TestTimeouts::action_timeout()); |
| // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such |
| // should be chosen. |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "third", true, ERR_PAC_STATUS_NOT_OK, L"", |
| base::TimeDelta::FromMilliseconds(1)); |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "fourth", true, ERR_NOT_IMPLEMENTED, L"", |
| base::TimeDelta::FromMilliseconds(1)); |
| client->RunTest(); |
| client->RunMessageLoopUntilComplete(); |
| ASSERT_THAT(client->result_, IsError(ERR_PAC_STATUS_NOT_OK)); |
| ASSERT_EQ(L"", client->pac_text_); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, |
| FailureCaseURLConfiguredMultipleAdaptersWithTimeout) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout(&client); |
| } |
| |
| void TestFailureCaseNoURLConfigured(FetcherClient* client) { |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "most_preferred", true, ERR_PAC_NOT_IN_DHCP, L"", |
| base::TimeDelta::FromMilliseconds(1)); |
| // This will time out. |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "second", false, ERR_IO_PENDING, L"bingo", |
| TestTimeouts::action_timeout()); |
| // This is the first non-ERR_PAC_NOT_IN_DHCP error and as such |
| // should be chosen. |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "third", true, ERR_PAC_NOT_IN_DHCP, L"", |
| base::TimeDelta::FromMilliseconds(1)); |
| client->RunTest(); |
| client->RunMessageLoopUntilComplete(); |
| ASSERT_THAT(client->result_, IsError(ERR_PAC_NOT_IN_DHCP)); |
| ASSERT_EQ(L"", client->pac_text_); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, FailureCaseNoURLConfigured) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestFailureCaseNoURLConfigured(&client); |
| } |
| |
| void TestFailureCaseNoDhcpAdapters(FetcherClient* client) { |
| client->RunTest(); |
| client->RunMessageLoopUntilComplete(); |
| ASSERT_THAT(client->result_, IsError(ERR_PAC_NOT_IN_DHCP)); |
| ASSERT_EQ(L"", client->pac_text_); |
| ASSERT_EQ(0, client->fetcher_.num_fetchers_created_); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, FailureCaseNoDhcpAdapters) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestFailureCaseNoDhcpAdapters(&client); |
| } |
| |
| void TestShortCircuitLessPreferredAdapters(FetcherClient* client) { |
| // Here we have a bunch of adapters; the first reports no PAC in DHCP, |
| // the second responds quickly with a PAC file, the rest take a long |
| // time. Verify that we complete quickly and do not wait for the slow |
| // adapters, i.e. we finish before timeout. |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "1", true, ERR_PAC_NOT_IN_DHCP, L"", |
| base::TimeDelta::FromMilliseconds(1)); |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "2", true, OK, L"bingo", |
| base::TimeDelta::FromMilliseconds(1)); |
| client->fetcher_.ConfigureAndPushBackAdapter( |
| "3", true, OK, L"wrongo", TestTimeouts::action_max_timeout()); |
| |
| // Increase the timeout to ensure the short circuit mechanism has |
| // time to kick in before the timeout waiting for more adapters kicks in. |
| client->fetcher_.max_wait_ = TestTimeouts::action_timeout(); |
| |
| base::ElapsedTimer timer; |
| client->RunTest(); |
| client->RunMessageLoopUntilComplete(); |
| ASSERT_TRUE(client->fetcher_.HasPendingFetchers()); |
| // Assert that the time passed is definitely less than the wait timer |
| // timeout, to get a second signal that it was the shortcut mechanism |
| // (in OnFetcherDone) that kicked in, and not the timeout waiting for |
| // more adapters. |
| ASSERT_GT(client->fetcher_.max_wait_ - (client->fetcher_.max_wait_ / 10), |
| timer.Elapsed()); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, ShortCircuitLessPreferredAdapters) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestShortCircuitLessPreferredAdapters(&client); |
| } |
| |
| void TestImmediateCancel(FetcherClient* client) { |
| TestURLRequestContext context; |
| std::unique_ptr<DummyDhcpPacFileAdapterFetcher> adapter_fetcher( |
| new DummyDhcpPacFileAdapterFetcher(&context, client->GetTaskRunner())); |
| adapter_fetcher->Configure(true, OK, L"bingo", 1); |
| client->fetcher_.PushBackAdapter("a", adapter_fetcher.release()); |
| client->RunTest(); |
| client->fetcher_.Cancel(); |
| client->RunMessageLoopUntilWorkerDone(); |
| ASSERT_EQ(0, client->fetcher_.num_fetchers_created_); |
| } |
| |
| // Regression test to check that when we cancel immediately, no |
| // adapter fetchers get created. |
| TEST(DhcpPacFileFetcherWin, ImmediateCancel) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestImmediateCancel(&client); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, ReuseFetcher) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| |
| // The PacFileFetcher interface stipulates that only a single |
| // |Fetch()| may be in flight at once, but allows reuse, so test |
| // that the state transitions correctly from done to start in all |
| // cases we're testing. |
| |
| typedef void (*FetcherClientTestFunction)(FetcherClient*); |
| typedef std::vector<FetcherClientTestFunction> TestVector; |
| TestVector test_functions; |
| test_functions.push_back(TestNormalCaseURLConfiguredOneAdapter); |
| test_functions.push_back(TestNormalCaseURLConfiguredMultipleAdapters); |
| test_functions.push_back( |
| TestNormalCaseURLConfiguredMultipleAdaptersWithTimeout); |
| test_functions.push_back( |
| TestFailureCaseURLConfiguredMultipleAdaptersWithTimeout); |
| test_functions.push_back(TestFailureCaseNoURLConfigured); |
| test_functions.push_back(TestFailureCaseNoDhcpAdapters); |
| test_functions.push_back(TestShortCircuitLessPreferredAdapters); |
| test_functions.push_back(TestImmediateCancel); |
| |
| base::RandomShuffle(test_functions.begin(), test_functions.end()); |
| for (TestVector::const_iterator it = test_functions.begin(); |
| it != test_functions.end(); |
| ++it) { |
| (*it)(&client); |
| client.ResetTestState(); |
| } |
| |
| // Re-do the first test to make sure the last test that was run did |
| // not leave things in a bad state. |
| (*test_functions.begin())(&client); |
| } |
| |
| TEST(DhcpPacFileFetcherWin, OnShutdown) { |
| base::test::ScopedTaskEnvironment scoped_task_environment; |
| |
| FetcherClient client; |
| TestURLRequestContext context; |
| std::unique_ptr<DummyDhcpPacFileAdapterFetcher> adapter_fetcher( |
| new DummyDhcpPacFileAdapterFetcher(&context, client.GetTaskRunner())); |
| adapter_fetcher->Configure(true, OK, L"bingo", 1); |
| client.fetcher_.PushBackAdapter("a", adapter_fetcher.release()); |
| client.RunTest(); |
| |
| client.fetcher_.OnShutdown(); |
| EXPECT_TRUE(client.finished_); |
| EXPECT_THAT(client.result_, IsError(ERR_CONTEXT_SHUT_DOWN)); |
| |
| client.ResetTestState(); |
| EXPECT_THAT(client.RunTestThatMayFailSync(), IsError(ERR_CONTEXT_SHUT_DOWN)); |
| EXPECT_EQ(0u, context.url_requests()->size()); |
| } |
| |
| } // namespace |
| |
| } // namespace net |