blob: c306755e842f128279646d7cd35c88aa5e8ccdc1 [file] [log] [blame]
// 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 <string>
#include <chromeos/errors/error.h>
#include <dbus/mock_bus.h>
#include <dbus/mock_exported_object.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#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 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<string, Any>& options,
const string& error_code) {
chromeos::ErrorPtr error;
EXPECT_FALSE(service_.RegisterAsync(
&error, 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<string, Any>& options) {
chromeos::ErrorPtr error;
EXPECT_CALL(*bus_, GetExportedObject(_))
.WillOnce(Return(service_object_.get()));
EXPECT_TRUE(service_.RegisterAsync(
&error, service_id, addresses, service_info, options,
MakeMockCompletionAction()));
EXPECT_EQ(nullptr, error.get());
}
scoped_refptr<MockBus> bus_{new MockBus{Bus::Options{}}};
scoped_refptr<dbus::MockExportedObject> 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<string, Any>{{"not_valid", Any{"lies"}}},
kInvalidServiceOptions);
}
TEST_F(ServiceTest, ShouldRejectExtraMDnsOptions) {
const map<string, Any> mdns_options{
{"not_valid", Any{"lies"}},
};
const map<string, Any> options{
{kMDNSSectionName, mdns_options},
};
AssertMakeServiceFails(kValidServiceId,
MakeValidIpAddresses(),
ServiceInfo{},
options,
kInvalidServiceOptions);
}
TEST_F(ServiceTest, ShouldAcceptMDnsPort) {
const uint16_t kChosenPort = 22;
const map<string, Any> mdns_options{
{kMDNSPort, Any{kChosenPort}},
};
const map<string, Any> options{
{kMDNSSectionName, mdns_options},
};
AssertMakeServiceSuccess(kValidServiceId,
MakeValidIpAddresses(),
ServiceInfo{},
options);
EXPECT_EQ(service_.GetMDnsOptions().port, kChosenPort);
}
} // namespace peerd