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

Unified Diff: chrome/browser/local_discovery/service_discovery_client_unittest.cc

Issue 16272006: In-browser DNS-based service discovery system (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mdns_implementation
Patch Set: Created 7 years, 6 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: chrome/browser/local_discovery/service_discovery_client_unittest.cc
diff --git a/chrome/browser/local_discovery/service_discovery_client_unittest.cc b/chrome/browser/local_discovery/service_discovery_client_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a0c5e3f9f63298737f4d582689cfa1655b6bc821
--- /dev/null
+++ b/chrome/browser/local_discovery/service_discovery_client_unittest.cc
@@ -0,0 +1,545 @@
+// 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/weak_ptr.h"
+#include "chrome/browser/local_discovery/service_discovery_client_impl.h"
+#include "net/base/net_errors.h"
+#include "net/dns/dns_protocol.h"
+#include "net/dns/mdns_client_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::StrictMock;
+using ::testing::NiceMock;
+using ::testing::Mock;
+using ::testing::SaveArg;
+using ::testing::SetArgPointee;
+using ::testing::Return;
+using ::testing::Exactly;
+
+namespace local_discovery {
+
+namespace {
+
+const char kSamplePacketPTR[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x01, // 1 RR (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ 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, 0x00, // TTL (4 bytes) is 1 second.
+ 0x00, 0x01,
+ 0x00, 0x08, // RDLENGTH is 8 bytes.
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0xc0, 0x0c
+};
+
+const char kSamplePacketSRV[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x01, // 1 RR (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x21, // TYPE is SRV.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x00, // TTL (4 bytes) is 1 second.
+ 0x00, 0x01,
+ 0x00, 0x15, // RDLENGTH is 21 bytes.
+ 0x00, 0x00,
+ 0x00, 0x00,
+ 0x22, 0xb8, // port 8888
+ 0x07, 'm', 'y', 'h', 'e', 'l', 'l', 'o',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+};
+
+const char kSamplePacketTXT[] = {
+ // Header
+ 0x00, 0x00, // ID is zeroed out
+ 0x81, 0x80, // Standard query response, RA, no error
+ 0x00, 0x00, // No questions (for simplicity)
+ 0x00, 0x01, // 1 RR (answers)
+ 0x00, 0x00, // 0 authority RRs
+ 0x00, 0x00, // 0 additional RRs
+
+ 0x05, 'h', 'e', 'l', 'l', 'o',
+ 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
+ 0x04, '_', 't', 'c', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ 0x00, 0x10, // TYPE is PTR.
+ 0x00, 0x01, // CLASS is IN.
+ 0x00, 0x00, // TTL (4 bytes) is 20 hours, 47 minutes, 48 seconds.
+ 0x00, 0x01,
+ 0x00, 0x06, // RDLENGTH is 21 bytes.
+ 0x05, 'h', 'e', 'l', 'l', 'o'
+};
+
+class MockSocketFactory : public net::DatagramServerSocket {
+ public:
+ int Listen(const net::IPEndPoint& address) {
+ return ListenInternal(address.ToString());
+ }
+
+ MOCK_METHOD1(ListenInternal, int(const std::string& address));
+
+ MOCK_METHOD4(RecvFrom, int(net::IOBuffer* buffer, int size,
+ net::IPEndPoint* address,
+ const net::CompletionCallback& callback));
+
+ int SendTo(net::IOBuffer* buf, int buf_len, const net::IPEndPoint& address,
+ const net::CompletionCallback& callback) {
+ return SendToInternal(std::string(buf->data(), buf_len), address.ToString(),
+ callback);
+ }
+
+ MOCK_METHOD3(SendToInternal, int(const std::string& packet,
+ const std::string address,
+ const net::CompletionCallback& callback));
+
+ MOCK_METHOD1(SetReceiveBufferSize, bool(int32 size));
+ MOCK_METHOD1(SetSendBufferSize, bool(int32 size));
+
+ MOCK_METHOD0(Close, void());
+
+ MOCK_CONST_METHOD1(GetPeerAddress, int(net::IPEndPoint* address));
+ MOCK_CONST_METHOD1(GetLocalAddress, int(net::IPEndPoint* address));
+ MOCK_CONST_METHOD0(NetLog, const net::BoundNetLog&());
+
+ MOCK_METHOD0(AllowAddressReuse, void());
+ MOCK_METHOD0(AllowBroadcast, void());
+
+ int JoinGroup(const net::IPAddressNumber& group_address) const {
+ return JoinGroupInternal(net::IPAddressToString(group_address));
+ }
+
+ MOCK_CONST_METHOD1(JoinGroupInternal, int(const std::string& group));
+
+ int LeaveGroup(const net::IPAddressNumber& group_address) const {
+ return LeaveGroupInternal(net::IPAddressToString(group_address));
+ }
+
+ MOCK_CONST_METHOD1(LeaveGroupInternal, int(const std::string& group));
+
+ MOCK_METHOD1(SetMulticastTimeToLive, int(int ttl));
+
+ MOCK_METHOD1(SetMulticastLoopbackMode, int(bool loopback));
+};
+
+class MockSocketFactoryFactory
+ : public net::MDnsConnection::SocketFactory {
+ public:
+ MockSocketFactoryFactory() {
+ }
+
+ virtual ~MockSocketFactoryFactory() {
+ }
+
+ virtual scoped_ptr<net::DatagramServerSocket> CreateSocket() OVERRIDE {
+ scoped_ptr<MockSocketFactory> new_socket(
+ new NiceMock<MockSocketFactory>);
+
+ ON_CALL(*new_socket, SendToInternal(_, _, _))
+ .WillByDefault(Invoke(
+ this,
+ &MockSocketFactoryFactory::SendToInternal));
+
+ ON_CALL(*new_socket, RecvFrom(_, _, _, _))
+ .WillByDefault(Invoke(
+ this,
+ &MockSocketFactoryFactory::RecvFromInternal));
+
+ return new_socket.PassAs<net::DatagramServerSocket>();
+ }
+
+ int SendToInternal(const std::string& packet, const std::string& address,
+ const net::CompletionCallback& callback) {
+ OnSendTo(packet);
+ return packet.size();
+ }
+
+ // The latest recieve callback is always saved, since the net::MDnsConnection
+ // does not care which socket a packet is received on.
+ int RecvFromInternal(net::IOBuffer* buffer, int size,
+ net::IPEndPoint* address,
+ const net::CompletionCallback& callback) {
+ recv_buffer_ = buffer;
+ recv_buffer_size_ = size;
+ recv_callback_ = callback;
+ return net::ERR_IO_PENDING;
+ }
+
+ void SimulateReceive(const char* packet, int size) {
+ DCHECK(recv_buffer_size_ >= size);
+ DCHECK(recv_buffer_.get());
+ DCHECK(!recv_callback_.is_null());
+
+ memcpy(recv_buffer_->data(), packet, size);
+ net::CompletionCallback recv_callback = recv_callback_;
+ recv_callback_.Reset();
+ recv_callback.Run(size);
+ }
+
+ MOCK_METHOD1(OnSendTo, void(const std::string&));
+
+ private:
+ scoped_refptr<net::IOBuffer> recv_buffer_;
+ int recv_buffer_size_;
+ net::CompletionCallback recv_callback_;
+};
+
+class MockServiceTypeWatcherDelegate : public ServiceTypeWatcher::Delegate {
+ public:
+ MockServiceTypeWatcherDelegate() {}
+ virtual ~MockServiceTypeWatcherDelegate() {}
+
+ MOCK_METHOD2(OnServiceStatusChanged, void(bool, const std::string&));
+};
+
+class ServiceDiscoveryTest : public ::testing::Test {
+ public:
+ ServiceDiscoveryTest() : socket_factory_(new MockSocketFactoryFactory),
+ mdns_client_(
+ scoped_ptr<net::MDnsConnection::SocketFactory>(
+ socket_factory_)) {
+ net::MDnsClient::SetInstance(&mdns_client_);
+ }
+
+ virtual ~ServiceDiscoveryTest() {
+ net::MDnsClient::SetInstance(NULL);
+ }
+
+ protected:
+ void RunFor(base::TimeDelta time_period) {
+ base::CancelableCallback<void()> callback(base::Bind(
+ &ServiceDiscoveryTest::Stop, base::Unretained(this)));
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, callback.callback(), time_period);
+
+ base::MessageLoop::current()->Run();
+ callback.Cancel();
+ }
+
+ void Stop() {
+ base::MessageLoop::current()->Quit();
+ }
+
+ MockSocketFactoryFactory* socket_factory_;
+ net::MDnsClientImpl mdns_client_;
+ ServiceDiscoveryClientImpl service_discovery_client_;
+ base::MessageLoop loop_;
+};
+
+TEST_F(ServiceDiscoveryTest, AddRemoveService) {
+ scoped_ptr<ServiceTypeWatcher> watcher;
+ StrictMock<MockServiceTypeWatcherDelegate> delegate;
+
+ watcher = service_discovery_client_.CreateServiceTypeWatcher(
+ "_privet._tcp.local", true, false, &delegate);
+
+ EXPECT_CALL(*socket_factory_, OnSendTo(_))
+ .Times(2);
+
+ watcher->Start();
+
+ EXPECT_CALL(delegate,
+ OnServiceStatusChanged(true, "hello._privet._tcp.local"))
+ .Times(Exactly(1));
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketPTR, sizeof(kSamplePacketPTR));
+
+ EXPECT_CALL(delegate,
+ OnServiceStatusChanged(false, "hello._privet._tcp.local"))
+ .Times(Exactly(1));
+
+ RunFor(base::TimeDelta::FromSeconds(2));
+};
+
+TEST_F(ServiceDiscoveryTest, DiscoverNewServices) {
+ scoped_ptr<ServiceTypeWatcher> watcher;
+ StrictMock<MockServiceTypeWatcherDelegate> delegate;
+
+ watcher = service_discovery_client_.CreateServiceTypeWatcher(
+ "_privet._tcp.local", false, false, &delegate);
+
+ watcher->Start();
+
+ EXPECT_CALL(*socket_factory_, OnSendTo(_))
+ .Times(2);
+
+ watcher->DiscoverNewServices();
+};
+
+TEST_F(ServiceDiscoveryTest, GetAvailableServices) {
+ NiceMock<MockServiceTypeWatcherDelegate> delegate;
+
+ std::vector<std::string> data_expected;
+ std::vector<std::string> data;
+
+ data_expected.push_back("hello._privet._tcp.local");
+
+ scoped_ptr<ServiceTypeWatcher> watcher =
+ service_discovery_client_.CreateServiceTypeWatcher(
+ "_privet._tcp.local", false, false, &delegate);
+
+ watcher->Start();
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketPTR, sizeof(kSamplePacketPTR));
+
+ watcher->GetAvailableServices(&data);
+
+ EXPECT_EQ(data, data_expected);
+};
+
+class MockServiceReaderDelegate
+ : public ServiceReader::Delegate {
+ public:
+ MockServiceReaderDelegate() {}
+ virtual ~MockServiceReaderDelegate() {}
+
+ virtual void OnAddressChanged(
+ const std::string& service_name,
+ const net::HostPortPair& address) OVERRIDE {
+ OnAddressChangedInternal(service_name, address.ToString());
+ }
+
+ MOCK_METHOD2(OnAddressChangedInternal, void(const std::string& service_name,
+ const std::string& address));
+ MOCK_METHOD2(OnMetadataChanged,
+ void(const std::string& service_name,
+ const std::vector<std::string>& metadata));
+};
+
+class ServiceReaderTest : public ServiceDiscoveryTest {
+ public:
+ ServiceReaderTest() {
+ metadata_expected_.push_back("hello");
+ address_expected_ = net::HostPortPair("myhello.local", 8888);
+ }
+
+ ~ServiceReaderTest() {
+ }
+
+ void SetUp() {
+ reader_ = service_discovery_client_.CreateServiceReader(
+ "hello._privet._tcp.local", &delegate_);
+
+ EXPECT_CALL(*socket_factory_, OnSendTo(_))
+ .Times(4); // Two queries.
+
+ reader_->Start();
+
+ Mock::VerifyAndClear(&socket_factory_);
+ }
+
+ void OnReceiveAddress(ServiceReader::RequestStatus status,
+ const net::HostPortPair& address) {
+ OnReceiveAddressInternal(status, address.ToString());
+ }
+
+ MOCK_METHOD2(OnReceiveAddressInternal, void(ServiceReader::RequestStatus,
+ const std::string&));
+ MOCK_METHOD2(OnReceiveMetadata,
+ void(ServiceReader::RequestStatus,
+ const std::vector<std::string>&));
+ MOCK_METHOD1(OnReceiveTime, void(base::Time));
+
+ protected:
+ StrictMock<MockServiceReaderDelegate> delegate_;
+ scoped_ptr<ServiceReader> reader_;
+ net::HostPortPair address_expected_;
+ std::vector<std::string> metadata_expected_;
+ net::HostPortPair address_recieved_;
+ std::vector<std::string> metadata_recieved_;
+};
+
+TEST_F(ServiceReaderTest, ServiceWatcherUpdateDelegate) {
+ EXPECT_CALL(delegate_,
+ OnMetadataChanged("hello._privet._tcp.local",
+ metadata_expected_));
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketTXT, sizeof(kSamplePacketTXT));
+
+ Mock::VerifyAndClear(&delegate_);
+
+ EXPECT_CALL(delegate_,
+ OnAddressChangedInternal("hello._privet._tcp.local",
+ address_expected_.ToString()));
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketSRV, sizeof(kSamplePacketSRV));
+
+ Mock::VerifyAndClear(&delegate_);
+
+ EXPECT_TRUE(reader_->GetAddress(&address_recieved_));
+
+ EXPECT_TRUE(address_expected_.Equals(address_recieved_));
+
+ EXPECT_TRUE(reader_->GetMetadata(&metadata_recieved_));
+
+ EXPECT_EQ(metadata_expected_, metadata_recieved_);
+};
+
+TEST_F(ServiceReaderTest, NoData) {
+ Mock::VerifyAndClear(&socket_factory_);
+
+ EXPECT_FALSE(reader_->GetAddress(&address_recieved_));
+ EXPECT_FALSE(reader_->GetMetadata(&metadata_recieved_));
+}
+
+TEST_F(ServiceReaderTest, ServiceWatcherReadWithCallback) {
+ EXPECT_CALL(delegate_,
+ OnMetadataChanged("hello._privet._tcp.local",
+ metadata_expected_));
+ EXPECT_CALL(delegate_,
+ OnAddressChangedInternal("hello._privet._tcp.local",
+ address_expected_.ToString()));
+
+ // Test that double calls to ReadAddress/Metadata get double responses.
+ reader_->ReadAddress(base::Bind(&ServiceReaderTest::OnReceiveAddress,
+ base::Unretained(this)), false);
+ reader_->ReadAddress(base::Bind(&ServiceReaderTest::OnReceiveAddress,
+ base::Unretained(this)), false);
+ reader_->ReadMetadata(base::Bind(&ServiceReaderTest::OnReceiveMetadata,
+ base::Unretained(this)), false);
+ reader_->ReadMetadata(base::Bind(&ServiceReaderTest::OnReceiveMetadata,
+ base::Unretained(this)), false);
+
+ Mock::VerifyAndClear(socket_factory_);
+
+ EXPECT_CALL(*this, OnReceiveAddressInternal(ServiceReader::STATUS_SUCCESS,
+ address_expected_.ToString()))
+ .Times(Exactly(2));
+
+ EXPECT_CALL(*this, OnReceiveMetadata(ServiceReader::STATUS_SUCCESS,
+ metadata_expected_))
+ .Times(Exactly(2));
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketSRV, sizeof(kSamplePacketSRV));
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketTXT, sizeof(kSamplePacketTXT));
+
+ Mock::VerifyAndClear(this);
+
+ // Test that a successful read doesn't clobber read-ability, and reads from
+ // from cache.
+ EXPECT_CALL(*this, OnReceiveAddressInternal(ServiceReader::STATUS_SUCCESS,
+ address_expected_.ToString()))
+ .Times(Exactly(1));
+
+ EXPECT_CALL(*this, OnReceiveMetadata(ServiceReader::STATUS_SUCCESS,
+ metadata_expected_))
+ .Times(Exactly(1));
+
+ reader_->ReadAddress(base::Bind(&ServiceReaderTest::OnReceiveAddress,
+ base::Unretained(this)), false);
+ reader_->ReadMetadata(base::Bind(&ServiceReaderTest::OnReceiveMetadata,
+ base::Unretained(this)), false);
+};
+
+TEST_F(ServiceReaderTest, ServiceWatcherReadWithTimeout) {
+ reader_->ReadAddress(base::Bind(&ServiceReaderTest::OnReceiveAddress,
+ base::Unretained(this)), false);
+ reader_->ReadMetadata(base::Bind(&ServiceReaderTest::OnReceiveMetadata,
+ base::Unretained(this)), false);
+
+ EXPECT_CALL(*this, OnReceiveAddressInternal(ServiceReader::STATUS_TIMEOUT, _))
+ .Times(Exactly(1));
+
+ EXPECT_CALL(*this, OnReceiveMetadata(ServiceReader::STATUS_TIMEOUT, _))
+ .Times(Exactly(1));
+
+ RunFor(base::TimeDelta::FromSeconds(4));
+};
+
+TEST_F(ServiceReaderTest, ServiceWatcherExpiration) {
+ EXPECT_CALL(delegate_,
+ OnMetadataChanged("hello._privet._tcp.local",
+ metadata_expected_));
+ EXPECT_CALL(delegate_,
+ OnAddressChangedInternal("hello._privet._tcp.local",
+ address_expected_.ToString()));
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketSRV, sizeof(kSamplePacketSRV));
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketTXT, sizeof(kSamplePacketTXT));
+
+ EXPECT_TRUE(reader_->GetAddress(&address_recieved_));
+ EXPECT_TRUE(reader_->GetMetadata(&metadata_recieved_));
+
+ EXPECT_CALL(delegate_,
+ OnMetadataChanged("hello._privet._tcp.local",
+ std::vector<std::string>() ));
+ EXPECT_CALL(delegate_,
+ OnAddressChangedInternal("hello._privet._tcp.local",
+ net::HostPortPair().ToString()));
+
+ RunFor(base::TimeDelta::FromSeconds(2));
+
+ EXPECT_FALSE(reader_->GetAddress(&address_recieved_));
+ EXPECT_FALSE(reader_->GetMetadata(&metadata_recieved_));
+};
+
+TEST_F(ServiceReaderTest, Names) {
+ EXPECT_EQ("hello", reader_->GetHumanReadableName());
+ EXPECT_EQ("_privet._tcp.local", reader_->GetType());
+
+ reader_ = service_discovery_client_.CreateServiceReader(
+ "hello", &delegate_);
+ EXPECT_EQ("hello", reader_->GetHumanReadableName());
+ EXPECT_EQ("", reader_->GetType());
+}
+
+TEST_F(ServiceReaderTest, LastSeen) {
+ base::Time time_last_seen;
+ EXPECT_CALL(*this, OnReceiveTime(base::Time()));
+ reader_->ReadLastSeen(base::Bind(&ServiceReaderTest::OnReceiveTime,
+ base::Unretained(this)));
+ EXPECT_CALL(delegate_,
+ OnAddressChangedInternal("hello._privet._tcp.local",
+ address_expected_.ToString()));
+
+ socket_factory_->SimulateReceive(
+ kSamplePacketSRV, sizeof(kSamplePacketSRV));
+
+ EXPECT_CALL(*this, OnReceiveTime(_))
+ .WillOnce(SaveArg<0>(&time_last_seen));
+
+ reader_->ReadLastSeen(base::Bind(&ServiceReaderTest::OnReceiveTime,
+ base::Unretained(this)));
+
+ base::TimeDelta timediff = base::Time::Now() - time_last_seen;
+ if (timediff < base::TimeDelta()) timediff = -timediff;
+ EXPECT_TRUE(timediff < base::TimeDelta::FromSeconds(5));
+}
+
+} // namespace
+
+} // namespace local_discovery

Powered by Google App Engine
This is Rietveld 408576698