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

#include <stddef.h>
#include <stdint.h>

#include <array>
#include <memory>
#include <string_view>

#include "ash/constants/ash_features.h"
#include "base/base64.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chrome/browser/ash/crostini/crostini_pref_names.h"
#include "chrome/browser/ash/crostini/fake_crostini_features.h"
#include "chrome/browser/ash/drive/drivefs_test_support.h"
#include "chrome/browser/ash/extensions/file_manager/event_router.h"
#include "chrome/browser/ash/extensions/file_manager/event_router_factory.h"
#include "chrome/browser/ash/extensions/file_manager/private_api_misc.h"
#include "chrome/browser/ash/extensions/file_manager/select_file_dialog_extension_user_data.h"
#include "chrome/browser/ash/file_manager/file_watcher.h"
#include "chrome/browser/ash/file_manager/mount_test_util.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/ash/file_system_provider/icon_set.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "chrome/browser/ash/guest_os/guest_os_share_path_factory.h"
#include "chrome/browser/ash/policy/dlp/dialogs/files_policy_dialog.h"
#include "chrome/browser/ash/policy/dlp/dlp_files_controller_ash.h"
#include "chrome/browser/ash/policy/dlp/files_policy_notification_manager.h"
#include "chrome/browser/ash/policy/dlp/files_policy_notification_manager_factory.h"
#include "chrome/browser/ash/policy/dlp/test/mock_files_policy_notification_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_rules_manager.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
#include "chromeos/ash/components/dbus/cros_disks/cros_disks_client.h"
#include "chromeos/ash/components/dbus/vm_concierge/concierge_service.pb.h"
#include "chromeos/ash/components/disks/disk.h"
#include "chromeos/ash/components/disks/mock_disk_mount_manager.h"
#include "chromeos/dbus/dlp/dlp_client.h"
#include "chromeos/dbus/dlp/dlp_service.pb.h"
#include "components/drive/drive_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/common/extension.h"
#include "extensions/common/install_warning.h"
#include "google_apis/common/test_util.h"
#include "storage/browser/file_system/external_mount_points.h"

using ::testing::_;
using ::testing::ReturnRef;

namespace {

using ::ash::disks::Disk;
using ::ash::disks::DiskMountManager;
using ::ash::disks::FormatFileSystemType;

struct TestDiskInfo {
  const char* file_path;
  bool write_disabled_by_policy;
  const char* device_label;
  const char* drive_label;
  const char* vendor_id;
  const char* vendor_name;
  const char* product_id;
  const char* product_name;
  const char* fs_uuid;
  const char* storage_device_path;
  ash::DeviceType device_type;
  uint64_t size_in_bytes;
  bool is_parent;
  bool is_read_only_hardware;
  bool has_media;
  bool on_boot_device;
  bool on_removable_device;
  bool is_hidden;
  const char* file_system_type;
  const char* base_mount_path;
};

struct TestMountPoint {
  std::string source_path;
  std::string mount_path;
  ash::MountType mount_type;
  ash::MountError mount_error;

  // -1 if there is no disk info.
  int disk_info_index;
};

constexpr auto kTestDisks = std::to_array<TestDiskInfo>({
    {
        "file_path1",
        false,
        "device_label1",
        "drive_label1",
        "0123",
        "vendor1",
        "abcd",
        "product1",
        "FFFF-FFFF",
        "storage_device_path1",
        ash::DeviceType::kUSB,
        1073741824,
        false,
        false,
        false,
        false,
        false,
        false,
        "exfat",
        "",
    },
    {
        "file_path2",
        false,
        "device_label2",
        "drive_label2",
        "4567",
        "vendor2",
        "cdef",
        "product2",
        "0FFF-FFFF",
        "storage_device_path2",
        ash::DeviceType::kMobile,
        47723,
        true,
        true,
        true,
        true,
        false,
        false,
        "exfat",
        "",
    },
    {
        "file_path3",
        true,  // write_disabled_by_policy
        "device_label3",
        "drive_label3",
        "89ab",
        "vendor3",
        "ef01",
        "product3",
        "00FF-FFFF",
        "storage_device_path3",
        ash::DeviceType::kOpticalDisc,
        0,
        true,
        false,  // is_hardware_read_only
        false,
        true,
        false,
        false,
        "exfat",
        "",
    },
});

const char kLocalMountPointName[] = "local";

void AddLocalFileSystem(Profile* profile, base::FilePath root) {
  const char kTestFileContent[] = "The five boxing wizards jumped quickly";

  {
    base::ScopedAllowBlockingForTesting allow_io;
    ASSERT_TRUE(base::CreateDirectory(root.AppendASCII("test_dir")));
    ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
        root.AppendASCII("test_dir").AppendASCII("test_file.txt"),
        kTestFileContent));
  }

  ASSERT_TRUE(profile->GetMountPoints()->RegisterFileSystem(
      kLocalMountPointName, storage::kFileSystemTypeLocal,
      storage::FileSystemMountOption(), root));
  file_manager::VolumeManager::Get(profile)->AddVolumeForTesting(
      root, file_manager::VOLUME_TYPE_TESTING, ash::DeviceType::kUnknown,
      false /* read_only */);
}

}  // namespace

class FileManagerPrivateApiTest : public extensions::ExtensionApiTest {
 public:
  FileManagerPrivateApiTest() : disk_mount_manager_mock_(nullptr) {
    InitMountPoints();
  }

  ~FileManagerPrivateApiTest() override {
    DCHECK(!disk_mount_manager_mock_);
    DCHECK(!event_router_);
  }

  bool SetUpUserDataDirectory() override {
    return drive::SetUpUserDataDirectoryForDriveFsTest();
  }

  void SetUpOnMainThread() override {
    extensions::ExtensionApiTest::SetUpOnMainThread();
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    ASSERT_TRUE(non_watchable_dir_.CreateUniqueTempDir());

    event_router_ = file_manager::EventRouterFactory::GetForProfile(profile());
  }

  void TearDownOnMainThread() override {
    event_router_ = nullptr;
    extensions::ExtensionApiTest::TearDownOnMainThread();
  }

  // ExtensionApiTest override
  void SetUpInProcessBrowserTestFixture() override {
    extensions::ExtensionApiTest::SetUpInProcessBrowserTestFixture();
    disk_mount_manager_mock_ = new ash::disks::MockDiskMountManager;
    DiskMountManager::InitializeForTesting(disk_mount_manager_mock_);
    disk_mount_manager_mock_->SetupDefaultReplies();

    // override mock functions.
    ON_CALL(*disk_mount_manager_mock_, FindDiskBySourcePath(_))
        .WillByDefault(
            Invoke(this, &FileManagerPrivateApiTest::FindVolumeBySourcePath));
    EXPECT_CALL(*disk_mount_manager_mock_, disks())
        .WillRepeatedly(ReturnRef(volumes_));
    EXPECT_CALL(*disk_mount_manager_mock_, mount_points())
        .WillRepeatedly(ReturnRef(mount_points_));
  }

  // ExtensionApiTest override
  void TearDownInProcessBrowserTestFixture() override {
    DiskMountManager::Shutdown();
    disk_mount_manager_mock_ = nullptr;

    extensions::ExtensionApiTest::TearDownInProcessBrowserTestFixture();
  }

 private:
  void InitMountPoints() {
    const TestMountPoint kTestMountPoints[] = {
        {"device_path1",
         ash::CrosDisksClient::GetRemovableDiskMountPoint()
             .AppendASCII("mount_path1")
             .AsUTF8Unsafe(),
         ash::MountType::kDevice, ash::MountError::kSuccess, 0},
        {"device_path2",
         ash::CrosDisksClient::GetRemovableDiskMountPoint()
             .AppendASCII("mount_path2")
             .AsUTF8Unsafe(),
         ash::MountType::kDevice, ash::MountError::kSuccess, 1},
        {"device_path3",
         ash::CrosDisksClient::GetRemovableDiskMountPoint()
             .AppendASCII("mount_path3")
             .AsUTF8Unsafe(),
         ash::MountType::kDevice, ash::MountError::kSuccess, 2},
        {// Set source path inside another mounted volume.
         ash::CrosDisksClient::GetRemovableDiskMountPoint()
             .AppendASCII("mount_path3/archive.zip")
             .AsUTF8Unsafe(),
         ash::CrosDisksClient::GetArchiveMountPoint()
             .AppendASCII("archive_mount_path")
             .AsUTF8Unsafe(),
         ash::MountType::kArchive, ash::MountError::kSuccess, -1}};

    for (const auto& mp : kTestMountPoints) {
      mount_points_.insert(
          {mp.source_path, mp.mount_path, mp.mount_type, mp.mount_error});
      int disk_info_index = mp.disk_info_index;
      if (mp.disk_info_index >= 0) {
        EXPECT_GT(kTestDisks.size(), static_cast<size_t>(disk_info_index));
        if (static_cast<size_t>(disk_info_index) >= kTestDisks.size()) {
          return;
        }

        const TestDiskInfo& disk_info = kTestDisks[disk_info_index];

        std::unique_ptr<Disk> disk =
            Disk::Builder()
                .SetDevicePath(mp.source_path)
                .SetMountPath(mp.mount_path)
                .SetWriteDisabledByPolicy(disk_info.write_disabled_by_policy)
                .SetFilePath(disk_info.file_path)
                .SetDeviceLabel(disk_info.device_label)
                .SetDriveLabel(disk_info.drive_label)
                .SetVendorId(disk_info.vendor_id)
                .SetVendorName(disk_info.vendor_name)
                .SetProductId(disk_info.product_id)
                .SetProductName(disk_info.product_name)
                .SetFileSystemUUID(disk_info.fs_uuid)
                .SetStorageDevicePath(disk_info.storage_device_path)
                .SetDeviceType(disk_info.device_type)
                .SetSizeInBytes(disk_info.size_in_bytes)
                .SetIsParent(disk_info.is_parent)
                .SetIsReadOnlyHardware(disk_info.is_read_only_hardware)
                .SetHasMedia(disk_info.has_media)
                .SetOnBootDevice(disk_info.on_boot_device)
                .SetOnRemovableDevice(disk_info.on_removable_device)
                .SetIsHidden(disk_info.is_hidden)
                .SetFileSystemType(disk_info.file_system_type)
                .SetBaseMountPath(disk_info.base_mount_path)
                .Build();

        volumes_.insert(std::move(disk));
      }
    }
  }

  const Disk* FindVolumeBySourcePath(std::string_view source_path) {
    auto volume_it = volumes_.find(source_path);
    return (volume_it == volumes_.end()) ? nullptr : volume_it->get();
  }

 protected:
  void SshfsMount(const std::string& source_path,
                  const std::string& source_format,
                  const std::string& mount_label,
                  const std::vector<std::string>& mount_options,
                  ash::MountType type,
                  ash::MountAccessMode access_mode,
                  DiskMountManager::MountPathCallback callback) {
    DiskMountManager::MountPoint mount_point_info{
        source_path, "/media/fuse/" + mount_label,
        ash::MountType::kNetworkStorage};
    disk_mount_manager_mock_->NotifyMountEvent(
        DiskMountManager::MountEvent::MOUNTING, ash::MountError::kSuccess,
        mount_point_info);
    std::move(callback).Run(ash::MountError::kSuccess, mount_point_info);
  }

  void ExpectCrostiniMount() {
    std::vector<std::string> mount_options;
    EXPECT_CALL(*disk_mount_manager_mock_,
                MountPath("sftp://3:1234", "", "crostini_user_termina_penguin",
                          mount_options, ash::MountType::kNetworkStorage,
                          ash::MountAccessMode::kReadWrite, _))
        .WillOnce(Invoke(this, &FileManagerPrivateApiTest::SshfsMount));
  }

  base::ScopedTempDir temp_dir_;
  base::ScopedTempDir non_watchable_dir_;
  raw_ptr<ash::disks::MockDiskMountManager, DanglingUntriaged>
      disk_mount_manager_mock_ = nullptr;
  DiskMountManager::Disks volumes_;
  DiskMountManager::MountPoints mount_points_;
  raw_ptr<file_manager::EventRouter> event_router_ = nullptr;
};

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, Mount) {
  using ash::file_system_provider::IconSet;
  profile()->GetPrefs()->SetBoolean(drive::prefs::kDisableDrive, true);

  // Add a provided file system, to test passing the |configurable| and
  // |source| flags properly down to Files app.
  IconSet icon_set;
  icon_set.SetIcon(IconSet::IconSize::SIZE_16x16,
                   GURL("chrome://resources/testing-provider-id-16.jpg"));
  icon_set.SetIcon(IconSet::IconSize::SIZE_32x32,
                   GURL("chrome://resources/testing-provider-id-32.jpg"));
  ash::file_system_provider::ProvidedFileSystemInfo info(
      "testing-provider-id", ash::file_system_provider::MountOptions(),
      base::FilePath(), true /* configurable */, false /* watchable */,
      extensions::SOURCE_NETWORK, icon_set);

  file_manager::VolumeManager::Get(browser()->GetProfile())
      ->AddVolumeForTesting(file_manager::Volume::CreateForProvidedFileSystem(
          info, file_manager::MOUNT_CONTEXT_AUTO));

  // The test.js calls fileManagerPrivate.removeMount(name) twice for each name
  // "mount_path1" and "archive_mount_path". The first call should unmount with
  // no error, and the second should fail with an error. Record the events, and
  // verify them by name and occurrence count.
  int events[4] = {0, 0, 0, 0};

  EXPECT_CALL(*disk_mount_manager_mock_,
              UnmountPath(ash::CrosDisksClient::GetRemovableDiskMountPoint()
                              .AppendASCII("mount_path1")
                              .AsUTF8Unsafe(),
                          _))
      .WillOnce([&events](const std::string& path,
                          DiskMountManager::UnmountPathCallback callback) {
        const auto name = base::FilePath(path).BaseName();
        EXPECT_EQ("mount_path1", name.value());
        ++events[0];
        EXPECT_EQ(1, events[0]);
        std::move(callback).Run(ash::MountError::kSuccess);
      })
      .WillOnce([&events](const std::string& path,
                          DiskMountManager::UnmountPathCallback callback) {
        const auto name = base::FilePath(path).BaseName();
        EXPECT_EQ("mount_path1", name.value());
        ++events[1];
        EXPECT_EQ(1, events[1]);
        std::move(callback).Run(ash::MountError::kCancelled);
      });

  EXPECT_CALL(*disk_mount_manager_mock_,
              UnmountPath(ash::CrosDisksClient::GetArchiveMountPoint()
                              .AppendASCII("archive_mount_path")
                              .AsUTF8Unsafe(),
                          _))
      .WillOnce([&events](const std::string& path,
                          DiskMountManager::UnmountPathCallback callback) {
        const auto name = base::FilePath(path).BaseName();
        EXPECT_EQ("archive_mount_path", name.value());
        ++events[2];
        EXPECT_EQ(1, events[2]);
        std::move(callback).Run(ash::MountError::kSuccess);
      })
      .WillOnce([&events](const std::string& path,
                          DiskMountManager::UnmountPathCallback callback) {
        const auto name = base::FilePath(path).BaseName();
        EXPECT_EQ("archive_mount_path", name.value());
        ++events[3];
        EXPECT_EQ(1, events[3]);
        std::move(callback).Run(ash::MountError::kNeedPassword);
      });

  ASSERT_TRUE(RunExtensionTest("file_browser/mount_test", {},
                               {.load_as_component = true}))
      << message_;

  ASSERT_EQ(1, events[0]);
  ASSERT_EQ(1, events[1]);
  ASSERT_EQ(1, events[2]);
  ASSERT_EQ(1, events[3]);
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, FormatVolume) {
  // Catch-all rule to ensure that FormatMountedDevice does not get called with
  // other arguments that don't match the rules below.
  EXPECT_CALL(*disk_mount_manager_mock_, FormatMountedDevice(_, _, _)).Times(0);

  EXPECT_CALL(
      *disk_mount_manager_mock_,
      FormatMountedDevice(ash::CrosDisksClient::GetRemovableDiskMountPoint()
                              .AppendASCII("mount_path1")
                              .AsUTF8Unsafe(),
                          FormatFileSystemType::kVfat, "NEWLABEL1"))
      .Times(1);
  EXPECT_CALL(
      *disk_mount_manager_mock_,
      FormatMountedDevice(ash::CrosDisksClient::GetRemovableDiskMountPoint()
                              .AppendASCII("mount_path2")
                              .AsUTF8Unsafe(),
                          FormatFileSystemType::kExfat, "NEWLABEL2"))
      .Times(1);
  EXPECT_CALL(
      *disk_mount_manager_mock_,
      FormatMountedDevice(ash::CrosDisksClient::GetRemovableDiskMountPoint()
                              .AppendASCII("mount_path3")
                              .AsUTF8Unsafe(),
                          FormatFileSystemType::kNtfs, "NEWLABEL3"))
      .Times(1);

  ASSERT_TRUE(RunExtensionTest("file_browser/format_test", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, Permissions) {
  EXPECT_TRUE(RunExtensionTest("file_browser/permissions", {},
                               {.ignore_manifest_warnings = true}));
  const extensions::Extension* extension = GetSingleLoadedExtension();
  ASSERT_TRUE(extension);
  ASSERT_EQ(1u, extension->install_warnings().size());
  const extensions::InstallWarning& warning = extension->install_warnings()[0];
  EXPECT_EQ("fileManagerPrivate", warning.key);
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, AddFileWatch) {
  // Add a filesystem and Volume that is not watchable.
  AddLocalFileSystem(browser()->GetProfile(), non_watchable_dir_.GetPath());

  // Add a filesystem and Volume that is watchable.
  const base::FilePath downloads_dir = temp_dir_.GetPath();
  ASSERT_TRUE(file_manager::VolumeManager::Get(browser()->GetProfile())
                  ->RegisterDownloadsDirectoryForTesting(downloads_dir));

  ASSERT_TRUE(RunExtensionTest("file_browser/add_file_watch", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, Recent) {
  const base::FilePath downloads_dir = temp_dir_.GetPath();

  ASSERT_TRUE(file_manager::VolumeManager::Get(browser()->GetProfile())
                  ->RegisterDownloadsDirectoryForTesting(downloads_dir));

  // Create test files.
  {
    base::ScopedAllowBlockingForTesting allow_io;
    base::File image_file(downloads_dir.Append("all-justice.jpg"),
                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(image_file.IsValid());
    base::File audio_file(downloads_dir.Append("all-justice.mp3"),
                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(audio_file.IsValid());
    base::File video_file(downloads_dir.Append("all-justice.mp4"),
                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(video_file.IsValid());
    base::File text_file(downloads_dir.Append("all-justice.txt"),
                         base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(text_file.IsValid());
    ASSERT_TRUE(text_file.SetTimes(base::Time::Now(),
                                   base::Time::Now() - base::Days(60)));
  }

  ASSERT_TRUE(RunExtensionTest("file_browser/recent_test", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, MediaMetadata) {
  const base::FilePath test_dir = temp_dir_.GetPath();
  AddLocalFileSystem(browser()->GetProfile(), test_dir);

  // Get source media/test/data directory path.
  base::FilePath root_dir;
  CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &root_dir));
  const base::FilePath media_test_data_dir =
      root_dir.AppendASCII("media").AppendASCII("test").AppendASCII("data");

  // Returns a path to a media/test/data test file.
  auto get_media_test_data_file = [&](const std::string& file) {
    return media_test_data_dir.Append(base::FilePath::FromUTF8Unsafe(file));
  };

  // Create media test files.
  {
    base::ScopedAllowBlockingForTesting allow_io;

    const base::FilePath video = get_media_test_data_file("90rotation.mp4");
    ASSERT_TRUE(base::CopyFile(video, test_dir.Append(video.BaseName())));

    const base::FilePath audio = get_media_test_data_file("id3_png_test.mp3");
    ASSERT_TRUE(base::CopyFile(audio, test_dir.Append(audio.BaseName())));
  }

  // Get source chrome/test/data/chromeos/file_manager directory path.
  const base::FilePath files_test_data_dir = root_dir.AppendASCII("chrome")
                                                 .AppendASCII("test")
                                                 .AppendASCII("data")
                                                 .AppendASCII("chromeos")
                                                 .AppendASCII("file_manager");

  // Returns a path to a chrome/test/data/chromeos/file_manager test file.
  auto get_files_test_data_dir = [&](const std::string& file) {
    return files_test_data_dir.Append(base::FilePath::FromUTF8Unsafe(file));
  };

  // Create files test files.
  {
    base::ScopedAllowBlockingForTesting allow_io;

    const base::FilePath broke = get_files_test_data_dir("broken.jpg");
    ASSERT_TRUE(base::CopyFile(broke, test_dir.Append(broke.BaseName())));

    const base::FilePath empty = get_files_test_data_dir("empty.txt");
    ASSERT_TRUE(base::CopyFile(empty, test_dir.Append(empty.BaseName())));

    const base::FilePath image = get_files_test_data_dir("image3.jpg");
    ASSERT_TRUE(base::CopyFile(image, test_dir.Append(image.BaseName())));
  }

  ASSERT_TRUE(RunExtensionTest("file_browser/media_metadata", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, Crostini) {
  crostini::FakeCrostiniFeatures crostini_features;
  crostini_features.set_is_allowed_now(true);
  crostini_features.set_enabled(true);

  // Setup CrostiniManager for testing.
  crostini::CrostiniManager* crostini_manager =
      crostini::CrostiniManager::GetForProfile(browser()->GetProfile());
  crostini_manager->set_skip_restart_for_testing();
  crostini_manager->AddRunningVmForTesting(crostini::kCrostiniDefaultVmName, 3);
  crostini_manager->AddRunningContainerForTesting(
      crostini::kCrostiniDefaultVmName,
      crostini::ContainerInfo(crostini::kCrostiniDefaultContainerName,
                              "testuser", "/home/testuser", "PLACEHOLDER_IP",
                              1234));

  ExpectCrostiniMount();

  // Add 'testing' volume with 'test_dir', create 'share_dir' in Downloads.
  AddLocalFileSystem(browser()->GetProfile(), temp_dir_.GetPath());
  base::FilePath downloads;
  ASSERT_TRUE(
      storage::ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(
          file_manager::util::GetDownloadsMountPointName(browser()->GetProfile()),
          &downloads));
  // Setup prefs guest_os.paths_shared_to_vms.
  base::FilePath shared1 = downloads.AppendASCII("shared1");
  base::FilePath shared2 = downloads.AppendASCII("shared2");
  {
    base::ScopedAllowBlockingForTesting allow_io;
    ASSERT_TRUE(base::CreateDirectory(downloads.AppendASCII("share_dir")));
    ASSERT_TRUE(base::CreateDirectory(shared1));
    ASSERT_TRUE(base::CreateDirectory(shared2));
  }
  guest_os::GuestOsSharePath* guest_os_share_path =
      guest_os::GuestOsSharePathFactory::GetForProfile(browser()->GetProfile());
  guest_os_share_path->RegisterPersistedPaths(crostini::kCrostiniDefaultVmName,
                                              {shared1});
  guest_os_share_path->RegisterPersistedPaths(crostini::kCrostiniDefaultVmName,
                                              {shared2});

  ASSERT_TRUE(RunExtensionTest("file_browser/crostini_test", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, CrostiniIncognito) {
  crostini::FakeCrostiniFeatures crostini_features;
  crostini_features.set_is_allowed_now(true);
  crostini_features.set_enabled(true);

  // Setup CrostiniManager for testing.
  crostini::CrostiniManager* crostini_manager =
      crostini::CrostiniManager::GetForProfile(browser()->GetProfile());
  crostini_manager->set_skip_restart_for_testing();
  crostini_manager->AddRunningVmForTesting(crostini::kCrostiniDefaultVmName, 3);
  crostini_manager->AddRunningContainerForTesting(
      crostini::kCrostiniDefaultVmName,
      crostini::ContainerInfo(crostini::kCrostiniDefaultContainerName,
                              "testuser", "/home/testuser", "PLACEHOLDER_IP",
                              1234));

  ExpectCrostiniMount();

  scoped_refptr<extensions::FileManagerPrivateMountCrostiniFunction> function(
      new extensions::FileManagerPrivateMountCrostiniFunction());
  // Use incognito profile.
  function->SetBrowserContextForTesting(
      browser()->GetProfile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));

  extensions::api_test_utils::SendResponseHelper response_helper(
      function.get());
  function->RunWithValidation().Execute();
  response_helper.WaitForResponse();
  EXPECT_TRUE(response_helper.GetResponse());
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, HoldingSpace) {
  const base::FilePath test_dir = temp_dir_.GetPath();
  AddLocalFileSystem(browser()->GetProfile(), test_dir);

  {
    base::ScopedAllowBlockingForTesting allow_io;
    base::File image_file(test_dir.Append("test_image.jpg"),
                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(image_file.IsValid());
    base::File audio_file(test_dir.Append("test_audio.mp3"),
                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(audio_file.IsValid());
    base::File video_file(test_dir.Append("test_video.mp4"),
                          base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(video_file.IsValid());
  }

  EXPECT_TRUE(RunExtensionTest("file_browser/holding_space", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, GetVolumeRoot) {
  AddLocalFileSystem(browser()->GetProfile(), temp_dir_.GetPath());

  ASSERT_TRUE(RunExtensionTest("file_browser/get_volume_root", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, SearchFiles) {
  const base::FilePath downloads_dir = temp_dir_.GetPath();
  ASSERT_TRUE(file_manager::VolumeManager::Get(browser()->GetProfile())
                  ->RegisterDownloadsDirectoryForTesting(downloads_dir));

  {
    base::ScopedAllowBlockingForTesting allow_io;
    // Creates two files with the same prefix, in different locations.
    base::File root_image_file(
        downloads_dir.Append("foo.jpg"),
        base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(root_image_file.IsValid());

    ASSERT_TRUE(base::CreateDirectory(downloads_dir.AppendASCII("images")));
    base::File nested_image_file(
        downloads_dir.Append("images").Append("foo.jpg"),
        base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(nested_image_file.IsValid());

    // Creates two files with the same prefix, and different modified dates.
    base::File jan_15_file(downloads_dir.Append("bar_15012020.jpg"),
                           base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(jan_15_file.IsValid());
    static constexpr base::Time::Exploded kJan152020Noon = {.year = 2020,
                                                            .month = 1,
                                                            .day_of_week = 3,
                                                            .day_of_month = 15,
                                                            .hour = 12};
    base::Time jan_15_2020_noon;
    ASSERT_TRUE(base::Time::FromUTCExploded(kJan152020Noon, &jan_15_2020_noon));
    jan_15_file.SetTimes(jan_15_2020_noon, jan_15_2020_noon);

    base::File jan_01_file(downloads_dir.Append("bar_01012020.jpg"),
                           base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(jan_01_file.IsValid());
    static constexpr base::Time::Exploded kJan012020Noon = {.year = 2020,
                                                            .month = 1,
                                                            .day_of_week = 3,
                                                            .day_of_month = 1,
                                                            .hour = 12};
    base::Time jan_01_2020_noon;
    ASSERT_TRUE(base::Time::FromUTCExploded(kJan012020Noon, &jan_01_2020_noon));
    jan_01_file.SetTimes(jan_01_2020_noon, jan_01_2020_noon);
  }

  ASSERT_TRUE(RunExtensionTest("file_browser/search_files", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, GetPdfThumbnail) {
  AddLocalFileSystem(browser()->GetProfile(), temp_dir_.GetPath());
  base::FilePath downloads;
  ASSERT_TRUE(
      storage::ExternalMountPoints::GetSystemInstance()->GetRegisteredPath(
          file_manager::util::GetDownloadsMountPointName(browser()->GetProfile()),
          &downloads));

  {
    base::ScopedAllowBlockingForTesting allow_io;
    base::FilePath source_dir =
        base::PathService::CheckedGet(chrome::DIR_TEST_DATA).AppendASCII("pdf");
    ASSERT_TRUE(base::CopyFile(source_dir.AppendASCII("test.pdf"),
                               downloads.AppendASCII("test.pdf")));
    ASSERT_TRUE(base::CopyFile(source_dir.AppendASCII("combobox_form.pdf"),
                               downloads.AppendASCII("combobox_form.pdf")));
  }

  EXPECT_TRUE(RunExtensionTest("image_loader_private/get_pdf_thumbnail",
                               /*run_options=*/{},
                               /*load_options=*/{.load_as_component = true}));
}

class FileManagerPrivateApiDlpTest : public FileManagerPrivateApiTest {
 public:
  FileManagerPrivateApiDlpTest() {
    scoped_feature_list_.InitAndEnableFeature(
        ash::features::kDataLeakPreventionFilesRestriction);
  }

  FileManagerPrivateApiDlpTest(const FileManagerPrivateApiDlpTest&) = delete;
  void operator=(const FileManagerPrivateApiDlpTest&) = delete;

  ~FileManagerPrivateApiDlpTest() override = default;

  void SetUpOnMainThread() override {
    FileManagerPrivateApiTest::SetUpOnMainThread();
    ASSERT_TRUE(drive_path_.CreateUniqueTempDir());
  }

  void TearDownOnMainThread() override {
    // The files controller must be destroyed before the profile since it's
    // holding a pointer to it.
    files_controller_.reset();
    mock_rules_manager_ = nullptr;
    fpnm_ = nullptr;
    FileManagerPrivateApiTest::TearDownOnMainThread();
  }

  std::unique_ptr<KeyedService> SetDlpRulesManager(
      content::BrowserContext* context) {
    auto dlp_rules_manager = std::make_unique<policy::MockDlpRulesManager>(
        Profile::FromBrowserContext(context));
    mock_rules_manager_ = dlp_rules_manager.get();
    ON_CALL(*mock_rules_manager_, IsFilesPolicyEnabled)
        .WillByDefault(testing::Return(true));

    files_controller_ = std::make_unique<policy::DlpFilesControllerAsh>(
        *mock_rules_manager_, Profile::FromBrowserContext(context));
    ON_CALL(*mock_rules_manager_, GetDlpFilesController)
        .WillByDefault(testing::Return(files_controller_.get()));

    return dlp_rules_manager;
  }

  std::unique_ptr<KeyedService> SetFPNM(content::BrowserContext* context) {
    auto fpnm = std::make_unique<policy::MockFilesPolicyNotificationManager>(
        browser()->GetProfile());
    fpnm_ = fpnm.get();
    return fpnm;
  }

 protected:
  base::ScopedTempDir drive_path_;
  raw_ptr<policy::MockDlpRulesManager, DanglingUntriaged> mock_rules_manager_ =
      nullptr;
  std::unique_ptr<policy::DlpFilesControllerAsh> files_controller_;
  raw_ptr<policy::MockFilesPolicyNotificationManager, DanglingUntriaged> fpnm_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

class FileManagerPrivateApiDlpOldUXTest : public FileManagerPrivateApiDlpTest {
 protected:
  FileManagerPrivateApiDlpOldUXTest() {
    scoped_feature_list_.Reset();
    scoped_feature_list_.InitWithFeatures(
        /*enabled_features=*/{ash::features::
                                  kDataLeakPreventionFilesRestriction},
        /*disabled_features=*/{ash::features::kNewFilesPolicyUX});
  }

  FileManagerPrivateApiDlpOldUXTest(const FileManagerPrivateApiDlpOldUXTest&) =
      delete;
  void operator=(const FileManagerPrivateApiDlpOldUXTest&) = delete;

  ~FileManagerPrivateApiDlpOldUXTest() override = default;
};

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpOldUXTest, DlpBlockCopy) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());

  base::FilePath my_files_dir_ =
      file_manager::util::GetMyFilesFolderForProfile(browser()->GetProfile());
  {
    base::ScopedAllowBlockingForTesting allow_io;

    ASSERT_TRUE(base::CreateDirectory(my_files_dir_));
  }
  AddLocalFileSystem(browser()->GetProfile(), my_files_dir_);

  const char kTestFileName[] = "dlp_test_file.txt";
  const base::FilePath test_file_path = my_files_dir_.Append(kTestFileName);

  {
    base::ScopedAllowBlockingForTesting allow_io;
    base::File test_file(test_file_path,
                         base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(test_file.IsValid());

    ASSERT_TRUE(base::CreateDirectory(drive_path_.GetPath().Append("subdir")));
  }

  auto* file_system_context =
      file_manager::util::GetFileManagerFileSystemContext(browser()->GetProfile());
  ash::FileSystemBackend::Get(*file_system_context)
      ->GrantFileAccessToOrigin(
          url::Origin::Create(
              GURL("chrome-extension://"
                   "pkplfbidichfdicaijlchgnapepdginl/")),  // Testing
                                                           // extension
          base::FilePath(
              base::StrCat({kLocalMountPointName, "/", kTestFileName})));

  storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
      "drivefs-delayed_mount_2", storage::kFileSystemTypeDriveFs,
      storage::FileSystemMountOption(), drive_path_.GetPath());
  file_manager::VolumeManager::Get(browser()->GetProfile())
      ->AddVolumeForTesting(  // IN-TEST
          drive_path_.GetPath(), file_manager::VOLUME_TYPE_GOOGLE_DRIVE,
          ash::DeviceType::kUnknown,
          /*read_only=*/false);

  dlp::CheckFilesTransferResponse check_files_response;
  check_files_response.add_files_paths(test_file_path.value());
  chromeos::DlpClient::Get()->GetTestInterface()->SetCheckFilesTransferResponse(
      std::move(check_files_response));

  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled)
      .Times(testing::AnyNumber());
  EXPECT_CALL(*mock_rules_manager_, GetDlpFilesController)
      .Times(testing::AnyNumber());

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_block", {},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest, DlpMetadata) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled).Times(1);

  base::FilePath my_files_dir_ =
      file_manager::util::GetMyFilesFolderForProfile(browser()->GetProfile());
  {
    base::ScopedAllowBlockingForTesting allow_io;

    ASSERT_TRUE(base::CreateDirectory(my_files_dir_));
  }
  AddLocalFileSystem(browser()->GetProfile(), my_files_dir_);

  const base::FilePath blocked_file_path =
      my_files_dir_.Append("blocked_file.txt");
  const base::FilePath unrestricted_file_path =
      my_files_dir_.Append("unrestricted_file.txt");
  const base::FilePath untracked_file_path =
      my_files_dir_.Append("untracked_file.txt");

  {
    base::ScopedAllowBlockingForTesting allow_io;
    base::File blocked_test_file(
        blocked_file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(blocked_test_file.IsValid());

    base::File unrestricted_test_file(
        unrestricted_file_path,
        base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(unrestricted_test_file.IsValid());

    base::File untracked_test_file(
        untracked_file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(untracked_test_file.IsValid());
  }

  base::MockCallback<chromeos::DlpClient::AddFilesCallback> add_files_cb;
  EXPECT_CALL(add_files_cb, Run).Times(1);
  dlp::AddFilesRequest request;
  dlp::AddFileRequest* file_request1 = request.add_add_file_requests();
  file_request1->set_file_path(blocked_file_path.value());
  file_request1->set_source_url("https://example1.com");
  dlp::AddFileRequest* file_request2 = request.add_add_file_requests();
  file_request2->set_file_path(unrestricted_file_path.value());
  file_request2->set_source_url("https://example2.com");
  chromeos::DlpClient::Get()->AddFiles(request, add_files_cb.Get());

  EXPECT_CALL(*mock_rules_manager_, IsRestrictedByAnyRule)
      .WillOnce(testing::Return(policy::DlpRulesManager::Level::kBlock))
      .WillOnce(testing::Return(policy::DlpRulesManager::Level::kAllow))
      .RetiresOnSaturation();

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "default"},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest, DlpRestrictionDetails) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled).Times(1);

  policy::DlpRulesManager::AggregatedDestinations destinations;
  destinations[policy::DlpRulesManager::Level::kBlock].insert(
      "https://external.com");
  destinations[policy::DlpRulesManager::Level::kAllow].insert(
      "https://internal.com");
  policy::DlpRulesManager::AggregatedComponents components;
  components[policy::DlpRulesManager::Level::kBlock].insert(
      data_controls::Component::kArc);
  components[policy::DlpRulesManager::Level::kBlock].insert(
      data_controls::Component::kCrostini);
  components[policy::DlpRulesManager::Level::kBlock].insert(
      data_controls::Component::kPluginVm);
  components[policy::DlpRulesManager::Level::kBlock].insert(
      data_controls::Component::kUsb);
  components[policy::DlpRulesManager::Level::kAllow].insert(
      data_controls::Component::kDrive);
  EXPECT_CALL(*mock_rules_manager_, GetAggregatedDestinations)
      .WillOnce(testing::Return(destinations));
  EXPECT_CALL(*mock_rules_manager_, GetAggregatedComponents)
      .WillOnce(testing::Return(components));

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "restriction_details"},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest, DlpBlockedComponents) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled).Times(1);

  policy::DlpRulesManager::AggregatedComponents components;
  components[policy::DlpRulesManager::Level::kBlock].insert(
      data_controls::Component::kArc);
  components[policy::DlpRulesManager::Level::kBlock].insert(
      data_controls::Component::kCrostini);
  components[policy::DlpRulesManager::Level::kBlock].insert(
      data_controls::Component::kPluginVm);
  components[policy::DlpRulesManager::Level::kBlock].insert(
      data_controls::Component::kUsb);
  components[policy::DlpRulesManager::Level::kAllow].insert(
      data_controls::Component::kDrive);
  EXPECT_CALL(*mock_rules_manager_, GetAggregatedComponents)
      .WillOnce(testing::Return(components));

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "blocked_components"},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest, DlpMetadata_Disabled) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
  ON_CALL(*mock_rules_manager_, IsFilesPolicyEnabled)
      .WillByDefault(testing::Return(false));
  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled).Times(3);
  // We should not get to the point of checking DLP.
  EXPECT_CALL(*mock_rules_manager_, IsRestrictedByAnyRule).Times(0);
  EXPECT_CALL(*mock_rules_manager_, GetAggregatedComponents).Times(0);
  EXPECT_CALL(*mock_rules_manager_, GetAggregatedDestinations).Times(0);

  AddLocalFileSystem(browser()->GetProfile(), temp_dir_.GetPath());

  const base::FilePath blocked_file_path =
      temp_dir_.GetPath().Append("blocked_file.txt");

  {
    base::ScopedAllowBlockingForTesting allow_io;
    base::File blocked_test_file(
        blocked_file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(blocked_test_file.IsValid());
  }

  base::MockCallback<chromeos::DlpClient::AddFilesCallback> add_files_cb;
  EXPECT_CALL(add_files_cb, Run).Times(1);
  dlp::AddFilesRequest request;
  dlp::AddFileRequest* file_request = request.add_add_file_requests();
  file_request->set_file_path(blocked_file_path.value());
  file_request->set_source_url("https://example1.com");
  chromeos::DlpClient::Get()->AddFiles(request, add_files_cb.Get());

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "disabled"},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest, DlpMetadata_Error) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled).Times(2);
  // We should not get to the point of checking DLP.
  EXPECT_CALL(*mock_rules_manager_, IsRestrictedByAnyRule).Times(0);

  AddLocalFileSystem(browser()->GetProfile(), temp_dir_.GetPath());

  const base::FilePath blocked_file_path =
      temp_dir_.GetPath().Append("blocked_file.txt");

  {
    base::ScopedAllowBlockingForTesting allow_io;
    base::File blocked_test_file(
        blocked_file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
    ASSERT_TRUE(blocked_test_file.IsValid());
  }

  base::MockCallback<chromeos::DlpClient::AddFilesCallback> add_files_cb;
  EXPECT_CALL(add_files_cb, Run).Times(1);
  dlp::AddFilesRequest request;
  dlp::AddFileRequest* file_request = request.add_add_file_requests();
  file_request->set_file_path(blocked_file_path.value());
  file_request->set_source_url("https://example1.com");
  chromeos::DlpClient::Get()->AddFiles(request, add_files_cb.Get());

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "error"},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest,
                       DlpMetadata_DismissIOTask) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled).Times(0);
  // We should not get to the point of checking DLP.
  EXPECT_CALL(*mock_rules_manager_, IsRestrictedByAnyRule).Times(0);

  policy::FilesPolicyNotificationManagerFactory::GetInstance()
      ->SetTestingFactory(
          browser()->GetProfile(),
          base::BindRepeating(&FileManagerPrivateApiDlpTest::SetFPNM,
                              base::Unretained(this)));
  // FPNM is created lazily so initialize it before running the test.
  ASSERT_TRUE(
      policy::FilesPolicyNotificationManagerFactory::GetForBrowserContext(
          browser()->GetProfile()));
  // Expect only the valid task id.
  EXPECT_CALL(*fpnm_, OnErrorItemDismissed(1u));

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "dismissIOTask"},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest,
                       DlpMetadata_ProgressPausedTasks) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled).Times(0);
  // We should not get to the point of checking DLP.
  EXPECT_CALL(*mock_rules_manager_, IsRestrictedByAnyRule).Times(0);

  std::vector<storage::FileSystemURL> source_urls{
      storage::FileSystemURL::CreateForTest(
          GURL("filesystem:chrome-extension://abc/external/foo/src")),
  };
  auto dest = storage::FileSystemURL::CreateForTest(
      GURL("filesystem:chrome-extension://abc/external/foo/dest"));

  file_manager::VolumeManager* const volume_manager =
      file_manager::VolumeManager::Get(browser()->GetProfile());
  ASSERT_TRUE(volume_manager);
  file_manager::io_task::IOTaskController* io_task_controller =
      volume_manager->io_task_controller();
  ASSERT_TRUE(io_task_controller);

  // Create and pause the task.
  auto task_id = io_task_controller->Add(
      std::make_unique<file_manager::io_task::DummyIOTask>(
          source_urls, dest, file_manager::io_task::OperationType::kCopy,
          /*show_notification=*/true, /*progress_succeeds=*/false));
  file_manager::io_task::PauseParams pause_params;
  pause_params.policy_params =
      file_manager::io_task::PolicyPauseParams(policy::Policy::kDlp);
  io_task_controller->Pause(task_id, pause_params);

  // Set up FPNM, but only after creating and pausing the task so that it would
  // only get notified by the API call.
  policy::FilesPolicyNotificationManagerFactory::GetInstance()
      ->SetTestingFactory(
          browser()->GetProfile(),
          base::BindRepeating(&FileManagerPrivateApiDlpTest::SetFPNM,
                              base::Unretained(this)));
  // FPNM is created lazily so initialize it before running the test.
  ASSERT_TRUE(
      policy::FilesPolicyNotificationManagerFactory::GetForBrowserContext(
          browser()->GetProfile()));
  // Expect the pause status from ProgressPausedTasks.
  EXPECT_CALL(
      *fpnm_,
      OnIOTaskStatus(AllOf(
          testing::Field(&file_manager::io_task::ProgressStatus::task_id,
                         task_id),
          testing::Field(&file_manager::io_task::ProgressStatus::state,
                         file_manager::io_task::State::kPaused),
          testing::Field(&file_manager::io_task::ProgressStatus::pause_params,
                         pause_params))));
  // FPNM should also get notified to show the notification by this call.
  EXPECT_CALL(
      *fpnm_,
      ShowFilesPolicyNotification(
          "swa-file-operation-1",
          AllOf(testing::Field(&file_manager::io_task::ProgressStatus::task_id,
                               task_id),
                testing::Field(&file_manager::io_task::ProgressStatus::state,
                               file_manager::io_task::State::kPaused),
                testing::Field(
                    &file_manager::io_task::ProgressStatus::pause_params,
                    pause_params))));

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "progressPausedTasks"},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest,
                       DlpMetadata_ShowPolicyDialog) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());
  EXPECT_CALL(*mock_rules_manager_, IsFilesPolicyEnabled).Times(0);
  // We should not get to the point of checking DLP.
  EXPECT_CALL(*mock_rules_manager_, IsRestrictedByAnyRule).Times(0);

  // Set up FPNM.
  policy::FilesPolicyNotificationManagerFactory::GetInstance()
      ->SetTestingFactory(
          browser()->GetProfile(),
          base::BindRepeating(&FileManagerPrivateApiDlpTest::SetFPNM,
                              base::Unretained(this)));
  // FPNM is created lazily so initialize it before running the test.
  ASSERT_TRUE(
      policy::FilesPolicyNotificationManagerFactory::GetForBrowserContext(
          browser()->GetProfile()));

  // Expect only the calls with valid parameters.
  testing::InSequence s;
  EXPECT_CALL(*fpnm_, ShowDialog(1u, policy::FilesDialogType::kWarning));
  EXPECT_CALL(*fpnm_, ShowDialog(2u, policy::FilesDialogType::kError));

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "showPolicyDialog"},
                               {.load_as_component = true}));
}

IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiDlpTest,
                       DlpMetadata_GetDialogCaller) {
  policy::DlpRulesManagerFactory::GetInstance()->SetTestingFactory(
      browser()->GetProfile(),
      base::BindRepeating(&FileManagerPrivateApiDlpTest::SetDlpRulesManager,
                          base::Unretained(this)));
  ASSERT_TRUE(policy::DlpRulesManagerFactory::GetForPrimaryProfile());

  policy::DlpFileDestination caller(GURL("https://example.com"));
  SelectFileDialogExtensionUserData::SetDialogCallerForTesting(&caller);

  EXPECT_TRUE(RunExtensionTest("file_browser/dlp_metadata",
                               {.custom_arg = "getDialogCaller"},
                               {.load_as_component = true}));
}
