blob: f8e3278de52aeea7274ba69544fd9f97ce9aa015 [file] [log] [blame]
// 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