普通文本  |  1987行  |  79.4 KB

//
// Copyright (C) 2013 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/wifi/wifi_provider.h"

#include <map>
#include <set>
#include <string>
#include <vector>

#include <base/format_macros.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#if defined(__ANDROID__)
#include <dbus/service_constants.h>
#else
#include <chromeos/dbus/service_constants.h>
#endif  // __ANDROID__
#include <gtest/gtest.h>

#include "shill/mock_event_dispatcher.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/mock_profile.h"
#include "shill/mock_store.h"
#include "shill/net/ieee80211.h"
#include "shill/net/mock_time.h"
#include "shill/nice_mock_control.h"
#include "shill/supplicant/wpa_supplicant.h"
#include "shill/technology.h"
#include "shill/wifi/mock_wifi_service.h"
#include "shill/wifi/wifi_endpoint.h"

using base::StringPrintf;
using std::map;
using std::set;
using std::string;
using std::vector;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::ContainerEq;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgumentPointee;
using ::testing::StartsWith;
using ::testing::StrictMock;

namespace shill {

namespace {

const time_t kFirstWeek = 50;
const char kIllegalDayProfile[] = "IllegalDay";
const time_t kSecondsPerWeek = 60 * 60 * 24 * 7;
const time_t kTestDays = 20;

}  // namespace

class WiFiProviderTest : public testing::Test {
 public:
  WiFiProviderTest()
      : metrics_(nullptr),
        manager_(&control_, &dispatcher_, &metrics_),
        provider_(&control_, &dispatcher_, &metrics_, &manager_),
        default_profile_(
            new NiceMock<MockProfile>(
                &control_, &metrics_, &manager_, "default")),
        user_profile_(
            new NiceMock<MockProfile>(&control_, &metrics_, &manager_, "user")),
        storage_entry_index_(0) {
    provider_.time_ = &time_;

    string freq_string = StringPrintf("%s%d", WiFiProvider::kStorageFrequencies,
                                      0);
    profile_frequency_data_[freq_string].push_back(
        base::StringPrintf("@%" PRIu64, static_cast<uint64_t>(kFirstWeek)));
    profile_frequency_data_[freq_string].push_back("5001:1");
    profile_frequency_data_[freq_string].push_back("5002:2");

    freq_string = StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 1);
    profile_frequency_data_[freq_string].push_back(
        base::StringPrintf("@%" PRIu64, static_cast<uint64_t>(kFirstWeek) + 1));
    // Overlap one entry with previous block.
    profile_frequency_data_[freq_string].push_back("5001:1");
    profile_frequency_data_[freq_string].push_back("6001:1");
    profile_frequency_data_[freq_string].push_back("6002:2");

    freq_string = StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 2);
    profile_frequency_data_[freq_string].push_back(
        base::StringPrintf("@%" PRIu64, static_cast<uint64_t>(kFirstWeek) + 2));
    profile_frequency_data_[freq_string].push_back("7001:1");
    profile_frequency_data_[freq_string].push_back("7002:2");

    profile_frequency_data_[kIllegalDayProfile].push_back("7001:1");
    profile_frequency_data_[kIllegalDayProfile].push_back("7002:2");
  }

  virtual ~WiFiProviderTest() {}

  virtual void SetUp() {
    EXPECT_CALL(*default_profile_, IsDefault()).WillRepeatedly(Return(true));
    EXPECT_CALL(*default_profile_, GetStorage())
        .WillRepeatedly(Return(&default_profile_storage_));
    EXPECT_CALL(*default_profile_, GetConstStorage())
        .WillRepeatedly(Return(&default_profile_storage_));

    EXPECT_CALL(*user_profile_, IsDefault()).WillRepeatedly(Return(false));
    EXPECT_CALL(*user_profile_, GetStorage())
        .WillRepeatedly(Return(&user_profile_storage_));
    EXPECT_CALL(*user_profile_, GetConstStorage())
        .WillRepeatedly(Return(&user_profile_storage_));

    // Default expectations for UMA metrics. Individual test cases
    // will override these, by adding later expectations.
    EXPECT_CALL(metrics_, SendToUMA(
        Metrics::kMetricRememberedWiFiNetworkCount,
        _,
        Metrics::kMetricRememberedWiFiNetworkCountMin,
        Metrics::kMetricRememberedWiFiNetworkCountMax,
        Metrics::kMetricRememberedWiFiNetworkCountNumBuckets))
        .Times(AnyNumber());
    EXPECT_CALL(metrics_, SendToUMA(
        StartsWith("Network.Shill.WiFi.RememberedPrivateNetworkCount."),
        _,
        Metrics::kMetricRememberedWiFiNetworkCountMin,
        Metrics::kMetricRememberedWiFiNetworkCountMax,
        Metrics::kMetricRememberedWiFiNetworkCountNumBuckets))
        .Times(AnyNumber());
    EXPECT_CALL(metrics_, SendToUMA(
        StartsWith("Network.Shill.WiFi.RememberedSharedNetworkCount."),
        _,
        Metrics::kMetricRememberedWiFiNetworkCountMin,
        Metrics::kMetricRememberedWiFiNetworkCountMax,
        Metrics::kMetricRememberedWiFiNetworkCountNumBuckets))
        .Times(AnyNumber());
  }

  bool GetStringList(const std::string& /*group*/,
                     const std::string& key,
                     std::vector<std::string>* value) {
    if (!value) {
      return false;
    }
    if (ContainsKey(profile_frequency_data_, key)) {
      *value = profile_frequency_data_[key];
      return true;
    }
    return false;
  }

  bool GetIllegalDayStringList(const std::string& /*group*/,
                               const std::string& key,
                               std::vector<std::string>* value) {
    if (!value) {
      return false;
    }
    if (ContainsKey(profile_frequency_data_, key)) {
      *value = profile_frequency_data_[kIllegalDayProfile];
      return true;
    }
    return false;
  }

  // Used by mock invocations of RegisterService() to maintain the side-effect
  // of assigning a profile to |service|.
  void BindServiceToDefaultProfile(const ServiceRefPtr& service) {
    service->set_profile(default_profile_);
  }
  void BindServiceToUserProfile(const ServiceRefPtr& service) {
    service->set_profile(user_profile_);
  }

 protected:
  typedef scoped_refptr<MockWiFiService> MockWiFiServiceRefPtr;

  void CreateServicesFromProfile(Profile* profile) {
    provider_.CreateServicesFromProfile(profile);
  }

  void LoadAndFixupServiceEntries(Profile* profile) {
    provider_.LoadAndFixupServiceEntries(profile);
  }

  void Save() {
    provider_.Save(&default_profile_storage_);
  }

  const vector<WiFiServiceRefPtr> GetServices() {
    return provider_.services_;
  }

  const WiFiProvider::EndpointServiceMap& GetServiceByEndpoint() {
    return provider_.service_by_endpoint_;
  }

  bool GetRunning() {
    return provider_.running_;
  }

  void AddStringParameterToStorage(MockStore* storage,
                                   const string& id,
                                   const string& key,
                                   const string& value) {
    EXPECT_CALL(*storage, GetString(id, key, _))
        .WillRepeatedly(DoAll(SetArgumentPointee<2>(value),
                              Return(true)));
  }

  // Adds service to profile's storage. But does not set profile on the Service.
  string AddServiceToProfileStorage(Profile* profile,
                                    const char* ssid,
                                    const char* mode,
                                    const char* security,
                                    bool is_hidden,
                                    bool provide_hidden) {
    string id = base::StringPrintf("entry_%d", storage_entry_index_);
    auto profile_storage = static_cast<MockStore*>(profile->GetStorage());
    EXPECT_CALL(*profile_storage, GetString(id, _, _))
        .WillRepeatedly(Return(false));
    AddStringParameterToStorage(
        profile_storage, id, WiFiService::kStorageType, kTypeWifi);
    if (ssid) {
      const string ssid_string(ssid);
      const string hex_ssid(
          base::HexEncode(ssid_string.data(), ssid_string.size()));
      AddStringParameterToStorage(
          profile_storage, id, WiFiService::kStorageSSID, hex_ssid);
    }
    if (mode) {
      AddStringParameterToStorage(
          profile_storage, id, WiFiService::kStorageMode, mode);
    }
    if (security) {
      AddStringParameterToStorage(
          profile_storage, id, WiFiService::kStorageSecurity, security);
    }
    if (provide_hidden) {
      EXPECT_CALL(*profile_storage, GetBool(id, kWifiHiddenSsid, _))
          .WillRepeatedly(
              DoAll(SetArgumentPointee<2>(is_hidden), Return(true)));
    } else {
      EXPECT_CALL(*profile_storage, GetBool(id, kWifiHiddenSsid, _))
          .WillRepeatedly(Return(false));
    }
    storage_entry_index_++;
    return id;
  }

  void SetServiceParameters(const char* ssid,
                            const char* mode,
                            const char* security_class,
                            bool is_hidden,
                            bool provide_hidden,
                            KeyValueStore* args) {
    args->SetString(kTypeProperty, kTypeWifi);
    if (ssid) {
      // TODO(pstew): When Chrome switches to using kWifiHexSsid primarily for
      // GetService and friends, we should switch to doing so here ourselves.
      args->SetString(kSSIDProperty, ssid);
    }
    if (mode) {
      args->SetString(kModeProperty, mode);
    }
    if (security_class) {
      args->SetString(kSecurityClassProperty, security_class);
    }
    if (provide_hidden) {
      args->SetBool(kWifiHiddenSsid, is_hidden);
    }
  }

  ServiceRefPtr CreateTemporaryService(const char* ssid,
                                       const char* mode,
                                       const char* security,
                                       bool is_hidden,
                                       bool provide_hidden,
                                       Error* error) {
    KeyValueStore args;
    SetServiceParameters(
        ssid, mode, security, is_hidden, provide_hidden, &args);
    return provider_.CreateTemporaryService(args, error);
  }

  WiFiServiceRefPtr GetService(const char* ssid,
                               const char* mode,
                               const char* security_class,
                               bool is_hidden,
                               bool provide_hidden,
                               Error* error) {
    KeyValueStore args;
    SetServiceParameters(
        ssid, mode, security_class, is_hidden, provide_hidden, &args);
    return provider_.GetWiFiService(args, error);
  }

  WiFiServiceRefPtr GetWiFiService(const KeyValueStore& args, Error* error) {
    return provider_.GetWiFiService(args, error);
  }

  WiFiServiceRefPtr FindService(const vector<uint8_t>& ssid,
                                const string& mode,
                                const string& security) {
    return provider_.FindService(ssid, mode, security);
  }
  WiFiEndpointRefPtr MakeEndpoint(const string& ssid, const string& bssid,
                                  uint16_t frequency, int16_t signal_dbm) {
    return WiFiEndpoint::MakeOpenEndpoint(
        nullptr, nullptr, ssid, bssid,
        WPASupplicant::kNetworkModeInfrastructure, frequency, signal_dbm);
  }
  MockWiFiServiceRefPtr AddMockService(const vector<uint8_t>& ssid,
                                       const string& mode,
                                       const string& security,
                                       bool hidden_ssid) {
    MockWiFiServiceRefPtr service = new MockWiFiService(
        &control_,
        &dispatcher_,
        &metrics_,
        &manager_,
        &provider_,
        ssid,
        mode,
        security,
        hidden_ssid);
    provider_.services_.push_back(service);
    return service;
  }
  void AddEndpointToService(WiFiServiceRefPtr service,
                            const WiFiEndpointConstRefPtr& endpoint) {
    provider_.service_by_endpoint_[endpoint.get()] = service;
  }

  void BuildFreqCountStrings(vector<string>* strings) {
    // NOTE: These strings match the frequencies in |BuildFreqCountMap|.  They
    // are also provided, here, in sorted order to match the frequency map
    // (iterators for which will provide them in frequency-sorted order).
    static const char* kStrings[] = {
      "@20", "5180:14", "5240:16", "5745:7", "5765:4", "5785:14", "5805:5"
    };
    if (!strings) {
      LOG(ERROR) << "NULL |strings|.";
      return;
    }
    for (size_t i = 0; i < arraysize(kStrings); ++i) {
      (*strings).push_back(kStrings[i]);
    }
  }

  void BuildFreqCountMap(WiFiProvider::ConnectFrequencyMap* frequencies) {
    // NOTE: These structures match the strings in |BuildFreqCountStrings|.
    static const struct FreqCount {
      uint16_t freq;
      int64_t count;
    } kConnectFreq[] = {
      {5180, 14},
      {5240, 16},
      {5745, 7},
      {5765, 4},
      {5785, 14},
      {5805, 5}
    };
    if (!frequencies) {
      LOG(ERROR) << "NULL |frequencies|.";
      return;
    }
    for (size_t i = 0; i < arraysize(kConnectFreq); ++i) {
      (*frequencies)[kConnectFreq[i].freq] = kConnectFreq[i].count;
    }
  }

  void LoadConnectCountByFrequency(time_t today_seconds) {
    provider_.time_ = &time_;
    EXPECT_CALL(time_, GetSecondsSinceEpoch()).WillOnce(Return(today_seconds));
    const string kGroupId =
        StringPrintf("%s_0_0_%s_%s", kTypeWifi, kModeManaged, kSecurityNone);
    EXPECT_CALL(default_profile_storage_,
                GetString(kGroupId, _, _)).WillRepeatedly(Return(true));
    set<string> groups;
    groups.insert(kGroupId);
    EXPECT_CALL(default_profile_storage_, GetGroups()).WillOnce(Return(groups));
    // Provide data for block[0] through block[2] (block[3] is empty).
    EXPECT_CALL(
        default_profile_storage_,
        GetStringList(
            WiFiProvider::kStorageId,
            StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 0), _)).
        WillOnce(Invoke(this, &WiFiProviderTest::GetStringList));
    EXPECT_CALL(
        default_profile_storage_,
        GetStringList(
            WiFiProvider::kStorageId,
            StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 1), _)).
        WillOnce(Invoke(this, &WiFiProviderTest::GetStringList));
    EXPECT_CALL(
        default_profile_storage_,
        GetStringList(
            WiFiProvider::kStorageId,
            StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 2), _)).
        WillOnce(Invoke(this, &WiFiProviderTest::GetStringList));
    EXPECT_CALL(
        default_profile_storage_,
        GetStringList(
            WiFiProvider::kStorageId,
            StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 3), _)).
        WillOnce(Invoke(this, &WiFiProviderTest::GetStringList));

    LoadAndFixupServiceEntries(default_profile_.get());
  }

  map<string, vector<string>> profile_frequency_data_;
  NiceMockControl control_;
  MockEventDispatcher dispatcher_;
  MockMetrics metrics_;
  MockTime time_;
  StrictMock<MockManager> manager_;
  WiFiProvider provider_;
  scoped_refptr<MockProfile> default_profile_;
  scoped_refptr<MockProfile> user_profile_;
  StrictMock<MockStore> default_profile_storage_;
  StrictMock<MockStore> user_profile_storage_;
  int storage_entry_index_;  // shared across profiles
};

MATCHER(TypeWiFiPropertyMatch, "") {
  return arg.properties().size() == 1 &&
      arg.LookupString(kTypeProperty, "") == kTypeWifi;
}

MATCHER_P(RefPtrMatch, ref, "") {
  return ref.get() == arg.get();
}

TEST_F(WiFiProviderTest, Start) {
  // Doesn't do anything really.  Just testing for no crash.
  EXPECT_TRUE(GetServices().empty());
  EXPECT_FALSE(GetRunning());
  provider_.Start();
  EXPECT_TRUE(GetServices().empty());
  EXPECT_TRUE(GetRunning());
  EXPECT_TRUE(GetServiceByEndpoint().empty());
  EXPECT_FALSE(provider_.disable_vht());
}

TEST_F(WiFiProviderTest, Stop) {
  MockWiFiServiceRefPtr service0 = AddMockService(vector<uint8_t>(1, '0'),
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  MockWiFiServiceRefPtr service1 = AddMockService(vector<uint8_t>(1, '1'),
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  WiFiEndpointRefPtr endpoint = MakeEndpoint("", "00:00:00:00:00:00", 0, 0);
  AddEndpointToService(service0, endpoint);

  EXPECT_EQ(2, GetServices().size());
  EXPECT_FALSE(GetServiceByEndpoint().empty());
  EXPECT_CALL(*service0, ResetWiFi()).Times(1);
  EXPECT_CALL(*service1, ResetWiFi()).Times(1);
  EXPECT_CALL(manager_, DeregisterService(RefPtrMatch(service0))).Times(1);
  EXPECT_CALL(manager_, DeregisterService(RefPtrMatch(service1))).Times(1);
  provider_.Stop();
  // Verify now, so it's clear that this happened as a result of the call
  // above, and not anything in the destructor(s).
  Mock::VerifyAndClearExpectations(service0.get());
  Mock::VerifyAndClearExpectations(service1.get());
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_TRUE(GetServices().empty());
  EXPECT_TRUE(GetServiceByEndpoint().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileWithNoGroups) {
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillOnce(Return(set<string>()));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileMissingSSID) {
  set<string> groups;
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), nullptr, kModeManaged, kSecurityNone, false,
          true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileEmptySSID) {
  set<string> groups;
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "", kModeManaged, kSecurityNone, false,
          true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileMissingMode) {
  set<string> groups;
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "foo", nullptr, kSecurityNone, false, true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileEmptyMode) {
  set<string> groups;
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "foo", "", kSecurityNone, false, true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileMissingSecurity) {
  set<string> groups;
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "foo", kModeManaged, nullptr, false, true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileEmptySecurity) {
  set<string> groups;
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "foo", kModeManaged, "", false, true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileMissingHidden) {
  set<string> groups;
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "foo", kModeManaged, kSecurityNone, false,
          false));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileSingle) {
  set<string> groups;
  string kSSID("foo");
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(),
          kSSID.c_str(), kModeManaged, kSecurityNone, false, true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(manager_, RegisterService(_))
      .WillOnce(Invoke(this, &WiFiProviderTest::BindServiceToDefaultProfile));
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      1,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets)).Times(2);
  CreateServicesFromProfile(default_profile_.get());
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_EQ(1, GetServices().size());

  const WiFiServiceRefPtr service = GetServices().front();
  const string service_ssid(service->ssid().begin(), service->ssid().end());
  EXPECT_EQ(kSSID, service_ssid);
  EXPECT_EQ(kModeManaged, service->mode());
  EXPECT_TRUE(service->IsSecurityMatch(kSecurityNone));

  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  CreateServicesFromProfile(default_profile_.get());
  EXPECT_EQ(1, GetServices().size());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileHiddenButConnected) {
  set<string> groups;
  string kSSID("foo");
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(),
          kSSID.c_str(), kModeManaged, kSecurityNone, true, true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(manager_, RegisterService(_))
      .WillOnce(Invoke(this, &WiFiProviderTest::BindServiceToDefaultProfile));
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  EXPECT_CALL(manager_, IsTechnologyConnected(Technology::kWifi))
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, RequestScan(_, _, _)).Times(0);
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      1,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets)).Times(2);
  CreateServicesFromProfile(default_profile_.get());
  Mock::VerifyAndClearExpectations(&manager_);

  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, IsTechnologyConnected(_)).Times(0);
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  CreateServicesFromProfile(default_profile_.get());
}

TEST_F(WiFiProviderTest, CreateServicesFromProfileHiddenNotConnected) {
  set<string> groups;
  string kSSID("foo");
  groups.insert(AddServiceToProfileStorage(default_profile_.get(),
      kSSID.c_str(), kModeManaged, kSecurityNone, true, true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(manager_, RegisterService(_))
      .WillOnce(Invoke(this, &WiFiProviderTest::BindServiceToDefaultProfile));
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  EXPECT_CALL(manager_, IsTechnologyConnected(Technology::kWifi))
      .WillOnce(Return(false));
  EXPECT_CALL(manager_, RequestScan(Device::kProgressiveScan,
                                    kTypeWifi, _)).Times(1);
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      1,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets)).Times(2);
  CreateServicesFromProfile(default_profile_.get());
  Mock::VerifyAndClearExpectations(&manager_);

  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, IsTechnologyConnected(_)).Times(0);
  EXPECT_CALL(manager_, RequestScan(_, _, _)).Times(0);
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  CreateServicesFromProfile(default_profile_.get());
}

TEST_F(WiFiProviderTest, CreateTemporaryServiceFromProfileNonWiFi) {
  const string kEntryName("name");
  auto profile_storage =
      static_cast<MockStore*>(default_profile_->GetStorage());
  EXPECT_CALL(*profile_storage,
              GetString(kEntryName, WiFiService::kStorageType, _))
      .WillOnce(Return(false));
  Error error;
  EXPECT_EQ(nullptr,
            provider_.CreateTemporaryServiceFromProfile(
                default_profile_, kEntryName, &error));
  EXPECT_FALSE(error.IsSuccess());
  EXPECT_THAT(error.message(),
              StartsWith("Unspecified or invalid network type"));
}

TEST_F(WiFiProviderTest, CreateTemporaryServiceFromProfileMissingSSID) {
  string entry_name = AddServiceToProfileStorage(
          default_profile_.get(), nullptr, kModeManaged, kSecurityNone, false,
          true);
  Error error;
  EXPECT_EQ(nullptr,
            provider_.CreateTemporaryServiceFromProfile(
                default_profile_, entry_name, &error));
  EXPECT_FALSE(error.IsSuccess());
  EXPECT_THAT(error.message(), StartsWith("Unspecified or invalid SSID"));
}

TEST_F(WiFiProviderTest, CreateTemporaryServiceFromProfileMissingMode) {
  string entry_name = AddServiceToProfileStorage(
      default_profile_.get(), "foo", "", kSecurityNone, false, true);

  Error error;
  EXPECT_EQ(nullptr,
            provider_.CreateTemporaryServiceFromProfile(
                default_profile_, entry_name, &error));
  EXPECT_FALSE(error.IsSuccess());
  EXPECT_THAT(error.message(), StartsWith("Network mode not specified"));
}

TEST_F(WiFiProviderTest, CreateTemporaryServiceFromProfileMissingSecurity) {
  string entry_name = AddServiceToProfileStorage(
      default_profile_.get(), "foo", kModeManaged, "", false, true);

  Error error;
  EXPECT_EQ(nullptr,
            provider_.CreateTemporaryServiceFromProfile(
                default_profile_, entry_name, &error));
  EXPECT_FALSE(error.IsSuccess());
  EXPECT_THAT(error.message(),
              StartsWith("Unspecified or invalid security mode"));
}

TEST_F(WiFiProviderTest, CreateTemporaryServiceFromProfileMissingHidden) {
  string entry_name = AddServiceToProfileStorage(
      default_profile_.get(), "foo", kModeManaged, kSecurityNone, false, false);

  Error error;
  EXPECT_EQ(nullptr,
            provider_.CreateTemporaryServiceFromProfile(
                default_profile_, entry_name, &error));
  EXPECT_FALSE(error.IsSuccess());
  EXPECT_THAT(error.message(),
              StartsWith("Hidden SSID not specified"));
}

TEST_F(WiFiProviderTest, CreateTemporaryServiceFromProfile) {
  string entry_name = AddServiceToProfileStorage(
      default_profile_.get(), "foo", kModeManaged, kSecurityNone, false, true);

  Error error;
  EXPECT_NE(nullptr,
            provider_.CreateTemporaryServiceFromProfile(
                default_profile_, entry_name, &error));
  EXPECT_TRUE(error.IsSuccess());
}

TEST_F(WiFiProviderTest, CreateTwoServices) {
  set<string> groups;
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "foo", kModeManaged, kSecurityNone, false,
          true));
  groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "bar", kModeManaged, kSecurityNone, true,
          true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(manager_, RegisterService(_))
      .Times(2)
      .WillRepeatedly(
          Invoke(this, &WiFiProviderTest::BindServiceToDefaultProfile));
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  EXPECT_CALL(manager_, IsTechnologyConnected(Technology::kWifi))
      .WillOnce(Return(true));
  EXPECT_CALL(manager_, RequestScan(_, kTypeWifi, _)).Times(0);
  EXPECT_CALL(metrics_, SendToUMA(
      Metrics::kMetricRememberedWiFiNetworkCount,
      2,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(default_profile_.get());
  Mock::VerifyAndClearExpectations(&manager_);

  EXPECT_EQ(2, GetServices().size());
}

TEST_F(WiFiProviderTest, ServiceSourceStats) {
  set<string> default_profile_groups;
  default_profile_groups.insert(
      AddServiceToProfileStorage(
          default_profile_.get(), "foo", kModeManaged, kSecurityWpa, false,
          true));
  EXPECT_CALL(default_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(default_profile_groups));
  EXPECT_CALL(manager_, RegisterService(_))
      .WillOnce(Invoke(this, &WiFiProviderTest::BindServiceToDefaultProfile));
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  // Processing default profile does not generate UMA metrics.
  EXPECT_CALL(metrics_, SendToUMA(
      StartsWith("Network.Shill.WiFi.RememberedSystemNetworkCount."),
      _, _, _, _))
      .Times(0);
  EXPECT_CALL(metrics_, SendToUMA(
      StartsWith("Network.Shill.WiFi.RememberedUserNetworkCount."),
      _, _, _, _))
      .Times(0);
  CreateServicesFromProfile(default_profile_.get());
  Mock::VerifyAndClearExpectations(&manager_);

  set<string> user_profile_groups;
  user_profile_groups.insert(
      AddServiceToProfileStorage(
          user_profile_.get(), "bar", kModeManaged, kSecurityRsn, false, true));
  EXPECT_CALL(user_profile_storage_,
              GetGroupsWithProperties(TypeWiFiPropertyMatch()))
      .WillRepeatedly(Return(user_profile_groups));
  EXPECT_CALL(manager_, RegisterService(_))
      .WillOnce(Invoke(this, &WiFiProviderTest::BindServiceToUserProfile));
  EXPECT_CALL(manager_, IsServiceEphemeral(_)).WillRepeatedly(Return(false));
  // Processing user profile generates metrics for both, default profile,
  // and user profile.
  EXPECT_CALL(metrics_, SendToUMA(
      StartsWith("Network.Shill.WiFi.RememberedSystemNetworkCount."),
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets))
      .Times(3);  // none, wep, 802.1x
  EXPECT_CALL(metrics_, SendToUMA(
      StartsWith("Network.Shill.WiFi.RememberedUserNetworkCount."),
      0,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets))
      .Times(3);  // none, wep, 802.1x
  EXPECT_CALL(metrics_, SendToUMA(
      StartsWith("Network.Shill.WiFi.RememberedSystemNetworkCount.psk"),
      1,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  EXPECT_CALL(metrics_, SendToUMA(
      StartsWith("Network.Shill.WiFi.RememberedUserNetworkCount.psk"),
      1,
      Metrics::kMetricRememberedWiFiNetworkCountMin,
      Metrics::kMetricRememberedWiFiNetworkCountMax,
      Metrics::kMetricRememberedWiFiNetworkCountNumBuckets));
  CreateServicesFromProfile(user_profile_.get());
}

TEST_F(WiFiProviderTest, GetServiceEmptyMode) {
  Error error;
  EXPECT_FALSE(GetService("foo", "", kSecurityNone,
                          false, false, &error).get());
  EXPECT_EQ(Error::kNotSupported, error.type());
}

TEST_F(WiFiProviderTest, GetServiceNoMode) {
  Error error;
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  EXPECT_TRUE(GetService("foo", nullptr, kSecurityNone,
                          false, false, &error).get());
  EXPECT_TRUE(error.IsSuccess());
}

TEST_F(WiFiProviderTest, GetServiceBadMode) {
  Error error;
  EXPECT_FALSE(GetService("foo", "BogoMesh",
                          kSecurityNone,
                          false, false, &error).get());
  EXPECT_EQ(Error::kNotSupported, error.type());
  EXPECT_EQ("service mode is unsupported", error.message());
}

TEST_F(WiFiProviderTest, GetServiceNoSSID) {
  Error error;
  EXPECT_FALSE(GetService(nullptr, kModeManaged,
                          kSecurityNone, false, false,
                          &error).get());
  EXPECT_EQ(Error::kInvalidArguments, error.type());
  EXPECT_EQ("must specify SSID", error.message());
}

TEST_F(WiFiProviderTest, GetServiceEmptySSID) {
  Error error;
  EXPECT_FALSE(GetService("", kModeManaged,
                          kSecurityNone, false, false,
                          &error).get());
  EXPECT_EQ(Error::kInvalidNetworkName, error.type());
  EXPECT_EQ("SSID is too short", error.message());
}

TEST_F(WiFiProviderTest, GetServiceLongSSID) {
  Error error;
  string ssid(IEEE_80211::kMaxSSIDLen + 1, '0');
  EXPECT_FALSE(GetService(ssid.c_str(), kModeManaged,
                          kSecurityNone, false, false,
                          &error).get());
  EXPECT_EQ(Error::kInvalidNetworkName, error.type());
  EXPECT_EQ("SSID is too long", error.message());
}

TEST_F(WiFiProviderTest, GetServiceJustLongEnoughSSID) {
  Error error;
  string ssid(IEEE_80211::kMaxSSIDLen, '0');
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  EXPECT_TRUE(GetService(ssid.c_str(), kModeManaged,
                         kSecurityNone, false, false,
                         &error));
  EXPECT_TRUE(error.IsSuccess());
}

TEST_F(WiFiProviderTest, GetServiceBadSecurityClass) {
  Error error;
  EXPECT_FALSE(GetService("foo", kModeManaged,
                          kSecurityRsn, false, false,
                          &error));
  EXPECT_EQ(Error::kNotSupported, error.type());
  EXPECT_EQ("security class is unsupported", error.message());
}

TEST_F(WiFiProviderTest, GetServiceMinimal) {
  Error error;
  const string kSSID("foo");
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  WiFiServiceRefPtr service = GetService(kSSID.c_str(), kModeManaged,
                                         nullptr, false, false, &error);
  EXPECT_TRUE(service.get());
  EXPECT_TRUE(error.IsSuccess());
  const string service_ssid(service->ssid().begin(), service->ssid().end());
  EXPECT_EQ(kSSID, service_ssid);
  EXPECT_EQ(kModeManaged, service->mode());

  // These two should be set to their default values if not specified.
  EXPECT_TRUE(service->IsSecurityMatch(kSecurityNone));
  EXPECT_TRUE(service->hidden_ssid());
}

TEST_F(WiFiProviderTest, GetServiceFullySpecified) {
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  const string kSSID("bar");
  Error error;
  WiFiServiceRefPtr service0 = GetService(
      kSSID.c_str(), kModeManaged, kSecurityPsk, false, true, &error);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_TRUE(error.IsSuccess());
  const string service_ssid(service0->ssid().begin(), service0->ssid().end());
  EXPECT_EQ(kSSID, service_ssid);
  EXPECT_EQ(kModeManaged, service0->mode());
  EXPECT_TRUE(service0->IsSecurityMatch(kSecurityPsk));
  EXPECT_FALSE(service0->hidden_ssid());

  // Getting the same service parameters (even with a different hidden
  // parameter) should return the same service.
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  WiFiServiceRefPtr service1 =
      GetService(kSSID.c_str(), kModeManaged, kSecurityPsk, true, true, &error);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_EQ(service0.get(), service1.get());
  EXPECT_EQ(1, GetServices().size());

  // Getting the same ssid with different other parameters should return
  // a different service.
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  WiFiServiceRefPtr service2 = GetService(
      kSSID.c_str(), kModeManaged, kSecurityNone, true, true, &error);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_NE(service0.get(), service2.get());
  EXPECT_EQ(2, GetServices().size());
}

TEST_F(WiFiProviderTest, GetServiceByHexSsid) {
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  const string kSSID("bar");
  const string kHexSsid(base::HexEncode(kSSID.c_str(), kSSID.length()));

  KeyValueStore args;
  args.SetString(kTypeProperty, kTypeWifi);
  args.SetString(kWifiHexSsid, kHexSsid);
  args.SetString(kSecurityProperty, kSecurityPsk);
  args.SetBool(kWifiHiddenSsid, false);

  Error error;
  WiFiServiceRefPtr service = GetWiFiService(args, &error);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_TRUE(error.IsSuccess());
  const string service_ssid(service->ssid().begin(), service->ssid().end());
  EXPECT_EQ(kSSID, service_ssid);
  EXPECT_EQ(kModeManaged, service->mode());
  EXPECT_TRUE(service->IsSecurityMatch(kSecurityPsk));
  EXPECT_FALSE(service->hidden_ssid());


  // While here, make sure FindSimilarService also supports kWifiHexSsid.
  Error find_error;
  ServiceRefPtr find_service = provider_.FindSimilarService(args, &find_error);
  EXPECT_TRUE(find_error.IsSuccess());
  EXPECT_EQ(service.get(), find_service.get());
}

TEST_F(WiFiProviderTest, GetServiceWithSecurityAndSecurityClassMismatched) {
  const string kSSID("bar");
  KeyValueStore args;
  args.SetString(kTypeProperty, kTypeWifi);
  args.SetString(kSSIDProperty, kSSID);
  args.SetString(kSecurityProperty, kSecurityRsn);
  args.SetString(kSecurityClassProperty, kSecurityPsk);
  args.SetBool(kWifiHiddenSsid, false);

  Error error;
  WiFiServiceRefPtr service;
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  service = GetWiFiService(args, &error);
  EXPECT_FALSE(error.IsSuccess());
}

TEST_F(WiFiProviderTest, GetServiceWithSecurityAndSecurityClassMatching) {
  const string kSSID("bar");
  KeyValueStore args;
  args.SetString(kTypeProperty, kTypeWifi);
  args.SetString(kSSIDProperty, kSSID);
  args.SetString(kSecurityProperty, kSecurityPsk);
  args.SetString(kSecurityClassProperty, kSecurityPsk);
  args.SetBool(kWifiHiddenSsid, false);

  Error error;
  WiFiServiceRefPtr service;
  EXPECT_CALL(manager_, RegisterService(_));
  service = GetWiFiService(args, &error);
  EXPECT_TRUE(error.IsSuccess());
}

TEST_F(WiFiProviderTest,
       GetServiceWithSecurityAndSecurityClassMatchingButInvalidClass) {
  const string kSSID("bar");
  KeyValueStore args;
  args.SetString(kTypeProperty, kTypeWifi);
  args.SetString(kSSIDProperty, kSSID);
  args.SetString(kSecurityProperty, kSecurityRsn);
  args.SetString(kSecurityClassProperty, kSecurityRsn);
  args.SetBool(kWifiHiddenSsid, false);
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);

  Error error;
  WiFiServiceRefPtr service = GetWiFiService(args, &error);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_FALSE(error.IsSuccess());
}

TEST_F(WiFiProviderTest, GetServiceBadSecurity) {
  const string kSSID("bar");
  KeyValueStore args;
  args.SetString(kTypeProperty, kTypeWifi);
  args.SetString(kSSIDProperty, kSSID);
  args.SetString(kSecurityProperty, "pig-80211");
  args.SetBool(kWifiHiddenSsid, false);

  Error error;
  WiFiServiceRefPtr service;
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  service = GetWiFiService(args, &error);
  EXPECT_FALSE(error.IsSuccess());
  EXPECT_EQ(Error::kNotSupported, error.type());
  EXPECT_EQ("security mode is unsupported", error.message());
}

TEST_F(WiFiProviderTest, FindSimilarService) {
  // Since CreateTemporyService uses exactly the same validation as
  // GetService, don't bother with testing invalid parameters.
  const string kSSID("foo");
  KeyValueStore args;
  SetServiceParameters(
      kSSID.c_str(), kModeManaged, kSecurityNone,
      true, true, &args);
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  Error get_service_error;
  WiFiServiceRefPtr service = GetWiFiService(args, &get_service_error);
  EXPECT_EQ(1, GetServices().size());

  {
    Error error;
    ServiceRefPtr find_service = provider_.FindSimilarService(args, &error);
    EXPECT_EQ(service.get(), find_service.get());
    EXPECT_TRUE(error.IsSuccess());
  }

  args.SetBool(kWifiHiddenSsid, false);

  {
    Error error;
    ServiceRefPtr find_service = provider_.FindSimilarService(args, &error);
    EXPECT_EQ(service.get(), find_service.get());
    EXPECT_TRUE(error.IsSuccess());
  }

  args.SetString(kSecurityClassProperty, kSecurityPsk);

  {
    Error error;
    ServiceRefPtr find_service = provider_.FindSimilarService(args, &error);
    EXPECT_EQ(nullptr, find_service.get());
    EXPECT_EQ(Error::kNotFound, error.type());
  }
}

TEST_F(WiFiProviderTest, CreateTemporaryService) {
  // Since CreateTemporyService uses exactly the same validation as
  // GetService, don't bother with testing invalid parameters.
  const string kSSID("foo");
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  Error error;
  WiFiServiceRefPtr service0 = GetService(
      kSSID.c_str(), kModeManaged, kSecurityNone, true, true, &error);
  EXPECT_EQ(1, GetServices().size());
  Mock::VerifyAndClearExpectations(&manager_);

  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  ServiceRefPtr service1 =
      CreateTemporaryService(kSSID.c_str(), kModeManaged,
                             kSecurityNone, true, true, &error);

  // Test that a new service was created, but not registered with the
  // manager or added to the provider's service list.
  EXPECT_EQ(1, GetServices().size());
  EXPECT_TRUE(service0 != service1);
  EXPECT_TRUE(service1->HasOneRef());
}

TEST_F(WiFiProviderTest, FindServiceWPA) {
  const string kSSID("an_ssid");
  Error error;
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  KeyValueStore args;
  SetServiceParameters(
      kSSID.c_str(), kModeManaged, nullptr, false, false, &args);
  args.SetString(kSecurityProperty, kSecurityRsn);
  WiFiServiceRefPtr service = GetWiFiService(args, &error);
  ASSERT_TRUE(service);
  const vector<uint8_t> ssid_bytes(kSSID.begin(), kSSID.end());
  WiFiServiceRefPtr wpa_service(FindService(ssid_bytes, kModeManaged,
                                            kSecurityWpa));
  EXPECT_TRUE(wpa_service);
  EXPECT_EQ(service.get(), wpa_service.get());
  WiFiServiceRefPtr rsn_service(FindService(ssid_bytes, kModeManaged,
                                            kSecurityRsn));
  EXPECT_TRUE(rsn_service.get());
  EXPECT_EQ(service.get(), rsn_service.get());
  WiFiServiceRefPtr psk_service(FindService(ssid_bytes, kModeManaged,
                                            kSecurityPsk));
  EXPECT_EQ(service.get(), psk_service.get());
  WiFiServiceRefPtr wep_service(FindService(ssid_bytes, kModeManaged,
                                            kSecurityWep));
  EXPECT_TRUE(service.get() != wep_service.get());
  EXPECT_EQ(nullptr, wep_service.get());
}

TEST_F(WiFiProviderTest, FindServiceForEndpoint) {
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  Error error;
  const string kSSID("an_ssid");
  WiFiServiceRefPtr service = GetService(
      kSSID.c_str(), kModeManaged, kSecurityNone, false, true, &error);
  ASSERT_TRUE(service);
  WiFiEndpointRefPtr endpoint = MakeEndpoint(kSSID, "00:00:00:00:00:00", 0, 0);
  WiFiServiceRefPtr endpoint_service =
      provider_.FindServiceForEndpoint(endpoint);
  // Just because a matching service exists, we shouldn't necessarily have
  // it returned.  We will test that this function returns the correct
  // service if the endpoint is added below.
  EXPECT_EQ(nullptr, endpoint_service.get());
}

TEST_F(WiFiProviderTest, OnEndpointAdded) {
  provider_.Start();
  const string ssid0("an_ssid");
  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
  EXPECT_FALSE(FindService(ssid0_bytes, kModeManaged,
                           kSecurityNone));
  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  EXPECT_CALL(manager_, UpdateService(_)).Times(1);
  provider_.OnEndpointAdded(endpoint0);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_EQ(1, GetServices().size());
  WiFiServiceRefPtr service0(FindService(ssid0_bytes, kModeManaged,
                                         kSecurityNone));
  EXPECT_TRUE(service0);
  EXPECT_TRUE(service0->HasEndpoints());
  EXPECT_EQ(1, GetServiceByEndpoint().size());
  WiFiServiceRefPtr endpoint_service =
      provider_.FindServiceForEndpoint(endpoint0);
  EXPECT_EQ(service0.get(), endpoint_service.get());

  WiFiEndpointRefPtr endpoint1 = MakeEndpoint(ssid0, "00:00:00:00:00:01", 0, 0);
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
  provider_.OnEndpointAdded(endpoint1);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_EQ(1, GetServices().size());

  const string ssid1("another_ssid");
  const vector<uint8_t> ssid1_bytes(ssid1.begin(), ssid1.end());
  EXPECT_FALSE(FindService(ssid1_bytes, kModeManaged,
                           kSecurityNone));
  WiFiEndpointRefPtr endpoint2 = MakeEndpoint(ssid1, "00:00:00:00:00:02",
                                              0, 0);
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  EXPECT_CALL(manager_, UpdateService(_)).Times(1);
  provider_.OnEndpointAdded(endpoint2);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_EQ(2, GetServices().size());

  WiFiServiceRefPtr service1(FindService(ssid1_bytes, kModeManaged,
                                         kSecurityNone));
  EXPECT_TRUE(service1);
  EXPECT_TRUE(service1->HasEndpoints());
  EXPECT_TRUE(service1 != service0);
}

TEST_F(WiFiProviderTest, OnEndpointAddedWithSecurity) {
  provider_.Start();
  const string ssid0("an_ssid");
  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
  EXPECT_FALSE(FindService(ssid0_bytes, kModeManaged,
                           kSecurityNone));
  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
  endpoint0->set_security_mode(kSecurityRsn);
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  EXPECT_CALL(manager_, UpdateService(_)).Times(1);
  provider_.OnEndpointAdded(endpoint0);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_EQ(1, GetServices().size());
  WiFiServiceRefPtr service0(FindService(ssid0_bytes, kModeManaged,
                                         kSecurityWpa));
  EXPECT_TRUE(service0);
  EXPECT_TRUE(service0->HasEndpoints());
  EXPECT_EQ(kSecurityPsk, service0->security_);

  WiFiEndpointRefPtr endpoint1 = MakeEndpoint(ssid0, "00:00:00:00:00:01", 0, 0);
  endpoint1->set_security_mode(kSecurityWpa);
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
  provider_.OnEndpointAdded(endpoint1);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_EQ(1, GetServices().size());

  const string ssid1("another_ssid");
  const vector<uint8_t> ssid1_bytes(ssid1.begin(), ssid1.end());
  EXPECT_FALSE(FindService(ssid1_bytes, kModeManaged,
                           kSecurityNone));
  WiFiEndpointRefPtr endpoint2 = MakeEndpoint(ssid1, "00:00:00:00:00:02", 0, 0);
  endpoint2->set_security_mode(kSecurityWpa);
  EXPECT_CALL(manager_, RegisterService(_)).Times(1);
  EXPECT_CALL(manager_, UpdateService(_)).Times(1);
  provider_.OnEndpointAdded(endpoint2);
  Mock::VerifyAndClearExpectations(&manager_);
  EXPECT_EQ(2, GetServices().size());

  WiFiServiceRefPtr service1(FindService(ssid1_bytes, kModeManaged,
                                         kSecurityRsn));
  EXPECT_TRUE(service1);
  EXPECT_TRUE(service1->HasEndpoints());
  EXPECT_EQ(kSecurityPsk, service1->security_);
  EXPECT_TRUE(service1 != service0);
}

TEST_F(WiFiProviderTest, OnEndpointAddedWhileStopped) {
  // If we don't call provider_.Start(), OnEndpointAdded should have no effect.
  const string ssid("an_ssid");
  WiFiEndpointRefPtr endpoint = MakeEndpoint(ssid, "00:00:00:00:00:00", 0, 0);
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, UpdateService(_)).Times(0);
  provider_.OnEndpointAdded(endpoint);
  EXPECT_TRUE(GetServices().empty());
}

TEST_F(WiFiProviderTest, OnEndpointAddedToMockService) {
  // The previous test allowed the provider to create its own "real"
  // WiFiServices, which hides some of what we can test with mock
  // services.  Re-do an add-endpoint operation by seeding the provider
  // with a mock service.
  provider_.Start();
  const string ssid0("an_ssid");
  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
  MockWiFiServiceRefPtr service0 = AddMockService(ssid0_bytes,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  const string ssid1("another_ssid");
  const vector<uint8_t> ssid1_bytes(ssid1.begin(), ssid1.end());
  MockWiFiServiceRefPtr service1 = AddMockService(ssid1_bytes,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  EXPECT_EQ(service0.get(), FindService(ssid0_bytes,
                                        kModeManaged,
                                        kSecurityNone).get());
  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
  EXPECT_CALL(*service0, AddEndpoint(RefPtrMatch(endpoint0))).Times(1);
  EXPECT_CALL(*service1, AddEndpoint(_)).Times(0);
  provider_.OnEndpointAdded(endpoint0);
  Mock::VerifyAndClearExpectations(&manager_);
  Mock::VerifyAndClearExpectations(service0.get());
  Mock::VerifyAndClearExpectations(service1.get());

  WiFiEndpointRefPtr endpoint1 = MakeEndpoint(ssid0, "00:00:00:00:00:01", 0, 0);
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
  EXPECT_CALL(*service0, AddEndpoint(RefPtrMatch(endpoint1))).Times(1);
  EXPECT_CALL(*service1, AddEndpoint(_)).Times(0);
  provider_.OnEndpointAdded(endpoint1);
  Mock::VerifyAndClearExpectations(&manager_);
  Mock::VerifyAndClearExpectations(service0.get());
  Mock::VerifyAndClearExpectations(service1.get());

  WiFiEndpointRefPtr endpoint2 = MakeEndpoint(ssid1, "00:00:00:00:00:02", 0, 0);
  EXPECT_CALL(manager_, RegisterService(_)).Times(0);
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service1))).Times(1);
  EXPECT_CALL(*service0, AddEndpoint(_)).Times(0);
  EXPECT_CALL(*service1, AddEndpoint(RefPtrMatch(endpoint2))).Times(1);
  provider_.OnEndpointAdded(endpoint2);
}

TEST_F(WiFiProviderTest, OnEndpointRemoved) {
  provider_.Start();
  const string ssid0("an_ssid");
  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
  MockWiFiServiceRefPtr service0 = AddMockService(ssid0_bytes,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  const string ssid1("another_ssid");
  const vector<uint8_t> ssid1_bytes(ssid1.begin(), ssid1.end());
  MockWiFiServiceRefPtr service1 = AddMockService(ssid1_bytes,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  EXPECT_EQ(2, GetServices().size());

  // Remove the last endpoint of a non-remembered service.
  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
  AddEndpointToService(service0, endpoint0);
  EXPECT_EQ(1, GetServiceByEndpoint().size());

  EXPECT_CALL(*service0, RemoveEndpoint(RefPtrMatch(endpoint0))).Times(1);
  EXPECT_CALL(*service1, RemoveEndpoint(_)).Times(0);
  EXPECT_CALL(*service0, HasEndpoints()).WillOnce(Return(false));
  EXPECT_CALL(*service0, IsRemembered()).WillOnce(Return(false));
  EXPECT_CALL(*service0, ResetWiFi()).Times(1);
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(0);
  EXPECT_CALL(manager_, DeregisterService(RefPtrMatch(service0))).Times(1);
  provider_.OnEndpointRemoved(endpoint0);
  // Verify now, so it's clear that this happened as a result of the call
  // above, and not anything in the destructor(s).
  Mock::VerifyAndClearExpectations(&manager_);
  Mock::VerifyAndClearExpectations(service0.get());
  Mock::VerifyAndClearExpectations(service1.get());
  EXPECT_EQ(1, GetServices().size());
  EXPECT_EQ(service1.get(), GetServices().front().get());
  EXPECT_TRUE(GetServiceByEndpoint().empty());
}

TEST_F(WiFiProviderTest, OnEndpointRemovedButHasEndpoints) {
  provider_.Start();
  const string ssid0("an_ssid");
  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
  MockWiFiServiceRefPtr service0 = AddMockService(ssid0_bytes,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  EXPECT_EQ(1, GetServices().size());

  // Remove an endpoint of a non-remembered service.
  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
  AddEndpointToService(service0, endpoint0);
  EXPECT_EQ(1, GetServiceByEndpoint().size());

  EXPECT_CALL(*service0, RemoveEndpoint(RefPtrMatch(endpoint0))).Times(1);
  EXPECT_CALL(*service0, HasEndpoints()).WillOnce(Return(true));
  EXPECT_CALL(*service0, IsRemembered()).WillRepeatedly(Return(false));
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
  EXPECT_CALL(*service0, ResetWiFi()).Times(0);
  EXPECT_CALL(manager_, DeregisterService(_)).Times(0);
  provider_.OnEndpointRemoved(endpoint0);
  // Verify now, so it's clear that this happened as a result of the call
  // above, and not anything in the destructor(s).
  Mock::VerifyAndClearExpectations(&manager_);
  Mock::VerifyAndClearExpectations(service0.get());
  EXPECT_EQ(1, GetServices().size());
  EXPECT_TRUE(GetServiceByEndpoint().empty());
}

TEST_F(WiFiProviderTest, OnEndpointRemovedButIsRemembered) {
  provider_.Start();
  const string ssid0("an_ssid");
  const vector<uint8_t> ssid0_bytes(ssid0.begin(), ssid0.end());
  MockWiFiServiceRefPtr service0 = AddMockService(ssid0_bytes,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  EXPECT_EQ(1, GetServices().size());

  // Remove the last endpoint of a remembered service.
  WiFiEndpointRefPtr endpoint0 = MakeEndpoint(ssid0, "00:00:00:00:00:00", 0, 0);
  AddEndpointToService(service0, endpoint0);
  EXPECT_EQ(1, GetServiceByEndpoint().size());

  EXPECT_CALL(*service0, RemoveEndpoint(RefPtrMatch(endpoint0))).Times(1);
  EXPECT_CALL(*service0, HasEndpoints()).WillRepeatedly(Return(false));
  EXPECT_CALL(*service0, IsRemembered()).WillOnce(Return(true));
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(service0))).Times(1);
  EXPECT_CALL(*service0, ResetWiFi()).Times(0);
  EXPECT_CALL(manager_, DeregisterService(_)).Times(0);
  provider_.OnEndpointRemoved(endpoint0);
  // Verify now, so it's clear that this happened as a result of the call
  // above, and not anything in the destructor(s).
  Mock::VerifyAndClearExpectations(&manager_);
  Mock::VerifyAndClearExpectations(service0.get());
  EXPECT_EQ(1, GetServices().size());
  EXPECT_TRUE(GetServiceByEndpoint().empty());
}

TEST_F(WiFiProviderTest, OnEndpointRemovedWhileStopped) {
  // If we don't call provider_.Start(), OnEndpointRemoved should not
  // cause a crash even if a service matching the endpoint does not exist.
  const string ssid("an_ssid");
  WiFiEndpointRefPtr endpoint = MakeEndpoint(ssid, "00:00:00:00:00:00", 0, 0);
  provider_.OnEndpointRemoved(endpoint);
}

TEST_F(WiFiProviderTest, OnEndpointUpdated) {
  provider_.Start();

  // Create an endpoint and associate it with a mock service.
  const string ssid("an_ssid");
  WiFiEndpointRefPtr endpoint = MakeEndpoint(ssid, "00:00:00:00:00:00", 0, 0);

  const vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
  MockWiFiServiceRefPtr open_service = AddMockService(ssid_bytes,
                                                      kModeManaged,
                                                      kSecurityNone,
                                                      false);
  EXPECT_CALL(*open_service, AddEndpoint(RefPtrMatch(endpoint)));
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(open_service)));
  provider_.OnEndpointAdded(endpoint);
  Mock::VerifyAndClearExpectations(open_service.get());

  // WiFiProvider is running and endpoint matches this service.
  EXPECT_CALL(*open_service, NotifyEndpointUpdated(RefPtrMatch(endpoint)));
  EXPECT_CALL(*open_service, AddEndpoint(_)).Times(0);
  provider_.OnEndpointUpdated(endpoint);
  Mock::VerifyAndClearExpectations(open_service.get());

  // If the endpoint is changed in a way that causes it to match a different
  // service, the provider should transfer the endpoint from one service to
  // the other.
  MockWiFiServiceRefPtr rsn_service = AddMockService(ssid_bytes,
                                                     kModeManaged,
                                                     kSecurityRsn,
                                                     false);
  EXPECT_CALL(*open_service, RemoveEndpoint(RefPtrMatch(endpoint)));
  // We are playing out a scenario where the open service is not removed
  // since it still claims to have more endpoints remaining.
  EXPECT_CALL(*open_service, HasEndpoints()).WillOnce(Return(true));
  EXPECT_CALL(*rsn_service, AddEndpoint(RefPtrMatch(endpoint)));
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(open_service)));
  EXPECT_CALL(manager_, UpdateService(RefPtrMatch(rsn_service)));
  endpoint->set_security_mode(kSecurityRsn);
  provider_.OnEndpointUpdated(endpoint);
}

TEST_F(WiFiProviderTest, OnEndpointUpdatedWhileStopped) {
  // If we don't call provider_.Start(), OnEndpointUpdated should not
  // cause a crash even if a service matching the endpoint does not exist.
  const string ssid("an_ssid");
  WiFiEndpointRefPtr endpoint = MakeEndpoint(ssid, "00:00:00:00:00:00", 0, 0);
  provider_.OnEndpointUpdated(endpoint);
}

TEST_F(WiFiProviderTest, OnServiceUnloaded) {
  // This function should never unregister services itself -- the Manager
  // will automatically deregister the service if OnServiceUnloaded()
  // returns true (via WiFiService::Unload()).
  EXPECT_CALL(manager_, DeregisterService(_)).Times(0);

  MockWiFiServiceRefPtr service = AddMockService(vector<uint8_t>(1, '0'),
                                                 kModeManaged,
                                                 kSecurityNone,
                                                 false);
  EXPECT_EQ(1, GetServices().size());
  EXPECT_CALL(*service, HasEndpoints()).WillOnce(Return(true));
  EXPECT_CALL(*service, ResetWiFi()).Times(0);
  EXPECT_FALSE(provider_.OnServiceUnloaded(service));
  EXPECT_EQ(1, GetServices().size());
  Mock::VerifyAndClearExpectations(service.get());

  EXPECT_CALL(*service, HasEndpoints()).WillOnce(Return(false));
  EXPECT_CALL(*service, ResetWiFi()).Times(1);
  EXPECT_TRUE(provider_.OnServiceUnloaded(service));
  // Verify now, so it's clear that this happened as a result of the call
  // above, and not anything in the destructor(s).
  Mock::VerifyAndClearExpectations(service.get());
  EXPECT_TRUE(GetServices().empty());

  Mock::VerifyAndClearExpectations(&manager_);
}

TEST_F(WiFiProviderTest, LoadAndFixupServiceEntriesDefaultProfile) {
  // We test LoadAndFixupServiceEntries indirectly since it calls a static
  // method in WiFiService.
  EXPECT_CALL(metrics_, SendEnumToUMA(
      "Network.Shill.Wifi.ServiceFixupEntries",
      Metrics::kMetricServiceFixupDefaultProfile,
      Metrics::kMetricServiceFixupMax)).Times(1);
  EXPECT_CALL(default_profile_storage_, Flush()).Times(1);
  const string kGroupId =
      StringPrintf("%s_0_0_%s_%s", kTypeWifi, kModeManaged, kSecurityNone);
  EXPECT_CALL(default_profile_storage_,
              GetString(kGroupId, _, _)).WillRepeatedly(Return(false));
  EXPECT_CALL(default_profile_storage_,
              SetString(kGroupId, _, _)).WillRepeatedly(Return(true));
  set<string> groups;
  groups.insert(kGroupId);
  EXPECT_CALL(default_profile_storage_, GetGroups())
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 0), _)).
      WillOnce(Return(true));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 1), _)).
      WillOnce(Return(true));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 2), _)).
      WillOnce(Return(true));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 3), _)).
      WillOnce(Return(false));
  LoadAndFixupServiceEntries(default_profile_.get());
}

TEST_F(WiFiProviderTest, LoadAndFixupServiceEntriesUserProfile) {
  EXPECT_CALL(metrics_, SendEnumToUMA(
      "Network.Shill.Wifi.ServiceFixupEntries",
      Metrics::kMetricServiceFixupUserProfile,
      Metrics::kMetricServiceFixupMax)).Times(1);
  EXPECT_CALL(user_profile_storage_, Flush()).Times(1);
  const string kGroupId =
      StringPrintf("%s_0_0_%s_%s", kTypeWifi, kModeManaged, kSecurityNone);
  EXPECT_CALL(user_profile_storage_,
              GetString(kGroupId, _, _)).WillRepeatedly(Return(false));
  EXPECT_CALL(user_profile_storage_,
              SetString(kGroupId, _, _)).WillRepeatedly(Return(true));
  set<string> groups;
  groups.insert(kGroupId);
  EXPECT_CALL(user_profile_storage_, GetGroups())
      .WillRepeatedly(Return(groups));
  EXPECT_CALL(user_profile_storage_, GetStringList(_, _, _)).Times(0);
  LoadAndFixupServiceEntries(user_profile_.get());
}

TEST_F(WiFiProviderTest, LoadAndFixupServiceEntriesNothingToDo) {
  EXPECT_CALL(metrics_, SendEnumToUMA(_, _, _)).Times(0);
  EXPECT_CALL(default_profile_storage_, Flush()).Times(0);
  const string kGroupId =
      StringPrintf("%s_0_0_%s_%s", kTypeWifi, kModeManaged, kSecurityNone);
  EXPECT_CALL(default_profile_storage_,
              GetString(kGroupId, _, _)).WillRepeatedly(Return(true));
  set<string> groups;
  groups.insert(kGroupId);
  EXPECT_CALL(default_profile_storage_, GetGroups()).WillOnce(Return(groups));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 0), _)).
      WillOnce(Return(true));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 1), _)).
      WillOnce(Return(true));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 2), _)).
      WillOnce(Return(true));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 3), _)).
      WillOnce(Return(false));
  LoadAndFixupServiceEntries(default_profile_.get());
}

TEST_F(WiFiProviderTest, GetHiddenSSIDList) {
  EXPECT_TRUE(provider_.GetHiddenSSIDList().empty());
  const vector<uint8_t> ssid0(1, '0');
  AddMockService(ssid0, kModeManaged, kSecurityNone, false);
  EXPECT_TRUE(provider_.GetHiddenSSIDList().empty());

  const vector<uint8_t> ssid1(1, '1');
  MockWiFiServiceRefPtr service1 = AddMockService(ssid1,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  true);
  EXPECT_CALL(*service1, IsRemembered()).WillRepeatedly(Return(false));
  EXPECT_TRUE(provider_.GetHiddenSSIDList().empty());

  const vector<uint8_t> ssid2(1, '2');
  MockWiFiServiceRefPtr service2 = AddMockService(ssid2,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  true);
  EXPECT_CALL(*service2, IsRemembered()).WillRepeatedly(Return(true));
  ByteArrays ssid_list = provider_.GetHiddenSSIDList();

  EXPECT_EQ(1, ssid_list.size());
  EXPECT_TRUE(ssid_list[0] == ssid2);

  const vector<uint8_t> ssid3(1, '3');
  MockWiFiServiceRefPtr service3 = AddMockService(ssid3,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  EXPECT_CALL(*service2, IsRemembered()).WillRepeatedly(Return(true));

  ssid_list = provider_.GetHiddenSSIDList();
  EXPECT_EQ(1, ssid_list.size());
  EXPECT_TRUE(ssid_list[0] == ssid2);

  const vector<uint8_t> ssid4(1, '4');
  MockWiFiServiceRefPtr service4 = AddMockService(ssid4,
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  true);
  EXPECT_CALL(*service4, IsRemembered()).WillRepeatedly(Return(true));

  ssid_list = provider_.GetHiddenSSIDList();
  EXPECT_EQ(2, ssid_list.size());
  EXPECT_TRUE(ssid_list[0] == ssid2);
  EXPECT_TRUE(ssid_list[1] == ssid4);
}

TEST_F(WiFiProviderTest, StringListToFrequencyMap) {
  vector<string> strings;
  BuildFreqCountStrings(&strings);
  WiFiProvider::ConnectFrequencyMap frequencies_result;
  time_t days = WiFiProvider::StringListToFrequencyMap(strings,
                                                       &frequencies_result);

  WiFiProvider::ConnectFrequencyMap frequencies_expect;
  BuildFreqCountMap(&frequencies_expect);
  EXPECT_THAT(frequencies_result, ContainerEq(frequencies_expect));
  EXPECT_EQ(days, kTestDays);
}

TEST_F(WiFiProviderTest, StringListToFrequencyMapEmpty) {
  vector<string> strings;
  strings.push_back("@50");
  WiFiProvider::ConnectFrequencyMap frequencies_result;
  time_t days = WiFiProvider::StringListToFrequencyMap(strings,
                                                       &frequencies_result);
  EXPECT_TRUE(frequencies_result.empty());
  EXPECT_EQ(days, 50);
}

TEST_F(WiFiProviderTest, FrequencyMapToStringList) {
  WiFiProvider::ConnectFrequencyMap frequencies;
  BuildFreqCountMap(&frequencies);
  vector<string> strings_result;
  WiFiProvider::FrequencyMapToStringList(kTestDays, frequencies,
                                         &strings_result);

  vector<string> strings_expect;
  BuildFreqCountStrings(&strings_expect);
  EXPECT_THAT(strings_result, ContainerEq(strings_expect));
}

TEST_F(WiFiProviderTest, FrequencyMapToStringListEmpty) {
  WiFiProvider::ConnectFrequencyMap frequencies;
  vector<string> strings_result;
  WiFiProvider::FrequencyMapToStringList(kTestDays, frequencies,
                                         &strings_result);
  EXPECT_EQ(1, strings_result.size());
  EXPECT_EQ(*strings_result.begin(), "@20");
}

TEST_F(WiFiProviderTest, FrequencyMapBasicAging) {
  const time_t kThisWeek = kFirstWeek +
      WiFiProvider::kWeeksToKeepFrequencyCounts - 1;
  LoadConnectCountByFrequency(kThisWeek * kSecondsPerWeek);

  // Make sure we have data for all 3 blocks.
  WiFiProvider::ConnectFrequencyMap expected;
  expected[5001] = 2;
  expected[5002] = 2;
  expected[6001] = 1;
  expected[6002] = 2;
  expected[7001] = 1;
  expected[7002] = 2;
  EXPECT_THAT(provider_.connect_count_by_frequency_, ContainerEq(expected));

  // And, then, make sure we output the expected blocks of data.
  EXPECT_CALL(
      default_profile_storage_,
      SetStringList(WiFiProvider::kStorageId, _,
                    Eq(profile_frequency_data_[
                        StringPrintf(
                            "%s%d", WiFiProvider::kStorageFrequencies, 0)])));
  EXPECT_CALL(
      default_profile_storage_,
      SetStringList(WiFiProvider::kStorageId, _,
                    Eq(profile_frequency_data_[
                        StringPrintf(
                            "%s%d", WiFiProvider::kStorageFrequencies, 1)])));
  EXPECT_CALL(
      default_profile_storage_,
      SetStringList(WiFiProvider::kStorageId, _,
                    Eq(profile_frequency_data_[
                        StringPrintf(
                            "%s%d", WiFiProvider::kStorageFrequencies, 2)])));
  vector<string> frequencies;
  frequencies.push_back(base::StringPrintf("@%" PRIu64,
                                           static_cast<uint64_t>(kThisWeek)));
  EXPECT_CALL(
      default_profile_storage_,
      SetStringList(WiFiProvider::kStorageId, _, Eq(frequencies))).Times(0);
  Save();
}

TEST_F(WiFiProviderTest, FrequencyMapAgingIllegalDay) {
  provider_.time_ = &time_;
  const time_t kThisWeek = kFirstWeek +
      WiFiProvider::kWeeksToKeepFrequencyCounts - 1;
  const time_t kThisWeekSeconds = kThisWeek * kSecondsPerWeek;
  EXPECT_CALL(time_, GetSecondsSinceEpoch()).WillOnce(Return(kThisWeekSeconds));
  const string kGroupId =
      StringPrintf("%s_0_0_%s_%s", kTypeWifi, kModeManaged, kSecurityNone);
  EXPECT_CALL(default_profile_storage_,
              GetString(kGroupId, _, _)).WillRepeatedly(Return(true));
  set<string> groups;
  groups.insert(kGroupId);
  // Instead of block[1], return a block without the date.
  EXPECT_CALL(default_profile_storage_, GetGroups()).WillOnce(Return(groups));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 0), _)).
      WillOnce(Invoke(this, &WiFiProviderTest::GetStringList));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 1), _)).
      WillOnce(Invoke(this, &WiFiProviderTest::GetIllegalDayStringList));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 2), _)).
      WillOnce(Invoke(this, &WiFiProviderTest::GetStringList));
  EXPECT_CALL(default_profile_storage_, GetStringList(WiFiProvider::kStorageId,
      StringPrintf("%s%d", WiFiProvider::kStorageFrequencies, 3), _)).
      WillOnce(Invoke(this, &WiFiProviderTest::GetStringList));

  LoadAndFixupServiceEntries(default_profile_.get());

  // Verify that the received information only includes block[0] and block[2].
  WiFiProvider::ConnectFrequencyMap expected;
  expected[5001] = 1;
  expected[5002] = 2;
  expected[7001] = 1;
  expected[7002] = 2;
  EXPECT_THAT(provider_.connect_count_by_frequency_, ContainerEq(expected));

  // And, then, make sure we output the expected blocks of data.
  EXPECT_CALL(
      default_profile_storage_,
      SetStringList(WiFiProvider::kStorageId, _,
                    Eq(profile_frequency_data_[
                        StringPrintf(
                            "%s%d", WiFiProvider::kStorageFrequencies, 0)])));
  EXPECT_CALL(
      default_profile_storage_,
      SetStringList(WiFiProvider::kStorageId, _,
                    Eq(profile_frequency_data_[
                        StringPrintf(
                            "%s%d", WiFiProvider::kStorageFrequencies, 1)]))).
      Times(0);
  EXPECT_CALL(
      default_profile_storage_,
      SetStringList(WiFiProvider::kStorageId, _,
                    Eq(profile_frequency_data_[
                        StringPrintf(
                            "%s%d", WiFiProvider::kStorageFrequencies, 2)])));
  vector<string> frequencies;
  frequencies.push_back(base::StringPrintf("@%" PRIu64,
                                           static_cast<uint64_t>(kThisWeek)));
  EXPECT_CALL(default_profile_storage_,
              SetStringList(WiFiProvider::kStorageId, _, Eq(frequencies)))
      .Times(0);
  Save();
}

TEST_F(WiFiProviderTest, IncrementConnectCount) {
  const time_t kThisWeek = kFirstWeek +
      WiFiProvider::kWeeksToKeepFrequencyCounts - 1;
  const time_t kThisWeekSeconds = kThisWeek * kSecondsPerWeek;
  LoadConnectCountByFrequency(kThisWeekSeconds);

  EXPECT_CALL(time_, GetSecondsSinceEpoch()).WillOnce(Return(kThisWeekSeconds));
  EXPECT_CALL(manager_, UpdateWiFiProvider());
  EXPECT_CALL(metrics_, SendToUMA(Metrics::kMetricFrequenciesConnectedEver,
                                  _, _, _, _));
  time_t newest_week_at_start =
      provider_.connect_count_by_frequency_dated_.crbegin()->first;
  provider_.IncrementConnectCount(6002);

  // Make sure we have data for all 3 blocks.
  WiFiProvider::ConnectFrequencyMap expected;
  expected[5001] = 2;
  expected[5002] = 2;
  expected[6001] = 1;
  expected[6002] = 3;
  expected[7001] = 1;
  expected[7002] = 2;
  EXPECT_THAT(provider_.connect_count_by_frequency_, ContainerEq(expected));
  // Make sure we didn't delete the oldest block.
  EXPECT_TRUE(ContainsKey(provider_.connect_count_by_frequency_dated_,
                          kFirstWeek));
  // Make sure we didn't create a new block.
  time_t newest_week_at_end =
      provider_.connect_count_by_frequency_dated_.crbegin()->first;
  EXPECT_EQ(newest_week_at_start, newest_week_at_end);
}

TEST_F(WiFiProviderTest, IncrementConnectCountCreateNew) {
  time_t this_week = kFirstWeek + WiFiProvider::kWeeksToKeepFrequencyCounts - 1;
  LoadConnectCountByFrequency(this_week * kSecondsPerWeek);

  this_week += 2;
  EXPECT_CALL(time_, GetSecondsSinceEpoch()).
      WillOnce(Return(this_week * kSecondsPerWeek));
  EXPECT_CALL(manager_, UpdateWiFiProvider());
  EXPECT_CALL(metrics_, SendToUMA(Metrics::kMetricFrequenciesConnectedEver,
                                  _, _, _, _));
  time_t newest_week_at_start =
      provider_.connect_count_by_frequency_dated_.crbegin()->first;
  provider_.IncrementConnectCount(6001);

  // Make sure we have data for newest 2 blocks (only).
  WiFiProvider::ConnectFrequencyMap expected;
  expected[5001] = 1;
  expected[6001] = 2;
  expected[6002] = 2;
  expected[7001] = 1;
  expected[7002] = 2;
  EXPECT_THAT(provider_.connect_count_by_frequency_, ContainerEq(expected));
  // Verify that the oldest block is gone.
  EXPECT_FALSE(ContainsKey(provider_.connect_count_by_frequency_dated_,
                           kFirstWeek));
  // Make sure we created a new block and that it is for the current week.
  time_t newest_week_at_end =
      provider_.connect_count_by_frequency_dated_.crbegin()->first;
  EXPECT_NE(newest_week_at_start, newest_week_at_end);
  EXPECT_TRUE(ContainsKey(provider_.connect_count_by_frequency_dated_,
                          this_week));
}

TEST_F(WiFiProviderTest, ReportAutoConnectableServices) {
  MockWiFiServiceRefPtr service0 = AddMockService(vector<uint8_t>(1, '0'),
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  MockWiFiServiceRefPtr service1 = AddMockService(vector<uint8_t>(1, '1'),
                                                  kModeManaged,
                                                  kSecurityNone,
                                                  false);
  service0->EnableAndRetainAutoConnect();
  service0->SetConnectable(true);
  service1->EnableAndRetainAutoConnect();
  service1->SetConnectable(true);

  EXPECT_CALL(*service0, IsAutoConnectable(_))
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  EXPECT_CALL(*service1, IsAutoConnectable(_))
      .WillRepeatedly(Return(false));

  // With 1 auto connectable service.
  EXPECT_CALL(metrics_, NotifyWifiAutoConnectableServices(1));
  provider_.ReportAutoConnectableServices();

  // With no auto connectable service.
  EXPECT_CALL(metrics_, NotifyWifiAutoConnectableServices(_)).Times(0);
  provider_.ReportAutoConnectableServices();
}

TEST_F(WiFiProviderTest, NumAutoConnectableServices) {
  MockWiFiServiceRefPtr service0 = AddMockService(
      vector<uint8_t>(1, '0'), kModeManaged, kSecurityNone, false);
  MockWiFiServiceRefPtr service1 = AddMockService(
      vector<uint8_t>(1, '1'), kModeManaged, kSecurityNone, false);
  service0->EnableAndRetainAutoConnect();
  service0->SetConnectable(true);
  service1->EnableAndRetainAutoConnect();
  service1->SetConnectable(true);

  EXPECT_CALL(*service0, IsAutoConnectable(_))
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  EXPECT_CALL(*service1, IsAutoConnectable(_)).WillRepeatedly(Return(true));

  // 2 auto-connectable services.
  EXPECT_EQ(2, provider_.NumAutoConnectableServices());

  // 1 auto-connectable service.
  EXPECT_EQ(1, provider_.NumAutoConnectableServices());
}

TEST_F(WiFiProviderTest, GetSsidsConfiguredForAutoConnect) {
  vector<uint8_t> ssid0(3, '0');
  vector<uint8_t> ssid1(5, '1');
  ByteString ssid0_bytes(ssid0);
  ByteString ssid1_bytes(ssid1);
  MockWiFiServiceRefPtr service0 =
      AddMockService(ssid0, kModeManaged, kSecurityNone, false);
  MockWiFiServiceRefPtr service1 =
      AddMockService(ssid1, kModeManaged, kSecurityNone, false);
  // 2 services configured for auto-connect.
  service0->SetAutoConnect(true);
  service1->SetAutoConnect(true);
  vector<ByteString> service_list_0 =
      provider_.GetSsidsConfiguredForAutoConnect();
  EXPECT_EQ(2, service_list_0.size());
  EXPECT_TRUE(ssid0_bytes.Equals(service_list_0[0]));
  EXPECT_TRUE(ssid1_bytes.Equals(service_list_0[1]));

  // 1 service configured for auto-connect.
  service0->SetAutoConnect(false);
  service1->SetAutoConnect(true);
  vector<ByteString> service_list_1 =
      provider_.GetSsidsConfiguredForAutoConnect();
  EXPECT_EQ(1, service_list_1.size());
  EXPECT_TRUE(ssid1_bytes.Equals(service_list_1[0]));
}

}  // namespace shill