blob: 75931942d925f07bd0908f878d8b1a79532b526a [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) {
}
void UpdateManagedList(ManagedState::ManagedType type,
const base::ListValue& entries) override {
VLOG(1) << "UpdateManagedList[" << ManagedState::TypeToString(type) << "]: "
<< entries.GetSize();
UpdateEntries(GetTypeString(type), entries);
}
void UpdateManagedStateProperties(
ManagedState::ManagedType type,
const std::string& path,
const base::DictionaryValue& properties) override {
VLOG(2) << "UpdateManagedStateProperties: " << GetTypeString(type);
initial_property_updates(GetTypeString(type))[path] += 1;
}
void ProfileListChanged() override {}
void UpdateNetworkServiceProperty(const std::string& service_path,
const std::string& key,
const base::Value& value) override {
AddPropertyUpdate(shill::kServiceCompleteListProperty, service_path);
}
void UpdateDeviceProperty(const std::string& device_path,
const std::string& key,
const base::Value& value) override {
AddPropertyUpdate(shill::kDevicesProperty, device_path);
}
void UpdateIPConfigProperties(
ManagedState::ManagedType type,
const std::string& path,
const std::string& ip_config_path,
const base::DictionaryValue& properties) override {
AddPropertyUpdate(shill::kIPConfigsProperty, ip_config_path);
}
void TechnologyListChanged() override {
VLOG(1) << "TechnologyListChanged.";
++technology_list_updates_;
}
void CheckPortalListChanged(const std::string& check_portal_list) override {}
void ManagedStateListChanged(ManagedState::ManagedType type) override {
VLOG(1) << "ManagedStateListChanged: " << GetTypeString(type);
AddStateListUpdate(GetTypeString(type));
}
void DefaultNetworkServiceChanged(const std::string& service_path) override {}
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_; }
void reset_list_updates() {
VLOG(1) << "=== RESET LIST UPDATES ===";
list_updates_.clear();
technology_list_updates_ = 0;
}
int errors() { return errors_; }
private:
std::string GetTypeString(ManagedState::ManagedType type) {
if (type == ManagedState::MANAGED_TYPE_NETWORK)
return shill::kServiceCompleteListProperty;
if (type == ManagedState::MANAGED_TYPE_DEVICE)
return shill::kDevicesProperty;
NOTREACHED();
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) {
DCHECK(!type.empty());
VLOG(2) << "AddPropertyUpdate: " << type;
property_updates(type)[path] += 1;
}
void AddStateListUpdate(const std::string& type) {
DCHECK(!type.empty());
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) {
}
~ShillPropertyHandlerTest() override {}
void SetUp() override {
// Initialize DBusThreadManager with a stub implementation.
DBusThreadManager::Initialize();
// 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();
}
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, 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) {
VLOG(2) << "AddService: " << type << ": " << id << ": " << state;
ASSERT_TRUE(IsValidType(type));
service_test_->AddService(id /* service_path */,
id /* guid */,
id /* name */,
type,
state,
true /* visible */);
}
void AddServiceWithIPConfig(const std::string& type,
const std::string& id,
const std::string& state,
const std::string& ipconfig_path) {
ASSERT_TRUE(IsValidType(type));
service_test_->AddServiceWithIPConfig(id, /* service_path */
id /* guid */,
id /* name */,
type,
state,
ipconfig_path,
true /* visible */);
}
void AddServiceToProfile(const std::string& type,
const std::string& id,
bool visible) {
service_test_->AddService(id /* service_path */,
id /* guid */,
id /* name */,
type,
shill::kStateIdle,
visible);
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();
AddService(shill::kTypeEthernet, "stub_ethernet", shill::kStateOnline);
AddService(shill::kTypeWifi, "stub_wifi1", shill::kStateOnline);
AddService(shill::kTypeWifi, "stub_wifi2", shill::kStateIdle);
AddService(shill::kTypeCellular, "stub_cellular1", shill::kStateIdle);
}
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::kServiceCompleteListProperty).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 an enabled technology. Updates both the Available and Enabled lists.
listener_->reset_list_updates();
manager_test_->RemoveTechnology(shill::kTypeWifi);
message_loop_.RunUntilIdle();
EXPECT_EQ(2, listener_->technology_list_updates());
// Add a disabled technology.
listener_->reset_list_updates();
manager_test_->AddTechnology(shill::kTypeWifi, false);
message_loop_.RunUntilIdle();
EXPECT_EQ(1, listener_->technology_list_updates());
EXPECT_TRUE(shill_property_handler_->IsTechnologyAvailable(
shill::kTypeWifi));
EXPECT_FALSE(shill_property_handler_->IsTechnologyEnabled(shill::kTypeWifi));
// Enable the technology.
listener_->reset_list_updates();
DBusThreadManager::Get()->GetShillManagerClient()->EnableTechnology(
shill::kTypeWifi,
base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
message_loop_.RunUntilIdle();
EXPECT_EQ(1, listener_->technology_list_updates());
EXPECT_TRUE(shill_property_handler_->IsTechnologyEnabled(shill::kTypeWifi));
EXPECT_EQ(0, listener_->errors());
}
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerDevicePropertyChanged) {
const size_t kNumShillManagerClientStubImplDevices = 2;
EXPECT_EQ(kNumShillManagerClientStubImplDevices,
listener_->entries(shill::kDevicesProperty).size());
// Add a device.
listener_->reset_list_updates();
const std::string kTestDevicePath("test_wifi_device1");
AddDevice(shill::kTypeWifi, kTestDevicePath);
message_loop_.RunUntilIdle();
EXPECT_EQ(1, listener_->list_updates(shill::kDevicesProperty));
EXPECT_EQ(kNumShillManagerClientStubImplDevices + 1,
listener_->entries(shill::kDevicesProperty).size());
// Remove a device
listener_->reset_list_updates();
RemoveDevice(kTestDevicePath);
message_loop_.RunUntilIdle();
EXPECT_EQ(1, listener_->list_updates(shill::kDevicesProperty));
EXPECT_EQ(kNumShillManagerClientStubImplDevices,
listener_->entries(shill::kDevicesProperty).size());
EXPECT_EQ(0, listener_->errors());
}
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServicePropertyChanged) {
const size_t kNumShillManagerClientStubImplServices = 4;
EXPECT_EQ(kNumShillManagerClientStubImplServices,
listener_->entries(shill::kServiceCompleteListProperty).size());
// Add a service.
listener_->reset_list_updates();
const std::string kTestServicePath("test_wifi_service1");
AddService(shill::kTypeWifi, kTestServicePath, shill::kStateIdle);
message_loop_.RunUntilIdle();
// Add should trigger a service list update and update entries.
EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty));
EXPECT_EQ(kNumShillManagerClientStubImplServices + 1,
listener_->entries(shill::kServiceCompleteListProperty).size());
// Service receives an initial property update.
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServiceCompleteListProperty)[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 (but not a service list update).
EXPECT_EQ(1, listener_->property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath]);
// Change the visibility of a service. This will trigger a service list
// updates.
listener_->reset_list_updates();
DBusThreadManager::Get()->GetShillServiceClient()->SetProperty(
dbus::ObjectPath(kTestServicePath),
shill::kVisibleProperty,
base::FundamentalValue(false),
base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction));
message_loop_.RunUntilIdle();
EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty));
// Remove a service. This will update the entries and signal a service list
// update.
listener_->reset_list_updates();
RemoveService(kTestServicePath);
message_loop_.RunUntilIdle();
EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty));
EXPECT_EQ(kNumShillManagerClientStubImplServices,
listener_->entries(shill::kServiceCompleteListProperty).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(new base::StringValue("192.168.1.100"));
dns_servers.Append(new base::StringValue("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);
message_loop_.RunUntilIdle();
// This is the initial property update.
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServiceCompleteListProperty)[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 an IPConfigs update.
EXPECT_EQ(1, listener_->property_updates(
shill::kIPConfigsProperty)[kTestIPConfigPath]);
// Now, Add a new service with the IPConfig already set.
const std::string kTestServicePath2("test_wifi_service2");
AddServiceWithIPConfig(shill::kTypeWifi, kTestServicePath2,
shill::kStateIdle, kTestIPConfigPath);
message_loop_.RunUntilIdle();
// A service with the IPConfig property already set should trigger an
// additional IPConfigs update.
EXPECT_EQ(2, listener_->property_updates(
shill::kIPConfigsProperty)[kTestIPConfigPath]);
}
TEST_F(ShillPropertyHandlerTest, ShillPropertyHandlerServiceList) {
// Add an entry to the profile only.
const std::string kTestServicePath1("stub_wifi_profile_only1");
AddServiceToProfile(shill::kTypeWifi, kTestServicePath1, false /* visible */);
message_loop_.RunUntilIdle();
// Update the Manager properties. This should trigger a single list update,
// an initial property update, and a regular property update.
listener_->reset_list_updates();
shill_property_handler_->UpdateManagerProperties();
message_loop_.RunUntilIdle();
EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty));
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath1]);
EXPECT_EQ(1, listener_->property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath1]);
// Add a new entry to the services and the profile; should also trigger a
// service list update, and a property update.
listener_->reset_list_updates();
const std::string kTestServicePath2("stub_wifi_profile_only2");
AddServiceToProfile(shill::kTypeWifi, kTestServicePath2, true);
shill_property_handler_->UpdateManagerProperties();
message_loop_.RunUntilIdle();
EXPECT_EQ(1, listener_->list_updates(shill::kServiceCompleteListProperty));
EXPECT_EQ(1, listener_->initial_property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath2]);
EXPECT_EQ(1, listener_->property_updates(
shill::kServiceCompleteListProperty)[kTestServicePath2]);
}
TEST_F(ShillPropertyHandlerTest, ProhibitedTechnologies) {
std::vector<std::string> prohibited_technologies;
prohibited_technologies.push_back(shill::kTypeEthernet);
EXPECT_TRUE(
shill_property_handler_->IsTechnologyEnabled(shill::kTypeEthernet));
shill_property_handler_->SetProhibitedTechnologies(
prohibited_technologies, network_handler::ErrorCallback());
message_loop_.RunUntilIdle();
// Disabled
EXPECT_FALSE(
shill_property_handler_->IsTechnologyEnabled(shill::kTypeEthernet));
// Can not enable it back
shill_property_handler_->SetTechnologyEnabled(
shill::kTypeEthernet, true, network_handler::ErrorCallback());
message_loop_.RunUntilIdle();
EXPECT_FALSE(
shill_property_handler_->IsTechnologyEnabled(shill::kTypeEthernet));
// Can enable it back after policy changes
prohibited_technologies.clear();
shill_property_handler_->SetProhibitedTechnologies(
prohibited_technologies, network_handler::ErrorCallback());
shill_property_handler_->SetTechnologyEnabled(
shill::kTypeEthernet, true, network_handler::ErrorCallback());
message_loop_.RunUntilIdle();
EXPECT_TRUE(
shill_property_handler_->IsTechnologyEnabled(shill::kTypeEthernet));
}
} // namespace chromeos