chromium/ash/system/firmware_update/firmware_update_notification_controller_unittest.cc

// Copyright 2022 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/firmware_update/firmware_update_notification_controller.h"

#include <memory>
#include <optional>

#include "ash/public/cpp/test/test_system_tray_client.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "chromeos/ash/components/dbus/fwupd/fwupd_client.h"
#include "chromeos/ash/components/fwupd/firmware_update_manager.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
#include "components/user_manager/user_type.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"

namespace ash {

namespace {

using ::message_center::MessageCenter;

const char kFirmwareUpdateNotificationId[] =
    "cros_firmware_update_notification_id";

}  // namespace

class FirmwareUpdateNotificationControllerTest : public AshTestBase {
 public:
  FirmwareUpdateNotificationControllerTest() = default;
  FirmwareUpdateNotificationControllerTest(
      const FirmwareUpdateNotificationControllerTest&) = delete;
  FirmwareUpdateNotificationControllerTest& operator=(
      const FirmwareUpdateNotificationControllerTest&) = delete;
  ~FirmwareUpdateNotificationControllerTest() override = default;

  void SetUp() override {
    AshTestBase::SetUp();
    // Call NotifyFirstSessionReady to cause FirmwareUpdateManager to be
    // initialized since it is only meant to be initialized after core startup
    // tasks have been completed.
    Shell::Get()->session_controller()->NotifyFirstSessionReady();
  }

  FirmwareUpdateNotificationController* controller() {
    return Shell::Get()->firmware_update_notification_controller();
  }

  message_center::Notification* GetFirmwareUpdateNotification() {
    return MessageCenter::Get()->FindVisibleNotificationById(
        kFirmwareUpdateNotificationId);
  }

  int GetNumFirmwareUpdateUIOpened() {
    return GetSystemTrayClient()->show_firmware_update_count();
  }

  void ClickNotification(std::optional<int> button_index) {
    // No button index means the notification body was clicked.
    if (!button_index.has_value()) {
      message_center::Notification* notification =
          MessageCenter::Get()->FindVisibleNotificationById(
              kFirmwareUpdateNotificationId);
      notification->delegate()->Click(std::nullopt, std::nullopt);
      return;
    }

    message_center::Notification* notification =
        MessageCenter::Get()->FindVisibleNotificationById(
            kFirmwareUpdateNotificationId);
    notification->delegate()->Click(button_index, std::nullopt);
  }
};

TEST_F(FirmwareUpdateNotificationControllerTest, FirmwareUpdateNotification) {
  EXPECT_EQ(0u, MessageCenter::Get()->NotificationCount());

  controller()->NotifyFirmwareUpdateAvailable();
  EXPECT_EQ(1u, MessageCenter::Get()->NotificationCount());

  message_center::Notification* notification = GetFirmwareUpdateNotification();

  EXPECT_TRUE(notification);

  // Ensure this notification has one button.
  EXPECT_EQ(1u, notification->buttons().size());

  EXPECT_EQ(0, GetNumFirmwareUpdateUIOpened());
  // Click on the update button and expect it to open the Firmware Update
  // SWA.
  ClickNotification(/*button_index=*/0);
  EXPECT_EQ(1, GetNumFirmwareUpdateUIOpened());
  // Clicking on the notification will close it.
  EXPECT_EQ(0u, MessageCenter::Get()->NotificationCount());

  // Open new notification and click on its body.
  controller()->NotifyFirmwareUpdateAvailable();
  EXPECT_EQ(1u, MessageCenter::Get()->NotificationCount());
  ClickNotification(std::nullopt);
  EXPECT_EQ(2, GetNumFirmwareUpdateUIOpened());
  EXPECT_EQ(0u, MessageCenter::Get()->NotificationCount());
}

class FirmwareUpdateStartupNotificationTest : public NoSessionAshTestBase {
 public:
  FirmwareUpdateStartupNotificationTest() = default;

  ~FirmwareUpdateStartupNotificationTest() override = default;

  void SetUp() override {
    network_handler_test_helper_.RegisterPrefs(profile_prefs_.registry(),
                                               local_state_.registry());

    network_handler_test_helper_.InitializePrefs(&profile_prefs_,
                                                 &local_state_);
    FwupdClient::InitializeFake();
    dbus_client_ = FwupdClient::Get();
    firmware_update_manager_ = std::make_unique<FirmwareUpdateManager>();
    EXPECT_TRUE(FirmwareUpdateManager::IsInitialized());
    SetShouldShowNotificationForTest(true);
    NoSessionAshTestBase::SetUp();
  }

  void TearDown() override {
    firmware_update_notification_controller_.reset();
    firmware_update_manager_.reset();
    FwupdClient::Shutdown();
    NetworkHandler::Get()->ShutdownPrefServices();
    NoSessionAshTestBase::TearDown();
  }

 protected:
  void InitializeNotificationController() {
    firmware_update_notification_controller_ =
        std::make_unique<FirmwareUpdateNotificationController>(
            message_center());
  }

  message_center::MessageCenter* message_center() const {
    return message_center::MessageCenter::Get();
  }

  message_center::Notification* FindShortcutsChangedNotification() {
    return message_center::MessageCenter::Get()->FindVisibleNotificationById(
        kFirmwareUpdateNotificationId);
  }

  void SetShouldShowNotificationForTest(bool show_notification) {
    FirmwareUpdateManager::Get()->set_should_show_notification_for_test(
        show_notification);
  }

  void SimulateFetchingUpdates() {
    FirmwareUpdateManager::Get()->RequestAllUpdates(
        FirmwareUpdateManager::Source::kStartup);
  }

  raw_ptr<FwupdClient, DanglingUntriaged> dbus_client_ = nullptr;
  NetworkHandlerTestHelper network_handler_test_helper_;
  TestingPrefServiceSimple profile_prefs_;
  TestingPrefServiceSimple local_state_;
  std::unique_ptr<FirmwareUpdateManager> firmware_update_manager_;
  std::unique_ptr<FirmwareUpdateNotificationController>
      firmware_update_notification_controller_;
};

TEST_F(FirmwareUpdateStartupNotificationTest,
       StartupNotificationShownRegularUser) {
  // Notification should be shown at login.
  SimulateUserLogin("[email protected]");
  InitializeNotificationController();
  SimulateFetchingUpdates();
  EXPECT_TRUE(message_center()->FindVisibleNotificationById(
      kFirmwareUpdateNotificationId));
}

TEST_F(FirmwareUpdateStartupNotificationTest,
       StartupNotificationShownGuestUser) {
  // Notification should not be shown at login if the user is a guest.
  SimulateUserLogin("[email protected]", user_manager::UserType::kGuest);
  InitializeNotificationController();
  SimulateFetchingUpdates();
  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
      kFirmwareUpdateNotificationId));
}

TEST_F(FirmwareUpdateStartupNotificationTest, StartupNotificationShownKiosk) {
  // Notification should not be shown at login if the user is in kiosk mode.
  SimulateUserLogin("[email protected]", user_manager::UserType::kKioskApp);
  InitializeNotificationController();
  SimulateFetchingUpdates();
  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
      kFirmwareUpdateNotificationId));
}

TEST_F(FirmwareUpdateStartupNotificationTest,
       StartupNotificationShownKioskPWA) {
  // Notification should not be shown at login if the user is in kiosk mode.
  SimulateUserLogin("[email protected]", user_manager::UserType::kWebKioskApp);
  InitializeNotificationController();
  SimulateFetchingUpdates();
  EXPECT_FALSE(message_center()->FindVisibleNotificationById(
      kFirmwareUpdateNotificationId));
}
}  // namespace ash