chromium/chrome/browser/ash/ambient/managed_screensaver_browsertest.cc

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

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "ash/ambient/ui/ambient_view_ids.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_paths.h"
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/ambient/ambient_prefs.h"
#include "ash/public/cpp/autotest_ambient_api.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/test/repeating_test_future.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "chrome/browser/ash/login/lock/screen_locker_tester.h"
#include "chrome/browser/ash/login/login_manager_test.h"
#include "chrome/browser/ash/login/test/device_state_mixin.h"
#include "chrome/browser/ash/login/test/login_manager_mixin.h"
#include "chrome/browser/ash/login/test/user_policy_mixin.h"
#include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
#include "chrome/browser/ash/policy/core/device_policy_builder.h"
#include "chrome/browser/ash/policy/core/user_policy_test_helper.h"
#include "chrome/browser/ash/policy/test_support/embedded_policy_test_server_mixin.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
#include "components/ownership/mock_owner_key_util.h"
#include "components/policy/proto/cloud_policy.pb.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "content/public/test/browser_test.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "url/gurl.h"

namespace ash {

constexpr int64_t kMaxFileSizeInBytes = 8 * 1024 * 1024;  // 8 MB

constexpr base::TimeDelta kTestPerTransitionTimeout = base::Seconds(5);

const char kTestEmail[] = "[email protected]";
constexpr char kCacheDirectoryName[] = "managed_screensaver";
constexpr char kSigninCacheDirectoryPath[] = "signin";
const char kTestLargeImage[] = "test_large.jpg";
const char kTestInvalidImage[] = "test_invalid.jpf";
const char kRedImageFileName[] = "chromeos/screensaver/red.jpg";
const char kGreenImageFileName[] = "chromeos/screensaver/green.jpg";
const char kBlueImageFileName[] = "chromeos/screensaver/blue.jpg";

enum class TestType { LockScreen, LoginScreen };
struct ManagedScreensaverBrowserTestCase {
  std::string test_name;
  TestType test_type;
};

class ManagedScreensaverBrowserTest : public LoginManagerTest {
 public:
  ManagedScreensaverBrowserTest()
      : owner_key_util_(new ownership::MockOwnerKeyUtil()),
        https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
    feature_list_.InitAndEnableFeature(
        ash::features::kAmbientModeManagedScreensaver);
    https_server_.AddDefaultHandlers(GetChromeTestDataDir());
  }
  ~ManagedScreensaverBrowserTest() override = default;

  // LoginManagerTest overrides:
  void SetUpInProcessBrowserTestFixture() override {
    device_policy_.Build();
    OwnerSettingsServiceAshFactory::GetInstance()->SetOwnerKeyUtilForTesting(
        owner_key_util_);
    // Set the signing key in the owner key util
    owner_key_util_->SetPublicKeyFromPrivateKey(
        *device_policy_.GetSigningKey());
    // Override FakeSessionManagerClient.
    SessionManagerClient::InitializeFakeInMemory();

    FakeSessionManagerClient::Get()->set_device_policy(
        device_policy_.GetBlob());

    LoginManagerTest::SetUpInProcessBrowserTestFixture();
  }

  void InitializeForLoginScreen() {
    SetDevicePolicyEnabled(true);

    // Set intervals to zero so that we don't rely on time during testing.
    SetDevicePolicyImageDisplayIntervalSeconds(0);
    SetDevicePolicyScreenIdleTimeoutSeconds(0);
  }

  void InitializeForLockScreen() {
    const auto& users = login_manager_mixin_.users();
    EXPECT_EQ(users.size(), 1u);
    // Required so that fake session manager can be initialized with the correct
    // policy blob.
    user_policy_mixin_.RequestPolicyUpdate();

    LoginUser(test_account_id_);
    screen_locker_ = std::make_unique<ScreenLockerTester>();
    screen_locker_->Lock();
    SetPolicyEnabled(true);

    // Set intervals to zero so that we don't rely on time during testing
    SetPolicyImageDisplayIntervalSeconds(0);
    SetPolicyScreenIdleTimeoutSeconds(0);
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    LoginManagerTest::SetUpCommandLine(command_line);
    // Allow failing policy fetch so that we don't shutdown the profile on
    // failure.
    command_line->AppendSwitch(switches::kAllowFailedPolicyFetchForTest);
  }

  void SetUp() override {
    // Setup the HTTPS test server
    https_server_.ServeFilesFromDirectory(GetChromeTestDataDir());
    https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
    https_server_.RegisterRequestHandler(base::BindRepeating(
        &ManagedScreensaverBrowserTest::HandleRequest, base::Unretained(this)));

    ASSERT_TRUE(https_server_.InitializeAndListen());

    LoginManagerTest::SetUp();
  }

  void SetUpOnMainThread() override {
    LoginManagerTest::SetUpOnMainThread();
    https_server_.StartAcceptingConnections();
  }

  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
      const net::test_server::HttpRequest& request) {
    GURL absolute_url = https_server_.GetURL(request.relative_url);
    auto path = absolute_url.path();
    if (!path.ends_with(kTestLargeImage) &&
        !path.ends_with(kTestInvalidImage)) {
      return nullptr;
    }

    auto http_response =
        std::make_unique<net::test_server::BasicHttpResponse>();
    http_response->set_code(net::HTTP_OK);

    if (path.ends_with(kTestLargeImage)) {
      http_response->set_content(std::string(kMaxFileSizeInBytes + 1, 'a'));
    }
    if (path.ends_with(kTestInvalidImage)) {
      http_response->set_content("invalid");
    }
    http_response->set_content_type("image/jpeg");

    return http_response;
  }

  void TearDownOnMainThread() override {
    screen_locker_.reset();
    LoginManagerTest::TearDownOnMainThread();
  }

  void SetDevicePolicyImages(const std::vector<std::string>& images) {
    enterprise_management::DeviceScreensaverLoginScreenImagesProto*
        mutable_images = device_policy_.payload()
                             .mutable_device_screensaver_login_screen_images();
    mutable_images->clear_device_screensaver_login_screen_images();
    for (const auto& image_path : images) {
      mutable_images->add_device_screensaver_login_screen_images(
          https_server_.GetURL("/" + image_path).spec());
    }
  }

  void SetDevicePolicyEnabled(bool enabled) {
    if (!enabled) {
      // Simulate policy-group guard by clearing other policies when the managed
      // screensaver policy is disabled.
      device_policy_.payload().Clear();
    }
    device_policy_.payload()
        .mutable_device_screensaver_login_screen_enabled()
        ->set_device_screensaver_login_screen_enabled(enabled);
  }

  void SetDevicePolicyImageDisplayIntervalSeconds(int64_t interval) {
    device_policy_.payload()
        .mutable_device_screensaver_login_screen_image_display_interval_seconds()
        ->set_device_screensaver_login_screen_image_display_interval_seconds(
            interval);
  }

  void SetDevicePolicyScreenIdleTimeoutSeconds(int64_t timeout) {
    device_policy_.payload()
        .mutable_device_screensaver_login_screen_idle_timeout_seconds()
        ->set_device_screensaver_login_screen_idle_timeout_seconds(timeout);
  }

  // Note: Waits for changes to policy preferences. Verifies that all
  // |policy_prefs| receive a policy update, fails the test otherwise.
  void RefreshDevicePolicyAndWait(
      const std::vector<std::string>& policy_prefs) {
    base::test::RepeatingTestFuture<void> test_future;
    PrefChangeRegistrar registar;
    registar.Init(
        Shell::Get()->session_controller()->GetSigninScreenPrefService());

    for (const std::string& path : policy_prefs) {
      registar.Add(path, test_future.GetCallback());
    }
    device_policy_.Build();
    FakeSessionManagerClient::Get()->set_device_policy(
        device_policy_.GetBlob());
    FakeSessionManagerClient::Get()->OnPropertyChangeComplete(
        /*success=*/true);
    for (size_t count = 0; count < policy_prefs.size(); count++) {
      ASSERT_TRUE(test_future.Wait())
          << "Timed out trying to wait for pref update";
      test_future.Take();
    }
  }

  void RefreshUserPolicyAndWait() {
    Profile* profile =
        ash::ProfileHelper::Get()->GetProfileByAccountId(test_account_id_);
    user_policy_test_helper_.RefreshPolicyAndWait(profile);
  }

  // Wait for the images to be downloaded to the screensaver directory.
  void WaitForImages(const std::vector<std::string>& images,
                     const base::FilePath& directory) {
    int64_t expected_directory_size = 0;
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      base::FilePath test_data_dir;
      base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
      base::File::Info info;

      for (auto image_path : images) {
        base::FilePath temp_dir_path = test_data_dir;
        EXPECT_TRUE(GetFileInfo(temp_dir_path.AppendASCII(image_path), &info));
        expected_directory_size += info.size;
      }
    }

    // Wait until the download directory size is equal to the expected directory
    // size, which would indicate that all the images have been downloaded.
    ASSERT_TRUE(base::test::RunUntil([&]() {
      base::ScopedAllowBlockingForTesting allow_blocking;
      return ComputeDirectorySize(directory) == expected_directory_size;
    }));
  }

  // TODO(b:280809373): Remove mutable subproto1 once policies are released.
  void SetPolicyImages(const std::vector<std::string>& images) {
    // Policy update variable should be explicitly declared here, otherwise it
    // might go out of scope immediately and cause multiple updates if we
    // inline the call in the loop.
    std::unique_ptr<ScopedUserPolicyUpdate> policy =
        user_policy_mixin_.RequestPolicyUpdate();

    auto* mutable_images = policy->policy_payload()
                               ->mutable_subproto1()
                               ->mutable_screensaverlockscreenimages();

    // Fake the update as set by policy
    mutable_images->mutable_policy_options()->set_mode(
        enterprise_management::PolicyOptions::MANDATORY);
    mutable_images->mutable_value()->mutable_entries()->Clear();
    for (const auto& image_path : images) {
      mutable_images->mutable_value()->mutable_entries()->Add(
          std::string(https_server_.GetURL("/" + image_path).spec()));
    }
  }

  void SetPolicyEnabled(bool enabled) {
    if (!enabled) {
      // Simulate policy-group guard by clearing other policies when the managed
      // screensaver policy is disabled.
      user_policy_mixin_.RequestPolicyUpdate()->policy_payload()->Clear();
    }
    user_policy_mixin_.RequestPolicyUpdate()
        ->policy_payload()
        ->mutable_subproto1()
        ->mutable_screensaverlockscreenenabled()
        ->set_value(enabled);
  }

  void SetPolicyImageDisplayIntervalSeconds(int64_t interval) {
    std::unique_ptr<ScopedUserPolicyUpdate> policy =
        user_policy_mixin_.RequestPolicyUpdate();
    policy->policy_payload()
        ->mutable_subproto1()
        ->mutable_screensaverlockscreenimagedisplayintervalseconds()
        ->set_value(interval);
  }

  void SetPolicyScreenIdleTimeoutSeconds(int64_t timeout) {
    std::unique_ptr<ScopedUserPolicyUpdate> policy =
        user_policy_mixin_.RequestPolicyUpdate();
    policy->policy_payload()
        ->mutable_subproto1()
        ->mutable_screensaverlockscreenidletimeoutseconds()
        ->set_value(timeout);
  }

  views::View* GetContainerView() {
    auto* widget =
        Shell::GetPrimaryRootWindowController()->ambient_widget_for_testing();

    if (widget) {
      auto* container_view = widget->GetContentsView();
      DCHECK(container_view &&
             container_view->GetID() == kAmbientContainerView);
      return container_view;
    }
    return nullptr;
  }

 protected:
  const AccountId test_account_id_ =
      AccountId::FromUserEmailGaiaId(kTestEmail,
                                     signin::GetTestGaiaIdForEmail(kTestEmail));

  const LoginManagerMixin::TestUserInfo managed_user_{test_account_id_};

  EmbeddedPolicyTestServerMixin policy_server_mixin_{&mixin_host_};

  UserPolicyMixin user_policy_mixin_{&mixin_host_, test_account_id_,
                                     &policy_server_mixin_};

  std::unique_ptr<base::test::TestFuture<void>> test_future_;
  std::unique_ptr<ScreenLockerTester> screen_locker_;

  base::test::ScopedFeatureList feature_list_;
  policy::DevicePolicyBuilder device_policy_;
  policy::UserPolicyTestHelper user_policy_test_helper_{kTestEmail,
                                                        &policy_server_mixin_};
  scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_;

  DeviceStateMixin device_state_{
      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
  net::test_server::EmbeddedTestServer https_server_;

  LoginManagerMixin login_manager_mixin_{&mixin_host_, {managed_user_}};
};

class ManagedScreensaverBrowserTestForAnyScreen
    : public ManagedScreensaverBrowserTest,
      public ::testing::WithParamInterface<ManagedScreensaverBrowserTestCase> {
 public:
  void Init() {
    const ManagedScreensaverBrowserTestCase test_case = GetParam();
    switch (test_case.test_type) {
      case TestType::LockScreen:
        ManagedScreensaverBrowserTest::InitializeForLockScreen();
        // Call refresh policy manually to not have multiple refresh calls
        // running at the same time.
        RefreshUserPolicyAndWait();
        return;
      case TestType::LoginScreen:
        ManagedScreensaverBrowserTest::InitializeForLoginScreen();
        RefreshDevicePolicyAndWait(
            {ambient::prefs::kAmbientModeManagedScreensaverEnabled,
             ambient::prefs::
                 kAmbientModeManagedScreensaverImageDisplayIntervalSeconds,
             ambient::prefs::kAmbientModeManagedScreensaverIdleTimeoutSeconds});
        return;
    }
    NOTREACHED_IN_MIGRATION();
  }

  void SetPolicy(bool enabled) {
    const ManagedScreensaverBrowserTestCase test_case = GetParam();
    switch (test_case.test_type) {
      case TestType::LockScreen:
        SetPolicyEnabled(enabled);
        RefreshUserPolicyAndWait();

        // Set intervals to zero so that we don't rely on time during testing.
        // This is needed as disabling the policy can unset other policy values.
        if (!enabled) {
          SetPolicyImageDisplayIntervalSeconds(0);
          SetPolicyScreenIdleTimeoutSeconds(0);
          RefreshUserPolicyAndWait();
        }
        return;
      case TestType::LoginScreen:
        SetDevicePolicyEnabled(enabled);
        RefreshDevicePolicyAndWait(
            {ambient::prefs::kAmbientModeManagedScreensaverEnabled});
        // Set intervals to zero so that we don't rely on time during testing.
        // This is needed as disabling the policy can unset other policy values.
        if (!enabled) {
          SetDevicePolicyImageDisplayIntervalSeconds(0);
          SetDevicePolicyScreenIdleTimeoutSeconds(0);
          RefreshDevicePolicyAndWait(
              {ambient::prefs::
                   kAmbientModeManagedScreensaverImageDisplayIntervalSeconds,
               ambient::prefs::
                   kAmbientModeManagedScreensaverIdleTimeoutSeconds});
        }
        return;
    }
    NOTREACHED_IN_MIGRATION();
  }

  void SetImages(const std::vector<std::string>& images,
                 bool wait_for_images = false) {
    const ManagedScreensaverBrowserTestCase test_case = GetParam();
    switch (test_case.test_type) {
      case TestType::LockScreen:
        SetPolicyImages(images);
        // Call refresh policy manually to not have multiple refresh calls
        // running at the same time.
        RefreshUserPolicyAndWait();
        break;
      case TestType::LoginScreen:
        SetDevicePolicyImages(images);
        RefreshDevicePolicyAndWait(
            {ambient::prefs::kAmbientModeManagedScreensaverImages});
        break;
    }
    if (wait_for_images) {
      WaitForImages(images, GetPolicyHandlerCachePath());
    }
  }

  base::FilePath GetPolicyHandlerCachePath() {
    const ManagedScreensaverBrowserTestCase test_case = GetParam();
    switch (test_case.test_type) {
      case TestType::LoginScreen:
        return base::PathService::CheckedGet(
                   ash::DIR_DEVICE_POLICY_SCREENSAVER_DATA)
            .AppendASCII(kSigninCacheDirectoryPath);
      case TestType::LockScreen:
        return base::PathService::CheckedGet(base::DIR_HOME)
            .AppendASCII(kCacheDirectoryName);
    }
  }
};

INSTANTIATE_TEST_SUITE_P(
    ManagedScreensaverBrowserTestForAnyScreenTests,
    ManagedScreensaverBrowserTestForAnyScreen,
    ::testing::ValuesIn<ManagedScreensaverBrowserTestCase>({
        {"LoginScreen", TestType::LoginScreen},
        {"LockScreen", TestType::LockScreen},
    }),
    [](const ::testing::TestParamInfo<
        ManagedScreensaverBrowserTestForAnyScreen::ParamType>& info) {
      return info.param.test_name;
    });

IN_PROC_BROWSER_TEST_P(ManagedScreensaverBrowserTestForAnyScreen, BasicTest) {
  Init();
  SetImages({kRedImageFileName, kBlueImageFileName, kGreenImageFileName},
            /*wait_for_images=*/true);
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
  test_future_ = std::make_unique<base::test::TestFuture<void>>();
  AutotestAmbientApi test_api;
  test_api.WaitForPhotoTransitionAnimationCompleted(
      /*num_completions=*/3, /*timeout=*/3 * kTestPerTransitionTimeout,
      /*on_complete=*/test_future_->GetCallback(),
      /*on_timeout=*/base::BindOnce([]() { NOTREACHED_IN_MIGRATION(); }));
  ASSERT_TRUE(test_future_->Wait());
  ASSERT_NE(nullptr, GetContainerView());

  // Confirm that setting the policy to disabled cleans up the images from the
  // filesystem.
  SetPolicy(/*enabled=*/false);

  test_future_ = std::make_unique<base::test::TestFuture<void>>();
  test_api.WaitForPhotoTransitionAnimationCompleted(
      /*num_completions=*/1, /*timeout=*/kTestPerTransitionTimeout,
      /*on_complete=*/base::BindOnce([]() { NOTREACHED_IN_MIGRATION(); }),
      /*on_timeout=*/test_future_->GetCallback());
  ASSERT_TRUE(test_future_->Wait());
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_EQ(0, ComputeDirectorySize(GetPolicyHandlerCachePath()));
  }
  ASSERT_EQ(nullptr, GetContainerView());
}

IN_PROC_BROWSER_TEST_P(ManagedScreensaverBrowserTestForAnyScreen,
                       OneImageDoesNotStartAmbientMode) {
  Init();
  SetImages({kRedImageFileName}, /*wait_for_images=*/true);
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
  AutotestAmbientApi test_api;
  test_future_ = std::make_unique<base::test::TestFuture<void>>();
  test_api.WaitForPhotoTransitionAnimationCompleted(
      /*num_completions=*/1, /*timeout=*/kTestPerTransitionTimeout,
      /*on_complete=*/base::BindOnce([]() { NOTREACHED_IN_MIGRATION(); }),
      /*on_timeout=*/test_future_->GetCallback());
  ASSERT_TRUE(test_future_->Wait());

  ASSERT_EQ(nullptr, GetContainerView());
}

IN_PROC_BROWSER_TEST_P(ManagedScreensaverBrowserTestForAnyScreen,
                       ImageMoreThanMaxSizeNotDownloadedOrShown) {
  Init();
  SetImages({kTestLargeImage, kBlueImageFileName});
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);

  AutotestAmbientApi test_api;
  test_future_ = std::make_unique<base::test::TestFuture<void>>();
  // The large image will not even be downloaded and will fail to download.
  test_api.WaitForPhotoTransitionAnimationCompleted(
      /*num_completions=*/1, /*timeout=*/kTestPerTransitionTimeout,
      /*on_complete=*/base::BindOnce([]() { NOTREACHED_IN_MIGRATION(); }),
      /*on_timeout=*/test_future_->GetCallback());
  ASSERT_TRUE(test_future_->Wait());
  ASSERT_EQ(nullptr, GetContainerView());
}

IN_PROC_BROWSER_TEST_P(ManagedScreensaverBrowserTestForAnyScreen,
                       InvalidImageDownloadedButNotShown) {
  Init();
  SetImages({kTestInvalidImage, kBlueImageFileName});
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);

  AutotestAmbientApi test_api;
  test_future_ = std::make_unique<base::test::TestFuture<void>>();
  // The invalid image is downloaded but the screensaver will not start up and
  // show images as the second image will fail to decode.
  test_api.WaitForPhotoTransitionAnimationCompleted(
      /*num_completions=*/1, /*timeout=*/kTestPerTransitionTimeout,
      /*on_complete=*/base::BindOnce([]() { NOTREACHED_IN_MIGRATION(); }),
      /*on_timeout=*/test_future_->GetCallback());
  ASSERT_TRUE(test_future_->Wait());
  ASSERT_EQ(nullptr, GetContainerView());
}

IN_PROC_BROWSER_TEST_P(ManagedScreensaverBrowserTestForAnyScreen,
                       ClearingTheImagesStopsScreensaver) {
  Init();
  ui::ScopedAnimationDurationScaleMode test_duration_mode(
      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
  SetImages({kRedImageFileName, kBlueImageFileName, kGreenImageFileName},
            /*wait_for_images=*/true);
  AutotestAmbientApi test_api;

  test_future_ = std::make_unique<base::test::TestFuture<void>>();
  test_api.WaitForPhotoTransitionAnimationCompleted(
      /*num_completions=*/3, /*timeout=*/3 * kTestPerTransitionTimeout,
      /*on_complete=*/test_future_->GetCallback(),
      /*on_timeout=*/base::BindOnce([]() { NOTREACHED_IN_MIGRATION(); }));
  ASSERT_TRUE(test_future_->Wait());
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_TRUE(ComputeDirectorySize(GetPolicyHandlerCachePath()) > 0);
  }
  ASSERT_NE(nullptr, GetContainerView());

  SetImages({});
  test_future_ = std::make_unique<base::test::TestFuture<void>>();
  test_api.WaitForPhotoTransitionAnimationCompleted(
      /*num_completions=*/1, /*timeout=*/kTestPerTransitionTimeout,
      /*on_complete=*/base::BindOnce([]() { NOTREACHED_IN_MIGRATION(); }),
      /*on_timeout=*/test_future_->GetCallback());
  ASSERT_TRUE(test_future_->Wait());
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_EQ(0, ComputeDirectorySize(GetPolicyHandlerCachePath()));
  }
  ASSERT_EQ(nullptr, GetContainerView());
}

}  // namespace ash