407 lines
14 KiB
C++
407 lines
14 KiB
C++
//
|
|
// Copyright (C) 2012 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#include "shill/vpn/vpn_provider.h"
|
|
|
|
#include <memory>
|
|
#include <set>
|
|
|
|
#if defined(__ANDROID__)
|
|
#include <dbus/service_constants.h>
|
|
#else
|
|
#include <chromeos/dbus/service_constants.h>
|
|
#endif // __ANDROID__
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "shill/error.h"
|
|
#include "shill/fake_store.h"
|
|
#include "shill/mock_adaptors.h"
|
|
#include "shill/mock_device_info.h"
|
|
#include "shill/mock_manager.h"
|
|
#include "shill/mock_metrics.h"
|
|
#include "shill/mock_profile.h"
|
|
#include "shill/mock_store.h"
|
|
#include "shill/nice_mock_control.h"
|
|
#include "shill/vpn/mock_vpn_driver.h"
|
|
#include "shill/vpn/mock_vpn_service.h"
|
|
|
|
using std::string;
|
|
using std::unique_ptr;
|
|
using testing::_;
|
|
using testing::DoAll;
|
|
using testing::NiceMock;
|
|
using testing::Return;
|
|
using testing::SetArgumentPointee;
|
|
using testing::StartsWith;
|
|
|
|
namespace shill {
|
|
|
|
class VPNProviderTest : public testing::Test {
|
|
public:
|
|
VPNProviderTest()
|
|
: metrics_(nullptr),
|
|
manager_(&control_, nullptr, &metrics_),
|
|
device_info_(&control_, nullptr, &metrics_, &manager_),
|
|
provider_(&control_, nullptr, &metrics_, &manager_) {}
|
|
|
|
virtual ~VPNProviderTest() {}
|
|
|
|
protected:
|
|
static const char kHost[];
|
|
static const char kName[];
|
|
|
|
string GetServiceFriendlyName(const ServiceRefPtr& service) {
|
|
return service->friendly_name();
|
|
}
|
|
|
|
void SetConnectState(const ServiceRefPtr& service,
|
|
Service::ConnectState state) {
|
|
service->state_ = state;
|
|
}
|
|
|
|
void AddService(const VPNServiceRefPtr& service) {
|
|
provider_.services_.push_back(service);
|
|
}
|
|
|
|
VPNServiceRefPtr GetServiceAt(int idx) {
|
|
return provider_.services_[idx];
|
|
}
|
|
|
|
size_t GetServiceCount() const {
|
|
return provider_.services_.size();
|
|
}
|
|
|
|
NiceMockControl control_;
|
|
MockMetrics metrics_;
|
|
MockManager manager_;
|
|
MockDeviceInfo device_info_;
|
|
VPNProvider provider_;
|
|
};
|
|
|
|
const char VPNProviderTest::kHost[] = "10.8.0.1";
|
|
const char VPNProviderTest::kName[] = "vpn-name";
|
|
|
|
TEST_F(VPNProviderTest, GetServiceNoType) {
|
|
KeyValueStore args;
|
|
Error e;
|
|
args.SetString(kTypeProperty, kTypeVPN);
|
|
ServiceRefPtr service = provider_.GetService(args, &e);
|
|
EXPECT_EQ(Error::kNotSupported, e.type());
|
|
EXPECT_FALSE(service);
|
|
}
|
|
|
|
TEST_F(VPNProviderTest, GetServiceUnsupportedType) {
|
|
KeyValueStore args;
|
|
Error e;
|
|
args.SetString(kTypeProperty, kTypeVPN);
|
|
args.SetString(kProviderTypeProperty, "unknown-vpn-type");
|
|
args.SetString(kProviderHostProperty, kHost);
|
|
args.SetString(kNameProperty, kName);
|
|
ServiceRefPtr service = provider_.GetService(args, &e);
|
|
EXPECT_EQ(Error::kNotSupported, e.type());
|
|
EXPECT_FALSE(service);
|
|
}
|
|
|
|
TEST_F(VPNProviderTest, GetService) {
|
|
KeyValueStore args;
|
|
args.SetString(kTypeProperty, kTypeVPN);
|
|
args.SetString(kProviderTypeProperty, kProviderOpenVpn);
|
|
args.SetString(kProviderHostProperty, kHost);
|
|
args.SetString(kNameProperty, kName);
|
|
|
|
{
|
|
Error error;
|
|
ServiceRefPtr service = provider_.FindSimilarService(args, &error);
|
|
EXPECT_EQ(Error::kNotFound, error.type());
|
|
EXPECT_EQ(nullptr, service.get());
|
|
}
|
|
|
|
EXPECT_EQ(0, GetServiceCount());
|
|
|
|
ServiceRefPtr service;
|
|
{
|
|
Error error;
|
|
EXPECT_CALL(manager_, device_info()).WillOnce(Return(&device_info_));
|
|
EXPECT_CALL(manager_, RegisterService(_));
|
|
service = provider_.GetService(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
ASSERT_TRUE(service);
|
|
testing::Mock::VerifyAndClearExpectations(&manager_);
|
|
}
|
|
|
|
EXPECT_EQ("vpn_10_8_0_1_vpn_name", service->GetStorageIdentifier());
|
|
EXPECT_EQ(kName, GetServiceFriendlyName(service));
|
|
|
|
EXPECT_EQ(1, GetServiceCount());
|
|
|
|
// Configure the service to set its properties (including Provider.Host).
|
|
{
|
|
Error error;
|
|
service->Configure(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
}
|
|
|
|
// None of the calls below should cause a new service to be registered.
|
|
EXPECT_CALL(manager_, RegisterService(_)).Times(0);
|
|
|
|
// A second call should return the same service.
|
|
{
|
|
Error error;
|
|
ServiceRefPtr get_service = provider_.GetService(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
ASSERT_EQ(service, get_service);
|
|
}
|
|
|
|
EXPECT_EQ(1, GetServiceCount());
|
|
|
|
// FindSimilarService should also return this service.
|
|
{
|
|
Error error;
|
|
ServiceRefPtr similar_service = provider_.FindSimilarService(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_EQ(service, similar_service);
|
|
}
|
|
|
|
EXPECT_EQ(1, GetServiceCount());
|
|
|
|
// However, CreateTemporaryService should create a different service.
|
|
{
|
|
Error error;
|
|
ServiceRefPtr temporary_service =
|
|
provider_.CreateTemporaryService(args, &error);
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
EXPECT_NE(service, temporary_service);
|
|
|
|
// However this service will not be part of the provider.
|
|
EXPECT_EQ(1, GetServiceCount());
|
|
}
|
|
}
|
|
|
|
TEST_F(VPNProviderTest, OnDeviceInfoAvailable) {
|
|
const string kInterfaceName("tun0");
|
|
const int kInterfaceIndex = 1;
|
|
|
|
unique_ptr<MockVPNDriver> bad_driver(new MockVPNDriver());
|
|
EXPECT_CALL(*bad_driver.get(), ClaimInterface(_, _))
|
|
.Times(2)
|
|
.WillRepeatedly(Return(false));
|
|
provider_.services_.push_back(new VPNService(&control_, nullptr, &metrics_,
|
|
nullptr, bad_driver.release()));
|
|
|
|
EXPECT_FALSE(provider_.OnDeviceInfoAvailable(kInterfaceName,
|
|
kInterfaceIndex));
|
|
|
|
unique_ptr<MockVPNDriver> good_driver(new MockVPNDriver());
|
|
EXPECT_CALL(*good_driver.get(), ClaimInterface(_, _))
|
|
.WillOnce(Return(true));
|
|
provider_.services_.push_back(new VPNService(&control_, nullptr, &metrics_,
|
|
nullptr, good_driver.release()));
|
|
|
|
unique_ptr<MockVPNDriver> dup_driver(new MockVPNDriver());
|
|
EXPECT_CALL(*dup_driver.get(), ClaimInterface(_, _))
|
|
.Times(0);
|
|
provider_.services_.push_back(new VPNService(&control_, nullptr, &metrics_,
|
|
nullptr, dup_driver.release()));
|
|
|
|
EXPECT_TRUE(provider_.OnDeviceInfoAvailable(kInterfaceName, kInterfaceIndex));
|
|
provider_.services_.clear();
|
|
}
|
|
|
|
TEST_F(VPNProviderTest, RemoveService) {
|
|
scoped_refptr<MockVPNService> service0(
|
|
new MockVPNService(&control_, nullptr, &metrics_, nullptr, nullptr));
|
|
scoped_refptr<MockVPNService> service1(
|
|
new MockVPNService(&control_, nullptr, &metrics_, nullptr, nullptr));
|
|
scoped_refptr<MockVPNService> service2(
|
|
new MockVPNService(&control_, nullptr, &metrics_, nullptr, nullptr));
|
|
|
|
provider_.services_.push_back(service0.get());
|
|
provider_.services_.push_back(service1.get());
|
|
provider_.services_.push_back(service2.get());
|
|
|
|
ASSERT_EQ(3, provider_.services_.size());
|
|
|
|
provider_.RemoveService(service1);
|
|
|
|
EXPECT_EQ(2, provider_.services_.size());
|
|
EXPECT_EQ(service0, provider_.services_[0]);
|
|
EXPECT_EQ(service2, provider_.services_[1]);
|
|
|
|
provider_.RemoveService(service2);
|
|
|
|
EXPECT_EQ(1, provider_.services_.size());
|
|
EXPECT_EQ(service0, provider_.services_[0]);
|
|
|
|
provider_.RemoveService(service0);
|
|
EXPECT_EQ(0, provider_.services_.size());
|
|
}
|
|
|
|
MATCHER_P(ServiceWithStorageId, storage_id, "") {
|
|
return arg->GetStorageIdentifier() == storage_id;
|
|
}
|
|
|
|
TEST_F(VPNProviderTest, CreateServicesFromProfile) {
|
|
FakeStore storage;
|
|
storage.SetString("no_type", "Name", "No Type Entry");
|
|
storage.SetString("no_vpn", "Type", "wimax");
|
|
storage.SetString("vpn_no_provider_type", "Type", "vpn");
|
|
storage.SetString("vpn_no_name", "Type", "vpn");
|
|
storage.SetString("vpn_no_name", "Provider.Type", "openvpn");
|
|
storage.SetString("vpn_no_host", "Type", "vpn");
|
|
storage.SetString("vpn_no_host", "Provider.Type", "openvpn");
|
|
storage.SetString("vpn_ho_host", "Name", "name");
|
|
storage.SetString("vpn_complete", "Type", "vpn");
|
|
storage.SetString("vpn_complete", "Provider.Type", "openvpn");
|
|
storage.SetString("vpn_complete", "Name", "name");
|
|
storage.SetString("vpn_complete", "Provider.Host", "1.2.3.4");
|
|
|
|
scoped_refptr<MockProfile> profile(
|
|
new NiceMock<MockProfile>(&control_, &metrics_, &manager_, ""));
|
|
EXPECT_CALL(*profile, GetConstStorage()).WillRepeatedly(Return(&storage));
|
|
|
|
EXPECT_CALL(manager_, device_info()).WillRepeatedly(Return(nullptr));
|
|
EXPECT_CALL(manager_,
|
|
RegisterService(ServiceWithStorageId("vpn_complete")));
|
|
EXPECT_CALL(*profile,
|
|
ConfigureService(ServiceWithStorageId("vpn_complete")))
|
|
.WillOnce(Return(true));
|
|
provider_.CreateServicesFromProfile(profile);
|
|
|
|
GetServiceAt(0)->driver()->args()->SetString(kProviderHostProperty,
|
|
"1.2.3.4");
|
|
// Calling this again should not create any more services (checked by the
|
|
// Times(1) above).
|
|
provider_.CreateServicesFromProfile(profile);
|
|
}
|
|
|
|
TEST_F(VPNProviderTest, CreateService) {
|
|
static const char kName[] = "test-vpn-service";
|
|
static const char kStorageID[] = "test_vpn_storage_id";
|
|
static const char* kTypes[] = {
|
|
kProviderOpenVpn,
|
|
kProviderL2tpIpsec,
|
|
kProviderThirdPartyVpn
|
|
};
|
|
const size_t kTypesCount = arraysize(kTypes);
|
|
EXPECT_CALL(manager_, device_info())
|
|
.Times(kTypesCount)
|
|
.WillRepeatedly(Return(&device_info_));
|
|
EXPECT_CALL(manager_, RegisterService(_)).Times(kTypesCount);
|
|
for (size_t i = 0; i < kTypesCount; i++) {
|
|
Error error;
|
|
VPNServiceRefPtr service =
|
|
provider_.CreateService(kTypes[i], kName, kStorageID, &error);
|
|
ASSERT_TRUE(service) << kTypes[i];
|
|
ASSERT_TRUE(service->driver()) << kTypes[i];
|
|
EXPECT_EQ(kTypes[i], service->driver()->GetProviderType());
|
|
EXPECT_EQ(kName, GetServiceFriendlyName(service)) << kTypes[i];
|
|
EXPECT_EQ(kStorageID, service->GetStorageIdentifier()) << kTypes[i];
|
|
EXPECT_TRUE(error.IsSuccess()) << kTypes[i];
|
|
}
|
|
Error error;
|
|
VPNServiceRefPtr unknown_service =
|
|
provider_.CreateService("unknown-vpn-type", kName, kStorageID, &error);
|
|
EXPECT_FALSE(unknown_service);
|
|
EXPECT_EQ(Error::kNotSupported, error.type());
|
|
}
|
|
|
|
TEST_F(VPNProviderTest, CreateTemporaryServiceFromProfile) {
|
|
FakeStore storage;
|
|
storage.SetString("no_vpn", "Type", "wimax");
|
|
storage.SetString("vpn_no_provider_type", "Type", "vpn");
|
|
storage.SetString("vpn_no_name", "Type", "vpn");
|
|
storage.SetString("vpn_no_name", "Provider.Type", "openvpn");
|
|
storage.SetString("vpn_no_host", "Type", "vpn");
|
|
storage.SetString("vpn_no_host", "Provider.Type", "openvpn");
|
|
storage.SetString("vpn_no_host", "Name", "name");
|
|
storage.SetString("vpn_complete", "Type", "vpn");
|
|
storage.SetString("vpn_complete", "Provider.Type", "openvpn");
|
|
storage.SetString("vpn_complete", "Name", "name");
|
|
storage.SetString("vpn_complete", "Provider.Host", "1.2.3.4");
|
|
|
|
scoped_refptr<MockProfile> profile(
|
|
new NiceMock<MockProfile>(&control_, &metrics_, &manager_, ""));
|
|
EXPECT_CALL(*profile, GetConstStorage()).WillRepeatedly(Return(&storage));
|
|
Error error;
|
|
|
|
// Non VPN entry.
|
|
EXPECT_EQ(nullptr,
|
|
provider_.CreateTemporaryServiceFromProfile(profile,
|
|
"no_vpn",
|
|
&error));
|
|
EXPECT_FALSE(error.IsSuccess());
|
|
EXPECT_THAT(error.message(),
|
|
StartsWith("Unspecified or invalid network type"));
|
|
|
|
// VPN type not specified.
|
|
error.Reset();
|
|
EXPECT_EQ(nullptr,
|
|
provider_.CreateTemporaryServiceFromProfile(profile,
|
|
"vpn_no_provider_type",
|
|
&error));
|
|
EXPECT_FALSE(error.IsSuccess());
|
|
EXPECT_THAT(error.message(), StartsWith("VPN type not specified"));
|
|
|
|
// Name not specified.
|
|
error.Reset();
|
|
EXPECT_EQ(nullptr,
|
|
provider_.CreateTemporaryServiceFromProfile(profile,
|
|
"vpn_no_name",
|
|
&error));
|
|
EXPECT_FALSE(error.IsSuccess());
|
|
EXPECT_THAT(error.message(), StartsWith("Network name not specified"));
|
|
|
|
// Host not specified.
|
|
error.Reset();
|
|
EXPECT_EQ(nullptr,
|
|
provider_.CreateTemporaryServiceFromProfile(profile,
|
|
"vpn_no_host",
|
|
&error));
|
|
EXPECT_FALSE(error.IsSuccess());
|
|
EXPECT_THAT(error.message(), StartsWith("Host not specified"));
|
|
|
|
// Valid VPN service entry.
|
|
error.Reset();
|
|
EXPECT_NE(nullptr,
|
|
provider_.CreateTemporaryServiceFromProfile(profile,
|
|
"vpn_complete",
|
|
&error));
|
|
EXPECT_TRUE(error.IsSuccess());
|
|
}
|
|
|
|
TEST_F(VPNProviderTest, HasActiveService) {
|
|
EXPECT_FALSE(provider_.HasActiveService());
|
|
|
|
scoped_refptr<MockVPNService> service0(
|
|
new MockVPNService(&control_, nullptr, &metrics_, nullptr, nullptr));
|
|
scoped_refptr<MockVPNService> service1(
|
|
new MockVPNService(&control_, nullptr, &metrics_, nullptr, nullptr));
|
|
scoped_refptr<MockVPNService> service2(
|
|
new MockVPNService(&control_, nullptr, &metrics_, nullptr, nullptr));
|
|
|
|
AddService(service0);
|
|
AddService(service1);
|
|
AddService(service2);
|
|
EXPECT_FALSE(provider_.HasActiveService());
|
|
|
|
SetConnectState(service1, Service::kStateAssociating);
|
|
EXPECT_TRUE(provider_.HasActiveService());
|
|
|
|
SetConnectState(service1, Service::kStateOnline);
|
|
EXPECT_TRUE(provider_.HasActiveService());
|
|
}
|
|
|
|
} // namespace shill
|