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

#include <memory>
#include <optional>
#include <string>

#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/metrics/chrome_metrics_service_client.h"
#include "chrome/browser/metrics/chrome_metrics_services_manager_client.h"
#include "chrome/browser/metrics/testing/metrics_consent_override.h"
#include "chrome/browser/metrics/testing/metrics_reporting_pref_helper.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/unified_consent/unified_consent_service_factory.h"
#include "components/metrics/demographics/demographic_metrics_provider.h"
#include "components/metrics/demographics/demographic_metrics_test_utils.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/sync/base/features.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_service_impl.h"
#include "components/sync/service/sync_token_status.h"
#include "components/sync/test/fake_server_network_resources.h"
#include "components/ukm/ukm_recorder_observer.h"
#include "components/ukm/ukm_service.h"
#include "components/ukm/ukm_test_helper.h"
#include "components/unified_consent/unified_consent_service.h"
#include "components/version_info/version_info.h"
#include "components/webui/chrome_urls/pref_names.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "content/public/test/navigation_handle_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/network/test/test_network_quality_tracker.h"
#include "third_party/metrics_proto/ukm/report.pb.h"
#include "third_party/metrics_proto/user_demographics.pb.h"
#include "url/url_constants.h"

#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#else
#include "chrome/browser/flags/android/chrome_feature_list.h"
#include "chrome/browser/flags/android/chrome_session_state.h"
#include "chrome/browser/ui/android/tab_model/tab_model.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#include "chrome/browser/ui/android/tab_model/tab_model_observer.h"
#include "chrome/browser/ui/android/tab_model/tab_model_test_helper.h"
#include "chrome/test/base/android/android_browser_test.h"
#include "content/public/browser/web_contents.h"
#endif  // !BUILDFLAG(IS_ANDROID)

#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/profiles/profile_picker.h"
#include "chrome/browser/ui/profiles/profile_ui_test_utils.h"
#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)

namespace metrics {
namespace {

#if !BUILDFLAG(IS_ANDROID)
using PlatformBrowser = BrowserWindowInterface*;
#else
typedef std::unique_ptr<TestTabModel> PlatformBrowser;
#endif  // !BUILDFLAG(IS_ANDROID)

// Clears the specified data using BrowsingDataRemover.
void ClearBrowsingData(Profile* profile) {
  content::BrowsingDataRemover* remover = profile->GetBrowsingDataRemover();
  content::BrowsingDataRemoverCompletionObserver observer(remover);
  remover->RemoveAndReply(
      base::Time(), base::Time::Max(),
      chrome_browsing_data_remover::DATA_TYPE_HISTORY,
      content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB, &observer);
  observer.BlockUntilCompletion();
  // Make sure HistoryServiceObservers have a chance to be notified.
  content::RunAllTasksUntilIdle();
}

ukm::UkmService* GetUkmService() {
  return g_browser_process->GetMetricsServicesManager()->GetUkmService();
}

class TestUkmRecorderObserver : public ukm::UkmRecorderObserver {
 public:
  explicit TestUkmRecorderObserver(ukm::UkmRecorderImpl* ukm_recorder)
      : ukm_recorder_(ukm_recorder) {
    ukm_recorder_->AddUkmRecorderObserver(base::flat_set<uint64_t>(), this);
  }

  ~TestUkmRecorderObserver() override {
    ukm_recorder_->RemoveUkmRecorderObserver(this);
  }

  void OnEntryAdded(ukm::mojom::UkmEntryPtr entry) override {}

  void OnUpdateSourceURL(ukm::SourceId source_id,
                         const std::vector<GURL>& urls) override {}

  void OnPurgeRecordingsWithUrlScheme(const std::string& url_scheme) override {}

  void OnPurge() override {}

  void ExpectAllowedStateChanged(ukm::UkmConsentState expected_state) {
    expected_allowed_state_ = expected_state;
    expected_allowed_bool_ = std::nullopt;
    base::RunLoop loop;
    quit_closure_ = loop.QuitClosure();
    loop.Run();
  }

  void ExpectAllowedStateChanged(bool expected_allowed) {
    expected_allowed_bool_ = expected_allowed;
    expected_allowed_state_ = std::nullopt;
    base::RunLoop loop;
    quit_closure_ = loop.QuitClosure();
    loop.Run();
  }

  void OnUkmAllowedStateChanged(ukm::UkmConsentState allowed_state) override {
    if (expected_allowed_state_ && allowed_state == *expected_allowed_state_) {
      std::move(quit_closure_).Run();
    }
  }

  void OnUkmAllowedStateChanged(bool allowed_state) override {
    if (expected_allowed_bool_ && allowed_state == *expected_allowed_bool_) {
      std::move(quit_closure_).Run();
    }
  }

 private:
  std::optional<ukm::UkmConsentState> expected_allowed_state_;
  std::optional<bool> expected_allowed_bool_;
  base::OnceClosure quit_closure_;
  raw_ptr<ukm::UkmRecorderImpl> ukm_recorder_;
};

}  // namespace

// Test fixture that provides access to some UKM internals.
class UkmBrowserTestBase : public SyncTest {
 public:
  UkmBrowserTestBase() : SyncTest(SINGLE_CLIENT) {
    // Explicitly enable UKM and disable metrics reporting. Disabling metrics
    // reporting should affect only UMA--not UKM.
    scoped_feature_list_.InitWithFeatures({ukm::kUkmFeature},
                                          {internal::kMetricsReportingFeature});
  }

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

#if !BUILDFLAG(IS_ANDROID)
  ukm::UkmSource* NavigateAndGetSource(const GURL& url,
                                       BrowserWindowInterface* browser,
                                       ukm::UkmTestHelper* ukm_test_helper) {
    content::NavigationHandleObserver observer(
        browser->GetTabStripModel()->GetActiveWebContents(), url);
    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, url));
    const ukm::SourceId source_id = ukm::ConvertToSourceId(
        observer.navigation_id(), ukm::SourceIdType::NAVIGATION_ID);
    return ukm_test_helper->GetSource(source_id);
  }
#endif  // !BUILDFLAG(IS_ANDROID)

 protected:
  // Creates and returns a platform-appropriate browser for |profile|.
  PlatformBrowser CreatePlatformBrowser(Profile* profile) {
#if !BUILDFLAG(IS_ANDROID)
    return CreateBrowser(profile);
#else
    std::unique_ptr<TestTabModel> tab_model =
        std::make_unique<TestTabModel>(profile);
    tab_model->SetWebContentsList(
        {content::WebContents::Create(
             content::WebContents::CreateParams(profile))
             .release()});
    TabModelList::AddTabModel(tab_model.get());
    EXPECT_TRUE(content::NavigateToURL(tab_model->GetActiveWebContents(),
                                       GURL("about:blank")));
    return tab_model;
#endif  // !BUILDFLAG(IS_ANDROID)
  }

  // Creates a platform-appropriate incognito browser for |profile|.
  PlatformBrowser CreateIncognitoPlatformBrowser(Profile* profile) {
    EXPECT_TRUE(profile->IsOffTheRecord());
#if !BUILDFLAG(IS_ANDROID)
    return CreateIncognitoBrowser(profile);
#else
    return CreatePlatformBrowser(profile);
#endif  // !BUILDFLAG(IS_ANDROID)
  }

  // Closes |browser| in a way that is appropriate for the platform.
  void ClosePlatformBrowser(PlatformBrowser& browser) {
#if !BUILDFLAG(IS_ANDROID)
    CloseBrowserSynchronously(browser);
#else
    TabModelList::RemoveTabModel(browser.get());
    browser.reset();
#endif  // !BUILDFLAG(IS_ANDROID)
  }

  std::unique_ptr<SyncServiceImplHarness> EnableSyncForProfile(
      Profile* profile) {
    std::unique_ptr<SyncServiceImplHarness> harness =
        SyncServiceImplHarness::Create(
            profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);

    EXPECT_TRUE(harness->SetupSync());

    // If unified consent is enabled, then enable url-keyed-anonymized data
    // collection through the consent service.
    // Note: If unfied consent is not enabled, then UKM will be enabled based on
    // the history sync state.
    unified_consent::UnifiedConsentService* consent_service =
        UnifiedConsentServiceFactory::GetForProfile(profile);
    if (consent_service) {
      consent_service->SetUrlKeyedAnonymizedDataCollectionEnabled(true);
    }

    return harness;
  }

#if !BUILDFLAG(IS_ANDROID)
  Profile* CreateNonSyncProfile() {
    ProfileManager* profile_manager = g_browser_process->profile_manager();
    base::FilePath new_path =
        profile_manager->GenerateNextProfileDirectoryPath();
    Profile& profile =
        profiles::testing::CreateProfileSync(profile_manager, new_path);
    return &profile;
  }
#endif  // !BUILDFLAG(IS_ANDROID)

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

class UkmBrowserTest : public UkmBrowserTestBase {
 public:
  UkmBrowserTest() = default;

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

#if BUILDFLAG(IS_ANDROID)
  void PreRunTestOnMainThread() override {
    UkmBrowserTestBase::PreRunTestOnMainThread();
    // At some point during set-up, Android's TabModelList is populated with a
    // TabModel. However, it is desirable to begin the tests with an empty
    // TabModelList to avoid complicated logic in CreatePlatformBrowser.
    //
    // For example, if the pre-existing TabModel is not deleted and if the first
    // tab created in a test is an incognito tab, then CreatePlatformBrowser
    // would need to remove the pre-existing TabModel and add a new one.
    // Having an empty TabModelList allows us to simply add the appropriate
    // TabModel.
    EXPECT_EQ(1U, TabModelList::models().size());
    initial_tab_model_ = TabModelList::models()[0].get();
    TabModelList::RemoveTabModel(initial_tab_model_);
    EXPECT_EQ(0U, TabModelList::models().size());
  }

  void PostRunTestOnMainThread() override {
    // Restore the initial tab model so the browser can shut down cleanly.
    TabModelList::AddTabModel(initial_tab_model_);
  }
#endif  // BUILDFLAG(IS_ANDROID)

 private:
#if BUILDFLAG(IS_ANDROID)
  raw_ptr<TabModel> initial_tab_model_;
#endif  // !BUILDFLAG(IS_ANDROID)
};

// This tests if UKM service is enabled/disabled appropriately based on an
// input bool param. The bool reflects if metrics reporting state is
// enabled/disabled via prefs.
#if !BUILDFLAG(IS_ANDROID)
class UkmConsentParamBrowserTest : public UkmBrowserTestBase,
                                   public testing::WithParamInterface<bool> {
 public:
  UkmConsentParamBrowserTest() = default;

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

  static bool IsMetricsAndCrashReportingEnabled() {
    return ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
  }

  // InProcessBrowserTest overrides.
  bool SetUpUserDataDirectory() override {
    local_state_path_ = SetUpUserDataDirectoryForTesting(
        is_metrics_reporting_enabled_initial_value());
    return !local_state_path_.empty();
  }

  void CreatedBrowserMainParts(content::BrowserMainParts* parts) override {
    UkmBrowserTestBase::CreatedBrowserMainParts(parts);
    // IsMetricsReportingEnabled() in non-official builds always returns false.
    // Enable the official build checks so that this test can work in both
    // official and non-official builds.
    ChromeMetricsServiceAccessor::SetForceIsMetricsReportingEnabledPrefLookup(
        true);
  }

  bool is_metrics_reporting_enabled_initial_value() const { return GetParam(); }

 private:
  base::FilePath local_state_path_;
};
#endif  // !BUILDFLAG(IS_ANDROID)

// Test the reporting of the synced user's birth year and gender.
class UkmBrowserTestWithDemographics
    : public UkmBrowserTestBase,
      public testing::WithParamInterface<test::DemographicsTestParams> {
 public:
  UkmBrowserTestWithDemographics() : UkmBrowserTestBase() {
    test::DemographicsTestParams param = GetParam();
    if (param.enable_feature) {
      scoped_feature_list_.InitWithFeatures(
          // enabled_features
          {kDemographicMetricsReporting,
           ukm::kReportUserNoisedUserBirthYearAndGender},
          // disabled_features
          {});
    } else {
      scoped_feature_list_.InitWithFeatures(
          // enabled_features
          {},
          // disabled_features
          {kDemographicMetricsReporting,
           ukm::kReportUserNoisedUserBirthYearAndGender});
    }
  }

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

  PrefService* local_state() { return g_browser_process->local_state(); }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

// Make sure that UKM is disabled while an incognito window is open.
// LINT.IfChange(RegularPlusIncognitoCheck)
// Disabled on Android due to flakiness. See crbug.com/355609356.
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_RegularPlusIncognitoCheck DISABLED_RegularPlusIncognitoCheck
#else
#define MAYBE_RegularPlusIncognitoCheck RegularPlusIncognitoCheck
#endif
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MAYBE_RegularPlusIncognitoCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  PlatformBrowser browser1 = CreatePlatformBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  Profile* incognito_profile =
      profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);
  PlatformBrowser incognito_browser1 =
      CreateIncognitoPlatformBrowser(incognito_profile);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  // Opening another regular browser mustn't enable UKM.
  PlatformBrowser browser2 = CreatePlatformBrowser(profile);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  // Opening and closing another Incognito browser mustn't enable UKM.
  PlatformBrowser incognito_browser2 =
      CreateIncognitoPlatformBrowser(incognito_profile);
  ClosePlatformBrowser(incognito_browser2);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  ClosePlatformBrowser(browser2);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  ClosePlatformBrowser(incognito_browser1);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  // Client ID should not have been reset.
  EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());

  ClosePlatformBrowser(browser1);
}
// LINT.ThenChange(/ios/chrome/browser/metrics/ukm_egtest.mm:RegularPlusIncognitoCheck)

// Make sure opening a real window after Incognito doesn't enable UKM.
// LINT.IfChange(IncognitoPlusRegularCheck)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, IncognitoPlusRegularCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  Profile* incognito_profile =
      profile->GetPrimaryOTRProfile(/*create_if_needed=*/true);
  PlatformBrowser incognito_browser =
      CreateIncognitoPlatformBrowser(incognito_profile);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  PlatformBrowser browser = CreatePlatformBrowser(profile);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  ClosePlatformBrowser(incognito_browser);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());

  ClosePlatformBrowser(browser);
}
// LINT.ThenChange(/ios/chrome/browser/metrics/ukm_egtest.mm:IncognitoPlusRegularCheck)

// Make sure that UKM is disabled while a guest profile's window is open.
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, RegularPlusGuestCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* regular_browser = CreateBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();

  // Create browser for guest profile.
  BrowserWindowInterface* guest_browser =
      InProcessBrowserTest::CreateGuestBrowser();
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  CloseBrowserSynchronously(guest_browser);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  // Client ID should not have been reset.
  EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());

  CloseBrowserSynchronously(regular_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)

// ProfilePicker and System profile do not exist on ChromeOS and on Android.
#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
// Displaying the ProfilePicker implicitly creates a System Profile.
// System Profile shouldn't have any effect on the UKM Enable Status.
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, ProfilePickerCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* regular_browser = CreateBrowser(profile);
  ASSERT_TRUE(ukm_test_helper.IsRecordingEnabled());

  // ProfilePicker creates a SystemProfile.
  ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
      ProfilePicker::EntryPoint::kProfileMenuManageProfiles));
  profiles::testing::WaitForPickerWidgetCreated();

  Profile* system_profile =
      g_browser_process->profile_manager()->GetProfileByPath(
          ProfileManager::GetSystemProfilePath());
  ASSERT_TRUE(system_profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());

  ProfilePicker::Hide();
  profiles::testing::WaitForPickerClosed();
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());

  CloseBrowserSynchronously(regular_browser);
}
#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)

// Not applicable to Android as it doesn't have multiple profiles.
#if !BUILDFLAG(IS_ANDROID)
// Make sure that UKM is disabled while an non-sync profile's window is open.
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, OpenNonSyncCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* sync_browser = CreateBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  Profile* nonsync_profile = CreateNonSyncProfile();
  BrowserWindowInterface* nonsync_browser = CreateBrowser(nonsync_profile);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  CloseBrowserSynchronously(nonsync_browser);
  // TODO(crbug.com/40530708): UKM doesn't actually get re-enabled yet.
  // EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
#if !BUILDFLAG(IS_CHROMEOS)
  // Client ID should not have been reset.
  EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());
#else
  // When flag is enabled, removing MSBB consent will cause the client id to be
  // reset. In this case a new profile with sync turned off is added which also
  // removes consent.
  EXPECT_NE(original_client_id, ukm_test_helper.GetClientId());
#endif

  CloseBrowserSynchronously(sync_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// Make sure that UKM is disabled when metrics consent is revoked.
// LINT.IfChange(MetricsConsentCheck)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MetricsConsentCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  PlatformBrowser browser = CreatePlatformBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  // Make sure there is a persistent log.
  ukm_test_helper.BuildAndStoreLog();
  EXPECT_TRUE(ukm_test_helper.HasUnsentLogs());

  metrics_consent.Update(false);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_FALSE(ukm_test_helper.HasUnsentLogs());

  metrics_consent.Update(true);

  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  // Client ID should have been reset.
  EXPECT_NE(original_client_id, ukm_test_helper.GetClientId());

  ClosePlatformBrowser(browser);
}
// LINT.ThenChange(/ios/chrome/browser/metrics/ukm_egtest.mm:MetricsConsentCheck)

#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, LogProtoData) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* sync_browser = CreateBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  // Make sure there is a persistent log.
  ukm_test_helper.BuildAndStoreLog();
  EXPECT_TRUE(ukm_test_helper.HasUnsentLogs());

  // Check log contents.
  std::unique_ptr<ukm::Report> report = ukm_test_helper.GetUkmReport();
  EXPECT_EQ(original_client_id, report->client_id());
  // Note: The version number reported in the proto may have a suffix, such as
  // "-64-devel", so use use StartsWith() rather than checking for equality.
  EXPECT_TRUE(base::StartsWith(report->system_profile().app_version(),
                               version_info::GetVersionNumber(),
                               base::CompareCase::SENSITIVE));

  EXPECT_EQ(base::SysInfo::HardwareModelName(),
            report->system_profile().hardware().hardware_class());

  CloseBrowserSynchronously(sync_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// LINT.IfChange(AddSyncedUserBirthYearAndGenderToProtoData)
IN_PROC_BROWSER_TEST_P(UkmBrowserTestWithDemographics,
                       AddSyncedUserBirthYearAndGenderToProtoData) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::DemographicsTestParams param = GetParam();
  test::MetricsConsentOverride metrics_consent(true);

  base::HistogramTester histogram;

  const base::Time now = base::Time::Now();
  test::UpdateNetworkTime(now, g_browser_process->network_time_tracker());
  const int test_birth_year = test::GetMaximumEligibleBirthYear(now);
  const UserDemographicsProto::Gender test_gender =
      UserDemographicsProto::GENDER_FEMALE;

  // Add the test synced user birth year and gender priority prefs to the sync
  // server data.
  test::AddUserBirthYearAndGenderToSyncServer(GetFakeServer()->AsWeakPtr(),
                                              test_birth_year, test_gender);

  Profile* test_profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(test_profile);

  // Make sure that there is only one Profile to allow reporting the user's
  // birth year and gender.
  ASSERT_EQ(1, num_clients());

  PlatformBrowser browser = CreatePlatformBrowser(test_profile);

  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  // Log UKM metrics report.
  ukm_test_helper.BuildAndStoreLog();
  EXPECT_TRUE(ukm_test_helper.HasUnsentLogs());

  // Check the log's content.
  std::unique_ptr<ukm::Report> report = ukm_test_helper.GetUkmReport();
  if (param.expect_reported_demographics) {
    EXPECT_EQ(test::GetNoisedBirthYear(local_state(), test_birth_year),
              report->user_demographics().birth_year());
    EXPECT_EQ(test_gender, report->user_demographics().gender());
  } else {
    EXPECT_FALSE(report->has_user_demographics());
  }

#if !BUILDFLAG(IS_CHROMEOS)
  // Sign out the user to revoke all refresh tokens. This prevents any posted
  // tasks from successfully fetching an access token during the tear-down
  // phase and crashing on a DCHECK. See crbug.com/40704261 for more details.
  harness->SignOutPrimaryAccount();
#endif  // !BUILDFLAG(IS_CHROMEOS)
  ClosePlatformBrowser(browser);
}
// LINT.ThenChange(/ios/chrome/browser/metrics/demographics_egtest.mm:AddSyncedUserBirthYearAndGenderToProtoDataEnabled_ukmBrowsertest)
// ThenChange(/ios/chrome/browser/metrics/demographics_egtest.mm:AddSyncedUserBirthYearAndGenderToProtoDataDisabled_ukmBrowsertest)

#if BUILDFLAG(IS_CHROMEOS)
// Cannot test for the enabled feature on Chrome OS because there are always
// multiple profiles.
static const auto kDemographicsTestParams = testing::Values(
    test::DemographicsTestParams{/*enable_feature=*/false,
                                 /*expect_reported_demographics=*/false});
#else
static const auto kDemographicsTestParams = testing::Values(
    test::DemographicsTestParams{/*enable_feature=*/false,
                                 /*expect_reported_demographics=*/false},
    test::DemographicsTestParams{/*enable_feature=*/true,
                                 /*expect_reported_demographics=*/true});
#endif

INSTANTIATE_TEST_SUITE_P(,
                         UkmBrowserTestWithDemographics,
                         kDemographicsTestParams);

// Verifies that network provider attaches effective connection type correctly
// to the UKM report.
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, NetworkProviderPopulatesSystemProfile) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  // Override network quality to 2G. This should cause the
  // |max_effective_connection_type| in the system profile to be set to 2G.
  g_browser_process->network_quality_tracker()
      ->ReportEffectiveConnectionTypeForTesting(
          net::EFFECTIVE_CONNECTION_TYPE_2G);

  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* sync_browser = CreateBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  // Override network quality to 4G. This should cause the
  // |max_effective_connection_type| in the system profile to be set to 4G.
  g_browser_process->network_quality_tracker()
      ->ReportEffectiveConnectionTypeForTesting(
          net::EFFECTIVE_CONNECTION_TYPE_4G);

  // Make sure there is a persistent log.
  ukm_test_helper.BuildAndStoreLog();
  EXPECT_TRUE(ukm_test_helper.HasUnsentLogs());
  // Check log contents.
  std::unique_ptr<ukm::Report> report = ukm_test_helper.GetUkmReport();

  EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G,
            report->system_profile().network().min_effective_connection_type());
  EXPECT_EQ(SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_4G,
            report->system_profile().network().max_effective_connection_type());

  CloseBrowserSynchronously(sync_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// Verifies that install date is attached.
// Disabled on Android due to flakiness. See crbug.com/355609356.
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_InstallDateProviderPopulatesSystemProfile \
  DISABLED_InstallDateProviderPopulatesSystemProfile
#else
#define MAYBE_InstallDateProviderPopulatesSystemProfile \
  InstallDateProviderPopulatesSystemProfile
#endif
IN_PROC_BROWSER_TEST_F(UkmBrowserTest,
                       MAYBE_InstallDateProviderPopulatesSystemProfile) {
  PrefService* local_state = g_browser_process->local_state();
  local_state->SetInt64(prefs::kInstallDate, 123456);

  ukm::UkmTestHelper ukm_test_helper(GetUkmService());

  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  PlatformBrowser browser = CreatePlatformBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  // Make sure there is a persistent log.
  ukm_test_helper.BuildAndStoreLog();
  EXPECT_TRUE(ukm_test_helper.HasUnsentLogs());
  // Check log contents.
  std::unique_ptr<ukm::Report> report = ukm_test_helper.GetUkmReport();

  // Rounded from the 123456 we set earlier, to nearest hour.
  EXPECT_EQ(122400, report->system_profile().install_date());

  ClosePlatformBrowser(browser);
}

// Make sure that providing consent doesn't enable UKM when sync is disabled.
// Flaky on Android crbug.com/40700711
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_ConsentAddedButNoSyncCheck DISABLED_ConsentAddedButNoSyncCheck
#else
#define MAYBE_ConsentAddedButNoSyncCheck ConsentAddedButNoSyncCheck
#endif
// LINT.IfChange(ConsentAddedButNoSyncCheck)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MAYBE_ConsentAddedButNoSyncCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(false);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  PlatformBrowser browser = CreatePlatformBrowser(profile);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  metrics_consent.Update(true);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());

  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  g_browser_process->GetMetricsServicesManager()->UpdateUploadPermissions();
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());

  ClosePlatformBrowser(browser);
}
// LINT.ThenChange(/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java:ConsentAddedButNoSyncCheck)
// ThenChange(/ios/chrome/browser/metrics/model/ukm_egtest.mm:ConsentAddedButNoSyncCheck)

// Make sure that extension URLs are disabled when an open sync window
// disables it.
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, SingleDisableExtensionsSyncCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* sync_browser = CreateBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(ukm_test_helper.IsExtensionRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  ASSERT_TRUE(
      harness->DisableSelectableType(syncer::UserSelectableType::kExtensions));
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_FALSE(ukm_test_helper.IsExtensionRecordingEnabled());

  ASSERT_TRUE(
      harness->EnableSelectableType(syncer::UserSelectableType::kExtensions));
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(ukm_test_helper.IsExtensionRecordingEnabled());
  // Client ID should not be reset.
  EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());

  CloseBrowserSynchronously(sync_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// Make sure that extension URLs are disabled when any open sync window
// disables it.
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MultiDisableExtensionsSyncCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile1 = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness1 =
      EnableSyncForProfile(profile1);

  BrowserWindowInterface* browser1 = CreateBrowser(profile1);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  Profile* profile2 = CreateNonSyncProfile();
  std::unique_ptr<SyncServiceImplHarness> harness2 =
      EnableSyncForProfile(profile2);
  BrowserWindowInterface* browser2 = CreateBrowser(profile2);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(ukm_test_helper.IsExtensionRecordingEnabled());
#if !BUILDFLAG(IS_CHROMEOS)
  EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());
#else
  // If the feature is enabled, then the client id will have been reset. The
  // client id is reset when the second profile is initially created without any
  // sync preferences enabled or MSBB. With the feature, any consent on to off
  // change for MSBB or App Sync will cause the client id to reset.
  EXPECT_NE(original_client_id, ukm_test_helper.GetClientId());
  original_client_id = ukm_test_helper.GetClientId();
#endif

  ASSERT_TRUE(
      harness2->DisableSelectableType(syncer::UserSelectableType::kExtensions));
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_FALSE(ukm_test_helper.IsExtensionRecordingEnabled());

  ASSERT_TRUE(
      harness2->EnableSelectableType(syncer::UserSelectableType::kExtensions));
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(ukm_test_helper.IsExtensionRecordingEnabled());
  EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());

  CloseBrowserSynchronously(browser2);
  CloseBrowserSynchronously(browser1);
}
#endif  // !BUILDFLAG(IS_ANDROID)

#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, LogsTabId) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  BrowserWindowInterface* sync_browser = CreateBrowser(profile);

  const ukm::UkmSource* first_source =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title1.html"),
                           sync_browser, &ukm_test_helper);

  // Tab ids are incremented starting from 1. Since we started a new sync
  // browser, this is at least the second tab (or third if InitialWebUI is
  // enabled).
  int64_t initial_tab_id = first_source->navigation_data().tab_id;
  EXPECT_GT(initial_tab_id, 0);

  // Ensure the tab id is constant in a single tab.
  const ukm::UkmSource* second_source =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title2.html"),
                           sync_browser, &ukm_test_helper);
  EXPECT_EQ(initial_tab_id, second_source->navigation_data().tab_id);

  // Add a new tab, it should get a new tab id incremented by 1.
  chrome::NewTab(sync_browser, NewTabTypes::kNoUserAction);
  const ukm::UkmSource* third_source =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title3.html"),
                           sync_browser, &ukm_test_helper);
  EXPECT_EQ(initial_tab_id + 1, third_source->navigation_data().tab_id);
}
#endif  // !BUILDFLAG(IS_ANDROID)

#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, LogsPreviousSourceId) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  BrowserWindowInterface* sync_browser = CreateBrowser(profile);

  const ukm::UkmSource* first_source =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title1.html"),
                           sync_browser, &ukm_test_helper);

  const ukm::UkmSource* second_source =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title2.html"),
                           sync_browser, &ukm_test_helper);
  EXPECT_EQ(first_source->id(),
            second_source->navigation_data().previous_source_id);

  // Open a new tab with window.open.
  content::WebContents* opener =
      sync_browser->GetTabStripModel()->GetActiveWebContents();
  GURL new_tab_url = embedded_test_server()->GetURL("/title3.html");
  content::TestNavigationObserver waiter(new_tab_url);
  waiter.StartWatchingNewWebContents();
  EXPECT_TRUE(content::ExecJs(
      opener, content::JsReplace("window.open($1)", new_tab_url)));
  waiter.Wait();
  EXPECT_NE(opener, sync_browser->GetTabStripModel()->GetActiveWebContents());
  ukm::SourceId new_id = sync_browser->GetTabStripModel()
                             ->GetActiveWebContents()
                             ->GetPrimaryMainFrame()
                             ->GetPageUkmSourceId();
  ukm::UkmSource* new_tab_source = ukm_test_helper.GetSource(new_id);
  EXPECT_NE(nullptr, new_tab_source);
  EXPECT_EQ(ukm::kInvalidSourceId,
            new_tab_source->navigation_data().previous_source_id);

  // Subsequent navigations within the tab should get a previous_source_id field
  // set.
  const ukm::UkmSource* subsequent_source =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title3.html"),
                           sync_browser, &ukm_test_helper);
  EXPECT_EQ(new_tab_source->id(),
            subsequent_source->navigation_data().previous_source_id);
}
#endif  // !BUILDFLAG(IS_ANDROID)

#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, LogsOpenerSource) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  BrowserWindowInterface* sync_browser = CreateBrowser(profile);

  const ukm::UkmSource* first_source =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title1.html"),
                           sync_browser, &ukm_test_helper);
  // This tab was not opened by another tab, so it should not have an opener
  // id.
  EXPECT_EQ(ukm::kInvalidSourceId,
            first_source->navigation_data().opener_source_id);

  // Open a new tab with window.open.
  content::WebContents* opener =
      sync_browser->GetTabStripModel()->GetActiveWebContents();
  GURL new_tab_url = embedded_test_server()->GetURL("/title2.html");
  content::TestNavigationObserver waiter(new_tab_url);
  waiter.StartWatchingNewWebContents();
  EXPECT_TRUE(content::ExecJs(
      opener, content::JsReplace("window.open($1)", new_tab_url)));
  waiter.Wait();
  EXPECT_NE(opener, sync_browser->GetTabStripModel()->GetActiveWebContents());
  ukm::SourceId new_id = sync_browser->GetTabStripModel()
                             ->GetActiveWebContents()
                             ->GetPrimaryMainFrame()
                             ->GetPageUkmSourceId();
  ukm::UkmSource* new_tab_source = ukm_test_helper.GetSource(new_id);
  EXPECT_NE(nullptr, new_tab_source);
  EXPECT_EQ(first_source->id(),
            new_tab_source->navigation_data().opener_source_id);

  // Subsequent navigations within the tab should not get an opener set.
  const ukm::UkmSource* subsequent_source =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title3.html"),
                           sync_browser, &ukm_test_helper);
  EXPECT_EQ(ukm::kInvalidSourceId,
            subsequent_source->navigation_data().opener_source_id);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// ChromeOS doesn't have the concept of sign-out so this test doesn't make sense
// there.
//
// Flaky on Android: https://crbug.com/40700532.
//
// Make sure that UKM is disabled when the profile signs out of Sync.
// LINT.IfChange(SingleSyncSignoutCheck)
#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, SingleSyncSignoutCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  PlatformBrowser browser = CreatePlatformBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  harness->SignOutPrimaryAccount();
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_NE(original_client_id, ukm_test_helper.GetClientId());

  ClosePlatformBrowser(browser);
}
#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
// LINT.ThenChange(/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java:SingleSyncSignoutCheck)
// ThenChange(/ios/chrome/browser/metrics/model/ukm_egtest.mm:SingleSyncSignoutCheck)

// ChromeOS doesn't have the concept of sign-out so this test doesn't make sense
// there. Android doesn't have multiple profiles.
#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
// Make sure that UKM is disabled when any profile signs out of Sync.
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MultiSyncSignoutCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile1 = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness1 =
      EnableSyncForProfile(profile1);

  BrowserWindowInterface* browser1 = CreateBrowser(profile1);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  Profile* profile2 = CreateNonSyncProfile();
  std::unique_ptr<SyncServiceImplHarness> harness2 =
      EnableSyncForProfile(profile2);
  BrowserWindowInterface* browser2 = CreateBrowser(profile2);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());

  harness2->SignOutPrimaryAccount();
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_NE(original_client_id, ukm_test_helper.GetClientId());

  CloseBrowserSynchronously(browser2);
  CloseBrowserSynchronously(browser1);
}
#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)

// Make sure that if history/sync services weren't available when we tried to
// attach listeners, UKM is not enabled.
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, ServiceListenerInitFailedCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);
  ChromeMetricsServiceClient::SetNotificationListenerSetupFailedForTesting(
      true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* sync_browser = CreateBrowser(profile);
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
  CloseBrowserSynchronously(sync_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// Make sure that UKM is not affected by MetricsReporting Feature (sampling).
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MetricsReportingCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  // Need to set the Metrics Default to OPT_OUT to trigger MetricsReporting.
  PrefService* local_state = g_browser_process->local_state();
  ForceRecordMetricsReportingDefaultState(local_state,
                                          EnableMetricsDefault::OPT_OUT);
  // Verify that kMetricsReportingFeature is disabled (i.e. other metrics
  // services will be sampled out).
  EXPECT_FALSE(
      base::FeatureList::IsEnabled(internal::kMetricsReportingFeature));

  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* sync_browser = CreateBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());

  CloseBrowserSynchronously(sync_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// Make sure that pending data is deleted when user deletes history.
// LINT.IfChange(HistoryDeleteCheck)
// Flaky on Android: https://crbug.com/40721445.
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_HistoryDeleteCheck DISABLED_HistoryDeleteCheck
#else
#define MAYBE_HistoryDeleteCheck HistoryDeleteCheck
#endif
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, MAYBE_HistoryDeleteCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  PlatformBrowser browser = CreatePlatformBrowser(profile);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  uint64_t original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0U, original_client_id);

  const ukm::SourceId kDummySourceId = 0x54321;
  ukm_test_helper.RecordSourceForTesting(kDummySourceId);
  EXPECT_TRUE(ukm_test_helper.HasSource(kDummySourceId));

  ClearBrowsingData(profile);
  // Other sources may already have been recorded since the data was cleared,
  // but the dummy source should be gone.
  EXPECT_FALSE(ukm_test_helper.HasSource(kDummySourceId));
  // Client ID should NOT be reset.
  EXPECT_EQ(original_client_id, ukm_test_helper.GetClientId());
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());

  ClosePlatformBrowser(browser);
}
// LINT.ThenChange(/chrome/android/javatests/src/org/chromium/chrome/browser/metrics/UkmTest.java:HistoryDeleteCheck)
// ThenChange(/ios/chrome/browser/metrics/model/ukm_egtest.mm:HistoryDeleteCheck)

// On ChromeOS, the test profile starts with a primary account already set, so
// this test doesn't apply.
#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, NotEnabledForNonSyncingAccountSync) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);

  // Signing in (without granting sync consent or explicitly setting up Sync)
  // should trigger starting the Sync machinery in standalone transport mode.
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      SyncServiceImplHarness::Create(
          profile, SyncServiceImplHarness::SigninType::FAKE_SIGNIN);
  syncer::SyncService* sync_service =
      SyncServiceFactory::GetForProfile(profile);

  ASSERT_TRUE(harness->SignInNoWaitForCompletion());
  ASSERT_NE(syncer::SyncService::TransportState::DISABLED,
            sync_service->GetTransportState());
  ASSERT_TRUE(harness->AwaitSyncTransportActive());
  ASSERT_EQ(syncer::SyncService::TransportState::ACTIVE,
            sync_service->GetTransportState());

  // History Sync is not active.
  ASSERT_FALSE(sync_service->GetActiveDataTypes().Has(syncer::HISTORY));
  ASSERT_FALSE(sync_service->GetActiveDataTypes().Has(
      syncer::HISTORY_DELETE_DIRECTIVES));

  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
}
#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)

#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_P(UkmConsentParamBrowserTest, GroupPolicyConsentCheck) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  // Note we are not using the synthetic MetricsConsentOverride since we are
  // testing directly from prefs.

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  BrowserWindowInterface* sync_browser = CreateBrowser(profile);

  // The input param controls whether we set the prefs related to group policy
  // enabled or not. Based on its value, we should report the same value for
  // both if reporting is enabled and if UKM service is enabled.
  bool is_enabled = is_metrics_reporting_enabled_initial_value();
  EXPECT_EQ(is_enabled,
            UkmConsentParamBrowserTest::IsMetricsAndCrashReportingEnabled());
  EXPECT_EQ(is_enabled, ukm_test_helper.IsRecordingEnabled());

  CloseBrowserSynchronously(sync_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID)

#if !BUILDFLAG(IS_ANDROID)
// Verify UKM is enabled/disabled for both potential settings of group policy.
INSTANTIATE_TEST_SUITE_P(UkmConsentParamBrowserTests,
                         UkmConsentParamBrowserTest,
                         testing::Bool());
#endif  // !BUILDFLAG(IS_ANDROID)

// Verify that sources kept alive in-memory will be discarded by UKM service in
// one reporting cycle after the web contents are destroyed when the tab is
// closed or when the user navigated away in the same tab.
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, EvictObsoleteSources) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  BrowserWindowInterface* sync_browser = CreateBrowser(profile);

  ukm::SourceId source_id1 = ukm::kInvalidSourceId;
  ukm::SourceId source_id2 = ukm::kInvalidSourceId;

  const std::vector<GURL> test_urls = {
      embedded_test_server()->GetURL("/title1.html"),
      embedded_test_server()->GetURL("/title2.html"),
      embedded_test_server()->GetURL("/title3.html")};

  // Open a blank new tab.
  ASSERT_TRUE(AddTabAtIndexToBrowser(sync_browser, 1, GURL(url::kAboutBlankURL),
                                     ui::PAGE_TRANSITION_TYPED, true));
  // Gather source id from the NavigationHandle assigned to navigations that
  // start with the expected URL.
  content::NavigationHandleObserver tab_1_observer(
      sync_browser->GetTabStripModel()->GetActiveWebContents(), test_urls[0]);
  // Navigate to a test URL in this new tab.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(sync_browser, test_urls[0]));
  // Get the source id associated to the last committed navigation, which could
  // differ from the id from WebContents for example if the site executes a
  // same-document navigation (e.g. history.pushState/replaceState). This
  // navigation source id is the one marked as obsolete by UKM recorder.
  source_id1 = ukm::ConvertToSourceId(tab_1_observer.navigation_id(),
                                      ukm::SourceIdType::NAVIGATION_ID);

  // The UKM report contains this newly-created source.
  ukm_test_helper.BuildAndStoreLog();
  std::unique_ptr<ukm::Report> report = ukm_test_helper.GetUkmReport();
  bool has_source_id1 = false;
  bool has_source_id2 = false;
  for (const auto& s : report->sources()) {
    has_source_id1 |= s.id() == source_id1;
    has_source_id2 |= s.id() == source_id2;
  }
  EXPECT_TRUE(has_source_id1);
  EXPECT_FALSE(has_source_id2);

  // Navigate to another URL in a new tab.
  ASSERT_TRUE(AddTabAtIndexToBrowser(sync_browser, 2, GURL(url::kAboutBlankURL),
                                     ui::PAGE_TRANSITION_TYPED, true));
  content::NavigationHandleObserver tab_2_observer(
      sync_browser->GetTabStripModel()->GetActiveWebContents(), test_urls[1]);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(sync_browser, test_urls[1]));
  source_id2 = ukm::ConvertToSourceId(tab_2_observer.navigation_id(),
                                      ukm::SourceIdType::NAVIGATION_ID);

  // The next report should again contain source 1 because the tab is still
  // alive, and also source 2 associated to the new tab that has just been
  // opened.
  ukm_test_helper.BuildAndStoreLog();
  report = ukm_test_helper.GetUkmReport();
  has_source_id1 = false;
  has_source_id2 = false;
  for (const auto& s : report->sources()) {
    has_source_id1 |= s.id() == source_id1;
    has_source_id2 |= s.id() == source_id2;
  }
  EXPECT_TRUE(has_source_id1);
  EXPECT_TRUE(has_source_id2);

  // Close the tab corresponding to source 1, this should mark source 1 as
  // obsolete. Next report will still contain source 1 because we might have
  // associated entries before it was closed.
  sync_browser->GetTabStripModel()->CloseWebContentsAt(
      1, TabCloseTypes::CLOSE_NONE);

  ukm_test_helper.BuildAndStoreLog();
  report = ukm_test_helper.GetUkmReport();
  has_source_id1 = false;
  has_source_id2 = false;
  for (const auto& s : report->sources()) {
    has_source_id1 |= s.id() == source_id1;
    has_source_id2 |= s.id() == source_id2;
  }
  EXPECT_TRUE(has_source_id1);
  EXPECT_TRUE(has_source_id2);

  // Navigate to a new URL in the current tab, this will mark source 2 that was
  // in the current tab as obsolete.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(sync_browser, test_urls[2]));

  // The previous report was the last one that could potentially contain entries
  // for source 1. Source 1 is thus no longer included in future reports. This
  // report will still contain source 2 because we might have associated entries
  // since the last report.
  ukm_test_helper.BuildAndStoreLog();
  report = ukm_test_helper.GetUkmReport();
  has_source_id1 = false;
  has_source_id2 = false;
  for (const auto& s : report->sources()) {
    has_source_id1 |= s.id() == source_id1;
    has_source_id2 |= s.id() == source_id2;
  }
  EXPECT_FALSE(has_source_id1);
  EXPECT_TRUE(has_source_id2);

  // Neither source 1 or source 2 is alive anymore.
  ukm_test_helper.BuildAndStoreLog();
  report = ukm_test_helper.GetUkmReport();
  has_source_id1 = false;
  has_source_id2 = false;
  for (const auto& s : report->sources()) {
    has_source_id1 |= s.id() == source_id1;
    has_source_id2 |= s.id() == source_id2;
  }
  EXPECT_FALSE(has_source_id1);
  EXPECT_FALSE(has_source_id2);

  CloseBrowserSynchronously(sync_browser);
}
#endif  // !BUILDFLAG(IS_ANDROID)

// Verify that correct sources are marked as obsolete when same-document
// navigation happens.
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest,
                       MarkObsoleteSourcesSameDocumentNavigation) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  BrowserWindowInterface* sync_browser = CreateBrowser(profile);

  // First navigation.
  const ukm::SourceId source_id1 =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title1.html"),
                           sync_browser, &ukm_test_helper)
          ->id();

  EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id1));

  // Cross-document navigation where the previous navigation is cross-document.
  const ukm::SourceId source_id2 =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title2.html"),
                           sync_browser, &ukm_test_helper)
          ->id();
  EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id1));
  EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id2));

  // Same-document navigation where the previous navigation is cross-document.
  const ukm::SourceId source_id3 =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title2.html#a"),
                           sync_browser, &ukm_test_helper)
          ->id();
  EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id1));
  EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id2));
  EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id3));

  // Same-document navigation where the previous navigation is same-document.
  const ukm::SourceId source_id4 =
      NavigateAndGetSource(embedded_test_server()->GetURL("/title2.html#b"),
                           sync_browser, &ukm_test_helper)
          ->id();
  EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id1));
  EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id2));
  EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id3));
  EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id4));

  // Cross-document navigation where the previous navigation is same-document.
  NavigateAndGetSource(embedded_test_server()->GetURL("/title1.html"),
                       sync_browser, &ukm_test_helper)
      ->id();
  EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id1));
  EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id2));
  EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id3));
  EXPECT_TRUE(ukm_test_helper.IsSourceObsolete(source_id4));
}
#endif  // !BUILDFLAG(IS_ANDROID)

// Verify that sources are not marked as obsolete by a new navigation that does
// not commit.
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, NotMarkSourcesIfNavigationNotCommitted) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  BrowserWindowInterface* sync_browser = CreateBrowser(profile);

  // An example navigation that commits.
  const GURL test_url_with_commit =
      embedded_test_server()->GetURL("/title1.html");

  // An example of a navigation returning "204 No Content" which does not commit
  // (i.e. the WebContents stays at the existing URL).
  const GURL test_url_no_commit =
      embedded_test_server()->GetURL("/page204.html");

  // Get the source id from the committed navigation.
  const ukm::SourceId source_id =
      NavigateAndGetSource(test_url_with_commit, sync_browser, &ukm_test_helper)
          ->id();

  // Initial default state.
  EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id));

  // New navigation did not commit, thus the source should still be kept alive.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(sync_browser, test_url_no_commit));
  EXPECT_FALSE(ukm_test_helper.IsSourceObsolete(source_id));
}
#endif  // !BUILDFLAG(IS_ANDROID)

#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, DebugUiRenders) {
  test::MetricsConsentOverride metrics_consent(true);

  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  PlatformBrowser browser = CreatePlatformBrowser(profile);

  ukm::UkmService* ukm_service(GetUkmService());
  EXPECT_TRUE(ukm_service->IsSamplingConfigured());

  // chrome://ukm
  g_browser_process->local_state()->SetBoolean(
      chrome_urls::kInternalOnlyUisEnabled, true);
  const GURL debug_url(content::GetWebUIURLString(content::kChromeUIUkmHost));

  content::TestNavigationObserver waiter(debug_url);
  waiter.WatchExistingWebContents();
  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, debug_url));
  waiter.WaitForNavigationFinished();
}
#endif  // !BUILDFLAG(IS_ANDROID)

#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(UkmBrowserTest, AllowedStateChanged) {
  ukm::UkmTestHelper ukm_test_helper(GetUkmService());
  test::MetricsConsentOverride metrics_consent(true);
  Profile* test_profile = ProfileManager::GetLastUsedProfileIfLoaded();
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(test_profile);

  CreatePlatformBrowser(test_profile);

  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(g_browser_process->GetMetricsServicesManager()
                  ->IsUkmAllowedForAllProfiles());

  TestUkmRecorderObserver observer(GetUkmService());
  unified_consent::UnifiedConsentService* consent_service =
      UnifiedConsentServiceFactory::GetForProfile(test_profile);
  consent_service->SetUrlKeyedAnonymizedDataCollectionEnabled(false);
#if !BUILDFLAG(IS_CHROMEOS)
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_FALSE(g_browser_process->GetMetricsServicesManager()
                   ->IsUkmAllowedForAllProfiles());
  // Expect nothing to be consented to.
  observer.ExpectAllowedStateChanged(ukm::UkmConsentState());
#else
  // ChromeOS has a different behavior compared to other platforms.
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(g_browser_process->GetMetricsServicesManager()
                  ->IsUkmAllowedForAllProfiles());
  observer.ExpectAllowedStateChanged(ukm::UkmConsentState({ukm::APPS}));
#endif

  consent_service->SetUrlKeyedAnonymizedDataCollectionEnabled(true);
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(g_browser_process->GetMetricsServicesManager()
                  ->IsUkmAllowedForAllProfiles());
  observer.ExpectAllowedStateChanged(ukm::UkmConsentState::All());
}
#endif  // !BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_CHROMEOS)
class UkmBrowserTestForAppConsent : public UkmBrowserTestBase {
 public:
  UkmBrowserTestForAppConsent() = default;
};

IN_PROC_BROWSER_TEST_F(UkmBrowserTestForAppConsent, MetricsClientEnablement) {
  ukm::UkmService* ukm_service = GetUkmService();
  ukm::UkmTestHelper ukm_test_helper(ukm_service);
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  unified_consent::UnifiedConsentService* consent_service =
      UnifiedConsentServiceFactory::GetForProfile(profile);
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);

  // All consents are on.
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::MSBB));
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::EXTENSIONS));
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::APPS));

  // Turn off MSBB consent.
  consent_service->SetUrlKeyedAnonymizedDataCollectionEnabled(false);

  // Still have AppKM consent.
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_FALSE(ukm_service->recording_enabled(ukm::MSBB));
  EXPECT_FALSE(ukm_service->recording_enabled(ukm::EXTENSIONS));
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::APPS));

  // Turn off App-sync.
  auto* user_settings = harness->service()->GetUserSettings();
  auto registered_os_sync_types =
      user_settings->GetRegisteredSelectableOsTypes();
  registered_os_sync_types.Remove(syncer::UserSelectableOsType::kOsApps);
  user_settings->SetSelectedOsTypes(false, registered_os_sync_types);

  // UKM recording is now disabled since MSBB and App-sync consent
  // has been removed.
  EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_FALSE(ukm_service->recording_enabled(ukm::MSBB));
  EXPECT_FALSE(ukm_service->recording_enabled(ukm::EXTENSIONS));
  EXPECT_FALSE(ukm_service->recording_enabled(ukm::APPS));
}

// LINT.IfChange(ClientIdResetWhenConsentRemoved)
IN_PROC_BROWSER_TEST_F(UkmBrowserTestForAppConsent,
                       ClientIdResetWhenConsentRemoved) {
  ukm::UkmService* ukm_service = GetUkmService();
  ukm::UkmTestHelper ukm_test_helper(ukm_service);
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  unified_consent::UnifiedConsentService* consent_service =
      UnifiedConsentServiceFactory::GetForProfile(profile);
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  const auto original_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(0ul, original_client_id);

  // All consents are on.
  EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::MSBB));
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::EXTENSIONS));
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::APPS));

  // Turn off MSBB consent.
  consent_service->SetUrlKeyedAnonymizedDataCollectionEnabled(false);

  EXPECT_FALSE(ukm_service->recording_enabled(ukm::MSBB));

  // Client ID should reset when MSBB is disabled.
  const auto app_sync_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(original_client_id, app_sync_client_id);

  // Turn off app sync.
  auto* user_settings = harness->service()->GetUserSettings();
  auto registered_os_sync_types =
      user_settings->GetRegisteredSelectableOsTypes();
  registered_os_sync_types.Remove(syncer::UserSelectableOsType::kOsApps);
  user_settings->SetSelectedOsTypes(false, registered_os_sync_types);

  EXPECT_FALSE(ukm_service->recording_enabled(ukm::APPS));

  // Client ID should reset when app sync is disable.
  const auto final_client_id = ukm_test_helper.GetClientId();
  EXPECT_NE(app_sync_client_id, final_client_id);
}
// LINT.ThenChange(/ios/chrome/browser/metrics/model/ukm_egtest.mm:ClientIdResetWhenConsentRemoved)

IN_PROC_BROWSER_TEST_F(UkmBrowserTestForAppConsent,
                       EnsurePurgeOnConsentChange) {
  ukm::UkmService* ukm_service = GetUkmService();
  ukm::UkmTestHelper ukm_test_helper(ukm_service);
  test::MetricsConsentOverride metrics_consent(true);
  Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
  unified_consent::UnifiedConsentService* consent_service =
      UnifiedConsentServiceFactory::GetForProfile(profile);
  std::unique_ptr<SyncServiceImplHarness> harness =
      EnableSyncForProfile(profile);
  BrowserWindowInterface* sync_browser = CreateBrowser(profile);

  const std::vector<GURL> test_urls = {
      embedded_test_server()->GetURL("/title1.html"),
      embedded_test_server()->GetURL("/title2.html"),
      embedded_test_server()->GetURL("/title3.html")};

  // Verify all consents are enabled.
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::MSBB));
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::EXTENSIONS));
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::APPS));

  int tab_index = 1;
  // Generate MSBB ukm entries by navigating to some test webpages.
  for (const auto& url : test_urls) {
    ASSERT_TRUE(AddTabAtIndexToBrowser(sync_browser, tab_index++,
                                       GURL(url::kAboutBlankURL),
                                       ui::PAGE_TRANSITION_TYPED, true));
    NavigateAndGetSource(url, sync_browser, &ukm_test_helper);
  }

  // Revoke MSBB consent.
  consent_service->SetUrlKeyedAnonymizedDataCollectionEnabled(false);

  // Verify that MSBB consent was revoked.
  EXPECT_FALSE(ukm_service->recording_enabled(ukm::MSBB));
  EXPECT_FALSE(ukm_service->recording_enabled(ukm::EXTENSIONS));
  EXPECT_TRUE(ukm_service->recording_enabled(ukm::APPS));

  ukm_test_helper.BuildAndStoreLog();
  const std::unique_ptr<ukm::Report> report = ukm_test_helper.GetUkmReport();

  // Verify that the only sources in the report are APP_ID.
  // NOTE(crbug.com/40248943): It was noticed that there was an APP_ID source
  // generated despite not being explicitly created. No entries are associated
  // with it though.
  for (int i = 0; i < report->sources_size(); ++i) {
    const auto id = report->sources(i).id();
    EXPECT_EQ(ukm::GetSourceIdType(id), ukm::SourceIdType::APP_ID);
  }
}
#endif  // BUILDFLAG(IS_CHROMEOS)

}  // namespace metrics
