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