chromium/chrome/browser/ash/crostini/crostini_upgrade_available_notification_unittest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/crostini/crostini_upgrade_available_notification.h"

#include "base/metrics/histogram_base.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_future.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/crostini/crostini_util.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/notifications/system_notification_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/crostini_upgrader/crostini_upgrader_dialog.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/ash/components/dbus/chunneld/chunneld_client.h"
#include "chromeos/ash/components/dbus/cicerone/cicerone_client.h"
#include "chromeos/ash/components/dbus/cicerone/cicerone_service.pb.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "chromeos/ash/components/dbus/seneschal/seneschal_client.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/message_center/public/cpp/notification.h"

namespace crostini {

class CrostiniUpgradeAvailableNotificationTest
    : public BrowserWithTestWindowTest {
 public:
  CrostiniUpgradeAvailableNotificationTest()
      : BrowserWithTestWindowTest(
            Browser::TYPE_NORMAL,
            content::BrowserTaskEnvironment::REAL_IO_THREAD) {}

  CrostiniUpgradeAvailableNotificationTest(
      const CrostiniUpgradeAvailableNotificationTest&) = delete;
  CrostiniUpgradeAvailableNotificationTest& operator=(
      const CrostiniUpgradeAvailableNotificationTest&) = delete;

  ~CrostiniUpgradeAvailableNotificationTest() override {}

  void SetUp() override {
    BrowserWithTestWindowTest::SetUp();
    ash::ChunneldClient::InitializeFake();
    ash::CiceroneClient::InitializeFake();
    ash::ConciergeClient::InitializeFake();
    ash::SeneschalClient::InitializeFake();

    TestingBrowserProcess::GetGlobal()->SetSystemNotificationHelper(
        std::make_unique<SystemNotificationHelper>());
    display_service_ = std::make_unique<NotificationDisplayServiceTester>(
        nullptr /* profile */);
  }

  void TearDown() override {
    RunUntilIdle();
    display_service_.reset();
    BrowserWithTestWindowTest::TearDown();
    ash::SeneschalClient::Shutdown();
    ash::ConciergeClient::Shutdown();
    ash::CiceroneClient::Shutdown();
    ash::ChunneldClient::Shutdown();
  }

  ash::CrostiniUpgraderDialog* GetCrostiniUpgraderDialog() {
    auto url = GURL{chrome::kChromeUICrostiniUpgraderUrl};
    return static_cast<ash::CrostiniUpgraderDialog*>(
        ash::SystemWebDialogDelegate::FindInstance(url.spec()));
  }

  void SafelyCloseDialog() {
    auto* upgrader_dialog = GetCrostiniUpgraderDialog();

    if (!upgrader_dialog) {
      return;
    }

    // Now there should be enough WebUI hooked up to close properly.
    base::test::TestFuture<void> result_future;
    upgrader_dialog->SetDeletionClosureForTesting(result_future.GetCallback());
    upgrader_dialog->Close();
    ASSERT_TRUE(result_future.Wait());
  }

  void ExpectDialog() {
    // A new Widget was created in ShowUi() or since the last VerifyUi().
    EXPECT_TRUE(crostini_manager()->GetCrostiniDialogStatus(
        crostini::DialogType::UPGRADER));

    EXPECT_NE(nullptr, GetCrostiniUpgraderDialog());
  }

  void ExpectNoDialog() {
    // No new Widget was created in ShowUi() or since the last VerifyUi().
    EXPECT_FALSE(crostini_manager()->GetCrostiniDialogStatus(
        crostini::DialogType::UPGRADER));
    // Our dialog has really been deleted.
    EXPECT_EQ(nullptr, GetCrostiniUpgraderDialog());
  }

  void DowngradeOSRelease() {
    vm_tools::cicerone::OsRelease os_release;
    os_release.set_id("debian");
    os_release.set_version_id("9");
    auto container_id = crostini::DefaultContainerId();
    crostini_manager()->SetContainerOsRelease(container_id, os_release);
  }

  crostini::CrostiniManager* crostini_manager() {
    return crostini::CrostiniManager::GetForProfile(profile());
  }

  void RunUntilIdle() { task_environment()->RunUntilIdle(); }

  std::optional<message_center::Notification> GetNotification(std::string id) {
    return display_service_->GetNotification(id);
  }

 private:
  std::unique_ptr<NotificationDisplayServiceTester> display_service_;
};

TEST_F(CrostiniUpgradeAvailableNotificationTest, ShowsWhenNotified) {
  base::HistogramTester histogram_tester;

  DowngradeOSRelease();

  base::test::TestFuture<void> result_future;
  auto notification = CrostiniUpgradeAvailableNotification::Show(
      profile(), result_future.GetCallback());

  ExpectNoDialog();

  // Wait for notification, press Upgrade
  ASSERT_TRUE(notification);
  notification->Get()->delegate()->Click(0, std::nullopt);
  ASSERT_TRUE(result_future.Wait());

  // Dialog should show because we clicked button 0 (Upgrade).
  ExpectDialog();

  RunUntilIdle();
  // There should no longer be a button to click
  EXPECT_TRUE(notification->Get()->buttons().empty());

  SafelyCloseDialog();
  ExpectNoDialog();

  histogram_tester.ExpectUniqueSample(
      "Crostini.UpgradeDialogEvent",
      static_cast<base::HistogramBase::Sample>(
          crostini::UpgradeDialogEvent::kDialogShown),
      1);

  histogram_tester.ExpectUniqueSample(
      "Crostini.UpgradeAvailable",
      static_cast<base::HistogramBase::Sample>(
          crostini::CrostiniUpgradeAvailableNotificationClosed::kUpgradeButton),
      1);
}

}  // namespace crostini