chromium/components/metrics/metrics_service_unittest.cc

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

#include "components/metrics/metrics_service.h"

#include <stdint.h>

#include <algorithm>
#include <memory>
#include <string>
#include <string_view>

#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_snapshot_manager.h"
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/statistics_recorder.h"
#include "base/metrics/user_metrics.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
#include "components/metrics/clean_exit_beacon.h"
#include "components/metrics/client_info.h"
#include "components/metrics/cloned_install_detector.h"
#include "components/metrics/environment_recorder.h"
#include "components/metrics/log_decoder.h"
#include "components/metrics/metrics_features.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_scheduler.h"
#include "components/metrics/metrics_state_manager.h"
#include "components/metrics/metrics_upload_scheduler.h"
#include "components/metrics/stability_metrics_helper.h"
#include "components/metrics/test/test_enabled_state_provider.h"
#include "components/metrics/test/test_metrics_provider.h"
#include "components/metrics/test/test_metrics_service_client.h"
#include "components/metrics/unsent_log_store_metrics_impl.h"
#include "components/prefs/testing_pref_service.h"
#include "components/variations/active_field_trials.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
#include "third_party/metrics_proto/system_profile.pb.h"
#include "third_party/zlib/google/compression_utils.h"

namespace metrics {
namespace {

const char kTestPrefName[] =;

class TestUnsentLogStore : public UnsentLogStore {};

// Returns true if |id| is present in |proto|'s collection of FieldTrials.
bool IsFieldTrialPresent(const SystemProfileProto& proto,
                         const std::string& trial_name,
                         const std::string& group_name) {}

class TestMetricsService : public MetricsService {};

class TestMetricsLog : public MetricsLog {};

const char kOnDidCreateMetricsLogHistogramName[] =;

class TestMetricsProviderForOnDidCreateMetricsLog : public TestMetricsProvider {};

const char kProvideHistogramsHistogramName[] =;

class TestMetricsProviderForProvideHistograms : public TestMetricsProvider {};

class TestMetricsProviderForProvideHistogramsEarlyReturn
    : public TestMetricsProviderForProvideHistograms {};

class TestIndependentMetricsProvider : public MetricsProvider {};

class MetricsServiceTest : public testing::Test {};

class MetricsServiceTestWithFeatures
    : public MetricsServiceTest,
      public ::testing::WithParamInterface<std::tuple<bool>> {};

struct StartupVisibilityTestParams {};

class MetricsServiceTestWithStartupVisibility
    : public MetricsServiceTest,
      public ::testing::WithParamInterface<
          std::tuple<StartupVisibilityTestParams, bool>> {};

class ExperimentTestMetricsProvider : public TestMetricsProvider {};

bool HistogramExists(std::string_view name) {}

base::HistogramBase::Count GetHistogramDeltaTotalCount(std::string_view name) {}

}  // namespace

INSTANTIATE_TEST_SUITE_P();

TEST_P(MetricsServiceTestWithFeatures, RecordId) {}

TEST_P(MetricsServiceTestWithFeatures, InitialStabilityLogAfterCleanShutDown) {}

TEST_P(MetricsServiceTestWithFeatures, InitialStabilityLogAtProviderRequest) {}

TEST_P(MetricsServiceTestWithFeatures, IndependentLogAtProviderRequest) {}

TEST_P(MetricsServiceTestWithFeatures, OnDidCreateMetricsLogAtShutdown) {}

TEST_P(MetricsServiceTestWithFeatures, ProvideHistograms) {}

TEST_P(MetricsServiceTestWithFeatures, ProvideHistogramsEarlyReturn) {}

INSTANTIATE_TEST_SUITE_P();

TEST_P(MetricsServiceTestWithStartupVisibility, InitialStabilityLogAfterCrash) {}

TEST_P(MetricsServiceTestWithFeatures,
       InitialLogsHaveOnDidCreateMetricsLogHistograms) {}

TEST_P(MetricsServiceTestWithFeatures, MarkCurrentHistogramsAsReported) {}

TEST_P(MetricsServiceTestWithFeatures, LogHasUserActions) {}

TEST_P(MetricsServiceTestWithFeatures, FirstLogCreatedBeforeUnsentLogsSent) {}

TEST_P(MetricsServiceTestWithFeatures,
       MetricsProviderOnRecordingDisabledCalledOnInitialStop) {}

TEST_P(MetricsServiceTestWithFeatures, MetricsProvidersInitialized) {}

// Verify that FieldTrials activated by a MetricsProvider are reported by the
// FieldTrialsProvider.
TEST_P(MetricsServiceTestWithFeatures, ActiveFieldTrialsReported) {}

TEST_P(MetricsServiceTestWithFeatures,
       SystemProfileDataProvidedOnEnableRecording) {}

// Verify that the two separate MetricsSchedulers (MetricsRotationScheduler and
// MetricsUploadScheduler) function together properly.
TEST_P(MetricsServiceTestWithFeatures, SplitRotation) {}

TEST_P(MetricsServiceTestWithFeatures, LastLiveTimestamp) {}

TEST_P(MetricsServiceTestWithFeatures, EnablementObserverNotification) {}

// Verifies that when a cloned install is detected, logs are purged.
TEST_P(MetricsServiceTestWithFeatures, PurgeLogsOnClonedInstallDetected) {}

#if BUILDFLAG(IS_CHROMEOS_LACROS)
// ResetClientId is only enabled on certain targets.
TEST_P(MetricsServiceTestWithFeatures, SetClientIdToExternalId) {
  EnableMetricsReporting();
  TestMetricsServiceClient client;
  TestMetricsService service(GetMetricsStateManager(), &client,
                             GetLocalState());

  const std::string client_id = "d92ad666-a420-4c73-8718-94311ae2ff5f";

  EXPECT_NE(service.GetClientId(), client_id);

  service.SetExternalClientId(client_id);
  // Reset will cause the client id to be regenerated. If an external client id
  // is provided, it should defer to using that id instead of creating its own.
  service.ResetClientId();

  EXPECT_EQ(service.GetClientId(), client_id);
}
#endif  //  BUILDFLAG(IS_CHROMEOS_LACROS)

#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_P(MetricsServiceTestWithFeatures,
       OngoingLogNotFlushedBeforeInitialLogWhenUserLogStoreSet) {
  EnableMetricsReporting();
  TestMetricsServiceClient client;
  TestMetricsService service(GetMetricsStateManager(), &client,
                             GetLocalState());

  service.InitializeMetricsRecordingState();
  // Start() will create the first ongoing log.
  service.Start();
  ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state());

  MetricsLogStore* test_log_store = service.LogStoreForTest();
  std::unique_ptr<TestUnsentLogStore> alternate_ongoing_log_store =
      InitializeTestLogStoreAndGet();
  TestUnsentLogStore* alternate_ongoing_log_store_ptr =
      alternate_ongoing_log_store.get();

  ASSERT_EQ(0u, test_log_store->initial_log_count());
  ASSERT_EQ(0u, test_log_store->ongoing_log_count());

  service.SetUserLogStore(std::move(alternate_ongoing_log_store));

  // Initial logs should not have been collected so the ongoing log being
  // recorded should not be flushed when a user log store is mounted.
  ASSERT_EQ(0u, test_log_store->initial_log_count());
  ASSERT_EQ(0u, test_log_store->ongoing_log_count());

  // Fast forward the time by |initialization_delay|, which is when the pending
  // init tasks will run.
  base::TimeDelta initialization_delay = service.GetInitializationDelay();
  task_environment_.FastForwardBy(initialization_delay);
  EXPECT_EQ(TestMetricsService::INIT_TASK_DONE, service.state());

  // Fast forward the time until the MetricsRotationScheduler first runs, which
  // should complete the first ongoing log.
  // Note: The first log is only created after N = GetInitialIntervalSeconds()
  // seconds since the start, and since we already fast forwarded by
  // |initialization_delay| once, we only need to fast forward by
  // N - |initialization_delay|.
  task_environment_.FastForwardBy(
      base::Seconds(MetricsScheduler::GetInitialIntervalSeconds()) -
      initialization_delay);
  ASSERT_EQ(TestMetricsService::SENDING_LOGS, service.state());
  // When the init task is complete, the first ongoing log should be created
  // in the alternate ongoing log store.
  EXPECT_EQ(0u, test_log_store->initial_log_count());
  EXPECT_EQ(0u, test_log_store->ongoing_log_count());
  EXPECT_EQ(1u, alternate_ongoing_log_store_ptr->size());
}

TEST_P(MetricsServiceTestWithFeatures,
       OngoingLogFlushedAfterInitialLogWhenUserLogStoreSet) {
  EnableMetricsReporting();
  TestMetricsServiceClient client;
  TestMetricsService service(GetMetricsStateManager(), &client,
                             GetLocalState());

  service.InitializeMetricsRecordingState();
  // Start() will create the first ongoing log.
  service.Start();
  ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state());

  MetricsLogStore* test_log_store = service.LogStoreForTest();
  std::unique_ptr<TestUnsentLogStore> alternate_ongoing_log_store =
      InitializeTestLogStoreAndGet();

  // Init state.
  ASSERT_EQ(0u, test_log_store->initial_log_count());
  ASSERT_EQ(0u, test_log_store->ongoing_log_count());

  // Fast forward the time by |initialization_delay|, which is when the pending
  // init tasks will run.
  base::TimeDelta initialization_delay = service.GetInitializationDelay();
  task_environment_.FastForwardBy(initialization_delay);
  EXPECT_EQ(TestMetricsService::INIT_TASK_DONE, service.state());

  // Fast forward the time until the MetricsRotationScheduler first runs, which
  // should complete the first ongoing log.
  // Note: The first log is only created after N = GetInitialIntervalSeconds()
  // seconds since the start, and since we already fast forwarded by
  // |initialization_delay| once, we only need to fast forward by
  // N - |initialization_delay|.
  task_environment_.FastForwardBy(
      base::Seconds(MetricsScheduler::GetInitialIntervalSeconds()) -
      initialization_delay);
  ASSERT_EQ(TestMetricsService::SENDING_LOGS, service.state());
  ASSERT_EQ(0u, test_log_store->initial_log_count());
  ASSERT_EQ(1u, test_log_store->ongoing_log_count());

  // User log store set post-init.
  service.SetUserLogStore(std::move(alternate_ongoing_log_store));

  // Another log should have been flushed from setting the user log store.
  ASSERT_EQ(0u, test_log_store->initial_log_count());
  ASSERT_EQ(2u, test_log_store->ongoing_log_count());
}

TEST_P(MetricsServiceTestWithFeatures,
       OngoingLogDiscardedAfterEarlyUnsetUserLogStore) {
  EnableMetricsReporting();
  TestMetricsServiceClient client;
  TestMetricsService service(GetMetricsStateManager(), &client,
                             GetLocalState());

  service.InitializeMetricsRecordingState();
  // Start() will create the first ongoing log.
  service.Start();
  ASSERT_EQ(TestMetricsService::INIT_TASK_SCHEDULED, service.state());

  MetricsLogStore* test_log_store = service.LogStoreForTest();
  std::unique_ptr<TestUnsentLogStore> alternate_ongoing_log_store =
      InitializeTestLogStoreAndGet();

  ASSERT_EQ(0u, test_log_store->initial_log_count());
  ASSERT_EQ(0u, test_log_store->ongoing_log_count());

  service.SetUserLogStore(std::move(alternate_ongoing_log_store));

  // Unset the user log store before we started sending logs.
  base::UmaHistogramBoolean("Test.Before.Histogram", true);
  service.UnsetUserLogStore();
  base::UmaHistogramBoolean("Test.After.Histogram", true);

  // Verify that the current log was discarded.
  EXPECT_FALSE(service.GetCurrentLogForTest());

  // Verify that histograms from before unsetting the user log store were
  // flushed.
  EXPECT_EQ(0, GetHistogramDeltaTotalCount("Test.Before.Histogram"));
  EXPECT_EQ(1, GetHistogramDeltaTotalCount("Test.After.Histogram"));

  // Clean up histograms.
  base::StatisticsRecorder::ForgetHistogramForTesting("Test.Before.Histogram");
  base::StatisticsRecorder::ForgetHistogramForTesting("Test.After.Histogram");
}

TEST_P(MetricsServiceTestWithFeatures,
       UnsettingLogStoreShouldDisableRecording) {
  EnableMetricsReporting();
  TestMetricsServiceClient client;
  TestMetricsService service(GetMetricsStateManager(), &client,
                             GetLocalState());

  service.InitializeMetricsRecordingState();
  // Start() will register the service to start recording.
  service.Start();
  ASSERT_TRUE(service.recording_active());

  // Register, set and unset a log store.
  // This will clear the log file and thus should also stop recording.
  std::unique_ptr<TestUnsentLogStore> alternate_ongoing_log_store =
      InitializeTestLogStoreAndGet();
  service.SetUserLogStore(std::move(alternate_ongoing_log_store));
  service.UnsetUserLogStore();
  ASSERT_FALSE(service.recording_active());

  // This should not crash.
  base::RecordAction(base::UserMetricsAction("TestAction"));
}

#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

}  // namespace metrics