1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <memory>
18 
19 #include <base/optional.h>
20 #include <base/time/time.h>
21 #include <base/test/simple_test_clock.h>
22 #include <brillo/message_loops/fake_message_loop.h>
23 #include <brillo/message_loops/message_loop_utils.h>
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 
27 #include "update_engine/cros/fake_system_state.h"
28 #include "update_engine/update_manager/fake_state.h"
29 #include "update_engine/update_manager/update_time_restrictions_monitor.h"
30 
31 using brillo::FakeMessageLoop;
32 using brillo::MessageLoop;
33 using brillo::MessageLoopRunMaxIterations;
34 using chromeos_update_engine::FakeSystemState;
35 
36 namespace chromeos_update_manager {
37 
38 namespace {
39 
40 constexpr base::TimeDelta kDurationOffset = base::TimeDelta::FromMinutes(1);
41 constexpr base::TimeDelta kHourDuration = base::TimeDelta::FromHours(1);
42 constexpr base::TimeDelta kMinuteDuration = base::TimeDelta::FromMinutes(1);
43 // Initial time: Monday, May 4th 2020 8:13 AM before interval.
44 constexpr base::Time::Exploded kInitialTimeBeforeInterval{
45     2020, 5, 0, 4, 10, 13, 0, 0};
46 // Initial time: Monday, May 4th 2020 10:20 AM within interval.
47 constexpr base::Time::Exploded kInitialTimeWithinInterval{
48     2020, 5, 0, 4, 10, 20, 0, 0};
49 const int current_restricted_interval_index = 0;
50 
51 const WeeklyTimeIntervalVector kTestOneDisallowedTimeIntervals{
52     // Monday 8:15 AM to Monday 9:30 PM.
53     WeeklyTimeInterval(WeeklyTime(1, kHourDuration * 8 + kMinuteDuration * 15),
54                        WeeklyTime(1, kHourDuration * 9 + kMinuteDuration * 30)),
55 };
56 
57 const WeeklyTimeIntervalVector kTestTwoDisallowedTimeIntervals{
58     // Monday 10:15 AM to Monday 3:30 PM.
59     WeeklyTimeInterval(
60         WeeklyTime(1, kHourDuration * 10 + kMinuteDuration * 15),
61         WeeklyTime(1, kHourDuration * 15 + kMinuteDuration * 30)),
62     // Wednesday 8:30 PM to Thursday 8:40 AM.
63     WeeklyTimeInterval(WeeklyTime(3, kHourDuration * 20 + kMinuteDuration * 30),
64                        WeeklyTime(4, kHourDuration * 8 + kMinuteDuration * 40)),
65 };
66 
67 }  // namespace
68 
69 class MockUpdateTimeRestrictionsMonitorDelegate
70     : public UpdateTimeRestrictionsMonitor::Delegate {
71  public:
72   virtual ~MockUpdateTimeRestrictionsMonitorDelegate() = default;
73 
74   MOCK_METHOD0(OnRestrictedIntervalStarts, void());
75 };
76 
77 class UmUpdateTimeRestrictionsMonitorTest : public ::testing::Test {
78  protected:
UmUpdateTimeRestrictionsMonitorTest()79   UmUpdateTimeRestrictionsMonitorTest() {
80     fake_loop_.SetAsCurrent();
81     FakeSystemState::CreateInstance();
82   }
83 
TearDown()84   void TearDown() override { EXPECT_FALSE(fake_loop_.PendingTasks()); }
85 
SetNow(const base::Time::Exploded & exploded_now)86   bool SetNow(const base::Time::Exploded& exploded_now) {
87     base::Time now;
88     if (!base::Time::FromLocalExploded(exploded_now, &now))
89       return false;
90 
91     test_clock_.SetNow(now);
92     FakeSystemState::Get()->fake_clock()->SetWallclockTime(now);
93     return true;
94   }
95 
AdvanceAfterTimestamp(const WeeklyTime & timestamp)96   void AdvanceAfterTimestamp(const WeeklyTime& timestamp) {
97     const WeeklyTime now = WeeklyTime::FromTime(test_clock_.Now());
98     const base::TimeDelta duration =
99         now.GetDurationTo(timestamp) + kDurationOffset;
100     test_clock_.Advance(duration);
101     FakeSystemState::Get()->fake_clock()->SetWallclockTime(test_clock_.Now());
102   }
103 
VerifyExpectationsOnDelegate()104   void VerifyExpectationsOnDelegate() {
105     testing::Mock::VerifyAndClearExpectations(&mock_delegate_);
106   }
107 
UpdateRestrictedIntervals(const WeeklyTimeIntervalVector & policy_value)108   void UpdateRestrictedIntervals(const WeeklyTimeIntervalVector& policy_value) {
109     auto* policy_variable =
110         fake_state_.device_policy_provider()->var_disallowed_time_intervals();
111     policy_variable->reset(new WeeklyTimeIntervalVector(policy_value));
112     policy_variable->NotifyValueChanged();
113   }
114 
IsMonitoringInterval()115   bool IsMonitoringInterval() {
116     return monitor_.has_value() && monitor_.value().IsMonitoringInterval();
117   }
118 
BuildMonitorAndVerify(const WeeklyTimeIntervalVector * policy_value,bool expect_delegate_called,bool expect_monitoring)119   void BuildMonitorAndVerify(const WeeklyTimeIntervalVector* policy_value,
120                              bool expect_delegate_called,
121                              bool expect_monitoring) {
122     if (expect_delegate_called)
123       EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
124     else
125       EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
126 
127     fake_state_.device_policy_provider()
128         ->var_disallowed_time_intervals()
129         ->reset(policy_value != nullptr
130                     ? new WeeklyTimeIntervalVector(*policy_value)
131                     : nullptr);
132     monitor_.emplace(fake_state_.device_policy_provider(), &mock_delegate_);
133     if (expect_delegate_called)
134       MessageLoopRunMaxIterations(MessageLoop::current(), 10);
135     VerifyExpectationsOnDelegate();
136 
137     if (expect_monitoring)
138       EXPECT_TRUE(IsMonitoringInterval());
139     else
140       EXPECT_FALSE(IsMonitoringInterval());
141   }
142 
143   base::SimpleTestClock test_clock_;
144   FakeMessageLoop fake_loop_{&test_clock_};
145   FakeState fake_state_;
146   MockUpdateTimeRestrictionsMonitorDelegate mock_delegate_;
147   base::Optional<UpdateTimeRestrictionsMonitor> monitor_;
148 };
149 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,PolicyIsNotSet)150 TEST_F(UmUpdateTimeRestrictionsMonitorTest, PolicyIsNotSet) {
151   BuildMonitorAndVerify(
152       nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
153 }
154 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,PolicyHasEmptyIntervalList)155 TEST_F(UmUpdateTimeRestrictionsMonitorTest, PolicyHasEmptyIntervalList) {
156   WeeklyTimeIntervalVector empty_policy;
157   BuildMonitorAndVerify(&empty_policy,
158                         /*expect_delegate_called=*/false,
159                         /*expect_monitoring=*/false);
160 }
161 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,CurrentTimeOutsideOfRestrictedInterval)162 TEST_F(UmUpdateTimeRestrictionsMonitorTest,
163        CurrentTimeOutsideOfRestrictedInterval) {
164   ASSERT_TRUE(SetNow(kInitialTimeBeforeInterval));
165   BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
166                         /*expect_delegate_called=*/false,
167                         /*expect_monitoring=*/true);
168 
169   // Monitor should only notify start when passing start of interval.
170   EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
171   AdvanceAfterTimestamp(
172       kTestTwoDisallowedTimeIntervals[current_restricted_interval_index]
173           .start());
174   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
175   VerifyExpectationsOnDelegate();
176 }
177 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,CurrentTimeWithinRestrictedInterval)178 TEST_F(UmUpdateTimeRestrictionsMonitorTest,
179        CurrentTimeWithinRestrictedInterval) {
180   // Monitor should notify start when it is built with current
181   // time within interval.
182   ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
183   BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
184                         /*expect_delegate_called=*/true,
185                         /*expect_monitoring=*/false);
186 }
187 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,PolicyChangeFromNotSetToOutsideInterval)188 TEST_F(UmUpdateTimeRestrictionsMonitorTest,
189        PolicyChangeFromNotSetToOutsideInterval) {
190   // Build monitor with empty initial list of intervals.
191   BuildMonitorAndVerify(
192       nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
193 
194   // Monitor should not do any notification right after intervals update.
195   ASSERT_TRUE(SetNow(kInitialTimeBeforeInterval));
196   EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
197   UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
198   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
199   VerifyExpectationsOnDelegate();
200   EXPECT_TRUE(IsMonitoringInterval());
201 
202   // Advance time within new interval and check that notification happen.
203   EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
204   AdvanceAfterTimestamp(
205       kTestTwoDisallowedTimeIntervals[current_restricted_interval_index]
206           .start());
207   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
208   VerifyExpectationsOnDelegate();
209 }
210 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,PolicyChangeFromNotSetToWithinInterval)211 TEST_F(UmUpdateTimeRestrictionsMonitorTest,
212        PolicyChangeFromNotSetToWithinInterval) {
213   // Build monitor with empty initial list of intervals.
214   BuildMonitorAndVerify(
215       nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
216 
217   // Advance time inside upcoming new interval and update the intervals.
218   // Monitor should immediately notify about started interval.
219   ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
220   EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
221   UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
222   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
223   VerifyExpectationsOnDelegate();
224 }
225 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,PolicyChangeFromNotSetToEmptyInterval)226 TEST_F(UmUpdateTimeRestrictionsMonitorTest,
227        PolicyChangeFromNotSetToEmptyInterval) {
228   BuildMonitorAndVerify(
229       nullptr, /*expect_delegate_called=*/false, /*expect_monitoring=*/false);
230 
231   EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
232   UpdateRestrictedIntervals(WeeklyTimeIntervalVector());
233   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
234   VerifyExpectationsOnDelegate();
235   EXPECT_FALSE(IsMonitoringInterval());
236 }
237 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,PolicyChangeFromOneOutsideIntervalToAnother)238 TEST_F(UmUpdateTimeRestrictionsMonitorTest,
239        PolicyChangeFromOneOutsideIntervalToAnother) {
240   // Build monitor with current time outside the intervals.
241   BuildMonitorAndVerify(&kTestTwoDisallowedTimeIntervals,
242                         /*expect_delegate_called=*/false,
243                         /*expect_monitoring=*/true);
244 
245   // Update the intervals to outide of current time and no notification should
246   // happen yet.
247   EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(0);
248   UpdateRestrictedIntervals(kTestOneDisallowedTimeIntervals);
249   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
250   VerifyExpectationsOnDelegate();
251 
252   // Advance time within new interval. Monitor should notify about started
253   // interval.
254   EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
255   AdvanceAfterTimestamp(
256       kTestOneDisallowedTimeIntervals[current_restricted_interval_index]
257           .start());
258   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
259   VerifyExpectationsOnDelegate();
260 }
261 
TEST_F(UmUpdateTimeRestrictionsMonitorTest,PolicyChangeFromOutsideIntervalToWithin)262 TEST_F(UmUpdateTimeRestrictionsMonitorTest,
263        PolicyChangeFromOutsideIntervalToWithin) {
264   ASSERT_TRUE(SetNow(kInitialTimeWithinInterval));
265 
266   // Build monitor with current time outside the intervals.
267   BuildMonitorAndVerify(&kTestOneDisallowedTimeIntervals,
268                         /*expect_delegate_called=*/false,
269                         /*expect_monitoring=*/true);
270 
271   // Update interval such that current time is within it. Monitor should notify
272   // about started interval.
273   EXPECT_CALL(mock_delegate_, OnRestrictedIntervalStarts()).Times(1);
274   UpdateRestrictedIntervals(kTestTwoDisallowedTimeIntervals);
275   MessageLoopRunMaxIterations(MessageLoop::current(), 10);
276   VerifyExpectationsOnDelegate();
277 }
278 
279 }  // namespace chromeos_update_manager
280