// 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/network_state_handler.h" #include #include #include #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_manager_client.h" #include "chromeos/dbus/shill_profile_client.h" #include "chromeos/dbus/shill_service_client.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler_observer.h" #include "dbus/object_path.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace { void ErrorCallbackFunction(const std::string& error_name, const std::string& error_message) { LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message; } const std::string kShillManagerClientStubDefaultService = "eth1"; const std::string kShillManagerClientStubDefaultWireless = "wifi1"; const std::string kShillManagerClientStubWireless2 = "wifi2"; const std::string kShillManagerClientStubCellular = "cellular1"; using chromeos::NetworkState; using chromeos::NetworkStateHandler; class TestObserver : public chromeos::NetworkStateHandlerObserver { public: explicit TestObserver(NetworkStateHandler* handler) : handler_(handler), manager_changed_count_(0), network_count_(0), default_network_change_count_(0), favorite_count_(0) { } virtual ~TestObserver() { } virtual void NetworkManagerChanged() OVERRIDE { ++manager_changed_count_; } virtual void NetworkListChanged() OVERRIDE { NetworkStateHandler::NetworkStateList networks; handler_->GetNetworkList(&networks); network_count_ = networks.size(); if (network_count_ == 0) { default_network_ = ""; default_network_connection_state_ = ""; } NetworkStateHandler::FavoriteStateList favorites; handler_->GetFavoriteList(&favorites); favorite_count_ = favorites.size(); } virtual void DefaultNetworkChanged(const NetworkState* network) OVERRIDE { ++default_network_change_count_; default_network_ = network ? network->path() : ""; default_network_connection_state_ = network ? network->connection_state() : ""; } virtual void NetworkConnectionStateChanged( const NetworkState* network) OVERRIDE { network_connection_state_[network->path()] = network->connection_state(); connection_state_changes_[network->path()]++; } virtual void NetworkPropertiesUpdated(const NetworkState* network) OVERRIDE { DCHECK(network); property_updates_[network->path()]++; } size_t manager_changed_count() { return manager_changed_count_; } size_t network_count() { return network_count_; } size_t default_network_change_count() { return default_network_change_count_; } std::string default_network() { return default_network_; } std::string default_network_connection_state() { return default_network_connection_state_; } size_t favorite_count() { return favorite_count_; } int PropertyUpdatesForService(const std::string& service_path) { return property_updates_[service_path]; } int ConnectionStateChangesForService(const std::string& service_path) { return connection_state_changes_[service_path]; } std::string NetworkConnectionStateForService( const std::string& service_path) { return network_connection_state_[service_path]; } private: NetworkStateHandler* handler_; size_t manager_changed_count_; size_t network_count_; size_t default_network_change_count_; std::string default_network_; std::string default_network_connection_state_; size_t favorite_count_; std::map property_updates_; std::map connection_state_changes_; std::map network_connection_state_; DISALLOW_COPY_AND_ASSIGN(TestObserver); }; } // namespace namespace chromeos { class NetworkStateHandlerTest : public testing::Test { public: NetworkStateHandlerTest() {} virtual ~NetworkStateHandlerTest() {} virtual void SetUp() OVERRIDE { // Initialize DBusThreadManager with a stub implementation. DBusThreadManager::InitializeWithStub(); SetupNetworkStateHandler(); message_loop_.RunUntilIdle(); } virtual void TearDown() OVERRIDE { network_state_handler_->RemoveObserver(test_observer_.get(), FROM_HERE); test_observer_.reset(); network_state_handler_.reset(); DBusThreadManager::Shutdown(); } void SetupNetworkStateHandler() { SetupDefaultShillState(); network_state_handler_.reset(new NetworkStateHandler); test_observer_.reset(new TestObserver(network_state_handler_.get())); network_state_handler_->AddObserver(test_observer_.get(), FROM_HERE); network_state_handler_->InitShillPropertyHandler(); } protected: void SetupDefaultShillState() { message_loop_.RunUntilIdle(); // Process any pending updates ShillDeviceClient::TestInterface* device_test = DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface(); device_test->ClearDevices(); device_test->AddDevice("/device/stub_wifi_device1", flimflam::kTypeWifi, "stub_wifi_device1"); device_test->AddDevice("/device/stub_cellular_device1", flimflam::kTypeCellular, "stub_cellular_device1"); ShillServiceClient::TestInterface* service_test = DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); service_test->ClearServices(); const bool add_to_visible = true; const bool add_to_watchlist = true; service_test->AddService(kShillManagerClientStubDefaultService, kShillManagerClientStubDefaultService, flimflam::kTypeEthernet, flimflam::kStateOnline, add_to_visible, add_to_watchlist); service_test->AddService(kShillManagerClientStubDefaultWireless, kShillManagerClientStubDefaultWireless, flimflam::kTypeWifi, flimflam::kStateOnline, add_to_visible, add_to_watchlist); service_test->AddService(kShillManagerClientStubWireless2, kShillManagerClientStubWireless2, flimflam::kTypeWifi, flimflam::kStateIdle, add_to_visible, add_to_watchlist); service_test->AddService(kShillManagerClientStubCellular, kShillManagerClientStubCellular, flimflam::kTypeCellular, flimflam::kStateIdle, add_to_visible, add_to_watchlist); } base::MessageLoopForUI message_loop_; scoped_ptr network_state_handler_; scoped_ptr test_observer_; private: DISALLOW_COPY_AND_ASSIGN(NetworkStateHandlerTest); }; TEST_F(NetworkStateHandlerTest, NetworkStateHandlerStub) { // Ensure that the network list is the expected size. const size_t kNumShillManagerClientStubImplServices = 4; EXPECT_EQ(kNumShillManagerClientStubImplServices, test_observer_->network_count()); // Ensure that the first stub network is the default network. EXPECT_EQ(kShillManagerClientStubDefaultService, test_observer_->default_network()); EXPECT_EQ(kShillManagerClientStubDefaultService, network_state_handler_->ConnectedNetworkByType( NetworkStateHandler::kMatchTypeDefault)->path()); EXPECT_EQ(kShillManagerClientStubDefaultService, network_state_handler_->ConnectedNetworkByType( flimflam::kTypeEthernet)->path()); EXPECT_EQ(kShillManagerClientStubDefaultWireless, network_state_handler_->ConnectedNetworkByType( NetworkStateHandler::kMatchTypeWireless)->path()); EXPECT_EQ(flimflam::kStateOnline, test_observer_->default_network_connection_state()); } TEST_F(NetworkStateHandlerTest, TechnologyChanged) { // There may be several manager changes during initialization. size_t initial_changed_count = test_observer_->manager_changed_count(); // Enable a technology. EXPECT_NE(NetworkStateHandler::TECHNOLOGY_ENABLED, network_state_handler_->GetTechnologyState(flimflam::kTypeWimax)); network_state_handler_->SetTechnologyEnabled( flimflam::kTypeWimax, true, network_handler::ErrorCallback()); // The technology state should immediately change to ENABLING and we should // receive a manager changed callback. EXPECT_EQ(initial_changed_count + 1, test_observer_->manager_changed_count()); EXPECT_EQ(NetworkStateHandler::TECHNOLOGY_ENABLING, network_state_handler_->GetTechnologyState(flimflam::kTypeWimax)); message_loop_.RunUntilIdle(); // Ensure we receive another manager changed callbacks when the technology // becomes enabled. EXPECT_EQ(initial_changed_count + 2, test_observer_->manager_changed_count()); EXPECT_EQ(NetworkStateHandler::TECHNOLOGY_ENABLED, network_state_handler_->GetTechnologyState(flimflam::kTypeWimax)); } TEST_F(NetworkStateHandlerTest, TechnologyState) { ShillManagerClient::TestInterface* manager_test = DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface(); EXPECT_EQ(NetworkStateHandler::TECHNOLOGY_UNAVAILABLE, network_state_handler_->GetTechnologyState(flimflam::kTypeWimax)); manager_test->AddTechnology(flimflam::kTypeWimax, false); message_loop_.RunUntilIdle(); EXPECT_EQ(NetworkStateHandler::TECHNOLOGY_AVAILABLE, network_state_handler_->GetTechnologyState(flimflam::kTypeWimax)); manager_test->SetTechnologyInitializing(flimflam::kTypeWimax, true); message_loop_.RunUntilIdle(); EXPECT_EQ(NetworkStateHandler::TECHNOLOGY_UNINITIALIZED, network_state_handler_->GetTechnologyState(flimflam::kTypeWimax)); manager_test->SetTechnologyInitializing(flimflam::kTypeWimax, false); network_state_handler_->SetTechnologyEnabled( flimflam::kTypeWimax, true, network_handler::ErrorCallback()); message_loop_.RunUntilIdle(); EXPECT_EQ(NetworkStateHandler::TECHNOLOGY_ENABLED, network_state_handler_->GetTechnologyState(flimflam::kTypeWimax)); manager_test->RemoveTechnology(flimflam::kTypeWimax); message_loop_.RunUntilIdle(); EXPECT_EQ(NetworkStateHandler::TECHNOLOGY_UNAVAILABLE, network_state_handler_->GetTechnologyState(flimflam::kTypeWimax)); } TEST_F(NetworkStateHandlerTest, ServicePropertyChanged) { // Set a service property. const std::string eth1 = kShillManagerClientStubDefaultService; EXPECT_EQ("", network_state_handler_->GetNetworkState(eth1)->security()); EXPECT_EQ(1, test_observer_->PropertyUpdatesForService(eth1)); base::StringValue security_value("TestSecurity"); DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( dbus::ObjectPath(eth1), flimflam::kSecurityProperty, security_value, base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); message_loop_.RunUntilIdle(); EXPECT_EQ("TestSecurity", network_state_handler_->GetNetworkState(eth1)->security()); EXPECT_EQ(2, test_observer_->PropertyUpdatesForService(eth1)); // Changing a service to the existing value should not trigger an update. DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( dbus::ObjectPath(eth1), flimflam::kSecurityProperty, security_value, base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); message_loop_.RunUntilIdle(); EXPECT_EQ(2, test_observer_->PropertyUpdatesForService(eth1)); } TEST_F(NetworkStateHandlerTest, FavoriteState) { // Set the profile entry of a service const std::string wifi1 = kShillManagerClientStubDefaultWireless; ShillProfileClient::TestInterface* profile_test = DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface(); EXPECT_TRUE(profile_test->AddService("/profile/default", wifi1)); message_loop_.RunUntilIdle(); network_state_handler_->UpdateManagerProperties(); message_loop_.RunUntilIdle(); EXPECT_EQ(1u, test_observer_->favorite_count()); } TEST_F(NetworkStateHandlerTest, NetworkConnectionStateChanged) { // Change a network state. ShillServiceClient::TestInterface* service_test = DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); const std::string eth1 = kShillManagerClientStubDefaultService; base::StringValue connection_state_idle_value(flimflam::kStateIdle); service_test->SetServiceProperty(eth1, flimflam::kStateProperty, connection_state_idle_value); message_loop_.RunUntilIdle(); EXPECT_EQ(flimflam::kStateIdle, test_observer_->NetworkConnectionStateForService(eth1)); EXPECT_EQ(2, test_observer_->ConnectionStateChangesForService(eth1)); // Confirm that changing the connection state to the same value does *not* // signal the observer. service_test->SetServiceProperty(eth1, flimflam::kStateProperty, connection_state_idle_value); message_loop_.RunUntilIdle(); EXPECT_EQ(2, test_observer_->ConnectionStateChangesForService(eth1)); } TEST_F(NetworkStateHandlerTest, DefaultServiceChanged) { ShillManagerClient::TestInterface* manager_test = DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface(); ASSERT_TRUE(manager_test); ShillServiceClient::TestInterface* service_test = DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); ASSERT_TRUE(service_test); // Change the default network by moving wifi1 to the front of the list // and changing the state of eth1 to Idle. const std::string wifi1 = kShillManagerClientStubDefaultWireless; manager_test->MoveServiceToIndex(wifi1, 0, true); const std::string eth1 = kShillManagerClientStubDefaultService; base::StringValue connection_state_idle_value(flimflam::kStateIdle); service_test->SetServiceProperty(eth1, flimflam::kStateProperty, connection_state_idle_value); message_loop_.RunUntilIdle(); EXPECT_EQ(wifi1, test_observer_->default_network()); EXPECT_EQ(flimflam::kStateOnline, test_observer_->default_network_connection_state()); // We should have seen 2 default network updates - for the default // service change, and for the state change. EXPECT_EQ(2u, test_observer_->default_network_change_count()); // Updating a property on the default network should trigger // a default network change. DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( dbus::ObjectPath(wifi1), flimflam::kSecurityProperty, base::StringValue("TestSecurity"), base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); message_loop_.RunUntilIdle(); EXPECT_EQ(3u, test_observer_->default_network_change_count()); // No default network updates for signal strength changes. DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( dbus::ObjectPath(wifi1), flimflam::kSignalStrengthProperty, base::FundamentalValue(32), base::Bind(&base::DoNothing), base::Bind(&ErrorCallbackFunction)); message_loop_.RunUntilIdle(); EXPECT_EQ(3u, test_observer_->default_network_change_count()); } TEST_F(NetworkStateHandlerTest, RequestUpdate) { // Request an update for kShillManagerClientStubDefaultWireless. EXPECT_EQ(1, test_observer_->PropertyUpdatesForService( kShillManagerClientStubDefaultWireless)); EXPECT_TRUE(network_state_handler_->RequestUpdateForNetwork( kShillManagerClientStubDefaultWireless)); message_loop_.RunUntilIdle(); EXPECT_EQ(2, test_observer_->PropertyUpdatesForService( kShillManagerClientStubDefaultWireless)); // Request an update for all networks. network_state_handler_->RequestUpdateForAllNetworks(); message_loop_.RunUntilIdle(); // kShillManagerClientStubDefaultWireless should now have 3 updates EXPECT_EQ(3, test_observer_->PropertyUpdatesForService( kShillManagerClientStubDefaultWireless)); // Other networks should have 2 updates (inital + request). EXPECT_EQ(2, test_observer_->PropertyUpdatesForService( kShillManagerClientStubDefaultService)); EXPECT_EQ(2, test_observer_->PropertyUpdatesForService( kShillManagerClientStubWireless2)); EXPECT_EQ(2, test_observer_->PropertyUpdatesForService( kShillManagerClientStubCellular)); } } // namespace chromeos