blob: 431cccf3a695a4386d64a7126cbcc8164ff3ccb9 [file] [log] [blame]
// Copyright (c) 2012 The Chromium 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 "chromeos/network/shill_property_handler.h"
#include <map>
#include <set>
#include <string>
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/values.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/shill_device_client.h"
#include "chromeos/dbus/shill_ipconfig_client.h"
#include "chromeos/dbus/shill_manager_client.h"
#include "chromeos/dbus/shill_profile_client.h"
#include "chromeos/dbus/shill_service_client.h"
#include "dbus/object_path.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
void DoNothingWithCallStatus(DBusMethodCallStatus call_status) {
}
void ErrorCallbackFunction(const std::string& error_name,
const std::string& error_message) {
LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message;
}
class TestListener : public internal::ShillPropertyHandler::Listener {
public:
TestListener() : technology_list_updates_(0),
errors_(0) {
}
virtual void UpdateManagedList(ManagedState::ManagedType type,
const base::ListValue& entries) OVERRIDE {
UpdateEntries(GetTypeString(type), entries);
}
virtual void UpdateManagedStateProperties(
ManagedState::ManagedType type,
const std::string& path,
const base::DictionaryValue& properties) OVERRIDE {
AddInitialPropertyUpdate(GetTypeString(type), path);
}
virtual void ProfileListChanged() OVERRIDE {
}
virtual void UpdateNetworkServiceProperty(
const std::string& service_path,
const std::string& key,
const base::Value& value) OVERRIDE {
AddPropertyUpdate(shill::kServicesProperty, service_path);
}
virtual void UpdateDeviceProperty(
const std::string& device_path,
const std::string& key,
const base::Value& value) OVERRIDE {
AddPropertyUpdate(shill::kDevicesProperty, device_path);
}
virtual void TechnologyListChanged() OVERRIDE {
++technology_list_updates_;
}
virtual void CheckPortalListChanged(
const std::string& check_portal_list) OVERRIDE {
}
virtual void ManagedStateListChanged(
ManagedState::ManagedType type) OVERRIDE {
AddStateListUpdate(GetTypeString(type));
}
std::vector<std::string>& entries(const std::string& type) {
return entries_[type];
}
std::map<std::string, int>& property_updates(const std::string& type) {
return property_updates_[type];
}
std::map<std::string, int>& initial_property_updates(
const std::string& type) {
return initial_property_updates_[type];
}
int list_updates(const std::string& type) { return list_updates_[type]; }
int technology_list_updates() { return technology_list_updates_; }
int errors() { return errors_; }
private:
std::string GetTypeString(ManagedState::ManagedType type) {
if (type == ManagedState::MANAGED_TYPE_NETWORK) {
return shill::kServicesProperty;
} else if (type == ManagedState::MANAGED_TYPE_FAVORITE) {
return shill::kServiceCompleteListProperty;
} else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
return shill::kDevicesProperty;
}
LOG(ERROR) << "UpdateManagedList called with unrecognized type: " << type;
++errors_;
return std::string();
}
void UpdateEntries(const std::string& type, const base::ListValue& entries) {
if (type.empty())
return;
entries_[type].clear();
for (base::ListValue::const_iterator iter = entries.begin();
iter != entries.end(); ++iter) {
std::string path;
if ((*iter)->GetAsString(&path))
entries_[type].push_back(path);
}
}
void AddPropertyUpdate(const std::string& type, const std::string& path) {
if (type.empty())
return;
property_updates(type)[path] += 1;
}
void AddInitialPropertyUpdate(const std::string& type,
const std::string& path) {
if (type.empty())
return;
initial_property_updates(type)[path] += 1;
}
void AddStateListUpdate(const std::string& type) {
if (type.empty())
return;
list_updates_[type] += 1;
}
// Map of list-type -> paths
std::map<std::string, std::vector<std::string> > entries_;
// Map of list-type -> map of paths -> update counts
std::map<std::string, std::map<std::string, int> > property_updates_;
std::map<std::string, std::map<std::string, int> > initial_property_updates_;
// Map of list-type -> list update counts
std::map<std::string, int > list_updates_;
int technology_list_updates_;
int errors_;
};
} // namespace
class ShillPropertyHandlerTest : public testing::Test {
public:
ShillPropertyHandlerTest()
: manager_test_(NULL),
device_test_(NULL),
service_test_(NULL),
profile_test_(NULL) {
}
virtual ~ShillPropertyHandlerTest() {
}
virtual void SetUp() OVERRIDE {
// Initialize DBusThreadManager with a stub implementation.
DBusThreadManager::InitializeWithStub();
// Get the test interface for manager / device / service and clear the
// default stub properties.
manager_test_ =
DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface();
ASSERT_TRUE(manager_test_);
device_test_ =
DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface();
ASSERT_TRUE(device_test_);
service_test_ =
DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
ASSERT_TRUE(service_test_);
profile_test_ =
DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface();
ASSERT_TRUE(profile_test_);
SetupShillPropertyHandler();
message_loop_.RunUntilIdle();
}
virtual void TearDown() OVERRIDE {
shill_property_handler_.reset();
listener_.reset();
DBusThreadManager::Shutdown();
}
void AddDevice(const std::string& type, const std::string& id) {
ASSERT_TRUE(IsValidType(type));
device_test_->AddDevice(id, type, std::string("/device/" + id));
}
void RemoveDevice(const std::string& id) {
device_test_->RemoveDevice(id);
}
void AddService(const std::string& type,
const std::string& id,
const std::string& state,
bool add_to_watch_list) {
ASSERT_TRUE(IsValidType(type));
service_test_->AddService(id, id, type, state,
true /* visible */, add_to_watch_list);
}
void AddServiceWithIPConfig(const std::string& type,
const std::string& id,
const std::string& state,
const std::string& ipconfig_path,
bool add_to_watch_list) {
ASSERT_TRUE(IsValidType(type));
service_test_->AddServiceWithIPConfig(id, id, type, state,
ipconfig_path,
true /* visible */,
add_to_watch_list);
}
void AddServiceToProfile(const std::string& type,
const std::string& id,
bool visible) {
service_test_->AddService(id, id, type, shill::kStateIdle,
visible, false /* watch */);
std::vector<std::string> profiles;
profile_test_->GetProfilePaths(&profiles);
ASSERT_TRUE(profiles.size() > 0);
base::DictionaryValue properties; // Empty entry
profile_test_->AddService(profiles[0], id);
}
void RemoveService(const std::string& id) {
service_test_->RemoveService(id);
}
// Call this after any initial Shill client setup
void SetupShillPropertyHandler() {
SetupDefaultShillState();
listener_.reset(new TestListener);
shill_property_handler_.reset(
new internal::ShillPropertyHandler(listener_.get()));
shill_property_handler_->Init();
}
bool IsValidType(const std::string& type) {
return (type == shill::kTypeEthernet ||
type == shill::kTypeEthernetEap ||
type == shill::kTypeWifi ||
type == shill::kTypeWimax ||
type == shill::kTypeBluetooth ||
type == shill::kTypeCellular ||
type == shill::kTypeVPN);
}
protected:
void SetupDefaultShillState() {
message_loop_.RunUntilIdle(); // Process any pending updates
device_test_->ClearDevices();
AddDevice(shill::kTypeWifi, "stub_wifi_device1");
AddDevice(shill::kTypeCellular, "stub_cellular_device1");
service_test_->ClearServices();
const bool add_to_watchlist = true;
AddService(shill::kTypeEthernet, "stub_ethernet",
shill::kStateOnline, add_to_watchlist);
AddService(shill::kTypeWifi, "stub_wifi1",
shill::kStateOnline, add_to_watchlist);
AddService(shill::kTypeWifi, "stub_wifi2",
shill::kStateIdle, add_to_watchlist);
AddService(shill::kTypeCellular, "stub_cellular1",
shill::kStateIdle, add_to_watchlist);
}
base::MessageLoopForUI message_loop_;
scoped_ptr<TestListener> listener_;
scoped_ptr<internal::ShillPropertyHandler> shill_property_handler_;
ShillManagerClient::TestInterface* manager_test_;
ShillDeviceClient::TestInterface* device_test_;
ShillServiceClient::TestInterface* service_test_;
ShillProfileClient::TestInterface* profile_test_;
private:
DISALLOW_COPY_AND_ASSIGN(ShillPropertyHandlerTest);
};
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerStub) {
EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable(shill::kTypeWifi));
EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(shill::kTypeWifi));
const size_t kNumShillManagerClientStubImplDevices = 2;
EXPECT_EQ(kNumShillManagerClientStubImplDevices,
listener_->entries(shill::kDevicesProperty).size());
const size_t kNumShillManagerClientStubImplServices = 4;
EXPECT_EQ(kNumShillManagerClientStubImplServices,
listener_->entries(shill::kServicesProperty).size());
EXPECT_EQ(0, listener_->errors());
}
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerTechnologyChanged) {
const int initial_technology_updates = 2; // Available and Enabled lists
EXPECT_EQ(initial_technology_updates, listener_->technology_list_updates());
// Remove a technology. Updates both the Available and Enabled lists.
manager_test_->RemoveTechnology(shill::kTypeWimax);
message_loop_.RunUntilIdle();
EXPECT_EQ(initial_technology_updates + 2,
listener_->technology_list_updates());
// Add a disabled technology.
manager_test_->AddTechnology(shill::kTypeWimax, false);
message_loop_.RunUntilIdle();
EXPECT_EQ(initial_technology_updates + 3,
listener_->technology_list_updates());
EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable(
shill::kTypeWimax));
EXPECT_FALSE(shill_property_handler_->IsTechnologyEnabled(shill::kTypeWimax));
// Enable the technology.
DBusThreadManager::Get()->GetShillManagerClient()->EnableTechnology(
shill::kTypeWimax,
base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
message_loop_.RunUntilIdle();
EXPECT_EQ(initial_technology_updates + 4,
listener_->technology_list_updates());
EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(shill::kTypeWimax));
EXPECT_EQ(0, listener_->errors());
}
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerDevicePropertyChanged) {
EXPECT_EQ(1, listener_->list_updates(shill::kDevicesProperty));
const size_t kNumShillManagerClientStubImplDevices = 2;
EXPECT_EQ(kNumShillManagerClientStubImplDevices,
listener_->entries(shill::kDevicesProperty).size());
// Add a device.
const std::string kTestDevicePath("test_wifi_device1");
AddDevice(shill::kTypeWifi, kTestDevicePath);
message_loop_.RunUntilIdle();
EXPECT_EQ(2, listener_->list_updates(shill::kDevicesProperty));
EXPECT_EQ(kNumShillManagerClientStubImplDevices + 1,
listener_->entries(shill::kDevicesProperty).size());
// Device changes are not observed.
// Remove a device
RemoveDevice(kTestDevicePath);
message_loop_.RunUntilIdle();
EXPECT_EQ(3, listener_->list_updates(shill::kDevicesProperty));
EXPECT_EQ(kNumShillManagerClientStubImplDevices,
listener_->entries(shill::kDevicesProperty).size());
EXPECT_EQ(0, listener_->errors());
}
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServicePropertyChanged) {
EXPECT_EQ(1, listener_->list_updates(shill::kServicesProperty));
const size_t kNumShillManagerClientStubImplServices = 4;
EXPECT_EQ(kNumShillManagerClientStubImplServices,
listener_->entries(shill::kServicesProperty).size());
// Add an unwatched service.
const std::string kTestServicePath("test_wifi_service1");
AddService(shill::kTypeWifi, kTestServicePath, shill::kStateIdle, false);
message_loop_.RunUntilIdle();
// Watched and unwatched services trigger a service list update.
EXPECT_EQ(2, listener_->list_updates(shill::kServicesProperty));
EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
listener_->entries(shill::kServicesProperty).size());
// Service receives an initial property update.
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServicesProperty)[kTestServicePath]);
// Change a property.
base::FundamentalValue scan_interval(3);
DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
dbus::ObjectPath(kTestServicePath),
shill::kScanIntervalProperty,
scan_interval,
base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
message_loop_.RunUntilIdle();
// Property change triggers an update.
EXPECT_EQ(1, listener_->property_updates(
shill::kServicesProperty)[kTestServicePath]);
// Add the existing service to the watch list.
AddService(shill::kTypeWifi, kTestServicePath, shill::kStateIdle, true);
message_loop_.RunUntilIdle();
// Service list update should be received when watch list changes.
EXPECT_EQ(2, listener_->list_updates(shill::kServicesProperty));
// Number of services shouldn't change.
EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
listener_->entries(shill::kServicesProperty).size());
// Change a property.
DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
dbus::ObjectPath(kTestServicePath),
shill::kScanIntervalProperty,
scan_interval,
base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
message_loop_.RunUntilIdle();
// Property change should trigger another update.
EXPECT_EQ(2, listener_->property_updates(
shill::kServicesProperty)[kTestServicePath]);
// Remove a service
RemoveService(kTestServicePath);
message_loop_.RunUntilIdle();
EXPECT_EQ(3, listener_->list_updates(shill::kServicesProperty));
EXPECT_EQ(kNumShillManagerClientStubImplServices,
listener_->entries(shill::kServicesProperty).size());
EXPECT_EQ(0, listener_->errors());
}
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerIPConfigPropertyChanged) {
// Set the properties for an IP Config object.
const std::string kTestIPConfigPath("test_ip_config_path");
base::StringValue ip_address("192.168.1.1");
DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
dbus::ObjectPath(kTestIPConfigPath),
shill::kAddressProperty, ip_address,
base::Bind(&DoNothingWithCallStatus));
base::ListValue dns_servers;
dns_servers.Append(base::Value::CreateStringValue("192.168.1.100"));
dns_servers.Append(base::Value::CreateStringValue("192.168.1.101"));
DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
dbus::ObjectPath(kTestIPConfigPath),
shill::kNameServersProperty, dns_servers,
base::Bind(&DoNothingWithCallStatus));
base::FundamentalValue prefixlen(8);
DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
dbus::ObjectPath(kTestIPConfigPath),
shill::kPrefixlenProperty, prefixlen,
base::Bind(&DoNothingWithCallStatus));
base::StringValue gateway("192.0.0.1");
DBusThreadManager::Get()->GetShillIPConfigClient()->SetProperty(
dbus::ObjectPath(kTestIPConfigPath),
shill::kGatewayProperty, gateway,
base::Bind(&DoNothingWithCallStatus));
message_loop_.RunUntilIdle();
// Add a service with an empty ipconfig and then update
// its ipconfig property.
const std::string kTestServicePath1("test_wifi_service1");
AddService(shill::kTypeWifi, kTestServicePath1, shill::kStateIdle, true);
message_loop_.RunUntilIdle();
// This is the initial property update.
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServicesProperty)[kTestServicePath1]);
DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
dbus::ObjectPath(kTestServicePath1),
shill::kIPConfigProperty,
base::StringValue(kTestIPConfigPath),
base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
message_loop_.RunUntilIdle();
// IPConfig property change on the service should trigger property updates for
// IP Address, DNS, prefixlen, and gateway.
EXPECT_EQ(4, listener_->property_updates(
shill::kServicesProperty)[kTestServicePath1]);
// Now, Add a new watched service with the IPConfig already set.
const std::string kTestServicePath2("test_wifi_service2");
AddServiceWithIPConfig(shill::kTypeWifi, kTestServicePath2,
shill::kStateIdle, kTestIPConfigPath, true);
message_loop_.RunUntilIdle();
// A watched service with the IPConfig property already set must trigger
// property updates for IP Address, DNS, prefixlen, and gateway when added.
EXPECT_EQ(4, listener_->property_updates(
shill::kServicesProperty)[kTestServicePath2]);
}
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServiceCompleteList) {
// Initial list updates.
EXPECT_EQ(1, listener_->list_updates(shill::kServicesProperty));
EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty));
// Add a new entry to the profile only; should trigger a single list update
// for both Services and ServiceCompleteList, and a single property update
// for ServiceCompleteList.
const std::string kTestServicePath1("stub_wifi_profile_only1");
AddServiceToProfile(shill::kTypeWifi, kTestServicePath1, false);
shill_property_handler_->UpdateManagerProperties();
message_loop_.RunUntilIdle();
EXPECT_EQ(2, listener_->list_updates(shill::kServicesProperty));
EXPECT_EQ(2, listener_->list_updates(shill::kServiceCompleteListProperty));
EXPECT_EQ(0, listener_->initial_property_updates(
shill::kServicesProperty)[kTestServicePath1]);
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath1]);
EXPECT_EQ(0, listener_->property_updates(
shill::kServicesProperty)[kTestServicePath1]);
EXPECT_EQ(0, listener_->property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath1]);
// Add a new entry to the services and the profile; should also trigger a
// single list update for both Services and ServiceCompleteList, and should
// trigger tow property updates for Services (one when the Profile propety
// changes, and one for the Request) and one ServiceCompleteList change for
// the Request.
const std::string kTestServicePath2("stub_wifi_profile_only2");
AddServiceToProfile(shill::kTypeWifi, kTestServicePath2, true);
shill_property_handler_->UpdateManagerProperties();
message_loop_.RunUntilIdle();
EXPECT_EQ(3, listener_->list_updates(shill::kServicesProperty));
EXPECT_EQ(3, listener_->list_updates(shill::kServiceCompleteListProperty));
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServicesProperty)[kTestServicePath2]);
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath2]);
// Expect one property update for the Profile property of the Network.
EXPECT_EQ(1, listener_->property_updates(
shill::kServicesProperty)[kTestServicePath2]);
EXPECT_EQ(0, listener_->property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath2]);
// Change a property of a Network in a Profile.
base::FundamentalValue scan_interval(3);
DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
dbus::ObjectPath(kTestServicePath2),
shill::kScanIntervalProperty,
scan_interval,
base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
message_loop_.RunUntilIdle();
// Property change should trigger an update for the Network only; no
// property updates pushed by Shill affect Favorites.
EXPECT_EQ(2, listener_->property_updates(
shill::kServicesProperty)[kTestServicePath2]);
EXPECT_EQ(0, listener_->property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath2]);
}
} // namespace chromeos