// Copyright 2019 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/guest_os/guest_os_engagement_metrics.h"
#include <algorithm>
#include <string>
#include <utility>
#include "base/test/metrics/histogram_tester.h"
#include "base/test/simple_test_clock.h"
#include "base/test/simple_test_tick_clock.h"
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power_manager/idle.pb.h"
#include "components/guest_os/guest_os_prefs.h"
#include "components/prefs/testing_pref_service.h"
#include "components/session_manager/core/session_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/test/test_windows.h"
namespace guest_os {
namespace {
constexpr char kUmaName[] = "Foo";
constexpr char kPrefPrefix[] = "Bar";
constexpr char kHistogramTotal[] = "Total";
constexpr char kHistogramForeground[] = "Foreground";
constexpr char kHistogramBackground[] = "Background";
constexpr char kHistogramActiveTotal[] = "FooTotal";
class GuestOsEngagementMetricsTest : public testing::Test {
protected:
GuestOsEngagementMetricsTest() = default;
GuestOsEngagementMetricsTest(const GuestOsEngagementMetricsTest&) = delete;
GuestOsEngagementMetricsTest& operator=(const GuestOsEngagementMetricsTest&) =
delete;
void SetUp() override {
chromeos::PowerManagerClient::InitializeFake();
ash::SessionManagerClient::InitializeFakeInMemory();
pref_service_ = std::make_unique<TestingPrefServiceSimple>();
matched_window_.reset(aura::test::CreateTestWindowWithId(0, nullptr));
non_matched_window_.reset(aura::test::CreateTestWindowWithId(0, nullptr));
prefs::RegisterEngagementProfilePrefs(pref_service_->registry(),
kPrefPrefix);
// The code doesn't work for correctly for a clock just at the epoch so
// advance by a day first.
test_clock_.Advance(base::Days(1));
CreateEngagementMetrics();
SetSessionState(session_manager::SessionState::ACTIVE);
}
void TearDown() override {
engagement_metrics_.reset();
non_matched_window_.reset();
matched_window_.reset();
pref_service_.reset();
ash::SessionManagerClient::Shutdown();
chromeos::PowerManagerClient::Shutdown();
}
GuestOsEngagementMetrics* engagement_metrics() {
return engagement_metrics_.get();
}
void SetSessionState(session_manager::SessionState state) {
session_manager_.SetSessionState(state);
}
void SetScreenDimmed(bool is_screen_dimmed) {
power_manager::ScreenIdleState screen_idle_state;
screen_idle_state.set_dimmed(is_screen_dimmed);
static_cast<chromeos::FakePowerManagerClient*>(
chromeos::PowerManagerClient::Get())
->SendScreenIdleStateChanged(screen_idle_state);
}
void AdvanceSeconds(int seconds) {
test_tick_clock_.Advance(base::Seconds(seconds));
}
void FocusMatchedWindow() {
engagement_metrics()->OnWindowActivated(
wm::ActivationChangeObserver::ActivationReason::INPUT_EVENT,
matched_window_.get(), nullptr);
}
void FocusNonMatchedWindow() {
engagement_metrics()->OnWindowActivated(
wm::ActivationChangeObserver::ActivationReason::INPUT_EVENT,
non_matched_window_.get(), nullptr);
}
void TriggerRecordEngagementTimeToUma() {
// Trigger UMA record by changing to next day.
test_clock_.Advance(base::Days(1));
engagement_metrics_->OnSessionStateChanged();
}
void ExpectTime(const std::string& histogram, int seconds) {
tester_.ExpectTimeBucketCount("Foo.EngagementTime." + histogram,
base::Seconds(seconds), 1);
}
void DestroyEngagementMetrics() { engagement_metrics_.reset(); }
void CreateEngagementMetrics() {
engagement_metrics_ =
GuestOsEngagementMetrics::GetEngagementMetricsForTesting(
pref_service_.get(),
base::BindRepeating(&GuestOsEngagementMetricsTest::MatchWindow,
base::Unretained(this)),
kPrefPrefix, kUmaName, &test_clock_, &test_tick_clock_);
}
private:
bool MatchWindow(const aura::Window* window) {
return window == matched_window_.get();
}
content::BrowserTaskEnvironment task_environment_;
session_manager::SessionManager session_manager_;
base::SimpleTestTickClock test_tick_clock_;
base::SimpleTestClock test_clock_;
base::HistogramTester tester_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
std::unique_ptr<aura::Window> matched_window_;
std::unique_ptr<aura::Window> non_matched_window_;
std::unique_ptr<GuestOsEngagementMetrics> engagement_metrics_;
};
TEST_F(GuestOsEngagementMetricsTest, RecordEngagementTimeSessionLocked) {
SetSessionState(session_manager::SessionState::LOCKED);
AdvanceSeconds(1);
SetSessionState(session_manager::SessionState::ACTIVE);
AdvanceSeconds(5);
SetSessionState(session_manager::SessionState::LOCKED);
AdvanceSeconds(10);
TriggerRecordEngagementTimeToUma();
ExpectTime(kHistogramTotal, 5);
ExpectTime(kHistogramForeground, 0);
ExpectTime(kHistogramBackground, 0);
ExpectTime(kHistogramActiveTotal, 0);
}
TEST_F(GuestOsEngagementMetricsTest, RecordEngagementTimeScreenDimmed) {
SetScreenDimmed(true);
AdvanceSeconds(1);
SetScreenDimmed(false);
AdvanceSeconds(5);
SetScreenDimmed(true);
AdvanceSeconds(10);
TriggerRecordEngagementTimeToUma();
ExpectTime(kHistogramTotal, 5);
ExpectTime(kHistogramForeground, 0);
ExpectTime(kHistogramBackground, 0);
ExpectTime(kHistogramActiveTotal, 0);
}
TEST_F(GuestOsEngagementMetricsTest, RecordEngagementTimeChangeFocus) {
FocusMatchedWindow();
AdvanceSeconds(2);
FocusNonMatchedWindow();
AdvanceSeconds(4);
FocusMatchedWindow();
AdvanceSeconds(10);
FocusNonMatchedWindow();
AdvanceSeconds(20);
// No background time is recorded as background activity is based on calls to
// SetBackgroundActive and not background windows.
TriggerRecordEngagementTimeToUma();
ExpectTime(kHistogramTotal, 36);
ExpectTime(kHistogramForeground, 12);
ExpectTime(kHistogramBackground, 0);
ExpectTime(kHistogramActiveTotal, 12);
}
TEST_F(GuestOsEngagementMetricsTest, RecordEngagementTimeBackgroundActive) {
AdvanceSeconds(10);
engagement_metrics()->SetBackgroundActive(true);
AdvanceSeconds(5);
engagement_metrics()->SetBackgroundActive(false);
AdvanceSeconds(1);
TriggerRecordEngagementTimeToUma();
ExpectTime(kHistogramTotal, 16);
ExpectTime(kHistogramForeground, 0);
ExpectTime(kHistogramBackground, 5);
ExpectTime(kHistogramActiveTotal, 5);
}
TEST_F(GuestOsEngagementMetricsTest,
RecordEngagementTimeBackgroundAndForeground) {
AdvanceSeconds(1);
engagement_metrics()->SetBackgroundActive(true);
AdvanceSeconds(2);
FocusMatchedWindow();
AdvanceSeconds(4);
FocusNonMatchedWindow();
AdvanceSeconds(10);
engagement_metrics()->SetBackgroundActive(false);
AdvanceSeconds(20);
TriggerRecordEngagementTimeToUma();
ExpectTime(kHistogramTotal, 37);
ExpectTime(kHistogramForeground, 4);
ExpectTime(kHistogramBackground, 12);
ExpectTime(kHistogramActiveTotal, 16);
}
TEST_F(GuestOsEngagementMetricsTest, RecordEngagementTimeIfDestroyed) {
AdvanceSeconds(1); // Total
engagement_metrics()->SetBackgroundActive(true);
FocusMatchedWindow();
AdvanceSeconds(1); // Total + foreground + active
DestroyEngagementMetrics();
AdvanceSeconds(1); // Nothing
CreateEngagementMetrics();
engagement_metrics()->SetBackgroundActive(true);
FocusNonMatchedWindow();
AdvanceSeconds(1); // Total + background + active
TriggerRecordEngagementTimeToUma();
ExpectTime(kHistogramTotal, 3);
ExpectTime(kHistogramForeground, 1);
ExpectTime(kHistogramBackground, 1);
ExpectTime(kHistogramActiveTotal, 2);
}
} // namespace
} // namespace guest_os