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

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: Uploading to check comments against diff (next upload will have files renamed) 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..3beae81a1f205fbf4d58c64f5707eb7d80c6292c
--- /dev/null
+++ b/net/dns/mdns_listener_unittest.cc
@@ -0,0 +1,872 @@
+// 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::Return;
+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,
+};
+
+class MockMDnsConnectionFactory;
+
+class MockMDnsConnection : public MDnsConnection {
+ public:
+ MockMDnsConnection(MDnsConnection::Delegate* delegate,
+ MockMDnsConnectionFactory* factory)
+ : delegate_(delegate), factory_(factory) {
+ }
+
+ virtual ~MockMDnsConnection() {
+ }
+
+ virtual bool Init() OVERRIDE {
+ return true;
+ }
+
+ virtual bool Send(IOBuffer* buffer, unsigned size) OVERRIDE;
+
+ void SimulateRecieve(const char* packet, int size);
+
+ private:
+ DnsResponse response;
+ MDnsConnection::Delegate* delegate_;
+ MockMDnsConnectionFactory* factory_;
+};
+
+class MockMDnsConnectionFactory : public MDnsConnectionFactory {
+ public:
+ MockMDnsConnectionFactory() {}
+ virtual ~MockMDnsConnectionFactory() {}
+ virtual scoped_ptr<MDnsConnection> CreateConnection(
+ MDnsConnection::Delegate* delegate,
+ base::TaskRunner* task_runner) OVERRIDE;
+
+ MOCK_METHOD1(OnSend, void(std::string));
+
+ MockMDnsConnection* latest_connection() { return latest_connection_; }
+
+ private:
+ MockMDnsConnection* latest_connection_;
+};
+
+class MockMDnsConnectionDelegate : public MDnsConnection::Delegate {
+ public:
+ virtual void HandlePacket(DnsResponse* response, int size) {
+ OnHandlePacket(std::string(response->io_buffer()->data(), size));
+ }
+
+ MOCK_METHOD1(OnHandlePacket, void(std::string));
+};
+
+bool MockMDnsConnection::Send(IOBuffer* buffer, unsigned size) {
+ factory_->OnSend(std::string(buffer->data(), size));
+ return true;
+}
+
+void MockMDnsConnection::SimulateRecieve(const char* packet, int size) {
+ memcpy(response.io_buffer()->data(), packet, size);
+ delegate_->HandlePacket(&response, size);
+}
+
+scoped_ptr<MDnsConnection> MockMDnsConnectionFactory::CreateConnection(
+ MDnsConnection::Delegate* delegate,
+ base::TaskRunner* task_runner) {
+ latest_connection_ = new MockMDnsConnection(delegate, this);
+
+ return scoped_ptr<MDnsConnection>(latest_connection_);
+}
+
+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();
+ void DeleteTransaction();
+ void DeleteBothListeners();
+
+ 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<MDnsClientImpl> test_client_;
+ IPEndPoint mdns_ipv4_endpoint_;
+ scoped_refptr<FakeTimeSystem> time_system_;
+ StrictMock<MockMDnsConnectionFactory> connection_factory_;
+
+ // Transactions and listeners that can be deleted by class methods for
+ // reentrancy tests.
+ scoped_ptr<MDnsTransaction> transaction_;
+ scoped_ptr<MDnsListener> listener1_;
+ scoped_ptr<MDnsListener> listener2_;
+};
+
+class MockListenerDelegate : public MDnsListener::Delegate {
+ public:
+ MOCK_METHOD2(OnRecordUpdate,
+ void(MDnsUpdateType, const RecordParsed*));
+ MOCK_METHOD2(OnNsecRecord, void(const std::string&, unsigned));
+};
+
+MDnsTest::MDnsTest()
+ : message_loop_(base::MessageLoop::current()) {
+ time_system_ = new FakeTimeSystem();
+ time_system_->SetNow(base::Time::FromDoubleT(1234.0));
+ test_client_.reset(new MDnsClientImpl(time_system_, time_system_,
+ &connection_factory_));
+}
+
+MDnsTest::~MDnsTest() {
+}
+
+void MDnsTest::SendPacket(const char* packet, unsigned size) {
+ connection_factory_.latest_connection()->SimulateRecieve(packet, size);
+}
+
+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) {
+ EXPECT_CALL(connection_factory_, OnSend(std::string(packet, size)));
+}
+
+void MDnsTest::DeleteTransaction() {
+ transaction_.reset();
+}
+
+void MDnsTest::DeleteBothListeners() {
+ listener1_.reset();
+ listener2_.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_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+ scoped_ptr<MDnsListener> listener_printer = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_printer._tcp.local", false, false,
+ &delegate_printer);
+ scoped_ptr<MDnsListener> listener_ptr = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "", false, false, &delegate_ptr);
+
+ ASSERT_TRUE(test_client_->IsListeningForTests());
+
+ // Send the same packet twice to ensure no records are double-counted.
+
+ 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));
+
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+
+ 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_client_->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();
+
+ time_system_->RunPendingTasks();
+
+ ASSERT_FALSE(test_client_->IsListeningForTests());
+}
+
+TEST_F(MDnsTest, PassiveListenersCleanup) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+
+ RecordParsedCopyContainer record_privet;
+ RecordParsedCopyContainer record_privet2;
+
+ scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(test_client_->IsListeningForTests());
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .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);
+
+ 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_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(test_client_->IsListeningForTests());
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .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_CALL(delegate_privet2, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet2,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ scoped_ptr<MDnsListener> listener_privet2 = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, true,
+ &delegate_privet2);
+
+ 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_client_->CreateListener(
+ dns_protocol::kTypePTR, "_printer._tcp.local", false, false,
+ &delegate_printer);
+
+ ASSERT_TRUE(test_client_->IsListeningForTests());
+
+ EXPECT_CALL(delegate_printer, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_printer,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ // 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));
+
+ 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_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(test_client_->IsListeningForTests());
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .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);
+
+ 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;
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ scoped_ptr<MDnsListener> listener_privet = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(listener_privet->SendQuery(false));
+
+ // Active listeners should send queries when created.
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ scoped_ptr<MDnsListener> listener_privet2 = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", true /*active*/,
+ false /*existing*/, &delegate_privet);
+}
+
+TEST_F(MDnsTest, TransactionNoCache) {
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ scoped_ptr<MDnsTransaction> transaction_privet =
+ test_client_->CreateTransaction(
+ dns_protocol::kTypePTR, "_privet._tcp.local",
+ base::Bind(&MDnsTest::MockableRecordCallback,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(test_client_->IsListeningForTests());
+
+ RecordParsedCopyContainer record_privet;
+
+ 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_client_->IsListeningForTests());
+}
+
+TEST_F(MDnsTest, TransactionWithCache) {
+ // Listener to force the client to listen
+ StrictMock<MockListenerDelegate> delegate_irrelevant;
+ scoped_ptr<MDnsListener> listener_irrelevant = test_client_->CreateListener(
+ dns_protocol::kTypeA, "codereview.chromium.local", false, false,
+ &delegate_irrelevant);
+
+ EXPECT_TRUE(test_client_->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_client_->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_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ ASSERT_TRUE(test_client_->IsListeningForTests());
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(Invoke(
+ &record_privet,
+ &RecordParsedCopyContainer::SaveWithDummyArg));
+
+ SendPacket(kSamplePacketAdditionalOnly, sizeof(kSamplePacket1));
+
+ time_system_->RunPendingTasks();
+
+ ExpectPtrRecord("_privet._tcp.local", "hello._privet._tcp.local",
+ record_privet);
+}
+
+TEST_F(MDnsTest, TransactionTimeout) {
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ scoped_ptr<MDnsTransaction> transaction_privet =
+ test_client_->CreateTransaction(
+ dns_protocol::kTypePTR, "_privet._tcp.local",
+ base::Bind(&MDnsTest::MockableRecordCallback,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(test_client_->IsListeningForTests());
+
+ 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_client_->IsListeningForTests());
+}
+
+TEST_F(MDnsTest, TransactionReentrantDelete) {
+ ExpectPacket(kQueryPacketPrivet, sizeof(kQueryPacketPrivet));
+
+ transaction_ = test_client_->CreateTransaction(
+ dns_protocol::kTypePTR, "_privet._tcp.local",
+ base::Bind(&MDnsTest::MockableRecordCallback,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(test_client_->IsListeningForTests());
+
+ 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_client_->IsListeningForTests());
+}
+
+// In order to reliably test reentrant listener deletes, we create two listeners
+// and have each of them delete both, so we're guaranteed to try and deliver a
+// callback to at least one deleted listener.
+
+TEST_F(MDnsTest, ListenerReentrantDelete) {
+ StrictMock<MockListenerDelegate> delegate_privet;
+
+ listener1_ = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ listener2_ = test_client_->CreateListener(
+ dns_protocol::kTypePTR, "_privet._tcp.local", false, false,
+ &delegate_privet);
+
+ EXPECT_CALL(delegate_privet, OnRecordUpdate(kMDnsRecordAdded, _))
+ .Times(Exactly(1))
+ .WillOnce(InvokeWithoutArgs(this, &MDnsTest::DeleteBothListeners));
+
+ EXPECT_TRUE(test_client_->IsListeningForTests());
+
+ SendPacket(kSamplePacket1, sizeof(kSamplePacket1));
+
+ time_system_->RunPendingTasks();
+
+ EXPECT_EQ(NULL, listener1_.get());
+ EXPECT_EQ(NULL, listener2_.get());
+
+ EXPECT_FALSE(test_client_->IsListeningForTests());
+}
+
+// Connection tests. These tests are disabled because, due to their dependence
+// on multicast networking, they are falky.
+
+class MDnsConnectionTest : public ::testing::Test {
+ public:
+ MDnsConnectionTest() : fake_time_system_(new FakeTimeSystem),
+ connection_(&delegate_, fake_time_system_) {
+ EXPECT_TRUE(ParseIPLiteralToNumber("224.0.0.251", &multicast_address4_));
+ // TODO(noamsml): Figure out why client socket fails to connect to ipv6
+ // multicast address.
+ EXPECT_TRUE(ParseIPLiteralToNumber("::1", &multicast_address6_));
+ connection_.Init();
+ }
+ protected:
+ IPAddressNumber multicast_address4_;
+ IPAddressNumber multicast_address6_;
+ StrictMock<MockMDnsConnectionDelegate> delegate_;
+ scoped_refptr<FakeTimeSystem> fake_time_system_;
+ MDnsConnectionImpl connection_;
+ TestCompletionCallback callback_;
+};
+
+TEST_F(MDnsConnectionTest, DISABLED_Recieve4) {
+ scoped_refptr<IOBuffer> buf(new IOBuffer(sizeof(kSamplePacket1)));
+ memcpy(buf->data(), kSamplePacket1, sizeof(kSamplePacket1));
+ UDPSocket socket(DatagramSocket::DEFAULT_BIND,
+ RandIntCallback(),
+ NULL, NetLog::Source());
+ EXPECT_CALL(delegate_, OnHandlePacket(std::string(kSamplePacket1,
+ sizeof(kSamplePacket1))));
+
+ EXPECT_EQ(OK, socket.Connect(IPEndPoint(multicast_address4_, 5353)));
+ int rv = socket.Write(buf, sizeof(kSamplePacket1), callback_.callback());
+ if (rv == ERR_IO_PENDING) {
+ rv = callback_.GetResult(rv);
+ }
+ EXPECT_GT(rv, OK);
+ base::MessageLoop::current()->RunUntilIdle(); // Socket uses message loop.
+ fake_time_system_->RunPendingTasks();
+}
+
+TEST_F(MDnsConnectionTest, DISABLED_Recieve6) {
+ scoped_refptr<IOBuffer> buf(new IOBuffer(sizeof(kSamplePacket2)));
+ memcpy(buf->data(), kSamplePacket2, sizeof(kSamplePacket2));
+ UDPSocket socket(DatagramSocket::DEFAULT_BIND,
+ RandIntCallback(),
+ NULL, NetLog::Source());
+ EXPECT_CALL(delegate_, OnHandlePacket(std::string(kSamplePacket2,
+ sizeof(kSamplePacket2))));
+
+ EXPECT_EQ(OK, socket.Connect(IPEndPoint(multicast_address6_, 5353)));
+ int rv = socket.Write(buf, sizeof(kSamplePacket2), callback_.callback());
+ if (rv == ERR_IO_PENDING) {
+ rv = callback_.GetResult(rv);
+ }
+ EXPECT_GT(rv, OK);
+ base::MessageLoop::current()->RunUntilIdle(); // Socket uses message loop.
+ fake_time_system_->RunPendingTasks();
+}
+
+} // namespace
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698