chromium/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc

// Copyright 2020 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/ash/child_accounts/family_user_session_metrics.h"

#include <memory>

#include "base/logging.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chrome/common/pref_names.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power_manager/idle.pb.h"
#include "chromeos/dbus/power_manager/suspend.pb.h"
#include "components/prefs/testing_pref_service.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/session_manager_types.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace {

constexpr base::TimeDelta kTenMinutes = base::Minutes(10);
constexpr base::TimeDelta kOneHour = base::Hours(1);
constexpr base::TimeDelta kOneDay = base::Days(1);

void SetScreenOff(bool is_screen_off) {
  power_manager::ScreenIdleState screen_idle_state;
  screen_idle_state.set_off(is_screen_off);
  chromeos::FakePowerManagerClient::Get()->SendScreenIdleStateChanged(
      screen_idle_state);
}

void SetSuspendImminent() {
  chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
      power_manager::SuspendImminent_Reason_OTHER);
}

void CancelSuspend() {
  chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
}

}  // namespace

class FamilyUserSessionMetricsTest : public testing::Test {
 protected:
  FamilyUserSessionMetricsTest() = default;
  ~FamilyUserSessionMetricsTest() override = default;

  void SetUp() override {
    chromeos::PowerManagerClient::InitializeFake();
    InitiateFamilyUserSessionMetrics();
    FamilyUserSessionMetrics::RegisterProfilePrefs(pref_service_.registry());
  }

  void TearDown() override {
    DestructFamilyUserSessionMetrics();
    chromeos::PowerManagerClient::Shutdown();
  }

  void DestructFamilyUserSessionMetrics() {
    family_user_session_metrics_.reset();
  }

  void InitiateFamilyUserSessionMetrics() {
    family_user_session_metrics_ =
        std::make_unique<FamilyUserSessionMetrics>(&pref_service_);
  }

  void SetupTaskRunnerWithTime(const std::string& start_time_str) {
    base::Time start_time;
    EXPECT_TRUE(base::Time::FromString(start_time_str.c_str(), &start_time));
    base::TimeDelta forward_by = start_time - base::Time::Now();
    EXPECT_LT(base::TimeDelta(), forward_by);
    task_environment_.FastForwardBy(forward_by);
  }

  void SetSessionState(session_manager::SessionState state) {
    session_manager_.SetSessionState(state);
  }

  void SetActiveSessionStartTime(base::Time time) {
    family_user_session_metrics_->SetActiveSessionStartForTesting(time);
  }

  session_manager::SessionState GetSessionState() {
    return session_manager_.session_state();
  }

  void OnNewDay() { family_user_session_metrics_->OnNewDay(); }

  PrefService* pref_service() { return &pref_service_; }

  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

 private:
  session_manager::SessionManager session_manager_;
  TestingPrefServiceSimple pref_service_;
  std::unique_ptr<FamilyUserSessionMetrics> family_user_session_metrics_;
};

TEST_F(FamilyUserSessionMetricsTest, SessionStateChange) {
  base::HistogramTester histogram_tester;
  base::UserActionTester user_action_tester;

  // Should see 0 in user session metrics at first.
  EXPECT_EQ(0,
            user_action_tester.GetActionCount(
                FamilyUserSessionMetrics::kSessionEngagementStartActionName));

  SetupTaskRunnerWithTime("1 Jan 2020 23:00");

  SetSessionState(session_manager::SessionState::ACTIVE);

  task_environment_.FastForwardBy(kTenMinutes);
  // Session locked at 23:10:00.
  SetSessionState(session_manager::SessionState::LOCKED);

  task_environment_.FastForwardBy(kOneHour);
  // Session activated at 00:10:00 on 2 Jan 2020.
  SetSessionState(session_manager::SessionState::ACTIVE);

  OnNewDay();

  task_environment_.FastForwardBy(kOneHour);
  // Session locked at 01:10:00 on 2 Jan 2020.
  SetSessionState(session_manager::SessionState::LOCKED);

  // Engagement start metric result:
  EXPECT_EQ(2,
            user_action_tester.GetActionCount(
                FamilyUserSessionMetrics::kSessionEngagementStartActionName));

  for (int i = 0; i <= 23; i++) {
    if (i == 0 || i == 1 || i == 23) {
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementWeekdayHistogramName, i,
          1);
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, i, 1);
    } else {
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementWeekdayHistogramName, i,
          0);
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, i, 0);
    }
  }

  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, 3);

  // Duration metric result:
  histogram_tester.ExpectUniqueTimeSample(
      FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName,
      kTenMinutes, 1);
  EXPECT_EQ(kOneHour, pref_service()->GetTimeDelta(
                          prefs::kFamilyUserMetricsSessionEngagementDuration));
}

TEST_F(FamilyUserSessionMetricsTest, ScreenStateChange) {
  base::HistogramTester histogram_tester;
  base::UserActionTester user_action_tester;

  SetupTaskRunnerWithTime("3 Jan 2020 23:10");

  SetSessionState(session_manager::SessionState::ACTIVE);
  task_environment_.FastForwardBy(kOneHour);

  // Test screen off after midnight at 0:10:00 on 4 Jan 2020.
  SetScreenOff(true);

  OnNewDay();

  // Engagement start metric result:
  EXPECT_EQ(1,
            user_action_tester.GetActionCount(
                FamilyUserSessionMetrics::kSessionEngagementStartActionName));

  // Engagement Hour metric result:
  histogram_tester.ExpectUniqueSample(
      FamilyUserSessionMetrics::kSessionEngagementWeekdayHistogramName, 23, 1);
  histogram_tester.ExpectUniqueSample(
      FamilyUserSessionMetrics::kSessionEngagementWeekendHistogramName, 0, 1);
  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, 2);

  // Duration metric result:
  histogram_tester.ExpectUniqueTimeSample(
      FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName,
      kOneHour, 1);
  EXPECT_EQ(base::TimeDelta(),
            pref_service()->GetTimeDelta(
                prefs::kFamilyUserMetricsSessionEngagementDuration));

  // Test screen on on 4 Jan 2020 0:10:00.
  SetScreenOff(false);

  task_environment_.FastForwardBy(base::Hours(25));
  // Test screen off on 5 Jan 2020 1:10:00.
  SetScreenOff(true);

  OnNewDay();

  // Engagement start metric result:
  EXPECT_EQ(2,
            user_action_tester.GetActionCount(
                FamilyUserSessionMetrics::kSessionEngagementStartActionName));

  // Engagement Hour metric result:
  histogram_tester.ExpectUniqueSample(
      FamilyUserSessionMetrics::kSessionEngagementWeekdayHistogramName, 23, 1);
  for (int i = 0; i <= 23; i++) {
    if (i == 0) {
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementWeekendHistogramName, i,
          3);
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, i, 3);
    } else if (i == 1) {
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementWeekendHistogramName, i,
          2);
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, i, 2);
    } else if (i == 23) {
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementWeekendHistogramName, i,
          1);
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, i, 2);

    } else {
      histogram_tester.ExpectBucketCount(
          FamilyUserSessionMetrics::kSessionEngagementWeekendHistogramName, i,
          1);
    }
  }

  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, 28);

  // Duration metric result:
  histogram_tester.ExpectTimeBucketCount(
      FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName,
      kOneHour, 1);
  histogram_tester.ExpectTimeBucketCount(
      FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName,
      kOneDay, 1);
  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 2);
  EXPECT_EQ(base::TimeDelta(),
            pref_service()->GetTimeDelta(
                prefs::kFamilyUserMetricsSessionEngagementDuration));
}

TEST_F(FamilyUserSessionMetricsTest, SuspendStateChange) {
  base::HistogramTester histogram_tester;
  base::UserActionTester user_action_tester;

  SetupTaskRunnerWithTime("4 Jan 2020 6:00");

  SetSessionState(session_manager::SessionState::ACTIVE);
  task_environment_.FastForwardBy(kTenMinutes);

  // Test suspend at 6:10:00.
  SetSuspendImminent();
  task_environment_.FastForwardBy(kTenMinutes);

  // Test cancel at 6:20:00.
  CancelSuspend();

  task_environment_.FastForwardBy(kTenMinutes);

  // Test suspend at 6:30:00.
  SetSuspendImminent();
  task_environment_.FastForwardBy(kTenMinutes);

  // Session locked at 6:40:00.
  SetSessionState(session_manager::SessionState::LOCKED);

  // Engagement start metric result:
  EXPECT_EQ(2,
            user_action_tester.GetActionCount(
                FamilyUserSessionMetrics::kSessionEngagementStartActionName));

  // Engagement Hour metric result:
  histogram_tester.ExpectUniqueSample(
      FamilyUserSessionMetrics::kSessionEngagementWeekendHistogramName, 6, 2);
  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, 2);

  // Duration metric result:
  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 0);
  EXPECT_EQ(base::Minutes(20),
            pref_service()->GetTimeDelta(
                prefs::kFamilyUserMetricsSessionEngagementDuration));
}

TEST_F(FamilyUserSessionMetricsTest, ClockBackward) {
  base::HistogramTester histogram_tester;
  base::UserActionTester user_action_tester;

  SetupTaskRunnerWithTime("1 Jan 2020 10:00");

  SetSessionState(session_manager::SessionState::ACTIVE);

  base::Time mock_session_start;
  ASSERT_TRUE(base::Time::FromString("1 Jan 2020 11:00", &mock_session_start));

  // Set session start prefs to 11:00:00. Mock a state that start time > end
  // time.
  SetActiveSessionStartTime(mock_session_start);

  // Session locked at 10:00:00.
  SetSessionState(session_manager::SessionState::LOCKED);

  // Engagement start metric result:
  EXPECT_EQ(1,
            user_action_tester.GetActionCount(
                FamilyUserSessionMetrics::kSessionEngagementStartActionName));

  // Engagement Hour metric result:
  // Engagement hour and duration data will be ignored if start time > end time.
  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementWeekdayHistogramName, 0);
  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, 0);

  // Duration metric result:
  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 0);
  EXPECT_EQ(base::TimeDelta(),
            pref_service()->GetTimeDelta(
                prefs::kFamilyUserMetricsSessionEngagementDuration));
}

// Tests destroying FamilyUserSessionMetrics without invoking
// OnUsageTimeStateChange(). It may happens during shutdown of device.
TEST_F(FamilyUserSessionMetricsTest,
       DestructionAndCreationOfFamilyUserSessionMetrics) {
  base::HistogramTester histogram_tester;
  base::UserActionTester user_action_tester;

  SetupTaskRunnerWithTime("1 Jan 2020 10:00");

  SetSessionState(session_manager::SessionState::ACTIVE);

  task_environment_.FastForwardBy(kTenMinutes);

  DestructFamilyUserSessionMetrics();
  SetSessionState(session_manager::SessionState::UNKNOWN);

  // Engagement start metric result:
  EXPECT_EQ(1,
            user_action_tester.GetActionCount(
                FamilyUserSessionMetrics::kSessionEngagementStartActionName));

  // Engagement Hour metric result:
  histogram_tester.ExpectUniqueSample(
      FamilyUserSessionMetrics::kSessionEngagementWeekdayHistogramName, 10, 1);

  // Duration metric result:
  histogram_tester.ExpectTotalCount(
      prefs::kFamilyUserMetricsSessionEngagementDuration, 0);
  EXPECT_EQ(kTenMinutes,
            pref_service()->GetTimeDelta(
                prefs::kFamilyUserMetricsSessionEngagementDuration));

  // Test restart.
  InitiateFamilyUserSessionMetrics();
  EXPECT_NE(session_manager::SessionState::ACTIVE, GetSessionState());
  SetSessionState(session_manager::SessionState::ACTIVE);

  // Engagement start metric result:
  EXPECT_EQ(2,
            user_action_tester.GetActionCount(
                FamilyUserSessionMetrics::kSessionEngagementStartActionName));

  task_environment_.FastForwardBy(kTenMinutes);
  SetSessionState(session_manager::SessionState::LOCKED);

  // Engagement Hour metric result:
  histogram_tester.ExpectUniqueSample(
      FamilyUserSessionMetrics::kSessionEngagementWeekdayHistogramName, 10, 2);
  histogram_tester.ExpectTotalCount(
      FamilyUserSessionMetrics::kSessionEngagementTotalHistogramName, 2);

  // Duration metric result:
  histogram_tester.ExpectTotalCount(
      prefs::kFamilyUserMetricsSessionEngagementDuration, 0);
  EXPECT_EQ(base::Minutes(20),
            pref_service()->GetTimeDelta(
                prefs::kFamilyUserMetricsSessionEngagementDuration));
}

}  // namespace ash