Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(322)

Unified Diff: net/dns/mdns_listener_unittest.cc

Issue 15733008: Multicast DNS implementation (initial) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mdns_implementation2
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/dns/mdns_listener_unittest.cc
diff --git a/net/dns/mdns_listener_unittest.cc b/net/dns/mdns_listener_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a6ec70833182992df8ea10af7f8e238e3d4bb746
--- /dev/null
+++ b/net/dns/mdns_listener_unittest.cc
@@ -0,0 +1,840 @@
+// Copyright (c) 2013 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 "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "net/base/rand_callback.h"
+#include "net/base/test_completion_callback.h"
+#include "net/dns/mdns_listener_impl.h"
+#include "net/dns/record_rdata.h"
+#include "net/test/fake_time_system.h"
+#include "net/udp/udp_client_socket.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::StrictMock;
+using ::testing::Exactly;
+using ::testing::_;
+
+namespace net {
+
+namespace {
+
+const char kSamplePacket1[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x02, // 2 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ // Answer 1
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+ 0x24, 0x74,
+ 0x00, 0x08, // RDLENGTH is 8 bytes.
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0xc0, 0x0c,
+
+ // Answer 2
+ 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r',
+ 0xc0, 0x14, // Pointer to "._tcp.local"
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 49 seconds.
+ 0x24, 0x75,
+ 0x00, 0x08, // RDLENGTH is 8 bytes.
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0xc0, 0x32
+ };
+
+const char kCorruptedPacketBadQuestion[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x01, // One question
+ 0x00, 0x02, // 2 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ // Question is corrupted and cannot be read.
+ 0x99, 'h', 'e', 'l', 'l', 'o',
+ 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00,
+
+ // Answer 1
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+ 0x24, 0x74,
+ 0x00, 0x99, // RDLENGTH is impossible
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0xc0, 0x0c,
+
+ // Answer 2
+ 0x08, '_', 'p', 'r', // Useless trailing data.
+};
+
+const char kCorruptedPacketUnsalvagable[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x02, // 2 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ // Answer 1
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+ 0x24, 0x74,
+ 0x00, 0x99, // RDLENGTH is impossible
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0xc0, 0x0c,
+
+ // Answer 2
+ 0x08, '_', 'p', 'r', // Useless trailing data.
+};
+
+const char kCorruptedPacketSalvagable[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x02, // 2 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ // Answer 1
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+ 0x24, 0x74,
+ 0x00, 0x08, // RDLENGTH is 8 bytes.
+ 0x99, 'h', 'e', 'l', 'l', 'o', // Bad RDATA format.
+ 0xc0, 0x0c,
+
+ // Answer 2
+ 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r',
+ 0xc0, 0x14, // Pointer to "._tcp.local"
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 49 seconds.
+ 0x24, 0x75,
+ 0x00, 0x08, // RDLENGTH is 8 bytes.
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0xc0, 0x32
+};
+
+const char kSamplePacket2[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x02, // 2 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ // Answer 1
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+ 0x24, 0x74,
+ 0x00, 0x08, // RDLENGTH is 8 bytes.
+ 0x05, 'z', 'z', 'z', 'z', 'z',
+ 0xc0, 0x0c,
+
+ // Answer 2
+ 0x08, '_', 'p', 'r', 'i', 'n', 't', 'e', 'r',
+ 0xc0, 0x14, // Pointer to "._tcp.local"
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+ 0x24, 0x74,
+ 0x00, 0x08, // RDLENGTH is 8 bytes.
+ 0x05, 'z', 'z', 'z', 'z', 'z',
+ 0xc0, 0x32
+ };
+
+const char kQueryPacketPrivet[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x00, 0x00, // Standard query.
+ 0x00, 0x01, // One question.
+ 0x00, 0x00, // 0 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ // Question
+ // This part is echoed back from the respective query.
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+};
+
+const char kSamplePacketAdditionalOnly[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x00, // 2 RRs (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x01, // 0 additional RRs
+
+ // Answer 1
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x0c, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x01, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+ 0x24, 0x74,
+ 0x00, 0x08, // RDLENGTH is 8 bytes.
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0xc0, 0x0c,
+ };
+
+
+// Connect to any port on an address. Returns the bound address.
+IPEndPoint BindToAnyPort(UDPSocket* socket,
+ const IPAddressNumber& address) {
+ IPEndPoint endpoint(address, 0);
+ int status = socket->Bind(endpoint);
+ DCHECK(status == OK);
+ status = socket->GetLocalAddress(&endpoint);
+ DCHECK(status == OK);
+
+ return endpoint;
+}
+
+class TestableMDnsListenerFactoryImpl : public MDnsListenerFactoryImpl {
+ public:
+ explicit TestableMDnsListenerFactoryImpl(FakeTimeSystem* time_system)
+ : MDnsListenerFactoryImpl(time_system, time_system) {
+ }
+
+ void SetIPv4SendEndpoint(const IPEndPoint& ipv4_send_endpoint) {
+ ipv4_send_endpoint_ = ipv4_send_endpoint;
+ }
+
+ void SetIPv6SendEndpoint(const IPEndPoint& ipv6_send_endpoint) {
+ ipv6_send_endpoint_ = ipv6_send_endpoint;
+ }
+
+ IPEndPoint ipv4_bind_endpoint() { return ipv4_bind_endpoint_; }
+
+ protected:
+ virtual IPEndPoint GetIPv4SendEndpoint() OVERRIDE {
+ return ipv4_send_endpoint_;
+ }
+
+ virtual IPEndPoint GetIPv6SendEndpoint() OVERRIDE {
+ return ipv6_send_endpoint_;
+ }
+
+ virtual bool BindToAddressAndMulticastGroup(
+ UDPSocket* socket,
+ const IPEndPoint& address,
+ const IPAddressNumber& group) OVERRIDE {
+ // Bind only to local address. Use address length to determine protocol.
+ IPAddressNumber local_address;
+ if (address.address().size() == kIPv4AddressSize) {
+ bool success = ParseIPLiteralToNumber("127.0.0.1",
+ &local_address);
+ DCHECK(success);
+ } else {
+ DCHECK(address.address().size() == kIPv6AddressSize);
+ bool success = ParseIPLiteralToNumber("::1",
+ &local_address);
+ DCHECK(success);
+ }
+
+ IPEndPoint bound_endpoint = BindToAnyPort(socket, local_address);
+ if (address.address().size() == kIPv4AddressSize) {
+ ipv4_bind_endpoint_ = bound_endpoint;
+ }
+ return true;
+ }
+
+ private:
+ IPEndPoint ipv4_send_endpoint_;
+ IPEndPoint ipv6_send_endpoint_;
+ IPEndPoint ipv4_bind_endpoint_;
+};
+
+class RecordParsedCopyContainer {
+ public:
+ RecordParsedCopyContainer() {}
+ ~RecordParsedCopyContainer() {}
+
+ bool is_set() const { return value_.get() != NULL; }
+
+ void SaveWithDummyArg(int unused, const RecordParsed* value) {
+ Save(value);
+ }
+
+ void Save(const RecordParsed* value) {
+ value_ = value->Clone();
+ }
+
+ const RecordParsed* operator->() const {
+ return value_.get();
+ }
+
+ private:
+ scoped_ptr<const RecordParsed> value_;
+};
+
+class MDnsTest : public ::testing::Test {
+ public:
+ MDnsTest();
+ virtual ~MDnsTest();
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+ void DeleteTransaction();
+
+ MOCK_METHOD2(MockableRecordCallback, void(MDnsTransactionResult,
+ const RecordParsed*));
+
+ protected:
+ void ExpectPacket(const char* packet, unsigned size);
+ void SendPacket(const char* packet, unsigned size);
+ void ExpectPtrRecord(
+ const std::string& name,
+ const std::string& ptrdomain,
+ const RecordParsedCopyContainer& record);
+
+ base::MessageLoop* message_loop_;
+ scoped_ptr<TestableMDnsListenerFactoryImpl> test_factory_;
+ scoped_ptr<UDPSocket> client_socket_;
+ scoped_ptr<UDPSocket> server_socket_;
+ scoped_ptr<UDPSocket> server_socket6_;
+ IPEndPoint mdns_ipv4_endpoint_;
+ scoped_refptr<FakeTimeSystem> time_system_;
+
+ scoped_ptr<MDnsTransaction> transaction_;
+};
+
+class MockListenerDelegate : public MDnsListenerFactory::Delegate {
+ public:
+ MOCK_METHOD2(OnRecordUpdate,
+ void(MDnsUpdateType, const RecordParsed*));
+ MOCK_METHOD2(OnNSecRecord, void(const std::string&, unsigned));
+};
+
+MDnsTest::MDnsTest()
+ : message_loop_(base::MessageLoop::current()),
+ client_socket_(new UDPSocket(DatagramSocket::DEFAULT_BIND,
+ net::RandIntCallback(),
+ NULL,
+ net::NetLog::Source())),
szym 2013/05/24 15:32:22 To minimize flakiness, we'd test this with MockUDP
Noam Samuel 2013/05/24 21:59:18 Hm. I tend to agree that, in retrospect, using moc
Noam Samuel 2013/05/29 21:25:16 Done.
szym 2013/06/02 19:01:23 I still see UDPSocket rather than DatagramServerSo
Noam Samuel 2013/06/03 17:57:55 DatagramServerSocket doesn't support multicasting
szym 2013/06/03 18:38:30 I think so.
Noam Samuel 2013/06/04 00:08:02 Added support in interface: https://codereview.chr
+ server_socket_(new UDPSocket(DatagramSocket::DEFAULT_BIND,
+ net::RandIntCallback(),
+ NULL,
+ net::NetLog::Source())),
+ server_socket6_(new UDPSocket(DatagramSocket::DEFAULT_BIND,
+ net::RandIntCallback(),
+ NULL,
+ net::NetLog::Source())) {
+ time_system_ = new FakeTimeSystem();
+ time_system_->SetNow(base::Time::FromDoubleT(1234.0));
+ test_factory_.reset(new TestableMDnsListenerFactoryImpl(time_system_));
+}
+
+MDnsTest::~MDnsTest() {
+}
+
+void MDnsTest::SetUp() {
+ IPAddressNumber loopback4;
+ ParseIPLiteralToNumber("127.0.0.1", &loopback4);
+
+ IPAddressNumber loopback6;
+ ParseIPLiteralToNumber("::1", &loopback6);
+
+ test_factory_->SetIPv4SendEndpoint(
+ BindToAnyPort(server_socket_.get(), loopback4));
+ test_factory_->SetIPv6SendEndpoint(
+ BindToAnyPort(server_socket6_.get(), loopback6));
+
+ client_socket_->Bind(IPEndPoint(loopback4, 0));
+}
+
+void MDnsTest::TearDown() {
+ client_socket_->Close();
szym 2013/05/24 15:32:22 Not necessary. ~MDnsTest (called immediately after
Noam Samuel 2013/05/29 21:25:16 Done.
+}
+
+void MDnsTest::SendPacket(const char* packet, unsigned size) {
+ TestCompletionCallback write_callback;
+
+ scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(size));
+ memcpy(buffer->data(), packet, size);
+
+ int socket_result =
+ client_socket_->SendTo(buffer,
+ size,
+ test_factory_->ipv4_bind_endpoint(),
+ write_callback.callback());
+
+ if (socket_result == ERR_IO_PENDING) {
+ socket_result = write_callback.WaitForResult();
+ }
+
+ ASSERT_EQ(size, static_cast<unsigned>(socket_result));
+
+ // Since the network layer uses MessageLoop, we RunUntilIdle here
+ // so the tests do not have to.
+ MessageLoop::current()->RunUntilIdle();
+}
+
+void MDnsTest::ExpectPtrRecord(
+ const std::string& name,
+ const std::string& ptrdomain,
+ const RecordParsedCopyContainer& record) {
+ EXPECT_TRUE(record.is_set());
+ EXPECT_EQ(name, record->name());
+ EXPECT_EQ(dns_protocol::kTypePTR, record->type());
+ EXPECT_EQ(dns_protocol::kClassIN, record->klass());
+ const PtrRecordRdata* rdata = record->rdata<PtrRecordRdata>();
+ EXPECT_TRUE(rdata != NULL);
+ EXPECT_EQ(ptrdomain, rdata->ptrdomain());
+}
+
+void MDnsTest::ExpectPacket(
+ const char* packet,
+ unsigned size) {
+ TestCompletionCallback recv_callback;
+ IPEndPoint recv_endpoint;
+ scoped_refptr<IOBufferWithSize> buf = new IOBufferWithSize(size);
+ int rv;
+
+ do {
+ rv = server_socket_->RecvFrom(buf.get(), buf->size(),
+ &recv_endpoint, recv_callback.callback());
+
+ if (rv == ERR_IO_PENDING) {
+ rv = recv_callback.WaitForResult();
+ }
+ DCHECK_GT(rv, 0);
+ // This is an easy way to compare to addresses for a test.
+ } while (recv_endpoint.ToString() !=
+ test_factory_->ipv4_bind_endpoint().ToString());
+
+ ASSERT_EQ(static_cast<int>(size), rv);
+ ASSERT_EQ(0, memcmp(packet, buf->data(), size));
+}
+
+void MDnsTest::DeleteTransaction() {
+ transaction_.reset();
+}
+
+TEST_F(MDnsTest, PassiveListeners) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+ StrictMock<MockListenerDelegate> delegate_printer;
+ StrictMock<MockListenerDelegate> delegate_ptr;
+
+ RecordParsedCopyContainer record_privet;
+ RecordParsedCopyContainer record_printer;
+
+ scoped_ptr<MDnsListener> listener_privet = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+ scoped_ptr<MDnsListener> listener_printer = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_printer._tcp.local", false, false,
+ &delegate_printer);
+ scoped_ptr<MDnsListener> listener_ptr = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "", false, false, &delegate_ptr);
+
+ ASSERT_TRUE(test_factory_->IsListeningForTests());
+
+ // Send the same packet twice to ensure no records are double-counted.
+
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ EXPECT_CALL(delegate_printer, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_printer,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ EXPECT_CALL(delegate_ptr, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(2));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet);
+
+ ExpectPtrRecord("_printer._tcp.local", "hello._printer._tcp.local",
+ record_printer);
+
+ listener_privet.reset();
+ listener_printer.reset();
+
+ ASSERT_TRUE(test_factory_->IsListeningForTests());
+
+ EXPECT_CALL(delegate_ptr, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(2));
+
+ SendPacket(kSamplePacket2, sizeof(kSamplePacket2));
+
+ time_system_->RunPendingTasks();
+
+ // Test to make sure mdns listener is not active with no listeners present.
+ listener_ptr.reset();
+
+ ASSERT_FALSE(test_factory_->IsListeningForTests());
+}
+
+TEST_F(MDnsTest, PassiveListenersCleanup) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+
+ RecordParsedCopyContainer record_privet;
+ RecordParsedCopyContainer record_privet2;
+
+ scoped_ptr<MDnsListener> listener_privet = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(test_factory_->IsListeningForTests());
+
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet);
+
+ base::Time expiration = time_system_->Now() + base::TimeDelta::FromSeconds(
+ record_privet->ttl());
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordRemoved, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet2,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ time_system_->SetNow(expiration);
+ time_system_->RunPendingTasks();
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet2);
+}
+
+TEST_F(MDnsTest, PassiveListenersNotifyExisting) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+ StrictMock<MockListenerDelegate> delegate_privet2;
+
+ RecordParsedCopyContainer record_privet;
+ RecordParsedCopyContainer record_privet2;
+
+ scoped_ptr<MDnsListener> listener_privet = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(test_factory_->IsListeningForTests());
+
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet);
+
+ scoped_ptr<MDnsListener> listener_privet2 = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, true,
+ &delegate_privet2);
+
+ EXPECT_CALL(delegate_privet2, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet2,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet2);
+}
+
+TEST_F(MDnsTest, MalformedPacket) {
+ StrictMock<MockListenerDelegate> delegate_printer;
+
+ RecordParsedCopyContainer record_printer;
+
+ scoped_ptr<MDnsListener> listener_printer = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_printer._tcp.local", false, false,
+ &delegate_printer);
+
+ ASSERT_TRUE(test_factory_->IsListeningForTests());
+
+ // First, send unsalvagable packet to ensure we can deal with it.
+ SendPacket(kCorruptedPacketUnsalvagable,
+ sizeof(kCorruptedPacketUnsalvagable));
+
+ // Regression test: send a packet where the question cannot be read.
+ SendPacket(kCorruptedPacketBadQuestion,
+ sizeof(kCorruptedPacketBadQuestion));
+
+ // Then send salvagable packet to ensure we can extract useful records.
+ SendPacket(kCorruptedPacketSalvagable,
+ sizeof(kCorruptedPacketSalvagable));
+
+ EXPECT_CALL(delegate_printer, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_printer,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_printer._tcp.local", "hello._printer._tcp.local",
+ record_printer);
+}
+
+TEST_F(MDnsTest, QueryCache) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+
+ RecordParsedCopyContainer record_privet;
+ std::vector<const RecordParsed*> records_from_cache;
+
+ scoped_ptr<MDnsListener> listener_privet = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(test_factory_->IsListeningForTests());
+
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet);
+
+ listener_privet->QueryCache(&records_from_cache);
+
+ EXPECT_EQ(1u, records_from_cache.size());
+ EXPECT_EQ("_privet._tcp.local", records_from_cache.front()->name());
+ EXPECT_EQ(dns_protocol::kTypePTR, records_from_cache.front()->type());
+ EXPECT_EQ(dns_protocol::kClassIN, records_from_cache.front()->klass());
+
+ const PtrRecordRdata* ptr_rdata =
+ records_from_cache.front()->rdata<PtrRecordRdata>();
+
+ EXPECT_TRUE(ptr_rdata != NULL);
+
+ EXPECT_EQ("hello._privet._tcp.local",
+ ptr_rdata->ptrdomain());
+}
+
+TEST_F(MDnsTest, Query) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+ scoped_ptr<MDnsListener> listener_privet = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(listener_privet->SendQuery(false));
+
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ // Active listeners should send queries when created.
+
+ scoped_ptr<MDnsListener> listener_privet2 = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", true /*active*/,
+ false /*existing*/, &delegate_privet);
+
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+}
+
+TEST_F(MDnsTest, TransactionNoCache) {
+ scoped_ptr<MDnsTransaction> transaction_privet =
+ test_factory_->CreateTransaction(
+ dns_protocol::kTypePTR, "_privet._tcp.local",
+ base::Bind(&MDnsTest::MockableRecordCallback,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(test_factory_->IsListeningForTests());
+
+ RecordParsedCopyContainer record_privet;
+
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionSuccess, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(&record_privet,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet);
+
+ EXPECT_FALSE(test_factory_->IsListeningForTests());
+}
+
+TEST_F(MDnsTest, TransactionWithCache) {
+ // Listener to force the factory to listen
+ StrictMock<MockListenerDelegate> delegate_irrelevant;
+ scoped_ptr<MDnsListener> listener_irrelevant = test_factory_->CreateListener(
+ dns_protocol::kTypeA, "codereview.chromium.local", false, false,
+ &delegate_irrelevant);
+
+ EXPECT_TRUE(test_factory_->IsListeningForTests());
+
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+
+ time_system_->RunPendingTasks();
+
+ RecordParsedCopyContainer record_privet;
+
+ EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionSuccess, _))
+ .WillOnce(Invoke(&record_privet,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ scoped_ptr<MDnsTransaction> transaction_privet =
+ test_factory_->CreateTransaction(
+ dns_protocol::kTypePTR, "_privet._tcp.local",
+ base::Bind(&MDnsTest::MockableRecordCallback,
+ base::Unretained(this)));
+
+ // "Expect no packet"?
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet);
+}
+
+TEST_F(MDnsTest, AdditionalRecords) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+
+ RecordParsedCopyContainer record_privet;
+
+ scoped_ptr<MDnsListener> listener_privet = test_factory_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(test_factory_->IsListeningForTests());
+
+ // Send the same packet twice to ensure no records are double-counted.
+
+ SendPacket(kSamplePacketAdditionalOnly, sizeof(kSamplePacket1));
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet);
+}
+
+TEST_F(MDnsTest, TransactionTimeout) {
+ scoped_ptr<MDnsTransaction> transaction_privet =
+ test_factory_->CreateTransaction(
+ dns_protocol::kTypePTR, "_privet._tcp.local",
+ base::Bind(&MDnsTest::MockableRecordCallback,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(test_factory_->IsListeningForTests());
+
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionTimeout, NULL))
+ .Times(Exactly(1));
+
+ time_system_->SetNow(time_system_->Now() + base::TimeDelta::FromMinutes(5));
+
+ time_system_->RunPendingTasks();
+
+ EXPECT_FALSE(test_factory_->IsListeningForTests());
+}
+
+TEST_F(MDnsTest, TransactionReentrantDelete) {
+ transaction_ = test_factory_->CreateTransaction(
+ dns_protocol::kTypePTR, "_privet._tcp.local",
+ base::Bind(&MDnsTest::MockableRecordCallback,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(test_factory_->IsListeningForTests());
+
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ EXPECT_CALL(*this, MockableRecordCallback(kMDnsTransactionTimeout, NULL))
+ .Times(Exactly(1))
+ .WillOnce(InvokeWithoutArgs(this, &MDnsTest::DeleteTransaction));
+
+ time_system_->SetNow(time_system_->Now() + base::TimeDelta::FromMinutes(5));
+
+ time_system_->RunPendingTasks();
+
+ EXPECT_EQ(NULL, transaction_.get());
+
+ EXPECT_FALSE(test_factory_->IsListeningForTests());
+}
+
+} // namespace
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698