| Index: net/dns/dns_transaction_unittest.cc
|
| diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
|
| index 3e3c2fe7a0f464dfa63082e7f65f2339a38a3fb8..8d15b446b620fe66de07814cde5d75b9cd377824 100644
|
| --- a/net/dns/dns_transaction_unittest.cc
|
| +++ b/net/dns/dns_transaction_unittest.cc
|
| @@ -4,259 +4,314 @@
|
|
|
| #include "net/dns/dns_transaction.h"
|
|
|
| -#include <deque>
|
| -#include <vector>
|
| -
|
| #include "base/bind.h"
|
| -#include "base/test/test_timeouts.h"
|
| -#include "base/time.h"
|
| -#include "net/dns/dns_protocol.h"
|
| -#include "net/dns/dns_query.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "net/base/big_endian.h"
|
| +#include "net/base/net_log.h"
|
| +#include "net/base/sys_addrinfo.h"
|
| #include "net/dns/dns_response.h"
|
| #include "net/dns/dns_session.h"
|
| #include "net/dns/dns_test_util.h"
|
| #include "net/socket/socket_test_util.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| +// TODO(szym): test timeout.
|
| +// TODO(szym): test server fallback.
|
| +// TODO(szym): test suffix search.
|
| +
|
| namespace net {
|
|
|
| namespace {
|
|
|
| -// A mock for RandIntCallback that always returns 0.
|
| -int ReturnZero(int min, int max) {
|
| - return 0;
|
| -}
|
| -
|
| class DnsTransactionTest : public testing::Test {
|
| - protected:
|
| + public:
|
| + class TestHelper {
|
| + public:
|
| + // If |answer_count| < 0, it is the expected error code.
|
| + TestHelper(const char* name,
|
| + uint16 type,
|
| + const MockWrite& write,
|
| + const MockRead& read,
|
| + int answer_count)
|
| + : qname(name),
|
| + qtype(type),
|
| + expected_answer_count(answer_count),
|
| + completed(false) {
|
| + writes.push_back(write);
|
| + reads.push_back(read);
|
| + ReadBigEndian<uint16>(write.data, &transaction_id);
|
| + data.reset(new StaticSocketDataProvider(&reads[0], reads.size(),
|
| + &writes[0], writes.size()));
|
| + }
|
| +
|
| + void MakeRequest(DnsTransactionFactory* client) {
|
| + EXPECT_EQ(NULL, transaction.get());
|
| + transaction = client->CreateTransaction(
|
| + qname,
|
| + qtype,
|
| + base::Bind(&TestHelper::OnTransactionComplete,
|
| + base::Unretained(this)),
|
| + BoundNetLog());
|
| + EXPECT_EQ(qname, transaction->GetHostname());
|
| + EXPECT_EQ(qtype, transaction->GetType());
|
| + int rv = transaction->Start();
|
| + if (rv != ERR_IO_PENDING) {
|
| + EXPECT_NE(OK, rv);
|
| + EXPECT_EQ(expected_answer_count, rv);
|
| + }
|
| + }
|
| +
|
| + void Cancel() {
|
| + ASSERT_TRUE(transaction.get() != NULL);
|
| + transaction.reset(NULL);
|
| + }
|
| +
|
| + void OnTransactionComplete(DnsTransaction* t,
|
| + int rv,
|
| + const DnsResponse* response) {
|
| + EXPECT_FALSE(completed);
|
| + EXPECT_EQ(transaction.get(), t);
|
| +
|
| + if (expected_answer_count >= 0) {
|
| + EXPECT_EQ(OK, rv);
|
| + EXPECT_EQ(expected_answer_count, response->answer_count());
|
| +
|
| + DnsRecordParser parser = response->Parser();
|
| + DnsResourceRecord record;
|
| + for (int i = 0; i < expected_answer_count; ++i) {
|
| + EXPECT_TRUE(parser.ParseRecord(&record));
|
| + }
|
| + EXPECT_TRUE(parser.AtEnd());
|
| +
|
| + } else {
|
| + EXPECT_EQ(expected_answer_count, rv);
|
| + EXPECT_EQ(NULL, response);
|
| + }
|
| +
|
| + completed = true;
|
| + }
|
| +
|
| + void CancelOnTransactionComplete(DnsTransaction* req,
|
| + int rv,
|
| + const DnsResponse* response) {
|
| + EXPECT_FALSE(completed);
|
| + Cancel();
|
| + }
|
| +
|
| + std::string qname;
|
| + uint16 qtype;
|
| + std::vector<MockWrite> writes;
|
| + std::vector<MockRead> reads;
|
| + uint16 transaction_id; // Id from first write.
|
| + scoped_ptr<StaticSocketDataProvider> data;
|
| + scoped_ptr<DnsTransaction> transaction;
|
| + int expected_answer_count;
|
| +
|
| + bool completed;
|
| + };
|
| +
|
| virtual void SetUp() OVERRIDE {
|
| - callback_ = base::Bind(&DnsTransactionTest::OnTransactionComplete,
|
| - base::Unretained(this));
|
| - qname_ = std::string(kT0DnsName, arraysize(kT0DnsName));
|
| - // Use long timeout to prevent timing out on slow bots.
|
| - ConfigureSession(base::TimeDelta::FromMilliseconds(
|
| - TestTimeouts::action_timeout_ms()));
|
| + helpers_.push_back(new TestHelper(
|
| + kT0HostName,
|
| + kT0Qtype,
|
| + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
|
| + arraysize(kT0QueryDatagram)),
|
| + MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram),
|
| + arraysize(kT0ResponseDatagram)),
|
| + arraysize(kT0IpAddresses) + 1)); // +1 for CNAME RR
|
| +
|
| + helpers_.push_back(new TestHelper(
|
| + kT1HostName,
|
| + kT1Qtype,
|
| + MockWrite(true, reinterpret_cast<const char*>(kT1QueryDatagram),
|
| + arraysize(kT1QueryDatagram)),
|
| + MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram),
|
| + arraysize(kT1ResponseDatagram)),
|
| + arraysize(kT1IpAddresses) + 1)); // +1 for CNAME RR
|
| +
|
| + helpers_.push_back(new TestHelper(
|
| + kT2HostName,
|
| + kT2Qtype,
|
| + MockWrite(true, reinterpret_cast<const char*>(kT2QueryDatagram),
|
| + arraysize(kT2QueryDatagram)),
|
| + MockRead(true, reinterpret_cast<const char*>(kT2ResponseDatagram),
|
| + arraysize(kT2ResponseDatagram)),
|
| + arraysize(kT2IpAddresses) + 1)); // +1 for CNAME RR
|
| +
|
| + helpers_.push_back(new TestHelper(
|
| + kT3HostName,
|
| + kT3Qtype,
|
| + MockWrite(true, reinterpret_cast<const char*>(kT3QueryDatagram),
|
| + arraysize(kT3QueryDatagram)),
|
| + MockRead(true, reinterpret_cast<const char*>(kT3ResponseDatagram),
|
| + arraysize(kT3ResponseDatagram)),
|
| + arraysize(kT3IpAddresses) + 2)); // +2 for CNAME RR
|
| +
|
| + CreateFactory();
|
| }
|
|
|
| - void ConfigureSession(const base::TimeDelta& timeout) {
|
| - IPEndPoint dns_server;
|
| - bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server);
|
| - ASSERT_TRUE(rv);
|
| + void CreateFactory() {
|
| + MockClientSocketFactory* socket_factory = new MockClientSocketFactory();
|
| +
|
| + transaction_ids_.clear();
|
| + for (unsigned i = 0; i < helpers_.size(); ++i) {
|
| + socket_factory->AddSocketDataProvider(helpers_[i]->data.get());
|
| + transaction_ids_.push_back(static_cast<int>(helpers_[i]->transaction_id));
|
| + }
|
|
|
| DnsConfig config;
|
| - config.nameservers.push_back(dns_server);
|
| - config.attempts = 3;
|
| - config.timeout = timeout;
|
|
|
| - session_ = new DnsSession(config,
|
| - new MockClientSocketFactory(),
|
| - base::Bind(&ReturnZero),
|
| - NULL /* NetLog */);
|
| - }
|
| + IPEndPoint dns_server;
|
| + {
|
| + bool rv = CreateDnsAddress(kDnsIp, kDnsPort, &dns_server);
|
| + EXPECT_TRUE(rv);
|
| + }
|
| + config.nameservers.push_back(dns_server);
|
|
|
| - void StartTransaction() {
|
| - transaction_.reset(new DnsTransaction(session_.get(),
|
| - qname_,
|
| - kT0Qtype,
|
| - callback_,
|
| - BoundNetLog()));
|
| + DnsSession* session = new DnsSession(
|
| + config,
|
| + socket_factory,
|
| + base::Bind(&DnsTransactionTest::GetNextId, base::Unretained(this)),
|
| + NULL /* NetLog */);
|
|
|
| - int rv0 = transaction_->Start();
|
| - EXPECT_EQ(ERR_IO_PENDING, rv0);
|
| + factory_ = DnsTransactionFactory::CreateFactory(session);
|
| }
|
|
|
| - void OnTransactionComplete(DnsTransaction* transaction, int rv) {
|
| - EXPECT_EQ(transaction_.get(), transaction);
|
| - EXPECT_EQ(qname_, transaction->query()->qname().as_string());
|
| - EXPECT_EQ(kT0Qtype, transaction->query()->qtype());
|
| - rv_ = rv;
|
| - MessageLoop::current()->Quit();
|
| + virtual void TearDown() OVERRIDE {
|
| + STLDeleteElements(&helpers_);
|
| }
|
|
|
| - MockClientSocketFactory& factory() {
|
| - return *static_cast<MockClientSocketFactory*>(session_->socket_factory());
|
| + int GetNextId(int min, int max) {
|
| + EXPECT_FALSE(transaction_ids_.empty());
|
| + int id = transaction_ids_.front();
|
| + transaction_ids_.pop_front();
|
| + EXPECT_GE(id, min);
|
| + EXPECT_LE(id, max);
|
| + return id;
|
| }
|
|
|
| - int rv() const { return rv_; }
|
| -
|
| - private:
|
| - DnsTransaction::ResultCallback callback_;
|
| - std::string qname_;
|
| - scoped_refptr<DnsSession> session_;
|
| - scoped_ptr<DnsTransaction> transaction_;
|
| -
|
| - int rv_;
|
| + protected:
|
| + std::vector<TestHelper*> helpers_;
|
| + std::deque<int> transaction_ids_;
|
| + scoped_ptr<DnsTransactionFactory> factory_;
|
| };
|
|
|
| -TEST_F(DnsTransactionTest, NormalQueryResponseTest) {
|
| - MockWrite writes0[] = {
|
| - MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
|
| - arraysize(kT0QueryDatagram))
|
| - };
|
| +TEST_F(DnsTransactionTest, Lookup) {
|
| + helpers_[0]->MakeRequest(factory_.get());
|
|
|
| - MockRead reads0[] = {
|
| - MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram),
|
| - arraysize(kT0ResponseDatagram))
|
| - };
|
| + // Wait until result.
|
| + MessageLoop::current()->RunAllPending();
|
|
|
| - StaticSocketDataProvider data(reads0, arraysize(reads0),
|
| - writes0, arraysize(writes0));
|
| - factory().AddSocketDataProvider(&data);
|
| + EXPECT_TRUE(helpers_[0]->completed);
|
| +}
|
|
|
| - StartTransaction();
|
| - MessageLoop::current()->Run();
|
| +TEST_F(DnsTransactionTest, ConcurrentLookup) {
|
| + for (unsigned i = 0; i < helpers_.size(); ++i) {
|
| + helpers_[i]->MakeRequest(factory_.get());
|
| + }
|
|
|
| - EXPECT_EQ(OK, rv());
|
| - // TODO(szym): test fields of |transaction_->response()|
|
| + MessageLoop::current()->RunAllPending();
|
|
|
| - EXPECT_TRUE(data.at_read_eof());
|
| - EXPECT_TRUE(data.at_write_eof());
|
| + for (unsigned i = 0; i < helpers_.size(); ++i) {
|
| + EXPECT_TRUE(helpers_[i]->completed);
|
| + }
|
| }
|
|
|
| -TEST_F(DnsTransactionTest, MismatchedQueryResponseTest) {
|
| - MockWrite writes0[] = {
|
| - MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
|
| - arraysize(kT0QueryDatagram))
|
| - };
|
| -
|
| - MockRead reads1[] = {
|
| - MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram),
|
| - arraysize(kT1ResponseDatagram))
|
| - };
|
| -
|
| - StaticSocketDataProvider data(reads1, arraysize(reads1),
|
| - writes0, arraysize(writes0));
|
| - factory().AddSocketDataProvider(&data);
|
| +TEST_F(DnsTransactionTest, CancelLookup) {
|
| + for (unsigned i = 0; i < helpers_.size(); ++i) {
|
| + helpers_[i]->MakeRequest(factory_.get());
|
| + }
|
|
|
| - StartTransaction();
|
| - MessageLoop::current()->Run();
|
| + helpers_[0]->Cancel();
|
| + helpers_[2]->Cancel();
|
|
|
| - EXPECT_EQ(ERR_DNS_MALFORMED_RESPONSE, rv());
|
| + MessageLoop::current()->RunAllPending();
|
|
|
| - EXPECT_TRUE(data.at_read_eof());
|
| - EXPECT_TRUE(data.at_write_eof());
|
| + EXPECT_FALSE(helpers_[0]->completed);
|
| + EXPECT_TRUE(helpers_[1]->completed);
|
| + EXPECT_FALSE(helpers_[2]->completed);
|
| + EXPECT_TRUE(helpers_[3]->completed);
|
| }
|
|
|
| -// Test that after the first timeout we do a fresh connection and if we get
|
| -// a response on the new connection, we return it.
|
| -TEST_F(DnsTransactionTest, FirstTimeoutTest) {
|
| - MockWrite writes0[] = {
|
| - MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
|
| - arraysize(kT0QueryDatagram))
|
| - };
|
| -
|
| - MockRead reads0[] = {
|
| - MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram),
|
| - arraysize(kT0ResponseDatagram))
|
| - };
|
| +TEST_F(DnsTransactionTest, DestroyClient) {
|
| + for (unsigned i = 0; i < helpers_.size(); ++i) {
|
| + helpers_[i]->MakeRequest(factory_.get());
|
| + }
|
|
|
| - scoped_refptr<DelayedSocketData> socket0_data(
|
| - new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
|
| - scoped_refptr<DelayedSocketData> socket1_data(
|
| - new DelayedSocketData(0, reads0, arraysize(reads0),
|
| - writes0, arraysize(writes0)));
|
| + // Destroying the client does not affect running requests.
|
| + factory_.reset(NULL);
|
|
|
| - // Use short timeout to speed up the test.
|
| - ConfigureSession(base::TimeDelta::FromMilliseconds(
|
| - TestTimeouts::tiny_timeout_ms()));
|
| - factory().AddSocketDataProvider(socket0_data.get());
|
| - factory().AddSocketDataProvider(socket1_data.get());
|
| + MessageLoop::current()->RunAllPending();
|
|
|
| - StartTransaction();
|
| + for (unsigned i = 0; i < helpers_.size(); ++i) {
|
| + EXPECT_TRUE(helpers_[i]->completed);
|
| + }
|
| +}
|
|
|
| - MessageLoop::current()->Run();
|
| +TEST_F(DnsTransactionTest, DestroyRequestFromCallback) {
|
| + // Custom callback to cancel the completing request.
|
| + helpers_[0]->transaction = factory_->CreateTransaction(
|
| + helpers_[0]->qname,
|
| + helpers_[0]->qtype,
|
| + base::Bind(&TestHelper::CancelOnTransactionComplete,
|
| + base::Unretained(helpers_[0])),
|
| + BoundNetLog());
|
|
|
| - EXPECT_EQ(OK, rv());
|
| + int rv = helpers_[0]->transaction->Start();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
|
|
| - EXPECT_TRUE(socket0_data->at_read_eof());
|
| - EXPECT_TRUE(socket0_data->at_write_eof());
|
| - EXPECT_TRUE(socket1_data->at_read_eof());
|
| - EXPECT_TRUE(socket1_data->at_write_eof());
|
| - EXPECT_EQ(2u, factory().udp_client_sockets().size());
|
| -}
|
| + for (unsigned i = 1; i < helpers_.size(); ++i) {
|
| + helpers_[i]->MakeRequest(factory_.get());
|
| + }
|
|
|
| -// Test that after the first timeout we do a fresh connection, and after
|
| -// the second timeout we do another fresh connection, and if we get a
|
| -// response on the second connection, we return it.
|
| -TEST_F(DnsTransactionTest, SecondTimeoutTest) {
|
| - MockWrite writes0[] = {
|
| - MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
|
| - arraysize(kT0QueryDatagram))
|
| - };
|
| + MessageLoop::current()->RunAllPending();
|
|
|
| - MockRead reads0[] = {
|
| - MockRead(true, reinterpret_cast<const char*>(kT0ResponseDatagram),
|
| - arraysize(kT0ResponseDatagram))
|
| - };
|
| -
|
| - scoped_refptr<DelayedSocketData> socket0_data(
|
| - new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
|
| - scoped_refptr<DelayedSocketData> socket1_data(
|
| - new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
|
| - scoped_refptr<DelayedSocketData> socket2_data(
|
| - new DelayedSocketData(0, reads0, arraysize(reads0),
|
| - writes0, arraysize(writes0)));
|
| -
|
| - // Use short timeout to speed up the test.
|
| - ConfigureSession(base::TimeDelta::FromMilliseconds(
|
| - TestTimeouts::tiny_timeout_ms()));
|
| - factory().AddSocketDataProvider(socket0_data.get());
|
| - factory().AddSocketDataProvider(socket1_data.get());
|
| - factory().AddSocketDataProvider(socket2_data.get());
|
| -
|
| - StartTransaction();
|
| -
|
| - MessageLoop::current()->Run();
|
| -
|
| - EXPECT_EQ(OK, rv());
|
| -
|
| - EXPECT_TRUE(socket0_data->at_read_eof());
|
| - EXPECT_TRUE(socket0_data->at_write_eof());
|
| - EXPECT_TRUE(socket1_data->at_read_eof());
|
| - EXPECT_TRUE(socket1_data->at_write_eof());
|
| - EXPECT_TRUE(socket2_data->at_read_eof());
|
| - EXPECT_TRUE(socket2_data->at_write_eof());
|
| - EXPECT_EQ(3u, factory().udp_client_sockets().size());
|
| + EXPECT_FALSE(helpers_[0]->completed);
|
| + for (unsigned i = 1; i < helpers_.size(); ++i) {
|
| + EXPECT_TRUE(helpers_[i]->completed);
|
| + }
|
| }
|
|
|
| -// Test that after the first timeout we do a fresh connection, and after
|
| -// the second timeout we do another fresh connection and after the third
|
| -// timeout we give up and return a timeout error.
|
| -TEST_F(DnsTransactionTest, ThirdTimeoutTest) {
|
| - MockWrite writes0[] = {
|
| - MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
|
| - arraysize(kT0QueryDatagram))
|
| - };
|
| +TEST_F(DnsTransactionTest, HandleFailure) {
|
| + STLDeleteElements(&helpers_);
|
| + // Wrong question.
|
| + helpers_.push_back(new TestHelper(
|
| + kT0HostName,
|
| + kT0Qtype,
|
| + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
|
| + arraysize(kT0QueryDatagram)),
|
| + MockRead(true, reinterpret_cast<const char*>(kT1ResponseDatagram),
|
| + arraysize(kT1ResponseDatagram)),
|
| + ERR_DNS_MALFORMED_RESPONSE));
|
| +
|
| + // Response with NXDOMAIN.
|
| + uint8 nxdomain_response[arraysize(kT0QueryDatagram)];
|
| + memcpy(nxdomain_response, kT0QueryDatagram, arraysize(nxdomain_response));
|
| + nxdomain_response[2] &= 0x80; // Response bit.
|
| + nxdomain_response[3] &= 0x03; // NXDOMAIN bit.
|
| + helpers_.push_back(new TestHelper(
|
| + kT0HostName,
|
| + kT0Qtype,
|
| + MockWrite(true, reinterpret_cast<const char*>(kT0QueryDatagram),
|
| + arraysize(kT0QueryDatagram)),
|
| + MockRead(true, reinterpret_cast<const char*>(nxdomain_response),
|
| + arraysize(nxdomain_response)),
|
| + ERR_NAME_NOT_RESOLVED));
|
| +
|
| + CreateFactory();
|
| +
|
| + for (unsigned i = 0; i < helpers_.size(); ++i) {
|
| + helpers_[i]->MakeRequest(factory_.get());
|
| + }
|
| +
|
| + MessageLoop::current()->RunAllPending();
|
|
|
| - scoped_refptr<DelayedSocketData> socket0_data(
|
| - new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
|
| - scoped_refptr<DelayedSocketData> socket1_data(
|
| - new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
|
| - scoped_refptr<DelayedSocketData> socket2_data(
|
| - new DelayedSocketData(2, NULL, 0, writes0, arraysize(writes0)));
|
| -
|
| - // Use short timeout to speed up the test.
|
| - ConfigureSession(base::TimeDelta::FromMilliseconds(
|
| - TestTimeouts::tiny_timeout_ms()));
|
| - factory().AddSocketDataProvider(socket0_data.get());
|
| - factory().AddSocketDataProvider(socket1_data.get());
|
| - factory().AddSocketDataProvider(socket2_data.get());
|
| -
|
| - StartTransaction();
|
| -
|
| - MessageLoop::current()->Run();
|
| -
|
| - EXPECT_EQ(ERR_DNS_TIMED_OUT, rv());
|
| -
|
| - EXPECT_TRUE(socket0_data->at_read_eof());
|
| - EXPECT_TRUE(socket0_data->at_write_eof());
|
| - EXPECT_TRUE(socket1_data->at_read_eof());
|
| - EXPECT_TRUE(socket1_data->at_write_eof());
|
| - EXPECT_TRUE(socket2_data->at_read_eof());
|
| - EXPECT_TRUE(socket2_data->at_write_eof());
|
| - EXPECT_EQ(3u, factory().udp_client_sockets().size());
|
| + for (unsigned i = 0; i < helpers_.size(); ++i) {
|
| + EXPECT_TRUE(helpers_[i]->completed);
|
| + }
|
| }
|
|
|
| } // namespace
|
|
|
| } // namespace net
|
| +
|
|
|