// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/system/update/update_notification_controller.h" #include "ash/public/cpp/ash_features.h" #include "ash/shell.h" #include "ash/system/model/system_tray_model.h" #include "ash/system/session/shutdown_confirmation_dialog.h" #include "ash/system/system_notification_controller.h" #include "ash/test/ash_test_base.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/strings/utf_string_conversions.h" #include "build/branding_buildflags.h" #include "ui/message_center/message_center.h" #if BUILDFLAG(GOOGLE_CHROME_BRANDING) #define SYSTEM_APP_NAME "Chrome OS" #else #define SYSTEM_APP_NAME "Chromium OS" #endif namespace ash { class UpdateNotificationControllerTest : public AshTestBase { public: UpdateNotificationControllerTest() = default; ~UpdateNotificationControllerTest() override = default; protected: bool HasNotification() { return message_center::MessageCenter::Get()->FindVisibleNotificationById( UpdateNotificationController::kNotificationId); } std::string GetNotificationTitle() { return base::UTF16ToUTF8( message_center::MessageCenter::Get() ->FindVisibleNotificationById( UpdateNotificationController::kNotificationId) ->title()); } std::string GetNotificationMessage() { return base::UTF16ToUTF8( message_center::MessageCenter::Get() ->FindVisibleNotificationById( UpdateNotificationController::kNotificationId) ->message()); } std::string GetNotificationButton(int index) { return base::UTF16ToUTF8( message_center::MessageCenter::Get() ->FindVisibleNotificationById( UpdateNotificationController::kNotificationId) ->buttons() .at(index) .title); } int GetNotificationButtonCount() { return message_center::MessageCenter::Get() ->FindVisibleNotificationById( UpdateNotificationController::kNotificationId) ->buttons() .size(); } int GetNotificationPriority() { return message_center::MessageCenter::Get() ->FindVisibleNotificationById( UpdateNotificationController::kNotificationId) ->priority(); } void AddSlowBootFilePath(const base::FilePath& file_path) { int bytes_written = base::WriteFile(file_path, "1\n", 2); EXPECT_TRUE(bytes_written == 2); Shell::Get() ->system_notification_controller() ->update_->slow_boot_file_path_ = file_path; } const char* GetUpdateNotificationId() { return UpdateNotificationController::kNotificationId; } ShutdownConfirmationDialog* GetSlowBootConfirmationDialog() { return Shell::Get() ->system_notification_controller() ->update_->confirmation_dialog_; } private: DISALLOW_COPY_AND_ASSIGN(UpdateNotificationControllerTest); }; // Tests that the update icon becomes visible when an update becomes // available. TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdate) { // The system starts with no update pending, so the notification isn't // visible. EXPECT_FALSE(HasNotification()); // Simulate an update. Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, false, UpdateType::kSystem); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification is now visible. ASSERT_TRUE(HasNotification()); EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update", GetNotificationMessage()); EXPECT_EQ("Restart to update", GetNotificationButton(0)); } // Tests that the update icon becomes visible when an update becomes // available. TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdateWithSlowReboot) { // The system starts with no update pending, so the notification isn't // visible. EXPECT_FALSE(HasNotification()); // Add a slow boot file. base::ScopedTempDir tmp_dir; ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); AddSlowBootFilePath(tmp_dir.GetPath().Append("slow_boot_required")); // Simulate an update. Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, false, UpdateType::kSystem); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification is now visible. ASSERT_TRUE(HasNotification()); EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update. This Chromebook needs to restart to apply an update. " "This can take up to 1 minute.", GetNotificationMessage()); EXPECT_EQ("Restart to update", GetNotificationButton(0)); // Ensure Slow Boot Dialog is not open. EXPECT_FALSE(GetSlowBootConfirmationDialog()); // Trigger Click on "Restart to Update" button in Notification. message_center::MessageCenter::Get()->ClickOnNotificationButton( GetUpdateNotificationId(), 0); // Ensure Slow Boot Dialog is open and notification is removed. ASSERT_TRUE(GetSlowBootConfirmationDialog()); EXPECT_FALSE(HasNotification()); // Click the cancel button on Slow Boot Confirmation Dialog. GetSlowBootConfirmationDialog()->CancelDialog(); // Ensure that the Slow Boot Dialog is closed and notification is visible. EXPECT_FALSE(GetSlowBootConfirmationDialog()); EXPECT_TRUE(HasNotification()); } #if BUILDFLAG(GOOGLE_CHROME_BRANDING) TEST_F(UpdateNotificationControllerTest, VisibilityAfterFlashUpdate) { // The system starts with no update pending, so the notification isn't // visible. EXPECT_FALSE(HasNotification()); // Simulate an update. Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, false, UpdateType::kFlash); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification is now visible. ASSERT_TRUE(HasNotification()); EXPECT_EQ("Adobe Flash Player update available", GetNotificationTitle()); EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update", GetNotificationMessage()); EXPECT_EQ("Restart to update", GetNotificationButton(0)); } #endif // Tests that the update icon's visibility after an update becomes // available for downloading over cellular connection. TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdateOverCellularAvailable) { // The system starts with no update pending, so the notification isn't // visible. EXPECT_FALSE(HasNotification()); // Simulate an update available for downloading over cellular connection. Shell::Get()->system_tray_model()->SetUpdateOverCellularAvailableIconVisible( true); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification is now visible. ASSERT_TRUE(HasNotification()); EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update", GetNotificationMessage()); EXPECT_EQ(0, GetNotificationButtonCount()); // Simulate the user's one time permission on downloading the update is // granted. Shell::Get()->system_tray_model()->SetUpdateOverCellularAvailableIconVisible( false); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification disappears. EXPECT_FALSE(HasNotification()); } TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdateRequiringFactoryReset) { // The system starts with no update pending, so the notification isn't // visible. EXPECT_FALSE(HasNotification()); // Simulate an update that requires factory reset. Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, true, false, UpdateType::kSystem); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification is now visible. ASSERT_TRUE(HasNotification()); EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ( "This update requires powerwashing your device." " Learn more about the latest " SYSTEM_APP_NAME " update.", GetNotificationMessage()); EXPECT_EQ("Restart to update", GetNotificationButton(0)); } TEST_F(UpdateNotificationControllerTest, VisibilityAfterRollback) { // The system starts with no update pending, so the notification isn't // visible. EXPECT_FALSE(HasNotification()); // Simulate a rollback. Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, true, UpdateType::kSystem); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification is now visible. ASSERT_TRUE(HasNotification()); EXPECT_EQ("Device will be rolled back", GetNotificationTitle()); EXPECT_EQ( "Your administrator is rolling back your device. All data will" " be deleted when the device is restarted.", GetNotificationMessage()); EXPECT_EQ("Restart and reset", GetNotificationButton(0)); } TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) { // The system starts with no update pending, so the notification isn't // visible. EXPECT_FALSE(HasNotification()); // Simulate an update. Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, false, UpdateType::kSystem); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification is now visible. ASSERT_TRUE(HasNotification()); EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update", GetNotificationMessage()); EXPECT_EQ("Restart to update", GetNotificationButton(0)); const std::string recommended_notification_title( SYSTEM_APP_NAME " will restart in 3 minutes"); const std::string recommended_notification_body( "Your administrator recommended that you restart " SYSTEM_APP_NAME " to apply an update"); // Simulate notification type set to recommended. Shell::Get()->system_tray_model()->SetUpdateNotificationState( NotificationStyle::kAdminRecommended, base::UTF8ToUTF16(recommended_notification_title), base::UTF8ToUTF16(recommended_notification_body)); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification's title and body have changed. ASSERT_TRUE(HasNotification()); EXPECT_EQ(recommended_notification_title, GetNotificationTitle()); EXPECT_EQ(recommended_notification_body, GetNotificationMessage()); EXPECT_EQ("Restart to update", GetNotificationButton(0)); EXPECT_NE(message_center::NotificationPriority::SYSTEM_PRIORITY, GetNotificationPriority()); const std::string required_notification_title(SYSTEM_APP_NAME " will restart in 3 minutes"); const std::string required_notification_body( "Your administrator required that you restart " SYSTEM_APP_NAME " to apply an update"); // Simulate notification type set to required. Shell::Get()->system_tray_model()->SetUpdateNotificationState( NotificationStyle::kAdminRequired, base::UTF8ToUTF16(required_notification_title), base::UTF8ToUTF16(required_notification_body)); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification's title and body have changed. ASSERT_TRUE(HasNotification()); EXPECT_EQ(required_notification_title, GetNotificationTitle()); EXPECT_EQ(required_notification_body, GetNotificationMessage()); EXPECT_EQ("Restart to update", GetNotificationButton(0)); // The admin required relaunch notification has system priority. EXPECT_EQ(message_center::NotificationPriority::SYSTEM_PRIORITY, GetNotificationPriority()); // Simulate notification type set back to default. Shell::Get()->system_tray_model()->SetUpdateNotificationState( NotificationStyle::kDefault, base::string16(), base::string16()); // Showing Update Notification posts a task to check for slow boot request // and use the result of that check to generate appropriate notification. Wait // until everything is complete and then check if the notification is visible. task_environment()->RunUntilIdle(); // The notification has the default text. ASSERT_TRUE(HasNotification()); EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME " update", GetNotificationMessage()); EXPECT_EQ("Restart to update", GetNotificationButton(0)); EXPECT_NE(message_center::NotificationPriority::SYSTEM_PRIORITY, GetNotificationPriority()); } } // namespace ash