普通文本  |  456行  |  14.82 KB

//
// 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_service.h"

#include <string>

#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/mock_adaptors.h"
#include "shill/mock_connection.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/net/mock_sockets.h"
#include "shill/nice_mock_control.h"
#include "shill/service_property_change_test.h"
#include "shill/vpn/mock_vpn_driver.h"
#include "shill/vpn/mock_vpn_provider.h"

using std::string;
using testing::_;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
using testing::ReturnRefOfCopy;

namespace shill {

class VPNServiceTest : public testing::Test {
 public:
  VPNServiceTest()
      : interface_name_("test-interface"),
        driver_(new MockVPNDriver()),
        manager_(&control_, nullptr, nullptr),
        metrics_(nullptr),
        device_info_(&control_, nullptr, nullptr, nullptr),
        connection_(new NiceMock<MockConnection>(&device_info_)),
        sockets_(new MockSockets()),
        service_(new VPNService(&control_, nullptr, &metrics_, &manager_,
                                driver_)) {
    service_->sockets_.reset(sockets_);  // Passes ownership.
  }

  virtual ~VPNServiceTest() {}

 protected:
  virtual void SetUp() {
    ON_CALL(*connection_, interface_name())
        .WillByDefault(ReturnRef(interface_name_));
    ON_CALL(*connection_, ipconfig_rpc_identifier())
        .WillByDefault(ReturnRef(ipconfig_rpc_identifier_));
  }

  virtual void TearDown() {
    EXPECT_CALL(device_info_, FlushAddresses(0));
  }

  void SetServiceState(Service::ConnectState state) {
    service_->state_ = state;
  }

  void SetHasEverConnected(bool connected) {
    service_->has_ever_connected_ = connected;
  }

  void SetConnectable(bool connectable) {
    service_->connectable_ = connectable;
  }

  const char* GetAutoConnOffline() {
    return Service::kAutoConnOffline;
  }

  const char* GetAutoConnNeverConnected() {
    return VPNService::kAutoConnNeverConnected;
  }

  const char* GetAutoConnVPNAlreadyActive() {
    return VPNService::kAutoConnVPNAlreadyActive;
  }

  bool IsAutoConnectable(const char** reason) const {
    return service_->IsAutoConnectable(reason);
  }

  // Takes ownership of |provider|.
  void SetVPNProvider(VPNProvider* provider) {
    manager_.vpn_provider_.reset(provider);
    manager_.UpdateProviderMapping();
  }

  ServiceMockAdaptor* GetAdaptor() {
    return static_cast<ServiceMockAdaptor*>(service_->adaptor());
  }

  std::string interface_name_;
  std::string ipconfig_rpc_identifier_;
  MockVPNDriver* driver_;  // Owned by |service_|.
  NiceMockControl control_;
  MockManager manager_;
  MockMetrics metrics_;
  MockDeviceInfo device_info_;
  scoped_refptr<NiceMock<MockConnection>> connection_;
  MockSockets* sockets_;  // Owned by |service_|.
  VPNServiceRefPtr service_;
};

TEST_F(VPNServiceTest, Connect) {
  EXPECT_TRUE(service_->connectable());
  Error error;
  EXPECT_CALL(*driver_, Connect(_, &error));
  service_->Connect(&error, "in test");
  EXPECT_TRUE(error.IsSuccess());
}

TEST_F(VPNServiceTest, ConnectAlreadyConnected) {
  Error error;
  EXPECT_CALL(*driver_, Connect(_, _)).Times(0);
  SetServiceState(Service::kStateOnline);
  service_->Connect(&error, "in test");
  EXPECT_EQ(Error::kAlreadyConnected, error.type());
  error.Reset();
  SetServiceState(Service::kStateConfiguring);
  service_->Connect(&error, "in test");
  EXPECT_EQ(Error::kInProgress, error.type());
}

TEST_F(VPNServiceTest, Disconnect) {
  Error error;
  EXPECT_CALL(*driver_, Disconnect());
  service_->Disconnect(&error, "in test");
  EXPECT_TRUE(error.IsSuccess());
}

TEST_F(VPNServiceTest, CreateStorageIdentifierNoHost) {
  KeyValueStore args;
  Error error;
  args.SetString(kNameProperty, "vpn-name");
  EXPECT_EQ("", VPNService::CreateStorageIdentifier(args, &error));
  EXPECT_EQ(Error::kInvalidProperty, error.type());
}

TEST_F(VPNServiceTest, CreateStorageIdentifierNoName) {
  KeyValueStore args;
  Error error;
  args.SetString(kProviderHostProperty, "10.8.0.1");
  EXPECT_EQ("", VPNService::CreateStorageIdentifier(args, &error));
  EXPECT_EQ(Error::kNotSupported, error.type());
}

TEST_F(VPNServiceTest, CreateStorageIdentifier) {
  KeyValueStore args;
  Error error;
  args.SetString(kNameProperty, "vpn-name");
  args.SetString(kProviderHostProperty, "10.8.0.1");
  EXPECT_EQ("vpn_10_8_0_1_vpn_name",
            VPNService::CreateStorageIdentifier(args, &error));
  EXPECT_TRUE(error.IsSuccess());
}

TEST_F(VPNServiceTest, GetStorageIdentifier) {
  EXPECT_EQ("", service_->GetStorageIdentifier());
  service_->set_storage_id("foo");
  EXPECT_EQ("foo", service_->GetStorageIdentifier());
}

TEST_F(VPNServiceTest, GetDeviceRpcId) {
  Error error;
  EXPECT_EQ("/", service_->GetDeviceRpcId(&error));
  EXPECT_EQ(Error::kNotSupported, error.type());
}

TEST_F(VPNServiceTest, Load) {
  NiceMock<MockStore> storage;
  static const char kStorageID[] = "storage-id";
  service_->set_storage_id(kStorageID);
  EXPECT_CALL(storage, ContainsGroup(kStorageID)).WillOnce(Return(true));
  EXPECT_CALL(*driver_, Load(&storage, kStorageID))
      .WillOnce(Return(true));
  EXPECT_TRUE(service_->Load(&storage));
}

TEST_F(VPNServiceTest, Save) {
  NiceMock<MockStore> storage;
  static const char kStorageID[] = "storage-id";
  service_->set_storage_id(kStorageID);
  EXPECT_CALL(*driver_, Save(&storage, kStorageID, false))
      .WillOnce(Return(true));
  EXPECT_TRUE(service_->Save(&storage));
}

TEST_F(VPNServiceTest, SaveCredentials) {
  NiceMock<MockStore> storage;
  static const char kStorageID[] = "storage-id";
  service_->set_storage_id(kStorageID);
  service_->set_save_credentials(true);
  EXPECT_CALL(*driver_, Save(&storage, kStorageID, true))
      .WillOnce(Return(true));
  EXPECT_TRUE(service_->Save(&storage));
}

TEST_F(VPNServiceTest, Unload) {
  service_->SetAutoConnect(true);
  service_->set_save_credentials(true);
  EXPECT_CALL(*driver_, Disconnect());
  EXPECT_CALL(*driver_, UnloadCredentials());
  MockVPNProvider* provider = new MockVPNProvider;
  SetVPNProvider(provider);
  provider->services_.push_back(service_);
  service_->Unload();
  EXPECT_FALSE(service_->auto_connect());
  EXPECT_FALSE(service_->save_credentials());
  EXPECT_TRUE(provider->services_.empty());
}

TEST_F(VPNServiceTest, InitPropertyStore) {
  EXPECT_CALL(*driver_, InitPropertyStore(service_->mutable_store()));
  service_->InitDriverPropertyStore();
}

TEST_F(VPNServiceTest, EnableAndRetainAutoConnect) {
  EXPECT_FALSE(service_->retain_auto_connect());
  EXPECT_FALSE(service_->auto_connect());
  service_->EnableAndRetainAutoConnect();
  EXPECT_TRUE(service_->retain_auto_connect());
  EXPECT_FALSE(service_->auto_connect());
}

TEST_F(VPNServiceTest, SetConnection) {
  EXPECT_FALSE(service_->connection_binder_.get());
  EXPECT_FALSE(service_->connection());
  EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(-1));
  service_->SetConnection(connection_);
  ASSERT_TRUE(service_->connection_binder_.get());
  EXPECT_EQ(connection_.get(),
            service_->connection_binder_->connection().get());
  EXPECT_EQ(connection_.get(), service_->connection().get());
  EXPECT_CALL(*driver_, OnConnectionDisconnected()).Times(0);
}

TEST_F(VPNServiceTest, OnConnectionDisconnected) {
  EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(-1));
  service_->SetConnection(connection_);
  EXPECT_CALL(*driver_, OnConnectionDisconnected()).Times(1);
  connection_->OnLowerDisconnect();
}

TEST_F(VPNServiceTest, IsAutoConnectableOffline) {
  EXPECT_TRUE(service_->connectable());
  const char* reason = nullptr;
  EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(false));
  EXPECT_FALSE(IsAutoConnectable(&reason));
  EXPECT_STREQ(GetAutoConnOffline(), reason);
}

TEST_F(VPNServiceTest, IsAutoConnectableNeverConnected) {
  EXPECT_TRUE(service_->connectable());
  EXPECT_FALSE(service_->has_ever_connected());
  const char* reason = nullptr;
  EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(true));
  EXPECT_FALSE(IsAutoConnectable(&reason));
  EXPECT_STREQ(GetAutoConnNeverConnected(), reason);
}

TEST_F(VPNServiceTest, IsAutoConnectableVPNAlreadyActive) {
  EXPECT_TRUE(service_->connectable());
  SetHasEverConnected(true);
  EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(true));
  MockVPNProvider* provider = new MockVPNProvider;
  SetVPNProvider(provider);
  EXPECT_CALL(*provider, HasActiveService()).WillOnce(Return(true));
  const char* reason = nullptr;
  EXPECT_FALSE(IsAutoConnectable(&reason));
  EXPECT_STREQ(GetAutoConnVPNAlreadyActive(), reason);
}

TEST_F(VPNServiceTest, IsAutoConnectableNotConnectable) {
  const char* reason = nullptr;
  SetConnectable(false);
  EXPECT_FALSE(IsAutoConnectable(&reason));
}

TEST_F(VPNServiceTest, IsAutoConnectable) {
  EXPECT_TRUE(service_->connectable());
  SetHasEverConnected(true);
  EXPECT_CALL(manager_, IsConnected()).WillOnce(Return(true));
  MockVPNProvider* provider = new MockVPNProvider;
  SetVPNProvider(provider);
  EXPECT_CALL(*provider, HasActiveService()).WillOnce(Return(false));
  const char* reason = nullptr;
  EXPECT_TRUE(IsAutoConnectable(&reason));
  EXPECT_FALSE(reason);
}

TEST_F(VPNServiceTest, SetNamePropertyTrivial) {
  Error error;
  // A null change returns false, but with error set to success.
  EXPECT_FALSE(service_->mutable_store()->SetAnyProperty(
      kNameProperty, brillo::Any(service_->friendly_name()), &error));
  EXPECT_FALSE(error.IsFailure());
}

TEST_F(VPNServiceTest, SetNameProperty) {
  const string kHost = "1.2.3.4";
  driver_->args()->SetString(kProviderHostProperty, kHost);
  string kOldId = service_->GetStorageIdentifier();
  Error error;
  const string kName = "New Name";
  scoped_refptr<MockProfile> profile(
      new MockProfile(&control_, &metrics_, &manager_));
  EXPECT_CALL(*profile, DeleteEntry(kOldId, _));
  EXPECT_CALL(*profile, UpdateService(_));
  service_->set_profile(profile);
  EXPECT_TRUE(service_->mutable_store()->SetAnyProperty(
      kNameProperty, brillo::Any(kName), &error));
  EXPECT_NE(service_->GetStorageIdentifier(), kOldId);
  EXPECT_EQ(kName, service_->friendly_name());
}

TEST_F(VPNServiceTest, PropertyChanges) {
  TestCommonPropertyChanges(service_, GetAdaptor());
  TestAutoConnectPropertyChange(service_, GetAdaptor());

  const string kHost = "1.2.3.4";
  scoped_refptr<MockProfile> profile(
      new NiceMock<MockProfile>(&control_, &metrics_, &manager_));
  service_->set_profile(profile);
  driver_->args()->SetString(kProviderHostProperty, kHost);
  TestNamePropertyChange(service_, GetAdaptor());
}

// Custom property setters should return false, and make no changes, if
// the new value is the same as the old value.
TEST_F(VPNServiceTest, CustomSetterNoopChange) {
  TestCustomSetterNoopChange(service_, &manager_);
}

TEST_F(VPNServiceTest, GetPhysicalTechnologyPropertyFailsIfNoCarrier) {
  scoped_refptr<Connection> null_connection;

  EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(-1));
  service_->SetConnection(connection_);
  EXPECT_EQ(connection_.get(), service_->connection().get());

  // Simulate an error in the GetCarrierConnection() returning a NULL reference.
  EXPECT_CALL(*connection_, GetCarrierConnection())
      .WillOnce(Return(null_connection));

  Error error;
  EXPECT_EQ("", service_->GetPhysicalTechnologyProperty(&error));
  EXPECT_EQ(Error::kOperationFailed, error.type());
}

TEST_F(VPNServiceTest, GetPhysicalTechnologyPropertyOverWifi) {
  scoped_refptr<NiceMock<MockConnection>> lower_connection_ =
      new NiceMock<MockConnection>(&device_info_);

  EXPECT_CALL(*connection_, technology())
      .Times(0);
  EXPECT_CALL(*connection_, GetCarrierConnection())
      .WillOnce(Return(lower_connection_));

  EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(-1));
  service_->SetConnection(connection_);
  EXPECT_EQ(connection_.get(), service_->connection().get());

  // Set the type of the lower connection to "wifi" and expect that type to be
  // returned by GetPhysicalTechnologyProperty().
  EXPECT_CALL(*lower_connection_, technology())
      .WillOnce(Return(Technology::kWifi));

  Error error;
  EXPECT_EQ(kTypeWifi, service_->GetPhysicalTechnologyProperty(&error));
  EXPECT_TRUE(error.IsSuccess());

  // Clear expectations now, so the Return(lower_connection_) action releases
  // the reference to |lower_connection_| allowing it to be destroyed now.
  Mock::VerifyAndClearExpectations(connection_.get());
  // Destroying the |lower_connection_| at function exit will also call an extra
  // FlushAddresses on the |device_info_| object.
  EXPECT_CALL(device_info_, FlushAddresses(0));
}

TEST_F(VPNServiceTest, GetTethering) {
  scoped_refptr<Connection> null_connection;

  EXPECT_CALL(*sockets_, Socket(_, _, _)).WillOnce(Return(-1));
  service_->SetConnection(connection_);
  EXPECT_EQ(connection_.get(), service_->connection().get());

  // Simulate an error in the GetCarrierConnection() returning a NULL reference.
  EXPECT_CALL(*connection_, GetCarrierConnection())
      .WillOnce(Return(null_connection));

  {
    Error error;
    EXPECT_EQ("", service_->GetTethering(&error));
    EXPECT_EQ(Error::kOperationFailed, error.type());
  }

  scoped_refptr<NiceMock<MockConnection>> lower_connection_ =
      new NiceMock<MockConnection>(&device_info_);

  EXPECT_CALL(*connection_, tethering()).Times(0);
  EXPECT_CALL(*connection_, GetCarrierConnection())
      .WillRepeatedly(Return(lower_connection_));

  const char kTethering[] = "moon unit";
  EXPECT_CALL(*lower_connection_, tethering())
      .WillOnce(ReturnRefOfCopy(string(kTethering)))
      .WillOnce(ReturnRefOfCopy(string()));

  {
    Error error;
    EXPECT_EQ(kTethering, service_->GetTethering(&error));
    EXPECT_TRUE(error.IsSuccess());
  }
  {
    Error error;
    EXPECT_EQ("", service_->GetTethering(&error));
    EXPECT_EQ(Error::kNotSupported, error.type());
  }

  // Clear expectations now, so the Return(lower_connection_) action releases
  // the reference to |lower_connection_| allowing it to be destroyed now.
  Mock::VerifyAndClearExpectations(connection_.get());
  // Destroying the |lower_connection_| at function exit will also call an extra
  // FlushAddresses on the |device_info_| object.
  EXPECT_CALL(device_info_, FlushAddresses(0));
}

}  // namespace shill