blob: 2e474e25578b60746d2db957136ac106e7f44d2b [file] [log] [blame]
//
// Copyright (C) 2020 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 <memory>
#include <base/optional.h>
#include <base/time/time.h>
#include <base/test/simple_test_clock.h>
#include <brillo/message_loops/fake_message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "update_engine/cros/fake_system_state.h"
#include "update_engine/update_manager/fake_state.h"
#include "update_engine/update_manager/update_time_restrictions_monitor.h"
using brillo::FakeMessageLoop;
using brillo::MessageLoop;
using brillo::MessageLoopRunMaxIterations;
using chromeos_update_engine::FakeSystemState;
namespace chromeos_update_manager {
namespace {
constexpr base::TimeDelta kDurationOffset = base::TimeDelta::FromMinutes(1);
constexpr base::TimeDelta kHourDuration = base::TimeDelta::FromHours(1);
constexpr base::TimeDelta kMinuteDuration = base::TimeDelta::FromMinutes(1);
// Initial time: Monday, May 4th 2020 8:13 AM before interval.
constexpr base::Time::Exploded kInitialTimeBeforeInterval{
2020, 5, 0, 4, 10, 13, 0, 0};
// Initial time: Monday, May 4th 2020 10:20 AM within interval.
constexpr base::Time::Exploded kInitialTimeWithinInterval{
2020, 5, 0, 4, 10, 20, 0, 0};
const int current_restricted_interval_index = 0;
const WeeklyTimeIntervalVector kTestOneDisallowedTimeIntervals{
// Monday 8:15 AM to Monday 9:30 PM.
WeeklyTimeInterval(WeeklyTime(1, kHourDuration * 8 + kMinuteDuration * 15),
WeeklyTime(1, kHourDuration * 9 + kMinuteDuration * 30)),
};
const WeeklyTimeIntervalVector kTestTwoDisallowedTimeIntervals{
// Monday 10:15 AM to Monday 3:30 PM.
WeeklyTimeInterval(
WeeklyTime(1, kHourDuration * 10 + kMinuteDuration * 15),
WeeklyTime(1, kHourDuration * 15 + kMinuteDuration * 30)),
// Wednesday 8:30 PM to Thursday 8:40 AM.
WeeklyTimeInterval(WeeklyTime(3, kHourDuration * 20 + kMinuteDuration * 30),
WeeklyTime(4, kHourDuration * 8 + kMinuteDuration * 40)),
};
} // namespace
class MockUpdateTimeRestrictionsMonitorDelegate
: public UpdateTimeRestrictionsMonitor::Delegate {
public:
virtual ~MockUpdateTimeRestrictionsMonitorDelegate() = default;
MOCK_METHOD0(OnRestrictedIntervalStarts, void());
};
class UmUpdateTimeRestrictionsMonitorTest : public ::testing::Test {
protected:
UmUpdateTimeRestrictionsMonitorTest() {
fake_loop_.SetAsCurrent();
FakeSystemState::CreateInstance();
}
void TearDown() override { EXPECT_FALSE(fake_loop_.PendingTasks()); }
bool SetNow(const base::Time::Exploded& exploded_now) {
base::Time now;
if (!base::Time::FromLocalExploded(exploded_now, &now))
return false;
test_clock_.SetNow(now);
FakeSystemState::Get()->fake_clock()->SetWallclockTime(now);
return true;
}
void AdvanceAfterTimestamp(const WeeklyTime& timestamp) {
const WeeklyTime now = WeeklyTime::FromTime(test_clock_.Now());
const base::TimeDelta duration =
now.GetDurationTo(timestamp) + kDurationOffset;
test_clock_.Advance(duration);
FakeSystemState::Get()->fake_clock()->SetWallclockTime(test_clock_.Now());
}
void VerifyExpectationsOnDelegate() {
testing::Mock::VerifyAndClearExpectations(&mock_delegate_);
}
void UpdateRestrictedIntervals(const WeeklyTimeIntervalVector& policy_value) {
auto* policy_variable =
fake_state_.device_policy_provider()->var_disallowed_time_intervals();
policy_variable->reset(new WeeklyTimeIntervalVector(policy_value));
policy_variable->NotifyValueChanged();
}
bool IsMonitoringInterval() {
return monitor_.has_value() && monitor_.value().IsMonitoringInterval();
}
void BuildMonitorAndVerify(const WeeklyTimeIntervalVector* policy_value,
bool expect_delegate_called,
bool expect_monitoring) {
if (expect_delegate_called)
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
else
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
fake_state_.device_policy_provider()
->var_disallowed_time_intervals()
->reset(policy_value != nullptr
? new WeeklyTimeIntervalVector(*policy_value)
: nullptr);
monitor_.emplace(fake_state_.device_policy_provider(), &mock_delegate_);
if (expect_delegate_called)
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
if (expect_monitoring)
EXPECT_TRUE(IsMonitoringInterval());
else
EXPECT_FALSE(IsMonitoringInterval());
}
base::SimpleTestClock test_clock_;
FakeMessageLoop fake_loop_{&test_clock_};
FakeState fake_state_;
MockUpdateTimeRestrictionsMonitorDelegate mock_delegate_;
base::Optional<UpdateTimeRestrictionsMonitor> monitor_;
};
TEST_F(UmUpdateTimeRestrictionsMonitorTest, PolicyIsNotSet) {
BuildMonitorAndVerify(
nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
}
TEST_F(UmUpdateTimeRestrictionsMonitorTest, PolicyHasEmptyIntervalList) {
WeeklyTimeIntervalVector empty_policy;
BuildMonitorAndVerify(&empty_policy,
/*expect_delegate_called=*/false,
/*expect_monitoring=*/false);
}
TEST_F(UmUpdateTimeRestrictionsMonitorTest,
CurrentTimeOutsideOfRestrictedInterval) {
ASSERT_TRUE(SetNow(kInitialTimeBeforeInterval));
BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
/*expect_delegate_called=*/false,
/*expect_monitoring=*/true);
// Monitor should only notify start when passing start of interval.
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
AdvanceAfterTimestamp(
kTestTwoDisallowedTimeIntervals[current_restricted_interval_index]
.start());
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
}
TEST_F(UmUpdateTimeRestrictionsMonitorTest,
CurrentTimeWithinRestrictedInterval) {
// Monitor should notify start when it is built with current
// time within interval.
ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
/*expect_delegate_called=*/true,
/*expect_monitoring=*/false);
}
TEST_F(UmUpdateTimeRestrictionsMonitorTest,
PolicyChangeFromNotSetToOutsideInterval) {
// Build monitor with empty initial list of intervals.
BuildMonitorAndVerify(
nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
// Monitor should not do any notification right after intervals update.
ASSERT_TRUE(SetNow(kInitialTimeBeforeInterval));
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
EXPECT_TRUE(IsMonitoringInterval());
// Advance time within new interval and check that notification happen.
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
AdvanceAfterTimestamp(
kTestTwoDisallowedTimeIntervals[current_restricted_interval_index]
.start());
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
}
TEST_F(UmUpdateTimeRestrictionsMonitorTest,
PolicyChangeFromNotSetToWithinInterval) {
// Build monitor with empty initial list of intervals.
BuildMonitorAndVerify(
nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
// Advance time inside upcoming new interval and update the intervals.
// Monitor should immediately notify about started interval.
ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
}
TEST_F(UmUpdateTimeRestrictionsMonitorTest,
PolicyChangeFromNotSetToEmptyInterval) {
BuildMonitorAndVerify(
nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
UpdateRestrictedIntervals(WeeklyTimeIntervalVector());
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
EXPECT_FALSE(IsMonitoringInterval());
}
TEST_F(UmUpdateTimeRestrictionsMonitorTest,
PolicyChangeFromOneOutsideIntervalToAnother) {
// Build monitor with current time outside the intervals.
BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
/*expect_delegate_called=*/false,
/*expect_monitoring=*/true);
// Update the intervals to outide of current time and no notification should
// happen yet.
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
UpdateRestrictedIntervals(kTestOneDisallowedTimeIntervals);
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
// Advance time within new interval. Monitor should notify about started
// interval.
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
AdvanceAfterTimestamp(
kTestOneDisallowedTimeIntervals[current_restricted_interval_index]
.start());
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
}
TEST_F(UmUpdateTimeRestrictionsMonitorTest,
PolicyChangeFromOutsideIntervalToWithin) {
ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
// Build monitor with current time outside the intervals.
BuildMonitorAndVerify(&kTestOneDisallowedTimeIntervals,
/*expect_delegate_called=*/false,
/*expect_monitoring=*/true);
// Update interval such that current time is within it. Monitor should notify
// about started interval.
EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
MessageLoopRunMaxIterations(MessageLoop::current(), 10);
VerifyExpectationsOnDelegate();
}
} // namespace chromeos_update_manager