chromium/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.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 <string>

#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chrome/browser/ash/app_mode/metrics/periodic_metrics_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/sync_wifi/network_test_helper.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/user_activity/user_activity_detector.h"

namespace ash {

namespace {

struct KioskSessionInternetAccessTestCase {
  std::string test_name;
  bool is_first_app_offline_enabled;
  bool is_second_app_offline_enabled;
  bool should_make_offline;
  KioskInternetAccessInfo expected_metric;
};

struct KioskSessionUserActivityTestCase {
  std::string test_name;
  base::TimeDelta idle_timeout;
  KioskUserActivity expected_metric;
};

}  // namespace

class BasePeriodicMetricsServiceTest {
 public:
  BasePeriodicMetricsServiceTest()
      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
        local_state_(std::make_unique<ScopedTestingLocalState>(
            TestingBrowserProcess::GetGlobal())),
        network_handler_test_helper_(
            std::make_unique<NetworkHandlerTestHelper>()),
        periodic_metrics_service_(local_state()),
        histogram_tester_(std::make_unique<base::HistogramTester>()) {}

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

  base::test::TaskEnvironment* task_environment() { return &task_environment_; }

  TestingPrefServiceSimple* local_state() { return local_state_->Get(); }

  base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }

  void RecordPreviousSessionMetrics() {
    periodic_metrics_service_.RecordPreviousSessionMetrics();
  }

  void StartRecordingPeriodicMetrics(bool is_offline_enabled = true) {
    periodic_metrics_service_.StartRecordingPeriodicMetrics(is_offline_enabled);
    // Some periodic metrics are calculated asynchronously.
    task_environment_.RunUntilIdle();
  }

  void EmulateKioskRestart(bool is_first_app_offline_enabled = true,
                           bool is_second_app_offline_enabled = true) {
    // Emulate running a kiosk session.
    RecordPreviousSessionMetrics();
    StartRecordingPeriodicMetrics(is_first_app_offline_enabled);
    // Nothing was saved in prefs. That means the kiosk session started the
    // first time.
    histogram_tester()->ExpectTotalCount(
        kKioskSessionRestartInternetAccessHistogram, 0);
    histogram_tester()->ExpectTotalCount(
        kKioskSessionRestartUserActivityHistogram, 0);

    // Emulate kiosk session restart.
    RecordPreviousSessionMetrics();
    StartRecordingPeriodicMetrics(is_second_app_offline_enabled);
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  sync_preferences::TestingPrefServiceSyncable user_prefs_;
  std::unique_ptr<ScopedTestingLocalState> local_state_;
  std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
  PeriodicMetricsService periodic_metrics_service_;
  std::unique_ptr<base::HistogramTester> histogram_tester_;
};

class PeriodicMetricsServiceTest : public BasePeriodicMetricsServiceTest,
                                   public ::testing::Test {
 public:
  PeriodicMetricsServiceTest() = default;
  PeriodicMetricsServiceTest(const PeriodicMetricsServiceTest&) = delete;
  PeriodicMetricsServiceTest& operator=(const PeriodicMetricsServiceTest&) =
      delete;
};

TEST_F(PeriodicMetricsServiceTest, PeriodicMetrics) {
  const char* const kPeriodicMetrics[] = {
      kKioskRamUsagePercentageHistogram, kKioskSwapUsagePercentageHistogram,
      kKioskDiskUsagePercentageHistogram, kKioskChromeProcessCountHistogram};
  for (const char* metric : kPeriodicMetrics) {
    histogram_tester()->ExpectTotalCount(metric, 0);
  }

  StartRecordingPeriodicMetrics();
  // Check that periodic metrics were recoreded right calling
  // `StartRecordingPeriodicMetrics`.
  for (const char* metric : kPeriodicMetrics) {
    histogram_tester()->ExpectTotalCount(metric, 1);
  }

  // Next time periodic metrics should be recorded only after
  // `kPeriodicMetricsInterval`.
  task_environment()->FastForwardBy(kPeriodicMetricsInterval / 2);
  for (const char* metric : kPeriodicMetrics) {
    histogram_tester()->ExpectTotalCount(metric, 1);
  }

  task_environment()->FastForwardBy(kPeriodicMetricsInterval / 2);
  for (const char* metric : kPeriodicMetrics) {
    histogram_tester()->ExpectTotalCount(metric, 2);
  }
}

class InternetPeriodicMetricsServiceTest
    : public ::testing::TestWithParam<KioskSessionInternetAccessTestCase>,
      public BasePeriodicMetricsServiceTest {
 public:
  InternetPeriodicMetricsServiceTest() = default;
  InternetPeriodicMetricsServiceTest(
      const InternetPeriodicMetricsServiceTest&) = delete;
  InternetPeriodicMetricsServiceTest& operator=(
      const InternetPeriodicMetricsServiceTest&) = delete;

  void SetUp() override { network_handler_test_helper_->AddDefaultProfiles(); }

  void TearDown() override {
    network_handler_test_helper_.reset();
    local_state()->RemoveUserPref(prefs::kKioskMetrics);
  }

  void MakeOffline() {
    network_handler_test_helper_->ResetDevicesAndServices();
  }
};

TEST_P(InternetPeriodicMetricsServiceTest, KioskInternetMetric) {
  const KioskSessionInternetAccessTestCase& test_config = GetParam();
  if (test_config.should_make_offline) {
    MakeOffline();
  }

  EmulateKioskRestart(test_config.is_first_app_offline_enabled,
                      test_config.is_second_app_offline_enabled);
  histogram_tester()->ExpectBucketCount(
      kKioskSessionRestartInternetAccessHistogram, test_config.expected_metric,
      1);
  histogram_tester()->ExpectTotalCount(
      kKioskSessionRestartInternetAccessHistogram, 1);
}

INSTANTIATE_TEST_SUITE_P(
    InternetAccessInfos,
    InternetPeriodicMetricsServiceTest,
    testing::ValuesIn<KioskSessionInternetAccessTestCase>({
        {/*test_name=*/"OnlineAndAppSupportsOffline",
         /*is_first_app_offline_enabled=*/true,
         /*is_second_app_offline_enabled=*/true, /*should_make_offline=*/false,
         /*expected_metric=*/
         KioskInternetAccessInfo::kOnlineAndAppSupportsOffline},
        {/*test_name=*/"OfflineAndAppSupportsOffline",
         /*is_first_app_offline_enabled=*/true,
         /*is_second_app_offline_enabled=*/true, /*should_make_offline=*/true,
         /*expected_metric=*/
         KioskInternetAccessInfo::kOfflineAndAppSupportsOffline},
        {/*test_name=*/"OfflineAndAppRequiresInternet",
         /*is_first_app_offline_enabled=*/false,
         /*is_second_app_offline_enabled=*/false, /*should_make_offline=*/true,
         /*expected_metric=*/
         KioskInternetAccessInfo::kOfflineAndAppRequiresInternet},
        {/*test_name=*/"DifferentSupportOfflineMode",
         /*is_first_app_offline_enabled=*/false,
         /*is_second_app_offline_enabled=*/true, /*should_make_offline=*/false,
         /*expected_metric=*/
         KioskInternetAccessInfo::kOnlineAndAppRequiresInternet},
    }),
    [](const testing::TestParamInfo<
        InternetPeriodicMetricsServiceTest::ParamType>& info) {
      return info.param.test_name;
    });

class UserActivityPeriodicMetricsServiceTest
    : public ::testing::TestWithParam<KioskSessionUserActivityTestCase>,
      public BasePeriodicMetricsServiceTest {
 public:
  UserActivityPeriodicMetricsServiceTest() = default;
  UserActivityPeriodicMetricsServiceTest(
      const UserActivityPeriodicMetricsServiceTest&) = delete;
  UserActivityPeriodicMetricsServiceTest& operator=(
      const UserActivityPeriodicMetricsServiceTest&) = delete;

  void TearDown() override {
    local_state()->RemoveUserPref(prefs::kKioskMetrics);
  }

  void SetDeviceIdleTime(base::TimeDelta idle_time,
                         base::TimeTicks from_time = base::TimeTicks::Now()) {
    ui::UserActivityDetector::Get()->set_last_activity_time_for_test(from_time -
                                                                     idle_time);
  }
};

TEST_P(UserActivityPeriodicMetricsServiceTest, KioskUserActivityMetric) {
  const KioskSessionUserActivityTestCase& test_config = GetParam();

  SetDeviceIdleTime(test_config.idle_timeout);
  EmulateKioskRestart();

  histogram_tester()->ExpectBucketCount(
      kKioskSessionRestartUserActivityHistogram, test_config.expected_metric,
      1);
  histogram_tester()->ExpectTotalCount(
      kKioskSessionRestartUserActivityHistogram, 1);
}

INSTANTIATE_TEST_SUITE_P(
    UserActivityInfos,
    UserActivityPeriodicMetricsServiceTest,
    testing::ValuesIn<KioskSessionUserActivityTestCase>({
        {/*test_name=*/"UserActiv",
         /*idle_timeout=*/base::Seconds(0),
         /*expected_metric=*/KioskUserActivity::kActive},
        {/*test_name=*/"UserAlmostIdle",
         /*idle_timeout=*/kFirstIdleTimeout / 2,
         /*expected_metric=*/KioskUserActivity::kActive},
        {/*test_name=*/"UserIdle",
         /*idle_timeout=*/kFirstIdleTimeout,
         /*expected_metric=*/KioskUserActivity::kIdle},
    }),
    [](const testing::TestParamInfo<
        UserActivityPeriodicMetricsServiceTest::ParamType>& info) {
      return info.param.test_name;
    });

// Check that after the first metrics record, ChromeOS is using a new idle
// timeout.
TEST_F(UserActivityPeriodicMetricsServiceTest, UserActivityTimeoutChanged) {
  ASSERT_TRUE(kFirstIdleTimeout < kRegularIdleTimeout / 2);
  SetDeviceIdleTime(kFirstIdleTimeout);

  // Emulate running a kiosk session. The user is idle.
  RecordPreviousSessionMetrics();
  StartRecordingPeriodicMetrics();

  // User was active during.
  SetDeviceIdleTime(
      kRegularIdleTimeout / 2,
      /*from_time=*/base::TimeTicks::Now() + kPeriodicMetricsInterval);
  task_environment()->FastForwardBy(kPeriodicMetricsInterval);

  // Emulate kiosk session restart.
  RecordPreviousSessionMetrics();

  histogram_tester()->ExpectBucketCount(
      kKioskSessionRestartUserActivityHistogram, KioskUserActivity::kActive, 1);
  histogram_tester()->ExpectTotalCount(
      kKioskSessionRestartUserActivityHistogram, 1);
}

TEST_F(UserActivityPeriodicMetricsServiceTest, UserBecomesIdle) {
  ASSERT_TRUE(kFirstIdleTimeout < kRegularIdleTimeout / 2);
  SetDeviceIdleTime(kFirstIdleTimeout / 2);

  // Emulate running a kiosk session.
  RecordPreviousSessionMetrics();
  StartRecordingPeriodicMetrics();

  // User becomes idle and metrics should be updated because of the timeout.
  SetDeviceIdleTime(kRegularIdleTimeout, /*from_time=*/base::TimeTicks::Now() +
                                             kPeriodicMetricsInterval);
  task_environment()->FastForwardBy(kPeriodicMetricsInterval);

  // Emulate kiosk session restart.
  RecordPreviousSessionMetrics();

  histogram_tester()->ExpectBucketCount(
      kKioskSessionRestartUserActivityHistogram, KioskUserActivity::kIdle, 1);
  histogram_tester()->ExpectTotalCount(
      kKioskSessionRestartUserActivityHistogram, 1);
}

}  // namespace ash