//
// 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