chromium/chrome/browser/metrics/chrome_android_metrics_provider_unittest.cc

// Copyright 2018 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_android_metrics_provider.h"

#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chrome/browser/flags/android/chrome_session_state.h"
#include "components/metrics/android_metrics_helper.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"

namespace {

using chrome::android::ActivityType;
using chrome::android::GetActivityType;
using chrome::android::GetCustomTabsVisibleValue;
using chrome::android::GetInitialActivityTypeForTesting;
using chrome::android::SetActivityType;
using chrome::android::SetInitialActivityTypeForTesting;

class ChromeAndroidMetricsProviderTest
    : public testing::TestWithParam<ActivityType> {
 public:
  ChromeAndroidMetricsProviderTest()
      : metrics_provider_(&pref_service_),
        orig_activity_type_(GetInitialActivityTypeForTesting()) {
    ChromeAndroidMetricsProvider::RegisterPrefs(pref_service_.registry());
  }
  ~ChromeAndroidMetricsProviderTest() override {
    // In case the test played with the activity type, restore it to what it
    // was before the test.
    SetInitialActivityTypeForTesting(orig_activity_type_);
    ChromeAndroidMetricsProvider::ResetGlobalStateForTesting();
  }

  ActivityType activity_type() const { return GetParam(); }

 protected:
  base::test::ScopedFeatureList scoped_feature_list_;
  base::HistogramTester histogram_tester_;
  TestingPrefServiceSimple pref_service_;
  ChromeAndroidMetricsProvider metrics_provider_;
  metrics::ChromeUserMetricsExtension uma_proto_;
  const ActivityType orig_activity_type_;
  base::test::TaskEnvironment task_environment_;
};

}  // namespace

TEST_F(ChromeAndroidMetricsProviderTest,
       ProvideCurrentSessionData_MultiWindowMode) {
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);
  histogram_tester_.ExpectTotalCount("Android.MultiWindowMode.Active", 1);
}

TEST_F(ChromeAndroidMetricsProviderTest,
       ProvideCurrentSessionData_AppNotifications) {
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);
  histogram_tester_.ExpectTotalCount("Android.AppNotificationStatus", 1);
}

TEST_F(ChromeAndroidMetricsProviderTest,
       OnDidCreateMetricsLog_HasMultipleUserProfiles) {
  metrics_provider_.OnDidCreateMetricsLog();
  histogram_tester_.ExpectTotalCount("Android.MultipleUserProfilesState", 1);
  // Caches value, test a second time.
  metrics_provider_.OnDidCreateMetricsLog();
  histogram_tester_.ExpectTotalCount("Android.MultipleUserProfilesState", 2);
}

TEST_F(ChromeAndroidMetricsProviderTest,
       ProvidePreviousSessionData_HasMultipleUserProfiles) {
  metrics_provider_.ProvidePreviousSessionData(&uma_proto_);
  histogram_tester_.ExpectTotalCount("Android.MultipleUserProfilesState", 1);
  // Caches value, test a second time.
  metrics_provider_.ProvidePreviousSessionData(&uma_proto_);
  histogram_tester_.ExpectTotalCount("Android.MultipleUserProfilesState", 2);
}

TEST_F(ChromeAndroidMetricsProviderTest,
       OnDidCreateMetricsLog_AndroidMetricsHelper) {
  metrics_provider_.OnDidCreateMetricsLog();
  histogram_tester_.ExpectTotalCount("Android.VersionCode", 1);
  histogram_tester_.ExpectTotalCount("Android.CpuAbiBitnessSupport", 1);
}

TEST_F(ChromeAndroidMetricsProviderTest,
       ProvidePreviousSessionData_AndroidMetricsHelper) {
  metrics_provider_.ProvidePreviousSessionData(&uma_proto_);
  histogram_tester_.ExpectTotalCount("Android.VersionCode", 0);
  histogram_tester_.ExpectTotalCount("Android.CpuAbiBitnessSupport", 1);
}

TEST_F(ChromeAndroidMetricsProviderTest,
       ProvidePreviousSessionDataWithSavedLocalState_AndroidMetricsHelper) {
  metrics::AndroidMetricsHelper::SaveLocalState(&pref_service_, 588700002);
  metrics_provider_.ProvidePreviousSessionData(&uma_proto_);
  histogram_tester_.ExpectTotalCount("Android.VersionCode", 1);
  histogram_tester_.ExpectTotalCount("Android.CpuAbiBitnessSupport", 1);
}

TEST_F(ChromeAndroidMetricsProviderTest,
       ProvideCurrentSessionData_DarkModeState) {
  ASSERT_FALSE(uma_proto_.system_profile().os().has_dark_mode_state());

  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);
  ASSERT_TRUE(uma_proto_.system_profile().os().has_dark_mode_state());
}

TEST_P(ChromeAndroidMetricsProviderTest, OnDidCreateMetricsLog_CustomTabs) {
  // Seed the activity type.
  SetInitialActivityTypeForTesting(activity_type());

  // Call the method under test.
  metrics_provider_.OnDidCreateMetricsLog();

  // Expect 1 sample for all activity types.
  histogram_tester_.ExpectUniqueSample(
      "CustomTabs.Visible", GetCustomTabsVisibleValue(activity_type()), 1);
  histogram_tester_.ExpectUniqueSample("Android.ChromeActivity.Type",
                                       activity_type(), 1);
}

TEST_P(ChromeAndroidMetricsProviderTest, ProvideCurrentSessionData_CustomTabs) {
  // Seed the activity type.
  SetInitialActivityTypeForTesting(activity_type());

  // Call the method under test.
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);

  // No emission of activity type histograms in ProvideCurrentSessionData.
  histogram_tester_.ExpectTotalCount("CustomTabs.Visible", 0);
  histogram_tester_.ExpectTotalCount("Android.ChromeActivity.Type", 0);
}

// Tests initial transition from kPreFirstTab to !kPreFirstTab.
TEST_P(ChromeAndroidMetricsProviderTest, SetActivityType_CustomTabs) {
  // kPreFirstTab -> kPreFirstTab is not a valid scenario. Early exit.
  if (activity_type() == ActivityType::kPreFirstTab)
    return;

  // Validating startup, so seed the activity type to kPreFirstTab,
  SetInitialActivityTypeForTesting(ActivityType::kPreFirstTab);

  // Set the activity type as if a new tab was created..
  SetActivityType(&pref_service_, activity_type());

  // Emission of Actiity type histograms has been deferred until now. So we
  // should see activity type samples.
  histogram_tester_.ExpectUniqueSample(
      "CustomTabs.Visible", GetCustomTabsVisibleValue(activity_type()), 1);
  histogram_tester_.ExpectUniqueSample("Android.ChromeActivity.Type",
                                       activity_type(), 1);
}

// Tests initial warmup records, no tab becomes visible
TEST_F(ChromeAndroidMetricsProviderTest, NoInitialTab) {
  // Validating startup, so seed the activity type to kPreFirstTab,
  SetInitialActivityTypeForTesting(ActivityType::kPreFirstTab);

  // On startup an initial record is created... but no tab becomes active
  // (example: background warmup or user is still going through FRE), and
  // the initial log record gets closed...
  metrics_provider_.OnDidCreateMetricsLog();
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);

  // Only one sample issued over lifetime of first record.
  histogram_tester_.ExpectUniqueSample(
      "CustomTabs.Visible",
      GetCustomTabsVisibleValue(ActivityType::kPreFirstTab), 1);
  histogram_tester_.ExpectUniqueSample("Android.ChromeActivity.Type",
                                       ActivityType::kPreFirstTab, 1);

  // ... and a new record opened...  triggering another sample.
  metrics_provider_.OnDidCreateMetricsLog();
  histogram_tester_.ExpectUniqueSample(
      "CustomTabs.Visible",
      GetCustomTabsVisibleValue(ActivityType::kPreFirstTab), 2);

  // ... and subsequently closed...
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);
  histogram_tester_.ExpectUniqueSample(
      "CustomTabs.Visible",
      GetCustomTabsVisibleValue(ActivityType::kPreFirstTab), 2);
  histogram_tester_.ExpectUniqueSample("Android.ChromeActivity.Type",
                                       ActivityType::kPreFirstTab, 2);
}

// Tests initial transition from kPreFirstTab to !kPreFirstTab.
TEST_P(ChromeAndroidMetricsProviderTest, InitialTab) {
  // kPreFirstTab -> kPreFirstTab is not a valid scenario. Early exit.
  if (activity_type() == ActivityType::kPreFirstTab)
    return;

  // Validating startup, so seed the activity type to kPreFirstTab,
  SetInitialActivityTypeForTesting(ActivityType::kPreFirstTab);

  // On startup an initial record is created... then a tab is resumed which
  // sets the activity type and eventually closes the first log record.
  metrics_provider_.OnDidCreateMetricsLog();
  SetActivityType(&pref_service_, activity_type());
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);

  // Only two samples issued over lifetime of first record, one undeclared and
  // one for the tested activity type.
  histogram_tester_.ExpectTotalCount("CustomTabs.Visible", 2);
  histogram_tester_.ExpectBucketCount(
      "CustomTabs.Visible",
      GetCustomTabsVisibleValue(ActivityType::kPreFirstTab), 1);
  histogram_tester_.ExpectBucketCount(
      "CustomTabs.Visible", GetCustomTabsVisibleValue(activity_type()), 1);
  histogram_tester_.ExpectTotalCount("Android.ChromeActivity.Type", 2);
  histogram_tester_.ExpectBucketCount("Android.ChromeActivity.Type",
                                      ActivityType::kPreFirstTab, 1);
  histogram_tester_.ExpectBucketCount("Android.ChromeActivity.Type",
                                      activity_type(), 1);

  // ... and a second record is opened/closed
  metrics_provider_.OnDidCreateMetricsLog();
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);

  // One additional sample issued over lifetime of second record.
  histogram_tester_.ExpectTotalCount("CustomTabs.Visible", 3);
  histogram_tester_.ExpectBucketCount(
      "CustomTabs.Visible",
      GetCustomTabsVisibleValue(ActivityType::kPreFirstTab), 1);
  histogram_tester_.ExpectBucketCount(
      "CustomTabs.Visible", GetCustomTabsVisibleValue(activity_type()), 2);
  histogram_tester_.ExpectTotalCount("Android.ChromeActivity.Type", 3);
  histogram_tester_.ExpectBucketCount("Android.ChromeActivity.Type",
                                      ActivityType::kPreFirstTab, 1);
  histogram_tester_.ExpectBucketCount("Android.ChromeActivity.Type",
                                      activity_type(), 2);
}

// Tests initial transition from kPreFirstTab to !kPreFirstTab.
TEST_P(ChromeAndroidMetricsProviderTest, TabSwitching) {
  // kPreFirstTab -> kPreFirstTab is not a valid scenario. Early exit.
  if (activity_type() == ActivityType::kPreFirstTab)
    return;

  const auto first_activity_type = activity_type();
  const auto second_activity_type =
      static_cast<ActivityType>((static_cast<int>(first_activity_type) + 1) %
                                static_cast<int>(ActivityType::kMaxValue));

  // Validating startup, so seed the activity type to kPreFirstTab,
  SetInitialActivityTypeForTesting(ActivityType::kPreFirstTab);

  // On startup an initial record is created
  metrics_provider_.OnDidCreateMetricsLog();

  // Then a tab is created/resumed which sets the activity type and eventually
  // closes the first log record and starts a second record.
  SetActivityType(&pref_service_, first_activity_type);
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);
  metrics_provider_.OnDidCreateMetricsLog();

  // A second tab is created/resumed: static data is updated, previous record
  // is closed, new record is created for new activity.
  SetActivityType(&pref_service_, second_activity_type);
  metrics_provider_.ProvideCurrentSessionData(&uma_proto_);
  metrics_provider_.OnDidCreateMetricsLog();

  // Two records were created for the first activity.
  histogram_tester_.ExpectBucketCount("Android.ChromeActivity.Type",
                                      first_activity_type, 2);

  // One additional record created for second activity..
  histogram_tester_.ExpectBucketCount("Android.ChromeActivity.Type",
                                      second_activity_type, 1);

  // The "CustomTabs.Visible" samples may collapse to the same bucket (or not)
  // depending on the actvity pair, so we don't test them here. The coverage
  // from the other tests is sufficient for validating "CustomTabs.Visible".
}

INSTANTIATE_TEST_SUITE_P(All,
                         ChromeAndroidMetricsProviderTest,
                         testing::Values(ActivityType::kTabbed,
                                         ActivityType::kCustomTab,
                                         ActivityType::kTrustedWebActivity,
                                         ActivityType::kWebapp,
                                         ActivityType::kWebApk,
                                         ActivityType::kAuthTab,
                                         ActivityType::kPreFirstTab));