//
// 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_device_policy_provider.h"
#include <memory>
#include <vector>
#include <base/memory/ptr_util.h>
#include <brillo/message_loops/fake_message_loop.h>
#include <brillo/message_loops/message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <policy/mock_device_policy.h>
#include <policy/mock_libpolicy.h>
#if USE_DBUS
#include <session_manager/dbus-proxies.h>
#include <session_manager/dbus-proxy-mocks.h>
#endif // USE_DBUS
#include "update_engine/common/test_utils.h"
#if USE_DBUS
#include "update_engine/dbus_test_utils.h"
#endif // USE_DBUS
#include "update_engine/update_manager/umtest_utils.h"
using base::TimeDelta;
using brillo::MessageLoop;
using chromeos_update_engine::ConnectionType;
using policy::DevicePolicy;
#if USE_DBUS
using chromeos_update_engine::dbus_test_utils::MockSignalHandler;
#endif // USE_DBUS
using std::set;
using std::string;
using std::unique_ptr;
using std::vector;
using testing::_;
using testing::DoAll;
using testing::Mock;
using testing::Return;
using testing::ReturnRef;
using testing::SetArgPointee;
namespace chromeos_update_manager {
class UmRealDevicePolicyProviderTest : public ::testing::Test {
protected:
void SetUp() override {
loop_.SetAsCurrent();
#if USE_DBUS
auto session_manager_proxy_mock =
new org::chromium::SessionManagerInterfaceProxyMock();
provider_.reset(new RealDevicePolicyProvider(
base::WrapUnique(session_manager_proxy_mock), &mock_policy_provider_));
#else
provider_.reset(new RealDevicePolicyProvider(&mock_policy_provider_));
#endif // USE_DBUS
// By default, we have a device policy loaded. Tests can call
// SetUpNonExistentDevicePolicy() to override this.
SetUpExistentDevicePolicy();
#if USE_DBUS
// Setup the session manager_proxy such that it will accept the signal
// handler and store it in the |property_change_complete_| once registered.
MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(property_change_complete_,
*session_manager_proxy_mock,
PropertyChangeComplete);
#endif // USE_DBUS
}
void TearDown() override {
provider_.reset();
// Check for leaked callbacks on the main loop.
EXPECT_FALSE(loop_.PendingTasks());
}
void SetUpNonExistentDevicePolicy() {
ON_CALL(mock_policy_provider_, Reload()).WillByDefault(Return(false));
ON_CALL(mock_policy_provider_, device_policy_is_loaded())
.WillByDefault(Return(false));
EXPECT_CALL(mock_policy_provider_, GetDevicePolicy()).Times(0);
}
void SetUpExistentDevicePolicy() {
// Setup the default behavior of the mocked PolicyProvider.
ON_CALL(mock_policy_provider_, Reload()).WillByDefault(Return(true));
ON_CALL(mock_policy_provider_, device_policy_is_loaded())
.WillByDefault(Return(true));
ON_CALL(mock_policy_provider_, GetDevicePolicy())
.WillByDefault(ReturnRef(mock_device_policy_));
}
brillo::FakeMessageLoop loop_{nullptr};
testing::NiceMock<policy::MockDevicePolicy> mock_device_policy_;
testing::NiceMock<policy::MockPolicyProvider> mock_policy_provider_;
unique_ptr<RealDevicePolicyProvider> provider_;
#if USE_DBUS
// The registered signal handler for the signal.
MockSignalHandler<void(const string&)> property_change_complete_;
#endif // USE_DBUS
};
TEST_F(UmRealDevicePolicyProviderTest, RefreshScheduledTest) {
// Check that the RefreshPolicy gets scheduled by checking the TaskId.
EXPECT_TRUE(provider_->Init());
EXPECT_NE(MessageLoop::kTaskIdNull, provider_->scheduled_refresh_);
loop_.RunOnce(false);
}
TEST_F(UmRealDevicePolicyProviderTest, FirstReload) {
// Checks that the policy is reloaded and the DevicePolicy is consulted twice:
// once on Init() and once again when the signal is connected.
EXPECT_CALL(mock_policy_provider_, Reload());
EXPECT_TRUE(provider_->Init());
Mock::VerifyAndClearExpectations(&mock_policy_provider_);
// We won't be notified that signal is connected without DBus.
#if USE_DBUS
EXPECT_CALL(mock_policy_provider_, Reload());
#endif // USE_DBUS
loop_.RunOnce(false);
}
TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded) {
// Checks that the policy is reloaded by RefreshDevicePolicy().
SetUpNonExistentDevicePolicy();
// We won't be notified that signal is connected without DBus.
#if USE_DBUS
EXPECT_CALL(mock_policy_provider_, Reload()).Times(3);
#else
EXPECT_CALL(mock_policy_provider_, Reload()).Times(2);
#endif // USE_DBUS
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
// Force the policy refresh.
provider_->RefreshDevicePolicy();
}
#if USE_DBUS
TEST_F(UmRealDevicePolicyProviderTest, SessionManagerSignalForcesReload) {
// Checks that a signal from the SessionManager forces a reload.
SetUpNonExistentDevicePolicy();
EXPECT_CALL(mock_policy_provider_, Reload()).Times(2);
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
Mock::VerifyAndClearExpectations(&mock_policy_provider_);
EXPECT_CALL(mock_policy_provider_, Reload());
ASSERT_TRUE(property_change_complete_.IsHandlerRegistered());
property_change_complete_.signal_callback().Run("success");
}
#endif // USE_DBUS
TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyEmptyVariables) {
SetUpNonExistentDevicePolicy();
EXPECT_CALL(mock_policy_provider_, GetDevicePolicy()).Times(0);
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(false,
provider_->var_device_policy_is_loaded());
UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel());
UmTestUtils::ExpectVariableNotSet(provider_->var_release_channel_delegated());
UmTestUtils::ExpectVariableNotSet(provider_->var_update_disabled());
UmTestUtils::ExpectVariableNotSet(provider_->var_target_version_prefix());
UmTestUtils::ExpectVariableNotSet(
provider_->var_rollback_to_target_version());
UmTestUtils::ExpectVariableNotSet(
provider_->var_rollback_allowed_milestones());
UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
UmTestUtils::ExpectVariableNotSet(
provider_->var_allowed_connection_types_for_update());
UmTestUtils::ExpectVariableNotSet(provider_->var_owner());
UmTestUtils::ExpectVariableNotSet(provider_->var_http_downloads_enabled());
UmTestUtils::ExpectVariableNotSet(provider_->var_au_p2p_enabled());
UmTestUtils::ExpectVariableNotSet(
provider_->var_allow_kiosk_app_control_chrome_version());
UmTestUtils::ExpectVariableNotSet(
provider_->var_auto_launched_kiosk_app_id());
UmTestUtils::ExpectVariableNotSet(provider_->var_disallowed_time_intervals());
}
TEST_F(UmRealDevicePolicyProviderTest, ValuesUpdated) {
SetUpNonExistentDevicePolicy();
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
Mock::VerifyAndClearExpectations(&mock_policy_provider_);
// Reload the policy with a good one and set some values as present. The
// remaining values are false.
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetReleaseChannel(_))
.WillOnce(DoAll(SetArgPointee<0>(string("mychannel")), Return(true)));
EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
.WillOnce(Return(false));
EXPECT_CALL(mock_device_policy_, GetAllowKioskAppControlChromeVersion(_))
.WillOnce(DoAll(SetArgPointee<0>(true), Return(true)));
EXPECT_CALL(mock_device_policy_, GetAutoLaunchedKioskAppId(_))
.WillOnce(DoAll(SetArgPointee<0>(string("myapp")), Return(true)));
provider_->RefreshDevicePolicy();
UmTestUtils::ExpectVariableHasValue(true,
provider_->var_device_policy_is_loaded());
// Test that at least one variable is set, to ensure the refresh occurred.
UmTestUtils::ExpectVariableHasValue(string("mychannel"),
provider_->var_release_channel());
UmTestUtils::ExpectVariableNotSet(
provider_->var_allowed_connection_types_for_update());
UmTestUtils::ExpectVariableHasValue(
true, provider_->var_allow_kiosk_app_control_chrome_version());
UmTestUtils::ExpectVariableHasValue(
string("myapp"), provider_->var_auto_launched_kiosk_app_id());
}
TEST_F(UmRealDevicePolicyProviderTest, RollbackToTargetVersionConverted) {
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetRollbackToTargetVersion(_))
#if USE_DBUS
.Times(2)
#else
.Times(1)
#endif // USE_DBUS
.WillRepeatedly(DoAll(SetArgPointee<0>(2), Return(true)));
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(
RollbackToTargetVersion::kRollbackAndPowerwash,
provider_->var_rollback_to_target_version());
}
TEST_F(UmRealDevicePolicyProviderTest, RollbackAllowedMilestonesOobe) {
SetUpNonExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetRollbackAllowedMilestones(_)).Times(0);
ON_CALL(mock_policy_provider_, IsConsumerDevice())
.WillByDefault(Return(false));
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableNotSet(
provider_->var_rollback_allowed_milestones());
}
TEST_F(UmRealDevicePolicyProviderTest, RollbackAllowedMilestonesConsumer) {
SetUpNonExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetRollbackAllowedMilestones(_)).Times(0);
ON_CALL(mock_policy_provider_, IsConsumerDevice())
.WillByDefault(Return(true));
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(
0, provider_->var_rollback_allowed_milestones());
}
TEST_F(UmRealDevicePolicyProviderTest,
RollbackAllowedMilestonesEnterprisePolicySet) {
SetUpExistentDevicePolicy();
ON_CALL(mock_device_policy_, GetRollbackAllowedMilestones(_))
.WillByDefault(DoAll(SetArgPointee<0>(2), Return(true)));
ON_CALL(mock_policy_provider_, IsConsumerDevice())
.WillByDefault(Return(false));
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(
2, provider_->var_rollback_allowed_milestones());
}
TEST_F(UmRealDevicePolicyProviderTest, ScatterFactorConverted) {
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
#if USE_DBUS
.Times(2)
#else
.Times(1)
#endif // USE_DBUS
.WillRepeatedly(DoAll(SetArgPointee<0>(1234), Return(true)));
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(TimeDelta::FromSeconds(1234),
provider_->var_scatter_factor());
}
TEST_F(UmRealDevicePolicyProviderTest, NegativeScatterFactorIgnored) {
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
#if USE_DBUS
.Times(2)
#else
.Times(1)
#endif // USE_DBUS
.WillRepeatedly(DoAll(SetArgPointee<0>(-1), Return(true)));
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
}
TEST_F(UmRealDevicePolicyProviderTest, AllowedTypesConverted) {
SetUpExistentDevicePolicy();
EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
#if USE_DBUS
.Times(2)
#else
.Times(1)
#endif // USE_DBUS
.WillRepeatedly(DoAll(
SetArgPointee<0>(set<string>{"bluetooth", "wifi", "not-a-type"}),
Return(true)));
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(
set<ConnectionType>{ConnectionType::kWifi, ConnectionType::kBluetooth},
provider_->var_allowed_connection_types_for_update());
}
TEST_F(UmRealDevicePolicyProviderTest, DisallowedIntervalsConverted) {
SetUpExistentDevicePolicy();
vector<DevicePolicy::WeeklyTimeInterval> intervals = {
{5, TimeDelta::FromHours(5), 6, TimeDelta::FromHours(8)},
{1, TimeDelta::FromHours(1), 3, TimeDelta::FromHours(10)}};
EXPECT_CALL(mock_device_policy_, GetDisallowedTimeIntervals(_))
.WillRepeatedly(DoAll(SetArgPointee<0>(intervals), Return(true)));
EXPECT_TRUE(provider_->Init());
loop_.RunOnce(false);
UmTestUtils::ExpectVariableHasValue(
WeeklyTimeIntervalVector{
WeeklyTimeInterval(WeeklyTime(5, TimeDelta::FromHours(5)),
WeeklyTime(6, TimeDelta::FromHours(8))),
WeeklyTimeInterval(WeeklyTime(1, TimeDelta::FromHours(1)),
WeeklyTime(3, TimeDelta::FromHours(10)))},
provider_->var_disallowed_time_intervals());
}
} // namespace chromeos_update_manager