chromium/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler_browsertest.cc

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

#include "chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.h"

#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/file_manager/file_manager_test_util.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/ash/file_manager/io_task_controller.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/file_manager/volume_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/drive/file_errors.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/network_connection_change_simulator.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/network_change_notifier.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_system_url.h"
#include "testing/gmock/include/gmock/gmock.h"

using storage::FileSystemURL;

namespace ash::cloud_upload {

using ::base::test::RunClosure;
using ::base::test::RunOnceCallback;
using drive::DriveIntegrationService;
using drive::util::ConnectionStatus;
using drive::util::SetDriveConnectionStatusForTesting;
using testing::_;

namespace {

// Returns full test file path to the given |file_name|.
base::FilePath GetTestFilePath(const std::string& file_name) {
  // Get the path to file manager's test data directory.
  base::FilePath source_dir;
  CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_dir));
  base::FilePath test_data_dir = source_dir.AppendASCII("chrome")
                                     .AppendASCII("test")
                                     .AppendASCII("data")
                                     .AppendASCII("chromeos")
                                     .AppendASCII("file_manager");
  return test_data_dir.Append(base::FilePath::FromUTF8Unsafe(file_name));
}

}  // namespace

// Tests the Drive upload workflow using the static `DriveUploadHandler::Upload`
// method. Ensures that the upload completes with the expected results.
class DriveUploadHandlerTest
    : public InProcessBrowserTest,
      public file_manager::io_task::IOTaskController::Observer {
 public:
  DriveUploadHandlerTest() {
    feature_list_.InitAndEnableFeature(
        chromeos::features::kUploadOfficeToCloud);
    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
    drive_mount_point_ = temp_dir_.GetPath().Append("drivefs");
    drive_root_dir_ = drive_mount_point_.AppendASCII("root");
    my_files_dir_ = temp_dir_.GetPath().Append("myfiles");
    read_only_dir_ = temp_dir_.GetPath().Append("readonly");

    net::NetworkChangeNotifier::SetTestNotificationsOnly(true);
  }

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

  void SetUpInProcessBrowserTestFixture() override {
    // Setup drive integration service.
    create_drive_integration_service_ = base::BindRepeating(
        &DriveUploadHandlerTest::CreateDriveIntegrationService,
        base::Unretained(this));
    service_factory_for_test_ = std::make_unique<
        drive::DriveIntegrationServiceFactory::ScopedFactoryForTest>(
        &create_drive_integration_service_);
  }

  void SetUpOnMainThread() override {
    SetDriveConnectionStatusForTesting(ConnectionStatus::kConnected);
    InProcessBrowserTest::SetUpOnMainThread();
  }

  void TearDown() override {
    InProcessBrowserTest::TearDown();
    storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems();
  }

  void TearDownOnMainThread() override {
    RemoveObservers();
    InProcessBrowserTest::TearDownOnMainThread();
  }

  DriveIntegrationService* CreateDriveIntegrationService(Profile* profile) {
    base::ScopedAllowBlockingForTesting allow_blocking;
    fake_drivefs_helpers_[profile] =
        std::make_unique<file_manager::test::FakeSimpleDriveFsHelper>(
            profile, drive_mount_point_);
    return new DriveIntegrationService(
        profile, "", drive_mount_point_,
        fake_drivefs_helpers_[profile]->CreateFakeDriveFsListenerFactory());
  }

  // Creates mount point for MyFiles and registers local filesystem.
  void SetUpMyFiles() {
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(base::CreateDirectory(my_files_dir_));
    }
    std::string mount_point_name =
        file_manager::util::GetDownloadsMountPointName(profile());
    storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
        mount_point_name);
    CHECK(storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
        mount_point_name, storage::kFileSystemTypeLocal,
        storage::FileSystemMountOption(), my_files_dir_));
    file_manager::VolumeManager::Get(profile())
        ->RegisterDownloadsDirectoryForTesting(my_files_dir_);
  }

  // Creates a new filesystem which represents a read-only location, files
  // cannot be moved from it.
  void SetUpReadOnlyLocation() {
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      ASSERT_TRUE(base::CreateDirectory(read_only_dir_));
    }
    std::string mount_point_name = "readonly";
    storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
        mount_point_name);
    EXPECT_TRUE(profile()->GetMountPoints()->RegisterFileSystem(
        mount_point_name, storage::kFileSystemTypeLocal,
        storage::FileSystemMountOption(), read_only_dir_));
    file_manager::VolumeManager::Get(profile())->AddVolumeForTesting(
        read_only_dir_, file_manager::VOLUME_TYPE_TESTING,
        ash::DeviceType::kUnknown, /*read_only=*/true);
  }

  void SetUpDrive() {
    // Create Drive root directory.
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      EXPECT_TRUE(base::CreateDirectory(drive_root_dir_));
    }
  }

  // Create and add a file with |test_file_name| to the file system
  // |source_path|. Return the created |source_file_url|.
  FileSystemURL SetUpSourceFile(const std::string& test_file_name,
                                base::FilePath source_path) {
    test_file_name_ = test_file_name;
    source_file_path_ = source_path.AppendASCII(test_file_name_);
    const base::FilePath test_file_path = GetTestFilePath(test_file_name_);
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      CHECK(base::CopyFile(test_file_path, source_file_path_));
    }

    // Check that the source file exists at the intended source location and is
    // not in Drive.
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      EXPECT_TRUE(base::PathExists(source_path.AppendASCII(test_file_name)));
      CheckPathNotFoundOnDrive(observed_relative_drive_path());
    }

    FileSystemURL source_file_url = FilePathToFileSystemURL(
        profile(),
        file_manager::util::GetFileManagerFileSystemContext(profile()),
        source_file_path_);

    return source_file_url;
  }

  void SetUpObservers() {
    // Subscribe to IOTasks updates to track the copy/move to Drive progress.
    file_manager::VolumeManager::Get(profile())
        ->io_task_controller()
        ->AddObserver(this);
  }

  void RemoveObservers() {
    file_manager::VolumeManager::Get(profile())
        ->io_task_controller()
        ->RemoveObserver(this);
  }

  // Resolves once the `OnUploadDone` callback is called with a valid URL, which
  // indicates the successful completion of the upload flow.
  void Wait() {
    base::ScopedAllowBlockingForTesting allow_blocking;
    ASSERT_FALSE(run_loop_);
    run_loop_ = std::make_unique<base::RunLoop>();
    run_loop_->Run();
    run_loop_ = nullptr;
  }

  void EndWait() {
    ASSERT_TRUE(run_loop_);
    run_loop_->Quit();
  }

  void CheckPathExistsOnDrive(const base::FilePath& path) {
    drive_integration_service()->GetDriveFsInterface()->GetMetadata(
        path,
        base::BindOnce(&DriveUploadHandlerTest::OnGetMetadataExpectSuccess,
                       base::Unretained(this)));
    base::ScopedAllowBlockingForTesting allow_blocking;
    Wait();
  }

  void CheckPathNotFoundOnDrive(const base::FilePath& path) {
    drive_integration_service()->GetDriveFsInterface()->GetMetadata(
        path,
        base::BindOnce(&DriveUploadHandlerTest::OnGetMetadataExpectNotFound,
                       base::Unretained(this)));
    base::ScopedAllowBlockingForTesting allow_blocking;
    Wait();
  }

  void OnGetMetadataExpectSuccess(drive::FileError error,
                                  drivefs::mojom::FileMetadataPtr metadata) {
    EXPECT_EQ(drive::FILE_ERROR_OK, error);
    EndWait();
  }

  void OnGetMetadataExpectNotFound(drive::FileError error,
                                   drivefs::mojom::FileMetadataPtr metadata) {
    EXPECT_EQ(drive::FILE_ERROR_NOT_FOUND, error);
    EndWait();
  }

  // `Wait` will not complete until this is called.
  void OnUploadDone(OfficeTaskResult task_result,
                    std::optional<GURL> url,
                    int64_t size) {
    if (fail_sync_) {
      ASSERT_FALSE(url);
    } else {
      ASSERT_TRUE(url);
    }
    observed_task_result_ = task_result;
    EndWait();
  }

  Profile* profile() { return browser()->profile(); }

  const base::FilePath source_file_path() { return source_file_path_; }

  mojo::Remote<drivefs::mojom::DriveFsDelegate>& drivefs_delegate() {
    return fake_drivefs().delegate();
  }

  DriveIntegrationService* drive_integration_service() {
    return drive::DriveIntegrationServiceFactory::FindForProfile(profile());
  }

  base::FilePath observed_relative_drive_path() {
    base::FilePath observed_relative_drive_path;
    drive_integration_service()->GetRelativeDrivePath(
        drive_root_dir_.AppendASCII(test_file_name_),
        &observed_relative_drive_path);
    return observed_relative_drive_path;
  }

 protected:
  file_manager::test::FakeSimpleDriveFs& fake_drivefs() {
    return fake_drivefs_helpers_[profile()]->fake_drivefs();
  }

  base::FilePath my_files_dir_;
  base::FilePath read_only_dir_;
  base::FilePath drive_mount_point_;
  base::FilePath drive_root_dir_;

  bool fail_sync_ = false;
  // Overrides `fail_sync_`
  base::RepeatingClosure on_transfer_complete_callback_;

  std::unique_ptr<ash::cloud_upload::CloudOpenMetrics> cloud_open_metrics_ =
      std::make_unique<CloudOpenMetrics>(CloudProvider::kGoogleDrive,
                                         /*file_count=*/1);
  base::SafeRef<CloudOpenMetrics> cloud_open_metrics_ref_ =
      cloud_open_metrics_->GetSafeRef();

  OfficeTaskResult observed_task_result_;
  base::HistogramTester histogram_;

 private:
  // IOTaskController::Observer:
  void OnIOTaskStatus(
      const file_manager::io_task::ProgressStatus& status) override {
    // Wait for the copy task to complete before starting the Drive sync.
    if (status.type == file_manager::io_task::OperationType::kCopy &&
        status.sources.size() == 1 &&
        status.sources[0].url.path() == source_file_path_ &&
        status.state == file_manager::io_task::State::kSuccess) {
      if (on_transfer_complete_callback_) {
        on_transfer_complete_callback_.Run();
      } else if (fail_sync_) {
        SimulateDriveUploadFailure();
      } else {
        SimulateDriveUploadEvents();
      }
    }
  }

  // Simulates the upload of the file to Drive by sending a series of fake
  // signals to the DriveFs delegate.
  void SimulateDriveUploadEvents() {
    // Set file metadata for `drivefs::mojom::DriveFs::GetMetadata`.
    drivefs::FakeMetadata metadata;
    metadata.path = observed_relative_drive_path();
    metadata.mime_type =
        "application/"
        "vnd.openxmlformats-officedocument.wordprocessingml.document";
    metadata.original_name = test_file_name_;
    metadata.doc_id = "abc123";
    metadata.alternate_url =
        "https://docs.google.com/document/d/"
        "smalldocxid?rtpof=true&usp=drive_fs";
    fake_drivefs().SetMetadata(std::move(metadata));

    // Simulate server sync events.
    drivefs::mojom::SyncingStatusPtr status =
        drivefs::mojom::SyncingStatus::New();
    status->item_events.emplace_back(
        std::in_place, 12, 34, observed_relative_drive_path().value(),
        drivefs::mojom::ItemEvent::State::kQueued, 123, 456,
        drivefs::mojom::ItemEventReason::kTransfer);
    drivefs_delegate()->OnSyncingStatusUpdate(status.Clone());
    drivefs_delegate().FlushForTesting();

    status = drivefs::mojom::SyncingStatus::New();
    status->item_events.emplace_back(
        std::in_place, 12, 34, observed_relative_drive_path().value(),
        drivefs::mojom::ItemEvent::State::kCompleted, 123, 456,
        drivefs::mojom::ItemEventReason::kTransfer);
    drivefs_delegate()->OnSyncingStatusUpdate(status.Clone());
    drivefs_delegate().FlushForTesting();
  }

  void SimulateDriveUploadFailure() {
    // Simulate server sync events.
    drivefs::mojom::SyncingStatusPtr status =
        drivefs::mojom::SyncingStatus::New();
    status->item_events.emplace_back(
        std::in_place, 12, 34, observed_relative_drive_path().value(),
        drivefs::mojom::ItemEvent::State::kQueued, 123, 456,
        drivefs::mojom::ItemEventReason::kTransfer);
    drivefs_delegate()->OnSyncingStatusUpdate(status.Clone());
    drivefs_delegate().FlushForTesting();

    drivefs::mojom::SyncingStatusPtr fail_status =
        drivefs::mojom::SyncingStatus::New();
    fail_status->item_events.emplace_back(
        std::in_place, 12, 34, observed_relative_drive_path().value(),
        drivefs::mojom::ItemEvent::State::kFailed, 123, 456,
        drivefs::mojom::ItemEventReason::kTransfer);
    drivefs_delegate()->OnSyncingStatusUpdate(fail_status->Clone());
    drivefs_delegate().FlushForTesting();
  }

  base::test::ScopedFeatureList feature_list_;
  base::ScopedTempDir temp_dir_;
  std::unique_ptr<base::RunLoop> run_loop_;

  drive::DriveIntegrationServiceFactory::FactoryCallback
      create_drive_integration_service_;
  std::unique_ptr<drive::DriveIntegrationServiceFactory::ScopedFactoryForTest>
      service_factory_for_test_;
  std::map<Profile*,
           std::unique_ptr<file_manager::test::FakeSimpleDriveFsHelper>>
      fake_drivefs_helpers_;

  // Used to track the upload progress during the tests.
  std::string test_file_name_;
  base::FilePath source_file_path_;
};

IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest, UploadFromMyFiles) {
  SetUpObservers();
  SetUpMyFiles();
  SetUpDrive();
  // Define the source file as a test docx file within MyFiles.
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, my_files_dir_);

  EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _))
      .WillOnce(RunOnceCallback<1>(drive::FileError::FILE_ERROR_OK));

  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url,
      base::BindOnce(&DriveUploadHandlerTest::OnUploadDone,
                     base::Unretained(this)),
      cloud_open_metrics_ref_);
  drive_upload_handler->Run();
  Wait();

  // Check that the source file has been moved to Drive.
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    EXPECT_FALSE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
    CheckPathExistsOnDrive(observed_relative_drive_path());
  }

  histogram_.ExpectUniqueSample(kGoogleDriveUploadResultMetricName,
                                OfficeFilesUploadResult::kSuccess, 1);
  ASSERT_EQ(observed_task_result_, OfficeTaskResult::kMoved);
}

IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest, UploadFromReadOnlyFileSystem) {
  SetUpObservers();
  SetUpReadOnlyLocation();
  SetUpDrive();
  // Define the source file as a test docx file within MyFiles.
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, read_only_dir_);

  EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _))
      .WillOnce(RunOnceCallback<1>(drive::FileError::FILE_ERROR_OK));

  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url,
      base::BindOnce(&DriveUploadHandlerTest::OnUploadDone,
                     base::Unretained(this)),
      cloud_open_metrics_ref_);
  drive_upload_handler->Run();
  Wait();

  // Check that the source file has been copied to Drive.
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    EXPECT_TRUE(base::PathExists(read_only_dir_.AppendASCII(test_file_name)));
    CheckPathExistsOnDrive(observed_relative_drive_path());
  }

  histogram_.ExpectUniqueSample(kGoogleDriveUploadResultMetricName,
                                OfficeFilesUploadResult::kSuccess, 1);
  ASSERT_EQ(observed_task_result_, OfficeTaskResult::kCopied);
}

// Test that when the sync to Drive fails, the file is not moved to Drive.
IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest, UploadFails) {
  fail_sync_ = true;
  SetUpObservers();
  SetUpMyFiles();
  SetUpDrive();
  // Define the source file as a test docx file within MyFiles.
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, my_files_dir_);

  EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _))
      .WillOnce(RunOnceCallback<1>(drive::FileError::FILE_ERROR_FAILED));

  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url,
      base::BindOnce(&DriveUploadHandlerTest::OnUploadDone,
                     base::Unretained(this)),
      cloud_open_metrics_ref_);
  drive_upload_handler->Run();
  Wait();

  // Check that the source file has not been moved to Drive.
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    EXPECT_TRUE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
    CheckPathNotFoundOnDrive(observed_relative_drive_path());
  }

  histogram_.ExpectUniqueSample(kGoogleDriveUploadResultMetricName,
                                OfficeFilesUploadResult::kSyncError, 1);
  ASSERT_EQ(observed_task_result_, OfficeTaskResult::kFailedToUpload);
}

IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest, UploadFromMyFilesNoConnection) {
  SetUpObservers();
  SetUpMyFiles();
  SetUpDrive();
  SetDriveConnectionStatusForTesting(ConnectionStatus::kNoNetwork);

  // Define the source file as a test docx file within MyFiles.
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, my_files_dir_);

  EXPECT_CALL(fake_drivefs(), ImmediatelyUpload(_, _)).Times(0);

  base::RunLoop run_loop;
  base::MockCallback<DriveUploadHandler::UploadCallback> upload_callback;
  EXPECT_CALL(upload_callback, Run(OfficeTaskResult::kFailedToUpload,
                                   std::optional<GURL>(std::nullopt), _))
      .WillOnce(RunClosure(run_loop.QuitClosure()));
  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url, upload_callback.Get(),
      cloud_open_metrics_ref_);
  drive_upload_handler->Run();
  run_loop.Run();

  // Check that the source file has not been moved to Drive.
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    EXPECT_TRUE(base::PathExists(my_files_dir_.AppendASCII(test_file_name)));
    CheckPathNotFoundOnDrive(observed_relative_drive_path());
  }

  histogram_.ExpectUniqueSample(kGoogleDriveUploadResultMetricName,
                                OfficeFilesUploadResult::kNoConnection, 1);
}

IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest,
                       UploadFromMyFilesConnectionLostDuringUpload) {
  SetUpObservers();
  SetUpMyFiles();
  SetUpDrive();

  // Define the source file as a test docx file within MyFiles.
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, my_files_dir_);

  on_transfer_complete_callback_ = base::BindLambdaForTesting([this] {
    SetDriveConnectionStatusForTesting(ConnectionStatus::kNoNetwork);
    drive_integration_service()->OnNetworkChanged();
  });

  base::RunLoop run_loop;
  base::MockCallback<DriveUploadHandler::UploadCallback> upload_callback;
  EXPECT_CALL(upload_callback, Run(OfficeTaskResult::kFailedToUpload,
                                   std::optional<GURL>(std::nullopt), _))
      .WillOnce(RunClosure(run_loop.QuitClosure()));
  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url, upload_callback.Get(),
      cloud_open_metrics_ref_);
  drive_upload_handler->Run();
  run_loop.Run();

  histogram_.ExpectUniqueSample(kGoogleDriveUploadResultMetricName,
                                OfficeFilesUploadResult::kNoConnection, 1);
}

IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest,
                       OnGetDriveMetadata_WhenNoMetadata) {
  // Set up a source file just to construct a DriveUploadHandler.
  SetUpMyFiles();
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, my_files_dir_);

  drivefs::mojom::FileMetadataPtr metadata;

  // Provide a FILE_ERROR_FAILED response.
  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url, base::DoNothing(), cloud_open_metrics_ref_);
  // This should call the OnFailedUpload() immediately since no "upload"
  // actually occurred so there is no need to do any clean up.
  drive_upload_handler->OnGetDriveMetadata(
      /*timed_out=*/true, /*error=*/drive::FILE_ERROR_FAILED,
      std::move(metadata));

  histogram_.ExpectUniqueSample(kGoogleDriveUploadResultMetricName,
                                OfficeFilesUploadResult::kCloudMetadataError,
                                1);
}

IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest,
                       OnGetDriveMetadata_WhenInvalidAlternateUrl) {
  // Set up a source file just to construct a DriveUploadHandler.
  SetUpMyFiles();
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, my_files_dir_);

  // Provide an invalid alternate url.
  drivefs::mojom::FileMetadataPtr metadata =
      drivefs::mojom::FileMetadata::New();
  metadata->content_mime_type =
      "application/"
      "vnd.openxmlformats-officedocument.wordprocessingml.document";
  metadata->alternate_url = "invalid";

  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url, base::DoNothing(), cloud_open_metrics_ref_);
  // This should call the OnFailedUpload() immediately since no "upload"
  // actually occurred so there is no need to do any clean up.
  drive_upload_handler->OnGetDriveMetadata(
      /*timed_out=*/true, /*error=*/drive::FILE_ERROR_OK, std::move(metadata));

  histogram_.ExpectUniqueSample(kGoogleDriveUploadResultMetricName,
                                OfficeFilesUploadResult::kInvalidAlternateUrl,
                                1);
}

IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest,
                       OnGetDriveMetadata_WhenHostIsUnexpected) {
  // Set up a source file just to construct a DriveUploadHandler.
  SetUpMyFiles();
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, my_files_dir_);

  // Provide an unexpected alternate url host.
  drivefs::mojom::FileMetadataPtr metadata =
      drivefs::mojom::FileMetadata::New();
  metadata->content_mime_type =
      "application/"
      "vnd.openxmlformats-officedocument.wordprocessingml.document";
  metadata->alternate_url =
      "https://unexpected.com/document/d/smalldocxid?rtpof=true&usp=drive_fs";

  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url, base::DoNothing(), cloud_open_metrics_ref_);
  // This should call the OnFailedUpload() immediately since no "upload"
  // actually occurred so there is no need to do any clean up.
  drive_upload_handler->OnGetDriveMetadata(
      /*timed_out=*/true, /*error=*/drive::FILE_ERROR_OK, std::move(metadata));

  histogram_.ExpectUniqueSample(
      kGoogleDriveUploadResultMetricName,
      OfficeFilesUploadResult::kUnexpectedAlternateUrlHost, 1);
}

IN_PROC_BROWSER_TEST_F(DriveUploadHandlerTest,
                       OnGetDriveMetadata_WhenFileNotAnOfficeFile) {
  // Set up a source file just to construct a DriveUploadHandler.
  SetUpMyFiles();
  const std::string test_file_name = "text.docx";
  FileSystemURL source_file_url =
      SetUpSourceFile(test_file_name, my_files_dir_);

  drivefs::mojom::FileMetadataPtr metadata =
      drivefs::mojom::FileMetadata::New();
  // Set the mime type to be not an Office file mime type.
  metadata->content_mime_type = "video/mp4";
  // Set the host to be drive.google.com instead of docs.google.com. This occurs
  // when a file with an Office extension is uploaded but it is actually not an
  // Office file.
  metadata->alternate_url =
      "https://drive.google.com/document/d/smalldocxid?rtpof=true&usp=drive_fs";

  auto drive_upload_handler = std::make_unique<DriveUploadHandler>(
      profile(), source_file_url, base::DoNothing(), cloud_open_metrics_ref_);
  // This should call the OnFailedUpload() immediately since no "upload"
  // actually occurred so there is no need to do any clean up.
  drive_upload_handler->OnGetDriveMetadata(
      /*timed_out=*/true, /*error=*/drive::FILE_ERROR_OK, std::move(metadata));

  histogram_.ExpectUniqueSample(kGoogleDriveUploadResultMetricName,
                                OfficeFilesUploadResult::kFileNotAnOfficeFile,
                                1);
}

}  // namespace ash::cloud_upload