// 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/service.h" #include #include #include #include #include #include #include "peerd/constants.h" #include "peerd/test_util.h" using IpAddresses = peerd::Service::IpAddresses; using ServiceInfo = peerd::Service::ServiceInfo; using chromeos::Any; using dbus::Bus; using dbus::MockBus; using dbus::MockExportedObject; using dbus::ObjectPath; using peerd::constants::options::service::kMDNSPort; using peerd::constants::options::service::kMDNSSectionName; using peerd::errors::service::kInvalidServiceId; using peerd::errors::service::kInvalidServiceInfo; using peerd::errors::service::kInvalidServiceOptions; using peerd::test_util::MakeMockCompletionAction; using std::map; using std::string; using std::unique_ptr; using testing::AnyNumber; using testing::Invoke; using testing::Return; using testing::_; namespace { IpAddresses MakeValidIpAddresses() { // TODO(wiley) return a non-trivial list here. return {}; } const char kServicePath[] = "/a/path"; const char kTestPeerId[] = "This is a uuid for ourselves."; const char kValidServiceId[] = "valid-id"; } // namespace namespace peerd { class ServiceTest : public ::testing::Test { public: void SetUp() override { // Ignore threading concerns. EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber()); EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber()); // Unless we expect to create a Service object, we won't. EXPECT_CALL(*bus_, GetExportedObject(_)).Times(0); // Just immediately call callbacks on ExportMethod calls. EXPECT_CALL(*service_object_, ExportMethod(_, _, _, _)) .WillRepeatedly(Invoke(&test_util::HandleMethodExport)); // Ignore Unregister calls. EXPECT_CALL(*service_object_, Unregister()).Times(AnyNumber()); } void AssertMakeServiceFails(const string& service_id, const IpAddresses& addresses, const ServiceInfo& service_info, const map& options, const string& error_code) { chromeos::ErrorPtr error; EXPECT_FALSE(service_.RegisterAsync( &error, kTestPeerId, service_id, addresses, service_info, options, MakeMockCompletionAction())); ASSERT_NE(nullptr, error.get()); EXPECT_TRUE(error->HasError(kPeerdErrorDomain, error_code)); } void AssertMakeServiceSuccess( const string& service_id, const IpAddresses& addresses, const ServiceInfo& service_info, const map& options) { chromeos::ErrorPtr error; EXPECT_CALL(*bus_, GetExportedObject(_)) .WillOnce(Return(service_object_.get())); EXPECT_TRUE(service_.RegisterAsync( &error, kTestPeerId, service_id, addresses, service_info, options, MakeMockCompletionAction())); EXPECT_EQ(nullptr, error.get()); } scoped_refptr bus_{new MockBus{Bus::Options{}}}; scoped_refptr service_object_{ new MockExportedObject{bus_.get(), ObjectPath{kServicePath}}}; Service service_{bus_, nullptr, ObjectPath{kServicePath}}; }; TEST_F(ServiceTest, ShouldRejectZeroLengthServiceId) { AssertMakeServiceFails("", MakeValidIpAddresses(), ServiceInfo(), {}, kInvalidServiceId); } TEST_F(ServiceTest, ShouldRejectLongServiceId) { AssertMakeServiceFails(string(Service::kMaxServiceIdLength + 1, 'a'), MakeValidIpAddresses(), ServiceInfo(), {}, kInvalidServiceId); } TEST_F(ServiceTest, ShouldRejectInvalidCharInServiceId) { AssertMakeServiceFails("not*allowed", MakeValidIpAddresses(), ServiceInfo(), {}, kInvalidServiceId); } TEST_F(ServiceTest, ShouldRejectHyphenPrefix) { AssertMakeServiceFails("-not-allowed", MakeValidIpAddresses(), ServiceInfo(), {}, kInvalidServiceId); } TEST_F(ServiceTest, ShouldRejectHyphenSuffix) { AssertMakeServiceFails("not-allowed-", MakeValidIpAddresses(), ServiceInfo(), {}, kInvalidServiceId); } TEST_F(ServiceTest, ShouldRejectAdjacentHyphens) { AssertMakeServiceFails("not--allowed", MakeValidIpAddresses(), ServiceInfo(), {}, kInvalidServiceId); } TEST_F(ServiceTest, ShouldRejectInvalidCharInServiceInfoKey) { const ServiceInfo info = {{"spaces are illegal", "valid value"}}; AssertMakeServiceFails(kValidServiceId, MakeValidIpAddresses(), info, {}, kInvalidServiceInfo); } TEST_F(ServiceTest, ShouldRejectServiceInfoPairTooLong) { const ServiceInfo info = { {"k", string(Service::kMaxServiceInfoPairLength, 'v')}, }; AssertMakeServiceFails(kValidServiceId, MakeValidIpAddresses(), info, {}, kInvalidServiceInfo); } TEST_F(ServiceTest, RegisterWhenInputIsValid) { AssertMakeServiceSuccess(kValidServiceId, MakeValidIpAddresses(), ServiceInfo(), {}); } TEST_F(ServiceTest, RegisterWhenInputIsValidBoundaryCases) { const ServiceInfo service_info = { {"a", string(Service::kMaxServiceInfoPairLength - 1, 'b')}, {"", ""}, {"b", ""}, }; AssertMakeServiceSuccess( string(Service::kMaxServiceIdLength, 'a'), MakeValidIpAddresses(), service_info, {}); } TEST_F(ServiceTest, ShouldRejectExtraOptionsSections) { AssertMakeServiceFails(kValidServiceId, MakeValidIpAddresses(), ServiceInfo{}, map{{"not_valid", Any{"lies"}}}, kInvalidServiceOptions); } TEST_F(ServiceTest, ShouldRejectExtraMDnsOptions) { const map mdns_options{ {"not_valid", Any{"lies"}}, }; const map options{ {kMDNSSectionName, mdns_options}, }; AssertMakeServiceFails(kValidServiceId, MakeValidIpAddresses(), ServiceInfo{}, options, kInvalidServiceOptions); } TEST_F(ServiceTest, ShouldAcceptMDnsPort) { const uint16_t kChosenPort = 22; const map mdns_options{ {kMDNSPort, Any{kChosenPort}}, }; const map options{ {kMDNSSectionName, mdns_options}, }; AssertMakeServiceSuccess(kValidServiceId, MakeValidIpAddresses(), ServiceInfo{}, options); EXPECT_EQ(service_.GetMDnsOptions().port, kChosenPort); } } // namespace peerd