summaryrefslogtreecommitdiffstats
path: root/net/base/async_host_resolver_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/base/async_host_resolver_unittest.cc')
-rw-r--r--net/base/async_host_resolver_unittest.cc574
1 files changed, 574 insertions, 0 deletions
diff --git a/net/base/async_host_resolver_unittest.cc b/net/base/async_host_resolver_unittest.cc
new file mode 100644
index 0000000..0e92981
--- /dev/null
+++ b/net/base/async_host_resolver_unittest.cc
@@ -0,0 +1,574 @@
+// Copyright (c) 2011 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/base/async_host_resolver.h"
+
+#include "base/bind.h"
+#include "base/scoped_ptr.h"
+#include "net/base/dns_test_util.h"
+#include "net/base/net_log.h"
+#include "net/base/rand_callback.h"
+#include "net/base/sys_addrinfo.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+void VerifyAddressList(const std::vector<const char*>& ip_addresses,
+ int port,
+ const AddressList& addrlist) {
+ ASSERT_LT(0u, ip_addresses.size());
+ ASSERT_NE(static_cast<addrinfo*>(NULL), addrlist.head());
+
+ IPAddressNumber ip_number;
+ const struct addrinfo* ainfo = addrlist.head();
+ for (std::vector<const char*>::const_iterator i = ip_addresses.begin();
+ i != ip_addresses.end(); ++i, ainfo = ainfo->ai_next) {
+ ASSERT_NE(static_cast<addrinfo*>(NULL), ainfo);
+ EXPECT_EQ(sizeof(struct sockaddr_in), ainfo->ai_addrlen);
+
+ const struct sockaddr* sa = ainfo->ai_addr;
+ const struct sockaddr_in* sa_in = (const struct sockaddr_in*) sa;
+ EXPECT_TRUE(htons(port) == sa_in->sin_port);
+ EXPECT_STREQ(*i, NetAddressToString(sa, ainfo->ai_addrlen).c_str());
+ }
+ ASSERT_EQ(static_cast<addrinfo*>(NULL), ainfo);
+}
+
+} // namespace
+
+static const int kPortNum = 80;
+static const size_t kMaxTransactions = 2;
+static const size_t kMaxPendingRequests = 1;
+static int transaction_ids[] = {0, 1, 2, 3};
+
+// The following fixture sets up an environment for four different lookups
+// with their data defined in dns_test_util.h. All tests make use of these
+// predefined variables instead of each defining their own, to avoid
+// boilerplate code in every test. Assuming every coming query is for a
+// distinct hostname, as |kMaxTransactions| is set to 2 and
+// |kMaxPendingRequests| is set to 1, first two queries start immediately
+// and the next one is sent to pending queue; as a result, the next query
+// should either fail itself or cause the pending query to fail depending
+// on its priority.
+class AsyncHostResolverTest : public testing::Test {
+ public:
+ AsyncHostResolverTest()
+ : info0_(HostPortPair(kT0HostName, kPortNum)),
+ info1_(HostPortPair(kT1HostName, kPortNum)),
+ info2_(HostPortPair(kT2HostName, kPortNum)),
+ info3_(HostPortPair(kT3HostName, kPortNum)),
+ ip_addresses0_(kT0IpAddresses,
+ kT0IpAddresses + arraysize(kT0IpAddresses)),
+ ip_addresses1_(kT1IpAddresses,
+ kT1IpAddresses + arraysize(kT1IpAddresses)),
+ ip_addresses2_(kT2IpAddresses,
+ kT2IpAddresses + arraysize(kT2IpAddresses)),
+ ip_addresses3_(kT3IpAddresses,
+ kT3IpAddresses + arraysize(kT3IpAddresses)),
+ test_prng_(std::deque<int>(
+ transaction_ids, transaction_ids + arraysize(transaction_ids))) {
+
+ rand_int_cb_ = base::Bind(&TestPrng::GetNext,
+ base::Unretained(&test_prng_));
+ // AF_INET only for now.
+ info0_.set_address_family(ADDRESS_FAMILY_IPV4);
+ info1_.set_address_family(ADDRESS_FAMILY_IPV4);
+ info2_.set_address_family(ADDRESS_FAMILY_IPV4);
+ info3_.set_address_family(ADDRESS_FAMILY_IPV4);
+
+ // Setup socket read/writes for transaction 0.
+ writes0_.push_back(
+ MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
+ arraysize(kT0QueryDatagram)));
+ reads0_.push_back(
+ MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram),
+ arraysize(kT0ResponseDatagram)));
+ data0_.reset(new StaticSocketDataProvider(&reads0_[0], reads0_.size(),
+ &writes0_[0], writes0_.size()));
+
+ // Setup socket read/writes for transaction 1.
+ writes1_.push_back(
+ MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram),
+ arraysize(kT1QueryDatagram)));
+ reads1_.push_back(
+ MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram),
+ arraysize(kT1ResponseDatagram)));
+ data1_.reset(new StaticSocketDataProvider(&reads1_[0], reads1_.size(),
+ &writes1_[0], writes1_.size()));
+
+ // Setup socket read/writes for transaction 2.
+ writes2_.push_back(
+ MockWrite(true, reinterpret_cast<const char*>(kT2QueryDatagram),
+ arraysize(kT2QueryDatagram)));
+ reads2_.push_back(
+ MockRead(true, reinterpret_cast<const char*>(kT2ResponseDatagram),
+ arraysize(kT2ResponseDatagram)));
+ data2_.reset(new StaticSocketDataProvider(&reads2_[0], reads2_.size(),
+ &writes2_[0], writes2_.size()));
+
+ // Setup socket read/writes for transaction 3.
+ writes3_.push_back(
+ MockWrite(true, reinterpret_cast<const char*>(kT3QueryDatagram),
+ arraysize(kT3QueryDatagram)));
+ reads3_.push_back(
+ MockRead(true, reinterpret_cast<const char*>(kT3ResponseDatagram),
+ arraysize(kT3ResponseDatagram)));
+ data3_.reset(new StaticSocketDataProvider(&reads3_[0], reads3_.size(),
+ &writes3_[0], writes3_.size()));
+
+ factory_.AddSocketDataProvider(data0_.get());
+ factory_.AddSocketDataProvider(data1_.get());
+ factory_.AddSocketDataProvider(data2_.get());
+ factory_.AddSocketDataProvider(data3_.get());
+
+ IPEndPoint dns_server;
+ bool rv0 = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server);
+ DCHECK(rv0);
+
+ resolver_.reset(
+ new AsyncHostResolver(
+ dns_server, kMaxTransactions, kMaxPendingRequests, rand_int_cb_,
+ &factory_, NULL));
+ }
+
+ protected:
+ AddressList addrlist0_, addrlist1_, addrlist2_, addrlist3_;
+ HostResolver::RequestInfo info0_, info1_, info2_, info3_;
+ std::vector<MockWrite> writes0_, writes1_, writes2_, writes3_;
+ std::vector<MockRead> reads0_, reads1_, reads2_, reads3_;
+ scoped_ptr<StaticSocketDataProvider> data0_, data1_, data2_, data3_;
+ std::vector<const char*> ip_addresses0_, ip_addresses1_,
+ ip_addresses2_, ip_addresses3_;
+ MockClientSocketFactory factory_;
+ TestPrng test_prng_;
+ RandIntCallback rand_int_cb_;
+ scoped_ptr<HostResolver> resolver_;
+ TestCompletionCallback callback0_, callback1_, callback2_, callback3_;
+};
+
+TEST_F(AsyncHostResolverTest, EmptyHostLookup) {
+ info0_.set_host_port_pair(HostPortPair("", kPortNum));
+ int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
+}
+
+TEST_F(AsyncHostResolverTest, IPv4LiteralLookup) {
+ const char* kIPLiteral = "192.168.1.2";
+ info0_.set_host_port_pair(HostPortPair(kIPLiteral, kPortNum));
+ info0_.set_host_resolver_flags(HOST_RESOLVER_CANONNAME);
+ int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(OK, rv);
+ std::vector<const char*> ip_addresses(1, kIPLiteral);
+ VerifyAddressList(ip_addresses, kPortNum, addrlist0_);
+ EXPECT_STREQ(kIPLiteral, addrlist0_.head()->ai_canonname);
+}
+
+TEST_F(AsyncHostResolverTest, IPv6LiteralLookup) {
+ info0_.set_host_port_pair(HostPortPair("2001:db8:0::42", kPortNum));
+ int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ // When support for IPv6 is added, this should succeed.
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
+}
+
+TEST_F(AsyncHostResolverTest, CachedOnlyLookup) {
+ info0_.set_only_use_cached_response(true);
+ int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ // When caching is added, this should succeed.
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
+}
+
+TEST_F(AsyncHostResolverTest, InvalidHostNameLookup) {
+ const std::string kHostName1(64, 'a');
+ info0_.set_host_port_pair(HostPortPair(kHostName1, kPortNum));
+ int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
+
+ const std::string kHostName2(4097, 'b');
+ info0_.set_host_port_pair(HostPortPair(kHostName2, kPortNum));
+ rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
+}
+
+TEST_F(AsyncHostResolverTest, Lookup) {
+ int rv = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ rv = callback0_.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_);
+}
+
+TEST_F(AsyncHostResolverTest, ConcurrentLookup) {
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+
+ rv0 = callback0_.WaitForResult();
+ EXPECT_EQ(OK, rv0);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_);
+
+ rv1 = callback1_.WaitForResult();
+ EXPECT_EQ(OK, rv1);
+ VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_);
+
+ rv2 = callback2_.WaitForResult();
+ EXPECT_EQ(OK, rv2);
+ VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_);
+
+ EXPECT_EQ(3u, factory_.udp_client_sockets().size());
+}
+
+TEST_F(AsyncHostResolverTest, SameHostLookupsConsumeSingleTransaction) {
+ // We pass the info0_ to all requests.
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info0_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ int rv2 = resolver_->Resolve(info0_, &addrlist2_, &callback2_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+
+ rv0 = callback0_.WaitForResult();
+ EXPECT_EQ(OK, rv0);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_);
+
+ rv1 = callback1_.WaitForResult();
+ EXPECT_EQ(OK, rv1);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist1_);
+
+ rv2 = callback2_.WaitForResult();
+ EXPECT_EQ(OK, rv2);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist2_);
+
+ // Although we have three lookups, a single UDP socket was used.
+ EXPECT_EQ(1u, factory_.udp_client_sockets().size());
+}
+
+TEST_F(AsyncHostResolverTest, CancelLookup) {
+ HostResolver::RequestHandle req0 = NULL, req2 = NULL;
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, &req0,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, &req2,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+
+ resolver_->CancelRequest(req0);
+ resolver_->CancelRequest(req2);
+
+ MessageLoop::current()->RunAllPending();
+
+ EXPECT_FALSE(callback0_.have_result());
+ EXPECT_FALSE(callback2_.have_result());
+
+ rv1 = callback1_.WaitForResult();
+ EXPECT_EQ(OK, rv1);
+ VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_);
+}
+
+// Tests the following scenario: start two resolutions for the same host,
+// cancel one of them, make sure that the other one completes.
+TEST_F(AsyncHostResolverTest, CancelSameHostLookup) {
+ HostResolver::RequestHandle req0 = NULL;
+
+ // Pass the info0_ to both requests.
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, &req0,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info0_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+
+ resolver_->CancelRequest(req0);
+ MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(callback0_.have_result());
+
+ rv1 = callback1_.WaitForResult();
+ EXPECT_EQ(OK, rv1);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist1_);
+
+ EXPECT_EQ(1u, factory_.udp_client_sockets().size());
+}
+
+// Test that a queued lookup completes.
+TEST_F(AsyncHostResolverTest, QueuedLookup) {
+ // kMaxTransactions is 2, thus the following requests consume all
+ // available transactions.
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+
+ // The following request will end up in queue.
+ int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+ EXPECT_EQ(1u,
+ static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending());
+
+ // Make sure all requests complete.
+ rv0 = callback0_.WaitForResult();
+ EXPECT_EQ(OK, rv0);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_);
+
+ rv1 = callback1_.WaitForResult();
+ EXPECT_EQ(OK, rv1);
+ VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_);
+
+ rv2 = callback2_.WaitForResult();
+ EXPECT_EQ(OK, rv2);
+ VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_);
+}
+
+// Test that cancelling a queued lookup works.
+TEST_F(AsyncHostResolverTest, CancelPendingLookup) {
+ // kMaxTransactions is 2, thus the following requests consume all
+ // available transactions.
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+
+ // The following request will end up in queue.
+ HostResolver::RequestHandle req2 = NULL;
+ int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, &req2,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+ EXPECT_EQ(1u,
+ static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending());
+
+ resolver_->CancelRequest(req2);
+
+ // Make sure first two requests complete while the cancelled one doesn't.
+ MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(callback2_.have_result());
+
+ rv0 = callback0_.WaitForResult();
+ EXPECT_EQ(OK, rv0);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_);
+
+ rv1 = callback1_.WaitForResult();
+ EXPECT_EQ(OK, rv1);
+ VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_);
+}
+
+TEST_F(AsyncHostResolverTest, ResolverDestructionCancelsLookups) {
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ // This one is queued.
+ int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(1u,
+ static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending());
+
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+
+ resolver_.reset();
+
+ MessageLoop::current()->RunAllPending();
+
+ EXPECT_FALSE(callback0_.have_result());
+ EXPECT_FALSE(callback1_.have_result());
+ EXPECT_FALSE(callback2_.have_result());
+}
+
+// Test that when the number of pending lookups is at max, a new lookup
+// with a priority lower than all of those in the queue fails.
+TEST_F(AsyncHostResolverTest, OverflowQueueWithLowPriorityLookup) {
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ // This one is queued and fills up the queue since its size is 1.
+ int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(1u,
+ static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending());
+
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+
+ // This one fails.
+ info3_.set_priority(LOWEST);
+ int rv3 = resolver_->Resolve(info3_, &addrlist3_, &callback3_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, rv3);
+
+ MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(callback3_.have_result());
+}
+
+// Test that when the number of pending lookups is at max, a new lookup
+// with a priority higher than any of those in the queue succeeds and
+// causes the lowest priority lookup in the queue to fail.
+TEST_F(AsyncHostResolverTest, OverflowQueueWithHighPriorityLookup) {
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+
+ // Next lookup is queued. Since this will be ejected from the queue and
+ // will not consume a socket from our factory, we are not passing it
+ // predefined members.
+ HostResolver::RequestInfo info(HostPortPair("cnn.com", 80));
+ info.set_address_family(ADDRESS_FAMILY_IPV4);
+ AddressList addrlist_fail;
+ TestCompletionCallback callback_fail;
+ int rv_fail = resolver_->Resolve(info, &addrlist_fail, &callback_fail, NULL,
+ BoundNetLog());
+ EXPECT_EQ(1u,
+ static_cast<AsyncHostResolver*>(resolver_.get())->GetNumPending());
+
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+ EXPECT_EQ(ERR_IO_PENDING, rv_fail);
+
+ // Lookup 2 causes the above to fail, but itself should succeed.
+ info2_.set_priority(HIGHEST);
+ int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL,
+ BoundNetLog());
+
+ rv0 = callback0_.WaitForResult();
+ EXPECT_EQ(OK, rv0);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_);
+
+ rv1 = callback1_.WaitForResult();
+ EXPECT_EQ(OK, rv1);
+ VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_);
+
+ rv_fail = callback_fail.WaitForResult();
+ EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, rv_fail);
+ EXPECT_EQ(static_cast<addrinfo*>(NULL), addrlist_fail.head());
+
+ rv2 = callback2_.WaitForResult();
+ EXPECT_EQ(OK, rv2);
+ VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_);
+}
+
+// Test that registering, unregistering, and notifying of observers of
+// resolution start, completion and cancellation (both due to CancelRequest
+// and resolver destruction) work.
+TEST_F(AsyncHostResolverTest, Observers) {
+ TestHostResolverObserver observer;
+ resolver_->AddObserver(&observer);
+
+ int rv0 = resolver_->Resolve(info0_, &addrlist0_, &callback0_, NULL,
+ BoundNetLog());
+ int rv1 = resolver_->Resolve(info1_, &addrlist1_, &callback1_, NULL,
+ BoundNetLog());
+ // We will cancel this one.
+ HostResolver::RequestHandle req2 = NULL;
+ int rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, &req2,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv0);
+ EXPECT_EQ(ERR_IO_PENDING, rv1);
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+
+ // Cancel lookup 2.
+ resolver_->CancelRequest(req2);
+
+ // Lookup 0 and 1 should succeed.
+ rv0 = callback0_.WaitForResult();
+ EXPECT_EQ(OK, rv0);
+ VerifyAddressList(ip_addresses0_, kPortNum, addrlist0_);
+
+ rv1 = callback1_.WaitForResult();
+ EXPECT_EQ(OK, rv1);
+ VerifyAddressList(ip_addresses1_, kPortNum, addrlist1_);
+
+ // Next lookup should not have finished.
+ MessageLoop::current()->RunAllPending();
+ EXPECT_FALSE(callback2_.have_result());
+
+ // Verify observer calls.
+ EXPECT_EQ(3u, observer.start_log.size());
+ EXPECT_EQ(2u, observer.finish_log.size());
+ EXPECT_EQ(1u, observer.cancel_log.size());
+
+ // Lookup 0 started and finished.
+ EXPECT_TRUE(observer.start_log[0] ==
+ TestHostResolverObserver::StartOrCancelEntry(0, info0_));
+ EXPECT_TRUE(observer.finish_log[0] ==
+ TestHostResolverObserver::FinishEntry(0, true, info0_));
+
+ // Ditto for lookup 1.
+ EXPECT_TRUE(observer.start_log[1] ==
+ TestHostResolverObserver::StartOrCancelEntry(1, info1_));
+ EXPECT_TRUE(observer.finish_log[1] ==
+ TestHostResolverObserver::FinishEntry(1, true, info1_));
+
+ // Lookup 2 was cancelled, hence, failed to finish.
+ EXPECT_TRUE(observer.start_log[2] ==
+ TestHostResolverObserver::StartOrCancelEntry(2, info2_));
+ EXPECT_TRUE(observer.cancel_log[0] ==
+ TestHostResolverObserver::StartOrCancelEntry(2, info2_));
+
+ // Unregister observer.
+ resolver_->RemoveObserver(&observer);
+
+ // We will do lookup 2 again but will not be cancel it this time.
+ rv2 = resolver_->Resolve(info2_, &addrlist2_, &callback2_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv2);
+
+ // Run lookup 2 to completion.
+ rv2 = callback2_.WaitForResult();
+ EXPECT_EQ(OK, rv2);
+ VerifyAddressList(ip_addresses2_, kPortNum, addrlist2_);
+
+ // Observer log should stay the same.
+ EXPECT_EQ(3u, observer.start_log.size());
+ EXPECT_EQ(2u, observer.finish_log.size());
+ EXPECT_EQ(1u, observer.cancel_log.size());
+
+ // Re-register observer.
+ resolver_->AddObserver(&observer);
+
+ // Start lookup 3.
+ int rv3 = resolver_->Resolve(info3_, &addrlist3_, &callback3_, NULL,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv3);
+
+ // Destroy the resolver and make sure that observer was notified of just
+ // the resolution start.
+ resolver_.reset();
+
+ EXPECT_EQ(4u, observer.start_log.size()); // Was incremented by 1.
+ EXPECT_EQ(2u, observer.finish_log.size());
+ EXPECT_EQ(1u, observer.cancel_log.size());
+
+ EXPECT_TRUE(observer.start_log[3] ==
+ TestHostResolverObserver::StartOrCancelEntry(4, info3_));
+}
+
+} // namespace net