You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
658 lines
26 KiB
658 lines
26 KiB
// 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/querier_impl.h"
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "absl/strings/str_join.h"
|
|
#include "absl/strings/str_split.h"
|
|
#include "absl/types/optional.h"
|
|
#include "discovery/common/testing/mock_reporting_client.h"
|
|
#include "discovery/dnssd/impl/conversion_layer.h"
|
|
#include "discovery/dnssd/testing/fake_network_interface_config.h"
|
|
#include "discovery/mdns/mdns_records.h"
|
|
#include "discovery/mdns/testing/mdns_test_util.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "platform/test/fake_clock.h"
|
|
#include "platform/test/fake_task_runner.h"
|
|
#include "util/osp_logging.h"
|
|
|
|
namespace openscreen {
|
|
namespace discovery {
|
|
namespace {
|
|
|
|
NetworkInterfaceIndex kNetworkInterface = 0;
|
|
|
|
class MockCallback : public DnsSdQuerier::Callback {
|
|
public:
|
|
MOCK_METHOD1(OnEndpointCreated, void(const DnsSdInstanceEndpoint&));
|
|
MOCK_METHOD1(OnEndpointUpdated, void(const DnsSdInstanceEndpoint&));
|
|
MOCK_METHOD1(OnEndpointDeleted, void(const DnsSdInstanceEndpoint&));
|
|
};
|
|
|
|
class MockMdnsService : public MdnsService {
|
|
public:
|
|
MOCK_METHOD4(
|
|
StartQuery,
|
|
void(const DomainName&, DnsType, DnsClass, MdnsRecordChangedCallback*));
|
|
|
|
MOCK_METHOD4(
|
|
StopQuery,
|
|
void(const DomainName&, DnsType, DnsClass, MdnsRecordChangedCallback*));
|
|
|
|
MOCK_METHOD1(ReinitializeQueries, void(const DomainName& name));
|
|
|
|
// Unused.
|
|
MOCK_METHOD3(StartProbe,
|
|
Error(MdnsDomainConfirmedProvider*, DomainName, IPAddress));
|
|
MOCK_METHOD1(RegisterRecord, Error(const MdnsRecord&));
|
|
MOCK_METHOD1(UnregisterRecord, Error(const MdnsRecord&));
|
|
MOCK_METHOD2(UpdateRegisteredRecord,
|
|
Error(const MdnsRecord&, const MdnsRecord&));
|
|
};
|
|
|
|
class MockDnsDataGraph : public DnsDataGraph {
|
|
public:
|
|
MOCK_METHOD2(StartTracking,
|
|
void(const DomainName& domain,
|
|
DomainChangeCallback on_start_tracking));
|
|
MOCK_METHOD2(StopTracking,
|
|
void(const DomainName& domain,
|
|
DomainChangeCallback on_start_tracking));
|
|
|
|
MOCK_CONST_METHOD2(
|
|
CreateEndpoints,
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>>(DomainGroup,
|
|
const DomainName&));
|
|
|
|
MOCK_METHOD4(ApplyDataRecordChange,
|
|
Error(MdnsRecord,
|
|
RecordChangedEvent,
|
|
DomainChangeCallback,
|
|
DomainChangeCallback));
|
|
|
|
MOCK_CONST_METHOD0(GetTrackedDomainCount, size_t());
|
|
|
|
MOCK_CONST_METHOD1(IsTracked, bool(const DomainName&));
|
|
};
|
|
|
|
} // namespace
|
|
|
|
using testing::_;
|
|
using testing::ByMove;
|
|
using testing::Return;
|
|
using testing::StrictMock;
|
|
|
|
class QuerierImplTesting : public QuerierImpl {
|
|
public:
|
|
QuerierImplTesting()
|
|
: QuerierImpl(&mock_service_,
|
|
&task_runner_,
|
|
&reporting_client_,
|
|
&network_config_),
|
|
clock_(Clock::now()),
|
|
task_runner_(&clock_) {}
|
|
|
|
StrictMock<MockMdnsService>& service() { return mock_service_; }
|
|
|
|
StrictMock<MockReportingClient>& reporting_client() {
|
|
return reporting_client_;
|
|
}
|
|
|
|
// NOTE: This should only be used for testing hard-to-achieve edge cases.
|
|
StrictMock<MockDnsDataGraph>& GetMockedGraph() {
|
|
if (!is_graph_mocked_) {
|
|
graph_ = std::make_unique<StrictMock<MockDnsDataGraph>>();
|
|
is_graph_mocked_ = true;
|
|
}
|
|
|
|
return static_cast<StrictMock<MockDnsDataGraph>&>(*graph_);
|
|
}
|
|
|
|
size_t GetTrackedDomainCount() { return graph_->GetTrackedDomainCount(); }
|
|
|
|
bool IsDomainTracked(const DomainName& domain) {
|
|
return graph_->IsTracked(domain);
|
|
}
|
|
|
|
using QuerierImpl::OnRecordChanged;
|
|
|
|
private:
|
|
FakeClock clock_;
|
|
FakeTaskRunner task_runner_;
|
|
FakeNetworkInterfaceConfig network_config_;
|
|
StrictMock<MockMdnsService> mock_service_;
|
|
StrictMock<MockReportingClient> reporting_client_;
|
|
|
|
bool is_graph_mocked_ = false;
|
|
};
|
|
|
|
class DnsSdQuerierImplTest : public testing::Test {
|
|
public:
|
|
DnsSdQuerierImplTest()
|
|
: querier(std::make_unique<QuerierImplTesting>()),
|
|
ptr_domain(DomainName{"_service", "_udp", domain}),
|
|
name(DomainName{instance, "_service", "_udp", domain}),
|
|
name2(DomainName{instance2, "_service", "_udp", domain}) {
|
|
EXPECT_FALSE(querier->IsQueryRunning(service));
|
|
|
|
EXPECT_CALL(querier->service(),
|
|
StartQuery(_, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
querier->StartQuery(service, &callback);
|
|
EXPECT_TRUE(querier->IsQueryRunning(service));
|
|
testing::Mock::VerifyAndClearExpectations(&querier->service());
|
|
|
|
EXPECT_TRUE(querier->IsQueryRunning(service));
|
|
testing::Mock::VerifyAndClearExpectations(&querier->service());
|
|
}
|
|
|
|
protected:
|
|
void ValidateRecordChangeStartsQuery(
|
|
const std::vector<PendingQueryChange>& changes,
|
|
const DomainName& name,
|
|
size_t expected_size) {
|
|
ValidateRecordChangeResult(changes, name, expected_size,
|
|
PendingQueryChange::kStartQuery);
|
|
}
|
|
|
|
void ValidateRecordChangeStopsQuery(
|
|
const std::vector<PendingQueryChange>& changes,
|
|
const DomainName& name,
|
|
size_t expected_size) {
|
|
ValidateRecordChangeResult(changes, name, expected_size,
|
|
PendingQueryChange::kStopQuery);
|
|
}
|
|
|
|
void CreateServiceInstance(const DomainName& service_domain,
|
|
MockCallback* cb) {
|
|
MdnsRecord ptr = GetFakePtrRecord(service_domain);
|
|
MdnsRecord srv = GetFakeSrvRecord(service_domain);
|
|
MdnsRecord txt = GetFakeTxtRecord(service_domain);
|
|
MdnsRecord a = GetFakeARecord(service_domain);
|
|
MdnsRecord aaaa = GetFakeAAAARecord(service_domain);
|
|
|
|
auto result = querier->OnRecordChanged(ptr, RecordChangedEvent::kCreated);
|
|
ValidateRecordChangeStartsQuery(result, service_domain, 1);
|
|
|
|
// NOTE: This verbose iterator handling is used to avoid gcc failures.
|
|
auto it = service_domain.labels().begin();
|
|
it++;
|
|
std::string service_name = *it;
|
|
it++;
|
|
std::string service_protocol = *it;
|
|
std::string service_id = "";
|
|
service_id.append(std::move(service_name))
|
|
.append(".")
|
|
.append(std::move(service_protocol));
|
|
ASSERT_TRUE(querier->IsQueryRunning(service_id));
|
|
|
|
result = querier->OnRecordChanged(srv, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(result.size(), size_t{0});
|
|
|
|
result = querier->OnRecordChanged(a, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(result.size(), size_t{0});
|
|
|
|
result = querier->OnRecordChanged(aaaa, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(result.size(), size_t{0});
|
|
|
|
EXPECT_CALL(*cb, OnEndpointCreated(_)).Times(1);
|
|
result = querier->OnRecordChanged(txt, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(result.size(), size_t{0});
|
|
testing::Mock::VerifyAndClearExpectations(cb);
|
|
}
|
|
|
|
std::string instance = "instance";
|
|
std::string instance2 = "instance2";
|
|
std::string service = "_service._udp";
|
|
std::string service2 = "_service2._udp";
|
|
std::string domain = "local";
|
|
StrictMock<MockCallback> callback;
|
|
std::unique_ptr<QuerierImplTesting> querier;
|
|
DomainName ptr_domain;
|
|
DomainName name;
|
|
DomainName name2;
|
|
|
|
private:
|
|
void ValidateRecordChangeResult(
|
|
const std::vector<PendingQueryChange>& changes,
|
|
const DomainName& name,
|
|
size_t expected_size,
|
|
PendingQueryChange::ChangeType change_type) {
|
|
EXPECT_EQ(changes.size(), expected_size);
|
|
auto it = std::find_if(
|
|
changes.begin(), changes.end(),
|
|
[&name, change_type](const PendingQueryChange& change) {
|
|
return change.dns_type == DnsType::kANY &&
|
|
change.dns_class == DnsClass::kANY &&
|
|
change.change_type == change_type && change.name == name;
|
|
});
|
|
EXPECT_TRUE(it != changes.end());
|
|
}
|
|
};
|
|
|
|
// Common Use Cases
|
|
//
|
|
// The below tests validate the common use cases for QuerierImpl, which we
|
|
// expect will be hit for reasonable actors on the network. For these tests, the
|
|
// real DnsDataGraph object will be used.
|
|
|
|
TEST_F(DnsSdQuerierImplTest, TestStartStopQueryCallsMdnsQueries) {
|
|
DomainName other_service_id(
|
|
DomainName{instance2, "_service2", "_udp", domain});
|
|
|
|
StrictMock<MockCallback> callback2;
|
|
EXPECT_FALSE(querier->IsQueryRunning(service2));
|
|
|
|
EXPECT_CALL(querier->service(),
|
|
StartQuery(_, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
querier->StartQuery(service2, &callback2);
|
|
EXPECT_TRUE(querier->IsQueryRunning(service2));
|
|
|
|
EXPECT_CALL(querier->service(),
|
|
StopQuery(_, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
querier->StopQuery(service2, &callback2);
|
|
EXPECT_FALSE(querier->IsQueryRunning(service2));
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, TestStartDuplicateQueryFiresCallbacksWhenAble) {
|
|
StrictMock<MockCallback> callback2;
|
|
CreateServiceInstance(name, &callback);
|
|
|
|
EXPECT_CALL(callback2, OnEndpointCreated(_)).Times(1);
|
|
querier->StartQuery(service, &callback2);
|
|
testing::Mock::VerifyAndClearExpectations(&callback2);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, TestStopQueryStopsTrackingRecords) {
|
|
CreateServiceInstance(name, &callback);
|
|
|
|
DomainName ptr_domain(++name.labels().begin(), name.labels().end());
|
|
EXPECT_CALL(querier->service(),
|
|
StopQuery(ptr_domain, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
EXPECT_CALL(querier->service(),
|
|
StopQuery(name, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
querier->StopQuery(service, &callback);
|
|
EXPECT_FALSE(querier->IsDomainTracked(ptr_domain));
|
|
EXPECT_FALSE(querier->IsDomainTracked(name));
|
|
EXPECT_EQ(querier->GetTrackedDomainCount(), size_t{0});
|
|
testing::Mock::VerifyAndClearExpectations(&callback);
|
|
|
|
EXPECT_CALL(querier->service(),
|
|
StartQuery(_, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
querier->StartQuery(service, &callback);
|
|
EXPECT_TRUE(querier->IsQueryRunning(service));
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, TestStopNonexistantQueryHasNoEffect) {
|
|
StrictMock<MockCallback> callback2;
|
|
querier->StopQuery(service, &callback2);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, TestAFollowingAAAAFiresSecondCallback) {
|
|
MdnsRecord ptr = GetFakePtrRecord(name);
|
|
MdnsRecord srv = GetFakeSrvRecord(name);
|
|
MdnsRecord txt = GetFakeTxtRecord(name);
|
|
MdnsRecord a = GetFakeARecord(name);
|
|
MdnsRecord aaaa = GetFakeAAAARecord(name);
|
|
|
|
std::vector<DnsSdInstanceEndpoint> endpoints;
|
|
auto changes = querier->OnRecordChanged(ptr, RecordChangedEvent::kCreated);
|
|
ValidateRecordChangeStartsQuery(changes, name, 1);
|
|
|
|
changes = querier->OnRecordChanged(srv, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
changes = querier->OnRecordChanged(txt, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
|
|
EXPECT_CALL(callback, OnEndpointCreated(_))
|
|
.WillOnce([&endpoints](const DnsSdInstanceEndpoint& ep) mutable {
|
|
endpoints.push_back(ep);
|
|
});
|
|
changes = querier->OnRecordChanged(aaaa, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
testing::Mock::VerifyAndClearExpectations(&callback);
|
|
|
|
EXPECT_CALL(callback, OnEndpointUpdated(_))
|
|
.WillOnce([&endpoints](const DnsSdInstanceEndpoint& ep) mutable {
|
|
endpoints.push_back(ep);
|
|
});
|
|
changes = querier->OnRecordChanged(a, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
testing::Mock::VerifyAndClearExpectations(&callback);
|
|
|
|
ASSERT_EQ(endpoints.size(), size_t{2});
|
|
DnsSdInstanceEndpoint& created = endpoints[0];
|
|
DnsSdInstanceEndpoint& updated = endpoints[1];
|
|
EXPECT_EQ(static_cast<DnsSdInstance>(created),
|
|
static_cast<DnsSdInstance>(updated));
|
|
|
|
ASSERT_EQ(created.addresses().size(), size_t{1});
|
|
EXPECT_TRUE(created.addresses()[0].IsV6());
|
|
|
|
ASSERT_EQ(updated.addresses().size(), size_t{2});
|
|
EXPECT_TRUE(created.addresses()[0] == updated.addresses()[0] ||
|
|
created.addresses()[0] == updated.addresses()[1]);
|
|
EXPECT_TRUE(updated.addresses()[0].IsV4() || updated.addresses()[1].IsV4());
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, TestGenerateTwoRecordsCallsCallbackTwice) {
|
|
DomainName third{"android", "local"};
|
|
MdnsRecord ptr1 = GetFakePtrRecord(name);
|
|
MdnsRecord srv1 = GetFakeSrvRecord(name, third);
|
|
MdnsRecord txt1 = GetFakeTxtRecord(name);
|
|
MdnsRecord ptr2 = GetFakePtrRecord(name2);
|
|
MdnsRecord srv2 = GetFakeSrvRecord(name2, third);
|
|
MdnsRecord txt2 = GetFakeTxtRecord(name2);
|
|
MdnsRecord a = GetFakeARecord(third);
|
|
|
|
auto changes = querier->OnRecordChanged(ptr1, RecordChangedEvent::kCreated);
|
|
ValidateRecordChangeStartsQuery(changes, name, 1);
|
|
|
|
changes = querier->OnRecordChanged(srv1, RecordChangedEvent::kCreated);
|
|
ValidateRecordChangeStartsQuery(changes, third, 1);
|
|
|
|
changes = querier->OnRecordChanged(txt1, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
|
|
changes = querier->OnRecordChanged(ptr2, RecordChangedEvent::kCreated);
|
|
ValidateRecordChangeStartsQuery(changes, name2, 1);
|
|
|
|
changes = querier->OnRecordChanged(srv2, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
|
|
changes = querier->OnRecordChanged(txt2, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
|
|
EXPECT_CALL(callback, OnEndpointCreated(_)).Times(2);
|
|
changes = querier->OnRecordChanged(a, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
testing::Mock::VerifyAndClearExpectations(&callback);
|
|
|
|
EXPECT_CALL(callback, OnEndpointDeleted(_)).Times(2);
|
|
changes = querier->OnRecordChanged(a, RecordChangedEvent::kExpired);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, TestCreateDeletePtrRecordResults) {
|
|
const auto ptr = GetFakePtrRecord(name);
|
|
|
|
auto result = querier->OnRecordChanged(ptr, RecordChangedEvent::kCreated);
|
|
ValidateRecordChangeStartsQuery(result, name, 1);
|
|
|
|
result = querier->OnRecordChanged(ptr, RecordChangedEvent::kExpired);
|
|
ValidateRecordChangeStopsQuery(result, name, 1);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, CallbackCalledWhenPtrDeleted) {
|
|
MdnsRecord ptr = GetFakePtrRecord(name);
|
|
MdnsRecord srv = GetFakeSrvRecord(name, name2);
|
|
MdnsRecord txt = GetFakeTxtRecord(name);
|
|
MdnsRecord a = GetFakeARecord(name2);
|
|
|
|
auto changes = querier->OnRecordChanged(ptr, RecordChangedEvent::kCreated);
|
|
ValidateRecordChangeStartsQuery(changes, name, 1);
|
|
|
|
changes = querier->OnRecordChanged(srv, RecordChangedEvent::kCreated);
|
|
ValidateRecordChangeStartsQuery(changes, name2, 1);
|
|
|
|
changes = querier->OnRecordChanged(txt, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
|
|
EXPECT_CALL(callback, OnEndpointCreated(_));
|
|
changes = querier->OnRecordChanged(a, RecordChangedEvent::kCreated);
|
|
EXPECT_EQ(changes.size(), size_t{0});
|
|
|
|
EXPECT_CALL(callback, OnEndpointDeleted(_));
|
|
changes = querier->OnRecordChanged(ptr, RecordChangedEvent::kExpired);
|
|
ValidateRecordChangeStopsQuery(changes, name, 2);
|
|
ValidateRecordChangeStopsQuery(changes, name2, 2);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, HardRefresh) {
|
|
MdnsRecord ptr = GetFakePtrRecord(name);
|
|
MdnsRecord srv = GetFakeSrvRecord(name, name2);
|
|
MdnsRecord txt = GetFakeTxtRecord(name);
|
|
MdnsRecord a = GetFakeARecord(name2);
|
|
|
|
querier->OnRecordChanged(ptr, RecordChangedEvent::kCreated);
|
|
querier->OnRecordChanged(srv, RecordChangedEvent::kCreated);
|
|
querier->OnRecordChanged(txt, RecordChangedEvent::kCreated);
|
|
|
|
EXPECT_CALL(callback, OnEndpointCreated(_));
|
|
querier->OnRecordChanged(a, RecordChangedEvent::kCreated);
|
|
testing::Mock::VerifyAndClearExpectations(&callback);
|
|
|
|
EXPECT_CALL(querier->service(),
|
|
StopQuery(ptr_domain, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
EXPECT_CALL(querier->service(),
|
|
StopQuery(name, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
EXPECT_CALL(querier->service(),
|
|
StopQuery(name2, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
EXPECT_CALL(querier->service(), ReinitializeQueries(_)).Times(1);
|
|
EXPECT_CALL(querier->service(),
|
|
StartQuery(ptr_domain, DnsType::kANY, DnsClass::kANY, _))
|
|
.Times(1);
|
|
querier->ReinitializeQueries(service);
|
|
testing::Mock::VerifyAndClearExpectations(querier.get());
|
|
}
|
|
|
|
// Edge Cases
|
|
//
|
|
// The below tests validate against edge cases that either either difficult to
|
|
// achieve, are not expected to be possible under normal circumstances but
|
|
// should be validated against for safety, or should only occur when either a
|
|
// bad actor or a misbehaving publisher is present on the network. To simplify
|
|
// these tests, the DnsDataGraph object will be mocked.
|
|
TEST_F(DnsSdQuerierImplTest, ErrorsOnlyAfterChangesAreLogged) {
|
|
MockDnsDataGraph& mock_graph = querier->GetMockedGraph();
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> before_changes{};
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> after_changes{};
|
|
after_changes.emplace_back(Error::Code::kItemNotFound);
|
|
after_changes.emplace_back(Error::Code::kItemNotFound);
|
|
after_changes.emplace_back(Error::Code::kItemAlreadyExists);
|
|
|
|
// Calls before and after applying record changes, then the error it logs.
|
|
EXPECT_CALL(mock_graph, CreateEndpoints(_, _))
|
|
.WillOnce(Return(ByMove(std::move(before_changes))))
|
|
.WillOnce(Return(ByMove(std::move(after_changes))));
|
|
EXPECT_CALL(querier->reporting_client(), OnRecoverableError(_)).Times(3);
|
|
|
|
// Call to apply record changes. The specifics are unimportant.
|
|
EXPECT_CALL(mock_graph, ApplyDataRecordChange(_, _, _, _))
|
|
.WillOnce(Return(Error::None()));
|
|
|
|
// Call with any record. The mocks make the specifics unimportant.
|
|
querier->OnRecordChanged(GetFakePtrRecord(name),
|
|
RecordChangedEvent::kCreated);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, ErrorsOnlyBeforeChangesNotLogged) {
|
|
MockDnsDataGraph& mock_graph = querier->GetMockedGraph();
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> before_changes{};
|
|
before_changes.emplace_back(Error::Code::kItemNotFound);
|
|
before_changes.emplace_back(Error::Code::kItemNotFound);
|
|
before_changes.emplace_back(Error::Code::kItemAlreadyExists);
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> after_changes{};
|
|
|
|
// Calls before and after applying record changes.
|
|
EXPECT_CALL(mock_graph, CreateEndpoints(_, _))
|
|
.WillOnce(Return(ByMove(std::move(before_changes))))
|
|
.WillOnce(Return(ByMove(std::move(after_changes))));
|
|
|
|
// Call to apply record changes. The specifics are unimportant.
|
|
EXPECT_CALL(mock_graph, ApplyDataRecordChange(_, _, _, _))
|
|
.WillOnce(Return(Error::None()));
|
|
|
|
// Call with any record. The mocks make the specifics unimportant.
|
|
querier->OnRecordChanged(GetFakePtrRecord(name),
|
|
RecordChangedEvent::kCreated);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, ErrorsBeforeAndAfterChangesNotLogged) {
|
|
MockDnsDataGraph& mock_graph = querier->GetMockedGraph();
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> before_changes{};
|
|
before_changes.emplace_back(Error::Code::kItemNotFound);
|
|
before_changes.emplace_back(Error::Code::kItemNotFound);
|
|
before_changes.emplace_back(Error::Code::kItemAlreadyExists);
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> after_changes{};
|
|
after_changes.emplace_back(Error::Code::kItemNotFound);
|
|
after_changes.emplace_back(Error::Code::kItemAlreadyExists);
|
|
after_changes.emplace_back(Error::Code::kItemNotFound);
|
|
|
|
// Calls before and after applying record changes.
|
|
EXPECT_CALL(mock_graph, CreateEndpoints(_, _))
|
|
.WillOnce(Return(ByMove(std::move(before_changes))))
|
|
.WillOnce(Return(ByMove(std::move(after_changes))));
|
|
|
|
// Call to apply record changes. The specifics are unimportant.
|
|
EXPECT_CALL(mock_graph, ApplyDataRecordChange(_, _, _, _))
|
|
.WillOnce(Return(Error::None()));
|
|
|
|
// Call with any record. The mocks make the specifics unimportant.
|
|
querier->OnRecordChanged(GetFakePtrRecord(name),
|
|
RecordChangedEvent::kCreated);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, OrderOfErrorsDoesNotAffectResults) {
|
|
MockDnsDataGraph& mock_graph = querier->GetMockedGraph();
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> before_changes{};
|
|
before_changes.emplace_back(Error::Code::kIndexOutOfBounds);
|
|
before_changes.emplace_back(Error::Code::kItemAlreadyExists);
|
|
before_changes.emplace_back(Error::Code::kOperationCancelled);
|
|
before_changes.emplace_back(Error::Code::kItemNotFound);
|
|
before_changes.emplace_back(Error::Code::kOperationInProgress);
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> after_changes{};
|
|
after_changes.emplace_back(Error::Code::kOperationInProgress);
|
|
after_changes.emplace_back(Error::Code::kUnknownError);
|
|
after_changes.emplace_back(Error::Code::kItemNotFound);
|
|
after_changes.emplace_back(Error::Code::kItemAlreadyExists);
|
|
after_changes.emplace_back(Error::Code::kOperationCancelled);
|
|
|
|
// Calls before and after applying record changes, then the error it logs.
|
|
EXPECT_CALL(mock_graph, CreateEndpoints(_, _))
|
|
.WillOnce(Return(ByMove(std::move(before_changes))))
|
|
.WillOnce(Return(ByMove(std::move(after_changes))));
|
|
EXPECT_CALL(querier->reporting_client(), OnRecoverableError(_)).Times(1);
|
|
|
|
// Call to apply record changes. The specifics are unimportant.
|
|
EXPECT_CALL(mock_graph, ApplyDataRecordChange(_, _, _, _))
|
|
.WillOnce(Return(Error::None()));
|
|
|
|
// Call with any record. The mocks make the specifics unimportant.
|
|
querier->OnRecordChanged(GetFakePtrRecord(name),
|
|
RecordChangedEvent::kCreated);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, ResultsWithMultipleAddressRecordsHandled) {
|
|
IPEndpoint endpointa{{192, 168, 86, 23}, 80};
|
|
IPEndpoint endpointb{{1, 2, 3, 4, 5, 6, 7, 8}, 80};
|
|
IPEndpoint endpointc{{192, 168, 0, 1}, 80};
|
|
IPEndpoint endpointd{{192, 168, 0, 2}, 80};
|
|
IPEndpoint endpointe{{192, 168, 0, 3}, 80};
|
|
|
|
DnsSdInstanceEndpoint instance1("instance1", "_service._udp", "local", {},
|
|
kNetworkInterface, {endpointa, endpointb});
|
|
DnsSdInstanceEndpoint instance2("instance2", "_service2._udp", "local", {},
|
|
kNetworkInterface, {endpointa, endpointb});
|
|
DnsSdInstanceEndpoint instance3("instance3", "_service._udp", "local", {},
|
|
kNetworkInterface, {endpointc});
|
|
DnsSdInstanceEndpoint instance4("instance1", "_service3._udp", "local", {},
|
|
kNetworkInterface, {endpointd, endpointe});
|
|
DnsSdInstanceEndpoint instance5("instance1", "_service3._udp", "local", {},
|
|
kNetworkInterface, {endpointe});
|
|
|
|
MockDnsDataGraph& mock_graph = querier->GetMockedGraph();
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> before_changes{};
|
|
before_changes.emplace_back(instance4);
|
|
before_changes.emplace_back(instance2);
|
|
before_changes.emplace_back(instance3);
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> after_changes{};
|
|
after_changes.emplace_back(instance5);
|
|
after_changes.emplace_back(instance3);
|
|
after_changes.emplace_back(instance1);
|
|
|
|
// Calls before and after applying record changes, then the error it logs.
|
|
EXPECT_CALL(mock_graph, CreateEndpoints(_, _))
|
|
.WillOnce(Return(ByMove(std::move(before_changes))))
|
|
.WillOnce(Return(ByMove(std::move(after_changes))));
|
|
EXPECT_CALL(callback, OnEndpointCreated(instance1));
|
|
EXPECT_CALL(callback, OnEndpointUpdated(instance5));
|
|
EXPECT_CALL(callback, OnEndpointDeleted(instance2));
|
|
|
|
// Call to apply record changes. The specifics are unimportant.
|
|
EXPECT_CALL(mock_graph, ApplyDataRecordChange(_, _, _, _))
|
|
.WillOnce(Return(Error::None()));
|
|
|
|
// Call with any record. The mocks make the specifics unimportant.
|
|
querier->OnRecordChanged(GetFakePtrRecord(name),
|
|
RecordChangedEvent::kCreated);
|
|
}
|
|
|
|
TEST_F(DnsSdQuerierImplTest, MixOfErrorsAndSuccessesHandledCorrectly) {
|
|
DnsSdInstanceEndpoint instance1("instance1", "_service._udp", "local", {},
|
|
kNetworkInterface, {{{192, 168, 2, 24}, 80}});
|
|
DnsSdInstanceEndpoint instance2("instance2", "_service2._udp", "local", {},
|
|
kNetworkInterface, {{{192, 168, 17, 2}, 80}});
|
|
DnsSdInstanceEndpoint instance3("instance3", "_service._udp", "local", {},
|
|
kNetworkInterface, {{{127, 0, 0, 1}, 80}});
|
|
DnsSdInstanceEndpoint instance4("instance1", "_service3._udp", "local", {},
|
|
kNetworkInterface, {{{127, 0, 0, 1}, 80}});
|
|
DnsSdInstanceEndpoint instance5("instance1", "_service3._udp", "local", {},
|
|
kNetworkInterface,
|
|
{{{127, 0, 0, 1}, 80}, {{127, 0, 0, 2}, 80}});
|
|
|
|
MockDnsDataGraph& mock_graph = querier->GetMockedGraph();
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> before_changes{};
|
|
before_changes.emplace_back(Error::Code::kIndexOutOfBounds);
|
|
before_changes.emplace_back(instance2);
|
|
before_changes.emplace_back(Error::Code::kItemAlreadyExists);
|
|
before_changes.emplace_back(Error::Code::kOperationCancelled);
|
|
before_changes.emplace_back(instance1);
|
|
before_changes.emplace_back(Error::Code::kItemNotFound);
|
|
before_changes.emplace_back(Error::Code::kOperationInProgress);
|
|
before_changes.emplace_back(instance4);
|
|
std::vector<ErrorOr<DnsSdInstanceEndpoint>> after_changes{};
|
|
after_changes.emplace_back(instance1);
|
|
after_changes.emplace_back(Error::Code::kOperationInProgress);
|
|
after_changes.emplace_back(Error::Code::kUnknownError);
|
|
after_changes.emplace_back(Error::Code::kItemNotFound);
|
|
after_changes.emplace_back(Error::Code::kItemAlreadyExists);
|
|
after_changes.emplace_back(instance3);
|
|
after_changes.emplace_back(instance5);
|
|
after_changes.emplace_back(Error::Code::kOperationCancelled);
|
|
|
|
// Calls before and after applying record changes, then the error it logs.
|
|
EXPECT_CALL(mock_graph, CreateEndpoints(_, _))
|
|
.WillOnce(Return(ByMove(std::move(before_changes))))
|
|
.WillOnce(Return(ByMove(std::move(after_changes))));
|
|
EXPECT_CALL(querier->reporting_client(), OnRecoverableError(_)).Times(1);
|
|
EXPECT_CALL(callback, OnEndpointCreated(instance3));
|
|
EXPECT_CALL(callback, OnEndpointUpdated(instance5));
|
|
EXPECT_CALL(callback, OnEndpointDeleted(instance2));
|
|
|
|
// Call to apply record changes. The specifics are unimportant.
|
|
EXPECT_CALL(mock_graph, ApplyDataRecordChange(_, _, _, _))
|
|
.WillOnce(Return(Error::None()));
|
|
|
|
// Call with any record. The mocks make the specifics unimportant.
|
|
querier->OnRecordChanged(GetFakePtrRecord(name),
|
|
RecordChangedEvent::kCreated);
|
|
}
|
|
|
|
} // namespace discovery
|
|
} // namespace openscreen
|