text: // Copyright 2018 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.h"
  
  #include <chromeos/dbus/service_constants.h>
  #include <gtest/gtest.h>
  
  #include "shill/cellular/cellular_capability.h"
  #include "shill/cellular/mock_cellular.h"
  #include "shill/cellular/mock_mobile_operator_info.h"
  #include "shill/cellular/mock_modem_info.h"
  #include "shill/fake_store.h"
  #include "shill/mock_adaptors.h"
  #include "shill/mock_control.h"
  #include "shill/mock_manager.h"
  #include "shill/mock_metrics.h"
  #include "shill/mock_profile.h"
  #include "shill/service_property_change_test.h"
  
  using std::string;
  using testing::_;
  using testing::AnyNumber;
  using testing::Mock;
  using testing::NiceMock;
  using testing::Return;
  
  namespace shill {
  
  namespace {
  const char kImsi[] = "111222123456789";
  const char kIccid[] = "1234567890000";
  }  // namespace
  
  class CellularServiceTest : public testing::Test {
   public:
    CellularServiceTest()
        : manager_(&control_, &dispatcher_, &metrics_),
          modem_info_(&control_, &manager_),
          adaptor_(nullptr) {
      Service::SetNextSerialNumberForTesting(0);
    }
    ~CellularServiceTest() override { adaptor_ = nullptr; }
  
    void SetUp() override {
      // Many tests set service properties which call Manager.UpdateService().
      EXPECT_CALL(manager_, UpdateService(_)).Times(AnyNumber());
      device_ = new MockCellular(&modem_info_, "usb0", kAddress, 3,
                                 Cellular::kTypeCdma, "", RpcIdentifier(""));
      // CellularService expects an IMSI and SIM ID be set in the Device.
      device_->set_imsi(kImsi);
      device_->set_iccid(kIccid);
      service_ =
          new CellularService(&manager_, kImsi, kIccid, device_->GetSimCardId());
      service_->SetDevice(device_.get());
      adaptor_ = static_cast<ServiceMockAdaptor*>(service_->adaptor());
  
      storage_id_ = service_->GetStorageIdentifier();
      storage_.SetString(storage_id_, CellularService::kStorageType,
                         kTypeCellular);
      storage_.SetString(storage_id_, CellularService::kStorageIccid, kIccid);
      storage_.SetString(storage_id_, CellularService::kStorageImsi, kImsi);
    }
  
   protected:
    static const char kAddress[];
  
    string GetFriendlyName() const { return service_->friendly_name(); }
    bool IsAutoConnectable(const char** reason) const {
      return service_->IsAutoConnectable(reason);
    }
    bool SetAutoConnectFull(bool connect) {
      return service_->SetAutoConnectFull(connect, /*error=*/nullptr);
    }
  
    EventDispatcher dispatcher_;
    NiceMock<MockControl> control_;
    MockMetrics metrics_;
    NiceMock<MockManager> manager_;
    MockModemInfo modem_info_;
    scoped_refptr<MockCellular> device_;
    CellularServiceRefPtr service_;
    ServiceMockAdaptor* adaptor_;  // Owned by |service_|.
    std::string storage_id_;
    FakeStore storage_;
  };
  
  const char CellularServiceTest::kAddress[] = "000102030405";
  
  TEST_F(CellularServiceTest, Constructor) {
    EXPECT_TRUE(service_->connectable());
  }
  
  TEST_F(CellularServiceTest, SetNetworkTechnology) {
    EXPECT_CALL(*adaptor_, EmitStringChanged(kNetworkTechnologyProperty,
                                             kNetworkTechnologyUmts));
    EXPECT_TRUE(service_->network_technology().empty());
    service_->SetNetworkTechnology(kNetworkTechnologyUmts);
    EXPECT_EQ(kNetworkTechnologyUmts, service_->network_technology());
    service_->SetNetworkTechnology(kNetworkTechnologyUmts);
  }
  
  TEST_F(CellularServiceTest, LogName) {
    EXPECT_EQ("cellular_0", service_->log_name());
    service_->SetNetworkTechnology(kNetworkTechnologyUmts);
    EXPECT_EQ("cellular_UMTS_0", service_->log_name());
    service_->SetNetworkTechnology(kNetworkTechnologyGsm);
    EXPECT_EQ("cellular_GSM_0", service_->log_name());
    service_->SetNetworkTechnology(kNetworkTechnologyLte);
    EXPECT_EQ("cellular_LTE_0", service_->log_name());
  }
  
  TEST_F(CellularServiceTest, SetRoamingState) {
    EXPECT_CALL(*adaptor_,
                EmitStringChanged(kRoamingStateProperty, kRoamingStateHome));
    EXPECT_TRUE(service_->roaming_state().empty());
    service_->SetRoamingState(kRoamingStateHome);
    EXPECT_EQ(kRoamingStateHome, service_->roaming_state());
    service_->SetRoamingState(kRoamingStateHome);
  }
  
  TEST_F(CellularServiceTest, SetServingOperator) {
    static const char kCode[] = "123456";
    static const char kName[] = "Some Cellular Operator";
    Stringmap test_operator;
    service_->SetServingOperator(test_operator);
    test_operator[kOperatorCodeKey] = kCode;
    test_operator[kOperatorNameKey] = kName;
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kServingOperatorProperty, _));
    service_->SetServingOperator(test_operator);
    const Stringmap& serving_operator = service_->serving_operator();
    ASSERT_NE(serving_operator.end(), serving_operator.find(kOperatorCodeKey));
    ASSERT_NE(serving_operator.end(), serving_operator.find(kOperatorNameKey));
    EXPECT_EQ(kCode, serving_operator.find(kOperatorCodeKey)->second);
    EXPECT_EQ(kName, serving_operator.find(kOperatorNameKey)->second);
    Mock::VerifyAndClearExpectations(adaptor_);
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kServingOperatorProperty, _))
        .Times(0);
    service_->SetServingOperator(serving_operator);
  }
  
  TEST_F(CellularServiceTest, SetOLP) {
    const char kMethod[] = "GET";
    const char kURL[] = "payment.url";
    const char kPostData[] = "post_man";
    Stringmap olp;
  
    service_->SetOLP("", "", "");
    olp = service_->olp();  // Copy to simplify assertions below.
    EXPECT_EQ("", olp[kPaymentPortalURL]);
    EXPECT_EQ("", olp[kPaymentPortalMethod]);
    EXPECT_EQ("", olp[kPaymentPortalPostData]);
  
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kPaymentPortalProperty, _));
    service_->SetOLP(kURL, kMethod, kPostData);
    olp = service_->olp();  // Copy to simplify assertions below.
    EXPECT_EQ(kURL, olp[kPaymentPortalURL]);
    EXPECT_EQ(kMethod, olp[kPaymentPortalMethod]);
    EXPECT_EQ(kPostData, olp[kPaymentPortalPostData]);
  }
  
  TEST_F(CellularServiceTest, SetUsageURL) {
    static const char kUsageURL[] = "usage.url";
    EXPECT_CALL(*adaptor_, EmitStringChanged(kUsageURLProperty, kUsageURL));
    EXPECT_TRUE(service_->usage_url().empty());
    service_->SetUsageURL(kUsageURL);
    EXPECT_EQ(kUsageURL, service_->usage_url());
    service_->SetUsageURL(kUsageURL);
  }
  
  TEST_F(CellularServiceTest, SetApn) {
    static const char kApn[] = "TheAPN";
    static const char kUsername[] = "commander.data";
    ProfileRefPtr profile(new NiceMock<MockProfile>(&manager_));
    service_->set_profile(profile);
    Error error;
    Stringmap testapn;
    testapn[kApnProperty] = kApn;
    testapn[kApnUsernameProperty] = kUsername;
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularApnProperty, _));
    service_->SetApn(testapn, &error);
    EXPECT_TRUE(error.IsSuccess());
    Stringmap resultapn = service_->GetApn(&error);
    EXPECT_TRUE(error.IsSuccess());
    Stringmap::const_iterator it = resultapn.find(kApnProperty);
    EXPECT_TRUE(it != resultapn.end() && it->second == kApn);
    it = resultapn.find(kApnUsernameProperty);
    EXPECT_TRUE(it != resultapn.end() && it->second == kUsername);
    EXPECT_NE(nullptr, service_->GetUserSpecifiedApn());
  }
  
  TEST_F(CellularServiceTest, ClearApn) {
    static const char kApn[] = "TheAPN";
    static const char kUsername[] = "commander.data";
    ProfileRefPtr profile(new NiceMock<MockProfile>(&manager_));
    service_->set_profile(profile);
    Error error;
    // Set up an APN to make sure that it later gets cleared.
    Stringmap testapn;
    testapn[kApnProperty] = kApn;
    testapn[kApnUsernameProperty] = kUsername;
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularApnProperty, _));
    service_->SetApn(testapn, &error);
    Stringmap resultapn = service_->GetApn(&error);
    ASSERT_TRUE(error.IsSuccess());
  
    Stringmap emptyapn;
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularLastGoodApnProperty, _))
        .Times(0);
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularApnProperty, _))
        .Times(1);
    service_->SetApn(emptyapn, &error);
    EXPECT_TRUE(error.IsSuccess());
    resultapn = service_->GetApn(&error);
    EXPECT_TRUE(resultapn.empty());
    EXPECT_EQ(nullptr, service_->GetUserSpecifiedApn());
  }
  
  TEST_F(CellularServiceTest, LastGoodApn) {
    static const char kApn[] = "TheAPN";
    static const char kUsername[] = "commander.data";
    ProfileRefPtr profile(new NiceMock<MockProfile>(&manager_));
    service_->set_profile(profile);
    Stringmap testapn;
    testapn[kApnProperty] = kApn;
    testapn[kApnUsernameProperty] = kUsername;
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularLastGoodApnProperty, _));
    service_->SetLastGoodApn(testapn);
    Stringmap* resultapn = service_->GetLastGoodApn();
    ASSERT_NE(nullptr, resultapn);
    EXPECT_EQ(kApn, (*resultapn)[kApnProperty]);
    EXPECT_EQ(kUsername, (*resultapn)[kApnUsernameProperty]);
  
    // Now set the user-specified APN, and check that LastGoodApn is preserved.
    Stringmap userapn;
    userapn[kApnProperty] = kApn;
    userapn[kApnUsernameProperty] = kUsername;
    EXPECT_CALL(*adaptor_, EmitStringmapChanged(kCellularApnProperty, _));
    Error error;
    service_->SetApn(userapn, &error);
  
    ASSERT_NE(nullptr, service_->GetLastGoodApn());
    EXPECT_EQ(kApn, (*resultapn)[kApnProperty]);
    EXPECT_EQ(kUsername, (*resultapn)[kApnUsernameProperty]);
  }
  
  TEST_F(CellularServiceTest, IsAutoConnectable) {
    // This test assumes AutoConnect is not disabled by policy.
    EXPECT_CALL(manager_, IsTechnologyAutoConnectDisabled(_))
        .WillRepeatedly(Return(false));
  
    const char* reason = nullptr;
  
    // Auto-connect should be suppressed if the device is not running.
    device_->running_ = false;
    EXPECT_FALSE(IsAutoConnectable(&reason));
    EXPECT_STREQ(CellularService::kAutoConnDeviceDisabled, reason);
  
    device_->running_ = true;
  
    // If we're in a process of activation, don't auto-connect.
    EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
                GetActivationState(_, _))
        .WillOnce(Return(PendingActivationStore::kStatePending));
    EXPECT_FALSE(IsAutoConnectable(&reason));
    EXPECT_STREQ(CellularService::kAutoConnActivating, reason);
    EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
                GetActivationState(_, _))
        .WillRepeatedly(Return(PendingActivationStore::kStateActivated));
  
    // Auto-connect should be suppressed if we're out of credits.
    service_->NotifySubscriptionStateChanged(SubscriptionState::kOutOfCredits);
    EXPECT_FALSE(IsAutoConnectable(&reason));
    EXPECT_STREQ(CellularService::kAutoConnOutOfCredits, reason);
    service_->NotifySubscriptionStateChanged(SubscriptionState::kProvisioned);
  
    // A PPP authentication failure means the Service is not auto-connectable.
    service_->SetFailure(Service::kFailurePPPAuth);
    EXPECT_FALSE(IsAutoConnectable(&reason));
    EXPECT_STREQ(CellularService::kAutoConnBadPPPCredentials, reason);
  
    // Reset failure state, to make the Service auto-connectable again.
    service_->SetState(Service::kStateIdle);
    EXPECT_TRUE(IsAutoConnectable(&reason));
  
    // The following test cases are copied from ServiceTest.IsAutoConnectable
  
    service_->SetConnectable(true);
    EXPECT_TRUE(IsAutoConnectable(&reason));
  
    // We should not auto-connect to a Service that a user has
    // deliberately disconnected.
    Error error;
    service_->UserInitiatedDisconnect("RPC", &error);
    EXPECT_FALSE(IsAutoConnectable(&reason));
    EXPECT_STREQ(Service::kAutoConnExplicitDisconnect, reason);
  
    // If the Service is reloaded, it is eligible for auto-connect again.
    EXPECT_TRUE(service_->Load(&storage_));
    EXPECT_TRUE(IsAutoConnectable(&reason));
  
    // A non-user initiated Disconnect doesn't change anything.
    service_->Disconnect(&error, "in test");
    EXPECT_TRUE(IsAutoConnectable(&reason));
  
    // A resume also re-enables auto-connect.
    service_->UserInitiatedDisconnect("RPC", &error);
    EXPECT_FALSE(IsAutoConnectable(&reason));
    service_->OnAfterResume();
    EXPECT_TRUE(IsAutoConnectable(&reason));
  
    service_->SetState(Service::kStateConnected);
    EXPECT_FALSE(IsAutoConnectable(&reason));
    EXPECT_STREQ(Service::kAutoConnConnected, reason);
  
    service_->SetState(Service::kStateAssociating);
    EXPECT_FALSE(IsAutoConnectable(&reason));
    EXPECT_STREQ(Service::kAutoConnConnecting, reason);
  }
  
  TEST_F(CellularServiceTest, LoadResetsPPPAuthFailure) {
    const string kDefaultUser;
    const string kDefaultPass;
    const string kNewUser("new-username");
    const string kNewPass("new-password");
    for (const auto change_username : {false, true}) {
      for (const auto change_password : {false, true}) {
        service_->ppp_username_ = kDefaultUser;
        service_->ppp_password_ = kDefaultPass;
        service_->SetFailure(Service::kFailurePPPAuth);
        EXPECT_TRUE(service_->IsFailed());
        EXPECT_EQ(Service::kFailurePPPAuth, service_->failure());
        if (change_username) {
          storage_.SetString(storage_id_, CellularService::kStoragePPPUsername,
                             kNewUser);
        }
        if (change_password) {
          storage_.SetString(storage_id_, CellularService::kStoragePPPPassword,
                             kNewPass);
        }
        EXPECT_TRUE(service_->Load(&storage_));
        if (change_username || change_password) {
          EXPECT_NE(Service::kFailurePPPAuth, service_->failure());
        } else {
          EXPECT_EQ(Service::kFailurePPPAuth, service_->failure());
        }
      }
    }
  }
  
  // The default storage_identifier_ will be {kCellular}_{kIccid}, however older
  // profile/storage entries may use a different identifier. This sets up an entry
  // with a matching ICCID but an arbitrary storage id and ensures that the older
  // storage_identifer_ value is set.
  TEST_F(CellularServiceTest, LoadFromProfileMatchingIccid) {
    string initial_storage_id = storage_id_;
    string matching_storage_id = "another-storage-id";
    storage_.DeleteGroup(initial_storage_id);
    storage_.SetString(matching_storage_id, CellularService::kStorageType,
                       kTypeCellular);
    storage_.SetString(matching_storage_id, CellularService::kStorageIccid,
                       kIccid);
    storage_.SetString(matching_storage_id, CellularService::kStorageImsi, kImsi);
  
    EXPECT_TRUE(service_->IsLoadableFrom(storage_));
    EXPECT_TRUE(service_->Load(&storage_));
    EXPECT_EQ(matching_storage_id, service_->GetStorageIdentifier());
  }
  
  TEST_F(CellularServiceTest, LoadFromFirstOfMultipleMatchingProfiles) {
    string initial_storage_id = storage_id_;
    string matching_storage_ids[] = {"another-storage-id1", "another-storage-id2",
                                     "another-storage-id3"};
    storage_.DeleteGroup(initial_storage_id);
    for (auto& matching_storage_id : matching_storage_ids) {
      storage_.SetString(matching_storage_id, CellularService::kStorageType,
                         kTypeCellular);
      storage_.SetString(matching_storage_id, CellularService::kStorageIccid,
                         kIccid);
      storage_.SetString(matching_storage_id, CellularService::kStorageImsi,
                         kImsi);
    }
    EXPECT_TRUE(service_->IsLoadableFrom(storage_));
    EXPECT_TRUE(service_->Load(&storage_));
    EXPECT_EQ(matching_storage_ids[0], service_->GetStorageIdentifier());
  }
  
  TEST_F(CellularServiceTest, Save) {
    EXPECT_TRUE(service_->Save(&storage_));
    std::string iccid;
    EXPECT_TRUE(
        storage_.GetString(storage_id_, CellularService::kStorageIccid, &iccid));
    EXPECT_EQ(iccid, device_->iccid());
  }
  
  TEST_F(CellularServiceTest, SaveAndLoadApn) {
    static const char kApn[] = "petal.net";
    static const char kUsername[] = "orekid";
    static const char kPassword[] = "arlet";
    static const char kAuthentication[] = "chap";
  
    Error error;
    Stringmap testapn;
    testapn[kApnProperty] = kApn;
    testapn[kApnUsernameProperty] = kUsername;
    testapn[kApnPasswordProperty] = kPassword;
    testapn[kApnAuthenticationProperty] = kAuthentication;
    service_->SetApn(testapn, &error);
    ASSERT_TRUE(error.IsSuccess());
    EXPECT_TRUE(service_->Save(&storage_));
  
    // Clear the APN, and then load it from storage again.
    Stringmap emptyapn;
    service_->SetApn(emptyapn, &error);
    ASSERT_TRUE(error.IsSuccess());
  
    EXPECT_TRUE(service_->Load(&storage_));
  
    Stringmap resultapn = service_->GetApn(&error);
    EXPECT_TRUE(error.IsSuccess());
    EXPECT_EQ(kApn, resultapn[kApnProperty]);
    EXPECT_EQ(kUsername, resultapn[kApnUsernameProperty]);
    EXPECT_EQ(kPassword, resultapn[kApnPasswordProperty]);
    EXPECT_EQ(kAuthentication, resultapn[kApnAuthenticationProperty]);
  }
  
  TEST_F(CellularServiceTest, IgnoreUnversionedLastGoodApn) {
    static const char kApn[] = "petal.net";
    static const char kUsername[] = "orekid";
    Stringmap testapn;
    testapn[kApnProperty] = kApn;
    testapn[kApnUsernameProperty] = kUsername;
    service_->SetLastGoodApn(testapn);
    ASSERT_TRUE(service_->Save(&storage_));
  
    // Now clear the LastGoodAPN and try to load it. It should be ignored.
    service_->ClearLastGoodApn();
    ASSERT_TRUE(service_->Load(&storage_));
    Stringmap* resultapn = service_->GetLastGoodApn();
    EXPECT_EQ(nullptr, resultapn);
  }
  
  TEST_F(CellularServiceTest, MergeDetailsFromApnList) {
    static const char kApn[] = "petal.net";
    static const char kUsername[] = "orekid";
    static const char kPassword[] = "arlet";
    static const char kAuthentication[] = "chap";
    Stringmap fullapn;
    fullapn[kApnProperty] = kApn;
    fullapn[kApnUsernameProperty] = kUsername;
    fullapn[kApnPasswordProperty] = kPassword;
    fullapn[kApnAuthenticationProperty] = kAuthentication;
    Stringmaps apn_list{fullapn};
    device_->set_apn_list(apn_list);
  
    // Just set an APN with only the name. Check that we are using
    // the rest of the details.
    Error error;
    Stringmap testapn;
    testapn[kApnProperty] = kApn;
    service_->SetApn(testapn, &error);
  
    Stringmap resultapn = service_->GetApn(&error);
    EXPECT_TRUE(error.IsSuccess());
    EXPECT_EQ(kApn, resultapn[kApnProperty]);
    EXPECT_EQ(kUsername, resultapn[kApnUsernameProperty]);
    EXPECT_EQ(kPassword, resultapn[kApnPasswordProperty]);
    EXPECT_EQ(kAuthentication, resultapn[kApnAuthenticationProperty]);
  }
  
  // Some of these tests duplicate signals tested above. However, it's
  // convenient to have all the property change notifications documented
  // (and tested) in one place.
  TEST_F(CellularServiceTest, PropertyChanges) {
    TestCommonPropertyChanges(service_, adaptor_);
    TestAutoConnectPropertyChange(service_, adaptor_);
  
    EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationTypeProperty, _));
    service_->SetActivationType(CellularService::kActivationTypeOTA);
    Mock::VerifyAndClearExpectations(adaptor_);
  
    EXPECT_NE(kActivationStateNotActivated, service_->activation_state());
    EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationStateProperty, _));
    service_->SetActivationState(kActivationStateNotActivated);
    Mock::VerifyAndClearExpectations(adaptor_);
  
    string network_technology = service_->network_technology();
    EXPECT_CALL(*adaptor_, EmitStringChanged(kNetworkTechnologyProperty, _));
    service_->SetNetworkTechnology(network_technology + "and some new stuff");
    Mock::VerifyAndClearExpectations(adaptor_);
  
    EXPECT_CALL(*adaptor_, EmitBoolChanged(kOutOfCreditsProperty, true));
    service_->NotifySubscriptionStateChanged(SubscriptionState::kOutOfCredits);
    Mock::VerifyAndClearExpectations(adaptor_);
    EXPECT_CALL(*adaptor_, EmitBoolChanged(kOutOfCreditsProperty, false));
    service_->NotifySubscriptionStateChanged(SubscriptionState::kProvisioned);
    Mock::VerifyAndClearExpectations(adaptor_);
  
    string roaming_state = service_->roaming_state();
    EXPECT_CALL(*adaptor_, EmitStringChanged(kRoamingStateProperty, _));
    service_->SetRoamingState(roaming_state + "and some new stuff");
    Mock::VerifyAndClearExpectations(adaptor_);
  }
  
  // Custom property setters should return false, and make no changes, if
  // the new value is the same as the old value.
  TEST_F(CellularServiceTest, CustomSetterNoopChange) {
    // Test that we didn't break any setters provided by the base class.
    TestCustomSetterNoopChange(service_, &manager_);
  
    // Test the new setter we added.
    // First set up our environment...
    static const char kApn[] = "TheAPN";
    static const char kUsername[] = "commander.data";
    Error error;
    Stringmap testapn;
    ProfileRefPtr profile(new NiceMock<MockProfile>(nullptr));
    service_->set_profile(profile);
    testapn[kApnProperty] = kApn;
    testapn[kApnUsernameProperty] = kUsername;
    // ... then set to a known value ...
    EXPECT_TRUE(service_->SetApn(testapn, &error));
    EXPECT_TRUE(error.IsSuccess());
    // ... then set to same value.
    EXPECT_FALSE(service_->SetApn(testapn, &error));
    EXPECT_TRUE(error.IsSuccess());
  }
  
  TEST_F(CellularServiceTest, IsMeteredByDefault) {
    // These services should be metered by default.
    EXPECT_TRUE(service_->IsMetered());
  }
  
  TEST_F(CellularServiceTest, SetActivationState) {
    // SetActivationState should emit a change.
    EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationStateProperty,
                                             kActivationStateNotActivated));
    service_->SetActivationState(kActivationStateNotActivated);
    EXPECT_EQ(service_->activation_state(), kActivationStateNotActivated);
    EXPECT_CALL(*adaptor_, EmitStringChanged(kActivationStateProperty, _))
        .Times(AnyNumber());
  
    // Setting the activation state to activated should also set AutoConnect.
    EXPECT_FALSE(service_->auto_connect());
    service_->SetActivationState(kActivationStateActivated);
    EXPECT_EQ(service_->activation_state(), kActivationStateActivated);
    EXPECT_TRUE(service_->auto_connect());
  
    // After a client sets AutoConnect to false, setting the activation state to
    // activated should not set AutoConnect.
    SetAutoConnectFull(false);
    EXPECT_FALSE(service_->auto_connect());
    service_->SetActivationState(kActivationStateNotActivated);
    EXPECT_EQ(service_->activation_state(), kActivationStateNotActivated);
    EXPECT_FALSE(service_->auto_connect());
    service_->SetActivationState(kActivationStateActivated);
    EXPECT_EQ(service_->activation_state(), kActivationStateActivated);
    EXPECT_FALSE(service_->auto_connect());
  }
  
  }  // namespace shill
  