blob: af304e48fe0af64fbff062cc88ac0992e2328491 [file] [log] [blame]
// Copyright 2019 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 "discovery/dnssd/impl/publisher_impl.h"
#include <utility>
#include <vector>
#include "discovery/common/testing/mock_reporting_client.h"
#include "discovery/dnssd/testing/fake_network_interface_config.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "platform/test/fake_clock.h"
#include "platform/test/fake_task_runner.h"
namespace openscreen {
namespace discovery {
namespace {
using testing::_;
using testing::Return;
using testing::StrictMock;
class MockClient : public DnsSdPublisher::Client {
public:
MOCK_METHOD2(OnEndpointClaimed,
void(const DnsSdInstance&, const DnsSdInstanceEndpoint&));
};
class MockMdnsService : public MdnsService {
public:
void StartQuery(const DomainName& name,
DnsType dns_type,
DnsClass dns_class,
MdnsRecordChangedCallback* callback) override {
FAIL();
}
void StopQuery(const DomainName& name,
DnsType dns_type,
DnsClass dns_class,
MdnsRecordChangedCallback* callback) override {
FAIL();
}
void ReinitializeQueries(const DomainName& name) override { FAIL(); }
MOCK_METHOD3(StartProbe,
Error(MdnsDomainConfirmedProvider*, DomainName, IPAddress));
MOCK_METHOD2(UpdateRegisteredRecord,
Error(const MdnsRecord&, const MdnsRecord&));
MOCK_METHOD1(RegisterRecord, Error(const MdnsRecord& record));
MOCK_METHOD1(UnregisterRecord, Error(const MdnsRecord& record));
};
class PublisherImplTest : public testing::Test {
public:
PublisherImplTest()
: clock_(Clock::now()),
task_runner_(&clock_),
publisher_(&mock_service_,
&reporting_client_,
&task_runner_,
&network_config_) {}
MockMdnsService* mdns_service() { return &mock_service_; }
TaskRunner* task_runner() { return &task_runner_; }
PublisherImpl* publisher() { return &publisher_; }
// Calls PublisherImpl::OnDomainFound() through the public interface it
// implements.
void CallOnDomainFound(const DomainName& domain, const DomainName& domain2) {
static_cast<MdnsDomainConfirmedProvider&>(publisher_)
.OnDomainFound(domain, domain2);
}
protected:
FakeNetworkInterfaceConfig network_config_;
FakeClock clock_;
FakeTaskRunner task_runner_;
StrictMock<MockMdnsService> mock_service_;
StrictMock<MockReportingClient> reporting_client_;
PublisherImpl publisher_;
};
TEST_F(PublisherImplTest, TestRegistrationAndDegrestration) {
IPAddress address = IPAddress(192, 168, 0, 0);
network_config_.set_address_v4(address);
const DomainName domain{"instance", "_service", "_udp", "domain"};
const DomainName domain2{"instance2", "_service", "_udp", "domain"};
const DnsSdInstance instance("instance", "_service._udp", "domain", {}, 80);
const DnsSdInstance instance2("instance2", "_service._udp", "domain", {}, 80);
MockClient client;
EXPECT_CALL(*mdns_service(), StartProbe(publisher(), domain, _)).Times(1);
publisher()->Register(instance, &client);
testing::Mock::VerifyAndClearExpectations(mdns_service());
int seen = 0;
EXPECT_CALL(*mdns_service(), RegisterRecord(_))
.Times(4)
.WillRepeatedly([&seen, &address,
&domain2](const MdnsRecord& record) mutable -> Error {
if (record.dns_type() == DnsType::kA) {
const ARecordRdata& data = absl::get<ARecordRdata>(record.rdata());
if (data.ipv4_address() == address) {
seen++;
}
} else if (record.dns_type() == DnsType::kSRV) {
const SrvRecordRdata& data =
absl::get<SrvRecordRdata>(record.rdata());
if (data.port() == 80) {
seen++;
}
}
if (record.dns_type() != DnsType::kPTR) {
EXPECT_EQ(record.name(), domain2);
}
return Error::None();
});
EXPECT_CALL(client, OnEndpointClaimed(instance, _))
.WillOnce([instance2](const DnsSdInstance& requested,
const DnsSdInstanceEndpoint& claimed) {
EXPECT_EQ(instance2, claimed);
});
CallOnDomainFound(domain, domain2);
EXPECT_EQ(seen, 2);
testing::Mock::VerifyAndClearExpectations(mdns_service());
testing::Mock::VerifyAndClearExpectations(&client);
seen = 0;
EXPECT_CALL(*mdns_service(), UnregisterRecord(_))
.Times(4)
.WillRepeatedly([&seen,
&address](const MdnsRecord& record) mutable -> Error {
if (record.dns_type() == DnsType::kA) {
const ARecordRdata& data = absl::get<ARecordRdata>(record.rdata());
if (data.ipv4_address() == address) {
seen++;
}
} else if (record.dns_type() == DnsType::kSRV) {
const SrvRecordRdata& data =
absl::get<SrvRecordRdata>(record.rdata());
if (data.port() == 80) {
seen++;
}
}
return Error::None();
});
publisher()->DeregisterAll("_service._udp");
EXPECT_EQ(seen, 2);
}
TEST_F(PublisherImplTest, TestUpdate) {
IPAddress address = IPAddress(192, 168, 0, 0);
network_config_.set_address_v4(address);
DomainName domain{"instance", "_service", "_udp", "domain"};
DnsSdTxtRecord txt;
txt.SetFlag("id", true);
DnsSdInstance instance("instance", "_service._udp", "domain", std::move(txt),
80);
MockClient client;
// Update a non-existent instance
EXPECT_FALSE(publisher()->UpdateRegistration(instance).ok());
// Update an instance during the probing phase
EXPECT_CALL(*mdns_service(), StartProbe(publisher(), domain, _)).Times(1);
EXPECT_EQ(publisher()->Register(instance, &client), Error::None());
testing::Mock::VerifyAndClearExpectations(mdns_service());
IPAddress address2 = IPAddress(1, 2, 3, 4, 5, 6, 7, 8);
network_config_.set_address_v4(IPAddress{});
network_config_.set_address_v6(address2);
DnsSdTxtRecord txt2;
txt2.SetFlag("id2", true);
DnsSdInstance instance2("instance", "_service._udp", "domain",
std::move(txt2), 80);
EXPECT_EQ(publisher()->UpdateRegistration(instance2), Error::None());
bool seen_v6 = false;
EXPECT_CALL(*mdns_service(), RegisterRecord(_))
.Times(4)
.WillRepeatedly([&seen_v6](const MdnsRecord& record) mutable -> Error {
EXPECT_NE(record.dns_type(), DnsType::kA);
if (record.dns_type() == DnsType::kAAAA) {
seen_v6 = true;
}
return Error::None();
});
EXPECT_CALL(client, OnEndpointClaimed(instance2, _))
.WillOnce([instance2](const DnsSdInstance& requested,
const DnsSdInstanceEndpoint& claimed) {
EXPECT_EQ(instance2, claimed);
});
CallOnDomainFound(domain, domain);
EXPECT_TRUE(seen_v6);
testing::Mock::VerifyAndClearExpectations(mdns_service());
testing::Mock::VerifyAndClearExpectations(&client);
// Update an instance once it has been published.
network_config_.set_address_v4(address);
network_config_.set_address_v6(IPAddress{});
EXPECT_CALL(*mdns_service(), RegisterRecord(_))
.WillOnce([](const MdnsRecord& record) -> Error {
EXPECT_EQ(record.dns_type(), DnsType::kA);
return Error::None();
});
EXPECT_CALL(*mdns_service(), UnregisterRecord(_))
.WillOnce([](const MdnsRecord& record) -> Error {
EXPECT_EQ(record.dns_type(), DnsType::kAAAA);
return Error::None();
});
EXPECT_CALL(*mdns_service(), UpdateRegisteredRecord(_, _))
.WillOnce(
[](const MdnsRecord& record, const MdnsRecord& record2) -> Error {
EXPECT_EQ(record.dns_type(), DnsType::kTXT);
EXPECT_EQ(record2.dns_type(), DnsType::kTXT);
return Error::None();
});
EXPECT_EQ(publisher()->UpdateRegistration(instance), Error::None());
testing::Mock::VerifyAndClearExpectations(mdns_service());
}
} // namespace
} // namespace discovery
} // namespace openscreen