You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
533 lines
22 KiB
533 lines
22 KiB
// |
|
// Copyright (C) 2014 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 "update_engine/update_manager/real_shill_provider.h" |
|
|
|
#include <memory> |
|
#include <utility> |
|
|
|
#include <base/time/time.h> |
|
#include <brillo/make_unique_ptr.h> |
|
#include <brillo/message_loops/fake_message_loop.h> |
|
#include <gmock/gmock.h> |
|
#include <gtest/gtest.h> |
|
#include <shill/dbus-constants.h> |
|
#include <shill/dbus-proxies.h> |
|
#include <shill/dbus-proxy-mocks.h> |
|
|
|
#include "update_engine/common/fake_clock.h" |
|
#include "update_engine/common/test_utils.h" |
|
#include "update_engine/dbus_test_utils.h" |
|
#include "update_engine/fake_shill_proxy.h" |
|
#include "update_engine/update_manager/umtest_utils.h" |
|
|
|
using base::Time; |
|
using base::TimeDelta; |
|
using chromeos_update_engine::ConnectionTethering; |
|
using chromeos_update_engine::ConnectionType; |
|
using chromeos_update_engine::FakeClock; |
|
using org::chromium::flimflam::ManagerProxyMock; |
|
using org::chromium::flimflam::ServiceProxyMock; |
|
using std::unique_ptr; |
|
using testing::Mock; |
|
using testing::Return; |
|
using testing::SetArgPointee; |
|
using testing::_; |
|
|
|
namespace { |
|
|
|
// Fake service paths. |
|
const char* const kFakeEthernetServicePath = "/fake/ethernet/service"; |
|
const char* const kFakeWifiServicePath = "/fake/wifi/service"; |
|
const char* const kFakeWimaxServicePath = "/fake/wimax/service"; |
|
const char* const kFakeBluetoothServicePath = "/fake/bluetooth/service"; |
|
const char* const kFakeCellularServicePath = "/fake/cellular/service"; |
|
const char* const kFakeVpnServicePath = "/fake/vpn/service"; |
|
const char* const kFakeUnknownServicePath = "/fake/unknown/service"; |
|
|
|
} // namespace |
|
|
|
namespace chromeos_update_manager { |
|
|
|
class UmRealShillProviderTest : public ::testing::Test { |
|
protected: |
|
// Initialize the RealShillProvider under test. |
|
void SetUp() override { |
|
fake_clock_.SetWallclockTime(InitTime()); |
|
loop_.SetAsCurrent(); |
|
fake_shill_proxy_ = new chromeos_update_engine::FakeShillProxy(); |
|
provider_.reset(new RealShillProvider(fake_shill_proxy_, &fake_clock_)); |
|
|
|
ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy(); |
|
|
|
// The PropertyChanged signal should be subscribed to. |
|
MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER( |
|
manager_property_changed_, *manager_proxy_mock, PropertyChanged); |
|
} |
|
|
|
void TearDown() override { |
|
provider_.reset(); |
|
// Check for leaked callbacks on the main loop. |
|
EXPECT_FALSE(loop_.PendingTasks()); |
|
} |
|
|
|
// These methods generate fixed timestamps for use in faking the current time. |
|
Time InitTime() { |
|
Time::Exploded now_exp; |
|
now_exp.year = 2014; |
|
now_exp.month = 3; |
|
now_exp.day_of_week = 2; |
|
now_exp.day_of_month = 18; |
|
now_exp.hour = 8; |
|
now_exp.minute = 5; |
|
now_exp.second = 33; |
|
now_exp.millisecond = 675; |
|
return Time::FromLocalExploded(now_exp); |
|
} |
|
|
|
Time ConnChangedTime() { |
|
return InitTime() + TimeDelta::FromSeconds(10); |
|
} |
|
|
|
// Sets the default_service object path in the response from the |
|
// ManagerProxyMock instance. |
|
void SetManagerReply(const char* default_service, bool reply_succeeds); |
|
|
|
// Sets the |service_type|, |physical_technology| and |service_tethering| |
|
// properties in the mocked service |service_path|. If any of the three |
|
// const char* is a nullptr, the corresponding property will not be included |
|
// in the response. |
|
// Returns the mock object pointer, owned by the |fake_shill_proxy_|. |
|
ServiceProxyMock* SetServiceReply(const std::string& service_path, |
|
const char* service_type, |
|
const char* physical_technology, |
|
const char* service_tethering); |
|
|
|
void InitWithDefaultService(const char* default_service) { |
|
SetManagerReply(default_service, true); |
|
// Check that provider initializes correctly. |
|
EXPECT_TRUE(provider_->Init()); |
|
// RunOnce to notify the signal handler was connected properly. |
|
EXPECT_TRUE(loop_.RunOnce(false)); |
|
} |
|
|
|
// Sends a signal informing the provider about a default connection |
|
// |service_path|. Sets the fake connection change time in |
|
// |conn_change_time_p| if provided. |
|
void SendDefaultServiceSignal(const std::string& service_path, |
|
Time* conn_change_time_p) { |
|
const Time conn_change_time = ConnChangedTime(); |
|
fake_clock_.SetWallclockTime(conn_change_time); |
|
ASSERT_TRUE(manager_property_changed_.IsHandlerRegistered()); |
|
manager_property_changed_.signal_callback().Run( |
|
shill::kDefaultServiceProperty, dbus::ObjectPath(service_path)); |
|
fake_clock_.SetWallclockTime(conn_change_time + TimeDelta::FromSeconds(5)); |
|
if (conn_change_time_p) |
|
*conn_change_time_p = conn_change_time; |
|
} |
|
|
|
// Sets up expectations for detection of a connection |service_path| with type |
|
// |shill_type_str| and tethering mode |shill_tethering_str|. Ensures that the |
|
// new connection status and change time are properly detected by the |
|
// provider. Writes the fake connection change time to |conn_change_time_p|, |
|
// if provided. |
|
void SetupConnectionAndAttrs(const std::string& service_path, |
|
const char* shill_type, |
|
const char* shill_tethering, |
|
Time* conn_change_time_p) { |
|
SetServiceReply(service_path, shill_type, nullptr, shill_tethering); |
|
// Note: We don't setup this |service_path| as the default service path but |
|
// we instead send a signal notifying the change since the code won't call |
|
// GetProperties on the Manager object at this point. |
|
|
|
// Send a signal about a new default service. |
|
Time conn_change_time; |
|
SendDefaultServiceSignal(service_path, &conn_change_time); |
|
|
|
// Query the connection status, ensure last change time reported correctly. |
|
UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected()); |
|
UmTestUtils::ExpectVariableHasValue(conn_change_time, |
|
provider_->var_conn_last_changed()); |
|
|
|
// Write the connection change time to the output argument. |
|
if (conn_change_time_p) |
|
*conn_change_time_p = conn_change_time; |
|
} |
|
|
|
// Sets up a connection and tests that its type is being properly detected by |
|
// the provider. |
|
void SetupConnectionAndTestType(const char* service_path, |
|
const char* shill_type, |
|
ConnectionType expected_conn_type) { |
|
// Set up and test the connection, record the change time. |
|
Time conn_change_time; |
|
SetupConnectionAndAttrs(service_path, |
|
shill_type, |
|
shill::kTetheringNotDetectedState, |
|
&conn_change_time); |
|
|
|
// Query the connection type, ensure last change time did not change. |
|
UmTestUtils::ExpectVariableHasValue(expected_conn_type, |
|
provider_->var_conn_type()); |
|
UmTestUtils::ExpectVariableHasValue(conn_change_time, |
|
provider_->var_conn_last_changed()); |
|
} |
|
|
|
// Sets up a connection and tests that its tethering mode is being properly |
|
// detected by the provider. |
|
void SetupConnectionAndTestTethering( |
|
const char* service_path, |
|
const char* shill_tethering, |
|
ConnectionTethering expected_conn_tethering) { |
|
// Set up and test the connection, record the change time. |
|
Time conn_change_time; |
|
SetupConnectionAndAttrs( |
|
service_path, shill::kTypeEthernet, shill_tethering, &conn_change_time); |
|
|
|
// Query the connection tethering, ensure last change time did not change. |
|
UmTestUtils::ExpectVariableHasValue(expected_conn_tethering, |
|
provider_->var_conn_tethering()); |
|
UmTestUtils::ExpectVariableHasValue(conn_change_time, |
|
provider_->var_conn_last_changed()); |
|
} |
|
|
|
brillo::FakeMessageLoop loop_{nullptr}; |
|
FakeClock fake_clock_; |
|
chromeos_update_engine::FakeShillProxy* fake_shill_proxy_; |
|
|
|
// The registered signal handler for the signal Manager.PropertyChanged. |
|
chromeos_update_engine::dbus_test_utils::MockSignalHandler< |
|
void(const std::string&, const brillo::Any&)> manager_property_changed_; |
|
|
|
unique_ptr<RealShillProvider> provider_; |
|
}; |
|
|
|
void UmRealShillProviderTest::SetManagerReply(const char* default_service, |
|
bool reply_succeeds) { |
|
ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy(); |
|
if (!reply_succeeds) { |
|
EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _)) |
|
.WillOnce(Return(false)); |
|
return; |
|
} |
|
|
|
// Create a dictionary of properties and optionally include the default |
|
// service. |
|
brillo::VariantDictionary reply_dict; |
|
reply_dict["SomeOtherProperty"] = 0xC0FFEE; |
|
|
|
if (default_service) { |
|
reply_dict[shill::kDefaultServiceProperty] = |
|
dbus::ObjectPath(default_service); |
|
} |
|
EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _)) |
|
.WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true))); |
|
} |
|
|
|
ServiceProxyMock* UmRealShillProviderTest::SetServiceReply( |
|
const std::string& service_path, |
|
const char* service_type, |
|
const char* physical_technology, |
|
const char* service_tethering) { |
|
brillo::VariantDictionary reply_dict; |
|
reply_dict["SomeOtherProperty"] = 0xC0FFEE; |
|
|
|
if (service_type) |
|
reply_dict[shill::kTypeProperty] = std::string(service_type); |
|
|
|
if (physical_technology) { |
|
reply_dict[shill::kPhysicalTechnologyProperty] = |
|
std::string(physical_technology); |
|
} |
|
|
|
if (service_tethering) |
|
reply_dict[shill::kTetheringProperty] = std::string(service_tethering); |
|
|
|
ServiceProxyMock* service_proxy_mock = new ServiceProxyMock(); |
|
|
|
// Plumb return value into mock object. |
|
EXPECT_CALL(*service_proxy_mock, GetProperties(_, _, _)) |
|
.WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true))); |
|
|
|
fake_shill_proxy_->SetServiceForPath( |
|
dbus::ObjectPath(service_path), |
|
brillo::make_unique_ptr(service_proxy_mock)); |
|
return service_proxy_mock; |
|
} |
|
|
|
|
|
// Query the connection status, type and time last changed, as they were set |
|
// during initialization (no signals). |
|
TEST_F(UmRealShillProviderTest, ReadBaseValues) { |
|
InitWithDefaultService("/"); |
|
// Query the provider variables. |
|
UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected()); |
|
UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type()); |
|
UmTestUtils::ExpectVariableHasValue(InitTime(), |
|
provider_->var_conn_last_changed()); |
|
} |
|
|
|
// Ensure that invalid DBus paths are ignored. |
|
TEST_F(UmRealShillProviderTest, InvalidServicePath) { |
|
InitWithDefaultService("invalid"); |
|
UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected()); |
|
UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type()); |
|
UmTestUtils::ExpectVariableHasValue(InitTime(), |
|
provider_->var_conn_last_changed()); |
|
} |
|
|
|
// Ensure that a service path property including a different type is ignored. |
|
TEST_F(UmRealShillProviderTest, InvalidServicePathType) { |
|
ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_->GetManagerProxy(); |
|
brillo::VariantDictionary reply_dict; |
|
reply_dict[shill::kDefaultServiceProperty] = "/not/an/object/path"; |
|
EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _)) |
|
.WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true))); |
|
|
|
EXPECT_TRUE(provider_->Init()); |
|
EXPECT_TRUE(loop_.RunOnce(false)); |
|
|
|
UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected()); |
|
} |
|
|
|
// Test that Ethernet connection is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTypeEthernet) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeEthernetServicePath, |
|
shill::kTypeEthernet, |
|
ConnectionType::kEthernet); |
|
} |
|
|
|
// Test that Wifi connection is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTypeWifi) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeWifiServicePath, |
|
shill::kTypeWifi, |
|
ConnectionType::kWifi); |
|
} |
|
|
|
// Test that Wimax connection is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTypeWimax) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeWimaxServicePath, |
|
shill::kTypeWimax, |
|
ConnectionType::kWimax); |
|
} |
|
|
|
// Test that Bluetooth connection is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTypeBluetooth) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeBluetoothServicePath, |
|
shill::kTypeBluetooth, |
|
ConnectionType::kBluetooth); |
|
} |
|
|
|
// Test that Cellular connection is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTypeCellular) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeCellularServicePath, |
|
shill::kTypeCellular, |
|
ConnectionType::kCellular); |
|
} |
|
|
|
// Test that an unknown connection is identified as such. |
|
TEST_F(UmRealShillProviderTest, ReadConnTypeUnknown) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeUnknownServicePath, |
|
"FooConnectionType", |
|
ConnectionType::kUnknown); |
|
} |
|
|
|
// Tests that VPN connection is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTypeVpn) { |
|
InitWithDefaultService("/"); |
|
// Mock logic for returning a default service path and its type. |
|
SetServiceReply(kFakeVpnServicePath, |
|
shill::kTypeVPN, |
|
shill::kTypeWifi, |
|
shill::kTetheringNotDetectedState); |
|
|
|
// Send a signal about a new default service. |
|
Time conn_change_time; |
|
SendDefaultServiceSignal(kFakeVpnServicePath, &conn_change_time); |
|
|
|
// Query the connection type, ensure last change time reported correctly. |
|
UmTestUtils::ExpectVariableHasValue(ConnectionType::kWifi, |
|
provider_->var_conn_type()); |
|
UmTestUtils::ExpectVariableHasValue(conn_change_time, |
|
provider_->var_conn_last_changed()); |
|
} |
|
|
|
// Ensure that the connection type is properly cached in the provider through |
|
// subsequent variable readings. |
|
TEST_F(UmRealShillProviderTest, ConnTypeCacheUsed) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeEthernetServicePath, |
|
shill::kTypeEthernet, |
|
ConnectionType::kEthernet); |
|
|
|
UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet, |
|
provider_->var_conn_type()); |
|
} |
|
|
|
// Ensure that the cached connection type remains valid even when a default |
|
// connection signal occurs but the connection is not changed. |
|
TEST_F(UmRealShillProviderTest, ConnTypeCacheRemainsValid) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeEthernetServicePath, |
|
shill::kTypeEthernet, |
|
ConnectionType::kEthernet); |
|
|
|
SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr); |
|
|
|
UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet, |
|
provider_->var_conn_type()); |
|
} |
|
|
|
// Ensure that the cached connection type is invalidated and re-read when the |
|
// default connection changes. |
|
TEST_F(UmRealShillProviderTest, ConnTypeCacheInvalidated) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestType(kFakeEthernetServicePath, |
|
shill::kTypeEthernet, |
|
ConnectionType::kEthernet); |
|
|
|
SetupConnectionAndTestType(kFakeWifiServicePath, |
|
shill::kTypeWifi, |
|
ConnectionType::kWifi); |
|
} |
|
|
|
// Test that a non-tethering mode is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTetheringNotDetected) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestTethering(kFakeWifiServicePath, |
|
shill::kTetheringNotDetectedState, |
|
ConnectionTethering::kNotDetected); |
|
} |
|
|
|
// Test that a suspected tethering mode is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTetheringSuspected) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestTethering(kFakeWifiServicePath, |
|
shill::kTetheringSuspectedState, |
|
ConnectionTethering::kSuspected); |
|
} |
|
|
|
// Test that a confirmed tethering mode is identified correctly. |
|
TEST_F(UmRealShillProviderTest, ReadConnTetheringConfirmed) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestTethering(kFakeWifiServicePath, |
|
shill::kTetheringConfirmedState, |
|
ConnectionTethering::kConfirmed); |
|
} |
|
|
|
// Test that an unknown tethering mode is identified as such. |
|
TEST_F(UmRealShillProviderTest, ReadConnTetheringUnknown) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestTethering(kFakeWifiServicePath, |
|
"FooConnTethering", |
|
ConnectionTethering::kUnknown); |
|
} |
|
|
|
// Ensure that the connection tethering mode is properly cached in the provider. |
|
TEST_F(UmRealShillProviderTest, ConnTetheringCacheUsed) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestTethering(kFakeEthernetServicePath, |
|
shill::kTetheringNotDetectedState, |
|
ConnectionTethering::kNotDetected); |
|
|
|
UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected, |
|
provider_->var_conn_tethering()); |
|
} |
|
|
|
// Ensure that the cached connection tethering mode remains valid even when a |
|
// default connection signal occurs but the connection is not changed. |
|
TEST_F(UmRealShillProviderTest, ConnTetheringCacheRemainsValid) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestTethering(kFakeEthernetServicePath, |
|
shill::kTetheringNotDetectedState, |
|
ConnectionTethering::kNotDetected); |
|
|
|
SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr); |
|
|
|
UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected, |
|
provider_->var_conn_tethering()); |
|
} |
|
|
|
// Ensure that the cached connection tethering mode is invalidated and re-read |
|
// when the default connection changes. |
|
TEST_F(UmRealShillProviderTest, ConnTetheringCacheInvalidated) { |
|
InitWithDefaultService("/"); |
|
SetupConnectionAndTestTethering(kFakeEthernetServicePath, |
|
shill::kTetheringNotDetectedState, |
|
ConnectionTethering::kNotDetected); |
|
|
|
SetupConnectionAndTestTethering(kFakeWifiServicePath, |
|
shill::kTetheringConfirmedState, |
|
ConnectionTethering::kConfirmed); |
|
} |
|
|
|
// Fake two DBus signals prompting a default connection change, but otherwise |
|
// give the same service path. Check connection status and the time it was last |
|
// changed, making sure that it is the time when the first signal was sent (and |
|
// not the second). |
|
TEST_F(UmRealShillProviderTest, ReadLastChangedTimeTwoSignals) { |
|
InitWithDefaultService("/"); |
|
// Send a default service signal twice, advancing the clock in between. |
|
Time conn_change_time; |
|
SetupConnectionAndAttrs(kFakeEthernetServicePath, |
|
shill::kTypeEthernet, |
|
shill::kTetheringNotDetectedState, |
|
&conn_change_time); |
|
// This will set the service path to the same value, so it should not call |
|
// GetProperties() again. |
|
SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr); |
|
|
|
// Query the connection status, ensure last change time reported as the first |
|
// time the signal was sent. |
|
UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected()); |
|
UmTestUtils::ExpectVariableHasValue(conn_change_time, |
|
provider_->var_conn_last_changed()); |
|
} |
|
|
|
// Make sure that the provider initializes correctly even if shill is not |
|
// responding, that variables can be obtained, and that they all return a null |
|
// value (indicating that the underlying values were not set). |
|
TEST_F(UmRealShillProviderTest, NoInitConnStatusReadBaseValues) { |
|
// Initialize the provider, no initial connection status response. |
|
SetManagerReply(nullptr, false); |
|
EXPECT_TRUE(provider_->Init()); |
|
EXPECT_TRUE(loop_.RunOnce(false)); |
|
UmTestUtils::ExpectVariableNotSet(provider_->var_is_connected()); |
|
UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type()); |
|
UmTestUtils::ExpectVariableNotSet(provider_->var_conn_last_changed()); |
|
} |
|
|
|
// Test that, once a signal is received, the connection status and other info |
|
// can be read correctly. |
|
TEST_F(UmRealShillProviderTest, NoInitConnStatusReadConnTypeEthernet) { |
|
// Initialize the provider with no initial connection status response. |
|
SetManagerReply(nullptr, false); |
|
EXPECT_TRUE(provider_->Init()); |
|
EXPECT_TRUE(loop_.RunOnce(false)); |
|
|
|
SetupConnectionAndAttrs(kFakeEthernetServicePath, |
|
shill::kTypeEthernet, |
|
shill::kTetheringNotDetectedState, |
|
nullptr); |
|
UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected()); |
|
} |
|
|
|
} // namespace chromeos_update_manager
|
|
|