blob: 2dd7f453c54759d93796b5a8bd25279715695806 [file] [log] [blame]
// Copyright 2020 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 "shill/cellular/cellular_service_provider.h"
#include <set>
#include <gtest/gtest.h>
#include "shill/cellular/cellular.h"
#include "shill/cellular/mock_modem_info.h"
#include "shill/fake_store.h"
#include "shill/mock_control.h"
#include "shill/mock_device_info.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_profile.h"
#include "shill/test_event_dispatcher.h"
using testing::NiceMock;
using testing::Return;
namespace shill {
namespace {
const char kTestDeviceName[] = "usb0";
const char kTestDeviceAddress[] = "000102030405";
const int kTestInterfaceIndex = 1;
const char kDBusService[] = "org.freedesktop.ModemManager1";
const RpcIdentifier kDBusPath("/org/freedesktop/ModemManager1/Modem/0");
} // namespace
class CellularServiceProviderTest : public testing::Test {
public:
CellularServiceProviderTest()
: manager_(&control_, &dispatcher_, &metrics_),
modem_info_(&control_, &manager_),
device_info_(&manager_),
profile_(new NiceMock<MockProfile>(&manager_)),
provider_(&manager_) {}
~CellularServiceProviderTest() override = default;
void SetUp() override {
provider_.Start();
provider_.set_profile_for_testing(profile_);
EXPECT_CALL(*profile_, GetConstStorage()).WillRepeatedly(Return(&storage_));
EXPECT_CALL(*profile_, GetStorage()).WillRepeatedly(Return(&storage_));
}
void TearDown() override { provider_.Stop(); }
// TODO(b/154014577): Provide eID for identifying sim cards once supported.
CellularRefPtr CreateDevice(const std::string& imsi,
const std::string& iccid) {
CellularRefPtr cellular = new Cellular(
&modem_info_, kTestDeviceName, kTestDeviceAddress, kTestInterfaceIndex,
Cellular::kType3gpp, kDBusService, kDBusPath);
cellular->CreateCapability(&modem_info_);
cellular->set_imsi(imsi);
cellular->set_iccid(iccid);
return cellular;
}
CellularRefPtr CreateDeviceWithEid(const std::string& imsi,
const std::string& iccid,
const std::string& eid) {
CellularRefPtr cellular = CreateDevice(imsi, iccid);
cellular->set_eid_for_testing(eid);
return cellular;
}
// TODO(b/154014577): Provide eID once supported.
void SetupCellularStore(const std::string& identifier,
const std::string& imsi,
const std::string& iccid,
const std::string& sim_card_id) {
storage_.SetString(identifier, kTypeProperty, kTypeCellular);
storage_.SetString(identifier, CellularService::kStorageImsi, imsi);
storage_.SetString(identifier, CellularService::kStorageIccid, iccid);
storage_.SetString(identifier, CellularService::kStorageSimCardId,
sim_card_id);
}
void StoreCellularProperty(const std::string& identifier,
const std::string& key,
const std::string& value) {
storage_.SetString(identifier, key, value);
}
std::set<std::string> GetStorageGroups() { return storage_.GetGroups(); }
const std::vector<CellularServiceRefPtr>& GetProviderServices() const {
return provider_.services_;
}
CellularServiceProvider* provider() { return &provider_; }
private:
EventDispatcherForTest dispatcher_;
NiceMock<MockControl> control_;
NiceMock<MockMetrics> metrics_;
MockManager manager_;
MockModemInfo modem_info_;
NiceMock<MockDeviceInfo> device_info_;
FakeStore storage_;
scoped_refptr<NiceMock<MockProfile>> profile_;
CellularServiceProvider provider_;
};
TEST_F(CellularServiceProviderTest, LoadService) {
CellularRefPtr device = CreateDevice("imsi1", "iccid1");
CellularServiceRefPtr service =
provider()->LoadServicesForDevice(device.get());
ASSERT_TRUE(service);
EXPECT_EQ(1u, GetProviderServices().size());
EXPECT_EQ("imsi1", service->imsi());
EXPECT_EQ("iccid1", service->iccid());
EXPECT_EQ("iccid1", service->sim_card_id());
EXPECT_TRUE(service->IsVisible());
// RemoveServicesForDevice does not destroy the services, but they should no
// longer be marked as visible.
provider()->RemoveServicesForDevice(device.get());
EXPECT_EQ(1u, GetProviderServices().size());
EXPECT_FALSE(service->IsVisible());
// Stopping should remove all services.
provider()->Stop();
EXPECT_EQ(0u, GetProviderServices().size());
}
TEST_F(CellularServiceProviderTest, LoadServiceFromProfile) {
CellularRefPtr device = CreateDevice("imsi1", "iccid1");
std::string identifier = device->GetStorageIdentifier();
// Add an entry in the storage with a saved property (ppp_username).
SetupCellularStore(identifier, "imsi1", "iccid1", "iccid1");
StoreCellularProperty(identifier, CellularService::kStoragePPPUsername,
"user1");
// Ensure that the service is loaded from storage.
CellularServiceRefPtr service =
provider()->LoadServicesForDevice(device.get());
ASSERT_TRUE(service);
EXPECT_EQ("imsi1", service->imsi());
EXPECT_EQ("iccid1", service->iccid());
EXPECT_EQ("user1", service->ppp_username());
}
TEST_F(CellularServiceProviderTest, LoadMultipleServicesFromProfile) {
// Set up two cellular services with the same SIM Card Id.
SetupCellularStore("cellular_1a", "imsi1a", "iccid1a", "eid1");
SetupCellularStore("cellular_1b", "imsi1b", "iccid1b", "eid1");
// Set up a third cellular service with a different SIM Card Id.
SetupCellularStore("cellular_2", "imsi2", "iccid2", "eid2");
CellularRefPtr device = CreateDeviceWithEid("imsi1a", "iccid1a", "eid1");
CellularServiceRefPtr service =
provider()->LoadServicesForDevice(device.get());
ASSERT_TRUE(service);
// cellular_1a should be returned.
EXPECT_EQ("imsi1a", service->imsi());
EXPECT_EQ("iccid1a", service->iccid());
// Both cellular_1a and cellular_1b services should be created.
const std::vector<CellularServiceRefPtr>& provider_services =
GetProviderServices();
ASSERT_EQ(2u, provider_services.size());
CellularServiceRefPtr service1a = provider_services[0];
EXPECT_EQ("iccid1a", service1a->iccid());
EXPECT_TRUE(service1a->connectable());
CellularServiceRefPtr service1b = provider_services[1];
EXPECT_EQ("iccid1b", service1b->iccid());
EXPECT_FALSE(service1b->connectable());
}
// When a SIM or eSIM is switched the Cellular Device will be rebuilt,
// generating a new call to LoadServicesForDevice with a different ICCID. This
// should remove services with the previous ICCID.
TEST_F(CellularServiceProviderTest, SwitchDeviceIccid) {
CellularRefPtr device = CreateDevice("imsi1", "iccid1");
CellularServiceRefPtr service =
provider()->LoadServicesForDevice(device.get());
ASSERT_TRUE(service);
EXPECT_EQ("imsi1", service->imsi());
EXPECT_EQ(1u, GetProviderServices().size());
unsigned int serial_number1 = service->serial_number();
// Removing services for the device does not destroy the services, but they
// should no longer be marked as visible.
provider()->RemoveServicesForDevice(device.get());
EXPECT_EQ(1u, GetProviderServices().size());
EXPECT_FALSE(service->IsVisible());
// Adding a device with a new ICCID should create a new service with a
// different serial number.
device = CreateDevice("imsi2", "iccid2");
service = provider()->LoadServicesForDevice(device.get());
ASSERT_TRUE(service);
EXPECT_EQ("imsi2", service->imsi());
EXPECT_EQ(1u, GetProviderServices().size());
EXPECT_NE(serial_number1, service->serial_number());
// Stopping should remove all services.
provider()->Stop();
EXPECT_EQ(0u, GetProviderServices().size());
}
TEST_F(CellularServiceProviderTest, RemoveObsoleteServiceFromProfile) {
CellularRefPtr device = CreateDevice("imsi1", "iccid1");
std::string identifier = device->GetStorageIdentifier();
// Add two entries in the storage with the same ICCID, one with an empty IMSI.
// Set a property on both.
SetupCellularStore(identifier, "", "iccid1", "iccid1");
StoreCellularProperty(identifier, CellularService::kStoragePPPUsername,
"user1");
SetupCellularStore(identifier, "imsi1", "iccid1", "iccid1");
StoreCellularProperty(identifier, CellularService::kStoragePPPUsername,
"user2");
// Ensure that the service with a non empty imsi loaded from storage.
CellularServiceRefPtr service =
provider()->LoadServicesForDevice(device.get());
ASSERT_TRUE(service);
EXPECT_EQ("imsi1", service->imsi());
EXPECT_EQ("iccid1", service->iccid());
EXPECT_EQ("user2", service->ppp_username());
// Only one provider service should exist.
EXPECT_EQ(1u, GetProviderServices().size());
}
} // namespace shill