blob: b13f0328c8bbcf292f7e33203cced4694bf6f6f5 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include <utility>
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_ash.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/ash/login/test/cryptohome_mixin.h"
#include "chrome/browser/ash/policy/affiliation/affiliation_mixin.h"
#include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h"
#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
#include "chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
#include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
#include "chrome/browser/policy/dm_token_utils.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom-shared.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/user_display_mode.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/dbus/missive/missive_client_test_observer.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
#include "components/reporting/proto/synced/record.pb.h"
#include "components/reporting/proto/synced/record_constants.pb.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/protos/app_types.pb.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_launcher.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
#include "url/gurl.h"
using ::testing::Eq;
using ::testing::StrEq;
namespace reporting {
namespace {
// Test DM token used to associate reported events.
constexpr char kDMToken[] = "token";
// Standalone webapp start URL.
constexpr char kWebAppUrl[] = "https://test.example.com";
// Assert event data in a record with relevant DM token and returns the
// underlying `MetricData` object.
const MetricData AssertEvent(Priority priority, const Record& record) {
EXPECT_THAT(priority, Eq(Priority::SLOW_BATCH));
EXPECT_THAT(record.destination(), Eq(Destination::EVENT_METRIC));
MetricData record_data;
EXPECT_TRUE(record_data.ParseFromString(record.data()));
EXPECT_TRUE(record_data.has_timestamp_ms());
EXPECT_TRUE(record_data.has_event_data());
EXPECT_TRUE(record_data.has_telemetry_data());
EXPECT_TRUE(record_data.telemetry_data().has_app_telemetry());
EXPECT_TRUE(record.has_dm_token());
EXPECT_THAT(record.dm_token(), StrEq(kDMToken));
return record_data;
}
// Returns true if the record includes the specified metric event type. False
// otherwise.
bool IsMetricEventOfType(MetricEventType metric_event_type,
const Record& record) {
MetricData record_data;
return record_data.ParseFromString(record.data()) &&
record_data.has_event_data() &&
(record_data.event_data().type() == metric_event_type);
}
// Browser test that validates events collected and reported by the
// `AppEventsObserver`. Inheriting from `DevicePolicyCrosBrowserTest` enables
// use of `AffiliationMixin` for setting up profile/device affiliation. Only
// available in Ash.
class AppEventsObserverBrowserTest
: public ::policy::DevicePolicyCrosBrowserTest {
protected:
AppEventsObserverBrowserTest() {
crypto_home_mixin_.MarkUserAsExisting(affiliation_mixin_.account_id());
scoped_feature_list_.InitAndEnableFeature(kEnableAppMetricsReporting);
::policy::SetDMTokenForTesting(
::policy::DMToken::CreateValidToken(kDMToken));
}
void SetUpCommandLine(base::CommandLine* command_line) override {
::policy::AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager(
command_line);
::policy::DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
}
void SetUpOnMainThread() override {
::policy::DevicePolicyCrosBrowserTest::SetUpOnMainThread();
SetPolicyEnabled(true);
if (content::IsPreTest()) {
// Preliminary setup - set up affiliated user.
::policy::AffiliationTestHelper::PreLoginUser(
affiliation_mixin_.account_id());
return;
}
// Login as affiliated user otherwise.
::policy::AffiliationTestHelper::LoginUser(affiliation_mixin_.account_id());
}
// Helper that installs a standalone webapp with the specified start url.
::web_app::AppId InstallStandaloneWebApp(const GURL& start_url) {
auto web_app_info = std::make_unique<WebAppInstallInfo>();
web_app_info->start_url = start_url;
web_app_info->scope = start_url.GetWithoutFilename();
web_app_info->display_mode = ::blink::mojom::DisplayMode::kStandalone;
web_app_info->user_display_mode =
::web_app::mojom::UserDisplayMode::kStandalone;
return ::web_app::test::InstallWebApp(profile(), std::move(web_app_info));
}
// Helper that uninstalls the standalone webapp with the specified app id.
void UninstallStandaloneWebApp(const ::web_app::AppId& app_id) {
::apps::AppServiceProxyFactory::GetForProfile(profile())->UninstallSilently(
app_id, ::apps::UninstallSource::kAppList);
}
void SetPolicyEnabled(bool is_enabled) {
scoped_testing_cros_settings_.device_settings()->SetBoolean(
::ash::kReportDeviceAppInfo, is_enabled);
}
Profile* profile() const {
return ash::ProfileHelper::Get()->GetProfileByAccountId(
affiliation_mixin_.account_id());
}
::policy::DevicePolicyCrosTestHelper test_helper_;
::policy::AffiliationMixin affiliation_mixin_{&mixin_host_, &test_helper_};
::ash::CryptohomeMixin crypto_home_mixin_{&mixin_host_};
base::test::ScopedFeatureList scoped_feature_list_;
::ash::ScopedTestingCrosSettings scoped_testing_cros_settings_;
};
IN_PROC_BROWSER_TEST_F(AppEventsObserverBrowserTest, PRE_ReportInstalledApp) {
// Dummy case that sets up the affiliated user through SetUpOnMainThread
// PRE-condition.
}
IN_PROC_BROWSER_TEST_F(AppEventsObserverBrowserTest, ReportInstalledApp) {
ASSERT_TRUE(base::FeatureList::IsEnabled(kEnableAppMetricsReporting));
::chromeos::MissiveClientTestObserver missive_observer(base::BindRepeating(
&IsMetricEventOfType, MetricEventType::APP_INSTALLED));
const auto& app_id = InstallStandaloneWebApp(GURL(kWebAppUrl));
const auto [priority, record] = missive_observer.GetNextEnqueuedRecord();
const auto metric_data = AssertEvent(priority, record);
ASSERT_TRUE(
metric_data.telemetry_data().app_telemetry().has_app_install_data());
const auto& app_install_data =
metric_data.telemetry_data().app_telemetry().app_install_data();
EXPECT_THAT(app_install_data.app_id(), StrEq(app_id));
EXPECT_THAT(app_install_data.app_type(),
Eq(::apps::ApplicationType::APPLICATION_TYPE_WEB));
EXPECT_THAT(
app_install_data.app_install_source(),
Eq(::apps::ApplicationInstallSource::APPLICATION_INSTALL_SOURCE_BROWSER));
EXPECT_THAT(
app_install_data.app_install_reason(),
Eq(::apps::ApplicationInstallReason::APPLICATION_INSTALL_REASON_SYNC));
EXPECT_THAT(
app_install_data.app_install_time(),
Eq(::apps::ApplicationInstallTime::APPLICATION_INSTALL_TIME_RUNNING));
}
IN_PROC_BROWSER_TEST_F(AppEventsObserverBrowserTest, PRE_ReportLaunchedApp) {
// Dummy case that sets up the affiliated user through SetUpOnMainThread
// PRE-condition.
}
IN_PROC_BROWSER_TEST_F(AppEventsObserverBrowserTest, ReportLaunchedApp) {
ASSERT_TRUE(base::FeatureList::IsEnabled(kEnableAppMetricsReporting));
const auto& app_id = InstallStandaloneWebApp(GURL(kWebAppUrl));
::chromeos::MissiveClientTestObserver missive_observer(
base::BindRepeating(&IsMetricEventOfType, MetricEventType::APP_LAUNCHED));
::web_app::LaunchWebAppBrowser(profile(), app_id);
const auto [priority, record] = missive_observer.GetNextEnqueuedRecord();
const auto metric_data = AssertEvent(priority, record);
ASSERT_TRUE(
metric_data.telemetry_data().app_telemetry().has_app_launch_data());
const auto& app_launch_data =
metric_data.telemetry_data().app_telemetry().app_launch_data();
EXPECT_THAT(
app_launch_data.app_launch_source(),
Eq(::apps::ApplicationLaunchSource::APPLICATION_LAUNCH_SOURCE_TEST));
EXPECT_THAT(app_launch_data.app_id(), StrEq(app_id));
EXPECT_THAT(app_launch_data.app_type(),
Eq(::apps::ApplicationType::APPLICATION_TYPE_WEB));
}
IN_PROC_BROWSER_TEST_F(AppEventsObserverBrowserTest, PRE_ReportUninstalledApp) {
// Dummy case that sets up the affiliated user through SetUpOnMainThread
// PRE-condition.
}
IN_PROC_BROWSER_TEST_F(AppEventsObserverBrowserTest, ReportUninstalledApp) {
ASSERT_TRUE(base::FeatureList::IsEnabled(kEnableAppMetricsReporting));
const auto& app_id = InstallStandaloneWebApp(GURL(kWebAppUrl));
::chromeos::MissiveClientTestObserver missive_observer(base::BindRepeating(
&IsMetricEventOfType, MetricEventType::APP_UNINSTALLED));
UninstallStandaloneWebApp(app_id);
const auto [priority, record] = missive_observer.GetNextEnqueuedRecord();
const auto metric_data = AssertEvent(priority, record);
ASSERT_TRUE(
metric_data.telemetry_data().app_telemetry().has_app_uninstall_data());
const auto& app_uninstall_data =
metric_data.telemetry_data().app_telemetry().app_uninstall_data();
EXPECT_THAT(app_uninstall_data.app_uninstall_source(),
Eq(::apps::ApplicationUninstallSource::
APPLICATION_UNINSTALL_SOURCE_APP_LIST));
EXPECT_THAT(app_uninstall_data.app_id(), StrEq(app_id));
EXPECT_THAT(app_uninstall_data.app_type(),
Eq(::apps::ApplicationType::APPLICATION_TYPE_WEB));
}
} // namespace
} // namespace reporting