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 |