// // 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 #include #include #include #include #include #include #include #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 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