| // Copyright 2018 The Chromium Authors |
| // 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 <optional> |
| |
| #include "ash/public/cpp/login_types.h" |
| #include "ash/public/cpp/notification_utils.h" |
| #include "ash/public/cpp/update_types.h" |
| #include "ash/resources/vector_icons/vector_icons.h" |
| #include "ash/shell.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/system/model/enterprise_domain_model.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/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/branding_buildflags.h" |
| #include "chromeos/constants/chromeos_features.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/chromeos/devicetype_utils.h" |
| #include "ui/chromeos/styles/cros_tokens_color_mappings.h" |
| #include "ui/color/color_id.h" |
| #include "ui/message_center/message_center.h" |
| #include "ui/message_center/message_center_observer.h" |
| #include "ui/message_center/public/cpp/notification_delegate.h" |
| #include "ui/message_center/public/cpp/notification_types.h" |
| |
| namespace ash { |
| namespace { |
| |
| const char kNotificationId[] = "chrome://update"; |
| const char* kDomain = "example.com"; |
| const char* kDeviceDomain = "example.org"; |
| |
| // Waits for the notification to be added. Needed because the controller |
| // posts a task to check for slow boot request before showing the |
| // notification. |
| class AddNotificationWaiter : public message_center::MessageCenterObserver { |
| public: |
| AddNotificationWaiter() { |
| message_center::MessageCenter::Get()->AddObserver(this); |
| } |
| ~AddNotificationWaiter() override { |
| message_center::MessageCenter::Get()->RemoveObserver(this); |
| } |
| |
| void Wait() { run_loop_.Run(); } |
| |
| // message_center::MessageCenterObserver: |
| void OnNotificationAdded(const std::string& notification_id) override { |
| if (notification_id == kNotificationId) |
| run_loop_.Quit(); |
| } |
| |
| base::RunLoop run_loop_; |
| }; |
| |
| void ShowDefaultUpdateNotification() { |
| Shell::Get()->system_tray_model()->ShowUpdateIcon( |
| UpdateSeverity::kLow, /*factory_reset_required=*/false, |
| /*rollback=*/false); |
| } |
| |
| } // namespace |
| |
| class UpdateNotificationControllerTest : public AshTestBase { |
| public: |
| UpdateNotificationControllerTest() = default; |
| |
| UpdateNotificationControllerTest(const UpdateNotificationControllerTest&) = |
| delete; |
| UpdateNotificationControllerTest& operator=( |
| const UpdateNotificationControllerTest&) = delete; |
| |
| ~UpdateNotificationControllerTest() override = default; |
| |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| |
| system_app_name_ = |
| l10n_util::GetStringUTF16(IDS_ASH_MESSAGE_CENTER_SYSTEM_APP_NAME); |
| |
| EnterpriseDomainModel* enterprise_domain = |
| Shell::Get()->system_tray_model()->enterprise_domain(); |
| enterprise_domain->SetEnterpriseAccountDomainInfo(kDomain); |
| enterprise_domain->SetDeviceEnterpriseInfo( |
| DeviceEnterpriseInfo{kDeviceDomain, ManagementDeviceMode::kNone}); |
| } |
| |
| protected: |
| message_center::Notification* GetNotification() { |
| return message_center::MessageCenter::Get()->FindVisibleNotificationById( |
| kNotificationId); |
| } |
| |
| bool HasNotification() { return GetNotification(); } |
| |
| std::string GetNotificationTitle() { |
| return base::UTF16ToUTF8(GetNotification()->title()); |
| } |
| |
| std::string GetNotificationMessage() { |
| return base::UTF16ToUTF8(GetNotification()->message()); |
| } |
| |
| std::string GetNotificationButton(int index) { |
| return base::UTF16ToUTF8(GetNotification()->buttons().at(index).title); |
| } |
| |
| int GetNotificationButtonCount() { |
| return GetNotification()->buttons().size(); |
| } |
| |
| int GetNotificationPriority() { return GetNotification()->priority(); } |
| |
| const gfx::VectorIcon& GetNotificationIcon() { |
| return GetNotification()->vector_small_image(); |
| } |
| |
| bool GetNotificationNeverTimeout() { |
| return GetNotification()->never_timeout(); |
| } |
| |
| void AddSlowBootFilePath(const base::FilePath& file_path) { |
| bool success = base::WriteFile(file_path, "1\n"); |
| EXPECT_TRUE(success); |
| Shell::Get() |
| ->system_notification_controller() |
| ->update_->slow_boot_file_path_ = file_path; |
| } |
| |
| ShutdownConfirmationDialog* GetSlowBootConfirmationDialog() { |
| return Shell::Get() |
| ->system_notification_controller() |
| ->update_->confirmation_dialog_; |
| } |
| |
| void CompareNotificationColor(SkColor expected_color, |
| ui::ColorId expected_color_id_for_jelly) { |
| const auto color_id = GetNotification()->accent_color_id(); |
| const auto color = GetNotification()->accent_color(); |
| |
| if (chromeos::features::IsJellyEnabled() && color_id.has_value()) { |
| // We use `ui::ColorId` for Jelly. |
| EXPECT_EQ(expected_color_id_for_jelly, color_id); |
| } else if (color.has_value()) { |
| EXPECT_EQ(expected_color, color); |
| } |
| } |
| |
| std::u16string system_app_name_; |
| }; |
| |
| // Tests that the update icon becomes visible when an update becomes available. |
| TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdate) { |
| ShowDefaultUpdateNotification(); |
| |
| // 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()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorNormal, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysPrimary); |
| EXPECT_TRUE(strcmp(kSystemMenuUpdateIcon.name, GetNotificationIcon().name) == |
| 0); |
| EXPECT_EQ("Update device", GetNotificationTitle()); |
| EXPECT_EQ("Learn more about the latest " + |
| base::UTF16ToUTF8(system_app_name_) + " update", |
| GetNotificationMessage()); |
| EXPECT_EQ("Restart to update", GetNotificationButton(0)); |
| |
| // Click the restart button. |
| message_center::MessageCenter::Get()->ClickOnNotificationButton( |
| kNotificationId, 0); |
| |
| // Restart was requested. |
| EXPECT_EQ(1, |
| GetSessionControllerClient()->request_restart_for_update_count()); |
| } |
| |
| // Tests that the update icon becomes visible when an update becomes |
| // available. |
| TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdateWithSlowReboot) { |
| // Add a slow boot file. |
| base::ScopedTempDir tmp_dir; |
| ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); |
| AddSlowBootFilePath(tmp_dir.GetPath().Append("slow_boot_required")); |
| |
| ShowDefaultUpdateNotification(); |
| |
| // 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()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorNormal, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysPrimary); |
| EXPECT_TRUE(strcmp(kSystemMenuUpdateIcon.name, GetNotificationIcon().name) == |
| 0); |
| EXPECT_EQ("Update device", GetNotificationTitle()); |
| EXPECT_EQ("Learn more about the latest " + |
| base::UTF16ToUTF8(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( |
| kNotificationId, 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()); |
| } |
| |
| // Tests that the update icon's visibility after an update becomes |
| // available for downloading over cellular connection. |
| TEST_F(UpdateNotificationControllerTest, |
| VisibilityAfterUpdateOverCellularAvailable) { |
| // 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()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorNormal, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysPrimary); |
| EXPECT_TRUE(strcmp(kSystemMenuUpdateIcon.name, GetNotificationIcon().name) == |
| 0); |
| EXPECT_EQ("Update device", GetNotificationTitle()); |
| EXPECT_EQ("Learn more about the latest " + |
| base::UTF16ToUTF8(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) { |
| // Simulate an update that requires factory reset. |
| Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, true, |
| 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(); |
| |
| const std::u16string chrome_os_device_name = ui::GetChromeOSDeviceName(); |
| |
| // The notification is now visible. |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorNormal, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysPrimary); |
| EXPECT_TRUE(strcmp(kSystemMenuUpdateIcon.name, GetNotificationIcon().name) == |
| 0); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_TITLE), |
| GetNotificationTitle()); |
| EXPECT_EQ(l10n_util::GetStringFUTF8(IDS_UPDATE_NOTIFICATION_MESSAGE_POWERWASH, |
| chrome_os_device_name, system_app_name_), |
| GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_ASH_STATUS_TRAY_RESET_TO_UPDATE), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, NoUpdateNotification) { |
| // The system starts with no update pending, so the notification isn't |
| // visible. |
| EXPECT_FALSE(HasNotification()); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, RollbackNotification) { |
| Shell::Get()->system_tray_model()->ShowUpdateIcon( |
| UpdateSeverity::kLow, /*factory_reset_required=*/true, |
| /*rollback=*/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(); |
| |
| const std::u16string chrome_os_device_name = ui::GetChromeOSDeviceName(); |
| |
| // The notification is now visible. |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorWarning, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysWarning); |
| EXPECT_TRUE( |
| strcmp(kSystemMenuRollbackIcon.name, GetNotificationIcon().name) == 0); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_ROLLBACK_NOTIFICATION_TITLE), |
| GetNotificationTitle()); |
| EXPECT_EQ(l10n_util::GetStringFUTF8(IDS_UPDATE_NOTIFICATION_MESSAGE_ROLLBACK, |
| base::ASCIIToUTF16(kDomain), |
| chrome_os_device_name), |
| GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_ROLLBACK_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, RollbackRecommendedNotification) { |
| Shell::Get()->system_tray_model()->ShowUpdateIcon( |
| UpdateSeverity::kLow, /*factory_reset_required=*/true, |
| /*rollback=*/true); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState( |
| {.requirement_type = RelaunchNotificationState::kRecommendedNotOverdue}); |
| |
| // 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(); |
| |
| const std::u16string chrome_os_device_name = ui::GetChromeOSDeviceName(); |
| |
| // Notification is the same as for a non-recommended rollback. |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorWarning, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysWarning); |
| EXPECT_TRUE( |
| strcmp(kSystemMenuRollbackIcon.name, GetNotificationIcon().name) == 0); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_ROLLBACK_NOTIFICATION_TITLE), |
| GetNotificationTitle()); |
| EXPECT_EQ(l10n_util::GetStringFUTF8(IDS_UPDATE_NOTIFICATION_MESSAGE_ROLLBACK, |
| base::ASCIIToUTF16(kDomain), |
| chrome_os_device_name), |
| GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_ROLLBACK_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, |
| RollbackRecommendedOverdueNotification) { |
| Shell::Get()->system_tray_model()->ShowUpdateIcon( |
| UpdateSeverity::kLow, /*factory_reset_required=*/true, |
| /*rollback=*/true); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState( |
| {.requirement_type = RelaunchNotificationState::kRecommendedAndOverdue}); |
| |
| // 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(); |
| |
| const std::u16string chrome_os_device_name = ui::GetChromeOSDeviceName(); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorCriticalWarning, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysError); |
| EXPECT_TRUE( |
| strcmp(kSystemMenuRollbackIcon.name, GetNotificationIcon().name) == 0); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_ROLLBACK_OVERDUE_NOTIFICATION_TITLE), |
| GetNotificationTitle()); |
| EXPECT_EQ(l10n_util::GetStringFUTF8( |
| IDS_UPDATE_NOTIFICATION_MESSAGE_ROLLBACK_OVERDUE, |
| base::ASCIIToUTF16(kDomain), chrome_os_device_name), |
| GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_ROLLBACK_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, RollbackRequiredNotification) { |
| Shell::Get()->system_tray_model()->ShowUpdateIcon( |
| UpdateSeverity::kLow, /*factory_reset_required=*/true, |
| /*rollback=*/true); |
| |
| constexpr base::TimeDelta remaining_time = base::Seconds(3); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState({ |
| .requirement_type = RelaunchNotificationState::kRequired, |
| .rounded_time_until_reboot_required = remaining_time, |
| }); |
| |
| const std::u16string chrome_os_device_name = ui::GetChromeOSDeviceName(); |
| |
| // 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(); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorCriticalWarning, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysError); |
| EXPECT_TRUE( |
| strcmp(kSystemMenuRollbackIcon.name, GetNotificationIcon().name) == 0); |
| EXPECT_EQ( |
| l10n_util::GetPluralStringFUTF8(IDS_ROLLBACK_REQUIRED_TITLE_SECONDS, 3), |
| GetNotificationTitle()); |
| EXPECT_EQ(l10n_util::GetStringFUTF8(IDS_ROLLBACK_REQUIRED_BODY, |
| base::ASCIIToUTF16(kDomain), |
| chrome_os_device_name), |
| GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_ROLLBACK_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationRecommended) { |
| ShowDefaultUpdateNotification(); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState( |
| {.requirement_type = RelaunchNotificationState::kRecommendedNotOverdue}); |
| |
| task_environment()->RunUntilIdle(); |
| |
| const std::string expected_notification_title = |
| l10n_util::GetStringUTF8(IDS_RELAUNCH_RECOMMENDED_TITLE); |
| const std::string expected_notification_body = l10n_util::GetStringFUTF8( |
| IDS_RELAUNCH_RECOMMENDED_BODY, base::ASCIIToUTF16(kDomain)); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorNormal, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysPrimary); |
| EXPECT_TRUE(strcmp(vector_icons::kBusinessIcon.name, |
| GetNotificationIcon().name) == 0); |
| EXPECT_EQ(expected_notification_title, GetNotificationTitle()); |
| EXPECT_EQ(expected_notification_body, GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, |
| SetUpdateNotificationRecommendedOverdue) { |
| ShowDefaultUpdateNotification(); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState( |
| {.requirement_type = RelaunchNotificationState::kRecommendedAndOverdue}); |
| |
| task_environment()->RunUntilIdle(); |
| |
| const std::string expected_notification_title = |
| l10n_util::GetStringUTF8(IDS_RELAUNCH_RECOMMENDED_OVERDUE_TITLE); |
| const std::string expected_notification_body = l10n_util::GetStringFUTF8( |
| IDS_RELAUNCH_RECOMMENDED_OVERDUE_BODY, base::ASCIIToUTF16(kDomain)); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorNormal, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysPrimary); |
| EXPECT_TRUE(strcmp(vector_icons::kBusinessIcon.name, |
| GetNotificationIcon().name) == 0); |
| EXPECT_EQ(expected_notification_title, GetNotificationTitle()); |
| EXPECT_EQ(expected_notification_body, GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationRequiredDays) { |
| ShowDefaultUpdateNotification(); |
| |
| constexpr base::TimeDelta remaining_time = base::Days(3); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState({ |
| .requirement_type = RelaunchNotificationState::kRequired, |
| .rounded_time_until_reboot_required = remaining_time, |
| }); |
| |
| task_environment()->RunUntilIdle(); |
| |
| const std::string expected_notification_title = |
| l10n_util::GetPluralStringFUTF8(IDS_RELAUNCH_REQUIRED_TITLE_DAYS, 3); |
| const std::string expected_notification_body = l10n_util::GetStringFUTF8( |
| IDS_RELAUNCH_REQUIRED_BODY, base::ASCIIToUTF16(kDomain)); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorWarning, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysWarning); |
| EXPECT_TRUE(strcmp(vector_icons::kBusinessIcon.name, |
| GetNotificationIcon().name) == 0); |
| EXPECT_EQ(message_center::SYSTEM_PRIORITY, GetNotificationPriority()); |
| EXPECT_EQ(true, GetNotificationNeverTimeout()); |
| EXPECT_EQ(expected_notification_title, GetNotificationTitle()); |
| EXPECT_EQ(expected_notification_body, GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, |
| SetUpdateNotificationRequiredWithDevicePolicySource) { |
| ShowDefaultUpdateNotification(); |
| AddNotificationWaiter waiter; |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState({ |
| .requirement_type = RelaunchNotificationState::kRequired, |
| .policy_source = RelaunchNotificationState::kDevice, |
| }); |
| |
| waiter.Wait(); |
| |
| const std::string expected_notification_body = l10n_util::GetStringFUTF8( |
| IDS_RELAUNCH_REQUIRED_BODY, base::ASCIIToUTF16(kDeviceDomain)); |
| |
| ASSERT_TRUE(HasNotification()); |
| EXPECT_EQ(expected_notification_body, GetNotificationMessage()); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, |
| SetUpdateNotificationWithoutAccountDomainManager) { |
| ShowDefaultUpdateNotification(); |
| Shell::Get() |
| ->system_tray_model() |
| ->enterprise_domain() |
| ->SetEnterpriseAccountDomainInfo(std::string()); |
| AddNotificationWaiter waiter; |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState( |
| {.requirement_type = RelaunchNotificationState::kRequired}); |
| |
| waiter.Wait(); |
| |
| const std::string expected_notification_body = l10n_util::GetStringFUTF8( |
| IDS_UPDATE_NOTIFICATION_MESSAGE_LEARN_MORE, system_app_name_); |
| |
| ASSERT_TRUE(HasNotification()); |
| EXPECT_EQ(expected_notification_body, GetNotificationMessage()); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationRequiredHours) { |
| ShowDefaultUpdateNotification(); |
| |
| constexpr base::TimeDelta remaining_time = base::Hours(3); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState({ |
| .requirement_type = RelaunchNotificationState::kRequired, |
| .rounded_time_until_reboot_required = remaining_time, |
| }); |
| |
| task_environment()->RunUntilIdle(); |
| |
| const std::string expected_notification_title = |
| l10n_util::GetPluralStringFUTF8(IDS_RELAUNCH_REQUIRED_TITLE_HOURS, 3); |
| const std::string expected_notification_body = l10n_util::GetStringFUTF8( |
| IDS_RELAUNCH_REQUIRED_BODY, base::ASCIIToUTF16(kDomain)); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorWarning, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysWarning); |
| EXPECT_TRUE(strcmp(vector_icons::kBusinessIcon.name, |
| GetNotificationIcon().name) == 0); |
| EXPECT_EQ(message_center::SYSTEM_PRIORITY, GetNotificationPriority()); |
| EXPECT_EQ(true, GetNotificationNeverTimeout()); |
| EXPECT_EQ(expected_notification_title, GetNotificationTitle()); |
| EXPECT_EQ(expected_notification_body, GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationRequiredMinutes) { |
| ShowDefaultUpdateNotification(); |
| |
| constexpr base::TimeDelta remaining_time = base::Minutes(3); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState({ |
| .requirement_type = RelaunchNotificationState::kRequired, |
| .rounded_time_until_reboot_required = remaining_time, |
| }); |
| |
| task_environment()->RunUntilIdle(); |
| |
| const std::string expected_notification_title = |
| l10n_util::GetPluralStringFUTF8(IDS_RELAUNCH_REQUIRED_TITLE_MINUTES, 3); |
| const std::string expected_notification_body = l10n_util::GetStringFUTF8( |
| IDS_RELAUNCH_REQUIRED_BODY, base::ASCIIToUTF16(kDomain)); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorWarning, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysWarning); |
| EXPECT_TRUE(strcmp(vector_icons::kBusinessIcon.name, |
| GetNotificationIcon().name) == 0); |
| EXPECT_EQ(message_center::SYSTEM_PRIORITY, GetNotificationPriority()); |
| EXPECT_EQ(true, GetNotificationNeverTimeout()); |
| EXPECT_EQ(expected_notification_title, GetNotificationTitle()); |
| EXPECT_EQ(expected_notification_body, GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationRequiredSeconds) { |
| ShowDefaultUpdateNotification(); |
| |
| constexpr base::TimeDelta remaining_time = base::Seconds(3); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState({ |
| .requirement_type = RelaunchNotificationState::kRequired, |
| .rounded_time_until_reboot_required = remaining_time, |
| }); |
| |
| task_environment()->RunUntilIdle(); |
| |
| const std::string expected_notification_title = |
| l10n_util::GetPluralStringFUTF8(IDS_RELAUNCH_REQUIRED_TITLE_SECONDS, 3); |
| const std::string expected_notification_body = l10n_util::GetStringFUTF8( |
| IDS_RELAUNCH_REQUIRED_BODY, base::ASCIIToUTF16(kDomain)); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorWarning, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysWarning); |
| EXPECT_TRUE(strcmp(vector_icons::kBusinessIcon.name, |
| GetNotificationIcon().name) == 0); |
| EXPECT_EQ(message_center::SYSTEM_PRIORITY, GetNotificationPriority()); |
| EXPECT_EQ(true, GetNotificationNeverTimeout()); |
| EXPECT_EQ(expected_notification_title, GetNotificationTitle()); |
| EXPECT_EQ(expected_notification_body, GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| } |
| |
| // Simulates setting the notification back to the default after showing |
| // one for recommended updates. |
| TEST_F(UpdateNotificationControllerTest, SetBackToDefault) { |
| ShowDefaultUpdateNotification(); |
| |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState( |
| {.requirement_type = RelaunchNotificationState::kRecommendedNotOverdue}); |
| |
| task_environment()->RunUntilIdle(); |
| |
| // Reset update state. |
| Shell::Get()->system_tray_model()->SetRelaunchNotificationState({}); |
| |
| task_environment()->RunUntilIdle(); |
| |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorNormal, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysPrimary); |
| EXPECT_TRUE(strcmp(kSystemMenuUpdateIcon.name, GetNotificationIcon().name) == |
| 0); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_TITLE), |
| GetNotificationTitle()); |
| EXPECT_EQ(l10n_util::GetStringFUTF8( |
| IDS_UPDATE_NOTIFICATION_MESSAGE_LEARN_MORE, system_app_name_), |
| GetNotificationMessage()); |
| EXPECT_EQ(l10n_util::GetStringUTF8(IDS_UPDATE_NOTIFICATION_RESTART_BUTTON), |
| GetNotificationButton(0)); |
| EXPECT_NE(message_center::NotificationPriority::SYSTEM_PRIORITY, |
| GetNotificationPriority()); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, |
| VisibilityAfterDeferredUpdateShowNotification) { |
| // Simulate a deferred update. |
| Shell::Get()->system_tray_model()->SetUpdateDeferred( |
| DeferredUpdateState::kShowNotification); |
| |
| // Wait until everything is complete and then check if the notification is |
| // visible. |
| task_environment()->RunUntilIdle(); |
| |
| // The notification is now visible. |
| ASSERT_TRUE(HasNotification()); |
| CompareNotificationColor( |
| /*expected_color=*/kSystemNotificationColorNormal, |
| /*expected_color_id_for_jelly=*/cros_tokens::kCrosSysPrimary); |
| EXPECT_TRUE(strcmp(kSystemMenuUpdateIcon.name, GetNotificationIcon().name) == |
| 0); |
| EXPECT_EQ("Update device", GetNotificationTitle()); |
| EXPECT_EQ( |
| "Get the latest features and security improvements. Updates happen in " |
| "the background.", |
| GetNotificationMessage()); |
| EXPECT_EQ("Update", GetNotificationButton(0)); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, |
| VisibilityAfterDeferredUpdateShowDialog) { |
| // Simulate a deferred update. |
| Shell::Get()->system_tray_model()->SetUpdateDeferred( |
| DeferredUpdateState::kShowDialog); |
| |
| // Wait until everything is complete and then check if the notification is |
| // not visible. |
| task_environment()->RunUntilIdle(); |
| |
| // The notification is not visible. |
| ASSERT_FALSE(HasNotification()); |
| } |
| |
| TEST_F(UpdateNotificationControllerTest, VisibilityAfterDeferredUpdateOff) { |
| // Simulate a deferred update. |
| Shell::Get()->system_tray_model()->SetUpdateDeferred( |
| DeferredUpdateState::kNone); |
| |
| // Wait until everything is complete and then check if the notification is |
| // not visible. |
| task_environment()->RunUntilIdle(); |
| |
| // The notification is not visible. |
| ASSERT_FALSE(HasNotification()); |
| } |
| |
| } // namespace ash |