chromium/chrome/browser/ash/file_manager/office_file_tasks_unittest.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 "chrome/browser/ash/file_manager/office_file_tasks.h"

#include <memory>
#include <string>

#include "base/test/metrics/histogram_tester.h"
#include "base/values.h"
#include "chrome/browser/ash/file_manager/app_id.h"
#include "chrome/browser/ash/file_manager/file_tasks.h"
#include "chrome/browser/ash/file_manager/office_file_tasks.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_open_metrics.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace file_manager::file_tasks {

class FileManagerOfficeFileTasksTest : public testing::Test {
 public:
  void SetUp() override {
    TestingProfile::Builder profile_builder;
    profile_ = profile_builder.Build();
  }

  TestingProfile* profile() { return profile_.get(); }

  const base::Value::Dict& tasks_by_mime_type() {
    return profile()->GetTestingPrefService()->GetDict(
        prefs::kDefaultTasksByMimeType);
  }

  const base::Value::Dict& tasks_by_suffix() {
    return profile()->GetTestingPrefService()->GetDict(
        prefs::kDefaultTasksBySuffix);
  }

  void ClearPrefs() {
    profile()->GetTestingPrefService()->ClearPref(
        prefs::kDefaultTasksByMimeType);
    profile()->GetTestingPrefService()->ClearPref(prefs::kDefaultTasksBySuffix);
  }

 protected:
  std::unique_ptr<ash::cloud_upload::CloudOpenMetrics>
      cloud_open_metrics_for_drive_ =
          std::make_unique<ash::cloud_upload::CloudOpenMetrics>(
              ash::cloud_upload::CloudProvider::kGoogleDrive,
              /*file_count=*/1);
  std::unique_ptr<ash::cloud_upload::CloudOpenMetrics>
      cloud_open_metrics_for_one_drive_ =
          std::make_unique<ash::cloud_upload::CloudOpenMetrics>(
              ash::cloud_upload::CloudProvider::kOneDrive,
              /*file_count=*/1);
  base::HistogramTester histogram_;

 private:
  content::BrowserTaskEnvironment task_environment_;
  std::unique_ptr<TestingProfile> profile_;
};

TEST_F(FileManagerOfficeFileTasksTest,
       UpdateDefaultTask_SetsOfficeFileHandlersForGroup) {
  std::string app_id = "abcdef";
  TaskType task_type = TASK_TYPE_FILE_HANDLER;
  std::string activity = "first_activity";
  TaskDescriptor fake_office_task(app_id, task_type, activity);

  UpdateDefaultTask(profile(), fake_office_task, {".doc"},
                    {"application/msword"});

  std::string expected_task_id = MakeTaskID(app_id, task_type, activity);
  ASSERT_EQ(*tasks_by_mime_type().FindString("application/msword"),
            expected_task_id);
  ASSERT_EQ(*tasks_by_mime_type().FindString(
                "application/"
                "vnd.openxmlformats-officedocument.wordprocessingml.document"),
            expected_task_id);

  ASSERT_EQ(*tasks_by_suffix().FindString(".doc"), expected_task_id);
  ASSERT_EQ(*tasks_by_suffix().FindString(".docx"), expected_task_id);
  ASSERT_EQ(tasks_by_suffix().FindString(".xls"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".pptx"), nullptr);

  ClearPrefs();

  UpdateDefaultTask(profile(), fake_office_task, {".xlsx"},
                    {"application/"
                     "vnd.openxmlformats-officedocument.spreadsheetml.sheet"});

  ASSERT_EQ(*tasks_by_mime_type().FindString("application/vnd.ms-excel"),
            expected_task_id);
  ASSERT_EQ(*tasks_by_mime_type().FindString(
                "application/vnd.ms-excel.sheet.macroEnabled.12"),
            expected_task_id);
  ASSERT_EQ(*tasks_by_mime_type().FindString(
                "application/"
                "vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
            expected_task_id);

  ASSERT_EQ(*tasks_by_suffix().FindString(".xls"), expected_task_id);
  ASSERT_EQ(*tasks_by_suffix().FindString(".xlsm"), expected_task_id);
  ASSERT_EQ(*tasks_by_suffix().FindString(".xlsx"), expected_task_id);
  ASSERT_EQ(tasks_by_suffix().FindString(".doc"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".pptx"), nullptr);

  ClearPrefs();

  UpdateDefaultTask(
      profile(), fake_office_task, {".pptx"},
      {"application/"
       "vnd.openxmlformats-officedocument.presentationml.presentation"});

  ASSERT_EQ(*tasks_by_mime_type().FindString("application/vnd.ms-powerpoint"),
            expected_task_id);
  ASSERT_EQ(
      *tasks_by_mime_type().FindString(
          "application/"
          "vnd.openxmlformats-officedocument.presentationml.presentation"),
      expected_task_id);

  ASSERT_EQ(*tasks_by_suffix().FindString(".ppt"), expected_task_id);
  ASSERT_EQ(*tasks_by_suffix().FindString(".pptx"), expected_task_id);
  ASSERT_EQ(tasks_by_suffix().FindString(".doc"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".xlsm"), nullptr);
}

TEST_F(FileManagerOfficeFileTasksTest,
       UpdateDefaultTask_DoesNotSetOfficeFileHandlersForGroup) {
  std::string app_id = "abcdef";
  TaskType task_type = TASK_TYPE_FILE_HANDLER;
  std::string activity = "first_activity";
  TaskDescriptor fake_office_task(app_id, task_type, activity);

  UpdateDefaultTask(profile(), fake_office_task, {".doc"}, {});

  std::string expected_task_id = MakeTaskID(app_id, task_type, activity);
  ASSERT_EQ(tasks_by_mime_type().FindString("application/msword"), nullptr);
  ASSERT_EQ(tasks_by_mime_type().FindString(
                "application/"
                "vnd.openxmlformats-officedocument.wordprocessingml.document"),
            nullptr);

  ASSERT_EQ(*tasks_by_suffix().FindString(".doc"), expected_task_id);
  ASSERT_EQ(tasks_by_suffix().FindString(".docx"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".xls"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".pptx"), nullptr);

  ClearPrefs();

  UpdateDefaultTask(profile(), fake_office_task, {}, {"application/msword"});

  ASSERT_EQ(*tasks_by_mime_type().FindString("application/msword"),
            expected_task_id);
  ASSERT_EQ(tasks_by_mime_type().FindString(
                "application/"
                "vnd.openxmlformats-officedocument.wordprocessingml.document"),
            nullptr);

  ASSERT_EQ(tasks_by_suffix().FindString(".doc"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".docx"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".xls"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".pptx"), nullptr);

  ClearPrefs();

  UpdateDefaultTask(profile(), fake_office_task, {".doc", ".xls"},
                    {"application/msword", "application/vnd.ms-excel"});

  ASSERT_EQ(*tasks_by_mime_type().FindString("application/msword"),
            expected_task_id);
  ASSERT_EQ(*tasks_by_mime_type().FindString("application/vnd.ms-excel"),
            expected_task_id);
  ASSERT_EQ(tasks_by_mime_type().FindString(
                "application/vnd.ms-excel.sheet.macroEnabled.12"),
            nullptr);

  ASSERT_EQ(*tasks_by_suffix().FindString(".doc"), expected_task_id);
  ASSERT_EQ(tasks_by_suffix().FindString(".docx"), nullptr);
  ASSERT_EQ(*tasks_by_suffix().FindString(".xls"), expected_task_id);
  ASSERT_EQ(tasks_by_suffix().FindString(".xlsm"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".xlsx"), nullptr);
  ASSERT_EQ(tasks_by_suffix().FindString(".pptx"), nullptr);
}

// Test the setting of a default file task for Office files to a Files App SWA.
TEST_F(FileManagerOfficeFileTasksTest, SetOfficeFileHandlersToFilesSWA) {
  TaskDescriptor task(kFileManagerSwaAppId, TaskType::TASK_TYPE_WEB_APP,
                      "chrome://file-manager/?a");

  // Check no default tasks exist for Doc files.
  std::string docx_mime(
      "application/"
      "vnd.openxmlformats-officedocument.wordprocessingml.document");
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), "application/msword", ".doc"));
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), docx_mime, ".docx"));
  // Set default task for Doc files as a Files App SWA with action id "a".
  SetWordFileHandlerToFilesSWA(profile(), "a");
  // Check the default task for Doc files is `task`.
  EXPECT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), "application/msword", ".doc"),
            task);
  EXPECT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), docx_mime, ".docx"),
            task);
  // Update with replace_existing=false should not change the default.
  SetWordFileHandlerToFilesSWA(profile(), "b", false);
  EXPECT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), "application/msword", ".doc"),
            task);
  EXPECT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), docx_mime, ".docx"),
            task);
  // Removing an action which is not set should not change the default.
  RemoveFilesSWAWordFileHandler(profile(), "b");
  EXPECT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), "application/msword", ".doc"),
            task);
  EXPECT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), docx_mime, ".docx"),
            task);
  // Remove the task.
  RemoveFilesSWAWordFileHandler(profile(), "a");
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), "application/msword", ".doc"));
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), docx_mime, ".docx"));

  // Check no default tasks exist for Excel files.
  std::string xlsx_mime(
      "application/"
      "vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), "application/vnd.ms-excel", ".xls"));
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), xlsx_mime, ".xlsx"));
  // Set default task for Excel files as a Files App SWA with action id "a".
  SetExcelFileHandlerToFilesSWA(profile(), "a");
  // Check the default task for Excel files is `task`.
  ASSERT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), "application/vnd.ms-excel", ".xls"),
            task);
  ASSERT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), xlsx_mime, ".xlsx"),
            task);
  // Remove the task.
  RemoveFilesSWAExcelFileHandler(profile(), "a");
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), "application/vnd.ms-excel", ".xls"));
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), xlsx_mime, ".xlsx"));

  // Check no default tasks exist for Powerpoint files.
  std::string pptx_mime(
      "application/"
      "vnd.openxmlformats-officedocument.presentationml.presentation");
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), "application/vnd.ms-powerpoint", ".ppt"));
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), pptx_mime, ".pptx"));
  // Set default task for Powerpoint files as a Files App SWA with action id
  // "a".
  SetPowerPointFileHandlerToFilesSWA(profile(), "a");
  // Check the default task for Powerpoint files is `task`.
  EXPECT_EQ(
      file_manager::file_tasks::GetDefaultTaskFromPrefs(
          *profile()->GetPrefs(), "application/vnd.ms-powerpoint", ".ppt"),
      task);
  EXPECT_EQ(file_manager::file_tasks::GetDefaultTaskFromPrefs(
                *profile()->GetPrefs(), pptx_mime, ".pptx"),
            task);
  // Remove the task.
  RemoveFilesSWAPowerPointFileHandler(profile(), "a");
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), "application/vnd.ms-powerpoint", ".ppt"));
  EXPECT_FALSE(file_manager::file_tasks::GetDefaultTaskFromPrefs(
      *profile()->GetPrefs(), pptx_mime, ".pptx"));
}

/**
 * Check Log*MetricsAfterFallback() maps the FallbackReason to the correct
 * OpenError and logs the TaskResult.
 */
TEST_F(FileManagerOfficeFileTasksTest,
       LogOneDriveMetricsAfterFallback_kOffline) {
  LogOneDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kOffline,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoice,
      std::move(cloud_open_metrics_for_one_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kOneDriveErrorMetricName,
      ash::cloud_upload::OfficeOneDriveOpenErrors::kOffline, 1);
  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kOneDriveTaskResultMetricName,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoice, 1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogOneDriveMetricsAfterFallback_kAndroidOneDriveUnsupportedLocation) {
  LogOneDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kAndroidOneDriveUnsupportedLocation,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoiceAfterOpen,
      std::move(cloud_open_metrics_for_one_drive_));

  histogram_.ExpectUniqueSample(ash::cloud_upload::kOneDriveErrorMetricName,
                                ash::cloud_upload::OfficeOneDriveOpenErrors::
                                    kAndroidOneDriveUnsupportedLocation,
                                1);
  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kOneDriveTaskResultMetricName,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoiceAfterOpen,
      1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogGoogleDriveMetricsAfterFallback_kOffline) {
  LogGoogleDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kOffline,
      ash::cloud_upload::OfficeTaskResult::kFallbackQuickOffice,
      std::move(cloud_open_metrics_for_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kDriveErrorMetricName,
      ash::cloud_upload::OfficeDriveOpenErrors::kOffline, 1);
  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kGoogleDriveTaskResultMetricName,
      ash::cloud_upload::OfficeTaskResult::kFallbackQuickOffice, 1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogGoogleDriveMetricsAfterFallback_kDriveDisabled) {
  LogGoogleDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kDriveDisabled,
      ash::cloud_upload::OfficeTaskResult::kCancelledAtFallback,
      std::move(cloud_open_metrics_for_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kDriveErrorMetricName,
      ash::cloud_upload::OfficeDriveOpenErrors::kDriveDisabled, 1);
  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kGoogleDriveTaskResultMetricName,
      ash::cloud_upload::OfficeTaskResult::kCancelledAtFallback, 1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogGoogleDriveMetricsAfterFallback_kNoDriveService) {
  LogGoogleDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kNoDriveService,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoice,
      std::move(cloud_open_metrics_for_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kDriveErrorMetricName,
      ash::cloud_upload::OfficeDriveOpenErrors::kNoDriveService, 1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogGoogleDriveMetricsAfterFallback_kDriveAuthenticationNotReady) {
  LogGoogleDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kDriveAuthenticationNotReady,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoice,
      std::move(cloud_open_metrics_for_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kDriveErrorMetricName,
      ash::cloud_upload::OfficeDriveOpenErrors::kDriveAuthenticationNotReady,
      1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogGoogleDriveMetricsAfterFallback_kDriveFsInterfaceError) {
  LogGoogleDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kDriveFsInterfaceError,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoice,
      std::move(cloud_open_metrics_for_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kDriveErrorMetricName,
      ash::cloud_upload::OfficeDriveOpenErrors::kDriveFsInterface, 1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogGoogleDriveMetricsAfterFallback_kMeteredConnection) {
  LogGoogleDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kMeteredConnection,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoice,
      std::move(cloud_open_metrics_for_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kDriveErrorMetricName,
      ash::cloud_upload::OfficeDriveOpenErrors::kMeteredConnection, 1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogGoogleDriveMetricsAfterFallback_kDisableDrivePreferenceSet) {
  LogGoogleDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kDisableDrivePreferenceSet,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoice,
      std::move(cloud_open_metrics_for_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kDriveErrorMetricName,
      ash::cloud_upload::OfficeDriveOpenErrors::kDisableDrivePreferenceSet, 1);
}

TEST_F(FileManagerOfficeFileTasksTest,
       LogGoogleDriveMetricsAfterFallback_kDriveDisabledForAccountType) {
  LogGoogleDriveMetricsAfterFallback(
      ash::office_fallback::FallbackReason::kDriveDisabledForAccountType,
      ash::cloud_upload::OfficeTaskResult::kCannotGetFallbackChoice,
      std::move(cloud_open_metrics_for_drive_));

  histogram_.ExpectUniqueSample(
      ash::cloud_upload::kDriveErrorMetricName,
      ash::cloud_upload::OfficeDriveOpenErrors::kDriveDisabledForAccountType,
      1);
}

TEST_F(FileManagerOfficeFileTasksTest, IsOfficeFileMimeType) {
  // Powerpoint.
  EXPECT_TRUE(IsOfficeFileMimeType("application/vnd.ms-powerpoint"));
  EXPECT_TRUE(IsOfficeFileMimeType(
      "application/"
      "vnd.openxmlformats-officedocument.presentationml.presentation"));
  // Excel.
  EXPECT_TRUE(IsOfficeFileMimeType("application/vnd.ms-excel"));
  EXPECT_TRUE(IsOfficeFileMimeType(
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
  // Word.
  EXPECT_TRUE(IsOfficeFileMimeType("application/msword"));
  EXPECT_TRUE(IsOfficeFileMimeType(
      "application/"
      "vnd.openxmlformats-officedocument.wordprocessingml.document"));

  EXPECT_FALSE(IsOfficeFileMimeType("text/plain"));
  EXPECT_FALSE(IsOfficeFileMimeType("video/webm"));
  EXPECT_FALSE(IsOfficeFileMimeType("image/png'"));
  EXPECT_FALSE(IsOfficeFileMimeType("image/png"));
}

}  // namespace file_manager::file_tasks