// Copyright 2021 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/apps/app_service/metrics/app_platform_metrics_service.h"
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "base/containers/extend.h"
#include "base/containers/flat_set.h"
#include "base/json/values_util.h"
#include "base/metrics/histogram_base.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "chrome/browser/apps/app_service/app_launch_params.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/browser_app_launcher.h"
#include "chrome/browser/apps/app_service/extension_apps_utils.h"
#include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h"
#include "chrome/browser/apps/app_service/metrics/app_platform_metrics_service_test_base.h"
#include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
#include "chrome/browser/apps/app_service/publishers/app_publisher.h"
#include "chrome/browser/ash/borealis/borealis_util.h"
#include "chrome/browser/ash/borealis/testing/apps.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/crostini/crostini_test_helper.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
#include "chrome/browser/metrics/usertype_by_devicetype_metrics_provider.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/test/base/test_browser_window_aura.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/standalone_browser/feature_refs.h"
#include "chromeos/components/mgs/managed_guest_session_test_utils.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 "chromeos/ui/base/app_types.h"
#include "chromeos/ui/base/window_properties.h"
#include "components/app_constants/constants.h"
#include "components/metrics/structured/recorder.h"
#include "components/metrics/structured/structured_events.h"
#include "components/metrics/structured/structured_metrics_client.h"
#include "components/metrics/structured/structured_metrics_features.h"
#include "components/metrics/structured/test/test_structured_metrics_provider.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/instance.h"
#include "components/services/app_service/public/cpp/instance_registry.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/test/test_sync_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/browser_task_environment.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
using ::testing::_;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::Sequence;
using ::testing::StrEq;
namespace apps {
namespace {
constexpr char kChromeAppId[] = "plfjlfohfjjpmmifkbcmalnmcebkklkh";
constexpr char kExtensionId[] = "mhjfbmdgcfjbbpaeojofohoefgiehjai";
constexpr char kAndroidAppId[] = "a";
constexpr char kSystemWebAppId[] = "system_web_app_id";
constexpr char kWebAppId1[] = "web_app_id_1";
constexpr char kWebAppId2[] = "web_app_id_2";
constexpr char kAndroidAppPublisherId[] = "com.google.A";
constexpr apps::InstanceState kActiveInstanceState =
static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning |
apps::InstanceState::kActive | apps::InstanceState::kVisible);
constexpr apps::InstanceState kInactiveInstanceState =
static_cast<apps::InstanceState>(apps::InstanceState::kStarted |
apps::InstanceState::kRunning);
const base::flat_set<InstallReason>
kAllowedInstallReasonsInManagedGuestSession = {
InstallReason::kSystem, InstallReason::kPolicy, InstallReason::kOem,
InstallReason::kDefault};
const base::flat_set<InstallReason>
kBlockedInstallReasonsInManagedGuestSession = {
InstallReason::kUnknown, InstallReason::kSync,
InstallReason::kUser, InstallReason::kSubApp,
InstallReason::kKiosk, InstallReason::kCommandLine};
namespace cros_events = metrics::structured::events::v2::cr_os_events;
// Mock observer that observes app platform metrics event callbacks for testing
// purposes.
class MockAppPlatformMetricsObserver : public AppPlatformMetrics::Observer {
public:
MockAppPlatformMetricsObserver() = default;
MockAppPlatformMetricsObserver(const MockAppPlatformMetricsObserver&) =
delete;
MockAppPlatformMetricsObserver& operator=(
const MockAppPlatformMetricsObserver&) = delete;
~MockAppPlatformMetricsObserver() override = default;
MOCK_METHOD(void,
OnAppInstalled,
(const std::string& app_id,
AppType app_type,
InstallSource app_install_source,
InstallReason app_install_reason,
InstallTime app_install_time),
(override));
MOCK_METHOD(void,
OnAppLaunched,
(const std::string& app_id,
AppType app_type,
apps::LaunchSource launch_source),
(override));
MOCK_METHOD(void,
OnAppUninstalled,
(const std::string& app_id,
AppType app_type,
UninstallSource app_uninstall_source),
(override));
MOCK_METHOD(void,
OnAppUsage,
(const std::string& app_id,
AppType app_type,
const base::UnguessableToken& instance_id,
base::TimeDelta running_time),
(override));
MOCK_METHOD(void, OnAppPlatformMetricsDestroyed, (), (override));
};
// Mock observer implementation for the `AppPlatformMetricsService` component.
class MockObserver : public AppPlatformMetricsService::Observer {
public:
MockObserver() = default;
MockObserver(const MockObserver&) = delete;
MockObserver& operator=(const MockObserver&) = delete;
~MockObserver() override = default;
MOCK_METHOD(void,
OnAppPlatformMetricsInit,
(AppPlatformMetrics * app_platform_metrics),
(override));
MOCK_METHOD(void,
OnWebsiteMetricsInit,
(WebsiteMetrics * website_metrics),
(override));
MOCK_METHOD(void, OnAppPlatformMetricsServiceWillBeDestroyed, (), (override));
};
class FakePublisher : public AppPublisher {
public:
FakePublisher(AppServiceProxy* proxy, AppType app_type)
: AppPublisher(proxy) {
RegisterPublisher(app_type);
}
MOCK_METHOD4(Launch,
void(const std::string& app_id,
int32_t event_flags,
LaunchSource launch_source,
WindowInfoPtr window_info));
MOCK_METHOD2(LaunchAppWithParams,
void(AppLaunchParams&& params, LaunchCallback callback));
MOCK_METHOD6(LoadIcon,
void(const std::string& app_id,
const IconKey& icon_key,
apps::IconType icon_type,
int32_t size_hint_in_dip,
bool allow_placeholder_icon,
LoadIconCallback callback));
};
// Impl for testing structured metrics that forwards all writes to the recorder
// directly.
class TestRecorder
: public metrics::structured::StructuredMetricsClient::RecordingDelegate {
public:
TestRecorder() {
metrics::structured::StructuredMetricsClient::Get()->SetDelegate(this);
}
bool IsReadyToRecord() const override { return true; }
void RecordEvent(metrics::structured::Event&& event) override {
metrics::structured::Recorder::GetInstance()->RecordEvent(std::move(event));
}
};
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);
}
} // namespace
// Tests for app platform metrics service.
class AppPlatformMetricsServiceTest
: public AppPlatformMetricsServiceTestBase,
public ::testing::WithParamInterface<bool> {
public:
void SetUp() override {
if (IsLacrosEnabled()) {
feature_list_.InitWithFeatures(
/*enabled_features=*/ash::standalone_browser::GetFeatureRefs(), {});
scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
ash::switches::kEnableLacrosForTesting);
} else {
feature_list_.InitWithFeatures(
{}, /*disabled_features=*/ash::standalone_browser::GetFeatureRefs());
}
AppPlatformMetricsServiceTestBase::SetUp();
InstallApps();
// The WebAppProvider system must be started after the apps are added, as
// the tests explicitly check that the apps start in the 'initializing'
// state (where they have not yet been registered with the WebAppProvider
// system).
web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
ASSERT_EQ(IsLacrosEnabled(), crosapi::browser_util::IsLacrosEnabled());
}
void TearDown() override {
AppPlatformMetricsServiceTestBase::TearDown();
browser_window1_.reset();
browser_window2_.reset();
metrics::structured::StructuredMetricsClient::Get()->UnsetDelegate();
}
AppTypeName GetWebAppTypeName() {
return IsLacrosEnabled() ? AppTypeName::kStandaloneBrowserWebApp
: AppTypeName::kWeb;
}
AppTypeName GetAppTypeName(const TestApp& test_app) {
return ::apps::GetAppTypeName(profile(), test_app.app_type, test_app.app_id,
apps::LaunchContainer::kLaunchContainerNone);
}
void InstallApps() {
pre_installed_apps_.insert(
{kAndroidAppId,
TestApp(kAndroidAppId, AppType::kArc, kAndroidAppPublisherId,
Readiness::kReady, InstallReason::kUser,
InstallSource::kPlayStore)});
pre_installed_apps_.insert(
{borealis::kClientAppId,
TestApp(borealis::kClientAppId, AppType::kBorealis, "",
Readiness::kReady, InstallReason::kUser,
InstallSource::kUnknown)});
borealis::CreateFakeApp(profile(), "borealistest", "steam://rungameid/123");
std::string borealis_app(borealis::FakeAppId("borealistest"));
pre_installed_apps_.insert(
{borealis_app.c_str(),
TestApp(borealis_app.c_str(), AppType::kBorealis, "",
Readiness::kReady, InstallReason::kUser,
InstallSource::kUnknown)});
vm_tools::apps::ApplicationList app_list =
crostini::CrostiniTestHelper::BasicAppList("test");
guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile())
->UpdateApplicationList(app_list);
std::string crostini_id =
crostini::CrostiniTestHelper::GenerateAppId("test");
pre_installed_apps_.insert(
{crostini_id,
TestApp(crostini_id, AppType::kCrostini, "", Readiness::kReady,
InstallReason::kUser, InstallSource::kUnknown)});
pre_installed_apps_.insert(
{kWebAppId1,
TestApp(kWebAppId1, AppType::kWeb, "https://foo.com",
Readiness::kReady, InstallReason::kSync, InstallSource::kSync,
/*should_notify_initialized=*/false)});
pre_installed_apps_.insert(
{kWebAppId2, TestApp(kWebAppId2, AppType::kWeb, "https://foo2.com",
Readiness::kReady, InstallReason::kSync,
InstallSource::kSync)});
pre_installed_apps_.insert(
{kSystemWebAppId,
TestApp(kSystemWebAppId, AppType::kSystemWeb, "https://os-settings",
Readiness::kReady, InstallReason::kSystem,
InstallSource::kSystem)});
pre_installed_apps_.insert(
{app_constants::kLacrosAppId,
TestApp(app_constants::kLacrosAppId, AppType::kStandaloneBrowser,
"Lacros", Readiness::kReady, InstallReason::kSystem,
InstallSource::kSystem)});
pre_installed_apps_.insert(
{kChromeAppId,
TestApp(kChromeAppId, AppType::kStandaloneBrowserChromeApp, "Vine",
Readiness::kReady, InstallReason::kUser,
InstallSource::kChromeWebStore,
/*should_notify_initialized=*/true,
/*is_platform_app=*/true)});
pre_installed_apps_.insert(
{kExtensionId,
TestApp(kExtensionId, AppType::kStandaloneBrowserExtension,
"PDF Viewer", Readiness::kReady, InstallReason::kUser,
InstallSource::kChromeWebStore)});
pre_installed_apps_.insert(
{"u", TestApp("u", AppType::kUnknown, "", Readiness::kReady,
InstallReason::kUnknown, InstallSource::kUnknown,
/*should_notify_initialized=*/false)});
pre_installed_apps_.insert(
{"p", TestApp("p", AppType::kPluginVm, "", Readiness::kReady,
InstallReason::kUser, InstallSource::kUnknown,
/*should_notify_initialized=*/false)});
pre_installed_apps_.insert(
{"r", TestApp("r", AppType::kRemote, "", Readiness::kReady,
InstallReason::kPolicy, InstallSource::kUnknown,
/*should_notify_initialized=*/false)});
pre_installed_apps_.insert(
{"subapp", TestApp("subapp", AppType::kWeb, "", Readiness::kReady,
InstallReason::kSubApp, InstallSource::kUnknown,
/*should_notify_initialized=*/false)});
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
CHECK(proxy);
for (const auto& [_, pre_installed_app] : pre_installed_apps_) {
AddApp(proxy, pre_installed_app);
}
}
void VerifyMetrics() {
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(AppTypeName::kArc),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kArc, apps::InstallReason::kUser),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kBuiltIn),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kBuiltIn, apps::InstallReason::kSystem),
/*count=*/1);
// Should be 4 Borealis apps: The installer + launcher created by the
// BorealisApps class, plus the two created in this test.
const int borealis_pre_installed = 2;
const int borealis_installed_by_test = 2;
histogram_tester().ExpectUniqueSample(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kBorealis),
/*sample=*/borealis_pre_installed + borealis_installed_by_test,
/*expected_bucket_count=*/1);
// The installer + launcher are preinstalled, the others are user-installed.
histogram_tester().ExpectUniqueSample(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kBorealis, apps::InstallReason::kDefault),
/*sample=*/borealis_pre_installed,
/*expected_bucket_count=*/1);
histogram_tester().ExpectUniqueSample(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kBorealis, apps::InstallReason::kUser),
/*sample=*/borealis_installed_by_test,
/*expected_bucket_count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kCrostini),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kCrostini, apps::InstallReason::kUser),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kChromeApp),
/*count=*/0);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
GetWebAppTypeName()),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
GetWebAppTypeName(), apps::InstallReason::kSync),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kPluginVm),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kPluginVm, apps::InstallReason::kUser),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kStandaloneBrowser),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kStandaloneBrowser, apps::InstallReason::kSystem),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kStandaloneBrowserChromeApp),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kStandaloneBrowserChromeApp,
apps::InstallReason::kUser),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kStandaloneBrowserChromeApp),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kStandaloneBrowserChromeApp,
apps::InstallReason::kUser),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kRemote),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kRemote, apps::InstallReason::kPolicy),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(
AppTypeName::kSystemWeb),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
AppTypeName::kSystemWeb, apps::InstallReason::kSystem),
/*count=*/1);
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountPerInstallReasonHistogramNameForTest(
GetWebAppTypeName(), apps::InstallReason::kSubApp),
/*count=*/1);
}
void set_user_segment(
UserTypeByDeviceTypeMetricsProvider::UserSegment user_segment) {
user_segment_ = user_segment;
}
std::unique_ptr<Browser> CreateBrowserWithAuraWindow1() {
std::unique_ptr<aura::Window> window = std::make_unique<aura::Window>(
&delegate1_, aura::client::WINDOW_TYPE_NORMAL);
window->SetId(0);
window->Init(ui::LAYER_TEXTURED);
Browser::CreateParams params(profile(), true);
params.type = Browser::TYPE_NORMAL;
browser_window1_ =
std::make_unique<TestBrowserWindowAura>(std::move(window));
params.window = browser_window1_.get();
return std::unique_ptr<Browser>(Browser::Create(params));
}
std::unique_ptr<Browser> CreateBrowserWithAuraWindow2() {
std::unique_ptr<aura::Window> window = std::make_unique<aura::Window>(
&delegate2_, aura::client::WINDOW_TYPE_NORMAL);
window->SetId(0);
window->Init(ui::LAYER_TEXTURED);
Browser::CreateParams params(profile(), true);
params.type = Browser::TYPE_NORMAL;
browser_window2_ =
std::make_unique<TestBrowserWindowAura>(std::move(window));
params.window = browser_window2_.get();
return std::unique_ptr<Browser>(Browser::Create(params));
}
std::unique_ptr<Browser> CreateBrowserWindow(
InstallReason install_reason = InstallReason::kUser) {
InstallOneApp(app_constants::kChromeAppId, AppType::kChromeApp, "Chrome",
Readiness::kReady, InstallSource::kSystem,
/*is_platform_app=*/false, WindowMode::kUnknown,
install_reason);
std::unique_ptr<Browser> browser = CreateBrowserWithAuraWindow1();
EXPECT_EQ(1U, BrowserList::GetInstance()->size());
return browser;
}
std::unique_ptr<aura::Window> CreateWebAppWindow(aura::Window* parent) {
std::unique_ptr<aura::Window> window(
aura::test::CreateTestWindowWithDelegate(&delegate1_, 1, gfx::Rect(),
parent));
return window;
}
GURL GetSourceUrlForApp(const std::string& app_id) {
return AppPlatformMetrics::GetURLForApp(profile(), app_id);
}
void VerifyAppLaunchPerAppTypeHistogram(base::HistogramBase::Count count,
AppTypeName app_type_name) {
histogram_tester().ExpectBucketCount(kAppLaunchPerAppTypeHistogramName,
app_type_name, count);
}
void VerifyAppLaunchPerAppTypeV2Histogram(base::HistogramBase::Count count,
AppTypeNameV2 app_type_name_v2) {
histogram_tester().ExpectBucketCount(kAppLaunchPerAppTypeV2HistogramName,
app_type_name_v2, count);
}
void VerifyAppRunningDuration(const base::TimeDelta time_delta,
AppTypeName app_type_name) {
const base::Value::Dict& dict =
GetPrefService()->GetDict(kAppRunningDuration);
std::string key = GetAppTypeHistogramName(app_type_name);
std::optional<base::TimeDelta> unreported_duration =
base::ValueToTimeDelta(dict.FindByDottedPath(key));
if (time_delta.is_zero()) {
EXPECT_FALSE(unreported_duration.has_value());
return;
}
ASSERT_TRUE(unreported_duration.has_value());
EXPECT_EQ(time_delta, unreported_duration.value());
}
void VerifyAppRunningDurationCountHistogram(
base::HistogramBase::Count expected_count,
AppTypeName app_type_name) {
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsRunningDurationHistogramNameForTest(
app_type_name),
expected_count);
}
void VerifyAppRunningDurationHistogram(
base::TimeDelta time_delta,
base::HistogramBase::Count expected_count,
AppTypeName app_type_name) {
histogram_tester().ExpectTimeBucketCount(
AppPlatformMetrics::GetAppsRunningDurationHistogramNameForTest(
app_type_name),
time_delta, expected_count);
}
void VerifyAppRunningPercentageCountHistogram(
base::HistogramBase::Count expected_count,
AppTypeName app_type_name) {
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsRunningPercentageHistogramNameForTest(
app_type_name),
expected_count);
}
void VerifyAppRunningPercentageHistogram(
int count,
base::HistogramBase::Count expected_count,
AppTypeName app_type_name) {
histogram_tester().ExpectBucketCount(
AppPlatformMetrics::GetAppsRunningPercentageHistogramNameForTest(
app_type_name),
count, expected_count);
}
void VerifyAppActivatedCount(int expected_count, AppTypeName app_type_name) {
const base::Value::Dict& dict =
GetPrefService()->GetDict(kAppActivatedCount);
std::string key = GetAppTypeHistogramName(app_type_name);
std::optional<int> activated_count = dict.FindIntByDottedPath(key);
if (expected_count == 0) {
EXPECT_FALSE(activated_count.has_value());
return;
}
ASSERT_TRUE(activated_count.has_value());
EXPECT_EQ(expected_count, activated_count.value());
}
void VerifyAppActivatedCountHistogram(
base::HistogramBase::Count expected_count,
AppTypeName app_type_name) {
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsActivatedCountHistogramNameForTest(
app_type_name),
expected_count);
}
void VerifyAppActivatedHistogram(int count,
base::HistogramBase::Count expected_count,
AppTypeName app_type_name) {
histogram_tester().ExpectBucketCount(
AppPlatformMetrics::GetAppsActivatedCountHistogramNameForTest(
app_type_name),
count, expected_count);
}
void VerifyAppUsageTimeCountHistogram(
base::HistogramBase::Count expected_count,
AppTypeName app_type_name) {
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsUsageTimeHistogramNameForTest(app_type_name),
expected_count);
}
void VerifyAppUsageTimeCountHistogram(
base::HistogramBase::Count expected_count,
AppTypeNameV2 app_type_name) {
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsUsageTimeHistogramNameForTest(app_type_name),
expected_count);
}
void VerifyAppUsageTimeHistogram(base::TimeDelta time_delta,
base::HistogramBase::Count expected_count,
AppTypeName app_type_name) {
histogram_tester().ExpectTimeBucketCount(
AppPlatformMetrics::GetAppsUsageTimeHistogramNameForTest(app_type_name),
time_delta, expected_count);
}
void VerifyAppUsageTimeHistogram(base::TimeDelta time_delta,
base::HistogramBase::Count expected_count,
AppTypeNameV2 app_type_name) {
histogram_tester().ExpectTimeBucketCount(
AppPlatformMetrics::GetAppsUsageTimeHistogramNameForTest(app_type_name),
time_delta, expected_count);
}
void VerifyAppUsageTimeUkmWithUkmName(const std::string& ukm_name,
const std::string& app_id,
base::TimeDelta duration,
AppTypeName app_type_name) {
const std::string kUrl = std::string("app://") + app_id;
const auto entries = test_ukm_recorder()->GetEntriesByName(ukm_name);
int usage_time = 0;
for (const ukm::mojom::UkmEntry* entry : entries) {
const ukm::UkmSource* src =
test_ukm_recorder()->GetSourceForSourceId(entry->source_id);
if (src == nullptr || src->url() != GURL(kUrl)) {
continue;
}
usage_time += *(test_ukm_recorder()->GetEntryMetric(entry, "Duration"));
test_ukm_recorder()->ExpectEntryMetric(
entry, "UserDeviceMatrix",
UserTypeByDeviceTypeMetricsProvider::ConstructUmaValue(
user_segment_, policy::MarketSegment::UNKNOWN));
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)app_type_name);
}
ASSERT_EQ(usage_time, duration.InMilliseconds()) << ukm_name;
}
void VerifyAppUsageTimeUkm(const std::string& app_id,
base::TimeDelta duration,
AppTypeName app_type_name) {
VerifyAppUsageTimeUkmWithUkmName("ChromeOSApp.UsageTime", app_id, duration,
app_type_name);
VerifyAppUsageTimeUkmWithUkmName("ChromeOSApp.UsageTimeReusedSourceId",
app_id, duration, app_type_name);
}
void VerifyAppUsageTimeUkmWithUkmName(const std::string& ukm_name,
const GURL& url,
base::TimeDelta duration,
AppTypeName app_type_name) {
const auto entries = test_ukm_recorder()->GetEntriesByName(ukm_name);
int usage_time = 0;
for (const ukm::mojom::UkmEntry* entry : entries) {
const ukm::UkmSource* src =
test_ukm_recorder()->GetSourceForSourceId(entry->source_id);
if (src == nullptr || src->url() != url) {
continue;
}
usage_time += *(test_ukm_recorder()->GetEntryMetric(entry, "Duration"));
test_ukm_recorder()->ExpectEntryMetric(entry, "UserDeviceMatrix", 0);
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)app_type_name);
}
ASSERT_EQ(usage_time, duration.InMilliseconds());
}
void VerifyAppUsageTimeUkm(const GURL& url,
base::TimeDelta duration,
AppTypeName app_type_name) {
VerifyAppUsageTimeUkmWithUkmName("ChromeOSApp.UsageTime", url, duration,
app_type_name);
VerifyAppUsageTimeUkmWithUkmName("ChromeOSApp.UsageTimeReusedSourceId", url,
duration, app_type_name);
}
void VerifyAppUsageTimeUkm(uint32_t count) {
auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UsageTime");
ASSERT_EQ(count, entries.size());
entries = test_ukm_recorder()->GetEntriesByName(
"ChromeOSApp.UsageTimeReusedSourceId");
ASSERT_EQ(count, entries.size());
}
void VerifyNoAppUsageTimeUkm() { VerifyAppUsageTimeUkm(/*count=*/0); }
void VerifyInstalledAppsUkm(const TestApp& app, InstallTime install_time) {
const auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InstalledApp");
int count = 0;
for (const ukm::mojom::UkmEntry* entry : entries) {
const ukm::UkmSource* src =
test_ukm_recorder()->GetSourceForSourceId(entry->source_id);
if (src == nullptr || src->url() != GetSourceUrlForApp(app.app_id)) {
continue;
}
++count;
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)GetAppTypeName(app));
test_ukm_recorder()->ExpectEntryMetric(entry, "InstallReason",
(int)app.install_reason);
test_ukm_recorder()->ExpectEntryMetric(entry, "InstallSource2",
(int)app.install_source);
test_ukm_recorder()->ExpectEntryMetric(entry, "InstallTime",
(int)install_time);
}
ASSERT_EQ(1, count);
}
void VerifyNoInstalledAppUkm(const TestApp& app) {
const auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InstalledApp");
for (const ukm::mojom::UkmEntry* entry : entries) {
const ukm::UkmSource* src =
test_ukm_recorder()->GetSourceForSourceId(entry->source_id);
if (src != nullptr) {
ASSERT_NE(src->url(), GetSourceUrlForApp(app.app_id));
}
}
}
void VerifyAppsLaunchUkm(const std::string& app_info,
AppTypeName app_type_name,
LaunchSource launch_source) {
const auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.Launch");
int count = 0;
for (const ukm::mojom::UkmEntry* entry : entries) {
const ukm::UkmSource* src =
test_ukm_recorder()->GetSourceForSourceId(entry->source_id);
if (src == nullptr || src->url() != GURL(app_info)) {
continue;
}
++count;
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)app_type_name);
test_ukm_recorder()->ExpectEntryMetric(entry, "LaunchSource",
(int)launch_source);
}
ASSERT_EQ(1, count);
}
void VerifyAppsUninstallUkm(const std::string& app_info,
AppTypeName app_type_name,
UninstallSource uninstall_source) {
const auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.UninstallApp");
int count = 0;
for (const ukm::mojom::UkmEntry* entry : entries) {
const ukm::UkmSource* src =
test_ukm_recorder()->GetSourceForSourceId(entry->source_id);
if (src == nullptr || src->url() != GURL(app_info)) {
continue;
}
++count;
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)app_type_name);
test_ukm_recorder()->ExpectEntryMetric(entry, "UninstallSource",
(int)uninstall_source);
}
ASSERT_EQ(1, count);
}
std::map<std::string, TestApp>& pre_installed_apps() {
return pre_installed_apps_;
}
bool IsLacrosEnabled() const { return GetParam(); }
protected:
base::test::ScopedFeatureList feature_list_;
base::test::ScopedCommandLine scoped_command_line_;
private:
std::unique_ptr<TestBrowserWindowAura> browser_window1_;
std::unique_ptr<TestBrowserWindowAura> browser_window2_;
aura::test::TestWindowDelegate delegate1_;
aura::test::TestWindowDelegate delegate2_;
std::map<std::string, TestApp> pre_installed_apps_;
UserTypeByDeviceTypeMetricsProvider::UserSegment user_segment_ =
UserTypeByDeviceTypeMetricsProvider::UserSegment::kUnmanaged;
};
// Tests OnNewDay() is called after more than one day passes.
TEST_P(AppPlatformMetricsServiceTest, MoreThanOneDay) {
task_environment_.FastForwardBy(base::Days(1) + base::Hours(1));
VerifyMetrics();
EXPECT_EQ(AppPlatformMetricsService::GetDayIdForTesting(base::Time::Now()),
GetDayIdPref());
}
// Tests OnNewDay() is called at midnight.
TEST_P(AppPlatformMetricsServiceTest, UntilMidnight) {
task_environment_.FastForwardBy(base::Hours(3));
VerifyMetrics();
EXPECT_EQ(AppPlatformMetricsService::GetDayIdForTesting(base::Time::Now()),
GetDayIdPref());
}
// Tests OnNewDay() is not called before midnight.
TEST_P(AppPlatformMetricsServiceTest, LessThanOneDay) {
task_environment_.FastForwardBy(base::Hours(1));
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(AppTypeName::kArc),
/*count=*/0);
EXPECT_EQ(AppPlatformMetricsService::GetDayIdForTesting(base::Time::Now()),
GetDayIdPref());
}
// Tests OnNewDay() is called after one day passes, even when the device is
// idle.
TEST_P(AppPlatformMetricsServiceTest, MoreThanOneDayDeviceIdle) {
SetScreenOff(true);
SetSuspendImminent();
task_environment_.FastForwardBy(base::Days(1));
VerifyMetrics();
EXPECT_EQ(AppPlatformMetricsService::GetDayIdForTesting(base::Time::Now()),
GetDayIdPref());
}
// Tests the UMA metrics that count the number of installed apps.
TEST_P(AppPlatformMetricsServiceTest, InstallApps) {
task_environment_.FastForwardBy(base::Hours(3));
VerifyMetrics();
InstallOneApp("aa", AppType::kArc, "com.google.AA", Readiness::kReady,
InstallSource::kPlayStore);
task_environment_.FastForwardBy(base::Days(1));
histogram_tester().ExpectTotalCount(
AppPlatformMetrics::GetAppsCountHistogramNameForTest(AppTypeName::kArc),
/*count=*/2);
}
TEST_P(AppPlatformMetricsServiceTest, BrowserWindow) {
InstallOneApp(app_constants::kChromeAppId, AppType::kChromeApp, "Chrome",
Readiness::kReady, InstallSource::kSystem);
BrowserList* active_browser_list = BrowserList::GetInstance();
// Expect BrowserList is empty at the beginning.
EXPECT_EQ(0U, active_browser_list->size());
std::unique_ptr<Browser> browser1 = CreateBrowserWithAuraWindow1();
EXPECT_EQ(1U, active_browser_list->size());
// Set the browser window active.
ModifyInstance(app_constants::kChromeAppId,
browser1->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppActivatedCount(/*expected_count=*/1, AppTypeName::kChromeBrowser);
task_environment_.FastForwardBy(base::Minutes(20));
// Set the browser window running in the background.
ModifyInstance(app_constants::kChromeAppId,
browser1->window()->GetNativeWindow(), kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppRunningDuration(base::Minutes(30), AppTypeName::kChromeBrowser);
// Test multiple browsers.
std::unique_ptr<Browser> browser2 = CreateBrowserWithAuraWindow2();
EXPECT_EQ(2U, active_browser_list->size());
ModifyInstance(app_constants::kChromeAppId,
browser2->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppActivatedCount(/*expected_count=*/2, AppTypeName::kChromeBrowser);
task_environment_.FastForwardBy(base::Minutes(20));
ModifyInstance(app_constants::kChromeAppId,
browser2->window()->GetNativeWindow(),
apps::InstanceState::kDestroyed);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppRunningDuration(base::Hours(1), AppTypeName::kChromeBrowser);
// Test date change.
task_environment_.FastForwardBy(base::Days(1));
VerifyAppRunningDurationCountHistogram(/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyAppRunningDurationHistogram(base::Hours(1),
/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyAppRunningPercentageCountHistogram(/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyAppRunningPercentageHistogram(100,
/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyAppActivatedCountHistogram(/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyAppActivatedHistogram(/*count*/ 2, /*expected_count=*/1,
AppTypeName::kChromeBrowser);
}
// Tests the UMA metrics when launching an app in one day .
TEST_P(AppPlatformMetricsServiceTest, OpenWindowInOneDay) {
std::string app_id = "aa";
InstallOneApp(app_id, AppType::kArc, "com.google.AA", Readiness::kReady,
InstallSource::kPlayStore);
// Create a window to simulate launching the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(app_id, window.get(), apps::InstanceState::kActive);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppActivatedCount(/*expected_count=*/1, AppTypeName::kArc);
// Close the window after running one hour.
task_environment_.FastForwardBy(base::Minutes(50));
ModifyInstance(app_id, window.get(), apps::InstanceState::kDestroyed);
task_environment_.FastForwardBy(base::Hours(1));
VerifyAppRunningDuration(base::Hours(1), AppTypeName::kArc);
// One day passes.
task_environment_.FastForwardBy(base::Hours(1));
VerifyAppRunningDurationCountHistogram(/*expected_count=*/1,
AppTypeName::kArc);
VerifyAppRunningDurationHistogram(base::Hours(1),
/*expected_count=*/1, AppTypeName::kArc);
VerifyAppActivatedCountHistogram(/*expected_count=*/1, AppTypeName::kArc);
VerifyAppActivatedHistogram(/*count*/ 1, /*expected_count=*/1,
AppTypeName::kArc);
VerifyAppRunningDuration(base::TimeDelta(), AppTypeName::kArc);
VerifyAppActivatedCount(/*expected_count=*/0, AppTypeName::kArc);
// One more day passes.
task_environment_.FastForwardBy(base::Days(1));
VerifyAppRunningDurationCountHistogram(/*expected_count=*/1,
AppTypeName::kArc);
VerifyAppRunningDurationHistogram(base::Hours(1),
/*expected_count=*/1, AppTypeName::kArc);
VerifyAppRunningPercentageCountHistogram(/*expected_count=*/1,
AppTypeName::kArc);
VerifyAppRunningPercentageHistogram(100,
/*expected_count=*/1, AppTypeName::kArc);
VerifyAppActivatedCountHistogram(/*expected_count=*/1, AppTypeName::kArc);
}
// Tests the UMA metrics when launching an app multiple days.
TEST_P(AppPlatformMetricsServiceTest, OpenWindowInMultipleDays) {
std::string app_id = "aa";
InstallOneApp(app_id, AppType::kArc, "com.google.AA", Readiness::kReady,
InstallSource::kPlayStore);
// Create a window to simulate launching the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(app_id, window.get(), apps::InstanceState::kActive);
task_environment_.FastForwardBy(base::Hours(1));
VerifyAppActivatedCount(/*expected_count=*/1, AppTypeName::kArc);
// One day passes.
task_environment_.FastForwardBy(base::Hours(2));
VerifyAppRunningDurationCountHistogram(/*expected_count=*/1,
AppTypeName::kArc);
VerifyAppActivatedCountHistogram(/*expected_count=*/1, AppTypeName::kArc);
VerifyAppActivatedHistogram(/*count*/ 1, /*expected_count=*/1,
AppTypeName::kArc);
task_environment_.FastForwardBy(base::Hours(2));
// Close the window after running five hours.
ModifyInstance(app_id, window.get(), apps::InstanceState::kDestroyed);
VerifyAppRunningDurationCountHistogram(/*expected_count=*/1,
AppTypeName::kArc);
VerifyAppRunningDurationHistogram(base::Hours(3),
/*expected_count=*/1, AppTypeName::kArc);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppRunningDuration(base::Hours(2), AppTypeName::kArc);
// One more day passes.
task_environment_.FastForwardBy(base::Days(1));
VerifyAppRunningDurationCountHistogram(/*expected_count=*/2,
AppTypeName::kArc);
VerifyAppRunningDurationHistogram(base::Hours(3),
/*expected_count=*/1, AppTypeName::kArc);
VerifyAppRunningDurationHistogram(base::Hours(2),
/*expected_count=*/1, AppTypeName::kArc);
VerifyAppActivatedCountHistogram(/*expected_count=*/1, AppTypeName::kArc);
VerifyAppRunningDuration(base::Hours(0), AppTypeName::kArc);
VerifyAppActivatedCount(/*expected_count=*/0, AppTypeName::kArc);
}
// Tests the UMA metrics when an app window is reactivated.
TEST_P(AppPlatformMetricsServiceTest, ReactiveWindow) {
std::string app_id = "aa";
InstallOneApp(app_id, AppType::kArc, "com.google.AA", Readiness::kReady,
InstallSource::kPlayStore);
// Create a window to simulate launching the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(app_id, window.get(), apps::InstanceState::kActive);
task_environment_.FastForwardBy(base::Minutes(30));
ModifyInstance(app_id, window.get(), kActiveInstanceState);
VerifyAppActivatedCount(/*expected_count=*/1, AppTypeName::kArc);
// Inactiva the window after running one hour.
task_environment_.FastForwardBy(base::Minutes(30));
ModifyInstance(app_id, window.get(), kInactiveInstanceState);
// Activa the window after running one hour.
task_environment_.FastForwardBy(base::Hours(1));
ModifyInstance(app_id, window.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppActivatedCount(/*expected_count=*/2, AppTypeName::kArc);
// Close the window after running half hour.
task_environment_.FastForwardBy(base::Minutes(20));
ModifyInstance(app_id, window.get(), apps::InstanceState::kDestroyed);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppRunningDuration(base::Hours(1) + base::Minutes(30),
AppTypeName::kArc);
// One day passes.
task_environment_.FastForwardBy(base::Minutes(20));
VerifyAppRunningDurationCountHistogram(/*expected_count=*/1,
AppTypeName::kArc);
VerifyAppRunningDurationHistogram(base::Hours(1) + base::Minutes(30),
/*expected_count=*/1, AppTypeName::kArc);
VerifyAppActivatedCountHistogram(/*expected_count=*/1, AppTypeName::kArc);
VerifyAppActivatedHistogram(/*count*/ 2, /*expected_count=*/1,
AppTypeName::kArc);
// 20 hours passes.
task_environment_.FastForwardBy(base::Hours(20));
// Create a new window.
window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(app_id, window.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppActivatedCount(/*expected_count=*/1, AppTypeName::kArc);
// Inactiva the window after running one hour.
task_environment_.FastForwardBy(base::Minutes(50));
ModifyInstance(app_id, window.get(), kInactiveInstanceState);
// Activa the window after running one hour.
task_environment_.FastForwardBy(base::Hours(1));
ModifyInstance(app_id, window.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Hours(1));
VerifyAppActivatedCount(/*expected_count=*/2, AppTypeName::kArc);
// One more day passes.
task_environment_.FastForwardBy(base::Hours(1));
VerifyAppRunningDurationCountHistogram(/*expected_count=*/2,
AppTypeName::kArc);
VerifyAppRunningDurationHistogram(base::Hours(3),
/*expected_count=*/1, AppTypeName::kArc);
VerifyAppActivatedCountHistogram(/*expected_count=*/2, AppTypeName::kArc);
VerifyAppActivatedHistogram(/*count*/ 2, /*expected_count=*/2,
AppTypeName::kArc);
// Inactiva the window after running one hour.
task_environment_.FastForwardBy(base::Hours(3));
ModifyInstance(app_id, window.get(), kInactiveInstanceState);
// Close the window after running five hour.
task_environment_.FastForwardBy(base::Hours(1));
ModifyInstance(app_id, window.get(), apps::InstanceState::kDestroyed);
task_environment_.FastForwardBy(base::Minutes(10));
VerifyAppRunningDuration(base::Hours(3), AppTypeName::kArc);
// One more day passes.
task_environment_.FastForwardBy(base::Days(1));
VerifyAppRunningDurationCountHistogram(/*expected_count=*/3,
AppTypeName::kArc);
VerifyAppRunningDurationHistogram(base::Hours(3),
/*expected_count=*/2, AppTypeName::kArc);
VerifyAppActivatedCountHistogram(/*expected_count=*/2, AppTypeName::kArc);
VerifyAppRunningDuration(base::Hours(0), AppTypeName::kArc);
VerifyAppActivatedCount(/*expected_count=*/0, AppTypeName::kArc);
}
// Tests the app running percentage UMA metrics when launch a browser window
// and an ARC app in one day.
TEST_P(AppPlatformMetricsServiceTest, AppRunningPercentage) {
std::unique_ptr<Browser> browser = CreateBrowserWindow();
// Set the browser window active.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Hours(1));
// Set the browser window running in the background.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
// Launch an ARC app.
std::string app_id = "aa";
InstallOneApp(app_id, AppType::kArc, "com.google.AA", Readiness::kReady,
InstallSource::kPlayStore);
// Create a window to simulate launching the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(app_id, window.get(), apps::InstanceState::kActive);
// Close the window after running one hour.
task_environment_.FastForwardBy(base::Hours(1));
ModifyInstance(app_id, window.get(), apps::InstanceState::kDestroyed);
// One day passes.
task_environment_.FastForwardBy(base::Hours(1));
VerifyAppRunningPercentageCountHistogram(/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyAppRunningPercentageCountHistogram(/*expected_count=*/1,
AppTypeName::kArc);
VerifyAppRunningPercentageHistogram(50,
/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyAppRunningPercentageHistogram(50,
/*expected_count=*/1, AppTypeName::kArc);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTime) {
// Create an ARC app window.
std::string app_id = "aa";
InstallOneApp(app_id, AppType::kArc, "com.google.AA", Readiness::kReady,
InstallSource::kPlayStore);
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(app_id, window.get(), apps::InstanceState::kActive);
task_environment_.FastForwardBy(base::Minutes(5));
VerifyAppUsageTimeCountHistogram(/*expected_count=*/1, AppTypeName::kArc);
VerifyAppUsageTimeCountHistogram(/*expected_count=*/1, AppTypeNameV2::kArc);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1, AppTypeName::kArc);
task_environment_.FastForwardBy(base::Minutes(2));
ModifyInstance(app_id, window.get(), kInactiveInstanceState);
std::unique_ptr<Browser> browser = CreateBrowserWindow();
// Set the browser window active.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(3));
VerifyAppUsageTimeCountHistogram(/*expected_count=*/2, AppTypeName::kArc);
VerifyAppUsageTimeCountHistogram(/*expected_count=*/2, AppTypeNameV2::kArc);
VerifyAppUsageTimeHistogram(base::Minutes(2),
/*expected_count=*/1, AppTypeName::kArc);
VerifyAppUsageTimeCountHistogram(/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyAppUsageTimeCountHistogram(/*expected_count=*/1,
AppTypeNameV2::kChromeBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(3),
/*expected_count=*/1,
AppTypeName::kChromeBrowser);
VerifyNoAppUsageTimeUkm();
task_environment_.FastForwardBy(base::Minutes(15));
VerifyAppUsageTimeCountHistogram(/*expected_count=*/2, AppTypeName::kArc);
VerifyAppUsageTimeCountHistogram(/*expected_count=*/2, AppTypeNameV2::kArc);
VerifyAppUsageTimeCountHistogram(/*expected_count=*/4,
AppTypeName::kChromeBrowser);
VerifyAppUsageTimeCountHistogram(/*expected_count=*/4,
AppTypeNameV2::kChromeBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/3,
AppTypeName::kChromeBrowser);
VerifyNoAppUsageTimeUkm();
// Set the browser window inactive.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(95));
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(18),
AppTypeName::kChromeBrowser);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTimeForLacros) {
if (!IsLacrosEnabled()) {
return;
}
// Install Chrome apps (hosted apps) during the running time.
std::string kChromeAppId1 = "bb";
InstallOneApp(kChromeAppId1, AppType::kStandaloneBrowserChromeApp, "BB",
Readiness::kReady, InstallSource::kChromeWebStore,
/*is_platform_app=*/false, WindowMode::kBrowser);
const base::UnguessableToken instance_id0 = base::UnguessableToken::Create();
const base::UnguessableToken instance_id1 = base::UnguessableToken::Create();
const base::UnguessableToken instance_id2 = base::UnguessableToken::Create();
// Create a StandaloneBrowser window, and set it as activated for
// `kLacrosAppId`.
auto window1 = std::make_unique<aura::Window>(nullptr);
window1->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(instance_id0, app_constants::kLacrosAppId, window1.get(),
kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Verify recording 5 minutes for AppTypeName::kStandaloneBrowser and
// AppTypeNameV2::kStandaloneBrowser.
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1,
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1,
AppTypeNameV2::kStandaloneBrowser);
// Create a chrome app tab for `kChromeAppId1`, and set it as activated. We
// don't need to set the Lacros window as inactivated, because the activated
// chrome app tab can set the Lacros window as inactivated. And when the
// chrome app tabs are inactivated, the Lacros window can be set as activated.
ModifyInstance(instance_id1, kChromeAppId1, window1.get(),
kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Verify recording 5 minutes for AppTypeName::kStandaloneBrowser and
// AppTypeNameV2::kStandaloneBrowserChromeAppTab.
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/2,
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1,
AppTypeNameV2::kStandaloneBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1,
AppTypeNameV2::kStandaloneBrowserChromeAppTab);
// The chrome app tab is inactivated, so the Lacros window is set as activated
// in code.
ModifyInstance(instance_id1, kChromeAppId1, window1.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Verify recording 5 minutes for AppTypeName::kStandaloneBrowser and
// AppTypeNameV2::kStandaloneBrowser.
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/3,
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/2,
AppTypeNameV2::kStandaloneBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1,
AppTypeNameV2::kStandaloneBrowserChromeAppTab);
// Set the Lacros window as inactivated.
ModifyInstance(instance_id0, app_constants::kLacrosAppId, window1.get(),
kInactiveInstanceState);
// Create a new window for `kChromeAppId`, and set it as activated.
auto window2 = std::make_unique<aura::Window>(nullptr);
window2->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(instance_id2, kChromeAppId, window2.get(),
kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Verify recording 5 minutes for AppTypeName::kStandaloneBrowserChromeApp and
// AppTypeNameV2::kStandaloneBrowserChromeAppWindow.
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/3,
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1,
AppTypeName::kStandaloneBrowserChromeApp);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/2,
AppTypeNameV2::kStandaloneBrowser);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1,
AppTypeNameV2::kStandaloneBrowserChromeAppTab);
VerifyAppUsageTimeHistogram(base::Minutes(5),
/*expected_count=*/1,
AppTypeNameV2::kStandaloneBrowserChromeAppWindow);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTimeUkm) {
std::unique_ptr<Browser> browser = CreateBrowserWindow();
// Set the browser window active.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
sync_service()->SetAllowedByEnterprisePolicy(false);
// Fast forward by 2 hours and verify no usage data is reported to UKM.
task_environment_.FastForwardBy(base::Hours(2));
VerifyNoAppUsageTimeUkm();
sync_service()->SetAllowedByEnterprisePolicy(true);
static constexpr base::TimeDelta kAppUsageDuration = base::Hours(1);
task_environment_.FastForwardBy(kAppUsageDuration);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
// Fast forward by 2 hours and verify usage data reported to UKM only includes
// usage data since sync was last enabled.
task_environment_.FastForwardBy(base::Hours(2));
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, kAppUsageDuration,
AppTypeName::kChromeBrowser);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTimeUkmReportAfterReboot) {
std::unique_ptr<Browser> browser = CreateBrowserWindow();
// Set the browser window active.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(30));
// Create a web app tab.
const GURL url = GURL("https://foo.com");
auto web_app_window =
CreateWebAppWindow(browser->window()->GetNativeWindow());
// Set the web app tab as activated.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(20));
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
ModifyWebAppInstance(kWebAppId1, web_app_window.get(),
kInactiveInstanceState);
VerifyNoAppUsageTimeUkm();
// Reset PlatformMetricsService to simulate the system reboot, and verify
// AppKM is restored from the user pref and reported after 5 minutes after
// reboot.
ResetAppPlatformMetricsService();
VerifyNoAppUsageTimeUkm();
task_environment_.FastForwardBy(base::Minutes(5));
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(30),
AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(url, base::Minutes(20), AppTypeName::kChromeBrowser);
// Set the browser window as activated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(10));
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
// Verify UKM is not reported.
VerifyAppUsageTimeUkm(/*count=*/2);
// Reset PlatformMetricsService to simulate the system reboot, and verify
// only the new AppKM is reported.
ResetAppPlatformMetricsService();
task_environment_.FastForwardBy(base::Minutes(5));
VerifyAppUsageTimeUkm(/*count=*/3);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(40),
AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(url, base::Minutes(20), AppTypeName::kChromeBrowser);
// Reset PlatformMetricsService to simulate the system reboot, and verify no
// more AppKM is reported.
ResetAppPlatformMetricsService();
task_environment_.FastForwardBy(base::Minutes(5));
VerifyAppUsageTimeUkm(/*count=*/3);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(40),
AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(url, base::Minutes(20), AppTypeName::kChromeBrowser);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTimeUkmWithMultipleWindows) {
// Create a browser window.
InstallOneApp(app_constants::kChromeAppId, AppType::kChromeApp, "Chrome",
Readiness::kReady, InstallSource::kSystem);
std::unique_ptr<Browser> browser1 = CreateBrowserWithAuraWindow1();
EXPECT_EQ(1U, BrowserList::GetInstance()->size());
// Set the browser window1 active.
ModifyInstance(app_constants::kChromeAppId,
browser1->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Set the browser window1 inactive.
ModifyInstance(app_constants::kChromeAppId,
browser1->window()->GetNativeWindow(), kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(1));
std::unique_ptr<Browser> browser2 = CreateBrowserWithAuraWindow2();
EXPECT_EQ(2U, BrowserList::GetInstance()->size());
// Set the browser window2 active.
ModifyInstance(app_constants::kChromeAppId,
browser2->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(7));
// Close windows.
ModifyInstance(app_constants::kChromeAppId,
browser1->window()->GetNativeWindow(),
apps::InstanceState::kDestroyed);
ModifyInstance(app_constants::kChromeAppId,
browser2->window()->GetNativeWindow(),
apps::InstanceState::kDestroyed);
VerifyNoAppUsageTimeUkm();
// Verify UKM is reported after 2hours.
task_environment_.FastForwardBy(base::Minutes(107));
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(12),
AppTypeName::kChromeBrowser);
}
TEST_P(AppPlatformMetricsServiceTest,
UsageTimeUkmForWebAppOpenInTabWithInactivatedBrowser) {
std::unique_ptr<Browser> browser = CreateBrowserWindow();
// Create a web app tab.
const GURL url = GURL("https://foo.com");
auto web_app_window =
CreateWebAppWindow(browser->window()->GetNativeWindow());
// Set the browser window as inactivated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
// Set the web app tab as activated.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
VerifyNoAppUsageTimeUkm();
// Set the browser window and web app tabs as inactivated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
ModifyWebAppInstance(kWebAppId1, web_app_window.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(2));
// Set the web app tab as activated.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(), kActiveInstanceState);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(3));
VerifyNoAppUsageTimeUkm();
// Set the web app tab as inactivated.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(1));
// Set the web app tab as destroyed.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(),
apps::InstanceState::kDestroyed);
// Set the browser window as destroyed.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(),
apps::InstanceState::kDestroyed);
VerifyNoAppUsageTimeUkm();
task_environment_.FastForwardBy(base::Minutes(109));
// Verify the app usage time AppKM for the web app and browser window.
VerifyAppUsageTimeUkm(/*count=*/2);
VerifyAppUsageTimeUkm(url, base::Minutes(8), AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(1),
AppTypeName::kChromeBrowser);
}
TEST_P(AppPlatformMetricsServiceTest,
UsageTimeUkmForWebAppOpenInTabWithActivatedBrowser) {
std::unique_ptr<Browser> browser = CreateBrowserWindow();
// Create a web app tab.
const GURL url = GURL("https://foo.com");
auto web_app_window =
CreateWebAppWindow(browser->window()->GetNativeWindow());
// Set the web app tab as activated.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(), kActiveInstanceState);
// Set the browser window as activated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
VerifyNoAppUsageTimeUkm();
// Set the web app tab as inactivated.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(3));
// Set the browser window as inactivated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
VerifyNoAppUsageTimeUkm();
task_environment_.FastForwardBy(base::Minutes(112));
// Verify the app usage time AppKM.
VerifyAppUsageTimeUkm(/*count=*/2);
VerifyAppUsageTimeUkm(url, base::Minutes(5), AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(3),
AppTypeName::kChromeBrowser);
// Set the browser window as activated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
// Set the web app tab as activated.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(2));
// Set the browser window as inactivated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
// Verify no more app usage time AppKM is recorded.
VerifyAppUsageTimeUkm(/*count=*/2);
// Set the web app tab as inactivated.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(118));
// Verify only the web app UKM is reported.
VerifyAppUsageTimeUkm(/*count=*/3);
VerifyAppUsageTimeUkm(url, base::Minutes(7), AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(3),
AppTypeName::kChromeBrowser);
// Set the browser window as activated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(1));
// Set the browser window as destroyed.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(),
apps::InstanceState::kDestroyed);
// Set the web app tab as destroyed.
ModifyWebAppInstance(kWebAppId1, web_app_window.get(),
apps::InstanceState::kDestroyed);
// Verify no more app usage time AppKM is recorded.
VerifyAppUsageTimeUkm(/*count=*/3);
task_environment_.FastForwardBy(base::Minutes(119));
VerifyAppUsageTimeUkm(/*count=*/4);
VerifyAppUsageTimeUkm(url, base::Minutes(7), AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(4),
AppTypeName::kChromeBrowser);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTimeUkmForMultipleWebAppOpenInTab) {
std::unique_ptr<Browser> browser = CreateBrowserWindow();
// Create web app tabs.
const GURL url1 = GURL("https://foo.com");
auto web_app_window1 =
CreateWebAppWindow(browser->window()->GetNativeWindow());
const GURL url2 = GURL("https://foo2.com");
auto web_app_window2 =
CreateWebAppWindow(browser->window()->GetNativeWindow());
// Set the web app tab 1 as activated.
ModifyWebAppInstance(kWebAppId1, web_app_window1.get(), kActiveInstanceState);
ModifyWebAppInstance(kWebAppId2, web_app_window2.get(),
kInactiveInstanceState);
// Set the browser window as activated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Set the web app tab 2 as activated.
ModifyWebAppInstance(kWebAppId2, web_app_window2.get(), kActiveInstanceState);
ModifyWebAppInstance(kWebAppId1, web_app_window1.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(4));
// Set the web app tabs as inactivated.
ModifyWebAppInstance(kWebAppId1, web_app_window1.get(),
kInactiveInstanceState);
ModifyWebAppInstance(kWebAppId2, web_app_window2.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(3));
VerifyNoAppUsageTimeUkm();
// Set the browser window as activated.
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
// Destroy the browser windows, and web app tabs.
ModifyWebAppInstance(kWebAppId1, web_app_window1.get(),
apps::InstanceState::kDestroyed);
ModifyWebAppInstance(kWebAppId2, web_app_window2.get(),
apps::InstanceState::kDestroyed);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(),
apps::InstanceState::kDestroyed);
task_environment_.FastForwardBy(base::Minutes(108));
// Verify the app usage time AppKM for the web apps and browser window.
VerifyAppUsageTimeUkm(/*count=*/3);
VerifyAppUsageTimeUkm(url1, base::Minutes(5), AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(url2, base::Minutes(4), AppTypeName::kChromeBrowser);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(3),
AppTypeName::kChromeBrowser);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTimeUkmForStandaloneBrowserApps) {
// Create a StandaloneBrowser window, and set it as activated for
// `kLacrosAppId`.
auto window1 = std::make_unique<aura::Window>(nullptr);
window1->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(app_constants::kLacrosAppId, window1.get(),
kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Create a Chrome app window, and set it as activated for `kChromeAppId`.
auto window2 = std::make_unique<aura::Window>(nullptr);
window2->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(app_constants::kLacrosAppId, window1.get(),
kInactiveInstanceState);
ModifyInstance(kChromeAppId, window2.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(4));
// Create a Extension window, and set it as activated for `kExtensionId`.
auto window3 = std::make_unique<aura::Window>(nullptr);
window3->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(kChromeAppId, window2.get(), kInactiveInstanceState);
ModifyInstance(kExtensionId, window3.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(3));
// Set the Extension window as inactived.
ModifyInstance(kExtensionId, window3.get(), kInactiveInstanceState);
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(108));
VerifyAppUsageTimeUkm(app_constants::kLacrosAppId, base::Minutes(5),
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeUkm(kChromeAppId, base::Minutes(4),
AppTypeName::kStandaloneBrowserChromeApp);
VerifyAppUsageTimeUkm(kExtensionId, base::Minutes(3),
AppTypeName::kStandaloneBrowserExtension);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTimeUkmForWebAppsOpenInLacrosTabs) {
if (!IsLacrosEnabled()) {
return;
}
const base::UnguessableToken instance_id0 = base::UnguessableToken::Create();
const base::UnguessableToken instance_id1 = base::UnguessableToken::Create();
const base::UnguessableToken instance_id2 = base::UnguessableToken::Create();
const GURL url1 = GURL("https://foo.com");
const GURL url2 = GURL("https://foo2.com");
// Create a StandaloneBrowser window, and set it as activated for
// `kLacrosAppId`.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(instance_id0, app_constants::kLacrosAppId, window.get(),
kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Create a web app tab for `kWebAppId1`, and set it as activated. We don't
// need to set the Lacros window as inactivated, because the activated web app
// tab can set the Lacros window as inactivated. And when the web app tabs are
// inactivated, the Lacros window can be set as activated.
ModifyInstance(instance_id1, kWebAppId1, window.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(4));
// Create a web app tab for `kWebAppId2`, and set it as activated.
ModifyInstance(instance_id2, kWebAppId2, window.get(), kActiveInstanceState);
ModifyInstance(instance_id1, kWebAppId1, window.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(3));
// The web app tabs are inactivated, so the Lacros window is set as activated
// in code.
ModifyInstance(instance_id2, kWebAppId2, window.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Set the Lacros window as inactivated.
ModifyInstance(instance_id0, app_constants::kLacrosAppId, window.get(),
kInactiveInstanceState);
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(108));
// The Lacros window is activated for 5 minutes before web app tabs are
// created, and the Lacros window is set as activated for 5 minutes again when
// web app tabs are inactivated.
VerifyAppUsageTimeUkm(app_constants::kLacrosAppId, base::Minutes(10),
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeUkm(url1, base::Minutes(4),
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeUkm(url2, base::Minutes(3),
AppTypeName::kStandaloneBrowser);
}
TEST_P(AppPlatformMetricsServiceTest, UsageTimeUkmForStandaloneChromeApps) {
if (!IsLacrosEnabled()) {
return;
}
// Install Chrome apps (hosted apps) during the running time.
std::string kChromeAppId1 = "bb";
InstallOneApp(kChromeAppId1, AppType::kStandaloneBrowserChromeApp, "BB",
Readiness::kReady, InstallSource::kChromeWebStore,
/*is_platform_app=*/false, WindowMode::kBrowser);
const base::UnguessableToken instance_id0 = base::UnguessableToken::Create();
const base::UnguessableToken instance_id1 = base::UnguessableToken::Create();
const base::UnguessableToken instance_id2 = base::UnguessableToken::Create();
// Create a StandaloneBrowser window, and set it as activated for
// `kLacrosAppId`.
auto window1 = std::make_unique<aura::Window>(nullptr);
window1->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(instance_id0, app_constants::kLacrosAppId, window1.get(),
kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Create a chrome app tab for `kChromeAppId1`, and set it as activated. We
// don't need to set the Lacros window as inactivated, because the activated
// chrome app tab can set the Lacros window as inactivated. And when the
// chrome app tabs are inactivated, the Lacros window can be set as activated.
ModifyInstance(instance_id1, kChromeAppId1, window1.get(),
kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(4));
// The chrome app tab is inactivated, so the Lacros window is set as activated
// in code.
ModifyInstance(instance_id1, kChromeAppId1, window1.get(),
kInactiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
// Set the Lacros window as inactivated.
ModifyInstance(instance_id0, app_constants::kLacrosAppId, window1.get(),
kInactiveInstanceState);
// Create a new window for `kChromeAppId`, and set it as activated.
auto window2 = std::make_unique<aura::Window>(nullptr);
window2->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(instance_id2, kChromeAppId, window2.get(),
kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(20));
// Set the `kChromeAppId` window as inactivated.
ModifyInstance(instance_id2, kChromeAppId, window2.get(),
kInactiveInstanceState);
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(86));
// The Lacros window is activated for 5 minutes before the chrome app tab is
// created, and the Lacros window is set as activated for 5 minutes again when
// the chrome app tab is inactivated.
VerifyAppUsageTimeUkm(app_constants::kLacrosAppId, base::Minutes(10),
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeUkm(kChromeAppId1, base::Minutes(4),
AppTypeName::kStandaloneBrowser);
VerifyAppUsageTimeUkm(kChromeAppId, base::Minutes(20),
AppTypeName::kStandaloneBrowserChromeApp);
}
TEST_P(AppPlatformMetricsServiceTest,
UsageTimeUkmForWebAppWithStandaloneLacrosWindow) {
if (!IsLacrosEnabled()) {
return;
}
const base::UnguessableToken instance_id = base::UnguessableToken::Create();
const GURL url = GURL("https://foo.com");
// Create a StandaloneBrowser web app window, and set it as activated for
// `kWebAppId1`.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
ModifyInstance(instance_id, kWebAppId1, window.get(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
ModifyInstance(instance_id, kWebAppId1, window.get(), kInactiveInstanceState);
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(115));
VerifyAppUsageTimeUkm(url, base::Minutes(5),
AppTypeName::kStandaloneBrowserWebApp);
}
TEST_P(AppPlatformMetricsServiceTest, InstalledAppsUkm) {
for (const auto& [_, pre_installed_app] : pre_installed_apps()) {
if (!pre_installed_app.publisher_id.empty()) {
VerifyInstalledAppsUkm(pre_installed_app, InstallTime::kInit);
}
}
// Install a new ARC app during the running time.
TestApp arc_app{"aa",
AppType::kArc,
"com.google.AA",
Readiness::kReady,
InstallReason::kUser,
InstallSource::kPlayStore};
InstallOneApp(arc_app);
// Verify the ARC app installed during the running time.
VerifyInstalledAppsUkm(arc_app, InstallTime::kRunning);
// Install Chrome apps (hosted apps) during the running time.
TestApp chrome_app1{"bb",
AppType::kStandaloneBrowserChromeApp,
"BB",
Readiness::kReady,
InstallReason::kUser,
InstallSource::kChromeWebStore,
/*should_notify_initialized=*/true,
/*is_platform_app=*/false,
WindowMode::kBrowser};
TestApp chrome_app2{"cc",
AppType::kStandaloneBrowserChromeApp,
"CC",
Readiness::kReady,
InstallReason::kUser,
InstallSource::kChromeWebStore,
/*should_notify_initialized=*/true,
/*is_platform_app=*/false,
WindowMode::kWindow};
InstallOneApp(chrome_app1);
InstallOneApp(chrome_app2);
VerifyInstalledAppsUkm(chrome_app1, InstallTime::kRunning);
VerifyInstalledAppsUkm(chrome_app2, InstallTime::kRunning);
}
TEST_P(AppPlatformMetricsServiceTest, LaunchApps) {
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
// Simulate registering publishers for the launch interface to record metrics.
proxy->RegisterPublishersForTesting();
FakePublisher fake_arc_apps(proxy, AppType::kArc);
FakePublisher fake_borealis_apps(proxy, AppType::kBorealis);
FakePublisher fake_standalone_browser(proxy, AppType::kStandaloneBrowser);
FakePublisher fake_standalone_browser_chrome_app(
proxy, AppType::kStandaloneBrowserChromeApp);
FakePublisher fake_standalone_browser_extension(
proxy, AppType::kStandaloneBrowserExtension);
EXPECT_CALL(fake_borealis_apps,
Launch(/*app_id=*/borealis::kClientAppId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(
/*app_id=*/borealis::kClientAppId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, nullptr);
VerifyAppsLaunchUkm("app://borealis/client", AppTypeName::kBorealis,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kBorealis);
VerifyAppLaunchPerAppTypeV2Histogram(1, AppTypeNameV2::kBorealis);
std::string fake_borealis_app = borealis::FakeAppId("borealistest");
EXPECT_CALL(fake_borealis_apps, Launch(fake_borealis_app, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(fake_borealis_app, ui::EF_NONE,
LaunchSource::kFromChromeInternal, nullptr);
VerifyAppsLaunchUkm("app://borealis/123", AppTypeName::kBorealis,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(2, AppTypeName::kBorealis);
VerifyAppLaunchPerAppTypeV2Histogram(2, AppTypeNameV2::kBorealis);
proxy->Launch(
/*app_id=*/crostini::CrostiniTestHelper::GenerateAppId("test"),
ui::EF_NONE, LaunchSource::kFromChromeInternal, nullptr);
VerifyAppsLaunchUkm("app://test/test", AppTypeName::kCrostini,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kCrostini);
VerifyAppLaunchPerAppTypeV2Histogram(1, AppTypeNameV2::kCrostini);
EXPECT_CALL(fake_arc_apps, Launch(kAndroidAppId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(kAndroidAppId, ui::EF_NONE, LaunchSource::kFromChromeInternal,
nullptr);
VerifyAppsLaunchUkm("app://com.google.A", AppTypeName::kArc,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kArc);
VerifyAppLaunchPerAppTypeV2Histogram(1, AppTypeNameV2::kArc);
EXPECT_CALL(fake_standalone_browser,
Launch(/*app_id=*/app_constants::kLacrosAppId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(
/*app_id=*/app_constants::kLacrosAppId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, nullptr);
VerifyAppsLaunchUkm("app://" + std::string(app_constants::kLacrosAppId),
AppTypeName::kStandaloneBrowser,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kStandaloneBrowser);
VerifyAppLaunchPerAppTypeV2Histogram(1, AppTypeNameV2::kStandaloneBrowser);
EXPECT_CALL(fake_standalone_browser_chrome_app,
Launch(/*app_id=*/kChromeAppId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(
/*app_id=*/kChromeAppId, ui::EF_NONE, LaunchSource::kFromChromeInternal,
nullptr);
VerifyAppsLaunchUkm("app://" + std::string(kChromeAppId),
AppTypeName::kStandaloneBrowserChromeApp,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(1,
AppTypeName::kStandaloneBrowserChromeApp);
VerifyAppLaunchPerAppTypeV2Histogram(
1, AppTypeNameV2::kStandaloneBrowserChromeAppWindow);
// Install Chrome apps (hosted apps) during the running time.
std::string kChromeAppId1 = "bb";
std::string kChromeAppId2 = "cc";
InstallOneApp(kChromeAppId1, AppType::kStandaloneBrowserChromeApp, "BB",
Readiness::kReady, InstallSource::kChromeWebStore,
/*is_platform_app=*/false, WindowMode::kBrowser);
InstallOneApp(kChromeAppId2, AppType::kStandaloneBrowserChromeApp, "CC",
Readiness::kReady, InstallSource::kChromeWebStore,
/*is_platform_app=*/false, WindowMode::kWindow);
// Launch `kChromeAppId1`.
EXPECT_CALL(fake_standalone_browser_chrome_app,
Launch(/*app_id=*/kChromeAppId1, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(
/*app_id=*/kChromeAppId1, ui::EF_NONE, LaunchSource::kFromChromeInternal,
nullptr);
// Verify `kChromeAppId1` launching as kStandaloneBrowser.
VerifyAppsLaunchUkm("app://" + kChromeAppId1, AppTypeName::kStandaloneBrowser,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(2 /*launch kLacrosAppId + kChromeAppId1*/,
AppTypeName::kStandaloneBrowser);
VerifyAppLaunchPerAppTypeV2Histogram(
1, AppTypeNameV2::kStandaloneBrowserChromeAppTab);
// Launch `kChromeAppId2` in a Lacros window tab.
EXPECT_CALL(fake_standalone_browser_chrome_app,
Launch(/*app_id=*/kChromeAppId2, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(
/*app_id=*/kChromeAppId2, ui::EF_NONE, LaunchSource::kFromChromeInternal,
nullptr);
// Verify `kChromeAppId2` launching as kStandaloneBrowserChromeApp.
VerifyAppsLaunchUkm("app://" + kChromeAppId2,
AppTypeName::kStandaloneBrowserChromeApp,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(2 /*Launch kChromeAppId + kChromeAppId2*/,
AppTypeName::kStandaloneBrowserChromeApp);
VerifyAppLaunchPerAppTypeV2Histogram(
2 /*Launch kChromeAppId + kChromeAppId2*/,
AppTypeNameV2::kStandaloneBrowserChromeAppWindow);
EXPECT_CALL(fake_standalone_browser_extension,
Launch(/*app_id=*/kExtensionId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(
/*app_id=*/kExtensionId, ui::EF_NONE, LaunchSource::kFromChromeInternal,
nullptr);
VerifyAppsLaunchUkm("app://" + std::string(kExtensionId),
AppTypeName::kStandaloneBrowserExtension,
LaunchSource::kFromChromeInternal);
VerifyAppLaunchPerAppTypeHistogram(1,
AppTypeName::kStandaloneBrowserExtension);
VerifyAppLaunchPerAppTypeV2Histogram(
1, AppTypeNameV2::kStandaloneBrowserExtension);
proxy->LaunchAppWithUrl(
/*app_id=*/kWebAppId1, ui::EF_NONE, GURL("https://boo.com/a"),
LaunchSource::kFromFileManager, nullptr);
VerifyAppsLaunchUkm("https://foo.com", GetWebAppTypeName(),
LaunchSource::kFromFileManager);
VerifyAppLaunchPerAppTypeHistogram(1, GetWebAppTypeName());
VerifyAppLaunchPerAppTypeV2Histogram(
1, IsLacrosEnabled() ? AppTypeNameV2::kStandaloneBrowserWebAppWindow
: AppTypeNameV2::kWebWindow);
// TODO(crbug.com/40199106): Register non-mojom apps and use
// AppServiceProxy::LaunchAppWithParams to test launching.
proxy->BrowserAppLauncher()->LaunchAppWithParamsForTesting(AppLaunchParams(
kWebAppId2, LaunchContainer::kLaunchContainerTab,
WindowOpenDisposition::NEW_FOREGROUND_TAB, LaunchSource::kFromTest));
if (IsLacrosEnabled()) {
VerifyAppsLaunchUkm("https://foo2.com", AppTypeName::kStandaloneBrowser,
LaunchSource::kFromTest);
VerifyAppLaunchPerAppTypeHistogram(
3 /*Launch kLacrosAppId + kChromeAppId1 + `w2`*/,
AppTypeName::kStandaloneBrowser);
} else {
VerifyAppsLaunchUkm("https://foo2.com", AppTypeName::kChromeBrowser,
LaunchSource::kFromTest);
VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kChromeBrowser);
}
VerifyAppLaunchPerAppTypeV2Histogram(
1, IsLacrosEnabled() ? AppTypeNameV2::kStandaloneBrowserWebAppTab
: AppTypeNameV2::kWebTab);
proxy->BrowserAppLauncher()->LaunchAppWithParamsForTesting(AppLaunchParams(
kSystemWebAppId, LaunchContainer::kLaunchContainerTab,
WindowOpenDisposition::NEW_FOREGROUND_TAB, LaunchSource::kFromTest));
VerifyAppsLaunchUkm("app://" + std::string(kSystemWebAppId),
AppTypeName::kSystemWeb, LaunchSource::kFromTest);
VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kSystemWeb);
VerifyAppLaunchPerAppTypeV2Histogram(1, AppTypeNameV2::kSystemWeb);
}
TEST_P(AppPlatformMetricsServiceTest, UninstallAppUkm) {
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
FakePublisher fake_arc_apps(proxy, AppType::kArc);
FakePublisher fake_standalone_browser_chrome_app(
proxy, AppType::kStandaloneBrowserChromeApp);
FakePublisher fake_standalone_browser_extension(
proxy, AppType::kStandaloneBrowserExtension);
proxy->UninstallSilently(kAndroidAppId, UninstallSource::kAppList);
VerifyAppsUninstallUkm("app://com.google.A", AppTypeName::kArc,
UninstallSource::kAppList);
proxy->UninstallSilently(
/*app_id=*/kChromeAppId, UninstallSource::kAppList);
VerifyAppsUninstallUkm("app://" + std::string(kChromeAppId),
AppTypeName::kStandaloneBrowserChromeApp,
UninstallSource::kAppList);
proxy->UninstallSilently(
/*app_id=*/kExtensionId, UninstallSource::kAppList);
VerifyAppsUninstallUkm("app://" + std::string(kExtensionId),
AppTypeName::kStandaloneBrowserExtension,
UninstallSource::kAppList);
}
TEST_P(AppPlatformMetricsServiceTest,
ShouldClearUsageInfoFromPrefStoreSubsequently) {
// Create a new window for the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
// Set the window active state.
const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
::apps::InstanceState::kActive);
static constexpr base::TimeDelta kExpectedRunningDuration = base::Minutes(5);
task_environment_.FastForwardBy(kExpectedRunningDuration);
// Close app window to stop tracking further usage and verify usage info is
// persisted in the pref store.
ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
::apps::InstanceState::kDestroyed);
const auto& usage_dict_pref = GetPrefService()->GetDict(kAppUsageTime);
ASSERT_THAT(usage_dict_pref.size(), Eq(1UL));
ASSERT_THAT(usage_dict_pref.Find(kInstanceId.ToString()), NotNull());
EXPECT_THAT(*usage_dict_pref.FindDict(kInstanceId.ToString())
->FindString(kUsageTimeAppIdKey),
StrEq(kAndroidAppId));
EXPECT_THAT(
base::ValueToTimeDelta(usage_dict_pref.FindDict(kInstanceId.ToString())
->Find(kUsageTimeDurationKey)),
Eq(kExpectedRunningDuration));
// Fast forward by two hours so it reports usage data and we can verify usage
// info is cleared from the pref store.
task_environment_.FastForwardBy(base::Hours(2));
VerifyAppRunningDuration(kExpectedRunningDuration, AppTypeName::kArc);
ASSERT_TRUE(GetPrefService()->GetDict(kAppUsageTime).empty());
}
TEST_P(AppPlatformMetricsServiceTest,
ShouldClearUsageInfoFromPrefStoreWhenSyncDisabled) {
// Save usage entry with no usage data to the pref store.
{
const base::UnguessableToken& kInstanceId =
base::UnguessableToken::Create();
ScopedDictPrefUpdate usage_dict(GetPrefService(), kAppUsageTime);
AppPlatformMetrics::UsageTime usage_time;
usage_time.app_id = "TestApp";
usage_dict->SetByDottedPath(kInstanceId.ToString(),
usage_time.ConvertToDict());
}
sync_service()->SetAllowedByEnterprisePolicy(false);
// Fast forward by two hours and verify usage info is cleared from the pref
// store.
task_environment_.FastForwardBy(base::Hours(2));
ASSERT_TRUE(GetPrefService()->GetDict(kAppUsageTime).empty());
}
TEST_P(AppPlatformMetricsServiceTest,
ShouldNotClearUsageInfoFromPrefStoreIfReportingUsageSet) {
// Create a new window for the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
// Set the window active state.
const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
::apps::InstanceState::kActive);
static constexpr base::TimeDelta kExpectedRunningDuration = base::Minutes(5);
task_environment_.FastForwardBy(kExpectedRunningDuration);
// Close app window to stop tracking further usage and verify usage info is
// persisted in the pref store.
ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
::apps::InstanceState::kDestroyed);
const auto& usage_dict_pref = GetPrefService()->GetDict(kAppUsageTime);
ASSERT_THAT(usage_dict_pref.size(), Eq(1UL));
ASSERT_THAT(usage_dict_pref.Find(kInstanceId.ToString()), NotNull());
EXPECT_THAT(*usage_dict_pref.FindDict(kInstanceId.ToString())
->FindString(kUsageTimeAppIdKey),
StrEq(kAndroidAppId));
EXPECT_THAT(
base::ValueToTimeDelta(usage_dict_pref.FindDict(kInstanceId.ToString())
->Find(kUsageTimeDurationKey)),
Eq(kExpectedRunningDuration));
// Set reporting usage time for the current app instance and persist it in the
// pref store.
{
ScopedDictPrefUpdate usage_dict(GetPrefService(), kAppUsageTime);
usage_dict->FindDictByDottedPath(kInstanceId.ToString())
->Set(kReportingUsageTimeDurationKey,
base::TimeDeltaToValue(kExpectedRunningDuration));
usage_dict->FindDictByDottedPath(kInstanceId.ToString())
->Set(kUsageTimeAppPublisherIdKey, kAndroidAppPublisherId);
}
// Fast forward by two hours so it reports usage data and we can verify usage
// info is not cleared from the pref store.
task_environment_.FastForwardBy(base::Hours(2));
VerifyAppRunningDuration(kExpectedRunningDuration, AppTypeName::kArc);
const auto& updated_usage_dict_pref =
GetPrefService()->GetDict(kAppUsageTime);
ASSERT_THAT(updated_usage_dict_pref.size(), Eq(1UL));
EXPECT_THAT(updated_usage_dict_pref.Find(kInstanceId.ToString()), NotNull());
EXPECT_THAT(*usage_dict_pref.FindDict(kInstanceId.ToString())
->FindString(kUsageTimeAppIdKey),
StrEq(kAndroidAppId));
EXPECT_THAT(*usage_dict_pref.FindDict(kInstanceId.ToString())
->FindString(kUsageTimeAppPublisherIdKey),
StrEq(kAndroidAppPublisherId));
EXPECT_THAT(
base::ValueToTimeDelta(usage_dict_pref.FindDict(kInstanceId.ToString())
->Find(kUsageTimeDurationKey)),
Eq(base::TimeDelta()));
EXPECT_THAT(
base::ValueToTimeDelta(usage_dict_pref.FindDict(kInstanceId.ToString())
->Find(kReportingUsageTimeDurationKey)),
Eq(kExpectedRunningDuration));
}
TEST_P(AppPlatformMetricsServiceTest, ShouldNotPersistUsageDataIfSyncDisabled) {
sync_service()->SetAllowedByEnterprisePolicy(false);
// Create a new window for the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
// Set the window active state and simulate app usage.
const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
::apps::InstanceState::kActive);
static constexpr base::TimeDelta kExpectedRunningDuration = base::Minutes(5);
task_environment_.FastForwardBy(kExpectedRunningDuration);
// Close app window to stop tracking further usage and verify usage info is
// not persisted in the pref store.
ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
::apps::InstanceState::kDestroyed);
ASSERT_TRUE(GetPrefService()->GetDict(kAppUsageTime).empty());
}
INSTANTIATE_TEST_SUITE_P(All,
AppPlatformMetricsServiceTest,
testing::Bool() /* IsLacrosEnabled */);
// Tests for app platform input metrics.
class AppPlatformInputMetricsTest : public AppPlatformMetricsServiceTest {
public:
void SetUp() override {
PreSetUp();
AppPlatformMetricsServiceTest::SetUp();
widget_ = ash::AshTestBase::CreateTestWidget(
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
}
// This function can be called before the `SetUp` function is called, then
// during the `SetUp` it will do nothing.
void PreSetUp() {
if (!ash_test_helper_) {
ash_test_helper_ = std::make_unique<ash::AshTestHelper>();
ash_test_helper_->SetUp();
}
}
void TearDown() override {
widget_.reset();
ash_test_helper_->TearDown();
AppPlatformMetricsServiceTest::TearDown();
}
AppPlatformInputMetrics* app_platform_input_metrics() {
return app_platform_metrics_service()->app_platform_input_metrics_.get();
}
aura::Window* window() { return widget_->GetNativeWindow(); }
void CreateInputEvent(InputEventSource event_source) {
switch (event_source) {
case InputEventSource::kUnknown:
break;
case InputEventSource::kMouse: {
ui::MouseEvent mouse_event(ui::EventType::kMouseReleased, gfx::Point(),
gfx::Point(), base::TimeTicks(), 0, 0);
ui::Event::DispatcherApi(&mouse_event).set_target(window());
app_platform_input_metrics()->OnMouseEvent(&mouse_event);
break;
}
case InputEventSource::kStylus: {
ui::TouchEvent touch_event(
ui::EventType::kTouchReleased, gfx::Point(), base::TimeTicks(),
ui::PointerDetails(ui::EventPointerType::kPen, 0));
ui::Event::DispatcherApi(&touch_event).set_target(window());
app_platform_input_metrics()->OnTouchEvent(&touch_event);
break;
}
case InputEventSource::kTouch: {
ui::TouchEvent touch_event(
ui::EventType::kTouchReleased, gfx::Point(), base::TimeTicks(),
ui::PointerDetails(ui::EventPointerType::kTouch, 0));
ui::Event::DispatcherApi(&touch_event).set_target(window());
app_platform_input_metrics()->OnTouchEvent(&touch_event);
break;
}
case InputEventSource::kKeyboard: {
ui::KeyEvent key_event(ui::EventType::kKeyReleased, ui::VKEY_MENU,
ui::EF_ALT_DOWN);
ui::Event::DispatcherApi(&key_event).set_target(window());
app_platform_input_metrics()->OnKeyEvent(&key_event);
break;
}
}
}
std::unique_ptr<Browser> CreateBrowser() {
Browser::CreateParams params(profile(), true);
params.type = Browser::TYPE_NORMAL;
browser_window_ = std::make_unique<TestBrowserWindow>();
params.window = browser_window_.get();
browser_window_->SetNativeWindow(window());
params.window = browser_window_.get();
return std::unique_ptr<Browser>(Browser::Create(params));
}
TestApp& ActivatePreInstalledApp(const std::string& app_id) {
EXPECT_TRUE(pre_installed_apps().contains(app_id));
TestApp& pre_installed_app = pre_installed_apps()[app_id];
ModifyInstance(pre_installed_app.app_id, window(), kActive);
return pre_installed_app;
}
void VerifyUkm(const std::string& app_info,
AppTypeName app_type_name,
int event_count,
InputEventSource event_source) {
VerifyUkm(1, app_info, app_type_name, event_count, event_source);
}
void VerifyUkm(const TestApp& app,
int event_count,
InputEventSource event_source) {
VerifyUkm(1, GetSourceUrlForApp(app.app_id).spec(), GetAppTypeName(app),
event_count, event_source);
}
void VerifyUkm(int count,
const std::string& app_info,
AppTypeName app_type_name,
int event_count,
InputEventSource event_source) {
const auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(count, (int)entries.size());
const auto* entry = entries[count - 1].get();
test_ukm_recorder()->ExpectEntrySourceHasUrl(entry, GURL(app_info));
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)app_type_name);
test_ukm_recorder()->ExpectEntryMetric(entry, "AppInputEventCount",
event_count);
test_ukm_recorder()->ExpectEntryMetric(entry, "AppInputEventSource",
(int)event_source);
}
void VerifyNoUkm() {
auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(0U, entries.size());
}
private:
std::unique_ptr<ash::AshTestHelper> ash_test_helper_;
// Where down events are dispatched to.
std::unique_ptr<views::Widget> widget_;
std::unique_ptr<TestBrowserWindow> browser_window_;
};
// Verify no more input event is recorded when the window is destroyed.
TEST_P(AppPlatformInputMetricsTest, WindowIsDestroyed) {
TestApp& pre_installed_app = ActivatePreInstalledApp(kAndroidAppId);
CreateInputEvent(InputEventSource::kMouse);
app_platform_input_metrics()->OnFiveMinutes();
VerifyNoUkm();
app_platform_input_metrics()->OnTwoHours();
VerifyUkm(pre_installed_app, /*event_count=*/1, InputEventSource::kMouse);
ModifyInstance(pre_installed_app.app_id, window(),
apps::InstanceState::kDestroyed);
CreateInputEvent(InputEventSource::kMouse);
app_platform_input_metrics()->OnTwoHours();
// Verify no more input event is recorded.
VerifyUkm(pre_installed_app, /*event_count=*/1, InputEventSource::kMouse);
}
TEST_P(AppPlatformInputMetricsTest, MouseEvent) {
TestApp& pre_installed_app = ActivatePreInstalledApp(kAndroidAppId);
CreateInputEvent(InputEventSource::kMouse);
app_platform_input_metrics()->OnFiveMinutes();
VerifyNoUkm();
app_platform_input_metrics()->OnTwoHours();
VerifyUkm(pre_installed_app, /*event_count=*/1, InputEventSource::kMouse);
}
TEST_P(AppPlatformInputMetricsTest, StylusEvent) {
TestApp& pre_installed_app = ActivatePreInstalledApp(kAndroidAppId);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnFiveMinutes();
VerifyNoUkm();
app_platform_input_metrics()->OnTwoHours();
VerifyUkm(pre_installed_app,
/*event_count=*/1, InputEventSource::kStylus);
}
TEST_P(AppPlatformInputMetricsTest, TouchEvents) {
TestApp& pre_installed_app = ActivatePreInstalledApp(kAndroidAppId);
CreateInputEvent(InputEventSource::kTouch);
CreateInputEvent(InputEventSource::kTouch);
app_platform_input_metrics()->OnFiveMinutes();
VerifyNoUkm();
app_platform_input_metrics()->OnTwoHours();
VerifyUkm(pre_installed_app, /*event_count=*/2, InputEventSource::kTouch);
}
TEST_P(AppPlatformInputMetricsTest, KeyEvents) {
TestApp& pre_installed_app = ActivatePreInstalledApp(kAndroidAppId);
CreateInputEvent(InputEventSource::kKeyboard);
app_platform_input_metrics()->OnFiveMinutes();
VerifyNoUkm();
app_platform_input_metrics()->OnTwoHours();
VerifyUkm(pre_installed_app, /*event_count=*/1, InputEventSource::kKeyboard);
CreateInputEvent(InputEventSource::kKeyboard);
CreateInputEvent(InputEventSource::kKeyboard);
app_platform_input_metrics()->OnFiveMinutes();
// Verify no more input events UKM recorded.
auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(1U, entries.size());
app_platform_input_metrics()->OnTwoHours();
// Verify 2 input metrics events are recorded.
entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(2U, entries.size());
std::set<int> counts;
for (const ukm::mojom::UkmEntry* entry : entries) {
test_ukm_recorder()->ExpectEntrySourceHasUrl(entry,
GURL("app://com.google.A"));
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)AppTypeName::kArc);
test_ukm_recorder()->ExpectEntryMetric(entry, "AppInputEventSource",
(int)InputEventSource::kKeyboard);
counts.insert(
*(test_ukm_recorder()->GetEntryMetric(entry, "AppInputEventCount")));
}
EXPECT_TRUE(base::Contains(counts, 1));
EXPECT_TRUE(base::Contains(counts, 2));
}
TEST_P(AppPlatformInputMetricsTest, MultipleEvents) {
TestApp& pre_installed_app = ActivatePreInstalledApp(kAndroidAppId);
CreateInputEvent(InputEventSource::kMouse);
CreateInputEvent(InputEventSource::kMouse);
CreateInputEvent(InputEventSource::kKeyboard);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnFiveMinutes();
VerifyNoUkm();
app_platform_input_metrics()->OnTwoHours();
// Verify 3 input metrics events are recorded.
const auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(3U, entries.size());
int event_source;
int mouse_event_count = 0;
int keyboard_event_count = 0;
int stylus_event_count = 0;
for (const ukm::mojom::UkmEntry* entry : entries) {
test_ukm_recorder()->ExpectEntrySourceHasUrl(
entry, GetSourceUrlForApp(pre_installed_app.app_id));
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)AppTypeName::kArc);
event_source =
*(test_ukm_recorder()->GetEntryMetric(entry, "AppInputEventSource"));
if (event_source == (int)InputEventSource::kMouse) {
mouse_event_count =
*(test_ukm_recorder()->GetEntryMetric(entry, "AppInputEventCount"));
} else if (event_source == (int)InputEventSource::kKeyboard) {
keyboard_event_count =
*(test_ukm_recorder()->GetEntryMetric(entry, "AppInputEventCount"));
} else if (event_source == (int)InputEventSource::kStylus) {
stylus_event_count =
*(test_ukm_recorder()->GetEntryMetric(entry, "AppInputEventCount"));
}
}
EXPECT_EQ(2, mouse_event_count);
EXPECT_EQ(1, keyboard_event_count);
EXPECT_EQ(1, stylus_event_count);
}
TEST_P(AppPlatformInputMetricsTest, BrowserWindow) {
InstallOneApp(app_constants::kChromeAppId, AppType::kChromeApp, "Chrome",
Readiness::kReady, InstallSource::kSystem);
auto browser = CreateBrowser();
// Set the browser window as activated.
ModifyInstance(app_constants::kChromeAppId, window(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kMouse);
app_platform_input_metrics()->OnFiveMinutes();
VerifyNoUkm();
app_platform_input_metrics()->OnTwoHours();
VerifyUkm(std::string("app://") + app_constants::kChromeAppId,
AppTypeName::kChromeBrowser, /*event_count=*/1,
InputEventSource::kMouse);
// Create a web app tab1.
const GURL url1 = GURL("https://foo.com");
auto web_app_window1 =
CreateWebAppWindow(browser->window()->GetNativeWindow());
// Set the web app tab1 as activated.
ModifyInstance(kWebAppId1, web_app_window1.get(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kMouse);
app_platform_input_metrics()->OnFiveMinutes();
// Verify no more input events UKM recorded.
auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(1U, entries.size());
app_platform_input_metrics()->OnTwoHours();
// Verify 2 input metrics events are recorded.
VerifyUkm(2, url1.spec(), AppTypeName::kChromeBrowser,
/*event_count=*/1, InputEventSource::kMouse);
// Create a web app tab2.
const GURL url2 = GURL("https://foo2.com");
auto web_app_window2 =
CreateWebAppWindow(browser->window()->GetNativeWindow());
// Set the web app tab2 as activated.
ModifyInstance(kWebAppId2, web_app_window2.get(), kActiveInstanceState);
ModifyInstance(kWebAppId1, web_app_window1.get(), kInactiveInstanceState);
CreateInputEvent(InputEventSource::kStylus);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnFiveMinutes();
// Verify no more input events UKM recorded.
entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(2U, entries.size());
app_platform_input_metrics()->OnTwoHours();
// Verify 3 input metrics events are recorded.
VerifyUkm(3, url2.spec(), AppTypeName::kChromeBrowser,
/*event_count=*/2, InputEventSource::kStylus);
// Set the web app tab2 as destroyed, and web app tab1 as activated.
ModifyInstance(kWebAppId2, web_app_window2.get(),
apps::InstanceState::kDestroyed);
ModifyInstance(kWebAppId1, web_app_window1.get(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kKeyboard);
app_platform_input_metrics()->OnFiveMinutes();
// Verify no more input events UKM recorded.
entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(3U, entries.size());
app_platform_input_metrics()->OnTwoHours();
// Verify 4 input metrics events are recorded.
VerifyUkm(4, url1.spec(), AppTypeName::kChromeBrowser,
/*event_count=*/1, InputEventSource::kKeyboard);
// Set the web app tab1 as inactivated.
ModifyInstance(kWebAppId1, web_app_window1.get(), kInactiveInstanceState);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnFiveMinutes();
// Verify no more input events UKM recorded.
entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(4U, entries.size());
app_platform_input_metrics()->OnTwoHours();
// Verify 5 input metrics events are recorded.
VerifyUkm(5, std::string("app://") + app_constants::kChromeAppId,
AppTypeName::kChromeBrowser,
/*event_count=*/1, InputEventSource::kStylus);
}
TEST_P(AppPlatformInputMetricsTest, InputEventsUkmReportAfterReboot) {
ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kActive);
CreateInputEvent(InputEventSource::kKeyboard);
CreateInputEvent(InputEventSource::kStylus);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnFiveMinutes();
VerifyNoUkm();
ModifyInstance(kAndroidAppId, window(), kInactiveInstanceState);
// Reset PlatformMetricsService to simulate the system reboot, and verify
// AppKM is restored from the user pref and reported after 5 minutes after
// reboot.
ResetAppPlatformMetricsService();
VerifyNoUkm();
ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kActive);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnFiveMinutes();
// Verify 2 input metrics events are recorded from pref.
auto entries =
test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(2U, entries.size());
int event_source;
int keyboard_event_count = 0;
int stylus_event_count = 0;
for (const ukm::mojom::UkmEntry* entry : entries) {
test_ukm_recorder()->ExpectEntrySourceHasUrl(entry,
GURL("app://com.google.A"));
test_ukm_recorder()->ExpectEntryMetric(entry, "AppType",
(int)AppTypeName::kArc);
event_source =
*(test_ukm_recorder()->GetEntryMetric(entry, "AppInputEventSource"));
if (event_source == (int)InputEventSource::kKeyboard) {
keyboard_event_count =
*(test_ukm_recorder()->GetEntryMetric(entry, "AppInputEventCount"));
} else if (event_source == (int)InputEventSource::kStylus) {
stylus_event_count =
*(test_ukm_recorder()->GetEntryMetric(entry, "AppInputEventCount"));
}
}
EXPECT_EQ(1, keyboard_event_count);
EXPECT_EQ(2, stylus_event_count);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnFiveMinutes();
// Verify no more input events UKM recorded.
entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(2U, entries.size());
ModifyInstance(kAndroidAppId, window(), kInactiveInstanceState);
// Reset PlatformMetricsService to simulate the system reboot, and verify
// only the new AppKM is reported.
ResetAppPlatformMetricsService();
// Verify no more input events UKM recorded.
entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(2U, entries.size());
app_platform_input_metrics()->OnFiveMinutes();
// Verify the input metrics events are recorded from pref.
VerifyUkm(/*count=*/3, "app://com.google.A", AppTypeName::kArc,
/*event_count=*/2, InputEventSource::kStylus);
// Reset PlatformMetricsService to simulate the system reboot, and verify no
// more AppKM is reported.
ResetAppPlatformMetricsService();
app_platform_input_metrics()->OnFiveMinutes();
// Verify no more input events UKM recorded.
entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
ASSERT_EQ(3U, entries.size());
}
TEST_P(AppPlatformInputMetricsTest, LacrosWindow) {
ModifyInstance(app_constants::kLacrosAppId, window(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnTwoHours();
VerifyUkm("app://" + std::string(app_constants::kLacrosAppId),
AppTypeName::kStandaloneBrowser, /*event_count=*/1,
InputEventSource::kStylus);
}
TEST_P(AppPlatformInputMetricsTest, StandaloneBrowserChromeApp) {
ModifyInstance(kChromeAppId, window(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kKeyboard);
app_platform_input_metrics()->OnTwoHours();
VerifyUkm("app://" + std::string(kChromeAppId),
AppTypeName::kStandaloneBrowserChromeApp, /*event_count=*/1,
InputEventSource::kKeyboard);
}
TEST_P(AppPlatformInputMetricsTest, StandaloneBrowserExtension) {
ModifyInstance(kExtensionId, window(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kMouse);
app_platform_input_metrics()->OnTwoHours();
VerifyUkm("app://" + std::string(kExtensionId),
AppTypeName::kStandaloneBrowserExtension, /*event_count=*/1,
InputEventSource::kMouse);
}
TEST_P(AppPlatformInputMetricsTest, LacrosWindowAndWebAppAndChromeApp) {
if (!IsLacrosEnabled()) {
return;
}
window()->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::LACROS);
const base::UnguessableToken instance_id0 = base::UnguessableToken::Create();
const base::UnguessableToken instance_id1 = base::UnguessableToken::Create();
const base::UnguessableToken instance_id2 = base::UnguessableToken::Create();
// Set window as activated for `kLacrosAppId`.
ModifyInstance(instance_id0, app_constants::kLacrosAppId, window(),
kActiveInstanceState);
CreateInputEvent(InputEventSource::kMouse);
app_platform_input_metrics()->OnTwoHours();
// Verify 1 input metrics event for kMouse is recorded.
VerifyUkm("app://" + std::string(app_constants::kLacrosAppId),
AppTypeName::kStandaloneBrowser, /*event_count=*/1,
InputEventSource::kMouse);
// Set the web app tab1 as activated. We don't need to set the Lacros window
// as inactivated, because the activated web app tab can set the Lacros window
// as inactivated. And when the web app tabs are inactivated, the Lacros
// window can be set as activated.
const GURL url1 = GURL("https://foo.com");
task_environment_.FastForwardBy(base::Minutes(4));
ModifyInstance(instance_id1, kWebAppId1, window(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kMouse);
app_platform_input_metrics()->OnTwoHours();
// Verify 2 input metrics events are recorded.
VerifyUkm(2, url1.spec(), AppTypeName::kStandaloneBrowser,
/*event_count=*/1, InputEventSource::kMouse);
// Install a Chrome app (hosted app) during the running time.
std::string kChromeAppId1 = "bb";
InstallOneApp(kChromeAppId1, AppType::kStandaloneBrowserChromeApp, "BB",
Readiness::kReady, InstallSource::kChromeWebStore,
/*is_platform_app=*/false, WindowMode::kBrowser);
// Set the Chrome app tab as activated.
ModifyInstance(instance_id2, kChromeAppId1, window(), kActiveInstanceState);
ModifyInstance(instance_id1, kWebAppId1, window(), kInactiveInstanceState);
CreateInputEvent(InputEventSource::kStylus);
app_platform_input_metrics()->OnTwoHours();
// Verify 3 input metrics events are recorded.
VerifyUkm(3, "app://" + kChromeAppId1, AppTypeName::kStandaloneBrowser,
/*event_count=*/1, InputEventSource::kStylus);
// Set the Chrome app tab as inactivated, then the Lacros window should be set
// as activated in code.
ModifyInstance(instance_id2, kChromeAppId1, window(), kInactiveInstanceState);
CreateInputEvent(InputEventSource::kKeyboard);
app_platform_input_metrics()->OnTwoHours();
// Verify 4 input metrics events are recorded.
VerifyUkm(4, "app://" + std::string(app_constants::kLacrosAppId),
AppTypeName::kStandaloneBrowser,
/*event_count=*/1, InputEventSource::kKeyboard);
}
INSTANTIATE_TEST_SUITE_P(All,
AppPlatformInputMetricsTest,
testing::Bool() /* IsLacrosEnabled */);
// Tests for app platform metrics observers.
class AppPlatformMetricsObserverTest : public AppPlatformMetricsServiceTest {
protected:
void SetUp() override {
AppPlatformMetricsServiceTest::SetUp();
// We transfer ownership of the unique_ptr for the app platform metrics
// service in some test scenarios. Therefore, we save a copy of the raw
// pointer so it can be used during teardown.
app_platform_metrics_service_ = app_platform_metrics_service();
app_platform_metrics_service_->AppPlatformMetrics()->AddObserver(
&observer_);
}
void TearDown() override {
// App platform metrics component has not been destructed yet, so we
// unregister the observer to reduce noise (observer is destructed before
// the component is).
app_platform_metrics_service_->AppPlatformMetrics()->RemoveObserver(
&observer_);
AppPlatformMetricsServiceTest::TearDown();
}
MockAppPlatformMetricsObserver observer_;
raw_ptr<AppPlatformMetricsService, DanglingUntriaged>
app_platform_metrics_service_;
};
TEST_P(AppPlatformMetricsObserverTest, ShouldNotifyObserverOnAppInstalled) {
// Observers should be notified even when app sync is disabled.
sync_service()->SetAllowedByEnterprisePolicy(false);
const std::string app_id(borealis::FakeAppId("borealis-fake"));
EXPECT_CALL(
observer_,
OnAppInstalled(app_id, AppType::kBorealis, InstallSource::kUnknown,
InstallReason::kUser, InstallTime::kRunning))
.Times(1);
InstallOneApp(app_id, AppType::kBorealis,
/*publisher_id=*/"", Readiness::kReady, InstallSource::kUnknown,
/*is_platform_app=*/false, WindowMode::kBrowser);
}
TEST_P(AppPlatformMetricsObserverTest, ShouldNotifyObserverOnAppLaunch) {
// Observers should be notified even when app sync is disabled.
sync_service()->SetAllowedByEnterprisePolicy(false);
// Launch a pre-installed app and verify the observer is notified.
EXPECT_CALL(observer_, OnAppLaunched(kAndroidAppId, AppType::kArc,
apps::LaunchSource::kFromChromeInternal))
.Times(1);
auto* const proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
FakePublisher fake_arc_apps(proxy, AppType::kArc);
proxy->Launch(kAndroidAppId, ui::EF_NONE,
apps::LaunchSource::kFromChromeInternal, nullptr);
task_environment_.RunUntilIdle();
}
TEST_P(AppPlatformMetricsObserverTest, ShouldNotifyObserverOnAppUninstall) {
// Observers should be notified even when app sync is disabled.
sync_service()->SetAllowedByEnterprisePolicy(false);
// Uninstall a pre-installed app and verify the observer is notified.
EXPECT_CALL(observer_, OnAppUninstalled(kAndroidAppId, AppType::kArc,
UninstallSource::kAppList))
.Times(1);
auto* const proxy = AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
FakePublisher fake_arc_apps(proxy, AppType::kArc);
proxy->UninstallSilently(kAndroidAppId, UninstallSource::kAppList);
task_environment_.RunUntilIdle();
}
TEST_P(AppPlatformMetricsObserverTest, ShouldNotifyObserverOnAppUsage) {
// Observers should be notified even when app sync is disabled.
sync_service()->SetAllowedByEnterprisePolicy(false);
// Create a new window for the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
// Set the window active state and verify the observer is notified
// with the appropriate running time with every notification.
const base::UnguessableToken& instance_id = base::UnguessableToken::Create();
ModifyInstance(instance_id, kAndroidAppId, window.get(),
kActiveInstanceState);
// Usage metrics are recorded every 5 minutes and on window inactivation, so
// we can expect two notifications with relevant usage times (5 minutes + 3
// minutes) across a 8 minute usage window.
Sequence s;
EXPECT_CALL(observer_, OnAppUsage(kAndroidAppId, AppType::kArc, instance_id,
base::Minutes(5)))
.Times(1)
.InSequence(s);
EXPECT_CALL(observer_, OnAppUsage(kAndroidAppId, AppType::kArc, instance_id,
base::Minutes(3)))
.Times(1)
.InSequence(s);
// Fast forward to trigger first notification.
task_environment_.FastForwardBy(base::Minutes(8));
// Set app inactive. This should also trigger the second notification with
// usage time delta after the first one.
ModifyInstance(instance_id, kAndroidAppId, window.get(),
kInactiveInstanceState);
}
TEST_P(AppPlatformMetricsObserverTest, ShouldNotNotifyUnregisteredObservers) {
auto* const proxy = AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
proxy->AppPlatformMetrics()->RemoveObserver(&observer_);
// Uninstall a pre-installed app and verify the unregistered observer
// is not notified.
EXPECT_CALL(observer_, OnAppUninstalled(kAndroidAppId, AppType::kArc,
UninstallSource::kAppList))
.Times(0);
proxy->UninstallSilently(kAndroidAppId, UninstallSource::kAppList);
task_environment_.RunUntilIdle();
}
TEST_P(AppPlatformMetricsObserverTest, ShouldNotifyObserverOnDestruction) {
// Create a new instance of `AppPlatformMetricsService` here so we can
// test destruction lifecycle without affecting pre-existing test teardown
// fixtures.
auto app_platform_metrics_service =
std::make_unique<AppPlatformMetricsService>(profile());
app_platform_metrics_service->Start(
apps::AppServiceProxyFactory::GetForProfile(profile())
->AppRegistryCache(),
apps::AppServiceProxyFactory::GetForProfile(profile())
->InstanceRegistry(),
apps::AppServiceProxyFactory::GetForProfile(profile())
->AppCapabilityAccessCache());
app_platform_metrics_service->AppPlatformMetrics()->AddObserver(&observer_);
EXPECT_CALL(observer_, OnAppPlatformMetricsDestroyed()).Times(1);
app_platform_metrics_service.reset();
task_environment_.RunUntilIdle();
}
INSTANTIATE_TEST_SUITE_P(All,
AppPlatformMetricsObserverTest,
testing::Bool() /* IsLacrosEnabled */);
// Tests for app discovery metrics test.
class AppDiscoveryMetricsTest : public AppPlatformMetricsServiceTest {
public:
void SetUp() override {
test_recorder_ = std::make_unique<TestRecorder>();
test_structured_metrics_provider_ =
std::make_unique<metrics::structured::TestStructuredMetricsProvider>();
test_structured_metrics_provider_->EnableRecording();
metrics::structured::Recorder::GetInstance()->SetUiTaskRunner(
task_environment_.GetMainThreadTaskRunner());
std::vector<base::test::FeatureRef> enabled;
std::vector<base::test::FeatureRef> disabled;
if (IsLacrosEnabled()) {
base::Extend(enabled, ash::standalone_browser::GetFeatureRefs());
scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
ash::switches::kEnableLacrosForTesting);
} else {
base::Extend(disabled, ash::standalone_browser::GetFeatureRefs());
}
feature_list_.InitWithFeatures(enabled, disabled);
AppPlatformMetricsServiceTestBase::SetUp();
ASSERT_EQ(IsLacrosEnabled(), crosapi::browser_util::IsLacrosEnabled());
}
metrics::structured::TestStructuredMetricsProvider*
test_structured_metrics_provider() {
return test_structured_metrics_provider_.get();
}
void ValidateAppInstallEvent(const metrics::structured::Event& event,
const std::string& app_url,
AppType app_type,
InstallSource install_source,
InstallReason install_reason) {
cros_events::AppDiscovery_AppInstalled expected_event;
EXPECT_EQ(expected_event.project_name(), event.project_name());
EXPECT_EQ(expected_event.event_name(), event.event_name());
expected_event.SetAppId(app_url)
.SetAppType(static_cast<int>(app_type))
.SetInstallSource(static_cast<int>(install_source))
.SetInstallReason(static_cast<int>(install_reason));
EXPECT_EQ(expected_event.metric_values(), event.metric_values());
}
void ValidateAppUninstallEvent(const metrics::structured::Event& event,
const std::string& app_url,
AppType app_type,
UninstallSource uninstall_source) {
cros_events::AppDiscovery_AppUninstall expected_event;
EXPECT_EQ(expected_event.project_name(), event.project_name());
EXPECT_EQ(expected_event.event_name(), event.event_name());
expected_event.SetAppId(app_url)
.SetAppType(static_cast<int>(app_type))
.SetUninstallSource(static_cast<int>(uninstall_source));
EXPECT_EQ(expected_event.metric_values(), event.metric_values());
}
void ValidateAppLaunchEvent(const metrics::structured::Event& event,
const std::string& app_id,
AppType app_type,
LaunchSource launch_source) {
cros_events::AppDiscovery_AppLaunched expected_event;
EXPECT_EQ(expected_event.project_name(), event.project_name());
EXPECT_EQ(expected_event.event_name(), event.event_name());
expected_event.SetAppId(app_id)
.SetAppType(static_cast<int>(app_type))
.SetLaunchSource(static_cast<int>(launch_source));
EXPECT_EQ(expected_event.metric_values(), event.metric_values());
}
void ValidateAppStateEvent(const metrics::structured::Event& event,
const std::string& app_id,
AppStateChange app_state) {
cros_events::AppDiscovery_AppStateChanged expected_event;
EXPECT_EQ(expected_event.project_name(), event.project_name());
EXPECT_EQ(expected_event.event_name(), event.event_name());
expected_event.SetAppId(app_id).SetAppState(static_cast<int>(app_state));
EXPECT_EQ(expected_event.metric_values(), event.metric_values());
}
private:
std::unique_ptr<TestRecorder> test_recorder_;
std::unique_ptr<metrics::structured::TestStructuredMetricsProvider>
test_structured_metrics_provider_;
};
TEST_P(AppDiscoveryMetricsTest, AppInstallStateMetricsRecorded) {
base::test::ScopedRunLoopTimeout default_timeout(FROM_HERE, base::Seconds(3));
// Setup publisher for arc app.
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
FakePublisher fake_arc_apps(proxy, AppType::kArc);
auto app_type = AppType::kArc;
const std::string app_id = "aa";
const std::string publisher_id = "test.publisher.package";
const std::string expected_app_id = base::StrCat({"app://", publisher_id});
auto install_source = InstallSource::kPlayStore;
// Wait for events to be recorded.
base::RunLoop install_event_run_loop;
auto install_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppInstallEvent(event, expected_app_id, app_type,
install_source, InstallReason::kUser);
install_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
install_record_callback);
InstallOneApp(app_id, app_type, publisher_id, Readiness::kReady,
install_source);
install_event_run_loop.Run();
// Uninstall the app.
base::RunLoop uninstall_event_run_loop;
const auto kUninstallSource = UninstallSource::kAppList;
auto uninstall_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppUninstallEvent(event, expected_app_id, app_type,
kUninstallSource);
uninstall_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
uninstall_record_callback);
proxy->UninstallSilently(app_id, kUninstallSource);
uninstall_event_run_loop.Run();
}
TEST_P(AppDiscoveryMetricsTest, AppInstallNotEmittedIfAppInstalled) {
base::test::ScopedRunLoopTimeout default_timeout(FROM_HERE, base::Seconds(3));
// Setup publisher for arc app.
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
// Install an ARC app to test.
AddApp(proxy,
{kAndroidAppId, AppType::kArc, kAndroidAppPublisherId,
Readiness::kReady, InstallReason::kUser, InstallSource::kPlayStore,
/*should_notify_initialized=*/true});
// Simulate registering publishers for the launch interface to record metrics.
proxy->RegisterPublishersForTesting();
FakePublisher fake_arc_apps(proxy, AppType::kArc);
// Try to install the same app.
auto app_type = AppType::kArc;
const std::string expected_app_id =
base::StrCat({"app://", kAndroidAppPublisherId});
auto install_source = InstallSource::kPlayStore;
// Install event should not be recorded.
auto install_record_callback =
base::BindLambdaForTesting([](const metrics::structured::Event& event) {
ADD_FAILURE() << "Should not be called!";
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
install_record_callback);
InstallOneApp(kAndroidAppId, app_type, kAndroidAppPublisherId,
Readiness::kReady, install_source);
}
TEST_P(AppDiscoveryMetricsTest, AppUninstallNotEmittedIfAppNotInstalled) {
base::test::ScopedRunLoopTimeout default_timeout(FROM_HERE, base::Seconds(3));
// Setup publisher for arc app.
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
// Simulate registering publishers for the launch interface to record metrics.
proxy->RegisterPublishersForTesting();
FakePublisher fake_arc_apps(proxy, AppType::kArc);
// Try to uninstall an app that is not installed.
base::RunLoop uninstall_event_run_loop;
const auto kUninstallSource = UninstallSource::kAppList;
auto uninstall_record_callback =
base::BindLambdaForTesting([](const metrics::structured::Event& event) {
ADD_FAILURE() << "Should not be called!";
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
uninstall_record_callback);
proxy->UninstallSilently(kAndroidAppId, kUninstallSource);
}
TEST_P(AppDiscoveryMetricsTest, AppActivityMetricsRecorded) {
base::test::ScopedRunLoopTimeout default_timeout(FROM_HERE, base::Seconds(3));
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
const std::string expected_app_id =
base::StrCat({"app://", kAndroidAppPublisherId});
// Install an ARC app to test.
AddApp(proxy,
{kAndroidAppId, AppType::kArc, kAndroidAppPublisherId,
Readiness::kReady, InstallReason::kUser, InstallSource::kPlayStore,
/*should_notify_initialized=*/true});
// Simulate registering publishers for the launch interface to record metrics.
proxy->RegisterPublishersForTesting();
FakePublisher fake_arc_apps(proxy, AppType::kArc);
// Create a window to simulate launching the app.
auto window = std::make_unique<aura::Window>(nullptr);
window->Init(ui::LAYER_NOT_DRAWN);
// Validate event recorded after event is recorded.
base::RunLoop launch_event_run_loop;
auto launch_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppLaunchEvent(event, expected_app_id, AppType::kArc,
LaunchSource::kFromChromeInternal);
launch_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
launch_record_callback);
EXPECT_CALL(fake_arc_apps, Launch(kAndroidAppId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(kAndroidAppId, ui::EF_NONE, LaunchSource::kFromChromeInternal,
nullptr);
ModifyInstance(kAndroidAppId, window.get(), apps::InstanceState::kStarted);
launch_event_run_loop.Run();
// Mark app as kRunning otherwise active event will not trigger since the app
// isn't considered to be running yet.
ModifyInstance(
kAndroidAppId, window.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kStarted |
apps::InstanceState::kRunning));
// Validate launch -> active event is recorded.
base::RunLoop active_event_run_loop;
auto active_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppStateEvent(event, expected_app_id, AppStateChange::kActive);
active_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
active_record_callback);
ModifyInstance(
kAndroidAppId, window.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kActive |
apps::InstanceState::kRunning));
active_event_run_loop.Run();
// Validate active -> inactive event is recorded.
base::RunLoop hidden_event_run_loop;
auto hidden_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppStateEvent(event, expected_app_id,
AppStateChange::kInactive);
hidden_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
hidden_record_callback);
ModifyInstance(
kAndroidAppId, window.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kHidden |
apps::InstanceState::kRunning));
hidden_event_run_loop.Run();
// Validate inactive -> active is recorded.
base::RunLoop active_event_run_loop2;
auto active_record_callback2 = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppStateEvent(event, expected_app_id, AppStateChange::kActive);
active_event_run_loop2.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
active_record_callback2);
ModifyInstance(
kAndroidAppId, window.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kActive |
apps::InstanceState::kRunning));
active_event_run_loop2.Run();
// Validate closed event is recorded.
base::RunLoop closed_event_run_loop;
auto closed_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppStateEvent(event, expected_app_id, AppStateChange::kClosed);
closed_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
closed_record_callback);
ModifyInstance(kAndroidAppId, window.get(), apps::InstanceState::kDestroyed);
closed_event_run_loop.Run();
}
TEST_P(AppDiscoveryMetricsTest, AppActivityMetricsRecordedForTwoInstances) {
base::test::ScopedRunLoopTimeout default_timeout(FROM_HERE, base::Seconds(3));
auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
const std::string expected_app_id =
base::StrCat({"app://", kAndroidAppPublisherId});
// Install an ARC app to test.
AddApp(proxy,
{kAndroidAppId, AppType::kArc, kAndroidAppPublisherId,
Readiness::kReady, InstallReason::kUser, InstallSource::kPlayStore,
/*should_notify_initialized=*/true});
// Simulate registering publishers for the launch interface to record metrics.
proxy->RegisterPublishersForTesting();
FakePublisher fake_arc_apps(proxy, AppType::kArc);
// Create a window to simulate launching the app.
auto window1 = std::make_unique<aura::Window>(nullptr);
auto window2 = std::make_unique<aura::Window>(nullptr);
window1->Init(ui::LAYER_NOT_DRAWN);
window2->Init(ui::LAYER_NOT_DRAWN);
// Validate event recorded after event is recorded.
base::RunLoop launch_event_run_loop;
auto launch_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppLaunchEvent(event, expected_app_id, AppType::kArc,
LaunchSource::kFromChromeInternal);
launch_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
launch_record_callback);
EXPECT_CALL(fake_arc_apps, Launch(kAndroidAppId, ui::EF_NONE,
LaunchSource::kFromChromeInternal, _))
.Times(1);
proxy->Launch(kAndroidAppId, ui::EF_NONE, LaunchSource::kFromChromeInternal,
nullptr);
ModifyInstance(kAndroidAppId, window1.get(), apps::InstanceState::kStarted);
ModifyInstance(kAndroidAppId, window2.get(), apps::InstanceState::kStarted);
launch_event_run_loop.Run();
// Mark app as kRunning otherwise active event will not trigger since the app
// isn't considered to be running yet.
ModifyInstance(
kAndroidAppId, window1.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kStarted |
apps::InstanceState::kRunning));
ModifyInstance(
kAndroidAppId, window2.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kStarted |
apps::InstanceState::kRunning));
// Validate launch -> active event is recorded.
base::RunLoop active_event_run_loop;
auto active_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppStateEvent(event, expected_app_id, AppStateChange::kActive);
active_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
active_record_callback);
ModifyInstance(
kAndroidAppId, window1.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kActive |
apps::InstanceState::kRunning));
active_event_run_loop.Run();
// Active event should not be recorded when 2nd instance becomes active.
auto active_record_callback2 =
base::BindLambdaForTesting([](const metrics::structured::Event& event) {
ADD_FAILURE() << "Should not be called!";
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
active_record_callback2);
ModifyInstance(
kAndroidAppId, window2.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kActive |
apps::InstanceState::kRunning));
// Inactive event is not recorded if one instance becomes inactive but other
// instance is active.
auto hidden_record_callback =
base::BindLambdaForTesting([](const metrics::structured::Event& event) {
ADD_FAILURE() << "Should not be called!";
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
hidden_record_callback);
ModifyInstance(
kAndroidAppId, window1.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kHidden |
apps::InstanceState::kRunning));
// Validate inactive event recorded if both instances are inactive.
base::RunLoop inactive_event_run_loop;
auto inactive_record_callback = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppStateEvent(event, expected_app_id,
AppStateChange::kInactive);
inactive_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
inactive_record_callback);
ModifyInstance(
kAndroidAppId, window2.get(),
static_cast<apps::InstanceState>(apps::InstanceState::kVisible |
apps::InstanceState::kRunning));
inactive_event_run_loop.Run();
// Validate closed event is not recorded when one instance is closed.
auto closed_record_callback =
base::BindLambdaForTesting([](const metrics::structured::Event& event) {
ADD_FAILURE() << "Should not be called!";
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
closed_record_callback);
ModifyInstance(kAndroidAppId, window1.get(), apps::InstanceState::kDestroyed);
// Validate closed event is recorded when both instances are closed.
base::RunLoop closed_event_run_loop;
auto closed_record_callback2 = base::BindLambdaForTesting(
[&, this](const metrics::structured::Event& event) {
ValidateAppStateEvent(event, expected_app_id, AppStateChange::kClosed);
closed_event_run_loop.Quit();
});
test_structured_metrics_provider()->SetOnEventsRecordClosure(
closed_record_callback2);
ModifyInstance(kAndroidAppId, window2.get(), apps::InstanceState::kDestroyed);
closed_event_run_loop.Run();
}
INSTANTIATE_TEST_SUITE_P(All,
AppDiscoveryMetricsTest,
testing::Bool() /* IsLacrosEnabled */);
class AppPlatformMetricsServiceObserverTest
: public AppPlatformMetricsServiceTestBase,
public ::testing::WithParamInterface<bool> {
protected:
void SetUp() override {
if (IsLacrosEnabled()) {
feature_list_.InitWithFeatures(
/*enabled_features=*/ash::standalone_browser::GetFeatureRefs(), {});
scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
ash::switches::kEnableLacrosForTesting);
} else {
feature_list_.InitWithFeatures(
{}, /*disabled_features=*/ash::standalone_browser::GetFeatureRefs());
}
// Set up test user.
AddRegularUser("[email protected]");
ASSERT_EQ(IsLacrosEnabled(), crosapi::browser_util::IsLacrosEnabled());
}
bool IsLacrosEnabled() const { return GetParam(); }
MockObserver* observer() { return &observer_; }
private:
base::test::ScopedFeatureList feature_list_;
base::test::ScopedCommandLine scoped_command_line_;
// Mock observer for the `AppPlatformMetricsService` component. Needs to
// outlive the lifetime of the component for testing purposes.
MockObserver observer_;
};
TEST_P(AppPlatformMetricsServiceObserverTest,
NotifyObserversOnAppPlatformMetricsInit) {
MockObserver* const observer_ptr = observer();
AppPlatformMetricsService app_platform_metrics_service(profile());
app_platform_metrics_service.AddObserver(observer_ptr);
EXPECT_CALL(*observer_ptr, OnAppPlatformMetricsInit(_))
.WillOnce([&](AppPlatformMetrics* app_platform_metrics) {
EXPECT_THAT(app_platform_metrics,
Eq(app_platform_metrics_service.AppPlatformMetrics()));
});
app_platform_metrics_service.Start(
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache(),
AppServiceProxyFactory::GetForProfile(profile())->InstanceRegistry(),
AppServiceProxyFactory::GetForProfile(profile())
->AppCapabilityAccessCache());
}
TEST_P(AppPlatformMetricsServiceObserverTest,
ShouldNotNotifyObserversOnAppPlatformMetricsInitIfUnregistered) {
MockObserver* const observer_ptr = observer();
AppPlatformMetricsService app_platform_metrics_service(profile());
// Unregister registered observer before init and verify observer is not
// notified.
app_platform_metrics_service.AddObserver(observer_ptr);
app_platform_metrics_service.RemoveObserver(observer_ptr);
EXPECT_CALL(*observer_ptr, OnAppPlatformMetricsInit(_)).Times(0);
app_platform_metrics_service.Start(
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache(),
AppServiceProxyFactory::GetForProfile(profile())->InstanceRegistry(),
AppServiceProxyFactory::GetForProfile(profile())
->AppCapabilityAccessCache());
}
TEST_P(AppPlatformMetricsServiceObserverTest,
ShouldNotifyObserverOnDestruction) {
MockObserver* const observer_ptr = observer();
auto app_platform_metrics_service =
std::make_unique<AppPlatformMetricsService>(profile());
app_platform_metrics_service->AddObserver(observer_ptr);
EXPECT_CALL(*observer_ptr, OnAppPlatformMetricsServiceWillBeDestroyed)
.Times(1);
app_platform_metrics_service.reset();
}
TEST_P(AppPlatformMetricsServiceObserverTest,
NotifyObserversOnWebsiteMetricsInit) {
MockObserver* const observer_ptr = observer();
AppPlatformMetricsService app_platform_metrics_service(profile());
app_platform_metrics_service.AddObserver(observer_ptr);
EXPECT_CALL(*observer_ptr, OnWebsiteMetricsInit)
.WillOnce([&](WebsiteMetrics* website_metrics) {
EXPECT_THAT(website_metrics,
Eq(app_platform_metrics_service.WebsiteMetrics()));
});
app_platform_metrics_service.Start(
AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache(),
AppServiceProxyFactory::GetForProfile(profile())->InstanceRegistry(),
AppServiceProxyFactory::GetForProfile(profile())
->AppCapabilityAccessCache());
}
INSTANTIATE_TEST_SUITE_P(All,
AppPlatformMetricsServiceObserverTest,
::testing::Bool() /* IsLacrosEnabled */);
class ManagedGuestSessionBaseTest {
public:
explicit ManagedGuestSessionBaseTest(bool initialize_login_state = true)
: managed_guest_session_(initialize_login_state) {}
void SetUp(AppPlatformMetricsServiceTest& app_platform_metrics_test) {
app_platform_metrics_test.set_user_segment(
UserTypeByDeviceTypeMetricsProvider::UserSegment::kManagedGuestSession);
// Sync is disabled for MGS, but AppKM should still be enabled.
app_platform_metrics_test.sync_service()->SetAllowedByEnterprisePolicy(
false);
}
void SimulateMgsShutdown(
AppPlatformMetricsServiceTest& app_platform_metrics_test) {
ukm::UkmRecorder::Get()->NotifyStartShutdown();
// User preferences are deleted on sign out in MGS.
app_platform_metrics_test.GetPrefService()->ClearPref(kAppUsageTime);
app_platform_metrics_test.GetPrefService()->ClearPref(kAppInputEventsKey);
app_platform_metrics_test.ResetAppPlatformMetricsService();
}
private:
chromeos::FakeManagedGuestSession managed_guest_session_;
};
class ManagedGuestSessionAppMetricsTest : public AppPlatformMetricsServiceTest {
public:
void SetUp() override {
managed_guest_session_ = std::make_unique<ManagedGuestSessionBaseTest>();
AppPlatformMetricsServiceTest::SetUp();
managed_guest_session_->SetUp(*this);
}
void TearDown() override {
managed_guest_session_.reset();
AppPlatformMetricsServiceTest::TearDown();
}
protected:
std::unique_ptr<ManagedGuestSessionBaseTest> managed_guest_session_;
};
TEST_P(ManagedGuestSessionAppMetricsTest, ReportsUsageTimeUkmAfter2Hours) {
std::unique_ptr<Browser> browser =
CreateBrowserWindow(InstallReason::kSystem);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
VerifyNoAppUsageTimeUkm();
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(115));
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(5),
AppTypeName::kChromeBrowser);
}
TEST_P(ManagedGuestSessionAppMetricsTest, UsageTimeUkmReportedOnShutdown) {
std::unique_ptr<Browser> browser =
CreateBrowserWindow(InstallReason::kSystem);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Minutes(5));
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
VerifyNoAppUsageTimeUkm();
managed_guest_session_->SimulateMgsShutdown(*this);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(5),
AppTypeName::kChromeBrowser);
}
TEST_P(ManagedGuestSessionAppMetricsTest, UsageTimeUkmReportedInShortSessions) {
std::unique_ptr<Browser> browser =
CreateBrowserWindow(InstallReason::kSystem);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
// "Usage metrics are updated every 5 minutes or during shutdown. Sleep only
// 3 minutes and simulate on shutdown update."
task_environment_.FastForwardBy(base::Minutes(3));
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kInactiveInstanceState);
VerifyNoAppUsageTimeUkm();
managed_guest_session_->SimulateMgsShutdown(*this);
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(3),
AppTypeName::kChromeBrowser);
}
TEST_P(ManagedGuestSessionAppMetricsTest,
DoNotReportUsageTimeUkmForBlockedInstalledReasons) {
for (InstallReason reason : kBlockedInstallReasonsInManagedGuestSession) {
std::unique_ptr<Browser> browser = CreateBrowserWindow(reason);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Hours(2));
VerifyNoAppUsageTimeUkm();
}
}
TEST_P(ManagedGuestSessionAppMetricsTest,
ReportsUsageTimeUkmForPolicyInstalledReasons) {
std::unique_ptr<Browser> browser =
CreateBrowserWindow(InstallReason::kPolicy);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Hours(2));
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(115),
AppTypeName::kChromeBrowser);
}
TEST_P(ManagedGuestSessionAppMetricsTest,
ReportsUsageTimeUkmForOemInstalledReasons) {
std::unique_ptr<Browser> browser = CreateBrowserWindow(InstallReason::kOem);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Hours(2));
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(115),
AppTypeName::kChromeBrowser);
}
TEST_P(ManagedGuestSessionAppMetricsTest,
ReportsUsageTimeUkmForDefaultInstalledReasons) {
std::unique_ptr<Browser> browser =
CreateBrowserWindow(InstallReason::kDefault);
ModifyInstance(app_constants::kChromeAppId,
browser->window()->GetNativeWindow(), kActiveInstanceState);
task_environment_.FastForwardBy(base::Hours(2));
VerifyAppUsageTimeUkm(app_constants::kChromeAppId, base::Minutes(115),
AppTypeName::kChromeBrowser);
}
TEST_P(ManagedGuestSessionAppMetricsTest,
InstalledAppsUkmReportedOnlyForAllowedInstallReasons) {
for (const auto& [_, pre_installed_app] : pre_installed_apps()) {
if (pre_installed_app.publisher_id.empty()) {
continue;
}
if (kAllowedInstallReasonsInManagedGuestSession.contains(
pre_installed_app.install_reason)) {
VerifyInstalledAppsUkm(pre_installed_app, InstallTime::kInit);
} else if (kBlockedInstallReasonsInManagedGuestSession.contains(
pre_installed_app.install_reason)) {
VerifyNoInstalledAppUkm(pre_installed_app);
} else {
// All install reasons should be covered by either
// `kAllowedInstallReasonsInMgs` or `kBlockedInstallReasonsInMgs`.
NOTREACHED_IN_MIGRATION();
}
}
}
INSTANTIATE_TEST_SUITE_P(All,
ManagedGuestSessionAppMetricsTest,
::testing::Bool() /* IsLacrosEnabled */);
class ManagedGuestSessionInputMetricsTest : public AppPlatformInputMetricsTest {
public:
void SetUp() override {
AppPlatformInputMetricsTest::PreSetUp();
managed_guest_session_ = std::make_unique<ManagedGuestSessionBaseTest>(
/*initialize_login_state=*/false);
AppPlatformInputMetricsTest::SetUp();
managed_guest_session_->SetUp(*this);
}
void TearDown() override {
managed_guest_session_.reset();
AppPlatformInputMetricsTest::TearDown();
}
protected:
std::unique_ptr<ManagedGuestSessionBaseTest> managed_guest_session_;
};
TEST_P(ManagedGuestSessionInputMetricsTest, InputEventUkmReportedAfter2Hours) {
TestApp& pre_installed_app = ActivatePreInstalledApp(kSystemWebAppId);
CreateInputEvent(InputEventSource::kTouch);
task_environment_.FastForwardBy(base::Minutes(5));
VerifyNoUkm();
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(115));
VerifyUkm(pre_installed_app, /*event_count=*/1, InputEventSource::kTouch);
}
TEST_P(ManagedGuestSessionInputMetricsTest, InputEventUkmReportedOnShutdown) {
TestApp& pre_installed_app = ActivatePreInstalledApp(kSystemWebAppId);
CreateInputEvent(InputEventSource::kTouch);
task_environment_.FastForwardBy(base::Minutes(5));
VerifyNoUkm();
managed_guest_session_->SimulateMgsShutdown(*this);
VerifyUkm(pre_installed_app, /*event_count=*/1, InputEventSource::kTouch);
}
TEST_P(ManagedGuestSessionInputMetricsTest,
InputEventUkmForPolicyInstalledApp) {
TestApp app = TestApp(app_constants::kChromeAppId, AppType::kChromeApp,
"Chrome", Readiness::kReady, InstallReason::kPolicy,
InstallSource::kSystem);
InstallOneApp(app);
auto browser = CreateBrowser();
// Set the browser window as activated.
ModifyInstance(app.app_id, window(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kMouse);
task_environment_.FastForwardBy(base::Minutes(5));
VerifyNoUkm();
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(115));
VerifyUkm(app, /*event_count=*/1, InputEventSource::kMouse);
}
TEST_P(ManagedGuestSessionInputMetricsTest,
DoNotReportInputEventUkmForUserInstalledApps) {
TestApp app =
TestApp(app_constants::kChromeAppId, AppType::kChromeApp, "Chrome",
Readiness::kReady, InstallReason::kUser, InstallSource::kSystem);
InstallOneApp(app);
auto browser = CreateBrowser();
// Set the browser window as activated.
ModifyInstance(app.app_id, window(), kActiveInstanceState);
CreateInputEvent(InputEventSource::kMouse);
task_environment_.FastForwardBy(base::Minutes(5));
VerifyNoUkm();
// Set time passed 2 hours to record the usage time AppKM.
task_environment_.FastForwardBy(base::Minutes(115));
VerifyNoUkm();
}
INSTANTIATE_TEST_SUITE_P(All,
ManagedGuestSessionInputMetricsTest,
::testing::Bool() /* IsLacrosEnabled */);
} // namespace apps