chromium/chrome/browser/ui/ash/test/feature_discovery_duration_reporter_browsertest.cc

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

#include "ash/public/cpp/feature_discovery_duration_reporter.h"
#include "ash/public/cpp/feature_discovery_metric_util.h"
#include "base/json/values_util.h"
#include "base/metrics/histogram_base.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/ash/login/login_manager_test.h"
#include "chrome/browser/ash/login/test/login_manager_mixin.h"
#include "chrome/browser/ash/login/ui/user_adding_screen.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/test/browser_test.h"

namespace ash {

namespace {

// The mock feature's discovery duration histogram.
constexpr char kMockHistogram[] = "FeatureDiscoveryTestMockFeature";

}  // namespace

class FeatureDiscoveryDurationReporterBrowserTest : public LoginManagerTest {
 public:
  FeatureDiscoveryDurationReporterBrowserTest() {
    login_mixin_.AppendRegularUsers(2);
    account_id1_ = login_mixin_.users()[0].account_id;
    account_id2_ = login_mixin_.users()[1].account_id;
  }
  FeatureDiscoveryDurationReporterBrowserTest(
      const FeatureDiscoveryDurationReporterBrowserTest&) = delete;
  FeatureDiscoveryDurationReporterBrowserTest& operator=(
      const FeatureDiscoveryDurationReporterBrowserTest&) = delete;
  ~FeatureDiscoveryDurationReporterBrowserTest() override = default;

  // LoginManagerTest:
  void SetUpOnMainThread() override {
    LoginManagerTest::SetUpOnMainThread();
    LoginUser(account_id1_);
    EXPECT_EQ(
        account_id1_,
        user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId());
  }

  void TearDownOnMainThread() override {
    LoginManagerTest::TearDownOnMainThread();
  }

  AccountId account_id1_;
  AccountId account_id2_;
  ash::LoginManagerMixin login_mixin_{&mixin_host_};
};

// Verifies that the feature discovery duration is recorded on a single active
// session.
IN_PROC_BROWSER_TEST_F(FeatureDiscoveryDurationReporterBrowserTest,
                       RecordFeatureDiscovery) {
  base::HistogramTester histogram_tester;
  auto* reporter = ash::FeatureDiscoveryDurationReporter::GetInstance();
  reporter->MaybeActivateObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  reporter->MaybeFinishObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  histogram_tester.ExpectTotalCount(kMockHistogram, 1);

  // Record the feature discovery for the mock feature again. Verify that
  // duplicate recordings should not be made.
  reporter->MaybeActivateObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  reporter->MaybeFinishObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  histogram_tester.ExpectTotalCount(kMockHistogram, 1);
}

// Verifies that the feature discovery duration recording works as expected
// when the active account is switched.
IN_PROC_BROWSER_TEST_F(FeatureDiscoveryDurationReporterBrowserTest,
                       RecordFeatureDiscoveryWithAccountSwitch) {
  base::HistogramTester histogram_tester;
  auto* reporter = ash::FeatureDiscoveryDurationReporter::GetInstance();

  // Switch to another account. Because user 2 is not primary, the feature
  // discovery duration should not be recorded.
  ash::UserAddingScreen::Get()->Start();
  AddUser(account_id2_);
  reporter->MaybeActivateObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  reporter->MaybeFinishObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  histogram_tester.ExpectTotalCount(kMockHistogram, 0);

  // Switch back to the primary account. Verify that the feature discovery
  // duration is recorded.
  user_manager::UserManager::Get()->SwitchActiveUser(account_id1_);
  reporter->MaybeActivateObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  reporter->MaybeFinishObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  histogram_tester.ExpectTotalCount(kMockHistogram, 1);
}

// Verifies that switching to another account then switching back should resume
// feature discovery duration recordings.
IN_PROC_BROWSER_TEST_F(FeatureDiscoveryDurationReporterBrowserTest,
                       ResumeMetricRecording) {
  base::HistogramTester histogram_tester;
  auto* reporter = ash::FeatureDiscoveryDurationReporter::GetInstance();
  reporter->MaybeActivateObservation(
      feature_discovery::TrackableFeature::kMockFeature);

  // Switch to another account without ending the observation explicitly.
  // Because user 2 is not primary, the feature discovery duration should not
  // be recorded.
  ash::UserAddingScreen::Get()->Start();
  AddUser(account_id2_);
  reporter->MaybeActivateObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  reporter->MaybeFinishObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  histogram_tester.ExpectTotalCount(kMockHistogram, 0);

  // Switch back to the onboarding account. Finish the observation without
  // starting explicitly. Verify that the metric is recorded.
  user_manager::UserManager::Get()->SwitchActiveUser(account_id1_);
  reporter->MaybeFinishObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  histogram_tester.ExpectTotalCount(kMockHistogram, 1);
}

IN_PROC_BROWSER_TEST_F(FeatureDiscoveryDurationReporterBrowserTest,
                       PRE_SaveCumulatedTimeWhenSignout) {
  auto* reporter = ash::FeatureDiscoveryDurationReporter::GetInstance();
  reporter->MaybeActivateObservation(
      feature_discovery::TrackableFeature::kMockFeature);

  // Wait for some time so that the cumulated time is not zero.
  base::RunLoop run_loop;
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(100));
  run_loop.Run();
}

// Verifies that unfinished observations should resume after re-login.
IN_PROC_BROWSER_TEST_F(FeatureDiscoveryDurationReporterBrowserTest,
                       SaveCumulatedTimeWhenSignout) {
  auto* reporter = ash::FeatureDiscoveryDurationReporter::GetInstance();

  // Fetch the cumulated time from the pref service and check it is non-zero.
  PrefService* primary_user_pref =
      ash::ProfileHelper::Get()
          ->GetProfileByUser(user_manager::UserManager::Get()->GetPrimaryUser())
          ->GetPrefs();
  const base::Value* duration_value =
      primary_user_pref->GetDict("FeatureDiscoveryReporterObservedFeatures")
          .FindDict("kMockFeature")
          ->Find("cumulative_duration");
  std::optional<base::TimeDelta> duration =
      base::ValueToTimeDelta(duration_value);
  EXPECT_NE(base::TimeDelta(), *duration);

  // Finish the observation. Verify that the discovery duration is recorded.
  base::HistogramTester histogram_tester;
  reporter->MaybeFinishObservation(
      feature_discovery::TrackableFeature::kMockFeature);
  histogram_tester.ExpectTotalCount(kMockHistogram, 1);
}

}  // namespace ash