// Copyright 2014 The Chromium OS 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 "peerd/discovered_peer.h" #include #include #include #include #include #include #include #include #include #include "peerd/technologies.h" #include "peerd/test_util.h" using base::Time; using chromeos::dbus_utils::AsyncEventSequencer; using dbus::ObjectPath; using dbus::MockExportedObject; using peerd::technologies::tech_t; using std::string; using std::unique_ptr; using testing::AnyNumber; using testing::Invoke; using testing::Mock; using testing::Property; using testing::Return; using testing::_; namespace { const char kPeerPath[] = "/org/chromium/peerd/peers/1"; const char kServicePath[] = "/org/chromium/peerd/peers/1/services/1"; const char kPeerId[] = "123e4567-e89b-12d3-a456-426655440000"; // Initial peer values. const char kName[] = "friendly name"; const char kNote[] = "descriptive note"; const time_t kPeerLastSeen = 100; // Updated version of those fields. const char kNewName[] = "new friendly name"; const char kNewNote[] = "new descriptive note"; const time_t kNewPeerLastSeen = 200; // Bad values for peer fields. const char kBadName[] = "#evil name"; const char kBadNote[] = "#evil note"; const time_t kStalePeerLastSeen = 5; // Service bits. const char kServiceId[] = "a-service-id"; const char kBadServiceId[] = "#bad_service_id"; const time_t kServiceStaleLastSeen = 201; const time_t kServiceLastSeen = 225; const time_t kServiceNewLastSeen = 250; tech_t kFakeTech1 = 1 << 29; tech_t kFakeTech2 = 1 << 30; } // namespace namespace peerd { class DiscoveredPeerTest : public testing::Test { public: void SetUp() override { // Ignore threading concerns. EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber()); EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber()); // We're going to set up a DiscoveredPeer; it needs a DBusObject. EXPECT_CALL(*bus_, GetExportedObject(Property(&ObjectPath::value, kPeerPath))) .WillOnce(Return(peer_object_.get())); EXPECT_CALL(*peer_object_, ExportMethod(_, _, _, _)) .WillRepeatedly(Invoke(&test_util::HandleMethodExport)); peer_.reset(new DiscoveredPeer{bus_, nullptr, ObjectPath{kPeerPath}, kFakeTech1}); EXPECT_TRUE(peer_->RegisterAsync( nullptr, kPeerId, kName, kNote, Time::FromTimeT(kPeerLastSeen), AsyncEventSequencer::GetDefaultCompletionAction())); // By default, no services should be exported. EXPECT_CALL(*bus_, GetExportedObject(_)) .Times(0); EXPECT_CALL(*service_object_, Unregister()).Times(0); // Don't worry about signals. EXPECT_CALL(*peer_object_, SendSignal(_)).Times(AnyNumber()); EXPECT_CALL(*service_object_, SendSignal(_)).Times(AnyNumber()); } void TearDown() override { Mock::VerifyAndClearExpectations(peer_object_.get()); Mock::VerifyAndClearExpectations(service_object_.get()); EXPECT_CALL(*peer_object_, Unregister()).Times(1); if (service_exposed_) { ExpectServiceRemoved(); } } void ExpectInitialPeerValues() { EXPECT_EQ(kName, peer_->GetFriendlyName()); EXPECT_EQ(kNote, peer_->GetNote()); EXPECT_EQ(Time::FromTimeT(kPeerLastSeen), peer_->GetLastSeen()); } void ExpectServiceExposed() { service_exposed_ = true; EXPECT_CALL(*bus_, GetExportedObject(Property(&ObjectPath::value, kServicePath))) .WillOnce(Return(service_object_.get())); EXPECT_CALL(*service_object_, ExportMethod(_, _, _, _)) .WillRepeatedly(Invoke(&test_util::HandleMethodExport)); } void ExpectServiceRemoved() { Mock::VerifyAndClearExpectations(service_object_.get()); service_exposed_ = false; EXPECT_CALL(*service_object_, Unregister()).Times(1); } void ExpectNoServicesDiscovered() { EXPECT_TRUE(peer_->service_metadata_.empty()); EXPECT_TRUE(peer_->services_.empty()); } void ExpectServiceHasValues(const string& service_id, const Service::IpAddresses& ip_addr, const Service::ServiceInfo& info, const base::Time& last_seen, tech_t tech) { auto service_it = peer_->services_.find(service_id); auto metadata_it = peer_->service_metadata_.find(service_id); // Better find this peer in both collections of data. ASSERT_NE(service_it, peer_->services_.end()); ASSERT_NE(metadata_it, peer_->service_metadata_.end()); // Fields in the peer should match. EXPECT_EQ(ip_addr, service_it->second->GetIpAddresses()); EXPECT_EQ(info, service_it->second->GetServiceInfo()); // Metadata should also match. EXPECT_EQ(last_seen, metadata_it->second.last_seen); EXPECT_EQ(tech, metadata_it->second.technology); } scoped_refptr bus_{new dbus::MockBus{dbus::Bus::Options{}}}; scoped_refptr peer_object_{ new MockExportedObject{bus_.get(), ObjectPath{kPeerPath}}}; scoped_refptr service_object_{ new MockExportedObject{bus_.get(), ObjectPath{kServicePath}}}; bool service_exposed_{false}; unique_ptr peer_; }; TEST_F(DiscoveredPeerTest, CanUpdateFromAdvertisement) { peer_->UpdateFromAdvertisement(kNewName, kNewNote, Time::FromTimeT(kNewPeerLastSeen), kFakeTech1); EXPECT_EQ(kNewName, peer_->GetFriendlyName()); EXPECT_EQ(kNewNote, peer_->GetNote()); EXPECT_EQ(Time::FromTimeT(kNewPeerLastSeen), peer_->GetLastSeen()); EXPECT_EQ(1, peer_->GetTechnologyCount()); } TEST_F(DiscoveredPeerTest, CannotUpdateFromInvalidAdvertisement) { peer_->UpdateFromAdvertisement(kBadName, kNewNote, Time::FromTimeT(kNewPeerLastSeen), kFakeTech1); ExpectInitialPeerValues(); } TEST_F(DiscoveredPeerTest, CannotPartiallyUpdatePeer) { // Only invalid note. peer_->UpdateFromAdvertisement(kNewName, kBadNote, Time::FromTimeT(kNewPeerLastSeen), kFakeTech1); ExpectInitialPeerValues(); // Only invalid name. peer_->UpdateFromAdvertisement(kBadName, kNewNote, Time::FromTimeT(kNewPeerLastSeen), kFakeTech1); ExpectInitialPeerValues(); // Stale advertisement. peer_->UpdateFromAdvertisement(kNewName, kNewNote, Time::FromTimeT(kStalePeerLastSeen), kFakeTech1); ExpectInitialPeerValues(); } TEST_F(DiscoveredPeerTest, CanAddService) { ExpectServiceExposed(); peer_->UpdateService(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); } TEST_F(DiscoveredPeerTest, CannotPartiallyAddService) { peer_->UpdateService(kBadServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); ExpectNoServicesDiscovered(); } TEST_F(DiscoveredPeerTest, CannotAddServiceForUnknownTechnology) { peer_->UpdateService(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech2); } TEST_F(DiscoveredPeerTest, CanUpdateService) { ExpectServiceExposed(); peer_->UpdateService(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); const Service::ServiceInfo valid_info{{"good_key", "valid value"}}; peer_->UpdateService(kServiceId, {}, valid_info, Time::FromTimeT(kServiceNewLastSeen), kFakeTech1); ExpectServiceHasValues(kServiceId, {}, valid_info, Time::FromTimeT(kServiceNewLastSeen), kFakeTech1); } TEST_F(DiscoveredPeerTest, CannotPartiallyUpdateService) { ExpectServiceExposed(); peer_->UpdateService(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); const Service::ServiceInfo invalid_info{{"#badkey", "valid value"}}; peer_->UpdateService(kServiceId, {}, invalid_info, Time::FromTimeT(kServiceLastSeen), kFakeTech2); ExpectServiceHasValues(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); } TEST_F(DiscoveredPeerTest, ShouldRejectStaleServiceUpdate) { peer_->UpdateFromAdvertisement(kName, kNote, Time::FromTimeT(kPeerLastSeen), kFakeTech2); ExpectServiceExposed(); peer_->UpdateService(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); const Service::ServiceInfo valid_info{{"good_key", "valid value"}}; peer_->UpdateService(kServiceId, {}, valid_info, Time::FromTimeT(kServiceStaleLastSeen), kFakeTech2); ExpectServiceHasValues(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); } TEST_F(DiscoveredPeerTest, RemoveTechnologyIsRecursive) { ExpectServiceExposed(); peer_->UpdateService(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); ExpectServiceRemoved(); peer_->RemoveTechnology(kFakeTech1); } TEST_F(DiscoveredPeerTest, HandlesMultipleTechnologies) { peer_->UpdateFromAdvertisement(kNewName, kNewNote, Time::FromTimeT(kNewPeerLastSeen), kFakeTech2); EXPECT_EQ(2, peer_->GetTechnologyCount()); ExpectServiceExposed(); peer_->UpdateService(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech1); // This should not create a new service object, just touch the existing one. peer_->UpdateService(kServiceId, {}, {}, Time::FromTimeT(kServiceLastSeen), kFakeTech2); peer_->RemoveTechnology(kFakeTech2); EXPECT_EQ(1, peer_->GetTechnologyCount()); ExpectServiceRemoved(); peer_->RemoveTechnologyFromService(kServiceId, kFakeTech1); } } // namespace peerd