chromium/ash/ambient/ambient_controller_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 "ash/ambient/ambient_controller.h"

#include <memory>
#include <string>
#include <string_view>
#include <utility>

#include "ash/ambient/ambient_constants.h"
#include "ash/ambient/ambient_managed_photo_controller.h"
#include "ash/ambient/ambient_ui_settings.h"
#include "ash/ambient/managed/screensaver_images_policy_handler.h"
#include "ash/ambient/metrics/ambient_metrics.h"
#include "ash/ambient/metrics/managed_screensaver_metrics.h"
#include "ash/ambient/test/ambient_ash_test_base.h"
#include "ash/ambient/test/ambient_ash_test_helper.h"
#include "ash/ambient/test/test_ambient_client.h"
#include "ash/ambient/ui/ambient_container_view.h"
#include "ash/ambient/ui/ambient_view_ids.h"
#include "ash/ambient/ui/photo_view.h"
#include "ash/ambient/util/ambient_util.h"
#include "ash/ambient/util/time_of_day_utils.h"
#include "ash/assistant/assistant_interaction_controller_impl.h"
#include "ash/constants/ambient_time_of_day_constants.h"
#include "ash/constants/ambient_video.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_paths.h"
#include "ash/login/login_screen_controller.h"
#include "ash/login/ui/lock_screen.h"
#include "ash/public/cpp/ambient/ambient_prefs.h"
#include "ash/public/cpp/ambient/ambient_ui_model.h"
#include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h"
#include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
#include "ash/public/cpp/personalization_app/time_of_day_test_utils.h"
#include "ash/public/cpp/test/in_process_data_decoder.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/test/test_ash_web_view.h"
#include "ash/wallpaper/wallpaper_controller_impl.h"
#include "ash/webui/personalization_app/mojom/personalization_app.mojom-shared.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_path_override.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "build/buildflag.h"
#include "chromeos/ash/components/assistant/buildflags.h"
#include "chromeos/ash/components/dbus/dlcservice/dlcservice.pb.h"
#include "chromeos/ash/components/dbus/dlcservice/fake_dlcservice_client.h"
#include "chromeos/ash/services/libassistant/public/cpp/assistant_interaction_metadata.h"
#include "chromeos/dbus/power_manager/suspend.pb.h"
#include "net/base/url_util.h"
#include "ui/base/user_activity/user_activity_detector.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/events/pointer_details.h"
#include "ui/events/test/event_generator.h"
#include "ui/events/test/test_event_handler.h"
#include "ui/events/types/event_type.h"

namespace ash {
namespace {

using ash::personalization_app::mojom::AmbientTheme;
using assistant::AssistantInteractionMetadata;

constexpr char kUser1[] = "[email protected]";
constexpr char kUser2[] = "[email protected]";
constexpr base::FilePath::CharType kTestDlcRootPath[] =
    FILE_PATH_LITERAL("/test/time_of_day");

// Expects argument of type `dlcservice::DlcsWithContent::DlcInfo`.
MATCHER(HasVideoDlcPackageId, "") {
  return arg.id() == kTimeOfDayDlcId;
}

std::vector<base::OnceClosure> GetEventGeneratorCallbacks(
    ui::test::EventGenerator* event_generator) {
  std::vector<base::OnceClosure> event_callbacks;

  event_callbacks.push_back(
      base::BindOnce(&ui::test::EventGenerator::ClickLeftButton,
                     base::Unretained(event_generator)));

  event_callbacks.push_back(
      base::BindOnce(&ui::test::EventGenerator::ClickRightButton,
                     base::Unretained(event_generator)));

  event_callbacks.push_back(
      base::BindOnce(&ui::test::EventGenerator::DragMouseBy,
                     base::Unretained(event_generator), /*dx=*/10,
                     /*dy=*/10));

  event_callbacks.push_back(
      base::BindOnce(&ui::test::EventGenerator::GestureScrollSequence,
                     base::Unretained(event_generator),
                     /*start=*/gfx::Point(10, 10),
                     /*end=*/gfx::Point(20, 10),
                     /*step_delay=*/base::Milliseconds(10),
                     /*steps=*/1));

  event_callbacks.push_back(
      base::BindOnce(&ui::test::EventGenerator::PressTouch,
                     base::Unretained(event_generator), std::nullopt));

  return event_callbacks;
}

class AmbientUiVisibilityBarrier : public AmbientUiModelObserver {
 public:
  explicit AmbientUiVisibilityBarrier(AmbientUiVisibility target_visibility)
      : target_visibility_(target_visibility) {
    observation_.Observe(AmbientUiModel::Get());
  }
  AmbientUiVisibilityBarrier(const AmbientUiVisibilityBarrier&) = delete;
  AmbientUiVisibilityBarrier& operator=(const AmbientUiVisibilityBarrier&) =
      delete;
  ~AmbientUiVisibilityBarrier() override = default;

  void WaitWithTimeout(base::TimeDelta timeout) {
    if (AmbientUiModel::Get()->ui_visibility() == target_visibility_)
      return;

    base::test::ScopedRunLoopTimeout run_loop_timeout(FROM_HERE, timeout);
    base::RunLoop run_loop;
    run_loop_quit_closure_ = run_loop.QuitClosure();
    run_loop.Run();
  }

 private:
  void OnAmbientUiVisibilityChanged(AmbientUiVisibility visibility) override {
    if (visibility == target_visibility_ && run_loop_quit_closure_) {
      // Post task so that any existing tasks get run before WaitWithTimeout()
      // completes.
      base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
          FROM_HERE, std::move(run_loop_quit_closure_));
    }
  }

  const AmbientUiVisibility target_visibility_;
  base::ScopedObservation<AmbientUiModel, AmbientUiModelObserver> observation_{
      this};
  base::RepeatingClosure run_loop_quit_closure_;
};

// UpdateDisplay triggers a rogue MouseEvent that cancels Ambient mode when
// testing with Xvfb. A corresponding MouseEvent is not fired on a real device
// when an external display is added. Ignore this MouseEvent for testing.
// Store the old |ShouldIgnoreNativePlatformEvents| value and reset it at the
// end of the test.
class ScopedIgnoreNativePlatformEvents {
 public:
  ScopedIgnoreNativePlatformEvents()
      : old_should_ignore_events_(
            ui::PlatformEventSource::ShouldIgnoreNativePlatformEvents()) {
    ui::PlatformEventSource::SetIgnoreNativePlatformEvents(true);
  }
  ScopedIgnoreNativePlatformEvents(const ScopedIgnoreNativePlatformEvents&) =
      delete;
  ScopedIgnoreNativePlatformEvents& operator=(
      const ScopedIgnoreNativePlatformEvents&) = delete;
  ~ScopedIgnoreNativePlatformEvents() {
    ui::PlatformEventSource::SetIgnoreNativePlatformEvents(
        old_should_ignore_events_);
  }

 private:
  const bool old_should_ignore_events_;
};

}  // namespace

class AmbientControllerTest : public AmbientAshTestBase {
 public:
  AmbientControllerTest() {
    dlcservice_client_.set_install_root_path(kTestDlcRootPath);
  }
  ~AmbientControllerTest() override = default;

  // AmbientAshTestBase:
  void SetUp() override {
    std::vector<base::test::FeatureRef> features_to_enable =
        personalization_app::GetTimeOfDayEnabledFeatures();
    feature_list_.InitWithFeatures(features_to_enable, {});
    AmbientAshTestBase::SetUp();
    GetSessionControllerClient()->set_show_lock_screen_views(true);
  }

  bool IsPrefObserved(const std::string& pref_name) {
    auto* pref_change_registrar =
        ambient_controller()->pref_change_registrar_.get();
    DCHECK(pref_change_registrar);
    return pref_change_registrar->IsObserved(pref_name);
  }

  bool CurrentThemeUsesPhotos() {
    switch (GetCurrentUiSettings().theme()) {
      case AmbientTheme::kSlideshow:
      case AmbientTheme::kFeelTheBreeze:
      case AmbientTheme::kFloatOnBy:
        return true;
      case AmbientTheme::kVideo:
        return false;
    }
  }

  bool AreSessionSpecificObserversBound() {
    auto* ctrl = ambient_controller();

    bool ui_model_bound = ctrl->ambient_ui_model_observer_.IsObserving();
    // Ideally, we should check whether
    // |ambient_ui_launcher()->backend_observer_.IsObserving()|. Check
    // |ambient_ui_launcher()| instead because
    // |ambient_controller->ambient_ui_launcher_| is not initialized in test.
    bool backend_model_bound = ambient_ui_launcher();
    bool power_manager_bound =
        ctrl->power_manager_client_observer_.IsObserving();
    bool fingerprint_bound = ctrl->fingerprint_observer_receiver_.is_bound();
    // The backend model is only necessary for themes that use photos from it.
    if (CurrentThemeUsesPhotos()) {
      EXPECT_EQ(ui_model_bound, backend_model_bound)
          << "observers should all have the same state";
    }
    EXPECT_EQ(ui_model_bound, power_manager_bound)
        << "observers should all have the same state";
    EXPECT_EQ(ui_model_bound, fingerprint_bound)
        << "observers should all have the same state";
    return ui_model_bound;
  }

  base::test::ScopedFeatureList feature_list_;

 protected:
  base::UserActionTester user_action_tester_;
  FakeDlcserviceClient dlcservice_client_;
};

// Tests for behavior that are agnostic to the AmbientUiSettings selected by
// the user should use this test harness.
//
// Currently there are test cases that actually fall under this category but
// do not use this test fixture. This is done purely for time constraint reasons
// (it takes a lot of compute time to repeat every single one of these test
// cases).
class AmbientControllerTestForAnyUiSettings
    : public AmbientControllerTest,
      public ::testing::WithParamInterface<AmbientUiSettings> {
 protected:
  void SetUp() override {
    AmbientControllerTest::SetUp();
    SetAmbientUiSettings(GetParam());
  }
};

INSTANTIATE_TEST_SUITE_P(
    AllUiSettings,
    AmbientControllerTestForAnyUiSettings,
    // Only one lottie-animated theme and video is
    // sufficient here. The main goal here is to make sure
    // that fundamental behavior holds for all themes.
    testing::Values(AmbientUiSettings(AmbientTheme::kSlideshow),
                    AmbientUiSettings(AmbientTheme::kVideo,
                                      AmbientVideo::kNewMexico)
#if BUILDFLAG(HAS_ASH_AMBIENT_ANIMATION_RESOURCES)
                        ,
                    AmbientUiSettings(AmbientTheme::kFeelTheBreeze)
#endif  // BUILDFLAG(HAS_ASH_AMBIENT_ANIMATION_RESOURCES)
                        ),
    [](const ::testing::TestParamInfo<AmbientUiSettings>& param_info) {
      return std::string(
          ambient::util::AmbientThemeToString(param_info.param.theme()));
    });

TEST_P(AmbientControllerTestForAnyUiSettings, ShowAmbientScreenUponLock) {
  LockScreen();
  // Lockscreen will not immediately show Ambient mode.
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Ambient mode will show after inacivity and successfully loading first
  // image.
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_FALSE(GetContainerViews().empty());
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kShouldShow);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // Clean up.
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_P(AmbientControllerTestForAnyUiSettings,
       NotShowAmbientWhenPrefNotEnabled) {
  SetAmbientModeEnabled(false);

  LockScreen();
  // Lockscreen will not immediately show Ambient mode.
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Ambient mode will not show after inacivity and successfully loading first
  // image.
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_TRUE(GetContainerViews().empty());
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Clean up.
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_P(AmbientControllerTestForAnyUiSettings, HideAmbientScreen) {
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_FALSE(GetContainerViews().empty());
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kShouldShow);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  HideAmbientScreen();

  FastForwardTiny();
  EXPECT_TRUE(GetContainerViews().empty());
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kHidden);

  // Clean up.
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_P(AmbientControllerTestForAnyUiSettings, CloseAmbientScreenUponUnlock) {
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_FALSE(GetContainerViews().empty());
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kShouldShow);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  UnlockScreen();

  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // The view should be destroyed along the widget.
  FastForwardTiny();
  EXPECT_TRUE(GetContainerViews().empty());
}

TEST_P(AmbientControllerTestForAnyUiSettings,
       CloseAmbientScreenUponUnlockSecondaryUser) {
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_FALSE(GetContainerViews().empty());
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kShouldShow);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  SimulateUserLogin(kUser2);
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // The view should be destroyed along the widget.
  FastForwardTiny();
  EXPECT_TRUE(GetContainerViews().empty());

  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // The view should be destroyed along the widget.
  FastForwardTiny();
  EXPECT_TRUE(GetContainerViews().empty());
}

TEST_F(AmbientControllerTest,
       CloseAmbientScreenUponPowerButtonClickInTabletMode) {
  ash::TabletModeControllerTestApi().EnterTabletMode();
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_FALSE(GetContainerViews().empty());
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  SimulatePowerButtonClick();

  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // The view should be destroyed along the widget.
  EXPECT_TRUE(GetContainerViews().empty());
}

TEST_F(AmbientControllerTest, ConsumerShouldNotRecordManagedMetrics) {
  base::HistogramTester histogram_tester;
  SetAmbientModeEnabled(true);

  SetAmbientModeEnabled(false);

  {
    base::test::ScopedFeatureList scoped_feature_list(
        ash::features::kAmbientModeManagedScreensaver);

    SetAmbientModeEnabled(true);

    SetAmbientModeEnabled(false);
  }

  histogram_tester.ExpectTotalCount(
      GetManagedScreensaverHistogram(kManagedScreensaverEnabledUMA),
      /*expected_count=*/0);
}

TEST_F(AmbientControllerTest, NotShowAmbientWhenLockSecondaryUser) {
  // Simulate the login screen.
  ClearLogin();
  SimulateUserLogin(kUser1);
  SetAmbientModeEnabled(true);

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_FALSE(GetContainerViews().empty());
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kShouldShow);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  SimulateUserLogin(kUser2);
  SetAmbientModeEnabled(true);

  // Ambient mode should not show for second user even if that user has the pref
  // turned on.
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // The view should be destroyed along the widget.
  FastForwardTiny();
  EXPECT_TRUE(GetContainerViews().empty());

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // The view should be destroyed along the widget.
  EXPECT_TRUE(GetContainerViews().empty());
}

TEST_P(AmbientControllerTestForAnyUiSettings,
       ShouldRequestAccessTokenWhenLockingScreen) {
  GetAmbientAshTestHelper()->ambient_client().SetAutomaticalyIssueToken(false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Lock the screen will request a token.
  LockScreen();
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Should close ambient widget already when unlocking screen.
  UnlockScreen();
  EXPECT_FALSE(IsAccessTokenRequestPending());
}

TEST_F(AmbientControllerTest, ShouldNotRequestAccessTokenWhenPrefNotEnabled) {
  SetAmbientModeEnabled(false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Lock the screen will not request a token.
  LockScreen();
  EXPECT_FALSE(IsAccessTokenRequestPending());

  UnlockScreen();
  EXPECT_FALSE(IsAccessTokenRequestPending());
}

TEST_P(AmbientControllerTestForAnyUiSettings, ShouldReturnCachedAccessToken) {
  GetAmbientAshTestHelper()->ambient_client().SetAutomaticalyIssueToken(false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Lock the screen will request a token.
  LockScreen();
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Another token request will return cached token.
  base::OnceClosure closure = base::MakeExpectedRunClosure(FROM_HERE);
  base::RunLoop run_loop;
  ambient_controller()->RequestAccessToken(base::BindLambdaForTesting(
      [&](const std::string& gaia_id, const std::string& access_token_fetched) {
        EXPECT_EQ(access_token_fetched, TestAmbientClient::kTestAccessToken);

        std::move(closure).Run();
        run_loop.Quit();
      }));
  EXPECT_FALSE(IsAccessTokenRequestPending());
  run_loop.Run();

  // Clean up.
  CloseAmbientScreen();
}

// The test body intentionally does not have any actual test expectations. The
// test just has to run without crashing on tear down.
// http://crbug.com/1428481
TEST_P(AmbientControllerTestForAnyUiSettings,
       ShutsDownWithoutCrashingWhileAmbientSessionActive) {
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  // Simulates what happens in a real shutdown scenario. The crash bug above
  // cannot be reproduced without this.
  ClearLogin();
}

TEST_F(AmbientControllerTest, ShouldReturnEmptyAccessToken) {
  GetAmbientAshTestHelper()->ambient_client().SetAutomaticalyIssueToken(false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Lock the screen will request a token.
  LockScreen();
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Another token request will return cached token.
  base::OnceClosure closure = base::MakeExpectedRunClosure(FROM_HERE);
  base::RunLoop run_loop_1;
  ambient_controller()->RequestAccessToken(base::BindLambdaForTesting(
      [&](const std::string& gaia_id, const std::string& access_token_fetched) {
        EXPECT_EQ(access_token_fetched, TestAmbientClient::kTestAccessToken);

        std::move(closure).Run();
        run_loop_1.Quit();
      }));
  EXPECT_FALSE(IsAccessTokenRequestPending());
  run_loop_1.Run();

  base::RunLoop run_loop_2;
  // When token expired, another token request will get empty token.
  constexpr base::TimeDelta kTokenRefreshDelay = base::Seconds(60);
  task_environment()->FastForwardBy(kTokenRefreshDelay);

  closure = base::MakeExpectedRunClosure(FROM_HERE);
  ambient_controller()->RequestAccessToken(base::BindLambdaForTesting(
      [&](const std::string& gaia_id, const std::string& access_token_fetched) {
        EXPECT_TRUE(access_token_fetched.empty());

        std::move(closure).Run();
        run_loop_2.Quit();
      }));
  EXPECT_FALSE(IsAccessTokenRequestPending());
  run_loop_2.Run();

  // Clean up.
  CloseAmbientScreen();
}

TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenAfterFailure) {
  GetAmbientAshTestHelper()->ambient_client().SetAutomaticalyIssueToken(false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Lock the screen will request a token.
  LockScreen();
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/true);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Token request automatically retry.
  task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
  EXPECT_TRUE(IsAccessTokenRequestPending());

  // Clean up.
  CloseAmbientScreen();
}

TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenWithBackoffPolicy) {
  GetAmbientAshTestHelper()->ambient_client().SetAutomaticalyIssueToken(false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Lock the screen will request a token.
  LockScreen();
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/true);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  base::TimeDelta delay1 = GetRefreshTokenDelay();
  task_environment()->FastForwardBy(delay1 * 1.1);
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/true);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  base::TimeDelta delay2 = GetRefreshTokenDelay();
  EXPECT_GT(delay2, delay1);

  task_environment()->FastForwardBy(delay2 * 1.1);
  EXPECT_TRUE(IsAccessTokenRequestPending());

  // Clean up.
  CloseAmbientScreen();
}

TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenOnlyThreeTimes) {
  GetAmbientAshTestHelper()->ambient_client().SetAutomaticalyIssueToken(false);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Lock the screen will request a token.
  LockScreen();
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/true);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // 1st retry.
  task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/true);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // 2nd retry.
  task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/true);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // 3rd retry.
  task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
  EXPECT_TRUE(IsAccessTokenRequestPending());
  IssueAccessToken(/*is_empty=*/true);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  // Will not retry.
  task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
  EXPECT_FALSE(IsAccessTokenRequestPending());

  CloseAmbientScreen();
}

TEST_F(AmbientControllerTest,
       CheckAcquireAndReleaseWakeLockWhenBatteryIsCharging) {
  // Simulate a device being connected to a charger initially.
  SetPowerStateCharging();

  // Lock screen to start ambient mode, and flush the loop to ensure
  // the acquire wake lock request has reached the wake lock provider.
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  HideAmbientScreen();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Ambient screen showup again after inactivity.
  FastForwardByLockScreenInactivityTimeout();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Unlock screen to exit ambient mode.
  UnlockScreen();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

TEST_F(AmbientControllerTest,
       CheckAcquireAndReleaseWakeLockWhenBatteryBatteryIsFullAndDischarging) {
  SetPowerStateDischarging();
  SetBatteryPercent(100.f);
  SetExternalPowerConnected();

  // Lock screen to start ambient mode, and flush the loop to ensure
  // the acquire wake lock request has reached the wake lock provider.
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  HideAmbientScreen();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Ambient screen showup again after inactivity.
  FastForwardByLockScreenInactivityTimeout();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Unlock screen to exit ambient mode.
  UnlockScreen();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

TEST_F(AmbientControllerTest,
       CheckAcquireAndReleaseWakeLockWhenBatteryStateChanged) {
  // When the battery is not charging
  // No power connected, should not acquire wake lock
  SetPowerStateDischarging();
  SetExternalPowerDisconnected();
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // External official power connected, should acquire wake lock.
  SetExternalPowerConnected();
  base::RunLoop().RunUntilIdle();
  HideAmbientScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // External USB power connected, should not acquire wake lock because the
  // power is not strong enough.
  SetExternalUsbPowerConnected();
  base::RunLoop().RunUntilIdle();
  HideAmbientScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // When the battery is charging, should acquire wake lock.
  SetPowerStateCharging();
  base::RunLoop().RunUntilIdle();
  HideAmbientScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

TEST_P(AmbientControllerTestForAnyUiSettings, ShouldCloseOnEvent) {
  auto* ambient_ui_model = AmbientUiModel::Get();

  std::vector<base::OnceClosure> event_callbacks =
      GetEventGeneratorCallbacks(GetEventGenerator());

  for (auto& event_callback : event_callbacks) {
    SetAmbientShownAndWaitForWidgets();
    FastForwardTiny();
    EXPECT_TRUE(ambient_controller()->IsShowing());

    std::move(event_callback).Run();

    FastForwardTiny();
    EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
    EXPECT_EQ(AmbientUiVisibility::kClosed, ambient_ui_model->ui_visibility());
    EXPECT_TRUE(GetContainerViews().empty());
  }
}

TEST_P(AmbientControllerTestForAnyUiSettings,
       ShouldDismissToLockScreenOnEvent) {
  auto* ambient_ui_model = AmbientUiModel::Get();

  std::vector<base::OnceClosure> event_callbacks =
      GetEventGeneratorCallbacks(GetEventGenerator());

  for (auto& event_callback : event_callbacks) {
    // Lock screen and fast forward a bit to verify entered hidden state.
    LockScreen();
    FastForwardTiny();
    EXPECT_EQ(AmbientUiVisibility::kHidden, ambient_ui_model->ui_visibility());

    // Wait for timeout to elapse so ambient is shown.
    FastForwardByLockScreenInactivityTimeout();
    EXPECT_EQ(AmbientUiVisibility::kShouldShow,
              ambient_ui_model->ui_visibility());
    EXPECT_TRUE(ambient_controller()->IsShowing());

    // Send an event.
    std::move(event_callback).Run();
    EXPECT_TRUE(GetContainerViews().empty());
    EXPECT_EQ(AmbientUiVisibility::kHidden, ambient_ui_model->ui_visibility());
    // The lock screen timer should have just restarted, so greater than 99% of
    // time remaining on the timer until ambient restarts.
    EXPECT_GT(GetRemainingLockScreenTimeoutFraction().value(), 0.99f);

    // Wait the timeout duration again.
    FastForwardByLockScreenInactivityTimeout();
    FastForwardTiny();
    // Ambient has started again due to elapsed timeout.
    EXPECT_EQ(AmbientUiVisibility::kShouldShow,
              ambient_ui_model->ui_visibility());
    EXPECT_TRUE(ambient_controller()->IsShowing());

    // Reset for next iteration.
    UnlockScreen();
  }
}

// Currently only runs for non-video screen saver settings due to needing to set
// photo download delay.
TEST_F(AmbientControllerTest, ShouldResetLockScreenInactivityTimerOnEvent) {
  auto* ambient_ui_model = AmbientUiModel::Get();
  // Set a long photo download delay so that state is
  // `AmbientUiVisibility::kShouldShow` but widget does not exist to receive
  // events yet.
  SetPhotoDownloadDelay(base::Seconds(1));
  SetAmbientTheme(AmbientTheme::kSlideshow);
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  // Ambient controller is shown but photos have not yet downloaded, so ambient
  // widget and container views do not exist.
  EXPECT_EQ(AmbientUiVisibility::kShouldShow,
            ambient_ui_model->ui_visibility());
  EXPECT_FALSE(ambient_controller()->IsShowing())
      << "Ambient container views should not exist because photos not yet "
         "downloaded";
  // Inactivity timer has elapsed so nullopt.
  EXPECT_FALSE(GetRemainingLockScreenTimeoutFraction().has_value());

  // Send a user activity through `UserActivityDetector`. `EventGenerator` is
  // not hooked up to `UserActivityDetector` in this test environment, so
  // manually trigger `UserActivityDetector` ourselves.
  auto* user_activity_detector = ui::UserActivityDetector::Get();
  ui::KeyEvent event(ui::EventType::kKeyPressed, ui::VKEY_A, ui::EF_NONE);
  user_activity_detector->DidProcessEvent(&event);
  EXPECT_EQ(AmbientUiVisibility::kShouldShow, ambient_ui_model->ui_visibility())
      << "Still shown because waiting for `OnKeyEvent` to be called";

  // Call `OnKeyEvent` via `EventGenerator`.
  GetEventGenerator()->PressAndReleaseKey(ui::VKEY_A);
  EXPECT_EQ(AmbientUiVisibility::kHidden, ambient_ui_model->ui_visibility())
      << "Should be kHidden because of recent OnKeyEvent call";
  EXPECT_GT(GetRemainingLockScreenTimeoutFraction().value(), 0.99)
      << "Lock screen inactivity timer should have restarted";

  FastForwardByLockScreenInactivityTimeout(0.5);
  EXPECT_GT(GetRemainingLockScreenTimeoutFraction().value(), 0.4);

  FastForwardByLockScreenInactivityTimeout(0.51);
  EXPECT_FALSE(GetRemainingLockScreenTimeoutFraction().has_value())
      << "Inactivity timer has stopped";
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_FALSE(ambient_controller()->IsShowing())
      << "Photos still have not yet downloaded";

  task_environment()->FastForwardBy(base::Seconds(2));
  // Finally visible and running now that images downloaded.
  EXPECT_TRUE(ambient_controller()->IsShowing());
}

TEST_P(AmbientControllerTestForAnyUiSettings,
       ShouldDismissContainerViewOnKeyEvent) {
  // Without user interaction, should show ambient mode.
  SetAmbientShownAndWaitForWidgets();
  EXPECT_TRUE(ambient_controller()->IsShowing());
  CloseAmbientScreen();

  // When ambient is shown, OnUserActivity() should ignore key event.
  SetAmbientShownAndWaitForWidgets();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // General key press will exit ambient mode.
  // Simulate key press to close the widget.
  ui::test::TestEventHandler event_handler;
  Shell::GetPrimaryRootWindow()->AddPreTargetHandler(&event_handler);
  PressAndReleaseKey(ui::VKEY_A);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // First key press event should be consumed by ambient mode when closing the
  // UI. Only the key release event gets propagated to the rest of the system.
  EXPECT_EQ(event_handler.num_key_events(), 1);
  Shell::GetPrimaryRootWindow()->RemovePreTargetHandler(&event_handler);
}

TEST_F(AmbientControllerTest, ShouldPropagateKeyPressIfNotRendering) {
  // Force ambient mode to be in a state where it's trying to download photos
  // but has not started rendering yet. In this state, the user should hit the
  // keyboard and see the effect in the existing UI (probably the lock screen).
  // The key stroke should also dismiss ambient mode.
  SetAmbientTheme(AmbientTheme::kSlideshow);
  DisableBackupCacheDownloads();
  backend_controller()->SetFetchScreenUpdateInfoResponseSize(0);

  ambient_controller()->SetUiVisibilityShouldShow();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_FALSE(GetContainerView());

  ui::test::TestEventHandler event_handler;
  Shell::GetPrimaryRootWindow()->AddPreTargetHandler(&event_handler);
  PressAndReleaseKey(ui::VKEY_A);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // Unlike the `ShouldDismissContainerViewOnKeyEvent` test case, both key
  // events (press and release) should be propagated to the `event_handler` in
  // the background.
  EXPECT_EQ(event_handler.num_key_events(), 2);
  Shell::GetPrimaryRootWindow()->RemovePreTargetHandler(&event_handler);
}

TEST_P(AmbientControllerTestForAnyUiSettings, ShowThenImmediatelyClose) {
  // Try to launch ambient mode. It may not finish initialization or start
  // rendering. Then close it immediately. Wait a while, and make sure no
  // pending tasks run that may launch the UI unexpectedly afterwards.
  ambient_controller()->SetUiVisibilityShouldShow();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  CloseAmbientScreen();
  ASSERT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  task_environment()->FastForwardBy(base::Minutes(1));
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_FALSE(GetContainerView());
}

TEST_F(AmbientControllerTest,
       ShouldDismissContainerViewOnKeyEventWhenLockScreenInBackground) {
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
  SetPowerStateCharging();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Should not lock the device and enter ambient mode when the screen is
  // dimmed.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
  EXPECT_FALSE(IsLocked());
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByBackgroundLockScreenTimeout();
  EXPECT_TRUE(IsLocked());
  // Should not disrupt ongoing ambient mode.
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // General key press will exit ambient mode.
  // Simulate key press to close the widget.
  PressAndReleaseKey(ui::VKEY_A);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest,
       ShouldShowAmbientScreenWithLockscreenWhenScreenIsDimmed) {
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
  SetPowerStateCharging();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Should enter ambient mode when the screen is dimmed.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
  EXPECT_FALSE(IsLocked());
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByBackgroundLockScreenTimeout();
  EXPECT_TRUE(IsLocked());
  // Should not disrupt ongoing ambient mode.
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // Closes ambient for clean-up.
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest,
       ShouldShowAmbientScreenWithLockscreenWithNoisyPowerEvents) {
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
  SetPowerStateCharging();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Should enter ambient mode when the screen is dimmed.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
  EXPECT_FALSE(IsLocked());

  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByBackgroundLockScreenTimeout(0.5001);
  SetPowerStateCharging();

  FastForwardByBackgroundLockScreenTimeout(0.5001);
  SetPowerStateCharging();

  EXPECT_TRUE(IsLocked());
  // Should not disrupt ongoing ambient mode.
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // Closes ambient for clean-up.
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest,
       ShouldShowAmbientScreenWithoutLockscreenWhenScreenIsDimmed) {
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
  // When power is discharging, we do not lock the screen with ambient
  // mode since we do not prevent the device go to sleep which will natually
  // lock the device.
  SetPowerStateDischarging();
  SetExternalPowerDisconnected();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Should not lock the device but still enter ambient mode when the screen is
  // dimmed.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
  EXPECT_FALSE(IsLocked());
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByBackgroundLockScreenTimeout();
  EXPECT_FALSE(IsLocked());

  // Closes ambient for clean-up.
  CloseAmbientScreen();
}

TEST_F(AmbientControllerTest, ShouldShowAmbientScreenWhenScreenIsDimmed) {
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(false);
  SetPowerStateCharging();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Should not lock the device but enter ambient mode when the screen is
  // dimmed.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
  EXPECT_FALSE(IsLocked());

  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByBackgroundLockScreenTimeout();
  EXPECT_FALSE(IsLocked());

  // Closes ambient for clean-up.
  CloseAmbientScreen();
}

TEST_F(AmbientControllerTest, HandlesPreviousImageFailuresWithLockScreen) {
  SetAmbientTheme(AmbientTheme::kSlideshow);
  // Simulate failures to download FIFE urls. Ambient mode should close and
  // remember the old failure.
  SetDownloadPhotoData("");
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  AmbientUiVisibilityBarrier ambient_closed_barrier(
      AmbientUiVisibility::kClosed);
  ambient_closed_barrier.WaitWithTimeout(base::Seconds(15));
  ASSERT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  UnlockScreen();

  // Now simulate FIFE downloads starting to work again. The device should be
  // able to enter ambient mode.
  ClearDownloadPhotoData();
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest, HandlesPreviousImageFailuresWithDimmedScreen) {
  SetAmbientTheme(AmbientTheme::kSlideshow);
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(false);
  SetPowerStateCharging();

  // Simulate failures to download FIFE urls. Ambient mode should close and
  // remember the old failure.
  SetDownloadPhotoData("");
  SetScreenIdleStateAndWait(/*is_screen_dimmed=*/true, /*is_off=*/false);
  FastForwardTiny();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  AmbientUiVisibilityBarrier ambient_closed_barrier(
      AmbientUiVisibility::kClosed);
  ambient_closed_barrier.WaitWithTimeout(base::Seconds(15));
  ASSERT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  SetScreenIdleStateAndWait(/*is_screen_dimmed=*/false, /*is_off=*/false);

  // Usually would enter ambient mode when the screen is dimmed, but this time
  // it shouldn't because of the previous image failures.
  SetScreenIdleStateAndWait(/*is_screen_dimmed=*/true, /*is_off=*/false);
  FastForwardTiny();
  ASSERT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  SetScreenIdleStateAndWait(/*is_screen_dimmed=*/false, /*is_off=*/false);

  // Now simulate FIFE downloads starting to work again. The device should be
  // able to enter ambient mode.
  ClearDownloadPhotoData();
  SetScreenIdleStateAndWait(/*is_screen_dimmed=*/true, /*is_off=*/false);
  FastForwardTiny();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // Closes ambient for clean-up.
  CloseAmbientScreen();
}

TEST_F(AmbientControllerTest, ShouldHideAmbientScreenWhenDisplayIsOff) {
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(false);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Should not lock the device and enter ambient mode when the screen is
  // dimmed.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
  EXPECT_FALSE(IsLocked());

  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // Should dismiss ambient mode screen.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/true);
  FastForwardTiny();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Screen back on again, should not have ambient screen.
  SetScreenIdleStateAndWait(/*dimmed=*/false, /*off=*/false);
  FastForwardTiny();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest,
       ShouldHideAmbientScreenWhenDisplayIsOffThenComesBackWithLockScreen) {
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
  SetPowerStateCharging();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Should not lock the device and enter ambient mode when the screen is
  // dimmed.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
  EXPECT_FALSE(IsLocked());

  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByBackgroundLockScreenTimeout();
  EXPECT_TRUE(IsLocked());

  // Should dismiss ambient mode screen.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/true);
  FastForwardTiny();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Screen back on again, should not have ambient screen, but still has lock
  // screen.
  SetScreenIdleStateAndWait(/*dimmed=*/false, /*off=*/false);
  EXPECT_TRUE(IsLocked());
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest,
       ShouldHideAmbientScreenWhenDisplayIsOffAndNotStartWhenLockScreen) {
  GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
  SetPowerStateDischarging();
  SetExternalPowerDisconnected();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Should not lock the device and enter ambient mode when the screen is
  // dimmed.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
  EXPECT_FALSE(IsLocked());

  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // Should not lock the device because the device is not charging.
  FastForwardByBackgroundLockScreenTimeout();
  EXPECT_FALSE(IsLocked());

  // Should dismiss ambient mode screen.
  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/true);
  FastForwardTiny();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Lock screen will not start ambient mode.
  LockScreen();
  EXPECT_TRUE(IsLocked());

  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Screen back on again, should not have ambient screen, but still has lock
  // screen.
  SetScreenIdleStateAndWait(/*dimmed=*/false, /*off=*/false);
  EXPECT_TRUE(IsLocked());
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest, HandlesPhotoDownloadOutage) {
  SetAmbientTheme(AmbientTheme::kSlideshow);
  SetDownloadPhotoData("");

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  AmbientUiVisibilityBarrier ambient_closed_barrier(
      AmbientUiVisibility::kClosed);
  ambient_closed_barrier.WaitWithTimeout(base::Seconds(15));
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_P(AmbientControllerTestForAnyUiSettings, HideCursor) {
  auto* cursor_manager = Shell::Get()->cursor_manager();
  LockScreen();

  cursor_manager->ShowCursor();
  EXPECT_TRUE(cursor_manager->IsCursorVisible());

  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_FALSE(GetContainerViews().empty());
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kShouldShow);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_FALSE(cursor_manager->IsCursorVisible());

  // Clean up.
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_P(AmbientControllerTestForAnyUiSettings, ShowsOnMultipleDisplays) {
  UpdateDisplay("800x600,800x600");
  FastForwardTiny();

  SetAmbientShownAndWaitForWidgets();

  auto* screen = display::Screen::GetScreen();
  EXPECT_EQ(screen->GetNumDisplays(), 2);
  EXPECT_EQ(GetContainerViews().size(), 2u);
  AmbientViewID expected_child_view_id;
  switch (GetParam().theme()) {
    case AmbientTheme::kVideo:
      expected_child_view_id = kAmbientVideoWebView;
      break;
    case AmbientTheme::kSlideshow:
      expected_child_view_id = AmbientViewID::kAmbientPhotoView;
      break;
    case AmbientTheme::kFeelTheBreeze:
    case AmbientTheme::kFloatOnBy:
      expected_child_view_id = AmbientViewID::kAmbientAnimationView;
      break;
  }
  EXPECT_TRUE(GetContainerViews().front()->GetViewByID(expected_child_view_id));
  EXPECT_TRUE(GetContainerViews().back()->GetViewByID(expected_child_view_id));
  // Check that each root controller has an ambient widget.
  for (auto* ctrl : RootWindowController::root_window_controllers())
    EXPECT_TRUE(ctrl->ambient_widget_for_testing() &&
                ctrl->ambient_widget_for_testing()->IsVisible());
}

TEST_P(AmbientControllerTestForAnyUiSettings, RespondsToDisplayAdded) {
  ScopedIgnoreNativePlatformEvents ignore_native_platform_events;

  UpdateDisplay("800x600");
  SetAmbientShownAndWaitForWidgets();

  auto* screen = display::Screen::GetScreen();
  EXPECT_EQ(screen->GetNumDisplays(), 1);
  EXPECT_EQ(GetContainerViews().size(), 1u);

  UpdateDisplay("800x600,800x600");
  FastForwardTiny();

  EXPECT_TRUE(ambient_controller()->IsShowing());
  EXPECT_EQ(screen->GetNumDisplays(), 2);
  EXPECT_EQ(GetContainerViews().size(), 2u);
  for (auto* ctrl : RootWindowController::root_window_controllers())
    EXPECT_TRUE(ctrl->ambient_widget_for_testing() &&
                ctrl->ambient_widget_for_testing()->IsVisible());
}

TEST_F(AmbientControllerTest, RespondsToDisplayAddedWhileInitializing) {
  static constexpr base::TimeDelta kPhotoDownloadDelay = base::Seconds(2);

  ScopedIgnoreNativePlatformEvents ignore_native_platform_events;

  SetAmbientTheme(AmbientTheme::kSlideshow);
  SetPhotoDownloadDelay(kPhotoDownloadDelay);

  UpdateDisplay("800x600");
  ambient_controller()->SetUiVisibilityShouldShow();

  // First photo is downloaded, but the minimum required to start
  // `kSlideshow` is two, so `AmbientPhotoController` should attempt to
  // download another before starting the ui.
  task_environment()->FastForwardBy(kPhotoDownloadDelay);
  ASSERT_TRUE(GetContainerViews().empty());

  // Now user plugs in second display.
  UpdateDisplay("800x600,800x600");

  task_environment()->FastForwardBy(kPhotoDownloadDelay);
  FastForwardTiny();

  EXPECT_TRUE(ambient_controller()->IsShowing());
  EXPECT_EQ(display::Screen::GetScreen()->GetNumDisplays(), 2);
  EXPECT_EQ(GetContainerViews().size(), 2u);
  for (auto* ctrl : RootWindowController::root_window_controllers()) {
    EXPECT_TRUE(ctrl->ambient_widget_for_testing() &&
                ctrl->ambient_widget_for_testing()->IsVisible());
  }
}

TEST_P(AmbientControllerTestForAnyUiSettings, HandlesDisplayRemoved) {
  UpdateDisplay("800x600,800x600");
  FastForwardTiny();

  SetAmbientShownAndWaitForWidgets();

  auto* screen = display::Screen::GetScreen();
  EXPECT_EQ(screen->GetNumDisplays(), 2);
  EXPECT_EQ(GetContainerViews().size(), 2u);
  EXPECT_TRUE(ambient_controller()->IsShowing());

  // Changing to one screen will destroy the widget on the non-primary screen.
  UpdateDisplay("800x600");
  FastForwardTiny();

  EXPECT_EQ(screen->GetNumDisplays(), 1);
  EXPECT_EQ(GetContainerViews().size(), 1u);
  EXPECT_TRUE(ambient_controller()->IsShowing());
}

TEST_F(AmbientControllerTest, ClosesAmbientBeforeSuspend) {
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  SimulateSystemSuspendAndWait(power_manager::SuspendImminent::Reason::
                                   SuspendImminent_Reason_LID_CLOSED);

  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  FastForwardByLockScreenInactivityTimeout();
  // Ambient mode should not resume until SuspendDone is received.
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest, RestartsAmbientAfterSuspend) {
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  SimulateSystemSuspendAndWait(
      power_manager::SuspendImminent::Reason::SuspendImminent_Reason_IDLE);

  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // This call should be blocked by prior |SuspendImminent| until |SuspendDone|.
  ambient_controller()->SetUiVisibilityShouldShow();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  SimulateSystemResumeAndWait();

  FastForwardByLockScreenInactivityTimeout();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest, ObservesPrefsWhenAmbientEnabled) {
  SetAmbientModeEnabled(false);

  // This pref is always observed.
  EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));

  std::vector<std::string> other_prefs{
      ambient::prefs::kAmbientModeLockScreenInactivityTimeoutSeconds,
      ambient::prefs::kAmbientModeLockScreenBackgroundTimeoutSeconds,
      ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds};

  for (auto& pref_name : other_prefs)
    EXPECT_FALSE(IsPrefObserved(pref_name));

  SetAmbientModeEnabled(true);

  EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));

  for (auto& pref_name : other_prefs)
    EXPECT_TRUE(IsPrefObserved(pref_name));
}

TEST_F(AmbientControllerTest, BindsObserversWhenAmbientEnabled) {
  auto* ctrl = ambient_controller();

  SetAmbientModeEnabled(false);

  // SessionObserver must always be observing to detect when user pref service
  // is started.
  EXPECT_TRUE(ctrl->session_observer_.IsObserving());

  EXPECT_FALSE(AreSessionSpecificObserversBound());

  SetAmbientModeEnabled(true);

  // Session observer should still be observing.
  EXPECT_TRUE(ctrl->session_observer_.IsObserving());

  EXPECT_TRUE(AreSessionSpecificObserversBound());
}

TEST_F(AmbientControllerTest, SwitchActiveUsersDoesNotDoubleBindObservers) {
  ClearLogin();
  SimulateUserLogin(kUser1);
  SetAmbientModeEnabled(true);

  TestSessionControllerClient* session = GetSessionControllerClient();

  // Observers are bound for primary user with Ambient mode enabled.
  EXPECT_TRUE(AreSessionSpecificObserversBound());
  EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));

  // Observers are still bound when secondary user logs in.
  SimulateUserLogin(kUser2);
  EXPECT_TRUE(AreSessionSpecificObserversBound());
  EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));

  // Observers are not re-bound for primary user when session is active.
  session->SwitchActiveUser(AccountId::FromUserEmail(kUser1));
  EXPECT_TRUE(AreSessionSpecificObserversBound());
  EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));

  //  Switch back to secondary user.
  session->SwitchActiveUser(AccountId::FromUserEmail(kUser2));
}

TEST_F(AmbientControllerTest, BindsObserversWhenAmbientOn) {
  auto* ctrl = ambient_controller();

  LockScreen();

  // Start monitoring user activity on hidden ui.
  EXPECT_TRUE(ctrl->user_activity_observer_.IsObserving());
  // Do not monitor power status yet.
  EXPECT_FALSE(ctrl->power_status_observer_.IsObserving());

  FastForwardByLockScreenInactivityTimeout();

  EXPECT_TRUE(ctrl->user_activity_observer_.IsObserving());
  EXPECT_TRUE(ctrl->power_status_observer_.IsObserving());

  UnlockScreen();

  EXPECT_FALSE(ctrl->user_activity_observer_.IsObserving());
  EXPECT_FALSE(ctrl->power_status_observer_.IsObserving());
}

TEST_P(AmbientControllerTestForAnyUiSettings,
       ShowDismissAmbientScreenUponAssistantQuery) {
  // Without user interaction, should show ambient mode.
  SetAmbientShownAndWaitForWidgets();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  // Trigger Assistant interaction.
  static_cast<AssistantInteractionControllerImpl*>(
      AssistantInteractionController::Get())
      ->OnInteractionStarted(AssistantInteractionMetadata());
  base::RunLoop().RunUntilIdle();

  // Ambient screen should dismiss.
  EXPECT_TRUE(GetContainerViews().empty());
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

// For all test cases that depend on ash ambient resources (lottie files, image
// assets, etc) being present to run.
#if BUILDFLAG(HAS_ASH_AMBIENT_ANIMATION_RESOURCES)
#define ANIMATION_TEST_WITH_RESOURCES(test_case_name) test_case_name
#else
#define ANIMATION_TEST_WITH_RESOURCES(test_case_name) DISABLED_##test_case_name
#endif  // BUILDFLAG(HAS_ASH_AMBIENT_ANIMATION_RESOURCES)

TEST_F(AmbientControllerTest,
       ANIMATION_TEST_WITH_RESOURCES(RendersCorrectView)) {
  SetAmbientTheme(AmbientTheme::kFeelTheBreeze);

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  ASSERT_TRUE(GetContainerView());
  EXPECT_FALSE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientAnimationView));

  UnlockScreen();
  SetAmbientTheme(AmbientTheme::kSlideshow);

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
  EXPECT_FALSE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientAnimationView));

  UnlockScreen();
  SetAmbientTheme(AmbientTheme::kFeelTheBreeze);

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  ASSERT_TRUE(GetContainerView());
  EXPECT_FALSE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientAnimationView));
}

TEST_F(AmbientControllerTest,
       ANIMATION_TEST_WITH_RESOURCES(ClearsCacheWhenSwitchingThemes)) {
  SetAmbientTheme(AmbientTheme::kSlideshow);

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  ASSERT_TRUE(GetContainerView());
  ASSERT_FALSE(GetCachedFiles().empty());

  UnlockScreen();
  SetAmbientTheme(AmbientTheme::kFeelTheBreeze);

  // Mimic a network outage where no photos can be downloaded. Since the cache
  // should have been cleared when we switched ambient animation themes, the
  // UI shouldn't start with a photo cached during slideshow mode.
  SetDownloadPhotoData(/*data=*/"");
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  EXPECT_FALSE(GetContainerView());
  EXPECT_TRUE(GetCachedFiles().empty());
}

TEST_F(AmbientControllerTest,
       ANIMATION_TEST_WITH_RESOURCES(MetricsStartupTimeSuspendAfterTimeMax)) {
  SetAmbientTheme(AmbientTheme::kSlideshow);
  base::HistogramTester histogram_tester;
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  task_environment()->FastForwardBy(ambient::kMetricsStartupTimeMax);
  FastForwardTiny();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  SimulateSystemSuspendAndWait(power_manager::SuspendImminent::Reason::
                                   SuspendImminent_Reason_LID_CLOSED);

  ASSERT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  histogram_tester.ExpectTotalCount("Ash.AmbientMode.StartupTime.SlideShow", 1);
  UnlockScreen();
}

TEST_F(AmbientControllerTest,
       ANIMATION_TEST_WITH_RESOURCES(MetricsStartupTimeScreenOffAfterTimeMax)) {
  SetAmbientTheme(AmbientTheme::kSlideshow);
  base::HistogramTester histogram_tester;
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();

  task_environment()->FastForwardBy(ambient::kMetricsStartupTimeMax);
  FastForwardTiny();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/true);

  ASSERT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  histogram_tester.ExpectTotalCount("Ash.AmbientMode.StartupTime.SlideShow", 1);
  UnlockScreen();
}

TEST_F(AmbientControllerTest, ShouldStartScreenSaverPreview) {
  ASSERT_EQ(0,
            user_action_tester_.GetActionCount(kScreenSaverPreviewUserAction));
  ambient_controller()->SetUiVisibilityPreview();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_FALSE(IsLocked());
  EXPECT_EQ(1,
            user_action_tester_.GetActionCount(kScreenSaverPreviewUserAction));
}

TEST_F(AmbientControllerTest,
       ShouldNotDismissScreenSaverPreviewOnUserActivity) {
  ambient_controller()->SetUiVisibilityPreview();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  ui::MouseEvent mouse_event(ui::EventType::kMouseReleased, gfx::Point(),
                             gfx::Point(), base::TimeTicks(), ui::EF_NONE,
                             ui::EF_NONE);
  ui::UserActivityDetector::Get()->DidProcessEvent(&mouse_event);
  FastForwardTiny();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest, ShouldDismissScreenSaverPreviewOnKeyReleased) {
  ambient_controller()->SetUiVisibilityPreview();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  GetEventGenerator()->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  GetEventGenerator()->PressKey(ui::VKEY_A, ui::EF_NONE);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest,
       ShouldNotDismissScreenSaverPreviewOnSomeMouseEvents) {
  ambient_controller()->SetUiVisibilityPreview();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  GetEventGenerator()->MoveMouseWheel(10, 10);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  GetEventGenerator()->SendMouseEnter();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  GetEventGenerator()->SendMouseExit();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest, ShouldDismissScreenSaverPreviewOnMouseClick) {
  ambient_controller()->SetUiVisibilityPreview();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  GetEventGenerator()->ClickLeftButton();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  ambient_controller()->SetUiVisibilityPreview();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  GetEventGenerator()->ClickRightButton();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest, MaybeDismissUIOnMouseMove) {
  ambient_controller()->SetUiVisibilityPreview();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  GetEventGenerator()->MoveMouseTo(gfx::Point(5, 5), /*count=*/2);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());

  task_environment()->FastForwardBy(kDismissPreviewOnMouseMoveDelay);
  FastForwardTiny();
  GetEventGenerator()->MoveMouseTo(gfx::Point(5, 5), /*count=*/2);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerTest, ShouldDismissScreenSaverPreviewOnTouch) {
  SetAmbientTheme(AmbientTheme::kSlideshow);

  // Case 1: Launch slide show, but it hasn't started rendering yet because it's
  // downloading photos. User hits touchpad, and that should close the ambient
  // session even though it never started rendering.
  ambient_controller()->SetUiVisibilityPreview();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_FALSE(GetContainerView());

  GetEventGenerator()->PressTouch();
  GetEventGenerator()->ReleaseTouch();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Case 2: Launch slide show and wait for it to starts rendering. User hits
  // touchpad, and that should close the ambient session.
  SetAmbientPreviewAndWaitForWidgets();
  ASSERT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());

  GetEventGenerator()->PressTouch();
  GetEventGenerator()->ReleaseTouch();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

class AmbientControllerForManagedScreensaverTest : public AmbientAshTestBase {
 public:
  AmbientControllerForManagedScreensaverTest() {
    CreateTestData();
    // Required as otherwise the PathService::CheckedGet fails in the
    // screensaver images policy handler.
    device_policy_screensaver_folder_override_ =
        std::make_unique<base::ScopedPathOverride>(
            ash::DIR_DEVICE_POLICY_SCREENSAVER_DATA, temp_dir_.GetPath());
  }
  void SetUp() override {
    scoped_feature_list_.InitAndEnableFeature(
        ash::features::kAmbientModeManagedScreensaver);
    AmbientAshTestBase::SetUp();
    // Disable consumer ambient mode
    SetAmbientModeEnabled(false);
    GetSessionControllerClient()->set_show_lock_screen_views(true);
  }

  void TearDown() override {
    image_file_paths_.clear();
    AmbientAshTestBase::TearDown();
  }

 protected:
  void CreateTestData() {
    bool success = temp_dir_.CreateUniqueTempDir();
    ASSERT_TRUE(success);
    base::FilePath image_1 =
        temp_dir_.GetPath().Append(FILE_PATH_LITERAL("IMAGE_1.jpg"));
    CreateTestImageJpegFile(image_1, 4, 4, SK_ColorRED);
    base::FilePath image_2 =
        temp_dir_.GetPath().Append(FILE_PATH_LITERAL("IMAGE_2.jpg"));
    CreateTestImageJpegFile(image_2, 8, 8, SK_ColorGREEN);

    image_file_paths_.push_back(image_1);
    image_file_paths_.push_back(image_2);
  }

  void SimulateScreensaverStart() {
    LockScreen();
    FastForwardByLockScreenInactivityTimeout();
    EXPECT_EQ(std::nullopt, GetRemainingLockScreenTimeoutFraction());
    EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  InProcessDataDecoder decoder_;
  std::vector<base::FilePath> image_file_paths_;
  base::ScopedTempDir temp_dir_;
  std::unique_ptr<base::ScopedPathOverride>
      device_policy_screensaver_folder_override_;
};

TEST_F(AmbientControllerForManagedScreensaverTest,
       VerifyEnabledPolicyHistogram) {
  base::HistogramTester histogram_tester;
  SetAmbientModeManagedScreensaverEnabled(true);

  SetAmbientModeManagedScreensaverEnabled(false);

  SetAmbientModeManagedScreensaverEnabled(true);

  EXPECT_THAT(histogram_tester.GetAllSamples(GetManagedScreensaverHistogram(
                  kManagedScreensaverEnabledUMA)),
              BucketsAre(base::Bucket(false, 1), base::Bucket(true, 2)));
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       ScreensaverIsShownWithEnoughImages) {
  SetAmbientModeManagedScreensaverEnabled(true);

  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  SimulateScreensaverStart();

  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));

  // Peripheral Ui is always hidden in managed screeensaver mode
  EXPECT_FALSE(GetAmbientSlideshowPeripheralUi()->GetVisible())
      << "Peripheral Ui should be hidden in managed mode";

  GetEventGenerator()->ClickLeftButton();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  ASSERT_FALSE(GetContainerView());
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       ScreensaverIsNotShownWithoutImages) {
  SetAmbientModeManagedScreensaverEnabled(true);
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_FALSE(GetContainerView());
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       UiLauncherIsNullWhenManagedAmbientModeIsDisabled) {
  SetAmbientModeEnabled(false);
  SetAmbientModeManagedScreensaverEnabled(false);

  ASSERT_FALSE(ambient_controller()->ambient_ui_launcher());

  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       DisablingManagedAmbientModeFallsbackToUserAmbientModeIfEnabled) {
  SetAmbientModeEnabled(true);
  SetAmbientModeManagedScreensaverEnabled(true);
  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  SimulateScreensaverStart();
  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
  SetAmbientModeManagedScreensaverEnabled(false);
  SetAmbientTheme(AmbientTheme::kSlideshow);
  UnlockScreen();

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
  EXPECT_TRUE(GetAmbientSlideshowPeripheralUi()->GetVisible());
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       LaunchingManagedAmbientModeAfterAmbientModeWorksAsExpected) {
  SetAmbientModeEnabled(/*enabled=*/true);
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);

  managed_policy_handler()->SetImagesForTesting(image_file_paths_);

  SimulateScreensaverStart();
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       LaunchingAmbientModeAfterManagedAmbientModeWorksAsExpected) {
  SetAmbientModeEnabled(/*enabled=*/false);
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);
  SetAmbientModeEnabled(/*enabled=*/true);

  managed_policy_handler()->SetImagesForTesting(image_file_paths_);

  SimulateScreensaverStart();
  UnlockScreen();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverTest, PrefObserverUpdatesUiModel) {
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);
  ASSERT_TRUE(ambient_controller()->ambient_ui_launcher());
  PrefService* pref_service =
      Shell::Get()->session_controller()->GetActivePrefService();
  AmbientUiModel* ui_model = ambient_controller()->ambient_ui_model();
  constexpr size_t kExpectedIdleTimeout = 55;
  constexpr size_t kExpectedPhotoRefreshInterval = 77;
  pref_service->SetInteger(
      ambient::prefs::kAmbientModeManagedScreensaverIdleTimeoutSeconds,
      kExpectedIdleTimeout);
  EXPECT_EQ(base::Seconds(kExpectedIdleTimeout),
            ui_model->lock_screen_inactivity_timeout());
  pref_service->SetInteger(
      ambient::prefs::kAmbientModeManagedScreensaverImageDisplayIntervalSeconds,
      kExpectedPhotoRefreshInterval);
  EXPECT_EQ(base::Seconds(kExpectedPhotoRefreshInterval),
            ui_model->photo_refresh_interval());
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       WorksWithAmbientManagedPhotoSource) {
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);

  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  SimulateScreensaverStart();

  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
  UnlockScreen();

  ASSERT_FALSE(GetContainerView());
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  SimulateScreensaverStart();
  // Will start as there are images present already
  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       ManagedAmbientModeGetsEnabledOnLockScreenAndStartsIt) {
  LockScreen();
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);
  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  FastForwardByLockScreenInactivityTimeout();
  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
}

class AmbientControllerForManagedScreensaverLoginScreenTest
    : public AmbientControllerForManagedScreensaverTest {
 public:
  void SetUp() override {
    // For login screen tests we don't want to start a session rather we want to
    // start on the login screen.
    set_start_session(false);
    AmbientControllerForManagedScreensaverTest::SetUp();
    SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);
    managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  }

  void TriggerScreensaverOnLoginScreen() {
    GetSessionControllerClient()->RequestSignOut();
    // The login screen can't be shown without a wallpaper.
    Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
    Shell::Get()->login_screen_controller()->ShowLoginScreen();
    GetSessionControllerClient()->FlushForTest();
    FastForwardByLockScreenInactivityTimeout();
  }
};

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       UMAEngagementTime) {
  base::HistogramTester histogram_tester;

  constexpr base::TimeDelta kExpectedTimeBucket1 = base::Seconds(5);
  constexpr base::TimeDelta kExpectedTimeBucket2 = base::Seconds(10);

  TriggerScreensaverOnLoginScreen();
  ASSERT_TRUE(GetContainerView());
  task_environment()->FastForwardBy(kExpectedTimeBucket1);
  // Dismiss Screensaver
  GetEventGenerator()->ClickLeftButton();
  ASSERT_FALSE(GetContainerView());
  FastForwardByLockScreenInactivityTimeout();
  ASSERT_TRUE(GetContainerView());
  task_environment()->FastForwardBy(kExpectedTimeBucket2);
  // Dismiss Screensaver
  GetEventGenerator()->ClickLeftButton();
  auto histogram_name = GetManagedScreensaverHistogram(
      kManagedScreensaverEngagementTimeSlideshowUMA);
  histogram_tester.ExpectTimeBucketCount(histogram_name, kExpectedTimeBucket1,
                                         1);
  histogram_tester.ExpectTimeBucketCount(histogram_name, kExpectedTimeBucket2,
                                         1);
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest, UMAStartupTime) {
  base::HistogramTester histogram_tester;

  constexpr base::TimeDelta kExpectedTimeBucket1 = base::Seconds(0);

  TriggerScreensaverOnLoginScreen();
  ASSERT_TRUE(GetContainerView());
  GetEventGenerator()->ClickLeftButton();
  ASSERT_FALSE(GetContainerView());
  FastForwardByLockScreenInactivityTimeout();
  ASSERT_TRUE(GetContainerView());

  auto histogram_name = GetManagedScreensaverHistogram(
      kManagedScreensaverStartupTimeSlideshowUMA);
  histogram_tester.ExpectTimeBucketCount(histogram_name, kExpectedTimeBucket1,
                                         2);
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       ShownOnLoginScreen) {
  TriggerScreensaverOnLoginScreen();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));
  GetEventGenerator()->ClickLeftButton();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       ShownOnLoginWhenPrefUpdatedLater) {
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/false);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  // Login screen is shown when the managed mode is disabled
  TriggerScreensaverOnLoginScreen();
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);
  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       NotShownOnLoginScreenWhenDisabled) {
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/false);
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       UserLogsInAmbientModeDisabledAndManagedAmbientModeEnabled) {
  TriggerScreensaverOnLoginScreen();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());

  // Simulate user session start (e.g. user login)
  CreateUserSessions(/*session_count=*/1);

  // Confirm that ambient mode is not shown if disabled. (disabled by default)
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_FALSE(GetContainerView());
  ASSERT_FALSE(ambient_controller()->ambient_ui_launcher());

  // Enabling and locking screen starts the managed ambient mode
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);
  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       UserLogsInAmbientModeEnabled) {
  TriggerScreensaverOnLoginScreen();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());

  // Simulate user session start (e.g. consumer user login)
  SimulateNewUserFirstLogin(kUser1);

  // Enabling and locking screen starts the consumer ambient mode
  SetAmbientModeEnabled(true);
  DisableBackupCacheDownloads();
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       ManagedScreensaverClosedWhenImagesCleared) {
  TriggerScreensaverOnLoginScreen();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());
  // Clear images
  managed_policy_handler()->SetImagesForTesting({});
  EXPECT_FALSE(ambient_controller()->IsShowing());
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Simulate login
  CreateUserSessions(/*session_count=*/1);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  SetAmbientModeManagedScreensaverEnabled(true);
  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());

  managed_policy_handler()->SetImagesForTesting({});
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       ManagedScreensaverClosedWhenImageLoadingFails) {
  TriggerScreensaverOnLoginScreen();
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());
  // Set invalid images ( i.e. either the paths are invalid or images themselves
  // have been deleted).
  std::vector<base::FilePath> invalid_image_paths = {
      base::FilePath(FILE_PATH_LITERAL("invalid_path_1")),
      base::FilePath(FILE_PATH_LITERAL("invalid_path_2"))};
  managed_policy_handler()->SetImagesForTesting(invalid_image_paths);
  // Fast forward a tiny amount to run any async tasks.
  FastForwardTiny();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  // Simulate login
  CreateUserSessions(/*session_count=*/1);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());

  SetAmbientModeManagedScreensaverEnabled(true);
  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  SimulateScreensaverStart();
  EXPECT_TRUE(ambient_controller()->IsShowing());
  managed_policy_handler()->SetImagesForTesting(invalid_image_paths);
  // Fast forward a tiny amount to run any async tasks.
  FastForwardTiny();
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       ManagedScreensaverNotShownInKioskSessions) {
  // Confirm that the screensaver is still triggered on the login screen
  TriggerScreensaverOnLoginScreen();
  // New tests are flaky most of the time in the flakiness cluster on CQ due to
  // mocked time, fast forward by 20% time to make sure that they work as
  // expected.
  // TODO(b/305199163) Remove after investigating the root cause and coming
  // up with a general solution.
  FastForwardByLockScreenInactivityTimeout(/*factor=*/0.2f);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  ASSERT_TRUE(GetContainerView());

  SimulateKioskMode(user_manager::UserType::kWebKioskApp);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  SetAmbientModeManagedScreensaverEnabled(true);
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  // There is no lock screen in kiosk sessions so we just try to forward the
  // time and try setting screen state to idle.
  FastForwardByLockScreenInactivityTimeout();
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
  SetScreenIdleStateAndWait(/*is_screen_dimmed=*/true, /*is_off=*/false);
  EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
            AmbientUiVisibility::kClosed);
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       ManagedScreensaverDoesNotShowCursorWhenDisabledOrNotStarted) {
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/false);
  TriggerScreensaverOnLoginScreen();
  ASSERT_FALSE(GetContainerView());

  // Hide the cursor.
  Shell::Get()->cursor_manager()->HideCursor();

  // Disabling an already disabled screensaver shouldn't show the cursor.
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/false);
  EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorVisible());

  // Just enabling the screensaver and updating the images one by one should not
  // change the cursor visibility.
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);
  managed_policy_handler()->SetImagesForTesting({image_file_paths_[0]});
  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorVisible());

  // Waiting for some time without activity should not change the cursor
  // visibility.
  FastForwardByLockScreenInactivityTimeout(/*factor=*/0.5f);
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorVisible());
}

TEST_F(AmbientControllerForManagedScreensaverLoginScreenTest,
       ManagedScreensaverInsufficientImagesErrorClearedOnGettingNewData) {
  TriggerScreensaverOnLoginScreen();
  // TODO(b/305199163) Remove after investigating the flakiness root cause and
  // coming up with a general solution.
  FastForwardByLockScreenInactivityTimeout(/*factor=*/0.2f);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_FALSE(managed_photo_controller()->HasScreenUpdateErrors());

  // Only set one image to trigger insufficient images error.
  managed_policy_handler()->SetImagesForTesting({image_file_paths_[0]});
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_TRUE(managed_photo_controller()->HasScreenUpdateErrors());

  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  FastForwardByLockScreenInactivityTimeout(/*factor=*/1.2f);

  // Confirm that the screensaver is shown and errors are cleared.
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_FALSE(managed_photo_controller()->HasScreenUpdateErrors());
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       ManagedScreensaverNotShownOnScreenDim) {
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);
  managed_policy_handler()->SetImagesForTesting(image_file_paths_);
  SetScreenIdleStateAndWait(/*is_screen_dimmed=*/true, /*is_off=*/false);
  EXPECT_FALSE(IsLocked());
  EXPECT_FALSE(ambient_controller()->ShouldShowAmbientUi());
}

TEST_F(AmbientControllerForManagedScreensaverTest,
       ManagedScreensaverAlwaysShowsFullImages) {
  const gfx::Rect screen_bounds_landscape(/*width=*/320, /*height=*/180);
  UpdateDisplay("320x180");
  SetAmbientModeManagedScreensaverEnabled(/*enabled=*/true);

  const base::FilePath image_large_1 =
      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("IMAGE_L.jpg"));
  CreateTestImageJpegFile(image_large_1, 400, 180, SK_ColorRED);
  const base::FilePath image_large_2 =
      temp_dir_.GetPath().Append(FILE_PATH_LITERAL("IMAGE_L_2.jpg"));

  CreateTestImageJpegFile(image_large_2, 400, 180, SK_ColorGREEN);

  const std::vector<base::FilePath> images{image_large_1, image_large_2};
  managed_policy_handler()->SetImagesForTesting(images);
  SimulateScreensaverStart();
  ASSERT_TRUE(GetContainerView());

  const gfx::Rect image_bounds_landscape =
      GetAmbientBackgroundImageView()->GetImageBoundsInScreenForTesting();
  EXPECT_TRUE(screen_bounds_landscape.Contains(image_bounds_landscape));

  // Top and bottom black bars of 18 pixels due to height scaling.
  EXPECT_EQ(image_bounds_landscape,
            gfx::Rect(/*x=*/0, /*y=*/18, /*width=*/320, /*height=*/144));

  // Rotate screen
  const gfx::Rect screen_bounds_portrait(/*width=*/180, /*height=*/320);
  UpdateDisplay("180x320");
  FastForwardByLockScreenInactivityTimeout();
  ASSERT_TRUE(GetContainerView());

  const gfx::Rect image_bounds_portrait =
      GetAmbientBackgroundImageView()->GetImageBoundsInScreenForTesting();
  EXPECT_TRUE(screen_bounds_portrait.Contains(image_bounds_portrait));

  // Top and bottom black bars of 119 pixels due to height scaling.
  EXPECT_EQ(image_bounds_portrait,
            gfx::Rect(/*x=*/0, /*y=*/119, /*width=*/180, /*height=*/81));
}

TEST_F(AmbientControllerTest, RendersCorrectViewForVideo) {
  SetAmbientUiSettings(
      AmbientUiSettings(AmbientTheme::kVideo, AmbientVideo::kNewMexico));

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  ASSERT_TRUE(GetContainerView());
  const TestAshWebView* web_view = static_cast<const TestAshWebView*>(
      GetContainerView()->GetViewByID(kAmbientVideoWebView));
  ASSERT_TRUE(web_view);
  EXPECT_TRUE(web_view->current_url().SchemeIsFile());
  const base::FilePath video_html_full_path =
      base::FilePath(kTestDlcRootPath).Append(kTimeOfDayVideoHtmlSubPath);
  EXPECT_EQ(web_view->current_url().path(), video_html_full_path.value());
  std::string video_file_requested;
  ASSERT_TRUE(net::GetValueForKeyInQuery(web_view->current_url(), "video_file",
                                         &video_file_requested));
  EXPECT_EQ(video_file_requested, kTimeOfDayNewMexicoVideo);

  UnlockScreen();
  SetAmbientTheme(AmbientTheme::kSlideshow);

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  ASSERT_TRUE(GetContainerView());
  EXPECT_TRUE(
      GetContainerView()->GetViewByID(AmbientViewID::kAmbientPhotoView));

  UnlockScreen();
  SetAmbientUiSettings(
      AmbientUiSettings(AmbientTheme::kVideo, AmbientVideo::kClouds));

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  ASSERT_TRUE(GetContainerView());
  web_view = static_cast<const TestAshWebView*>(
      GetContainerView()->GetViewByID(kAmbientVideoWebView));
  ASSERT_TRUE(web_view);
  EXPECT_TRUE(web_view->current_url().SchemeIsFile());
  EXPECT_EQ(web_view->current_url().path(), video_html_full_path.value());
  ASSERT_TRUE(net::GetValueForKeyInQuery(web_view->current_url(), "video_file",
                                         &video_file_requested));
  EXPECT_EQ(video_file_requested, kTimeOfDayCloudsVideo);
}

class AmbientControllerDurationTest : public AmbientAshTestBase {
 public:
  AmbientControllerDurationTest() = default;
  ~AmbientControllerDurationTest() override = default;

  void SetUp() override {
    AmbientAshTestBase::SetUp();
    GetSessionControllerClient()->set_show_lock_screen_views(true);
  }

 protected:
  base::test::ScopedFeatureList scoped_feature_list_;
};

TEST_F(AmbientControllerDurationTest, SetScreenSaverDuration) {
  // Duration is default to forever.
  SetAmbientModeEnabled(true);
  EXPECT_EQ(0, GetScreenSaverDuration());

  // Set screen saver duration.
  SetScreenSaverDuration(5);
  EXPECT_EQ(5, GetScreenSaverDuration());

  SetScreenSaverDuration(10);
  EXPECT_EQ(10, GetScreenSaverDuration());

  SetScreenSaverDuration(0);
  EXPECT_EQ(0, GetScreenSaverDuration());
}

TEST_F(AmbientControllerDurationTest, AcquireWakeLockAfterScreenSaverStarts) {
  // Simulate User logged in.
  ClearLogin();
  SimulateUserLogin(kUser1);

  // Set screen saver duration to forever.
  SetAmbientModeEnabled(true);
  SetScreenSaverDuration(0);
  EXPECT_EQ(0, GetScreenSaverDuration());

  // Simulate a device being connected to a charger initially.
  SetPowerStateCharging();

  // Lock screen to start ambient mode.
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  HideAmbientScreen();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Ambient screen showup again after inactivity.
  FastForwardByLockScreenInactivityTimeout();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Unlock screen to exit ambient mode.
  UnlockScreen();
  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

TEST_F(AmbientControllerDurationTest, ReleaseWakeLockWhenDurationIsReached) {
  // Simulate User logged in.
  ClearLogin();
  SimulateUserLogin(kUser1);

  // Simulate a device being connected to a charger initially.
  SetPowerStateCharging();

  // Set screen saver duration to any option that is not kForever.
  const int duration_minutes = 5;
  SetAmbientModeEnabled(true);
  SetScreenSaverDuration(duration_minutes);
  EXPECT_EQ(duration_minutes, GetScreenSaverDuration());

  // Lock screen to start ambient mode.
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Fast forward to when duration is reached. Verify that the wake lock has
  // been released.
  FastForwardByDurationInMinutes(duration_minutes);
  FastForwardTiny();
  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

TEST_F(AmbientControllerDurationTest, HoldWakeLockIfDurationIsSetToForever) {
  // Simulate User logged in.
  ClearLogin();
  SimulateUserLogin(kUser1);

  // Simulate a device being connected to a charger initially.
  SetPowerStateCharging();

  // Set screen saver duration to kForever.
  constexpr int kForever = 0;
  SetAmbientModeEnabled(true);
  SetScreenSaverDuration(kForever);
  EXPECT_EQ(kForever, GetScreenSaverDuration());

  // Lock screen to start ambient mode.
  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Fast forward to a time very long afterwards. Verify that screen saver is
  // still running.
  // Use 61 minutes because it is longer than any duration options but not too
  // long so that this test could complete within a few seconds.
  const int kLongTimeInMinutes = 61;
  FastForwardByDurationInMinutes(kLongTimeInMinutes);
  EXPECT_TRUE(ambient_controller()->ShouldShowAmbientUi());
  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

TEST_F(AmbientControllerDurationTest, DoNotAcquireWakeLockOnBatteryMode) {
  ClearLogin();
  SimulateUserLogin(kUser1);

  // Set power to battery mode.
  SetPowerStateDischarging();
  SetExternalPowerDisconnected();

  SetAmbientModeEnabled(true);
  SetScreenSaverDuration(0);
  EXPECT_EQ(0, GetScreenSaverDuration());

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

TEST_F(AmbientControllerDurationTest, AcquireWakeLockWhileOnAcMode) {
  ClearLogin();
  SimulateUserLogin(kUser1);

  // Set power to AC mode, charging.
  SetPowerStateCharging();
  SetExternalPowerConnected();

  SetAmbientModeEnabled(true);
  SetScreenSaverDuration(0);
  EXPECT_EQ(0, GetScreenSaverDuration());

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

TEST_F(AmbientControllerDurationTest, ReleaseWakeLockWhenUnplugged) {
  ClearLogin();
  SimulateUserLogin(kUser1);

  // Set power to AC mode. Verify that wake lock is acquired.
  SetPowerStateCharging();

  SetAmbientModeEnabled(true);
  SetScreenSaverDuration(0);
  EXPECT_EQ(0, GetScreenSaverDuration());

  LockScreen();
  FastForwardByLockScreenInactivityTimeout();
  FastForwardTiny();

  EXPECT_EQ(1, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));

  // Set power to battery mode. Verify that wake lock is released.
  SetPowerStateDischarging();
  SetExternalPowerDisconnected();
  FastForwardTiny();
  EXPECT_EQ(0, GetNumOfActiveWakeLocks(
                   device::mojom::WakeLockType::kPreventDisplaySleep));
}

}  // namespace ash