1 // Copyright 2018 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 
5 #include "ash/system/update/update_notification_controller.h"
6 
7 #include "ash/public/cpp/ash_features.h"
8 #include "ash/shell.h"
9 #include "ash/system/model/system_tray_model.h"
10 #include "ash/system/session/shutdown_confirmation_dialog.h"
11 #include "ash/system/system_notification_controller.h"
12 #include "ash/test/ash_test_base.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "build/branding_buildflags.h"
18 #include "ui/message_center/message_center.h"
19 
20 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
21 #define SYSTEM_APP_NAME "Chrome OS"
22 #else
23 #define SYSTEM_APP_NAME "Chromium OS"
24 #endif
25 
26 namespace ash {
27 
28 class UpdateNotificationControllerTest : public AshTestBase {
29  public:
30   UpdateNotificationControllerTest() = default;
31   ~UpdateNotificationControllerTest() override = default;
32 
33  protected:
HasNotification()34   bool HasNotification() {
35     return message_center::MessageCenter::Get()->FindVisibleNotificationById(
36         UpdateNotificationController::kNotificationId);
37   }
38 
GetNotificationTitle()39   std::string GetNotificationTitle() {
40     return base::UTF16ToUTF8(
41         message_center::MessageCenter::Get()
42             ->FindVisibleNotificationById(
43                 UpdateNotificationController::kNotificationId)
44             ->title());
45   }
46 
GetNotificationMessage()47   std::string GetNotificationMessage() {
48     return base::UTF16ToUTF8(
49         message_center::MessageCenter::Get()
50             ->FindVisibleNotificationById(
51                 UpdateNotificationController::kNotificationId)
52             ->message());
53   }
54 
GetNotificationButton(int index)55   std::string GetNotificationButton(int index) {
56     return base::UTF16ToUTF8(
57         message_center::MessageCenter::Get()
58             ->FindVisibleNotificationById(
59                 UpdateNotificationController::kNotificationId)
60             ->buttons()
61             .at(index)
62             .title);
63   }
64 
GetNotificationButtonCount()65   int GetNotificationButtonCount() {
66     return message_center::MessageCenter::Get()
67         ->FindVisibleNotificationById(
68             UpdateNotificationController::kNotificationId)
69         ->buttons()
70         .size();
71   }
72 
GetNotificationPriority()73   int GetNotificationPriority() {
74     return message_center::MessageCenter::Get()
75         ->FindVisibleNotificationById(
76             UpdateNotificationController::kNotificationId)
77         ->priority();
78   }
79 
AddSlowBootFilePath(const base::FilePath & file_path)80   void AddSlowBootFilePath(const base::FilePath& file_path) {
81     int bytes_written = base::WriteFile(file_path, "1\n", 2);
82     EXPECT_TRUE(bytes_written == 2);
83     Shell::Get()
84         ->system_notification_controller()
85         ->update_->slow_boot_file_path_ = file_path;
86   }
87 
GetUpdateNotificationId()88   const char* GetUpdateNotificationId() {
89     return UpdateNotificationController::kNotificationId;
90   }
91 
GetSlowBootConfirmationDialog()92   ShutdownConfirmationDialog* GetSlowBootConfirmationDialog() {
93     return Shell::Get()
94         ->system_notification_controller()
95         ->update_->confirmation_dialog_;
96   }
97 
98  private:
99   DISALLOW_COPY_AND_ASSIGN(UpdateNotificationControllerTest);
100 };
101 
102 // Tests that the update icon becomes visible when an update becomes
103 // available.
TEST_F(UpdateNotificationControllerTest,VisibilityAfterUpdate)104 TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdate) {
105   // The system starts with no update pending, so the notification isn't
106   // visible.
107   EXPECT_FALSE(HasNotification());
108 
109   // Simulate an update.
110   Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
111                                                     false, UpdateType::kSystem);
112 
113   // Showing Update Notification posts a task to check for slow boot request
114   // and use the result of that check to generate appropriate notification. Wait
115   // until everything is complete and then check if the notification is visible.
116   task_environment()->RunUntilIdle();
117 
118   // The notification is now visible.
119   ASSERT_TRUE(HasNotification());
120   EXPECT_EQ("Update available", GetNotificationTitle());
121   EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update",
122             GetNotificationMessage());
123   EXPECT_EQ("Restart to update", GetNotificationButton(0));
124 }
125 
126 // Tests that the update icon becomes visible when an update becomes
127 // available.
TEST_F(UpdateNotificationControllerTest,VisibilityAfterUpdateWithSlowReboot)128 TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdateWithSlowReboot) {
129   // The system starts with no update pending, so the notification isn't
130   // visible.
131   EXPECT_FALSE(HasNotification());
132 
133   // Add a slow boot file.
134   base::ScopedTempDir tmp_dir;
135   ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
136   AddSlowBootFilePath(tmp_dir.GetPath().Append("slow_boot_required"));
137 
138   // Simulate an update.
139   Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
140                                                     false, UpdateType::kSystem);
141 
142   // Showing Update Notification posts a task to check for slow boot request
143   // and use the result of that check to generate appropriate notification. Wait
144   // until everything is complete and then check if the notification is visible.
145   task_environment()->RunUntilIdle();
146 
147   // The notification is now visible.
148   ASSERT_TRUE(HasNotification());
149   EXPECT_EQ("Update available", GetNotificationTitle());
150   EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME
151             " update. This Chromebook needs to restart to apply an update. "
152             "This can take up to 1 minute.",
153             GetNotificationMessage());
154   EXPECT_EQ("Restart to update", GetNotificationButton(0));
155 
156   // Ensure Slow Boot Dialog is not open.
157   EXPECT_FALSE(GetSlowBootConfirmationDialog());
158 
159   // Trigger Click on "Restart to Update" button in Notification.
160   message_center::MessageCenter::Get()->ClickOnNotificationButton(
161       GetUpdateNotificationId(), 0);
162 
163   // Ensure Slow Boot Dialog is open and notification is removed.
164   ASSERT_TRUE(GetSlowBootConfirmationDialog());
165   EXPECT_FALSE(HasNotification());
166 
167   // Click the cancel button on Slow Boot Confirmation Dialog.
168   GetSlowBootConfirmationDialog()->CancelDialog();
169 
170   // Ensure that the Slow Boot Dialog is closed and notification is visible.
171   EXPECT_FALSE(GetSlowBootConfirmationDialog());
172   EXPECT_TRUE(HasNotification());
173 }
174 
175 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
TEST_F(UpdateNotificationControllerTest,VisibilityAfterFlashUpdate)176 TEST_F(UpdateNotificationControllerTest, VisibilityAfterFlashUpdate) {
177   // The system starts with no update pending, so the notification isn't
178   // visible.
179   EXPECT_FALSE(HasNotification());
180 
181   // Simulate an update.
182   Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
183                                                     false, UpdateType::kFlash);
184 
185   // Showing Update Notification posts a task to check for slow boot request
186   // and use the result of that check to generate appropriate notification. Wait
187   // until everything is complete and then check if the notification is visible.
188   task_environment()->RunUntilIdle();
189 
190   // The notification is now visible.
191   ASSERT_TRUE(HasNotification());
192   EXPECT_EQ("Adobe Flash Player update available", GetNotificationTitle());
193   EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update",
194             GetNotificationMessage());
195   EXPECT_EQ("Restart to update", GetNotificationButton(0));
196 }
197 #endif
198 
199 // Tests that the update icon's visibility after an update becomes
200 // available for downloading over cellular connection.
TEST_F(UpdateNotificationControllerTest,VisibilityAfterUpdateOverCellularAvailable)201 TEST_F(UpdateNotificationControllerTest,
202        VisibilityAfterUpdateOverCellularAvailable) {
203   // The system starts with no update pending, so the notification isn't
204   // visible.
205   EXPECT_FALSE(HasNotification());
206 
207   // Simulate an update available for downloading over cellular connection.
208   Shell::Get()->system_tray_model()->SetUpdateOverCellularAvailableIconVisible(
209       true);
210 
211   // Showing Update Notification posts a task to check for slow boot request
212   // and use the result of that check to generate appropriate notification. Wait
213   // until everything is complete and then check if the notification is visible.
214   task_environment()->RunUntilIdle();
215 
216   // The notification is now visible.
217   ASSERT_TRUE(HasNotification());
218   EXPECT_EQ("Update available", GetNotificationTitle());
219   EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update",
220             GetNotificationMessage());
221   EXPECT_EQ(0, GetNotificationButtonCount());
222 
223   // Simulate the user's one time permission on downloading the update is
224   // granted.
225   Shell::Get()->system_tray_model()->SetUpdateOverCellularAvailableIconVisible(
226       false);
227 
228   // Showing Update Notification posts a task to check for slow boot request
229   // and use the result of that check to generate appropriate notification. Wait
230   // until everything is complete and then check if the notification is visible.
231   task_environment()->RunUntilIdle();
232 
233   // The notification disappears.
234   EXPECT_FALSE(HasNotification());
235 }
236 
TEST_F(UpdateNotificationControllerTest,VisibilityAfterUpdateRequiringFactoryReset)237 TEST_F(UpdateNotificationControllerTest,
238        VisibilityAfterUpdateRequiringFactoryReset) {
239   // The system starts with no update pending, so the notification isn't
240   // visible.
241   EXPECT_FALSE(HasNotification());
242 
243   // Simulate an update that requires factory reset.
244   Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, true,
245                                                     false, UpdateType::kSystem);
246 
247   // Showing Update Notification posts a task to check for slow boot request
248   // and use the result of that check to generate appropriate notification. Wait
249   // until everything is complete and then check if the notification is visible.
250   task_environment()->RunUntilIdle();
251 
252   // The notification is now visible.
253   ASSERT_TRUE(HasNotification());
254   EXPECT_EQ("Update available", GetNotificationTitle());
255   EXPECT_EQ(
256       "This update requires powerwashing your device."
257       " Learn more about the latest " SYSTEM_APP_NAME " update.",
258       GetNotificationMessage());
259   EXPECT_EQ("Restart to update", GetNotificationButton(0));
260 }
261 
TEST_F(UpdateNotificationControllerTest,VisibilityAfterRollback)262 TEST_F(UpdateNotificationControllerTest, VisibilityAfterRollback) {
263   // The system starts with no update pending, so the notification isn't
264   // visible.
265   EXPECT_FALSE(HasNotification());
266 
267   // Simulate a rollback.
268   Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
269                                                     true, UpdateType::kSystem);
270 
271   // Showing Update Notification posts a task to check for slow boot request
272   // and use the result of that check to generate appropriate notification. Wait
273   // until everything is complete and then check if the notification is visible.
274   task_environment()->RunUntilIdle();
275 
276   // The notification is now visible.
277   ASSERT_TRUE(HasNotification());
278   EXPECT_EQ("Device will be rolled back", GetNotificationTitle());
279   EXPECT_EQ(
280       "Your administrator is rolling back your device. All data will"
281       " be deleted when the device is restarted.",
282       GetNotificationMessage());
283   EXPECT_EQ("Restart and reset", GetNotificationButton(0));
284 }
285 
TEST_F(UpdateNotificationControllerTest,SetUpdateNotificationStateTest)286 TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) {
287   // The system starts with no update pending, so the notification isn't
288   // visible.
289   EXPECT_FALSE(HasNotification());
290 
291   // Simulate an update.
292   Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
293                                                     false, UpdateType::kSystem);
294 
295   // Showing Update Notification posts a task to check for slow boot request
296   // and use the result of that check to generate appropriate notification. Wait
297   // until everything is complete and then check if the notification is visible.
298   task_environment()->RunUntilIdle();
299 
300   // The notification is now visible.
301   ASSERT_TRUE(HasNotification());
302   EXPECT_EQ("Update available", GetNotificationTitle());
303   EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update",
304             GetNotificationMessage());
305   EXPECT_EQ("Restart to update", GetNotificationButton(0));
306 
307   const std::string recommended_notification_title(
308       SYSTEM_APP_NAME " will restart in 3 minutes");
309   const std::string recommended_notification_body(
310       "Your administrator recommended that you restart " SYSTEM_APP_NAME
311       " to apply an update");
312 
313   // Simulate notification type set to recommended.
314   Shell::Get()->system_tray_model()->SetUpdateNotificationState(
315       NotificationStyle::kAdminRecommended,
316       base::UTF8ToUTF16(recommended_notification_title),
317       base::UTF8ToUTF16(recommended_notification_body));
318 
319   // Showing Update Notification posts a task to check for slow boot request
320   // and use the result of that check to generate appropriate notification. Wait
321   // until everything is complete and then check if the notification is visible.
322   task_environment()->RunUntilIdle();
323 
324   // The notification's title and body have changed.
325   ASSERT_TRUE(HasNotification());
326   EXPECT_EQ(recommended_notification_title, GetNotificationTitle());
327   EXPECT_EQ(recommended_notification_body, GetNotificationMessage());
328   EXPECT_EQ("Restart to update", GetNotificationButton(0));
329   EXPECT_NE(message_center::NotificationPriority::SYSTEM_PRIORITY,
330             GetNotificationPriority());
331 
332   const std::string required_notification_title(SYSTEM_APP_NAME
333                                                 " will restart in 3 minutes");
334   const std::string required_notification_body(
335       "Your administrator required that you restart " SYSTEM_APP_NAME
336       " to apply an update");
337 
338   // Simulate notification type set to required.
339   Shell::Get()->system_tray_model()->SetUpdateNotificationState(
340       NotificationStyle::kAdminRequired,
341       base::UTF8ToUTF16(required_notification_title),
342       base::UTF8ToUTF16(required_notification_body));
343 
344   // Showing Update Notification posts a task to check for slow boot request
345   // and use the result of that check to generate appropriate notification. Wait
346   // until everything is complete and then check if the notification is visible.
347   task_environment()->RunUntilIdle();
348 
349   // The notification's title and body have changed.
350   ASSERT_TRUE(HasNotification());
351   EXPECT_EQ(required_notification_title, GetNotificationTitle());
352   EXPECT_EQ(required_notification_body, GetNotificationMessage());
353   EXPECT_EQ("Restart to update", GetNotificationButton(0));
354   // The admin required relaunch notification has system priority.
355   EXPECT_EQ(message_center::NotificationPriority::SYSTEM_PRIORITY,
356             GetNotificationPriority());
357 
358   // Simulate notification type set back to default.
359   Shell::Get()->system_tray_model()->SetUpdateNotificationState(
360       NotificationStyle::kDefault, base::string16(), base::string16());
361 
362   // Showing Update Notification posts a task to check for slow boot request
363   // and use the result of that check to generate appropriate notification. Wait
364   // until everything is complete and then check if the notification is visible.
365   task_environment()->RunUntilIdle();
366 
367   // The notification has the default text.
368   ASSERT_TRUE(HasNotification());
369   EXPECT_EQ("Update available", GetNotificationTitle());
370   EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update",
371             GetNotificationMessage());
372   EXPECT_EQ("Restart to update", GetNotificationButton(0));
373   EXPECT_NE(message_center::NotificationPriority::SYSTEM_PRIORITY,
374             GetNotificationPriority());
375 }
376 
377 }  // namespace ash
378