text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // 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/bind.h"
  #include "base/callback.h"
  #include "base/lazy_instance.h"
  #include "base/macros.h"
  #include "base/strings/string_number_conversions.h"
  #include "base/strings/utf_string_conversions.h"
  #include "base/test/mock_entropy_provider.h"
  #include "base/test/scoped_feature_list.h"
  #include "base/test/scoped_mock_time_message_loop_task_runner.h"
  #include "base/threading/thread_local.h"
  #include "base/threading/thread_task_runner_handle.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/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/safe_browsing_prefs.h"
  #include "components/safe_browsing/core/proto/csd.pb.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 defined(OS_WIN)
  #include "base/test/test_reg_util_win.h"
  #endif
  
  // 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.
  class IncidentReportingServiceTest : public testing::Test {
   protected:
    // 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::TimeDelta::FromMilliseconds(5),
                                     task_runner),
            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);
        test_instance_.Get().Set(this);
      }
  
      ~TestIncidentReportingService() override {
        test_instance_.Get().Set(nullptr);
      }
  
      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_.Get().Get();
      }
  
      static void CollectEnvironmentData(
          safe_browsing::ClientIncidentReport_EnvironmentData* data) {
        current().collect_environment_callback_.Run(data);
      }
  
      static base::LazyInstance<base::ThreadLocalPointer<
          TestIncidentReportingService> >::Leaky test_instance_;
  
      PreProfileAddCallback pre_profile_add_callback_;
      CollectEnvironmentCallback collect_environment_callback_;
      CreateDownloadFinderCallback create_download_finder_callback_;
      StartUploadCallback start_upload_callback_;
      bool extension_collected_;
    };
  
    // 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,
    };
  
    // 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.
    };
  
    static const char kFakeOsName[];
    static const char kFakeDownloadToken[];
    static const char kFakeDownloadHost[];
    static const char kTestTrackedPrefPath[];
    static const char kFakeExtensionId[];
  
    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 defined(OS_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_attributes_storage()
          ->DisableProfileMetricsForTesting();
    }
  
    void CreateIncidentReportingService() {
      scoped_feature_list_.InitAndEnableFeature(
          safe_browsing::kIncidentReportingEnableUpload);
  
      instance_ = std::make_unique<TestIncidentReportingService>(
          base::ThreadTaskRunnerHandle::Get(),
          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::unique_ptr<base::Value> 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::SetExtendedReportingPrefForTests(
          prefs.get(),
          safe_browsing_opt_in == EXTENDED_REPORTING_ONLY ||
              safe_browsing_opt_in == SAFE_BROWSING_AND_EXTENDED_REPORTING);
      if (incidents_sent)
        prefs->Set(prefs::kSafeBrowsingIncidentsSent, *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)
          std::string(),  // supervised_user_id (unused)
          TestingProfile::TestingFactories());
      mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
      return profile;
    }
  
    // 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_;
  
    // Replaces the main MessageLoop's TaskRunner with a TaskRunner on which time
    // is mocked to allow testing of things bound to timers below.
    base::ScopedMockTimeMessageLoopTaskRunner mock_time_task_runner_;
  
    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::ThreadTaskRunnerHandle::Get()->PostTask(
            FROM_HERE,
            base::BindOnce(&FakeUploader::FinishUpload, base::Unretained(this)));
      }
      ~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_;
  
      DISALLOW_COPY_AND_ASSIGN(FakeUploader);
    };
  
    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::ThreadTaskRunnerHandle::Get()->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() override { std::move(on_deleted_).Run(); }
  
     private:
      explicit FakeDownloadFinder(base::OnceClosure on_deleted)
          : on_deleted_(std::move(on_deleted)) {}
  
      base::OnceClosure on_deleted_;
  
      DISALLOW_COPY_AND_ASSIGN(FakeDownloadFinder);
    };
  
    // 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::ThreadTaskRunnerHandle::Get()->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 defined(OS_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_;
  };
  
  // static
  base::LazyInstance<base::ThreadLocalPointer<
      IncidentReportingServiceTest::TestIncidentReportingService> >::Leaky
      IncidentReportingServiceTest::TestIncidentReportingService::test_instance_ =
          LAZY_INSTANCE_INITIALIZER;
  
  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);
  }
  
  const char IncidentReportingServiceTest::kFakeOsName[] = "fakedows";
  const char IncidentReportingServiceTest::kFakeDownloadToken[] = "fakedlt";
  const char IncidentReportingServiceTest::kFakeDownloadHost[] = "chromium.org";
  const char IncidentReportingServiceTest::kTestTrackedPrefPath[] = "some_pref";
  const char IncidentReportingServiceTest::kFakeExtensionId[] = "fakeExtensionId";
  
  // Tests that an incident added during profile initialization when safe browsing
  // extended reporting is on is uploaded.
  TEST_F(IncidentReportingServiceTest, AddIncident) {
    CreateIncidentReportingService();
  
    // Create the profile, thereby causing the test to begin.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, CoalesceIncidents) {
    CreateIncidentReportingService();
  
    // Create the profile, thereby causing the test to begin.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_ADD_TWO_INCIDENTS, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, NoSafeBrowsing) {
    CreateIncidentReportingService();
    // Create the profile, thereby causing the test to begin.
    CreateProfile("profile1", EXTENDED_REPORTING_ONLY,
                  ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, NoUploadBeforeExtendedReporting) {
    CreateIncidentReportingService();
    // Create the profile, thereby causing the test to begin.
    Profile* profile = CreateProfile("profile1", SAFE_BROWSING_ONLY,
                                     ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
        instance_->GetIncidentReceiver());
  
    // Add a process-wide test incident.
    receiver->AddIncidentForProcess(MakeTestIncident(nullptr));
  
    // Let all tasks run.
    mock_time_task_runner_->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());
  
    safe_browsing::SetExtendedReportingPrefForTests(profile->GetPrefs(), true);
  
    // Add a variation on the incident to the service.
    instance_->GetIncidentReceiver()->AddIncidentForProfile(
        profile, MakeTestIncident("squids"));
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                      ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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.
    mock_time_task_runner_->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_F(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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                      ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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.
    mock_time_task_runner_->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_F(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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, OneIncidentOneUpload) {
    CreateIncidentReportingService();
    // Create the profile, thereby causing the test to begin.
    Profile* profile =
        CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                      ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, TwoIncidentsTwoUploads) {
    CreateIncidentReportingService();
    // Create the profile, thereby causing the test to begin.
    Profile* profile =
        CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                      ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, TwoProfilesTwoUploads) {
    CreateIncidentReportingService();
    // Create the profile, thereby causing the test to begin.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, ProfileDestroyedDuringUpload) {
    CreateIncidentReportingService();
    // Create a profile for which an incident will be added.
    Profile* profile =
        CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                      ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
  
    // 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.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, ProcessWideNoProfileNoUpload) {
    CreateIncidentReportingService();
    // Add the test incident.
    AddTestIncident(nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, ProcessWideOneUpload) {
    CreateIncidentReportingService();
    // Add a profile that participates in safe browsing extended reporting.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Add the test incident.
    AddTestIncident(nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
    // An upload should have taken place.
    ExpectTestIncidentUploadWithBinaryDownload(1);
  
    // Add the incident to the service again.
    AddTestIncident(nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, ProcessWideTwoUploads) {
    CreateIncidentReportingService();
    // Add a profile that participates in safe browsing extended reporting.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Add the test incident.
    std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
        instance_->GetIncidentReceiver());
    receiver->AddIncidentForProcess(MakeTestIncident(nullptr));
  
    // Let all tasks run.
    mock_time_task_runner_->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.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, ProcessWideNoUploadAfterProfile) {
    CreateIncidentReportingService();
    // Add the test incident.
    AddTestIncident(nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
    // Verify that no report upload took place.
    AssertNoUpload();
  
    // Add a profile that participates in safe browsing extended reporting.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
    // No upload should have taken place.
    AssertNoUpload();
  
    // Ensure that no report processing remains.
    ASSERT_FALSE(instance_->IsProcessingReport());
  }
  
  TEST_F(IncidentReportingServiceTest, NoCollectionWithoutIncident) {
    CreateIncidentReportingService();
    // Register a callback.
    RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);
  
    // Let all tasks run.
    mock_time_task_runner_->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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, AnalysisAfterProfile) {
    CreateIncidentReportingService();
    // Register a callback.
    RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);
  
    // Let all tasks run.
    mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
    // Not run yet.
    ASSERT_FALSE(DelayedAnalysisRan());
  
    // Add a profile that participates in safe browsing extended reporting.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(IncidentReportingServiceTest, AnalysisWhenRegisteredWithProfile) {
    CreateIncidentReportingService();
    // Add a profile that participates in safe browsing.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Register a callback.
    RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(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,
                  nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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.
    mock_time_task_runner_->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_F(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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                      ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Add an incident.
    AddTestIncident(profile);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(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", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    // Let all tasks run.
    mock_time_task_runner_->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_F(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).
    std::unique_ptr<base::DictionaryValue> incidents_sent(
        new base::DictionaryValue());
    auto type_dict = std::make_unique<base::DictionaryValue>();
    type_dict->SetKey("foo", base::Value("47"));
    incidents_sent->SetKey(blocklist_load_type,
                           base::Value::FromUniquePtrValue(std::move(type_dict)));
    type_dict = std::make_unique<base::DictionaryValue>();
    type_dict->SetKey("bar", base::Value("43"));
    incidents_sent->SetKey(preference_type,
                           base::Value::FromUniquePtrValue(std::move(type_dict)));
  
    // Add a profile.
    Profile* profile =
        CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                      ON_PROFILE_ADDITION_NO_ACTION, std::move(incidents_sent));
  
    // Let all tasks run.
    mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
    const base::DictionaryValue* new_state =
        profile->GetPrefs()->GetDictionary(prefs::kSafeBrowsingIncidentsSent);
    // The legacy value must be gone.
    ASSERT_FALSE(new_state->HasKey(blocklist_load_type));
    // But other data must be untouched.
    ASSERT_TRUE(new_state->HasKey(preference_type));
  }
  
  // Tests that an identical incident added after an incident is pruned and
  // cleared leads to an upload.
  TEST_F(IncidentReportingServiceTest, ProcessWideUploadClearUpload) {
    CreateIncidentReportingService();
    // Add a profile that participates in safe browsing extended reporting.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
        instance_->GetIncidentReceiver());
  
    // Add the test incident.
    receiver->AddIncidentForProcess(MakeTestIncident(nullptr));
  
    // Let all tasks run.
    mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
    // An upload should have taken place.
    ExpectTestIncidentUploadWithBinaryDownload(1);
  
    // Clear incident data.
    receiver->ClearIncidentForProcess(MakeTestIncident(nullptr));
  
    // Let all tasks run.
    mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
    // No uploads should have taken place.
    ExpectTestIncidentUploadWithBinaryDownload(0);
  
    // Add the incident to the service again.
    receiver->AddIncidentForProcess(MakeTestIncident(nullptr));
  
    // Let all tasks run.
    mock_time_task_runner_->FastForwardUntilNoTasksRemain();
  
    // An upload should have taken place.
    ExpectTestIncidentUploadWithBinaryDownload(1);
  
    // Ensure that no report processing remains.
    ASSERT_FALSE(instance_->IsProcessingReport());
  }
  
  TEST_F(IncidentReportingServiceTest, ClearProcessIncidentOnCleanState) {
    CreateIncidentReportingService();
    // Add a profile that participates in safe browsing extended reporting.
    CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
                  ON_PROFILE_ADDITION_NO_ACTION, nullptr);
  
    std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
        instance_->GetIncidentReceiver());
  
    // Clear incident data.
    receiver->ClearIncidentForProcess(MakeTestIncident(nullptr));
  
    // Let all tasks run.
    mock_time_task_runner_->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
  