scheduled_feature_unittest.cc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <cmath>
  5. #include <limits>
  6. #include <memory>
  7. #include <sstream>
  8. #include <string>
  9. #include "ash/constants/ash_features.h"
  10. #include "ash/constants/ash_pref_names.h"
  11. #include "ash/public/cpp/schedule_enums.h"
  12. #include "ash/public/cpp/session/session_types.h"
  13. #include "ash/session/session_controller_impl.h"
  14. #include "ash/session/test_session_controller_client.h"
  15. #include "ash/shell.h"
  16. #include "ash/style/dark_light_mode_controller_impl.h"
  17. #include "ash/system/geolocation/geolocation_controller.h"
  18. #include "ash/system/geolocation/geolocation_controller_test_util.h"
  19. #include "ash/system/geolocation/test_geolocation_url_loader_factory.h"
  20. #include "ash/system/scheduled_feature/scheduled_feature.h"
  21. #include "ash/system/time/time_of_day.h"
  22. #include "ash/test/ash_test_base.h"
  23. #include "ash/test/ash_test_helper.h"
  24. #include "ash/test_shell_delegate.h"
  25. #include "base/command_line.h"
  26. #include "base/memory/ptr_util.h"
  27. #include "base/strings/pattern.h"
  28. #include "base/test/scoped_feature_list.h"
  29. #include "base/test/simple_test_clock.h"
  30. #include "base/time/time.h"
  31. #include "components/prefs/pref_service.h"
  32. #include "ui/compositor/layer.h"
  33. #include "ui/gfx/geometry/vector3d_f.h"
  34. namespace ash {
  35. namespace {
  36. constexpr char kUser1Email[] = "user1@featuredschedule";
  37. constexpr char kUser2Email[] = "user2@featuredschedule";
  38. enum AmPm { kAM, kPM };
  39. Geoposition CreateGeoposition(double latitude,
  40. double longitude,
  41. base::Time timestamp) {
  42. Geoposition position;
  43. position.latitude = latitude;
  44. position.longitude = longitude;
  45. position.status = Geoposition::STATUS_OK;
  46. position.accuracy = 10;
  47. position.timestamp = timestamp;
  48. return position;
  49. }
  50. class TestScheduledFeature : public ScheduledFeature {
  51. public:
  52. TestScheduledFeature(const std::string prefs_path_enabled,
  53. const std::string prefs_path_schedule_type,
  54. const std::string prefs_path_custom_start_time,
  55. const std::string prefs_path_custom_end_time)
  56. : ScheduledFeature(prefs_path_enabled,
  57. prefs_path_schedule_type,
  58. prefs_path_custom_start_time,
  59. prefs_path_custom_end_time) {}
  60. TestScheduledFeature(const TestScheduledFeature& other) = delete;
  61. TestScheduledFeature& operator=(const TestScheduledFeature& rhs) = delete;
  62. ~TestScheduledFeature() override {}
  63. const char* GetFeatureName() const override { return "TestFeature"; }
  64. };
  65. class ScheduledFeatureTest : public NoSessionAshTestBase {
  66. public:
  67. ScheduledFeatureTest() = default;
  68. ScheduledFeatureTest(const ScheduledFeatureTest& other) = delete;
  69. ScheduledFeatureTest& operator=(const ScheduledFeatureTest& rhs) = delete;
  70. ~ScheduledFeatureTest() override = default;
  71. PrefService* user1_pref_service() {
  72. return Shell::Get()->session_controller()->GetUserPrefServiceForUser(
  73. AccountId::FromUserEmail(kUser1Email));
  74. }
  75. PrefService* user2_pref_service() {
  76. return Shell::Get()->session_controller()->GetUserPrefServiceForUser(
  77. AccountId::FromUserEmail(kUser2Email));
  78. }
  79. TestScheduledFeature* feature() const { return feature_.get(); }
  80. GeolocationController* geolocation_controller() {
  81. return geolocation_controller_;
  82. }
  83. base::SimpleTestClock* test_clock() { return &test_clock_; }
  84. const base::OneShotTimer* timer_ptr() const { return timer_ptr_; }
  85. TestGeolocationUrlLoaderFactory* factory() const { return factory_; }
  86. // AshTestBase:
  87. void SetUp() override {
  88. NoSessionAshTestBase::SetUp();
  89. base::Time now;
  90. EXPECT_TRUE(base::Time::FromUTCString("23 Dec 2021 12:00:00", &now));
  91. test_clock()->SetNow(now);
  92. // Set the clock of geolocation controller to our test clock to control the
  93. // time now.
  94. geolocation_controller_ = ash::Shell::Get()->geolocation_controller();
  95. geolocation_controller()->SetClockForTesting(&test_clock_);
  96. // Every feature that is auto scheduled by default needs to set test clock.
  97. // Otherwise the tests will fails `DCHECK_GE(start_time, now)` in
  98. // `ScheduledFeature::RefreshScheduleTimer()` when the new user session
  99. // is entered and `InitFromUserPrefs()` triggers `RefreshScheduleTimer()`.
  100. ash::Shell::Get()->dark_light_mode_controller()->SetClockForTesting(
  101. &test_clock_);
  102. CreateTestUserSessions();
  103. // Simulate user 1 login.
  104. SimulateNewUserFirstLogin(kUser1Email);
  105. // Use user prefs of NightLight, which is an example of ScheduledFeature.
  106. feature_ = std::make_unique<TestScheduledFeature>(
  107. prefs::kNightLightEnabled, prefs::kNightLightScheduleType,
  108. prefs::kNightLightCustomStartTime, prefs::kNightLightCustomEndTime);
  109. ASSERT_FALSE(feature_->GetEnabled());
  110. feature_->SetClockForTesting(&test_clock_);
  111. feature_->OnActiveUserPrefServiceChanged(
  112. Shell::Get()->session_controller()->GetActivePrefService());
  113. timer_ptr_ = geolocation_controller()->GetTimerForTesting();
  114. // `factory_` allows the test to control the value of geoposition
  115. // that the geolocation provider sends back upon geolocation request.
  116. factory_ = static_cast<TestGeolocationUrlLoaderFactory*>(
  117. geolocation_controller()->GetFactoryForTesting());
  118. }
  119. void TearDown() override {
  120. feature_.reset();
  121. NoSessionAshTestBase::TearDown();
  122. }
  123. void CreateTestUserSessions() {
  124. auto* session_controller_client = GetSessionControllerClient();
  125. session_controller_client->Reset();
  126. session_controller_client->AddUserSession(kUser1Email);
  127. session_controller_client->AddUserSession(kUser2Email);
  128. }
  129. void SwitchActiveUser(const std::string& email) {
  130. GetSessionControllerClient()->SwitchActiveUser(
  131. AccountId::FromUserEmail(email));
  132. }
  133. bool GetEnabled() { return feature_->GetEnabled(); }
  134. ScheduleType GetScheduleType() { return feature_->GetScheduleType(); }
  135. void SetFeatureEnabled(bool enabled) { feature_->SetEnabled(enabled); }
  136. void SetScheduleType(ScheduleType type) { feature_->SetScheduleType(type); }
  137. // Convenience function for constructing a TimeOfDay object for exact hours
  138. // during the day. |hour| is between 1 and 12.
  139. TimeOfDay MakeTimeOfDay(int hour, AmPm am_pm) {
  140. DCHECK_GE(hour, 1);
  141. DCHECK_LE(hour, 12);
  142. if (am_pm == kAM) {
  143. hour %= 12;
  144. } else {
  145. if (hour != 12)
  146. hour += 12;
  147. hour %= 24;
  148. }
  149. return TimeOfDay(hour * 60).SetClock(&test_clock_);
  150. }
  151. // Fires the timer of the scheduler to request geoposition and wait for all
  152. // observers to receive the latest geoposition from the server.
  153. void FireTimerToFetchGeoposition() {
  154. GeopositionResponsesWaiter waiter(geolocation_controller_);
  155. EXPECT_TRUE(timer_ptr()->IsRunning());
  156. // Fast forward the scheduler to reach the time when the controller
  157. // requests for geoposition from the server in
  158. // `GeolocationController::RequestGeoposition`.
  159. timer_ptr_->FireNow();
  160. // Waits for the observers to receive the geoposition from the server.
  161. waiter.Wait();
  162. }
  163. // Sets the geoposition to be returned from the `factory_` upon the
  164. // `GeolocationController` request.
  165. void SetServerPosition(const Geoposition& position) {
  166. position_ = position;
  167. factory_->set_position(position_);
  168. }
  169. // Checks if the feature is observing geoposition changes.
  170. bool IsFeatureObservingGeoposition() {
  171. return geolocation_controller()->HasObserverForTesting(feature());
  172. }
  173. private:
  174. std::unique_ptr<TestScheduledFeature> feature_;
  175. GeolocationController* geolocation_controller_;
  176. base::SimpleTestClock test_clock_;
  177. base::OneShotTimer* timer_ptr_;
  178. TestGeolocationUrlLoaderFactory* factory_;
  179. Geoposition position_;
  180. };
  181. // Tests that switching users retrieves the feature settings for the active
  182. // user's prefs.
  183. TEST_F(ScheduledFeatureTest, UserSwitchAndSettingsPersistence) {
  184. // Start with user1 logged in and update to sunset-to-sunrise schedule type.
  185. const std::string kScheduleTypePrefString = prefs::kNightLightScheduleType;
  186. const ScheduleType user1_schedule_type = ScheduleType::kSunsetToSunrise;
  187. feature()->SetScheduleType(user1_schedule_type);
  188. EXPECT_EQ(user1_schedule_type, GetScheduleType());
  189. EXPECT_EQ(user1_pref_service()->GetInteger(kScheduleTypePrefString),
  190. static_cast<int>(user1_schedule_type));
  191. // Switch to user 2, and set to custom schedule type.
  192. SwitchActiveUser(kUser2Email);
  193. const ScheduleType user2_schedule_type = ScheduleType::kCustom;
  194. user2_pref_service()->SetInteger(kScheduleTypePrefString,
  195. static_cast<int>(user2_schedule_type));
  196. EXPECT_EQ(user2_schedule_type, GetScheduleType());
  197. EXPECT_EQ(user2_pref_service()->GetInteger(kScheduleTypePrefString),
  198. static_cast<int>(user2_schedule_type));
  199. // Switch back to user 1, to find feature schedule type is restored to
  200. // sunset-to-sunrise.
  201. SwitchActiveUser(kUser1Email);
  202. EXPECT_EQ(user1_schedule_type, GetScheduleType());
  203. }
  204. // Tests that the scheduler type is initiallized from user prefs and observes
  205. // geoposition when the scheduler is enabled.
  206. TEST_F(ScheduledFeatureTest, InitScheduleTypeFromUserPrefs) {
  207. // Start with user1 logged in with the default disabled scheduler, `kNone`.
  208. const std::string kScheduleTypePrefString = prefs::kNightLightScheduleType;
  209. const ScheduleType user1_schedule_type = ScheduleType::kNone;
  210. EXPECT_EQ(user1_schedule_type, GetScheduleType());
  211. // Check that the feature does not observe the geoposition when the schedule
  212. // type is `kNone`.
  213. EXPECT_FALSE(IsFeatureObservingGeoposition());
  214. // Update user2's schedule type pref to sunset-to-sunrise.
  215. const ScheduleType user2_schedule_type = ScheduleType::kSunsetToSunrise;
  216. user2_pref_service()->SetInteger(kScheduleTypePrefString,
  217. static_cast<int>(user2_schedule_type));
  218. // Switching to user2 should update the schedule type to sunset-to-sunrise.
  219. SwitchActiveUser(kUser2Email);
  220. EXPECT_EQ(user2_schedule_type, GetScheduleType());
  221. // Check that the feature starts observing geoposition when the schedule
  222. // type is changed to `kSunsetToSunrise`.
  223. EXPECT_TRUE(IsFeatureObservingGeoposition());
  224. // Set custom schedule to test that once we switch to the user1 with `kNone`
  225. // schedule type, the feature should remove itself from a geolocation
  226. // observer.
  227. feature()->SetScheduleType(ScheduleType::kCustom);
  228. EXPECT_TRUE(IsFeatureObservingGeoposition());
  229. // Make sure that switching back to user1 makes it remove itself from the
  230. // geoposition observer.
  231. SwitchActiveUser(kUser1Email);
  232. EXPECT_EQ(user1_schedule_type, GetScheduleType());
  233. EXPECT_FALSE(IsFeatureObservingGeoposition());
  234. }
  235. // Tests transitioning from kNone to kCustom and back to kNone schedule
  236. // types.
  237. TEST_F(ScheduledFeatureTest, ScheduleNoneToCustomTransition) {
  238. // Now is 6:00 PM.
  239. test_clock()->SetNow(MakeTimeOfDay(6, AmPm::kPM).ToTimeToday());
  240. SetFeatureEnabled(false);
  241. feature()->SetScheduleType(ScheduleType::kNone);
  242. // Start time is at 3:00 PM and end time is at 8:00 PM.
  243. feature()->SetCustomStartTime(MakeTimeOfDay(3, AmPm::kPM));
  244. feature()->SetCustomEndTime(MakeTimeOfDay(8, AmPm::kPM));
  245. // 15:00 18:00 20:00
  246. // <----- + ----------- + ----------- + ----->
  247. // | | |
  248. // start now end
  249. //
  250. // Even though "Now" is inside the feature interval, nothing should
  251. // change, since the schedule type is "none".
  252. EXPECT_FALSE(GetEnabled());
  253. // Now change the schedule type to custom, the feature should turn on
  254. // immediately, and the timer should be running with a delay of exactly 2
  255. // hours scheduling the end.
  256. feature()->SetScheduleType(ScheduleType::kCustom);
  257. EXPECT_TRUE(GetEnabled());
  258. EXPECT_TRUE(feature()->timer()->IsRunning());
  259. EXPECT_EQ(base::Hours(2), feature()->timer()->GetCurrentDelay());
  260. // If the user changes the schedule type to "none", the feature status
  261. // should not change, but the timer should not be running.
  262. feature()->SetScheduleType(ScheduleType::kNone);
  263. EXPECT_TRUE(GetEnabled());
  264. EXPECT_FALSE(feature()->timer()->IsRunning());
  265. }
  266. // Tests what happens when the time now reaches the end of the feature
  267. // interval when the feature mode is on.
  268. TEST_F(ScheduledFeatureTest, TestCustomScheduleReachingEndTime) {
  269. test_clock()->SetNow(MakeTimeOfDay(6, AmPm::kPM).ToTimeToday());
  270. feature()->SetCustomStartTime(MakeTimeOfDay(3, AmPm::kPM));
  271. feature()->SetCustomEndTime(MakeTimeOfDay(8, AmPm::kPM));
  272. feature()->SetScheduleType(ScheduleType::kCustom);
  273. EXPECT_TRUE(GetEnabled());
  274. // Simulate reaching the end time by triggering the timer's user task. Make
  275. // sure that the feature ended.
  276. //
  277. // 15:00 20:00
  278. // <----- + ------------------------ + ----->
  279. // | |
  280. // start end & now
  281. //
  282. // Now is 8:00 PM.
  283. test_clock()->SetNow(MakeTimeOfDay(8, AmPm::kPM).ToTimeToday());
  284. feature()->timer()->FireNow();
  285. EXPECT_FALSE(GetEnabled());
  286. // The timer should still be running, but now scheduling the start at 3:00 PM
  287. // tomorrow which is 19 hours from "now" (8:00 PM).
  288. EXPECT_TRUE(feature()->timer()->IsRunning());
  289. EXPECT_EQ(base::Hours(19), feature()->timer()->GetCurrentDelay());
  290. }
  291. // Tests that user toggles from the system menu or system settings override any
  292. // status set by an automatic schedule.
  293. TEST_F(ScheduledFeatureTest, ExplicitUserTogglesWhileScheduleIsActive) {
  294. // Start with the below custom schedule, where the feature is off.
  295. //
  296. // 15:00 20:00 23:00
  297. // <----- + ----------------- + ------------ + ---->
  298. // | | |
  299. // start end now
  300. //
  301. test_clock()->SetNow(MakeTimeOfDay(11, AmPm::kPM).ToTimeToday());
  302. feature()->SetCustomStartTime(MakeTimeOfDay(3, AmPm::kPM));
  303. feature()->SetCustomEndTime(MakeTimeOfDay(8, AmPm::kPM));
  304. feature()->SetScheduleType(ScheduleType::kCustom);
  305. EXPECT_FALSE(GetEnabled());
  306. // What happens if the user manually turns the feature on while the schedule
  307. // type says it should be off?
  308. // User toggles either from the system menu or the System Settings toggle
  309. // button must override any automatic schedule.
  310. SetFeatureEnabled(true);
  311. EXPECT_TRUE(GetEnabled());
  312. // The timer should still be running, but the feature should automatically
  313. // turn off at 8:00 PM tomorrow, which is 21 hours from now (11:00 PM).
  314. EXPECT_TRUE(feature()->timer()->IsRunning());
  315. EXPECT_EQ(base::Hours(21), feature()->timer()->GetCurrentDelay());
  316. // Manually turning it back off should also be respected, and this time the
  317. // start is scheduled at 3:00 PM tomorrow after 19 hours from "now" (8:00
  318. // PM).
  319. SetFeatureEnabled(false);
  320. EXPECT_FALSE(GetEnabled());
  321. EXPECT_TRUE(feature()->timer()->IsRunning());
  322. EXPECT_EQ(base::Hours(16), feature()->timer()->GetCurrentDelay());
  323. }
  324. // Tests that changing the custom start and end times, in such a way that
  325. // shouldn't change the current status, only updates the timer but doesn't
  326. // change the status.
  327. TEST_F(ScheduledFeatureTest, ChangingStartTimesThatDontChangeTheStatus) {
  328. // 16:00 18:00 22:00
  329. // <----- + ----------- + ----------- + ----->
  330. // | | |
  331. // now start end
  332. //
  333. test_clock()->SetNow(MakeTimeOfDay(4, AmPm::kPM).ToTimeToday()); // 4:00 PM.
  334. SetFeatureEnabled(false);
  335. feature()->SetScheduleType(ScheduleType::kNone);
  336. feature()->SetCustomStartTime(MakeTimeOfDay(6, AmPm::kPM)); // 6:00 PM.
  337. feature()->SetCustomEndTime(MakeTimeOfDay(10, AmPm::kPM)); // 10:00 PM.
  338. // Since now is outside the feature interval, changing the schedule type
  339. // to kCustom, shouldn't affect the status. Validate the timer is running
  340. // with a 2-hour delay.
  341. feature()->SetScheduleType(ScheduleType::kCustom);
  342. EXPECT_FALSE(GetEnabled());
  343. EXPECT_TRUE(feature()->timer()->IsRunning());
  344. EXPECT_EQ(base::Hours(2), feature()->timer()->GetCurrentDelay());
  345. // Change the start time in such a way that doesn't change the status, but
  346. // despite that, confirm that schedule has been updated.
  347. feature()->SetCustomStartTime(MakeTimeOfDay(7, AmPm::kPM)); // 7:00 PM.
  348. EXPECT_FALSE(GetEnabled());
  349. EXPECT_TRUE(feature()->timer()->IsRunning());
  350. EXPECT_EQ(base::Hours(3), feature()->timer()->GetCurrentDelay());
  351. // Changing the end time in a similar fashion to the above and expect no
  352. // change.
  353. feature()->SetCustomEndTime(MakeTimeOfDay(11, AmPm::kPM)); // 11:00 PM.
  354. EXPECT_FALSE(GetEnabled());
  355. EXPECT_TRUE(feature()->timer()->IsRunning());
  356. EXPECT_EQ(base::Hours(3), feature()->timer()->GetCurrentDelay());
  357. }
  358. // Tests that the feature should turn on at sunset time and turn off at sunrise
  359. // time.
  360. TEST_F(ScheduledFeatureTest, SunsetSunrise) {
  361. EXPECT_FALSE(GetEnabled());
  362. // Set time now to 10:00 AM.
  363. base::Time current_time = MakeTimeOfDay(10, AmPm::kAM).ToTimeToday();
  364. test_clock()->SetNow(current_time);
  365. EXPECT_FALSE(feature()->timer()->IsRunning());
  366. feature()->SetScheduleType(ScheduleType::kSunsetToSunrise);
  367. EXPECT_FALSE(GetEnabled());
  368. EXPECT_TRUE(feature()->timer()->IsRunning());
  369. EXPECT_EQ(geolocation_controller()->GetSunsetTime() - current_time,
  370. feature()->timer()->GetCurrentDelay());
  371. // Firing a timer should to advance the time to sunset and automatically turn
  372. // on the feature.
  373. current_time = geolocation_controller()->GetSunsetTime();
  374. test_clock()->SetNow(current_time);
  375. feature()->timer()->FireNow();
  376. EXPECT_TRUE(feature()->timer()->IsRunning());
  377. EXPECT_TRUE(GetEnabled());
  378. EXPECT_EQ(
  379. geolocation_controller()->GetSunriseTime() + base::Days(1) - current_time,
  380. feature()->timer()->GetCurrentDelay());
  381. // Firing a timer should advance the time to sunrise and automatically turn
  382. // off the feature.
  383. current_time = geolocation_controller()->GetSunriseTime();
  384. test_clock()->SetNow(current_time);
  385. feature()->timer()->FireNow();
  386. EXPECT_FALSE(GetEnabled());
  387. EXPECT_TRUE(feature()->timer()->IsRunning());
  388. EXPECT_EQ(geolocation_controller()->GetSunsetTime() - current_time,
  389. feature()->timer()->GetCurrentDelay());
  390. }
  391. // Tests that scheduled start time and end time of sunset-to-sunrise feature
  392. // are updated correctly if the geoposition changes.
  393. // TODO(crbug.com/1334047) Fix flakiness and re-enable on ChromeOS.
  394. #if BUILDFLAG(IS_CHROMEOS)
  395. #define MAYBE_SunsetSunriseGeoposition DISABLED_SunsetSunriseGeoposition
  396. #else
  397. #define MAYBE_SunsetSunriseGeoposition SunsetSunriseGeoposition
  398. #endif
  399. TEST_F(ScheduledFeatureTest, MAYBE_SunsetSunriseGeoposition) {
  400. constexpr double kFakePosition1_Latitude = 23.5;
  401. constexpr double kFakePosition1_Longitude = 55.88;
  402. constexpr double kFakePosition2_Latitude = 23.5;
  403. constexpr double kFakePosition2_Longitude = 10.9;
  404. // Position 1 sunset and sunrise times.
  405. //
  406. // sunset-4
  407. // <----- + --------- + ---------------- + ------->
  408. // | | |
  409. // now sunset sunrise
  410. //
  411. GeolocationControllerObserver observer1;
  412. geolocation_controller()->AddObserver(&observer1);
  413. EXPECT_TRUE(timer_ptr()->IsRunning());
  414. EXPECT_FALSE(observer1.possible_change_in_timezone());
  415. // Prepare a valid geoposition.
  416. const Geoposition position = CreateGeoposition(
  417. kFakePosition1_Latitude, kFakePosition1_Longitude, test_clock()->Now());
  418. // Set and fetch position update.
  419. SetServerPosition(position);
  420. FireTimerToFetchGeoposition();
  421. EXPECT_TRUE(observer1.possible_change_in_timezone());
  422. const base::Time sunset_time1 = geolocation_controller()->GetSunsetTime();
  423. const base::Time sunrise_time1 = geolocation_controller()->GetSunriseTime();
  424. // Our assumption is that GeolocationController gives us sunrise time
  425. // earlier in the same day before sunset.
  426. ASSERT_GT(sunset_time1, sunrise_time1);
  427. ASSERT_LT(sunset_time1 - base::Days(1), sunrise_time1);
  428. // Set time now to be 4 hours before sunset.
  429. test_clock()->SetNow(sunset_time1 - base::Hours(4));
  430. // Expect that timer is running and the start is scheduled after 4 hours.
  431. EXPECT_FALSE(feature()->GetEnabled());
  432. feature()->SetScheduleType(ScheduleType::kSunsetToSunrise);
  433. EXPECT_FALSE(feature()->GetEnabled());
  434. EXPECT_TRUE(IsFeatureObservingGeoposition());
  435. EXPECT_TRUE(feature()->timer()->IsRunning());
  436. EXPECT_EQ(base::Hours(4), feature()->timer()->GetCurrentDelay());
  437. // Simulate reaching sunset.
  438. test_clock()->SetNow(sunset_time1); // Now is sunset time of the position1.
  439. feature()->timer()->FireNow();
  440. EXPECT_TRUE(feature()->GetEnabled());
  441. // Timer is running scheduling the end at sunrise of the second day.
  442. EXPECT_TRUE(feature()->timer()->IsRunning());
  443. EXPECT_EQ(sunrise_time1 + base::Days(1) - test_clock()->Now(),
  444. feature()->timer()->GetCurrentDelay());
  445. // Simulate reaching sunrise.
  446. test_clock()->SetNow(sunrise_time1); // Now is sunrise time of the position1
  447. // Now simulate user changing position.
  448. // Position 2 sunset and sunrise times.
  449. //
  450. // <----- + --------- + ---------------- + ------->
  451. // | | |
  452. // sunset2 now (sunrise1) sunrise2
  453. //
  454. const Geoposition position2 = CreateGeoposition(
  455. kFakePosition2_Latitude, kFakePosition2_Longitude, test_clock()->Now());
  456. // Replace a response `position` with `position2`.
  457. factory()->ClearResponses();
  458. SetServerPosition(position2);
  459. FireTimerToFetchGeoposition();
  460. EXPECT_TRUE(observer1.possible_change_in_timezone());
  461. EXPECT_TRUE(IsFeatureObservingGeoposition());
  462. const base::Time sunset_time2 = geolocation_controller()->GetSunsetTime();
  463. const base::Time sunrise_time2 = geolocation_controller()->GetSunriseTime();
  464. // We choose the second location such that the new sunrise time is later
  465. // in the day compared to the old sunrise time, which is also the current
  466. // time.
  467. ASSERT_GT(test_clock()->Now(), sunset_time2);
  468. ASSERT_LT(test_clock()->Now(), sunset_time2 + base::Days(1));
  469. ASSERT_GT(test_clock()->Now(), sunrise_time2);
  470. ASSERT_LT(test_clock()->Now(), sunrise_time2 + base::Days(1));
  471. // Expect that the scheduled end delay has been updated to sunrise of the
  472. // same (second) day in location 2, and the status hasn't changed.
  473. EXPECT_TRUE(feature()->GetEnabled());
  474. EXPECT_TRUE(feature()->timer()->IsRunning());
  475. EXPECT_EQ(sunrise_time2 + base::Days(1) - test_clock()->Now(),
  476. feature()->timer()->GetCurrentDelay());
  477. // Simulate reaching sunrise.
  478. test_clock()->SetNow(sunrise_time2 +
  479. base::Days(1)); // Now is sunrise time of the position2.
  480. feature()->timer()->FireNow();
  481. EXPECT_FALSE(feature()->GetEnabled());
  482. // Timer is running scheduling the start at the sunset of the next day.
  483. EXPECT_TRUE(feature()->timer()->IsRunning());
  484. EXPECT_EQ(sunset_time2 + base::Days(1) - test_clock()->Now(),
  485. feature()->timer()->GetCurrentDelay());
  486. }
  487. // Tests that on device resume from sleep, the feature status is updated
  488. // correctly if the time has changed meanwhile.
  489. TEST_F(ScheduledFeatureTest, CustomScheduleOnResume) {
  490. // Now is 4:00 PM.
  491. test_clock()->SetNow(MakeTimeOfDay(4, AmPm::kPM).ToTimeToday());
  492. feature()->SetEnabled(false);
  493. // Start time is at 6:00 PM and end time is at 10:00 PM. The feature should be
  494. // off.
  495. // 16:00 18:00 22:00
  496. // <----- + ----------- + ----------- + ----->
  497. // | | |
  498. // now start end
  499. //
  500. feature()->SetCustomStartTime(MakeTimeOfDay(6, AmPm::kPM));
  501. feature()->SetCustomEndTime(MakeTimeOfDay(10, AmPm::kPM));
  502. feature()->SetScheduleType(ScheduleType::kCustom);
  503. EXPECT_FALSE(feature()->GetEnabled());
  504. EXPECT_TRUE(feature()->timer()->IsRunning());
  505. // The feature should be enabled in 2 hours.
  506. EXPECT_EQ(base::Hours(2), feature()->timer()->GetCurrentDelay());
  507. // Now simulate that the device was suspended for 3 hours, and the time now
  508. // is 7:00 PM when the devices was resumed. Expect that the feature turns on.
  509. test_clock()->SetNow(MakeTimeOfDay(7, AmPm::kPM).ToTimeToday());
  510. feature()->SuspendDone(base::TimeDelta::Max());
  511. EXPECT_TRUE(feature()->GetEnabled());
  512. EXPECT_TRUE(feature()->timer()->IsRunning());
  513. // The feature should be disabled in 3 hours.
  514. EXPECT_EQ(base::Hours(3), feature()->timer()->GetCurrentDelay());
  515. }
  516. // The following tests ensure that the feature schedule is correctly
  517. // refreshed when the start and end times are inverted (i.e. the "start time" as
  518. // a time of day today is in the future with respect to the "end time" also as a
  519. // time of day today).
  520. //
  521. // Case 1: "Now" is less than both "end" and "start".
  522. TEST_F(ScheduledFeatureTest, CustomScheduleInvertedStartAndEndTimesCase1) {
  523. // Now is 4:00 AM.
  524. test_clock()->SetNow(MakeTimeOfDay(4, AmPm::kAM).ToTimeToday());
  525. SetFeatureEnabled(false);
  526. // Start time is at 9:00 PM and end time is at 6:00 AM. "Now" is less than
  527. // both. The feature should be on.
  528. // 4:00 6:00 21:00
  529. // <----- + ----------- + ----------- + ----->
  530. // | | |
  531. // now end start
  532. //
  533. feature()->SetCustomStartTime(MakeTimeOfDay(9, AmPm::kPM));
  534. feature()->SetCustomEndTime(MakeTimeOfDay(6, AmPm::kAM));
  535. feature()->SetScheduleType(ScheduleType::kCustom);
  536. EXPECT_TRUE(GetEnabled());
  537. EXPECT_TRUE(feature()->timer()->IsRunning());
  538. // The feature should end in two hours.
  539. EXPECT_EQ(base::Hours(2), feature()->timer()->GetCurrentDelay());
  540. }
  541. // Case 2: "Now" is between "end" and "start".
  542. TEST_F(ScheduledFeatureTest, CustomScheduleInvertedStartAndEndTimesCase2) {
  543. // Now is 6:00 AM.
  544. test_clock()->SetNow(MakeTimeOfDay(6, AmPm::kAM).ToTimeToday());
  545. SetFeatureEnabled(false);
  546. // Start time is at 9:00 PM and end time is at 4:00 AM. "Now" is between both.
  547. // The feature should be off.
  548. // 4:00 6:00 21:00
  549. // <----- + ----------- + ----------- + ----->
  550. // | | |
  551. // end now start
  552. //
  553. feature()->SetCustomStartTime(MakeTimeOfDay(9, AmPm::kPM));
  554. feature()->SetCustomEndTime(MakeTimeOfDay(4, AmPm::kAM));
  555. feature()->SetScheduleType(ScheduleType::kCustom);
  556. EXPECT_FALSE(GetEnabled());
  557. EXPECT_TRUE(feature()->timer()->IsRunning());
  558. // The feature should start in 15 hours.
  559. EXPECT_EQ(base::Hours(15), feature()->timer()->GetCurrentDelay());
  560. }
  561. // Case 3: "Now" is greater than both "start" and "end".
  562. TEST_F(ScheduledFeatureTest, CustomScheduleInvertedStartAndEndTimesCase3) {
  563. // Now is 11:00 PM.
  564. test_clock()->SetNow(MakeTimeOfDay(11, AmPm::kPM).ToTimeToday());
  565. SetFeatureEnabled(false);
  566. // Start time is at 9:00 PM and end time is at 4:00 AM. "Now" is greater than
  567. // both. the feature should be on.
  568. // 4:00 21:00 23:00
  569. // <----- + ----------- + ----------- + ----->
  570. // | | |
  571. // end start now
  572. //
  573. feature()->SetCustomStartTime(MakeTimeOfDay(9, AmPm::kPM));
  574. feature()->SetCustomEndTime(MakeTimeOfDay(4, AmPm::kAM));
  575. feature()->SetScheduleType(ScheduleType::kCustom);
  576. EXPECT_TRUE(GetEnabled());
  577. EXPECT_TRUE(feature()->timer()->IsRunning());
  578. // The feature should end in 5 hours.
  579. EXPECT_EQ(base::Hours(5), feature()->timer()->GetCurrentDelay());
  580. }
  581. // Tests that manual changes to the feature status while a schedule is being
  582. // used will be remembered and reapplied across user switches.
  583. TEST_F(ScheduledFeatureTest, MultiUserManualStatusToggleWithSchedules) {
  584. // Setup user 1 to use a custom schedule from 3pm till 8pm, and user 2 to use
  585. // a sunset-to-sunrise schedule from 6pm till 6am.
  586. //
  587. //
  588. // |<--- User 1 NL on --->|
  589. // | |
  590. // <--------+-------------+--------+----------------------------+----------->
  591. // 3pm 6pm 8pm 6am
  592. // | |
  593. // |<----------- User 2 NL on ---------->|
  594. //
  595. // Test cases at:
  596. //
  597. // <---+---------+------------+------------+----------------------------+--->
  598. // 2pm 4pm 7pm 10pm 9am
  599. //
  600. test_clock()->SetNow(MakeTimeOfDay(2, kPM).ToTimeToday());
  601. feature()->SetCustomStartTime(MakeTimeOfDay(3, kPM));
  602. feature()->SetCustomEndTime(MakeTimeOfDay(8, kPM));
  603. feature()->SetScheduleType(ScheduleType::kCustom);
  604. SwitchActiveUser(kUser2Email);
  605. feature()->SetScheduleType(ScheduleType::kSunsetToSunrise);
  606. SwitchActiveUser(kUser1Email);
  607. struct {
  608. base::Time fake_now;
  609. bool user_1_expected_status;
  610. bool user_2_expected_status;
  611. } kTestCases[] = {
  612. {MakeTimeOfDay(2, kPM).ToTimeToday(), false, false},
  613. {MakeTimeOfDay(4, kPM).ToTimeToday(), true, false},
  614. {MakeTimeOfDay(7, kPM).ToTimeToday(), true, true},
  615. {MakeTimeOfDay(10, kPM).ToTimeToday(), false, true},
  616. {MakeTimeOfDay(9, kAM).ToTimeToday() +
  617. base::Days(1), // 9:00 AM tomorrow.
  618. false, false},
  619. };
  620. bool user_1_previous_status = false;
  621. for (const auto& test_case : kTestCases) {
  622. // Each test case begins when user_1 is active.
  623. const bool user_1_toggled_status = !test_case.user_1_expected_status;
  624. const bool user_2_toggled_status = !test_case.user_2_expected_status;
  625. // Apply the test's case fake time, and fire the timer if there's a change
  626. // expected in the feature's status.
  627. test_clock()->SetNow(test_case.fake_now);
  628. if (user_1_previous_status != test_case.user_1_expected_status)
  629. feature()->timer()->FireNow();
  630. user_1_previous_status = test_case.user_1_expected_status;
  631. // The untoggled states for both users should match the expected ones
  632. // according to their schedules.
  633. EXPECT_EQ(test_case.user_1_expected_status, feature()->GetEnabled());
  634. SwitchActiveUser(kUser2Email);
  635. EXPECT_EQ(test_case.user_2_expected_status, feature()->GetEnabled());
  636. // Manually toggle the feature for user_2 and expect that it will be
  637. // remembered when we switch to user_1 and back.
  638. feature()->SetEnabled(user_2_toggled_status);
  639. EXPECT_EQ(user_2_toggled_status, feature()->GetEnabled());
  640. SwitchActiveUser(kUser1Email);
  641. EXPECT_EQ(test_case.user_1_expected_status, feature()->GetEnabled());
  642. SwitchActiveUser(kUser2Email);
  643. EXPECT_EQ(user_2_toggled_status, feature()->GetEnabled());
  644. // Toggle it for user_1 as well, and expect it will be remembered and won't
  645. // affect the already toggled state for user_2.
  646. SwitchActiveUser(kUser1Email);
  647. EXPECT_EQ(test_case.user_1_expected_status, feature()->GetEnabled());
  648. feature()->SetEnabled(user_1_toggled_status);
  649. EXPECT_EQ(user_1_toggled_status, feature()->GetEnabled());
  650. SwitchActiveUser(kUser2Email);
  651. EXPECT_EQ(user_2_toggled_status, feature()->GetEnabled());
  652. // Toggle both users back to their original states in preparation for the
  653. // next test case.
  654. feature()->SetEnabled(test_case.user_2_expected_status);
  655. EXPECT_EQ(test_case.user_2_expected_status, feature()->GetEnabled());
  656. SwitchActiveUser(kUser1Email);
  657. EXPECT_EQ(user_1_toggled_status, feature()->GetEnabled());
  658. feature()->SetEnabled(test_case.user_1_expected_status);
  659. EXPECT_EQ(test_case.user_1_expected_status, feature()->GetEnabled());
  660. }
  661. }
  662. TEST_F(ScheduledFeatureTest,
  663. ManualStatusToggleCanPersistAfterResumeFromSuspend) {
  664. test_clock()->SetNow(MakeTimeOfDay(11, kAM).ToTimeToday());
  665. feature()->SetCustomStartTime(MakeTimeOfDay(3, kPM));
  666. feature()->SetCustomEndTime(MakeTimeOfDay(8, kPM));
  667. feature()->SetScheduleType(ScheduleType::kCustom);
  668. EXPECT_FALSE(feature()->GetEnabled());
  669. // Toggle the status manually and expect that the feature is scheduled to
  670. // turn back off at 8:00 PM.
  671. feature()->SetEnabled(true);
  672. EXPECT_TRUE(feature()->GetEnabled());
  673. EXPECT_TRUE(feature()->timer()->IsRunning());
  674. EXPECT_EQ(base::Hours(9), feature()->timer()->GetCurrentDelay());
  675. // Simulate suspend and then resume at 2:00 PM (which is outside the user's
  676. // custom schedule). However, the manual toggle to on should be kept.
  677. test_clock()->SetNow(MakeTimeOfDay(2, kPM).ToTimeToday());
  678. feature()->SuspendDone(base::TimeDelta{});
  679. EXPECT_TRUE(feature()->GetEnabled());
  680. // Suspend again and resume at 5:00 PM (which is within the user's custom
  681. // schedule). The schedule should be applied normally.
  682. test_clock()->SetNow(MakeTimeOfDay(5, kPM).ToTimeToday());
  683. feature()->SuspendDone(base::TimeDelta{});
  684. EXPECT_TRUE(feature()->GetEnabled());
  685. // Suspend and resume at 9:00 PM and expect the feature to be off.
  686. test_clock()->SetNow(MakeTimeOfDay(9, kPM).ToTimeToday());
  687. feature()->SuspendDone(base::TimeDelta{});
  688. EXPECT_FALSE(feature()->GetEnabled());
  689. }
  690. } // namespace
  691. } // namespace ash