#include "net/proxy_resolution/multi_threaded_proxy_resolver.h"
#include <array>
#include <memory>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_checker_impl.h"
#include "base/time/time.h"
#include "net/base/net_errors.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/schemeful_site.h"
#include "net/base/test_completion_callback.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_with_source.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.h"
#include "net/proxy_resolution/mock_proxy_resolver.h"
#include "net/proxy_resolution/proxy_info.h"
#include "net/proxy_resolution/proxy_resolver_factory.h"
#include "net/test/gtest_util.h"
#include "net/test/test_with_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using net::test::IsError;
using net::test::IsOk;
using base::ASCIIToUTF16;
namespace net {
namespace {
class MockProxyResolver : public ProxyResolver {
public:
MockProxyResolver() = default;
int GetProxyForURL(const GURL& query_url,
const NetworkAnonymizationKey& network_anonymization_key,
ProxyInfo* results,
CompletionOnceCallback callback,
std::unique_ptr<Request>* request,
const NetLogWithSource& net_log) override {
last_query_url_ = query_url;
last_network_anonymization_key_ = network_anonymization_key;
if (!resolve_latency_.is_zero())
base::PlatformThread::Sleep(resolve_latency_);
EXPECT_TRUE(worker_thread_checker_.CalledOnValidThread());
EXPECT_TRUE(callback.is_null());
EXPECT_TRUE(request == nullptr);
net_log.BeginEvent(NetLogEventType::PAC_JAVASCRIPT_ALERT);
results->UseNamedProxy(query_url.GetHost());
return request_count_++;
}
int request_count() const { return request_count_; }
void SetResolveLatency(base::TimeDelta latency) {
resolve_latency_ = latency;
}
const GURL& last_query_url() const { return last_query_url_; }
const NetworkAnonymizationKey& last_network_anonymization_key() const {
return last_network_anonymization_key_;
}
private:
base::ThreadCheckerImpl worker_thread_checker_;
int request_count_ = 0;
base::TimeDelta resolve_latency_;
GURL last_query_url_;
NetworkAnonymizationKey last_network_anonymization_key_;
};
class BlockableProxyResolver : public MockProxyResolver {
public:
enum class State {
NONE,
BLOCKED,
WILL_BLOCK,
};
BlockableProxyResolver() : condition_(&lock_) {}
BlockableProxyResolver(const BlockableProxyResolver&) = delete;
BlockableProxyResolver& operator=(const BlockableProxyResolver&) = delete;
~BlockableProxyResolver() override {
base::AutoLock lock(lock_);
EXPECT_NE(State::BLOCKED, state_);
}
void Block() {
base::AutoLock lock(lock_);
EXPECT_EQ(State::NONE, state_);
state_ = State::WILL_BLOCK;
condition_.Broadcast();
}
void Unblock() {
base::AutoLock lock(lock_);
EXPECT_EQ(State::BLOCKED, state_);
state_ = State::NONE;
condition_.Broadcast();
}
void WaitUntilBlocked() {
base::AutoLock lock(lock_);
while (state_ != State::BLOCKED)
condition_.Wait();
}
int GetProxyForURL(const GURL& query_url,
const NetworkAnonymizationKey& network_anonymization_key,
ProxyInfo* results,
CompletionOnceCallback callback,
std::unique_ptr<Request>* request,
const NetLogWithSource& net_log) override {
{
base::AutoLock lock(lock_);
EXPECT_NE(State::BLOCKED, state_);
if (state_ == State::WILL_BLOCK) {
state_ = State::BLOCKED;
condition_.Broadcast();
while (state_ == State::BLOCKED)
condition_.Wait();
}
}
return MockProxyResolver::GetProxyForURL(
query_url, network_anonymization_key, results, std::move(callback),
request, net_log);
}
private:
State state_ = State::NONE;
base::Lock lock_;
base::ConditionVariable condition_;
};
class BlockableProxyResolverFactory : public ProxyResolverFactory {
public:
BlockableProxyResolverFactory() : ProxyResolverFactory(false) {}
~BlockableProxyResolverFactory() override = default;
int CreateProxyResolver(const scoped_refptr<PacFileData>& script_data,
std::unique_ptr<ProxyResolver>* result,
CompletionOnceCallback callback,
std::unique_ptr<Request>* request) override {
auto resolver = std::make_unique<BlockableProxyResolver>();
BlockableProxyResolver* resolver_ptr = resolver.get();
*result = std::move(resolver);
base::AutoLock lock(lock_);
resolvers_.push_back(resolver_ptr);
script_data_.push_back(script_data);
return OK;
}
std::vector<raw_ptr<BlockableProxyResolver, VectorExperimental>> resolvers() {
base::AutoLock lock(lock_);
return resolvers_;
}
const std::vector<scoped_refptr<PacFileData>> script_data() {
base::AutoLock lock(lock_);
return script_data_;
}
private:
std::vector<raw_ptr<BlockableProxyResolver, VectorExperimental>> resolvers_;
std::vector<scoped_refptr<PacFileData>> script_data_;
base::Lock lock_;
};
class SingleShotMultiThreadedProxyResolverFactory
: public MultiThreadedProxyResolverFactory {
public:
SingleShotMultiThreadedProxyResolverFactory(
size_t max_num_threads,
std::unique_ptr<ProxyResolverFactory> factory)
: MultiThreadedProxyResolverFactory(max_num_threads, false),
factory_(std::move(factory)) {}
std::unique_ptr<ProxyResolverFactory> CreateProxyResolverFactory() override {
DCHECK(factory_);
return std::move(factory_);
}
private:
std::unique_ptr<ProxyResolverFactory> factory_;
};
class MultiThreadedProxyResolverTest : public TestWithTaskEnvironment {
public:
void Init(size_t num_threads) {
auto factory_owner = std::make_unique<BlockableProxyResolverFactory>();
factory_ = factory_owner.get();
resolver_factory_ =
std::make_unique<SingleShotMultiThreadedProxyResolverFactory>(
num_threads, std::move(factory_owner));
TestCompletionCallback ready_callback;
std::unique_ptr<ProxyResolverFactory::Request> request;
resolver_factory_->CreateProxyResolver(
PacFileData::FromUTF8("pac script bytes"), &resolver_,
ready_callback.callback(), &request);
EXPECT_TRUE(request);
ASSERT_THAT(ready_callback.WaitForResult(), IsOk());
ASSERT_EQ(1u, factory_->script_data().size());
EXPECT_EQ(u"pac script bytes", factory_->script_data()[0]->utf16());
}
void ClearResolver() { resolver_.reset(); }
BlockableProxyResolverFactory& factory() {
DCHECK(factory_);
return *factory_;
}
ProxyResolver& resolver() {
DCHECK(resolver_);
return *resolver_;
}
private:
raw_ptr<BlockableProxyResolverFactory, DanglingUntriaged> factory_ = nullptr;
std::unique_ptr<ProxyResolverFactory> factory_owner_;
std::unique_ptr<MultiThreadedProxyResolverFactory> resolver_factory_;
std::unique_ptr<ProxyResolver> resolver_;
};
TEST_F(MultiThreadedProxyResolverTest, SingleThread_Basic) {
const size_t kNumThreads = 1u;
ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
int rv;
TestCompletionCallback callback0;
RecordingNetLogObserver net_log_observer;
ProxyInfo results0;
rv = resolver().GetProxyForURL(
GURL("http://request0"), NetworkAnonymizationKey(), &results0,
callback0.callback(), nullptr,
NetLogWithSource::Make(NetLogSourceType::NONE));
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback0.WaitForResult();
EXPECT_EQ(0, rv);
EXPECT_EQ("PROXY request0:80", results0.ToDebugString());
auto entries0 = net_log_observer.GetEntries();
ASSERT_EQ(2u, entries0.size());
EXPECT_EQ(NetLogEventType::SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
TestCompletionCallback callback1;
ProxyInfo results1;
rv = resolver().GetProxyForURL(
GURL("http://request1"), NetworkAnonymizationKey(), &results1,
callback1.callback(), nullptr, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
TestCompletionCallback callback2;
ProxyInfo results2;
rv = resolver().GetProxyForURL(
GURL("http://request2"), NetworkAnonymizationKey(), &results2,
callback2.callback(), nullptr, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
TestCompletionCallback callback3;
ProxyInfo results3;
rv = resolver().GetProxyForURL(
GURL("http://request3"), NetworkAnonymizationKey(), &results3,
callback3.callback(), nullptr, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback1.WaitForResult();
EXPECT_EQ(1, rv);
EXPECT_EQ("PROXY request1:80", results1.ToDebugString());
rv = callback2.WaitForResult();
EXPECT_EQ(2, rv);
EXPECT_EQ("PROXY request2:80", results2.ToDebugString());
rv = callback3.WaitForResult();
EXPECT_EQ(3, rv);
EXPECT_EQ("PROXY request3:80", results3.ToDebugString());
}
TEST_F(MultiThreadedProxyResolverTest,
SingleThread_UpdatesNetLogWithThreadWait) {
const size_t kNumThreads = 1u;
ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
int rv;
factory().resolvers()[0]->Block();
std::unique_ptr<ProxyResolver::Request> request0;
TestCompletionCallback callback0;
ProxyInfo results0;
RecordingNetLogObserver net_log_observer;
NetLogWithSource log_with_source0 =
NetLogWithSource::Make(NetLogSourceType::NONE);
rv = resolver().GetProxyForURL(
GURL("http://request0"), NetworkAnonymizationKey(), &results0,
callback0.callback(), &request0, log_with_source0);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
TestCompletionCallback callback1;
ProxyInfo results1;
NetLogWithSource log_with_source1 =
NetLogWithSource::Make(NetLogSourceType::NONE);
rv = resolver().GetProxyForURL(
GURL("http://request1"), NetworkAnonymizationKey(), &results1,
callback1.callback(), nullptr, log_with_source1);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
std::unique_ptr<ProxyResolver::Request> request2;
TestCompletionCallback callback2;
ProxyInfo results2;
NetLogWithSource log_with_source2 =
NetLogWithSource::Make(NetLogSourceType::NONE);
rv = resolver().GetProxyForURL(
GURL("http://request2"), NetworkAnonymizationKey(), &results2,
callback2.callback(), &request2, log_with_source2);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
factory().resolvers()[0]->WaitUntilBlocked();
factory().resolvers()[0]->Unblock();
EXPECT_EQ(0, callback0.WaitForResult());
EXPECT_EQ("PROXY request0:80", results0.ToDebugString());
auto entries0 =
net_log_observer.GetEntriesForSource(log_with_source0.source());
ASSERT_EQ(2u, entries0.size());
EXPECT_EQ(NetLogEventType::SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
EXPECT_EQ(1, callback1.WaitForResult());
EXPECT_EQ("PROXY request1:80", results1.ToDebugString());
auto entries1 =
net_log_observer.GetEntriesForSource(log_with_source1.source());
ASSERT_EQ(4u, entries1.size());
EXPECT_TRUE(LogContainsBeginEvent(
entries1, 0, NetLogEventType::WAITING_FOR_PROXY_RESOLVER_THREAD));
EXPECT_TRUE(LogContainsEndEvent(
entries1, 1, NetLogEventType::WAITING_FOR_PROXY_RESOLVER_THREAD));
EXPECT_EQ(2, callback2.WaitForResult());
EXPECT_EQ("PROXY request2:80", results2.ToDebugString());
auto entries2 =
net_log_observer.GetEntriesForSource(log_with_source2.source());
ASSERT_EQ(4u, entries2.size());
EXPECT_TRUE(LogContainsBeginEvent(
entries2, 0, NetLogEventType::WAITING_FOR_PROXY_RESOLVER_THREAD));
EXPECT_TRUE(LogContainsEndEvent(
entries2, 1, NetLogEventType::WAITING_FOR_PROXY_RESOLVER_THREAD));
}
TEST_F(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
const size_t kNumThreads = 1u;
ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
int rv;
factory().resolvers()[0]->Block();
std::unique_ptr<ProxyResolver::Request> request0;
TestCompletionCallback callback0;
ProxyInfo results0;
rv = resolver().GetProxyForURL(
GURL("http://request0"), NetworkAnonymizationKey(), &results0,
callback0.callback(), &request0, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
factory().resolvers()[0]->WaitUntilBlocked();
TestCompletionCallback callback1;
ProxyInfo results1;
rv = resolver().GetProxyForURL(
GURL("http://request1"), NetworkAnonymizationKey(), &results1,
callback1.callback(), nullptr, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
std::unique_ptr<ProxyResolver::Request> request2;
TestCompletionCallback callback2;
ProxyInfo results2;
rv = resolver().GetProxyForURL(
GURL("http://request2"), NetworkAnonymizationKey(), &results2,
callback2.callback(), &request2, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
TestCompletionCallback callback3;
ProxyInfo results3;
rv = resolver().GetProxyForURL(
GURL("http://request3"), NetworkAnonymizationKey(), &results3,
callback3.callback(), nullptr, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
request0.reset();
request2.reset();
factory().resolvers()[0]->Unblock();
rv = callback1.WaitForResult();
EXPECT_EQ(1, rv);
EXPECT_EQ("PROXY request1:80", results1.ToDebugString());
rv = callback3.WaitForResult();
EXPECT_EQ(2, rv);
EXPECT_EQ("PROXY request3:80", results3.ToDebugString());
EXPECT_FALSE(callback0.have_result());
EXPECT_FALSE(callback2.have_result());
}
TEST_F(MultiThreadedProxyResolverTest,
SingleThread_WithNetworkAnonymizationKey) {
const SchemefulSite kSite(GURL("https://origin.test/"));
const auto kNetworkAnonymizationKey =
NetworkAnonymizationKey::CreateSameSite(kSite);
const GURL kUrl("https://url.test/");
const size_t kNumThreads = 1u;
ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
int rv;
factory().resolvers()[0]->Block();
std::unique_ptr<ProxyResolver::Request> request;
TestCompletionCallback callback;
ProxyInfo results;
rv = resolver().GetProxyForURL(kUrl, kNetworkAnonymizationKey, &results,
callback.callback(), &request,
NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
factory().resolvers()[0]->WaitUntilBlocked();
factory().resolvers()[0]->Unblock();
EXPECT_EQ(0, callback.WaitForResult());
EXPECT_EQ(kUrl, factory().resolvers()[0]->last_query_url());
EXPECT_EQ(kNetworkAnonymizationKey,
factory().resolvers()[0]->last_network_anonymization_key());
}
TEST_F(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
const size_t kNumThreads = 1u;
ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
ASSERT_EQ(1u, factory().resolvers().size());
factory().resolvers()[0]->Block();
int rv;
TestCompletionCallback callback0;
ProxyInfo results0;
rv = resolver().GetProxyForURL(
GURL("http://request0"), NetworkAnonymizationKey(), &results0,
callback0.callback(), nullptr, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
TestCompletionCallback callback1;
ProxyInfo results1;
rv = resolver().GetProxyForURL(
GURL("http://request1"), NetworkAnonymizationKey(), &results1,
callback1.callback(), nullptr, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
TestCompletionCallback callback2;
ProxyInfo results2;
rv = resolver().GetProxyForURL(
GURL("http://request2"), NetworkAnonymizationKey(), &results2,
callback2.callback(), nullptr, NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
factory().resolvers()[0]->WaitUntilBlocked();
factory().resolvers()[0]->SetResolveLatency(base::Milliseconds(100));
factory().resolvers()[0]->Unblock();
ClearResolver();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(callback0.have_result());
EXPECT_FALSE(callback1.have_result());
EXPECT_FALSE(callback2.have_result());
}
TEST_F(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
const size_t kNumThreads = 3u;
ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
ASSERT_EQ(1u, factory().resolvers().size());
const int kNumRequests = 8;
int rv;
std::array<TestCompletionCallback, kNumRequests> callback;
std::array<ProxyInfo, kNumRequests> results;
std::array<std::unique_ptr<ProxyResolver::Request>, kNumRequests> request;
rv = resolver().GetProxyForURL(
GURL("http://request0"), NetworkAnonymizationKey(), &results[0],
callback[0].callback(), &request[0], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback[0].WaitForResult();
EXPECT_EQ(0, rv);
EXPECT_EQ("PROXY request0:80", results[0].ToDebugString());
ASSERT_EQ(1u, factory().resolvers().size());
EXPECT_EQ(1, factory().resolvers()[0]->request_count());
base::RunLoop().RunUntilIdle();
factory().resolvers()[0]->Block();
rv = resolver().GetProxyForURL(
GURL("http://request1"), NetworkAnonymizationKey(), &results[1],
callback[1].callback(), &request[1], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
factory().resolvers()[0]->WaitUntilBlocked();
rv = resolver().GetProxyForURL(
GURL("http://request2"), NetworkAnonymizationKey(), &results[2],
callback[2].callback(), &request[2], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(0, callback[2].WaitForResult());
ASSERT_EQ(2u, factory().resolvers().size());
factory().resolvers()[1]->Block();
rv = resolver().GetProxyForURL(
GURL("http://request3"), NetworkAnonymizationKey(), &results[3],
callback[3].callback(), &request[3], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
factory().resolvers()[1]->WaitUntilBlocked();
rv = resolver().GetProxyForURL(
GURL("http://request4"), NetworkAnonymizationKey(), &results[4],
callback[4].callback(), &request[4], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_EQ(0, callback[4].WaitForResult());
ASSERT_EQ(3u, factory().resolvers().size());
ASSERT_EQ(3u, factory().script_data().size());
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(u"pac script bytes", factory().script_data()[i]->utf16())
<< "i=" << i;
}
rv = resolver().GetProxyForURL(
GURL("http://request5"), NetworkAnonymizationKey(), &results[5],
callback[5].callback(), &request[5], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = resolver().GetProxyForURL(
GURL("http://request6"), NetworkAnonymizationKey(), &results[6],
callback[6].callback(), &request[6], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = resolver().GetProxyForURL(
GURL("http://request7"), NetworkAnonymizationKey(), &results[7],
callback[7].callback(), &request[7], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
request[5].reset();
request[6].reset();
EXPECT_EQ(2, callback[7].WaitForResult());
EXPECT_FALSE(callback[5].have_result());
EXPECT_FALSE(callback[6].have_result());
factory().resolvers()[0]->Unblock();
factory().resolvers()[1]->Unblock();
EXPECT_EQ(1, callback[1].WaitForResult());
EXPECT_EQ(1, callback[3].WaitForResult());
EXPECT_EQ(2, factory().resolvers()[0]->request_count());
EXPECT_EQ(2, factory().resolvers()[1]->request_count());
EXPECT_EQ(3, factory().resolvers()[2]->request_count());
}
TEST_F(MultiThreadedProxyResolverTest, OneThreadBlocked) {
const size_t kNumThreads = 2u;
ASSERT_NO_FATAL_FAILURE(Init(kNumThreads));
int rv;
ASSERT_EQ(1u, factory().resolvers().size());
EXPECT_EQ(u"pac script bytes", factory().script_data()[0]->utf16());
const int kNumRequests = 4;
std::array<TestCompletionCallback, kNumRequests> callback;
std::array<ProxyInfo, kNumRequests> results;
std::array<std::unique_ptr<ProxyResolver::Request>, kNumRequests> request;
factory().resolvers()[0]->Block();
rv = resolver().GetProxyForURL(
GURL("http://request0"), NetworkAnonymizationKey(), &results[0],
callback[0].callback(), &request[0], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
factory().resolvers()[0]->WaitUntilBlocked();
for (int i = 1; i < kNumRequests; ++i) {
rv = resolver().GetProxyForURL(
GURL(base::StringPrintf("http://request%d", i)),
NetworkAnonymizationKey(), &results[i], callback[i].callback(),
&request[i], NetLogWithSource());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
}
for (int i = 1; i < kNumRequests; ++i) {
EXPECT_EQ(i - 1, callback[i].WaitForResult());
}
factory().resolvers()[0]->Unblock();
EXPECT_EQ(0, callback[0].WaitForResult());
ASSERT_EQ(2u, factory().resolvers().size());
EXPECT_EQ(1, factory().resolvers()[0]->request_count());
EXPECT_EQ(3, factory().resolvers()[1]->request_count());
}
class FailingProxyResolverFactory : public ProxyResolverFactory {
public:
FailingProxyResolverFactory() : ProxyResolverFactory(false) {}
int CreateProxyResolver(const scoped_refptr<PacFileData>& script_data,
std::unique_ptr<ProxyResolver>* result,
CompletionOnceCallback callback,
std::unique_ptr<Request>* request) override {
return ERR_PAC_SCRIPT_FAILED;
}
};
TEST_F(MultiThreadedProxyResolverTest, ProxyResolverFactoryError) {
const size_t kNumThreads = 1u;
SingleShotMultiThreadedProxyResolverFactory resolver_factory(
kNumThreads, std::make_unique<FailingProxyResolverFactory>());
TestCompletionCallback ready_callback;
std::unique_ptr<ProxyResolverFactory::Request> request;
std::unique_ptr<ProxyResolver> resolver;
EXPECT_EQ(ERR_IO_PENDING,
resolver_factory.CreateProxyResolver(
PacFileData::FromUTF8("pac script bytes"), &resolver,
ready_callback.callback(), &request));
EXPECT_TRUE(request);
EXPECT_THAT(ready_callback.WaitForResult(), IsError(ERR_PAC_SCRIPT_FAILED));
EXPECT_FALSE(resolver);
}
void Fail(int error) {
FAIL() << "Unexpected callback with error " << error;
}
TEST_F(MultiThreadedProxyResolverTest, CancelCreate) {
const size_t kNumThreads = 1u;
{
SingleShotMultiThreadedProxyResolverFactory resolver_factory(
kNumThreads, std::make_unique<BlockableProxyResolverFactory>());
std::unique_ptr<ProxyResolverFactory::Request> request;
std::unique_ptr<ProxyResolver> resolver;
EXPECT_EQ(ERR_IO_PENDING, resolver_factory.CreateProxyResolver(
PacFileData::FromUTF8("pac script bytes"),
&resolver, base::BindOnce(&Fail), &request));
EXPECT_TRUE(request);
request.reset();
}
base::RunLoop().RunUntilIdle();
}
void DeleteRequest(CompletionOnceCallback callback,
std::unique_ptr<ProxyResolverFactory::Request>* request,
int result) {
std::move(callback).Run(result);
request->reset();
}
TEST_F(MultiThreadedProxyResolverTest, DeleteRequestInFactoryCallback) {
const size_t kNumThreads = 1u;
SingleShotMultiThreadedProxyResolverFactory resolver_factory(
kNumThreads, std::make_unique<BlockableProxyResolverFactory>());
std::unique_ptr<ProxyResolverFactory::Request> request;
std::unique_ptr<ProxyResolver> resolver;
TestCompletionCallback callback;
EXPECT_EQ(ERR_IO_PENDING,
resolver_factory.CreateProxyResolver(
PacFileData::FromUTF8("pac script bytes"), &resolver,
base::BindOnce(&DeleteRequest, callback.callback(),
base::Unretained(&request)),
&request));
EXPECT_TRUE(request);
EXPECT_THAT(callback.WaitForResult(), IsOk());
}
TEST_F(MultiThreadedProxyResolverTest, DestroyFactoryWithRequestsInProgress) {
const size_t kNumThreads = 1u;
std::unique_ptr<ProxyResolverFactory::Request> request;
std::unique_ptr<ProxyResolver> resolver;
{
SingleShotMultiThreadedProxyResolverFactory resolver_factory(
kNumThreads, std::make_unique<BlockableProxyResolverFactory>());
EXPECT_EQ(ERR_IO_PENDING, resolver_factory.CreateProxyResolver(
PacFileData::FromUTF8("pac script bytes"),
&resolver, base::BindOnce(&Fail), &request));
EXPECT_TRUE(request);
}
base::RunLoop().RunUntilIdle();
}
}
}