chromium/chrome/browser/ash/policy/remote_commands/curtain_mode_chromeos_browsertest.cc

// Copyright 2023 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/curtain/remote_maintenance_curtain_view.h"
#include "ash/curtain/security_curtain_widget_controller.h"
#include "ash/public/cpp/ash_web_view.h"
#include "ash/public/cpp/style/dark_light_mode_controller.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "base/check_deref.h"
#include "base/scoped_observation.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/run_until.h"
#include "base/test/test_future.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "remoting/host/curtain_mode_chromeos.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/test/view_skia_gold_pixel_diff.h"
#include "ui/views/view_observer.h"
#include "ui/views/widget/widget.h"

namespace remoting {

namespace {

enum class ColorScheme {
  kDark,
  kLight,
};

bool ArePixelTestsEnabled() {
  return base::CommandLine::ForCurrentProcess()->HasSwitch(
      switches::kVerifyPixels);
}

std::string GetScreenshotName(ColorScheme color_scheme) {
  return base::StringPrintf(
      "curtain_%s_rev0", color_scheme == ColorScheme::kDark ? "dark" : "light");
}

// Helper class to wait until WebUI content finishes loading.
class LoadWaiter : public ash::AshWebView::Observer {
 public:
  explicit LoadWaiter(ash::AshWebView& view) : observation_(this) {
    observation_.Observe(&view);
  }
  ~LoadWaiter() override = default;

  // Blocks until the WebUI content has finished loading.
  [[nodiscard]] bool Wait() { return signal_.Wait(); }

 private:
  // `ash::AshWebView::Observer` implementation:
  void DidStopLoading() override { signal_.SetValue(); }

  base::test::TestFuture<void> signal_;
  base::ScopedObservation<ash::AshWebView, ash::AshWebView::Observer>
      observation_;
};

}  // namespace

class CurtainModeChromeOsPixelTest
    : public InProcessBrowserTest,
      public testing::WithParamInterface<ColorScheme> {
 public:
  CurtainModeChromeOsPixelTest() = default;
  CurtainModeChromeOsPixelTest(const CurtainModeChromeOsPixelTest&) = delete;
  CurtainModeChromeOsPixelTest& operator=(const CurtainModeChromeOsPixelTest&) =
      delete;
  ~CurtainModeChromeOsPixelTest() override = default;

  void EnableColorScheme(ColorScheme scheme) {
    bool is_dark = (scheme == ColorScheme::kDark);
    ash::DarkLightModeController::Get()->SetDarkModeEnabledForTest(is_dark);
  }

  views::Widget& WaitForCurtainWidget() {
    auto& root_window_controller =
        CHECK_DEREF(ash::Shell::GetPrimaryRootWindowController());

    EXPECT_TRUE(base::test::RunUntil([&]() {
      return root_window_controller.security_curtain_widget_controller() !=
             nullptr;
    }));

    return root_window_controller.security_curtain_widget_controller()
        ->GetWidget();
  }

  void WaitForWebUiToLoad(views::Widget& curtain_widget) {
    ash::AshWebView* view =
        static_cast<ash::AshWebView*>(curtain_widget.GetRootView()->GetViewByID(
            ash::curtain::kRemoteMaintenanceCurtainAshWebViewId));
    ASSERT_NE(view, nullptr);

    ASSERT_TRUE(LoadWaiter{*view}.Wait());
  }

  [[nodiscard]] bool CompareWithScreenshot(const views::Widget& widget,
                                           const std::string& screenshot) {
    views::ViewSkiaGoldPixelDiff pixel_diff(
        /*screenshot_prefix=*/
        ::testing::UnitTest::GetInstance()->current_test_suite()->name());
    return pixel_diff.CompareViewScreenshot(screenshot, widget.GetRootView());
  }
};

// This test is flaky because we often take the screenshot before the image
// is displayed. So far we haven't managed to find a reliable signal that is
// triggered after the image is displayed - even the JS on-ready signals trigger
// before the image is shown :(
// TODO(b/284233962): Find a reliable signal.
IN_PROC_BROWSER_TEST_P(CurtainModeChromeOsPixelTest,
                       DISABLED_CheckSecurityCurtain) {
  if (!ArePixelTestsEnabled()) {
    return;
  }

  const ColorScheme color_scheme = GetParam();
  EnableColorScheme(color_scheme);

  CurtainModeChromeOs curtain(
      base::SingleThreadTaskRunner::GetCurrentDefault());
  curtain.Activate();

  views::Widget& widget = WaitForCurtainWidget();
  WaitForWebUiToLoad(widget);

  EXPECT_TRUE(CompareWithScreenshot(widget, GetScreenshotName(color_scheme)));
}

INSTANTIATE_TEST_SUITE_P(All,
                         CurtainModeChromeOsPixelTest,
                         testing::Values(ColorScheme::kDark,
                                         ColorScheme::kLight));

}  // namespace remoting