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