blob: 339287beea3f0d226fb5c5ae3a2b4495e527bae0 [file] [log] [blame]
// Copyright 2025 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/glic/glic_metrics.h"
#include <optional>
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/background/startup_launch_manager.h"
#include "chrome/browser/glic/glic_pref_names.h"
#include "chrome/browser/glic/host/context/glic_focused_tab_manager.h"
#include "chrome/browser/glic/host/context/glic_tab_data.h"
#include "chrome/browser/glic/host/glic.mojom.h"
#include "chrome/browser/glic/public/glic_enabling.h"
#include "chrome/browser/glic/public/glic_keyed_service.h"
#include "chrome/browser/glic/test_support/glic_test_util.h"
#include "chrome/browser/glic/widget/glic_window_controller.h"
#include "chrome/browser/global_features.h"
#include "chrome/browser/status_icons/status_icon.h"
#include "chrome/browser/status_icons/status_icon_menu_model.h"
#include "chrome/browser/status_icons/status_tray.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "glic_metrics.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/command.h"
namespace glic {
namespace {
class MockDelegate : public GlicMetrics::Delegate {
public:
MockDelegate() = default;
~MockDelegate() override = default;
bool IsWindowShowing() const override { return showing_; }
bool IsWindowAttached() const override { return attached_; }
gfx::Size GetWindowSize() const override { return gfx::Size(); }
content::WebContents* GetContents() override { return contents_.get(); }
ActiveTabSharingState GetActiveTabSharingState() override {
return tab_sharing_state_;
}
int32_t GetNumPinnedTabs() const override { return num_pinned_tabs_; }
void SetWebContents(content::WebContents* contents) { contents_ = contents; }
raw_ptr<content::WebContents> contents_;
bool showing_ = false;
bool attached_ = false;
ActiveTabSharingState tab_sharing_state_ =
ActiveTabSharingState::kActiveTabIsShared;
int32_t num_pinned_tabs_ = 0;
};
class MockStatusIcon : public StatusIcon {
public:
explicit MockStatusIcon(const std::u16string& tool_tip)
: tool_tip_(tool_tip) {}
void SetImage(const gfx::ImageSkia& image) override {}
void SetToolTip(const std::u16string& tool_tip) override {
tool_tip_ = tool_tip;
}
void DisplayBalloon(const gfx::ImageSkia& icon,
const std::u16string& title,
const std::u16string& contents,
const message_center::NotifierId& notifier_id) override {}
void UpdatePlatformContextMenu(StatusIconMenuModel* menu) override {
menu_item_ = menu;
}
const std::u16string& tool_tip() const { return tool_tip_; }
StatusIconMenuModel* menu_item() const { return menu_item_; }
private:
raw_ptr<StatusIconMenuModel> menu_item_ = nullptr;
std::u16string tool_tip_;
};
class MockStatusTray : public StatusTray {
public:
std::unique_ptr<StatusIcon> CreatePlatformStatusIcon(
StatusIconType type,
const gfx::ImageSkia& image,
const std::u16string& tool_tip) override {
return std::make_unique<MockStatusIcon>(tool_tip);
}
const StatusIcons& GetStatusIconsForTest() const { return status_icons(); }
};
class TestStartupLaunchManager : public StartupLaunchManager {
public:
TestStartupLaunchManager() = default;
~TestStartupLaunchManager() override = default;
};
class GlicMetricsTest : public testing::Test {
public:
GlicMetricsTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SetUp() override {
scoped_feature_list_.InitWithFeatures(
{
features::kGlicClosedCaptioning,
},
{});
testing::Test::SetUp();
SetUpProfile();
SetUpGlicMetrics();
}
void SetUpProfile() {
StartupLaunchManager::SetInstanceForTesting(&startup_launch_manager_);
testing_profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(testing_profile_manager_->SetUp());
TestingBrowserProcess::GetGlobal()->SetStatusTray(
std::make_unique<MockStatusTray>());
TestingBrowserProcess::GetGlobal()->CreateGlobalFeaturesForTesting();
profile_ = testing_profile_manager_->CreateTestingProfile("profile");
ForceSigninAndModelExecutionCapability(profile_);
}
void SetUpGlicMetrics() {
enabling_ = std::make_unique<GlicEnabling>(
profile_, &testing_profile_manager_->profile_manager()
->GetProfileAttributesStorage());
metrics_ = std::make_unique<GlicMetrics>(profile_, enabling_.get());
auto delegate = std::make_unique<MockDelegate>();
delegate_ = delegate.get();
metrics_->SetDelegateForTesting(std::move(delegate));
}
void TearDown() override {
scoped_feature_list_.Reset();
delegate_ = nullptr;
metrics_.reset();
enabling_.reset();
TestingBrowserProcess::GetGlobal()->GetFeatures()->Shutdown();
profile_ = nullptr;
testing_profile_manager_.reset();
StartupLaunchManager::SetInstanceForTesting(nullptr);
testing::Test::TearDown();
}
void ExpectEntryPointImpressionLogged(
EntryPointStatus entry_point_impression) {
task_environment_.FastForwardBy(base::Minutes(16));
histogram_tester_.ExpectTotalCount("Glic.EntryPoint.Status", 1);
histogram_tester_.ExpectBucketCount("Glic.EntryPoint.Status",
entry_point_impression,
/*expected_count=*/1);
}
protected:
TestingPrefServiceSimple* local_state() {
return TestingBrowserProcess::GetGlobal()->GetTestingLocalState();
}
content::BrowserTaskEnvironment task_environment_;
TestStartupLaunchManager startup_launch_manager_;
content::RenderViewHostTestEnabler enabler_;
base::HistogramTester histogram_tester_;
base::UserActionTester user_action_tester_;
ukm::TestAutoSetUkmRecorder ukm_tester_;
raw_ptr<TestingProfile> profile_ = nullptr;
signin::IdentityTestEnvironment identity_env_;
std::unique_ptr<TestingProfileManager> testing_profile_manager_;
// Owned by `metrics_`.
raw_ptr<MockDelegate> delegate_;
std::unique_ptr<GlicEnabling> enabling_;
std::unique_ptr<GlicMetrics> metrics_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(GlicMetricsTest, Basic) {
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped(mojom::ResponseStopCause::kUnknown);
metrics_->OnResponseRated(/*positive=*/true);
metrics_->OnSessionTerminated();
histogram_tester_.ExpectTotalCount("Glic.Response.StopTime", 1);
histogram_tester_.ExpectTotalCount("Glic.Response.StopTime.UnknownCause", 1);
histogram_tester_.ExpectUniqueSample(
"Glic.Session.InputSubmit.BrowserActiveState", 5 /*kBrowserHidden*/, 1);
histogram_tester_.ExpectUniqueSample(
"Glic.Session.ResponseStart.BrowserActiveState", 5 /*kBrowserHidden*/, 1);
histogram_tester_.ExpectUniqueSample(
"Glic.Sharing.ActiveTabSharingState.OnUserInputSubmitted",
ActiveTabSharingState::kActiveTabIsShared, 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseInputSubmit"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStart"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStop"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStopUnknownCause"),
1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponse"), 0);
}
TEST_F(GlicMetricsTest, BasicVisible) {
delegate_->showing_ = true;
delegate_->attached_ = true;
metrics_->OnGlicWindowOpen(/*attached=*/true,
mojom::InvocationSource::kOsButton);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped(mojom::ResponseStopCause::kUnknown);
metrics_->OnResponseRated(/*positive=*/true);
metrics_->OnSessionTerminated();
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Response.StopTime", 1);
histogram_tester_.ExpectUniqueSample("Glic.Session.Open.BrowserActiveState",
5 /*kBrowserHidden*/, 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicSessionBegin"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseInputSubmit"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStart"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStop"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponse"), 1);
}
TEST_F(GlicMetricsTest, BasicUkm) {
delegate_->showing_ = true;
metrics_->OnGlicWindowOpen(/*attached=*/false, mojom::InvocationSource::kFre);
for (int i = 0; i < 2; ++i) {
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped(mojom::ResponseStopCause::kUnknown);
}
{
auto entries = ukm_tester_.GetEntriesByName("Glic.WindowOpen");
ASSERT_EQ(entries.size(), 1u);
auto entry = entries[0];
ukm_tester_.ExpectEntryMetric(entry, "Attached", false);
ukm_tester_.ExpectEntryMetric(
entry, "InvocationSource",
static_cast<int64_t>(mojom::InvocationSource::kFre));
auto* source = ukm_tester_.GetSourceForSourceId(entry->source_id);
EXPECT_FALSE(source);
}
{
auto entries = ukm_tester_.GetEntriesByName("Glic.Response");
ASSERT_EQ(entries.size(), 2u);
for (int i = 0; i < 2; ++i) {
auto entry = entries[i];
ukm_tester_.ExpectEntryMetric(entry, "Attached", false);
ukm_tester_.ExpectEntryMetric(
entry, "WebClientMode",
static_cast<int64_t>(mojom::WebClientMode::kText));
ukm_tester_.ExpectEntryMetric(
entry, "InvocationSource",
static_cast<int64_t>(mojom::InvocationSource::kFre));
auto* source = ukm_tester_.GetSourceForSourceId(entry->source_id);
EXPECT_FALSE(source);
}
}
}
TEST_F(GlicMetricsTest, BasicUkmWithTarget) {
// Create a SiteInstance, which is required to build a WebContents.
scoped_refptr<content::SiteInstance> site_instance =
content::SiteInstance::Create(profile_);
// Use WebContentsTester::CreateTestWebContents(...) to create a real
// WebContents suitable for unit testing.
std::unique_ptr<content::WebContents> web_contents =
content::WebContentsTester::CreateTestWebContents(profile_,
site_instance.get());
auto* tester = content::WebContentsTester::For(web_contents.get());
GURL url("https://www.google.com");
tester->NavigateAndCommit(url);
delegate_->SetWebContents(web_contents.get());
delegate_->showing_ = true;
metrics_->DidRequestContextFromFocusedTab();
metrics_->OnGlicWindowOpen(/*attached=*/false, mojom::InvocationSource::kFre);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped(mojom::ResponseStopCause::kUnknown);
ukm::SourceId ukm_id =
web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId();
{
auto entries = ukm_tester_.GetEntriesByName("Glic.WindowOpen");
ASSERT_EQ(entries.size(), 1u);
auto entry = entries[0];
EXPECT_EQ(entry->source_id, ukm_id);
}
{
auto entries = ukm_tester_.GetEntriesByName("Glic.Response");
ASSERT_EQ(entries.size(), 1u);
auto entry = entries[0];
EXPECT_EQ(entry->source_id, ukm_id);
}
delegate_->SetWebContents(nullptr);
}
TEST_F(GlicMetricsTest, BasicStopReasonOther) {
delegate_->showing_ = true;
delegate_->attached_ = true;
metrics_->OnGlicWindowOpen(/*attached=*/true,
mojom::InvocationSource::kOsButton);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped(mojom::ResponseStopCause::kOther);
metrics_->OnSessionTerminated();
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Response.StopTime.Other", 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStopOther"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStop"), 1);
}
TEST_F(GlicMetricsTest, BasicStopReasonByUser) {
delegate_->showing_ = true;
delegate_->attached_ = true;
metrics_->OnGlicWindowOpen(/*attached=*/true,
mojom::InvocationSource::kOsButton);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped(mojom::ResponseStopCause::kUser);
metrics_->OnSessionTerminated();
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Response.StopTime.ByUser", 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponseStopByUser"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("GlicResponse"), 1);
}
TEST_F(GlicMetricsTest, SegmentationOsButtonAttachedText) {
delegate_->showing_ = true;
delegate_->attached_ = true;
metrics_->OnGlicWindowOpen(/*attached=*/true,
mojom::InvocationSource::kOsButton);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped(mojom::ResponseStopCause::kUnknown);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Response.Segmentation", 1);
histogram_tester_.ExpectBucketCount(
"Glic.Response.Segmentation", ResponseSegmentation::kOsButtonAttachedText,
/*expected_count=*/1);
}
TEST_F(GlicMetricsTest, Segmentation3DotsMenuDetachedAudio) {
delegate_->showing_ = true;
delegate_->attached_ = false;
metrics_->OnGlicWindowOpen(/*attached=*/false,
mojom::InvocationSource::kThreeDotsMenu);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kAudio);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped(mojom::ResponseStopCause::kUnknown);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Response.Segmentation", 1);
histogram_tester_.ExpectBucketCount(
"Glic.Response.Segmentation",
ResponseSegmentation::kThreeDotsMenuDetachedAudio,
/*expected_count=*/1);
}
TEST_F(GlicMetricsTest, SessionDuration_LogsDuration) {
metrics_->OnGlicWindowOpen(/*attached=*/true,
mojom::InvocationSource::kOsButton);
int minutes = 10;
task_environment_.FastForwardBy(base::Minutes(minutes));
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.Duration", 1);
histogram_tester_.ExpectTimeBucketCount(
"Glic.Session.Duration", base::Minutes(minutes), /*expected_count=*/1);
}
TEST_F(GlicMetricsTest, SessionDuration_LogsError) {
// Trigger a call to |OnGlicWindowClose()| without opening the window first.
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.Duration", 0);
histogram_tester_.ExpectTotalCount("Glic.Metrics.Error", 1);
histogram_tester_.ExpectBucketCount("Glic.Metrics.Error",
Error::kWindowCloseWithoutWindowOpen,
/*expected_count=*/1);
}
TEST_F(GlicMetricsTest, ClosedCaptionsResponse_PrefLogsFalse) {
metrics_->LogClosedCaptionsShown();
histogram_tester_.ExpectUniqueSample("Glic.Response.ClosedCaptionsShown",
false, 1);
}
TEST_F(GlicMetricsTest, ClosedCaptionsResponse_PrefLogsTrue) {
profile_->GetPrefs()->SetBoolean(prefs::kGlicClosedCaptioningEnabled, true);
metrics_->LogClosedCaptionsShown();
histogram_tester_.ExpectUniqueSample("Glic.Response.ClosedCaptionsShown",
true, 1);
}
TEST_F(GlicMetricsTest, OnTabPinSharedSuccessful) {
metrics_->OnTabPinnedForSharing(
GlicTabPinnedForSharingResult::kPinTabForSharingSucceeded);
histogram_tester_.ExpectUniqueSample(
"Glic.Sharing.TabPinnedForSharing",
GlicTabPinnedForSharingResult::kPinTabForSharingSucceeded, 1);
}
TEST_F(GlicMetricsTest, OnTabPinSharedUnsuccessfulTooMany) {
metrics_->OnTabPinnedForSharing(
GlicTabPinnedForSharingResult::kPinTabForSharingFailedTooManyTabs);
histogram_tester_.ExpectUniqueSample(
"Glic.Sharing.TabPinnedForSharing",
GlicTabPinnedForSharingResult::kPinTabForSharingFailedTooManyTabs, 1);
}
TEST_F(GlicMetricsTest, OnTabPinSharedUnsuccessfulNotValid) {
metrics_->OnTabPinnedForSharing(
GlicTabPinnedForSharingResult::kPinTabForSharingFailedNotValidForSharing);
histogram_tester_.ExpectUniqueSample(
"Glic.Sharing.TabPinnedForSharing",
GlicTabPinnedForSharingResult::kPinTabForSharingFailedNotValidForSharing,
1);
}
TEST_F(GlicMetricsTest, ImpressionBeforeFreNotPermittedByPolicy) {
profile_->GetPrefs()->SetInteger(
prefs::kGlicCompletedFre,
static_cast<int>(prefs::FreStatus::kNotStarted));
ExpectEntryPointImpressionLogged(EntryPointStatus::kBeforeFreNotEligible);
}
TEST_F(GlicMetricsTest, ImpressionIncompleteFreNotPermittedByPolicy) {
profile_->GetPrefs()->SetInteger(
prefs::kGlicCompletedFre,
static_cast<int>(prefs::FreStatus::kIncomplete));
ExpectEntryPointImpressionLogged(EntryPointStatus::kIncompleteFreNotEligible);
}
// kGeminiSettings is by default enabled, however if we initialize a scoped
// feature list in a test, since the features were initially off during setup,
// glic is considered disabled until the kGeminiSettings pref changes and
// subscribers are notified. The following tests turn the feature flags on
// before setup happens, so that glic is enabled from the start.
class GlicMetricsFeaturesEnabledTest : public GlicMetricsTest {
public:
void SetUp() override {
scoped_feature_list_.InitWithFeatures(
{
features::kGlic,
features::kTabstripComboButton,
features::kGlicRollout,
},
{});
SetUpProfile();
// When Glic is enabled before the profile is setup GlicKeyedService starts
// and creates it's own GlicMetrics. Do not setup GlicMetrics again here so
// that duplicate metrics observers are not bound.
}
void TearDown() override {
scoped_feature_list_.Reset();
GlicMetricsTest::TearDown();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionBeforeFre) {
profile_->GetPrefs()->SetInteger(
prefs::kGlicCompletedFre,
static_cast<int>(prefs::FreStatus::kNotStarted));
ExpectEntryPointImpressionLogged(EntryPointStatus::kBeforeFreAndEligible);
}
TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionIncompleteFre) {
profile_->GetPrefs()->SetInteger(
prefs::kGlicCompletedFre,
static_cast<int>(prefs::FreStatus::kIncomplete));
ExpectEntryPointImpressionLogged(EntryPointStatus::kIncompleteFreAndEligible);
}
TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionAfterFreBrowserOnly) {
// kGeminiSettings is enabled
// kGlicPinnedToTabstrip is true
// kGlicLauncherEnabled is false
ExpectEntryPointImpressionLogged(EntryPointStatus::kAfterFreBrowserOnly);
}
TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionAfterFreOsOnly) {
// kGeminiSettings is enabled
profile_->GetPrefs()->SetBoolean(prefs::kGlicPinnedToTabstrip, false);
local_state()->SetBoolean(prefs::kGlicLauncherEnabled, true);
ExpectEntryPointImpressionLogged(EntryPointStatus::kAfterFreOsOnly);
}
TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionAfterFreEnabled) {
// kGeminiSettings is enabled
// kGlicPinnedToTabstrip is true
local_state()->SetBoolean(prefs::kGlicLauncherEnabled, true);
ExpectEntryPointImpressionLogged(EntryPointStatus::kAfterFreBrowserAndOs);
}
TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionAfterFreDisabledEntrypoints) {
// kGeminiSettings is enabled
profile_->GetPrefs()->SetBoolean(prefs::kGlicPinnedToTabstrip, false);
// kGlicLauncherEnabled is false
ExpectEntryPointImpressionLogged(EntryPointStatus::kAfterFreThreeDotOnly);
}
TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionAfterFreNotPermittedByPolicy) {
// kGeminiSettings is enabled
// kGlicPinnedToTabstrip is true
// kGlicLauncherEnabled is true
// Disable kGeminiSettings
profile_->GetPrefs()->SetInteger(
::prefs::kGeminiSettings,
static_cast<int>(glic::prefs::SettingsPolicyState::kDisabled));
ExpectEntryPointImpressionLogged(EntryPointStatus::kAfterFreNotEligible);
}
TEST_F(GlicMetricsFeaturesEnabledTest, EnablingChanged) {
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 0);
// Glic starts enabled and during profile creation GlicMetrics records a user
// action.
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 1);
profile_->GetPrefs()->SetInteger(
prefs::kGlicCompletedFre,
static_cast<int>(prefs::FreStatus::kNotStarted));
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 1);
profile_->GetPrefs()->SetInteger(
prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kCompleted));
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 2);
profile_->GetPrefs()->SetInteger(
::prefs::kGeminiSettings,
static_cast<int>(glic::prefs::SettingsPolicyState::kDisabled));
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 2);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 2);
profile_->GetPrefs()->SetInteger(
::prefs::kGeminiSettings,
static_cast<int>(glic::prefs::SettingsPolicyState::kEnabled));
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 2);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 3);
profile_->GetPrefs()->SetInteger(
prefs::kGlicCompletedFre,
static_cast<int>(prefs::FreStatus::kIncomplete));
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 3);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 3);
}
TEST_F(GlicMetricsFeaturesEnabledTest, PinnedChanged) {
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Pinned"), 0);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Unpinned"), 0);
profile_->GetPrefs()->SetBoolean(prefs::kGlicPinnedToTabstrip, false);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Pinned"), 0);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Unpinned"), 1);
profile_->GetPrefs()->SetBoolean(prefs::kGlicPinnedToTabstrip, true);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Pinned"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Unpinned"), 1);
}
TEST_F(GlicMetricsFeaturesEnabledTest, ShortcutStatus) {
task_environment_.FastForwardBy(base::Minutes(16));
histogram_tester_.ExpectTotalCount(
"Glic.OsEntrypoint.Settings.ShortcutStatus", 1);
histogram_tester_.ExpectBucketCount(
"Glic.OsEntrypoint.Settings.ShortcutStatus", /*true*/1,
/*expected_count=*/1);
local_state()->SetString(prefs::kGlicLauncherHotkey,
ui::Command::AcceleratorToString(ui::Accelerator()));
task_environment_.FastForwardBy(base::Minutes(16));
histogram_tester_.ExpectTotalCount(
"Glic.OsEntrypoint.Settings.ShortcutStatus", 2);
histogram_tester_.ExpectBucketCount(
"Glic.OsEntrypoint.Settings.ShortcutStatus", /*false*/0,
/*expected_count=*/1);
}
TEST_F(GlicMetricsTest, InputModesUsed) {
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.InputModesUsed", 1);
histogram_tester_.ExpectBucketCount("Glic.Session.InputModesUsed",
InputModesUsed::kOnlyText, 1);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.InputModesUsed", 2);
histogram_tester_.ExpectBucketCount("Glic.Session.InputModesUsed",
InputModesUsed::kNone, 1);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kAudio);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.InputModesUsed", 3);
histogram_tester_.ExpectBucketCount("Glic.Session.InputModesUsed",
InputModesUsed::kTextAndAudio, 1);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kAudio);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.InputModesUsed", 4);
histogram_tester_.ExpectBucketCount("Glic.Session.InputModesUsed",
InputModesUsed::kOnlyAudio, 1);
}
TEST_F(GlicMetricsTest, AttachStateChanges) {
// Attach changes during initialization should not be counted.
metrics_->OnAttachedToBrowser(AttachChangeReason::kInit);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.AttachStateChanges", 1);
histogram_tester_.ExpectBucketCount("Glic.Session.AttachStateChanges", 0, 1);
metrics_->OnAttachedToBrowser(AttachChangeReason::kDrag);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.AttachStateChanges", 2);
histogram_tester_.ExpectBucketCount("Glic.Session.AttachStateChanges", 1, 1);
metrics_->OnAttachedToBrowser(AttachChangeReason::kMenu);
metrics_->OnDetachedFromBrowser(AttachChangeReason::kMenu);
metrics_->OnAttachedToBrowser(AttachChangeReason::kMenu);
metrics_->OnDetachedFromBrowser(AttachChangeReason::kMenu);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
histogram_tester_.ExpectTotalCount("Glic.Session.AttachStateChanges", 3);
histogram_tester_.ExpectBucketCount("Glic.Session.AttachStateChanges", 4, 1);
}
TEST_F(GlicMetricsTest, TimeElapsedBetweenSessions) {
base::TimeDelta elapsed_time = base::Hours(2);
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
task_environment_.FastForwardBy(elapsed_time);
metrics_->OnGlicWindowOpen(/*attached=*/true,
mojom::InvocationSource::kOsButton);
histogram_tester_.ExpectTotalCount(
"Glic.PanelWebUi.ElapsedTimeBetweenSessions",
/*expected_count=*/1);
histogram_tester_.ExpectBucketCount(
"Glic.PanelWebUi.ElapsedTimeBetweenSessions", elapsed_time.InSeconds(),
1);
}
TEST_F(GlicMetricsTest, PositionOnOpenAndClose) {
display::Display display;
display.set_bounds(gfx::Rect(300, 350));
display.set_work_area(gfx::Rect(0, 50, 300, 300));
metrics_->OnGlicWindowShown(nullptr, display, gfx::Rect(50, 50, 0, 0));
metrics_->OnGlicWindowClose(nullptr, display, gfx::Rect(50, 150, 0, 0));
metrics_->OnGlicWindowShown(nullptr, display, gfx::Rect(50, 250, 0, 0));
metrics_->OnGlicWindowClose(nullptr, display, gfx::Rect(150, 50, 0, 0));
metrics_->OnGlicWindowShown(nullptr, display, gfx::Rect(150, 150, 0, 0));
metrics_->OnGlicWindowClose(nullptr, display, gfx::Rect(150, 250, 0, 0));
metrics_->OnGlicWindowShown(nullptr, display, gfx::Rect(250, 50, 0, 0));
metrics_->OnGlicWindowClose(nullptr, display, gfx::Rect(250, 150, 0, 0));
metrics_->OnGlicWindowShown(nullptr, display, gfx::Rect(250, 250, 0, 0));
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnOpen",
DisplayPosition::kTopLeft, 1);
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnClose",
DisplayPosition::kCenterLeft, 1);
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnOpen",
DisplayPosition::kBottomLeft, 1);
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnClose",
DisplayPosition::kTopCenter, 1);
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnOpen",
DisplayPosition::kCenterCenter, 1);
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnClose",
DisplayPosition::kBottomCenter, 1);
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnOpen",
DisplayPosition::kTopRight, 1);
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnClose",
DisplayPosition::kCenterRight, 1);
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnOpen",
DisplayPosition::kBottomRight, 1);
// point is not within the work area bounds
metrics_->OnGlicWindowShown(nullptr, display, gfx::Rect(-50, 50, 0, 0));
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnOpen",
DisplayPosition::kUnknown, 1);
metrics_->OnGlicWindowClose(nullptr, display, gfx::Rect(50, -50, 0, 0));
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnClose",
DisplayPosition::kUnknown, 1);
// no display
metrics_->OnGlicWindowShown(nullptr, std::nullopt, gfx::Rect(50, 50, 0, 0));
histogram_tester_.ExpectBucketCount("Glic.PositionOnDisplay.OnOpen",
DisplayPosition::kUnknown, 2);
}
TEST_F(GlicMetricsTest, TabFocusStateReporting) {
delegate_->tab_sharing_state_ = ActiveTabSharingState::kActiveTabIsShared;
// Should not record samples on denying tab access or with the panel not
// considered open.
profile_->GetPrefs()->SetBoolean(prefs::kGlicTabContextEnabled, false);
profile_->GetPrefs()->SetBoolean(prefs::kGlicTabContextEnabled, true);
// Marks the panel as open.
metrics_->OnGlicWindowOpen(/*attached=*/true,
mojom::InvocationSource::kOsButton);
// Enable OnGlicWindowOpenAndReady to record metrics.
metrics_->set_show_start_time(base::TimeTicks::Now());
// Records a sample of *.OnPanelOpenAndReady.
metrics_->OnGlicWindowOpenAndReady();
delegate_->tab_sharing_state_ = ActiveTabSharingState::kCannotShareActiveTab;
// Granting tab access records a sample of *.OnTabContextPermissionGranted.
profile_->GetPrefs()->SetBoolean(prefs::kGlicTabContextEnabled, false);
profile_->GetPrefs()->SetBoolean(prefs::kGlicTabContextEnabled, true);
// Should not record a sample as the user is granting a different permission.
profile_->GetPrefs()->SetBoolean(prefs::kGlicGeolocationEnabled, false);
profile_->GetPrefs()->SetBoolean(prefs::kGlicGeolocationEnabled, true);
delegate_->tab_sharing_state_ = ActiveTabSharingState::kNoTabCanBeShared;
// Records a sample of *.OnUserInputSubmitted.
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
// Marks the panel as closed.
metrics_->OnGlicWindowClose(nullptr, std::nullopt, gfx::Rect());
// Should not record samples on denying tab access or with the panel not
// considered open.
profile_->GetPrefs()->SetBoolean(prefs::kGlicTabContextEnabled, false);
profile_->GetPrefs()->SetBoolean(prefs::kGlicTabContextEnabled, true);
histogram_tester_.ExpectUniqueSample(
"Glic.Sharing.ActiveTabSharingState.OnPanelOpenAndReady",
ActiveTabSharingState::kActiveTabIsShared, 1);
histogram_tester_.ExpectUniqueSample(
"Glic.Sharing.ActiveTabSharingState.OnTabContextPermissionGranted",
ActiveTabSharingState::kCannotShareActiveTab, 1);
histogram_tester_.ExpectUniqueSample(
"Glic.Sharing.ActiveTabSharingState.OnUserInputSubmitted",
ActiveTabSharingState::kNoTabCanBeShared, 1);
}
TEST_F(GlicMetricsTest, FreToFirstQueryElapsedTimeReportedOnce) {
metrics_->OnFreAccepted();
task_environment_.FastForwardBy(base::Milliseconds(100));
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
histogram_tester_.ExpectTotalCount("Glic.FreToFirstQueryTime", 1);
histogram_tester_.ExpectUniqueSample("Glic.FreToFirstQueryTime", 100, 1);
histogram_tester_.ExpectUniqueSample("Glic.FreToFirstQueryTimeMax24H", 100,
1);
}
TEST_F(GlicMetricsTest, FreToFirstQueryElapsedTimeReportedOnlyOnce) {
metrics_->OnFreAccepted();
task_environment_.FastForwardBy(base::Milliseconds(100));
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
// Second time should be ignored.
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
histogram_tester_.ExpectTotalCount("Glic.FreToFirstQueryTime", 1);
histogram_tester_.ExpectUniqueSample("Glic.FreToFirstQueryTime", 100, 1);
histogram_tester_.ExpectUniqueSample("Glic.FreToFirstQueryTimeMax24H", 100,
1);
}
} // namespace
} // namespace glic