// Copyright 2014 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/safe_browsing/incident_reporting/incident_reporting_service.h"

#include <stdint.h>

#include <map>
#include <memory>
#include <string>
#include <utility>

#include "base/auto_reset.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/mock_entropy_provider.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/safe_browsing/incident_reporting/incident.h"
#include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
#include "chrome/browser/safe_browsing/incident_reporting/incident_report_uploader.h"
#include "chrome/browser/safe_browsing/incident_reporting/last_download_finder.h"
#include "chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/quota_service.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_WIN)
#include "base/test/test_reg_util_win.h"
#endif

namespace {

class TestIncidentReportingService;
constinit thread_local TestIncidentReportingService* test_instance = nullptr;

constexpr char kFakeOsName[] = "fakedows";
constexpr char kFakeDownloadToken[] = "fakedlt";
constexpr char kFakeDownloadHost[] = "chromium.org";
constexpr char kTestTrackedPrefPath[] = "some_pref";
constexpr char kFakeExtensionId[] = "fakeExtensionId";

// An IRS class that allows a test harness to provide a fake environment
// collector, extension collector and report uploader via callbacks.
class TestIncidentReportingService
    : public safe_browsing::IncidentReportingService {
 public:
  using PreProfileAddCallback = base::RepeatingCallback<void(Profile*)>;

  using CollectEnvironmentCallback = base::RepeatingCallback<void(
      safe_browsing::ClientIncidentReport_EnvironmentData*)>;

  using CreateDownloadFinderCallback = base::RepeatingCallback<
      std::unique_ptr<safe_browsing::LastDownloadFinder>(
          safe_browsing::LastDownloadFinder::LastDownloadCallback callback)>;

  using StartUploadCallback = base::RepeatingCallback<
      std::unique_ptr<safe_browsing::IncidentReportUploader>(
          safe_browsing::IncidentReportUploader::OnResultCallback,
          const safe_browsing::ClientIncidentReport& report)>;

  TestIncidentReportingService(
      const scoped_refptr<base::TaskRunner>& task_runner,
      const PreProfileAddCallback& pre_profile_add_callback,
      const CollectEnvironmentCallback& collect_environment_callback,
      const CreateDownloadFinderCallback& create_download_finder_callback,
      const StartUploadCallback& start_upload_callback)
      : IncidentReportingService(nullptr, base::Milliseconds(5), task_runner),
        resetter_(&test_instance, this),
        pre_profile_add_callback_(pre_profile_add_callback),
        collect_environment_callback_(collect_environment_callback),
        create_download_finder_callback_(create_download_finder_callback),
        start_upload_callback_(start_upload_callback),
        extension_collected_(false) {
    SetCollectEnvironmentHook(&CollectEnvironmentData, task_runner);
  }

  ~TestIncidentReportingService() override = default;

  bool IsProcessingReport() const {
    return IncidentReportingService::IsProcessingReport();
  }

  bool HasCollectedExtension() const { return extension_collected_; }

 protected:
  void OnProfileAdded(Profile* profile) override {
    pre_profile_add_callback_.Run(profile);
    safe_browsing::IncidentReportingService::OnProfileAdded(profile);
  }

  // A fake extension collection implementation invoked by the service during
  // operation.
  void DoExtensionCollection(
      safe_browsing::ClientIncidentReport_ExtensionData* data) override {
    ASSERT_NE(static_cast<safe_browsing::ClientIncidentReport_ExtensionData*>(
                  nullptr),
              data);
    data->mutable_last_installed_extension()->set_id(kFakeExtensionId);
    extension_collected_ = true;
  }

  std::unique_ptr<safe_browsing::LastDownloadFinder> CreateDownloadFinder(
      safe_browsing::LastDownloadFinder::LastDownloadCallback callback)
      override {
    return create_download_finder_callback_.Run(std::move(callback));
  }

  std::unique_ptr<safe_browsing::IncidentReportUploader> StartReportUpload(
      safe_browsing::IncidentReportUploader::OnResultCallback callback,
      const safe_browsing::ClientIncidentReport& report) override {
    return start_upload_callback_.Run(std::move(callback), report);
  }

 private:
  static TestIncidentReportingService& current() { return *test_instance; }

  static void CollectEnvironmentData(
      safe_browsing::ClientIncidentReport_EnvironmentData* data) {
    current().collect_environment_callback_.Run(data);
  }

  const base::AutoReset<TestIncidentReportingService*> resetter_;
  PreProfileAddCallback pre_profile_add_callback_;
  CollectEnvironmentCallback collect_environment_callback_;
  CreateDownloadFinderCallback create_download_finder_callback_;
  StartUploadCallback start_upload_callback_;
  bool extension_collected_;
};

// A test fixture that sets up a test task runner and makes it the thread's
// runner. The fixture implements a fake environment data collector, extension
// data collector and a fake report uploader.
// Tests for IncidentReportingService.
//
// The boolean parameter controls the state of the
// kExtendedReportingRemovePrefDependency feature. When true, the feature is
// enabled (SBER is deprecated). When false, the feature is disabled (SBER is
// enabled).
class IncidentReportingServiceTest : public testing::TestWithParam<bool> {
 protected:
  // Returns true if SBER is enabled (i.e. not deprecated).
  bool SberExists() { return !GetParam(); }

  // A type for specifying whether a profile created by CreateProfile
  // participates in safe browsing and safe browsing extended reporting.
  enum SafeBrowsingDisposition {
    OPT_OUT,
    SAFE_BROWSING_ONLY,
    EXTENDED_REPORTING_ONLY,
    SAFE_BROWSING_AND_EXTENDED_REPORTING,
    ENHANCED_PROTECTION,
  };

  // A type for specifying the action to be taken by the test fixture during
  // profile initialization (before OnProfileAdded is called).
  enum OnProfileAdditionAction {
    ON_PROFILE_ADDITION_NO_ACTION,
    ON_PROFILE_ADDITION_ADD_INCIDENT,  // Add an incident to the service.
    ON_PROFILE_ADDITION_ADD_TWO_INCIDENTS,  // Add two incidents to the service.
  };

  // A type for specifying the action to be taken by the test fixture when the
  // service creates a LastDownloadFinder.
  enum OnCreateDownloadFinderAction {
    // Post a task that reports a binary and non-binary download.
    ON_CREATE_DOWNLOAD_FINDER_DOWNLOADS_FOUND,
    // Post a task that reports a binary download.
    ON_CREATE_DOWNLOAD_FINDER_BINARY_DOWNLOAD_FOUND,
    // Post a task that reports a non-binary download.
    ON_CREATE_DOWNLOAD_FINDER_NON_BINARY_DOWNLOAD_FOUND,
    // Post a task that reports no downloads found.
    ON_CREATE_DOWNLOAD_FINDER_NO_DOWNLOADS,
    // Immediately return due to a lack of eligible profiles.
    ON_CREATE_DOWNLOAD_FINDER_NO_PROFILES,
  };

  // A type for specifying the action to be taken by the test fixture when its
  // delayed analysis callback is run.
  enum OnDelayedAnalysisAction {
    ON_DELAYED_ANALYSIS_NO_ACTION,
    ON_DELAYED_ANALYSIS_ADD_INCIDENT,  // Add an incident to the service.
  };

  IncidentReportingServiceTest()
      : profile_manager_(TestingBrowserProcess::GetGlobal()),
        on_create_download_finder_action_(
            ON_CREATE_DOWNLOAD_FINDER_DOWNLOADS_FOUND),
        on_delayed_analysis_action_(ON_DELAYED_ANALYSIS_NO_ACTION),
        upload_result_(safe_browsing::IncidentReportUploader::UPLOAD_SUCCESS),
        environment_collected_(),
        download_finder_created_(),
        download_finder_destroyed_(),
        uploader_destroyed_(),
        delayed_analysis_ran_() {}

  void SetUp() override {
    testing::Test::SetUp();
#if BUILDFLAG(IS_WIN)
    // Redirect HKCU so that the platform state store used by the test doesn't
    // collide with existing Chrome installs or other tests running in parallel.
    ASSERT_NO_FATAL_FAILURE(
        registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
#endif
    ASSERT_TRUE(profile_manager_.SetUp());
    // Disable profile metrics reporting, otherwise the calls to
    // FastForwardUntilNoTasksRemain() never return.
    profile_manager_.profile_manager()->DisableProfileMetricsForTesting();
    profile_manager_.profile_attributes_storage()
        ->DisableProfileMetricsForTesting();
  }

  void CreateIncidentReportingService() {
    if (SberExists()) {
      // SBER is enabled. Safe Browsing Incident Reporting uses SBER preference.
      scoped_feature_list_.InitWithFeatures(
          /*enabled_features=*/{safe_browsing::kIncidentReportingEnableUpload},
          /*disabled_features=*/{
              safe_browsing::kExtendedReportingRemovePrefDependency,
              safe_browsing::kHashPrefixRealTimeLookupsSamplePing});

    } else {
      // SBER is deprecated. Safe Browsing Incident Reporting uses Enhanced
      // Protection preference only.
      scoped_feature_list_.InitWithFeatures(
          /*enabled_features=*/
          {safe_browsing::kIncidentReportingEnableUpload,
           safe_browsing::kExtendedReportingRemovePrefDependency,
           safe_browsing::kHashPrefixRealTimeLookupsSamplePing},
          /*disabled_features=*/{});
    }

    instance_ = std::make_unique<TestIncidentReportingService>(
        base::SingleThreadTaskRunner::GetCurrentDefault(),
        base::BindRepeating(&IncidentReportingServiceTest::PreProfileAdd,
                            base::Unretained(this)),
        base::BindRepeating(
            &IncidentReportingServiceTest::CollectEnvironmentData,
            base::Unretained(this)),
        base::BindRepeating(&IncidentReportingServiceTest::CreateDownloadFinder,
                            base::Unretained(this)),
        base::BindRepeating(&IncidentReportingServiceTest::StartUpload,
                            base::Unretained(this)));
  }

  // Sets the action to be taken by the test fixture when the service creates a
  // LastDownloadFinder.
  void SetCreateDownloadFinderAction(OnCreateDownloadFinderAction action) {
    on_create_download_finder_action_ = action;
  }

  // Creates and returns a profile (owned by the profile manager) with or
  // without safe browsing enabled. An incident will be created within
  // PreProfileAdd if requested. |incidents_sent|, if provided, will be set
  // in the profile's preference.
  TestingProfile* CreateProfile(const std::string& profile_name,
                                SafeBrowsingDisposition safe_browsing_opt_in,
                                OnProfileAdditionAction on_addition_action,
                                std::optional<base::DictValue> incidents_sent) {
    // Create prefs for the profile with safe browsing enabled or not.
    std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> prefs(
        new sync_preferences::TestingPrefServiceSyncable);
    RegisterUserProfilePrefs(prefs->registry());
    prefs->SetBoolean(
        prefs::kSafeBrowsingEnabled,
        safe_browsing_opt_in == SAFE_BROWSING_ONLY ||
            safe_browsing_opt_in == SAFE_BROWSING_AND_EXTENDED_REPORTING ||
            safe_browsing_opt_in == ENHANCED_PROTECTION);
    if (safe_browsing_opt_in == ENHANCED_PROTECTION) {
      prefs->SetBoolean(prefs::kSafeBrowsingEnhanced, true);
    } else {
      safe_browsing::SetExtendedReportingPrefForTests(
          prefs.get(),
          safe_browsing_opt_in == EXTENDED_REPORTING_ONLY ||
              safe_browsing_opt_in == SAFE_BROWSING_AND_EXTENDED_REPORTING);
    }
    if (incidents_sent)
      prefs->SetDict(prefs::kSafeBrowsingIncidentsSent,
                     std::move(*incidents_sent));

    // Remember whether or not to create an incident.
    profile_properties_[profile_name].on_addition_action = on_addition_action;

    // Boom (or fizzle).
    auto* profile = profile_manager_.CreateTestingProfile(
        profile_name, std::move(prefs), base::ASCIIToUTF16(profile_name),
        0,  // avatar_id (unused)
        TestingProfile::TestingFactories());

    task_environment_.FastForwardUntilNoTasksRemain();

    return profile;
  }

  // Enables safe browsing incident reporting via appropriate preference (SBER
  // or Enhanced Protection).
  void EnableReporting(PrefService* prefs) {
    if (SberExists()) {
      // SBER is enabled. Safe Browsing Incident Reporting uses SBER preference.
      safe_browsing::SetExtendedReportingPrefForTests(prefs, true);
    } else {
      // SBER is deprecated. Safe Browsing Incident Reporting uses Enhanced
      // Protection preference only.
      prefs->SetBoolean(prefs::kSafeBrowsingEnhanced, true);
    }
  }

  // Configures a callback to run when the next upload is started that will post
  // a task to delete the profile. This task will run before the upload
  // finishes.
  void DeleteProfileOnUpload(Profile* profile) {
    ASSERT_TRUE(on_start_upload_callback_.is_null());
    on_start_upload_callback_ =
        base::BindOnce(&IncidentReportingServiceTest::DelayedDeleteProfile,
                       base::Unretained(this), profile);
  }

  // Returns an incident suitable for testing.
  std::unique_ptr<safe_browsing::Incident> MakeTestIncident(const char* value) {
    std::unique_ptr<
        safe_browsing::
            ClientIncidentReport_IncidentData_TrackedPreferenceIncident>
        incident(
            new safe_browsing::
                ClientIncidentReport_IncidentData_TrackedPreferenceIncident());
    incident->set_path(kTestTrackedPrefPath);
    if (value)
      incident->set_atomic_value(value);
    return std::make_unique<safe_browsing::TrackedPreferenceIncident>(
        std::move(incident), false /* is_personal */);
  }

  // Adds a test incident to the service.
  void AddTestIncident(Profile* profile) {
    std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
        instance_->GetIncidentReceiver());
    if (profile)
      receiver->AddIncidentForProfile(profile, MakeTestIncident(nullptr));
    else
      receiver->AddIncidentForProcess(MakeTestIncident(nullptr));
  }

  // Registers the callback to be run for delayed analysis.
  void RegisterAnalysis(OnDelayedAnalysisAction on_delayed_analysis_action) {
    on_delayed_analysis_action_ = on_delayed_analysis_action;
    instance_->RegisterDelayedAnalysisCallback(
        base::BindOnce(&IncidentReportingServiceTest::OnDelayedAnalysis,
                       base::Unretained(this)));
  }

  // Expects that |incident_count| incidents were uploaded by the service with
  // the described downloads, then clears the instance for subsequent incidents.
  void ExpectTestIncidentUploadWithBinaryDownload(int incident_count);
  void ExpectTestIncidentUploadWithNonBinaryDownload(int incident_count);
  void ExpectTestIncidentUploadWithBothDownloads(int incident_count);

  void AssertNoUpload() { ASSERT_FALSE(uploaded_report_); }

  bool HasCollectedEnvironmentAndExtensionData() const {
    return environment_collected_ && instance_->HasCollectedExtension();
  }
  bool HasCreatedDownloadFinder() const { return download_finder_created_; }
  bool DownloadFinderDestroyed() const { return download_finder_destroyed_; }
  bool UploaderDestroyed() const { return uploader_destroyed_; }
  bool DelayedAnalysisRan() const { return delayed_analysis_ran_; }

  // Fakes BrowserThreads and the main MessageLoop.
  content::BrowserTaskEnvironment task_environment_{
      content::BrowserTaskEnvironment::TimeSource::MOCK_TIME};

  extensions::QuotaService::ScopedDisablePurgeForTesting
      disable_purge_for_testing_;
  TestingProfileManager profile_manager_;
  std::unique_ptr<TestIncidentReportingService> instance_;
  base::OnceClosure on_start_upload_callback_;
  OnCreateDownloadFinderAction on_create_download_finder_action_;
  OnDelayedAnalysisAction on_delayed_analysis_action_;
  safe_browsing::IncidentReportUploader::Result upload_result_;
  bool environment_collected_;
  bool download_finder_created_;
  std::unique_ptr<safe_browsing::ClientIncidentReport> uploaded_report_;
  bool download_finder_destroyed_;
  bool uploader_destroyed_;
  bool delayed_analysis_ran_;

 private:
  // A fake IncidentReportUploader that posts a task to provide a given response
  // back to the incident reporting service. It also reports back to the test
  // harness via a closure when it is deleted by the incident reporting service.
  class FakeUploader : public safe_browsing::IncidentReportUploader {
   public:
    FakeUploader(
        base::OnceClosure on_deleted,
        safe_browsing::IncidentReportUploader::OnResultCallback callback,
        safe_browsing::IncidentReportUploader::Result result)
        : safe_browsing::IncidentReportUploader(std::move(callback)),
          on_deleted_(std::move(on_deleted)),
          result_(result) {
      // Post a task that will provide the response.
      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
          FROM_HERE,
          base::BindOnce(&FakeUploader::FinishUpload, base::Unretained(this)));
    }

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

    ~FakeUploader() override { std::move(on_deleted_).Run(); }

   private:
    void FinishUpload() {
      // Callbacks have a tendency to delete the uploader, so no touching
      // anything after this.
      std::move(callback_).Run(
          result_, std::unique_ptr<safe_browsing::ClientIncidentResponse>());
    }

    base::OnceClosure on_deleted_;
    safe_browsing::IncidentReportUploader::Result result_;
  };

  class FakeDownloadFinder : public safe_browsing::LastDownloadFinder {
   public:
    static std::unique_ptr<safe_browsing::LastDownloadFinder> Create(
        base::OnceClosure on_deleted,
        std::unique_ptr<safe_browsing::ClientIncidentReport_DownloadDetails>
            binary_download,
        std::unique_ptr<
            safe_browsing::ClientIncidentReport_NonBinaryDownloadDetails>
            non_binary_download,
        safe_browsing::LastDownloadFinder::LastDownloadCallback callback) {
      // Post a task to run the callback.
      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
          FROM_HERE,
          base::BindOnce(std::move(callback), std::move(binary_download),
                         std::move(non_binary_download)));
      return std::unique_ptr<safe_browsing::LastDownloadFinder>(
          new FakeDownloadFinder(std::move(on_deleted)));
    }

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

    ~FakeDownloadFinder() override { std::move(on_deleted_).Run(); }

   private:
    explicit FakeDownloadFinder(base::OnceClosure on_deleted)
        : on_deleted_(std::move(on_deleted)) {}

    base::OnceClosure on_deleted_;
  };

  // Confirms that the test incident(s) was/were uploaded by the service, then
  // clears the instance for subsequent incidents.
  void ExpectTestIncidentUploadedImpl(int incident_count,
                                      bool withBinaryDownload,
                                      bool withNonBinaryDownload) {
    if (incident_count == 0) {
      ASSERT_FALSE(uploaded_report_);
      return;
    }
    ASSERT_TRUE(uploaded_report_);
    ASSERT_EQ(incident_count, uploaded_report_->incident_size());
    for (int i = 0; i < incident_count; ++i) {
      ASSERT_TRUE(uploaded_report_->incident(i).has_incident_time_msec());
      ASSERT_NE(0LL, uploaded_report_->incident(i).incident_time_msec());
      ASSERT_TRUE(uploaded_report_->incident(i).has_tracked_preference());
      ASSERT_TRUE(
          uploaded_report_->incident(i).tracked_preference().has_path());
      ASSERT_EQ(std::string(kTestTrackedPrefPath),
                uploaded_report_->incident(i).tracked_preference().path());
    }
    ASSERT_TRUE(uploaded_report_->has_environment());
    ASSERT_TRUE(uploaded_report_->environment().has_os());
    ASSERT_TRUE(uploaded_report_->environment().os().has_os_name());
    ASSERT_EQ(std::string(kFakeOsName),
              uploaded_report_->environment().os().os_name());
    ASSERT_EQ(
        std::string(kFakeExtensionId),
        uploaded_report_->extension_data().last_installed_extension().id());

    if (withBinaryDownload) {
      ASSERT_EQ(std::string(kFakeDownloadToken),
                uploaded_report_->download().token());
    }
    if (withNonBinaryDownload) {
      ASSERT_EQ(std::string(kFakeDownloadHost),
                uploaded_report_->non_binary_download().host());
    }

    uploaded_report_.reset();
  }

  // Properties for a profile that impact the behavior of the test.
  struct ProfileProperties {
    ProfileProperties() : on_addition_action(ON_PROFILE_ADDITION_NO_ACTION) {}

    // The action taken by the test fixture during profile initialization
    // (before OnProfileAdded is called).
    OnProfileAdditionAction on_addition_action;
  };

  // Posts a task to delete the profile.
  void DelayedDeleteProfile(Profile* profile) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(&TestingProfileManager::DeleteTestingProfile,
                                  base::Unretained(&profile_manager_),
                                  profile->GetProfileUserName()));
  }

  // A callback run by the test fixture when a profile is added. An incident
  // is added.
  void PreProfileAdd(Profile* profile) {
    // The instance must have already been created.
    ASSERT_TRUE(instance_);
    // Add a test incident to the service if requested.
    OnProfileAdditionAction action =
        profile_properties_[profile->GetProfileUserName()].on_addition_action;
    switch (action) {
      case ON_PROFILE_ADDITION_ADD_INCIDENT:
        AddTestIncident(profile);
        break;
      case ON_PROFILE_ADDITION_ADD_TWO_INCIDENTS:
        AddTestIncident(profile);
        AddTestIncident(profile);
        break;
      default:
        ASSERT_EQ(ON_PROFILE_ADDITION_NO_ACTION, action);
        break;
    }
  }

  // A fake CollectEnvironmentData implementation invoked by the service during
  // operation.
  void CollectEnvironmentData(
      safe_browsing::ClientIncidentReport_EnvironmentData* data) {
    ASSERT_TRUE(data);
    data->mutable_os()->set_os_name(kFakeOsName);
    environment_collected_ = true;
  }

  // A fake CreateDownloadFinder implementation invoked by the service during
  // operation.
  std::unique_ptr<safe_browsing::LastDownloadFinder> CreateDownloadFinder(
      safe_browsing::LastDownloadFinder::LastDownloadCallback callback) {
    download_finder_created_ = true;
    std::unique_ptr<safe_browsing::ClientIncidentReport_DownloadDetails>
        binary_download;
    std::unique_ptr<
        safe_browsing::ClientIncidentReport_NonBinaryDownloadDetails>
        non_binary_download;
    if (on_create_download_finder_action_ ==
        ON_CREATE_DOWNLOAD_FINDER_NO_PROFILES) {
      return nullptr;
    }
    if (on_create_download_finder_action_ ==
            ON_CREATE_DOWNLOAD_FINDER_DOWNLOADS_FOUND ||
        on_create_download_finder_action_ ==
            ON_CREATE_DOWNLOAD_FINDER_BINARY_DOWNLOAD_FOUND) {
      binary_download = std::make_unique<
          safe_browsing::ClientIncidentReport_DownloadDetails>();
      binary_download->set_token(kFakeDownloadToken);
    }
    if (on_create_download_finder_action_ ==
            ON_CREATE_DOWNLOAD_FINDER_DOWNLOADS_FOUND ||
        on_create_download_finder_action_ ==
            ON_CREATE_DOWNLOAD_FINDER_NON_BINARY_DOWNLOAD_FOUND) {
      non_binary_download = std::make_unique<
          safe_browsing::ClientIncidentReport_NonBinaryDownloadDetails>();
      non_binary_download->set_host(kFakeDownloadHost);
    }

    return std::unique_ptr<safe_browsing::LastDownloadFinder>(
        FakeDownloadFinder::Create(
            base::BindOnce(
                &IncidentReportingServiceTest::OnDownloadFinderDestroyed,
                base::Unretained(this)),
            std::move(binary_download), std::move(non_binary_download),
            std::move(callback)));
  }

  // A fake StartUpload implementation invoked by the service during operation.
  std::unique_ptr<safe_browsing::IncidentReportUploader> StartUpload(
      safe_browsing::IncidentReportUploader::OnResultCallback callback,
      const safe_browsing::ClientIncidentReport& report) {
    // Remember the report that is being uploaded.
    uploaded_report_ =
        std::make_unique<safe_browsing::ClientIncidentReport>(report);
    // Run and clear the OnStartUpload callback, if provided.
    if (!on_start_upload_callback_.is_null()) {
      std::move(on_start_upload_callback_).Run();
      on_start_upload_callback_ = base::OnceClosure();
    }
    return std::make_unique<FakeUploader>(
        base::BindOnce(&IncidentReportingServiceTest::OnUploaderDestroyed,
                       base::Unretained(this)),
        std::move(callback), upload_result_);
  }

  void OnDownloadFinderDestroyed() { download_finder_destroyed_ = true; }
  void OnUploaderDestroyed() { uploader_destroyed_ = true; }

  void OnDelayedAnalysis(
      std::unique_ptr<safe_browsing::IncidentReceiver> receiver) {
    delayed_analysis_ran_ = true;
    if (on_delayed_analysis_action_ == ON_DELAYED_ANALYSIS_ADD_INCIDENT)
      receiver->AddIncidentForProcess(MakeTestIncident(nullptr));
  }

#if BUILDFLAG(IS_WIN)
  registry_util::RegistryOverrideManager registry_override_manager_;
#endif

  // A mapping of profile name to its corresponding properties.
  std::map<std::string, ProfileProperties> profile_properties_;

  base::test::ScopedFeatureList scoped_feature_list_;
};

void IncidentReportingServiceTest::ExpectTestIncidentUploadWithBinaryDownload(
    int incident_count) {
  ExpectTestIncidentUploadedImpl(incident_count, true, false);
}

void IncidentReportingServiceTest::
    ExpectTestIncidentUploadWithNonBinaryDownload(int incident_count) {
  ExpectTestIncidentUploadedImpl(incident_count, false, true);
}

void IncidentReportingServiceTest::ExpectTestIncidentUploadWithBothDownloads(
    int incident_count) {
  ExpectTestIncidentUploadedImpl(incident_count, true, false);
}

// Tests that an incident added during profile initialization when safe browsing
// extended reporting is on is uploaded.
TEST_P(IncidentReportingServiceTest, AddIncident) {
  CreateIncidentReportingService();

  // Create the profile, thereby causing the test to begin.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that environment and extension collection took place.
  EXPECT_TRUE(HasCollectedEnvironmentAndExtensionData());

  // Verify that the most recent download was looked for.
  EXPECT_TRUE(HasCreatedDownloadFinder());

  // Verify that report upload took place and contained the incident,
  // environment data, and download details.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Verify that the download finder and the uploader were destroyed.
  ASSERT_TRUE(DownloadFinderDestroyed());
  ASSERT_TRUE(UploaderDestroyed());

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that multiple incidents are coalesced into the same report.
TEST_P(IncidentReportingServiceTest, CoalesceIncidents) {
  CreateIncidentReportingService();

  // Create the profile, thereby causing the test to begin.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_TWO_INCIDENTS, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that environment and extension collection took place.
  EXPECT_TRUE(HasCollectedEnvironmentAndExtensionData());

  // Verify that the most recent download was looked for.
  EXPECT_TRUE(HasCreatedDownloadFinder());

  // Verify that report upload took place and contained the incident,
  // environment data, and download details.
  ExpectTestIncidentUploadWithBinaryDownload(2);

  // Verify that the download finder and the uploader were destroyed.
  ASSERT_TRUE(DownloadFinderDestroyed());
  ASSERT_TRUE(UploaderDestroyed());

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that an incident added during profile initialization when safe browsing
// is off is not uploaded.
TEST_P(IncidentReportingServiceTest, NoSafeBrowsing) {
  CreateIncidentReportingService();
  // Create the profile, thereby causing the test to begin.
  CreateProfile("profile1", SberExists() ? EXTENDED_REPORTING_ONLY : OPT_OUT,
                ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that no report upload took place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that incidents are only uploaded after a profile has been opted into
// extended reporting.
TEST_P(IncidentReportingServiceTest, NoUploadBeforeExtendedReporting) {
  CreateIncidentReportingService();
  // Create the profile, thereby causing the test to begin.
  Profile* profile = CreateProfile("profile1", SAFE_BROWSING_ONLY,
                                   ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
      instance_->GetIncidentReceiver());

  // Add a process-wide test incident.
  receiver->AddIncidentForProcess(MakeTestIncident(nullptr));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Downloads and environment data should have not been collected
  // (DownloadFinder will be created, but should be a no-op since no eligible
  // profiles are loaded).
  ASSERT_TRUE(HasCreatedDownloadFinder());
  ASSERT_FALSE(HasCollectedEnvironmentAndExtensionData());

  // Verify that no report upload took place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());

  EnableReporting(profile->GetPrefs());

  // Add a variation on the incident to the service.
  instance_->GetIncidentReceiver()->AddIncidentForProfile(
      profile, MakeTestIncident("squids"));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that environment collection took place.
  EXPECT_TRUE(HasCollectedEnvironmentAndExtensionData());

  // Verify that the most recent download was looked for.
  EXPECT_TRUE(HasCreatedDownloadFinder());

  // Verify that a report upload took place, and only the second incident was
  // uploaded.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that no incident report is uploaded if there is no recent download.
TEST_P(IncidentReportingServiceTest, NoDownloadNoUpload) {
  CreateIncidentReportingService();
  // Tell the fixture to return no downloads found.
  SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_NO_DOWNLOADS);

  // Create the profile, thereby causing the test to begin.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that the download finder was run but that no report upload took
  // place.
  EXPECT_TRUE(HasCreatedDownloadFinder());
  AssertNoUpload();
  EXPECT_TRUE(DownloadFinderDestroyed());

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that two incidents of the same type with different payloads lead to an
// upload even if the first one is pruned.
TEST_P(IncidentReportingServiceTest, NoDownloadPrunedIncidentOneUpload) {
  CreateIncidentReportingService();
  // Tell the fixture to return no downloads found.
  SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_NO_DOWNLOADS);

  // Create the profile, thereby causing the test to begin.
  Profile* profile = CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Assert that no report upload took place.
  AssertNoUpload();

  // Tell the fixture to return a download now.
  SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_DOWNLOADS_FOUND);

  // Add a variation on the incident to the service.
  instance_->GetIncidentReceiver()->AddIncidentForProfile(
      profile, MakeTestIncident("leeches"));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that an additional report upload took place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that an identical incident added after an incident is pruned due to not
// having a download does not lead to an upload.
TEST_P(IncidentReportingServiceTest, NoDownloadPrunedSameIncidentNoUpload) {
  CreateIncidentReportingService();
  // Tell the fixture to return no downloads found.
  SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_NO_DOWNLOADS);

  // Create the profile, thereby causing the test to begin.
  Profile* profile = CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Assert that no report upload took place.
  AssertNoUpload();

  // Tell the fixture to return a download now.
  SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_DOWNLOADS_FOUND);

  // Add the incident to the service again.
  AddTestIncident(profile);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that no additional report upload took place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that no incident report is uploaded if there is no recent download.
TEST_P(IncidentReportingServiceTest, NoProfilesNoUpload) {
  CreateIncidentReportingService();
  // Tell the fixture to pretend there are no profiles eligible for finding
  // downloads.
  SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_NO_PROFILES);

  // Create the profile, thereby causing the test to begin.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that the download finder was run but that no report upload took
  // place.
  EXPECT_TRUE(HasCreatedDownloadFinder());
  AssertNoUpload();
  // Although CreateDownloadFinder was called, no instance was returned so there
  // is nothing to have been destroyed.
  EXPECT_FALSE(DownloadFinderDestroyed());

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that an identical incident added after upload is not uploaded again.
TEST_P(IncidentReportingServiceTest, OneIncidentOneUpload) {
  CreateIncidentReportingService();
  // Create the profile, thereby causing the test to begin.
  Profile* profile = CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that report upload took place and contained the incident and
  // environment data.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Add the incident to the service again.
  AddTestIncident(profile);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that no additional report upload took place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that two incidents of the same type with different payloads lead to two
// uploads.
TEST_P(IncidentReportingServiceTest, TwoIncidentsTwoUploads) {
  CreateIncidentReportingService();
  // Create the profile, thereby causing the test to begin.
  Profile* profile = CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that report upload took place and contained the incident and
  // environment data.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Add a variation on the incident to the service.
  instance_->GetIncidentReceiver()->AddIncidentForProfile(
      profile, MakeTestIncident("leeches"));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that an additional report upload took place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that the same incident added for two different profiles in sequence
// results in two uploads.
TEST_P(IncidentReportingServiceTest, TwoProfilesTwoUploads) {
  CreateIncidentReportingService();
  // Create the profile, thereby causing the test to begin.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that report upload took place and contained the incident and
  // environment data.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Create a second profile with its own incident on addition.
  CreateProfile(
      "profile2",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that a second report upload took place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that an upload succeeds if the profile is destroyed while it is
// pending.
TEST_P(IncidentReportingServiceTest, ProfileDestroyedDuringUpload) {
  CreateIncidentReportingService();
  // Create a profile for which an incident will be added.
  Profile* profile = CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_ADD_INCIDENT, std::nullopt);

  // Hook up a callback to run when the upload is started that will post a task
  // to delete the profile. This task will run before the upload finishes.
  DeleteProfileOnUpload(profile);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that report upload took place and contained the incident and
  // environment data.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());

  // The lack of a crash indicates that the deleted profile was not accessed by
  // the service while handling the upload response.
}

// Tests that no upload results from adding an incident that is not affiliated
// with a profile.
TEST_P(IncidentReportingServiceTest, ProcessWideNoProfileNoUpload) {
  CreateIncidentReportingService();
  // Add the test incident.
  AddTestIncident(nullptr);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // No upload should have taken place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that there is an upload when a profile is present for a proc-wide
// incident and that pruning works.
TEST_P(IncidentReportingServiceTest, ProcessWideOneUpload) {
  CreateIncidentReportingService();
  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Add the test incident.
  AddTestIncident(nullptr);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // An upload should have taken place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Add the incident to the service again.
  AddTestIncident(nullptr);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that no additional report upload took place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that two process-wide incidents of the same type with different
// payloads added via the same callback lead to two uploads.
TEST_P(IncidentReportingServiceTest, ProcessWideTwoUploads) {
  CreateIncidentReportingService();
  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Add the test incident.
  std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
      instance_->GetIncidentReceiver());
  receiver->AddIncidentForProcess(MakeTestIncident(nullptr));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // An upload should have taken place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Add a variation on the incident to the service.
  receiver->AddIncidentForProcess(MakeTestIncident("leeches"));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that an additional report upload took place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that there is no upload when a profile appears after a proc-wide
// incident.
TEST_P(IncidentReportingServiceTest, ProcessWideNoUploadAfterProfile) {
  CreateIncidentReportingService();
  // Add the test incident.
  AddTestIncident(nullptr);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that no report upload took place.
  AssertNoUpload();

  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // No upload should have taken place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

TEST_P(IncidentReportingServiceTest, NoCollectionWithoutIncident) {
  CreateIncidentReportingService();
  // Register a callback.
  RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Confirm that the callback was not run.
  ASSERT_FALSE(DelayedAnalysisRan());

  // No collection should have taken place.
  ASSERT_FALSE(HasCollectedEnvironmentAndExtensionData());

  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Confirm that the callback was run.
  ASSERT_TRUE(DelayedAnalysisRan());

  // Still no collection should have taken place.
  ASSERT_FALSE(HasCollectedEnvironmentAndExtensionData());

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that delayed analysis callbacks are called following the addition of a
// profile that participates in safe browsing extended reporting.
TEST_P(IncidentReportingServiceTest, AnalysisAfterProfile) {
  CreateIncidentReportingService();
  // Register a callback.
  RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Not run yet.
  ASSERT_FALSE(DelayedAnalysisRan());

  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // And now they have.
  ASSERT_TRUE(DelayedAnalysisRan());

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that delayed analysis callbacks are called following their registration
// when a profile that participates in safe browsing extended reporting is
// already present.
TEST_P(IncidentReportingServiceTest, AnalysisWhenRegisteredWithProfile) {
  CreateIncidentReportingService();
  // Add a profile that participates in safe browsing.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Register a callback.
  RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Confirm that the callbacks were run.
  ASSERT_TRUE(DelayedAnalysisRan());

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that no upload results from a delayed analysis incident when no
// safe browsing extended reporting profile is present.
TEST_P(IncidentReportingServiceTest, DelayedAnalysisNoProfileNoUpload) {
  CreateIncidentReportingService();
  // Register a callback that will add an incident.
  RegisterAnalysis(ON_DELAYED_ANALYSIS_ADD_INCIDENT);

  // Add a profile that does not participate in safe browsing extended
  // reporting.
  CreateProfile("profile1", SAFE_BROWSING_ONLY, ON_PROFILE_ADDITION_NO_ACTION,
                std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // The callback should not have been run.
  ASSERT_FALSE(DelayedAnalysisRan());

  // No upload should have taken place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that there is an upload when a profile is present for a delayed
// analysis incident and that pruning works.
TEST_P(IncidentReportingServiceTest, DelayedAnalysisOneUpload) {
  CreateIncidentReportingService();
  // Register a callback that will add an incident.
  RegisterAnalysis(ON_DELAYED_ANALYSIS_ADD_INCIDENT);

  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // The callback should have been run.
  ASSERT_TRUE(DelayedAnalysisRan());

  // An upload should have taken place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Add the incident to the service again.
  AddTestIncident(nullptr);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that no additional report upload took place.
  AssertNoUpload();

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that the service stops processing when no download is found.
TEST_P(IncidentReportingServiceTest, NoDownloadNoWaiting) {
  CreateIncidentReportingService();
  // Tell the fixture to return no downloads found.
  SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_NO_DOWNLOADS);

  // Register a callback.
  RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);

  // Add a profile that participates in safe browsing extended reporting.
  Profile* profile = CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Add an incident.
  AddTestIncident(profile);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Verify that the download finder was run but that no report upload took
  // place.
  EXPECT_TRUE(HasCreatedDownloadFinder());
  AssertNoUpload();
  EXPECT_TRUE(DownloadFinderDestroyed());

  // Ensure that the report is dropped.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that the service sends the report if a non-binary download is found.
TEST_P(IncidentReportingServiceTest, NonBinaryDownloadStillUploads) {
  CreateIncidentReportingService();
  // Tell the fixture to return only the last non-binary download.
  SetCreateDownloadFinderAction(
      ON_CREATE_DOWNLOAD_FINDER_NON_BINARY_DOWNLOAD_FOUND);

  // Register a callback.
  RegisterAnalysis(ON_DELAYED_ANALYSIS_ADD_INCIDENT);

  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Confirm that the callbacks were run.
  ASSERT_TRUE(DelayedAnalysisRan());

  // Verify that a report upload took place.
  ExpectTestIncidentUploadWithNonBinaryDownload(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Tests that the service can send both a binary and non-binary download.
TEST_P(IncidentReportingServiceTest, UploadsWithBothDownloadTypes) {
  CreateIncidentReportingService();
  // Tell the fixture to return only the last non-binary download.
  SetCreateDownloadFinderAction(ON_CREATE_DOWNLOAD_FINDER_DOWNLOADS_FOUND);

  // Register a callback.
  RegisterAnalysis(ON_DELAYED_ANALYSIS_ADD_INCIDENT);

  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // Confirm that the callbacks were run.
  ASSERT_TRUE(DelayedAnalysisRan());

  // Verify that a report upload took place.
  ExpectTestIncidentUploadWithBothDownloads(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Test that a profile's prune state is properly cleaned upon load.
TEST_P(IncidentReportingServiceTest, CleanLegacyPruneState) {
  CreateIncidentReportingService();
  const std::string blocklist_load_type(base::NumberToString(
      static_cast<int>(safe_browsing::IncidentType::OBSOLETE_BLOCKLIST_LOAD)));
  const std::string preference_type(base::NumberToString(
      static_cast<int>(safe_browsing::IncidentType::TRACKED_PREFERENCE)));

  // Set up a prune state dict with data to be cleared (and not).
  base::DictValue incidents_sent;
  base::DictValue type_dict;
  type_dict.Set("foo", "47");
  incidents_sent.Set(blocklist_load_type, std::move(type_dict));
  type_dict = base::DictValue();
  type_dict.Set("bar", "43");
  incidents_sent.Set(preference_type, std::move(type_dict));

  // Add a profile.
  Profile* profile = CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::move(incidents_sent));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  const base::DictValue& new_state =
      profile->GetPrefs()->GetDict(prefs::kSafeBrowsingIncidentsSent);
  // The legacy value must be gone.
  ASSERT_FALSE(new_state.Find(blocklist_load_type));
  // But other data must be untouched.
  ASSERT_TRUE(new_state.Find(preference_type));
}

// Tests that an identical incident added after an incident is pruned and
// cleared leads to an upload.
TEST_P(IncidentReportingServiceTest, ProcessWideUploadClearUpload) {
  CreateIncidentReportingService();
  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
      instance_->GetIncidentReceiver());

  // Add the test incident.
  receiver->AddIncidentForProcess(MakeTestIncident(nullptr));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // An upload should have taken place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Clear incident data.
  receiver->ClearIncidentForProcess(MakeTestIncident(nullptr));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // No uploads should have taken place.
  ExpectTestIncidentUploadWithBinaryDownload(0);

  // Add the incident to the service again.
  receiver->AddIncidentForProcess(MakeTestIncident(nullptr));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // An upload should have taken place.
  ExpectTestIncidentUploadWithBinaryDownload(1);

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

TEST_P(IncidentReportingServiceTest, ClearProcessIncidentOnCleanState) {
  CreateIncidentReportingService();
  // Add a profile that participates in safe browsing extended reporting.
  CreateProfile(
      "profile1",
      SberExists() ? SAFE_BROWSING_AND_EXTENDED_REPORTING : ENHANCED_PROTECTION,
      ON_PROFILE_ADDITION_NO_ACTION, std::nullopt);

  std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
      instance_->GetIncidentReceiver());

  // Clear incident data.
  receiver->ClearIncidentForProcess(MakeTestIncident(nullptr));

  // Let all tasks run.
  task_environment_.FastForwardUntilNoTasksRemain();

  // No uploads should have taken place.
  ExpectTestIncidentUploadWithBinaryDownload(0);

  // Downloads and environment data should have not been collected.
  ASSERT_FALSE(HasCreatedDownloadFinder());
  ASSERT_FALSE(HasCollectedEnvironmentAndExtensionData());

  // Ensure that no report processing remains.
  ASSERT_FALSE(instance_->IsProcessingReport());
}

// Parallel uploads
// Shutdown during processing
// environment colection taking longer than incident delay timer
// environment colection taking longer than incident delay timer, and then
// another incident arriving

INSTANTIATE_TEST_SUITE_P(All, IncidentReportingServiceTest, testing::Bool());

}  // namespace
