// 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 "chrome/browser/metrics/chrome_metrics_service_client.h"

#include <string>

#include "base/files/file_path.h"
#include "base/metrics/persistent_histogram_allocator.h"
#include "base/process/process_handle.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/browser/metrics/chrome_metrics_services_manager_client.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/crash/core/common/shared_memory_user_stream_reader.h"
#include "components/metrics/client_info.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/metrics/dwa/dwa_recorder.h"
#include "components/metrics/file_metrics_provider.h"
#include "components/metrics/metrics_features.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_state_manager.h"
#include "components/metrics/system_profile_user_stream.h"
#include "components/metrics/test/test_enabled_state_provider.h"
#include "components/metrics/unsent_log_store.h"
#include "components/prefs/testing_pref_service.h"
#include "components/regional_capabilities/regional_capabilities_switches.h"
#include "components/ukm/ukm_service.h"
#include "components/ukm/ukm_test_helper.h"

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
#endif

#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/test/test_history_database.h"
#include "components/sync/test/test_sync_service.h"
#include "components/variations/synthetic_trial_registry.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/buildflags/buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"

#if !BUILDFLAG(IS_ANDROID)
#include "components/component_updater/mock_component_updater_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#endif

#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#endif

#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/dbus/power/power_manager_client.h"
#elif BUILDFLAG(IS_WIN)
#include "base/win/windows_version.h"
#include "chrome/browser/metrics/system_pdh_metrics_provider_win.h"
#endif
namespace {

std::unique_ptr<KeyedService> BuildTestHistoryService(
    content::BrowserContext* context) {
  auto service = std::make_unique<history::HistoryService>();
  service->Init(history::TestHistoryDatabaseParamsForPath(context->GetPath()));
  return service;
}

std::unique_ptr<KeyedService> BuildTestSyncService(
    content::BrowserContext* context) {
  return std::make_unique<syncer::TestSyncService>();
}

}  // namespace

class TestChromeMetricsServiceClient : public ChromeMetricsServiceClient {
 public:
  // Equivalent to ChromeMetricsServiceClient::Create
  static std::unique_ptr<TestChromeMetricsServiceClient> Create(
      metrics::MetricsStateManager* metrics_state_manager,
      variations::SyntheticTrialRegistry* synthetic_trial_registry) {
    // Needed because RegisterMetricsServiceProviders() checks for this.
    metrics::SubprocessMetricsProvider::CreateInstance();

    std::unique_ptr<TestChromeMetricsServiceClient> client(
        new TestChromeMetricsServiceClient(metrics_state_manager,
                                           synthetic_trial_registry));
    client->Initialize();

    return client;
  }

 private:
  explicit TestChromeMetricsServiceClient(
      metrics::MetricsStateManager* state_manager,
      variations::SyntheticTrialRegistry* synthetic_trial_registry)
      : ChromeMetricsServiceClient(state_manager, synthetic_trial_registry) {}

#if BUILDFLAG(IS_CHROMEOS)
  void AsyncInitSystemProfileProvider() override {}
#endif
};

class ChromeMetricsServiceClientTest : public testing::Test {
 public:
  ChromeMetricsServiceClientTest()
      : profile_manager_(TestingBrowserProcess::GetGlobal()),
        enabled_state_provider_(false /* consent */, false /* enabled */) {}

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

  void SetUp() override {
    testing::Test::SetUp();
    metrics::MetricsService::RegisterPrefs(prefs_.registry());
    synthetic_trial_registry_ =
        std::make_unique<variations::SyntheticTrialRegistry>();
    metrics_state_manager_ = metrics::MetricsStateManager::Create(
        &prefs_, &enabled_state_provider_, std::wstring(), base::FilePath());
    metrics_state_manager_->InstantiateFieldTrialList();
    ASSERT_TRUE(profile_manager_.SetUp());
#if BUILDFLAG(IS_CHROMEOS)
    scoped_feature_list_.InitWithFeatures(
        {features::kUmaStorageDimensions,
         features::kClassManagementEnabledMetricsProvider,
         metrics::dwa::kDwaFeature, switches::kDynamicProfileCountry},
        {});

    // ChromeOs Metrics Provider require g_login_state and power manager client
    // initialized before they can be instantiated.
    chromeos::PowerManagerClient::InitializeFake();
    ash::LoginState::Initialize();
#elif BUILDFLAG(IS_WIN)
    scoped_feature_list_.InitWithFeatures(
        {metrics::dwa::kDwaFeature, switches::kDynamicProfileCountry,
         features::kSystemPdhMetrics},
        {});
#elif BUILDFLAG(IS_ANDROID)
    scoped_feature_list_.InitWithFeatures({metrics::dwa::kDwaFeature}, {});
#else
    scoped_feature_list_.InitWithFeatures(
        {metrics::dwa::kDwaFeature, switches::kDynamicProfileCountry}, {});
#endif  // BUILDFLAG(IS_CHROMEOS)

#if !BUILDFLAG(IS_ANDROID)
    auto mock_component_updater = std::make_unique<
        testing::NiceMock<component_updater::MockComponentUpdateService>>();
    TestingBrowserProcess::GetGlobal()->SetComponentUpdater(
        std::move(mock_component_updater));
#endif
  }

  void TearDown() override {
#if !BUILDFLAG(IS_ANDROID)
    TestingBrowserProcess::GetGlobal()->SetComponentUpdater(nullptr);
#endif
#if BUILDFLAG(IS_CHROMEOS)
    ash::LoginState::Shutdown();
    chromeos::PowerManagerClient::Shutdown();
#endif  // BUILDFLAG(IS_CHROMEOS)
  }

  void TriggerOnAdvancedReportingEnabledForAllProfilesChanged(
      ChromeMetricsServiceClient* client,
      bool enabled,
      bool reset_client_state) {
    client->OnAdvancedReportingEnabledForAllProfilesChanged(enabled,
                                                            reset_client_state);
  }

  bool RegisterForProfileEvents(ChromeMetricsServiceClient* client,
                                Profile* profile) {
    return client->RegisterForProfileEvents(profile);
  }

  void OnProfileWillBeDestroyed(ChromeMetricsServiceClient* client,
                                Profile* profile) {
    client->OnProfileWillBeDestroyed(profile);
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  TestingPrefServiceSimple prefs_;
  TestingProfileManager profile_manager_;
  base::UserActionTester user_action_runner_;
  std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager_;
  std::unique_ptr<variations::SyntheticTrialRegistry> synthetic_trial_registry_;
  metrics::TestEnabledStateProvider enabled_state_provider_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

namespace {

bool TestIsProcessRunning(base::ProcessId pid) {
  // Odd are running, even are not.
  return (pid & 1) == 1;
}

TEST_F(ChromeMetricsServiceClientTest, FilterFiles) {
  TestChromeMetricsServiceClient::SetIsProcessRunningForTesting(
      &TestIsProcessRunning);

  base::ProcessId my_pid = base::GetCurrentProcId();
  base::FilePath active_dir(FILE_PATH_LITERAL("foo"));
  base::FilePath upload_dir(FILE_PATH_LITERAL("bar"));
  base::FilePath upload_path =
      base::GlobalHistogramAllocator::ConstructFilePathForUploadDir(
          upload_dir, "TestMetrics");
  EXPECT_EQ(
      metrics::FileMetricsProvider::FILTER_ACTIVE_THIS_PID,
      TestChromeMetricsServiceClient::FilterBrowserMetricsFiles(upload_path));

  EXPECT_EQ(
      metrics::FileMetricsProvider::FILTER_PROCESS_FILE,
      TestChromeMetricsServiceClient::FilterBrowserMetricsFiles(
          base::GlobalHistogramAllocator::ConstructFilePathForUploadDir(
              upload_dir, "Test", base::Time::Now(), (my_pid & ~1) + 10)));
  EXPECT_EQ(
      metrics::FileMetricsProvider::FILTER_TRY_LATER,
      TestChromeMetricsServiceClient::FilterBrowserMetricsFiles(
          base::GlobalHistogramAllocator::ConstructFilePathForUploadDir(
              upload_dir, "Test", base::Time::Now(), (my_pid & ~1) + 11)));
}

}  // namespace

TEST_F(ChromeMetricsServiceClientTest, TestRegisterUKMProviders) {
  // Test that UKM service has initialized all its metrics providers listed in
  // ChromeMetricsServiceClient::RegisterUKMProviders, for all platform with one
  // exception on ChromeOS.
  size_t expected_providers = 10;
#if BUILDFLAG(IS_CHROMEOS)
  // ChromeOSMetricsProvider
  expected_providers++;
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_ANDROID)
  // ChromeAndroidMetricsProvider
  expected_providers++;
#endif  // BUILDFLAG(IS_ANDROID)

  std::unique_ptr<ChromeMetricsServiceClient> chrome_metrics_service_client =
      TestChromeMetricsServiceClient::Create(metrics_state_manager_.get(),
                                             synthetic_trial_registry_.get());
  size_t observed_count = chrome_metrics_service_client->GetUkmService()
                              ->metrics_providers_.GetProviders()
                              .size();
  if (base::FeatureList::IsEnabled(ukm::kUkmFeature)) {
    EXPECT_EQ(expected_providers, observed_count);
  } else {
    EXPECT_EQ(0ul, observed_count);
  }
}

TEST_F(ChromeMetricsServiceClientTest, TestDwaServiceInitialized) {
  std::unique_ptr<ChromeMetricsServiceClient> chrome_metrics_service_client =
      TestChromeMetricsServiceClient::Create(metrics_state_manager_.get(),
                                             synthetic_trial_registry_.get());
  EXPECT_NE(chrome_metrics_service_client->GetDwaService(), nullptr);
}

TEST_F(ChromeMetricsServiceClientTest, TestRegisterMetricsServiceProviders) {
  // This is for the two metrics providers added in the MetricsService
  // constructor: StabilityMetricsProvider and MetricsStateMetricsProvider.
  size_t expected_providers = 2;

  // This is the number of metrics providers that are outside any #if macros.
  expected_providers += 26;

  int sample_rate;
  if (ChromeMetricsServicesManagerClient::GetSamplingRatePerMille(
          &sample_rate)) {
    // SamplingMetricsProvider.
    expected_providers++;
  }

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
  // MotherboardMetricProvider.
  expected_providers++;
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)

#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
  // ExtensionsMetricsProvider.
  expected_providers++;
#endif  // defined(ENABLE_EXTENSIONS_CORE)

#if BUILDFLAG(IS_ANDROID)
  // AndroidMetricsProvider, ChromeAndroidMetricsProvider,
  // PageLoadMetricsProvider, GmsMetricsProvider.
  expected_providers += 4;
#else
  // performance_manager::MetricsProvider
  expected_providers += 1;
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_WIN)
  // GoogleUpdateMetricsProviderWin, AntiVirusMetricsProvider,
  // TPMMetricsProvider, SystemMemoryListMetricsProvider.
  expected_providers += 4;

  // SystemPdhMetricsProvider is only supported on Win11.
  if (base::win::GetVersion() >= base::win::Version::WIN11 &&
      base::FeatureList::IsEnabled(features::kSystemPdhMetrics)) {
    ++expected_providers;
  }
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_CHROMEOS)
  // AmbientModeMetricsProvider,
  // CrosHealthdMetricsProvider, ChromeOSMetricsProvider,
  // ChromeOSHistogramMetricsProvider, ChromeShelfMetricsProvider,
  // ClassManagementEnabledMetricsProvider,
  // K12AgeClassificationMetricsProvider, KeyboardBacklightColorMetricsProvider,
  // PersonalizationAppThemeMetricsProvider, PrinterMetricsProvider,
  // FamilyUserMetricsProvider, FamilyLinkUserMetricsProvider,
  // UpdateEngineMetricsProvider, OsSettingsMetricsProvider,
  // UserTypeByDeviceTypeMetricsProvider, WallpaperMetricsProvider,
  // and VmmMetricsProvider.
  expected_providers += 17;
#endif  // BUILDFLAG(IS_CHROMEOS)

#if !BUILDFLAG(IS_CHROMEOS)
  // ChromeSigninStatusMetricsProvider (for non ChromeOS).
  // FamilyLinkUserMetricsProvider
  expected_providers += 2;
#endif  // !BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_MAC)
  // PowerMetricsProvider, GoogleUpdateMetricsProviderMac
  expected_providers += 2;
#endif  // BUILDFLAG(IS_MAC)

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  // DesktopPlatformFeaturesMetricsProvider
  // DesktopSessionMetricsProvider
  // UpdateMetricsProvider
  expected_providers += 3;
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
    BUILDFLAG(IS_CHROMEOS)
  // TabMetricsProvider
  // SkillsMetricsProvider
  expected_providers += 2;
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
        // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  // BluetoothMetricsProvider
  expected_providers += 1;
#endif

  // GlicMetricsProvider
  expected_providers += 1;

  std::unique_ptr<TestChromeMetricsServiceClient>
      chrome_metrics_service_client = TestChromeMetricsServiceClient::Create(
          metrics_state_manager_.get(), synthetic_trial_registry_.get());
  EXPECT_EQ(expected_providers,
            chrome_metrics_service_client->GetMetricsService()
                ->delegating_provider_.GetProviders()
                .size());
}

// This can't be a MAYBE test because it won't compile without the extensions
// header files but those can't even be included if this build flag is not
// set. This can't be in the anonymous namespace because it is a "friend" of
// the ChromeMetricsServiceClient class.
#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
TEST_F(ChromeMetricsServiceClientTest, IsWebstoreExtension) {
  static const char test_extension_id1[] = "abcdefghijklmnopqrstuvwxyzabcdef";
  static const char test_extension_id2[] = "bhcnanendmgjjeghamaccjnochlnhcgj";

  TestingProfile* test_profile = profile_manager_.CreateTestingProfile("p1");
  extensions::ExtensionRegistry* registry =
      extensions::ExtensionRegistry::Get(test_profile);
  ASSERT_TRUE(registry);

  scoped_refptr<const extensions::Extension> extension1 =
      extensions::ExtensionBuilder("e1").SetID(test_extension_id1).Build();
  registry->AddEnabled(extension1);

  scoped_refptr<const extensions::Extension> extension2 =
      extensions::ExtensionBuilder("e2")
          .SetID(test_extension_id2)
          .AddFlags(extensions::Extension::FROM_WEBSTORE)
          .Build();
  registry->AddEnabled(extension2);

  EXPECT_FALSE(TestChromeMetricsServiceClient::IsWebstoreExtension("foo"));
  EXPECT_FALSE(
      TestChromeMetricsServiceClient::IsWebstoreExtension(test_extension_id1));
  EXPECT_TRUE(
      TestChromeMetricsServiceClient::IsWebstoreExtension(test_extension_id2));
}
#endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE)

TEST_F(ChromeMetricsServiceClientTest, GetUploadSigningKey_NotEmpty) {
  std::unique_ptr<TestChromeMetricsServiceClient>
      chrome_metrics_service_client = TestChromeMetricsServiceClient::Create(
          metrics_state_manager_.get(), synthetic_trial_registry_.get());
  [[maybe_unused]] const std::string signing_key =
      chrome_metrics_service_client->GetUploadSigningKey();
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // The signing key should never be an empty string for a Chrome-branded build.
  EXPECT_FALSE(signing_key.empty());
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
}

TEST_F(ChromeMetricsServiceClientTest, GetUploadSigningKey_CanSignLogs) {
  std::unique_ptr<TestChromeMetricsServiceClient>
      chrome_metrics_service_client = TestChromeMetricsServiceClient::Create(
          metrics_state_manager_.get(), synthetic_trial_registry_.get());
  const std::string signing_key =
      chrome_metrics_service_client->GetUploadSigningKey();

  std::string signature =
      metrics::UnsentLogStore::ComputeHMACForLog("Test Log Data", signing_key);
  // This signature never fails, even if there is no signing key available:
  // empty keys are padded with zero bytes to the requisite length.
  EXPECT_FALSE(signature.empty());
}

TEST_F(ChromeMetricsServiceClientTest,
       OnAdvancedReportingEnabledForAllProfilesChanged) {
  base::test::ScopedFeatureList local_feature_list;
  local_feature_list.InitWithFeatures(
      {ukm::kUkmFeature, metrics::features::kRestructureMetricsConsentSettings},
      {});

  std::unique_ptr<ChromeMetricsServiceClient> client =
      TestChromeMetricsServiceClient::Create(metrics_state_manager_.get(),
                                             synthetic_trial_registry_.get());
  ukm::UkmService* ukm_service = client->GetUkmService();
  ASSERT_TRUE(ukm_service);

  uint64_t initial_client_id = ukm_service->client_id();

  // Trigger state change with reset_client_state = false.
  // UKM client ID should NOT change.
  TriggerOnAdvancedReportingEnabledForAllProfilesChanged(
      client.get(), /*enabled=*/false, /*reset_client_state=*/false);
  EXPECT_EQ(initial_client_id, ukm_service->client_id());

  // Trigger state change with reset_client_state = true.
  // UKM client ID SHOULD change.
  TriggerOnAdvancedReportingEnabledForAllProfilesChanged(
      client.get(), /*enabled=*/false, /*reset_client_state=*/true);
  EXPECT_NE(initial_client_id, ukm_service->client_id());
}

TEST_F(ChromeMetricsServiceClientTest,
       AdvancedReportingDataRetentionOnProfileUnload) {
  base::test::ScopedFeatureList local_feature_list;
  local_feature_list.InitWithFeatures(
      {ukm::kUkmFeature, metrics::features::kRestructureMetricsConsentSettings},
      {});

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  metrics::DesktopSessionDurationTracker::Initialize();
#endif

  std::unique_ptr<ChromeMetricsServiceClient> client =
      TestChromeMetricsServiceClient::Create(metrics_state_manager_.get(),
                                             synthetic_trial_registry_.get());
  ukm::UkmService* ukm_service = client->GetUkmService();
  ASSERT_TRUE(ukm_service);

  {
    ukm::UkmTestHelper ukm_test_helper(ukm_service);

    // Create and register a testing profile with mock history and sync services.
    TestingProfile::TestingFactories testing_factories;
    testing_factories.emplace_back(HistoryServiceFactory::GetInstance(),
                                   base::BindRepeating(&BuildTestHistoryService));
    testing_factories.emplace_back(SyncServiceFactory::GetInstance(),
                                   base::BindRepeating(&BuildTestSyncService));
    TestingProfile* profile =
        profile_manager_.CreateTestingProfile("p1", std::move(testing_factories));

    // Enable advanced reporting for this profile.
    // This will enable UKM recording and trigger initialization.
    metrics::MetricsReportingChoiceService::SetAdvancedReportingEnabled(
        profile->GetPrefs(), true);
    EXPECT_TRUE(client->IsUkmAllowedForAllProfiles());

    // Manually enable recording/reporting since MetricsServicesManager is not
    // running in this unit test.
    ukm_service->EnableRecording();
    ukm_service->EnableReporting();

    // Setup: build and store a dummy log to verify purging logic.
    ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
    ukm_test_helper.RecordSourceForTesting(source_id);
    ukm_test_helper.BuildAndStoreLog();
    ASSERT_TRUE(ukm_test_helper.HasUnsentLogs());

    uint64_t initial_client_id = ukm_service->client_id();

    // Simulate profile unloading (like during browser shutdown) by deleting the
    // profile. UKM client ID should NOT change, and unsent logs should NOT be
    // purged.
    profile_manager_.DeleteTestingProfile("p1");
    EXPECT_FALSE(client->IsUkmAllowedForAllProfiles());
    EXPECT_EQ(initial_client_id, ukm_service->client_id());
    EXPECT_TRUE(ukm_test_helper.HasUnsentLogs());
  }

#if BUILDFLAG(IS_CHROMEOS)
  ash::ScopedStubInstallAttributes stub_install_attributes;
#endif

  client.reset();

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  metrics::DesktopSessionDurationTracker::CleanupForTesting();
#endif
}

TEST_F(ChromeMetricsServiceClientTest,
       AdvancedReportingDataPurgeOnConsentRevocation) {
  base::test::ScopedFeatureList local_feature_list;
  local_feature_list.InitWithFeatures(
      {ukm::kUkmFeature, metrics::features::kRestructureMetricsConsentSettings},
      {});

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  metrics::DesktopSessionDurationTracker::Initialize();
#endif

  std::unique_ptr<ChromeMetricsServiceClient> client =
      TestChromeMetricsServiceClient::Create(metrics_state_manager_.get(),
                                             synthetic_trial_registry_.get());
  ukm::UkmService* ukm_service = client->GetUkmService();
  ASSERT_TRUE(ukm_service);

  {
    ukm::UkmTestHelper ukm_test_helper(ukm_service);

    // Create and register a testing profile with mock history and sync services.
    TestingProfile::TestingFactories testing_factories;
    testing_factories.emplace_back(HistoryServiceFactory::GetInstance(),
                                   base::BindRepeating(&BuildTestHistoryService));
    testing_factories.emplace_back(SyncServiceFactory::GetInstance(),
                                   base::BindRepeating(&BuildTestSyncService));
    TestingProfile* profile =
        profile_manager_.CreateTestingProfile("p1", std::move(testing_factories));

    // Enable advanced reporting for this profile.
    // This will enable UKM recording and trigger initialization.
    metrics::MetricsReportingChoiceService::SetAdvancedReportingEnabled(
        profile->GetPrefs(), true);
    EXPECT_TRUE(client->IsUkmAllowedForAllProfiles());

    // Manually enable recording/reporting since MetricsServicesManager is not
    // running in this unit test.
    ukm_service->EnableRecording();
    ukm_service->EnableReporting();

    // Setup: build and store a dummy log to verify purging logic.
    ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
    ukm_test_helper.RecordSourceForTesting(source_id);
    ukm_test_helper.BuildAndStoreLog();
    ASSERT_TRUE(ukm_test_helper.HasUnsentLogs());

    uint64_t initial_client_id = ukm_service->client_id();

    // Simulate user revoking consent (setting pref to false).
    // This will trigger state change with reset_client_state = true.
    // UKM client ID SHOULD change, and unsent logs SHOULD be purged.
    metrics::MetricsReportingChoiceService::SetAdvancedReportingEnabled(
        profile->GetPrefs(), false);
    EXPECT_FALSE(client->IsUkmAllowedForAllProfiles());
    EXPECT_NE(initial_client_id, ukm_service->client_id());
    EXPECT_FALSE(ukm_test_helper.HasUnsentLogs());

    // Clean up profile before shutting down the tracker.
    profile_manager_.DeleteTestingProfile("p1");
  }

#if BUILDFLAG(IS_CHROMEOS)
  ash::ScopedStubInstallAttributes stub_install_attributes;
#endif

  client.reset();

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  metrics::DesktopSessionDurationTracker::CleanupForTesting();
#endif
}

TEST_F(ChromeMetricsServiceClientTest,
       OnEnvironmentUpdate_SharedMemoryEnabled) {
  base::test::ScopedFeatureList local_feature_list;
  local_feature_list.InitAndEnableFeature(
      metrics::features::kSharedMemorySystemProfileMinidump);

  // Initialize the stream region first.
  metrics::SystemProfileUserStream::Get().Initialize();
  base::ReadOnlySharedMemoryRegion shared_region =
      metrics::SystemProfileUserStream::Get().DuplicateSharedMemoryRegion();
  ASSERT_TRUE(shared_region.IsValid());

  std::unique_ptr<TestChromeMetricsServiceClient> client =
      TestChromeMetricsServiceClient::Create(metrics_state_manager_.get(),
                                             synthetic_trial_registry_.get());

  std::string test_environment = "test_environment_payload";
  client->OnEnvironmentUpdate(&test_environment);

  base::ReadOnlySharedMemoryMapping mapping = shared_region.Map();
  ASSERT_TRUE(mapping.IsValid());

  std::optional<crash_reporter::UserStreamData> stream_data =
      crash_reporter::ExtractSharedMemoryUserStreamData(
          mapping.GetMemoryAsSpan<const uint8_t>());
  ASSERT_TRUE(stream_data.has_value());
  EXPECT_EQ(stream_data->user_stream_type, 0x4B6B0003u);
  EXPECT_EQ(
      std::string(stream_data->payload.begin(), stream_data->payload.end()),
      test_environment);
}
