blob: a3c1eb5c70732de06693a6e4d8bf889195c3b028 [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/glic/glic_enabling.h"
#include "chrome/browser/glic/glic_focused_tab_manager.h"
#include "chrome/browser/glic/glic_keyed_service.h"
#include "chrome/browser/glic/glic_pref_names.h"
#include "chrome/browser/glic/glic_tab_data.h"
#include "chrome/browser/glic/glic_test_util.h"
#include "chrome/browser/glic/glic_window_controller.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/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 "services/metrics/public/cpp/ukm_recorder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace glic {
namespace {
// This mock is a wrapper around the API in GlicWindowController which is
// exposed to GlicMetrics. It doesn't do anything.
class MockWindowController : public GlicWindowController {
public:
MockWindowController(Profile* profile,
signin::IdentityManager* identity_manager,
GlicEnabling* enabling)
: GlicWindowController(profile,
identity_manager,
/*service=*/nullptr,
enabling) {}
~MockWindowController() override = default;
bool IsShowing() const override { return showing_; }
bool IsAttached() override { return attached_; }
bool showing_ = false;
bool attached_ = false;
};
class MockTabManager : public GlicFocusedTabManager {
public:
MockTabManager(Profile* profile, GlicWindowController& window_controller)
: GlicFocusedTabManager(profile, window_controller) {}
~MockTabManager() override = default;
FocusedTabData GetFocusedTabData() override {
return FocusedTabData(contents_, std::nullopt, std::nullopt);
}
void SetWebContents(content::WebContents* contents) { contents_ = contents; }
raw_ptr<content::WebContents> contents_;
};
class GlicMetricsTest : public testing::Test {
public:
GlicMetricsTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SetUp() override {
enabling_ = std::make_unique<GlicEnabling>(&profile_);
controller_ = std::make_unique<MockWindowController>(
&profile_, identity_env_.identity_manager(), enabling_.get());
tab_manager_ = std::make_unique<MockTabManager>(&profile_, *controller_);
metrics_ = std::make_unique<GlicMetrics>(&profile_, enabling_.get());
metrics_->SetControllers(controller_.get(), tab_manager_.get());
ForceSigninAndModelExecutionCapability(&profile_);
}
protected:
content::BrowserTaskEnvironment task_environment_;
content::RenderViewHostTestEnabler enabler_;
base::HistogramTester histogram_tester_;
base::UserActionTester user_action_tester_;
ukm::TestAutoSetUkmRecorder ukm_tester_;
TestingProfile profile_;
signin::IdentityTestEnvironment identity_env_;
std::unique_ptr<GlicEnabling> enabling_;
std::unique_ptr<MockWindowController> controller_;
std::unique_ptr<MockTabManager> tab_manager_;
std::unique_ptr<GlicMetrics> metrics_;
};
TEST_F(GlicMetricsTest, Basic) {
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped();
metrics_->OnResponseRated(/*positive=*/true);
metrics_->OnSessionTerminated();
histogram_tester_.ExpectTotalCount("Glic.Response.StopTime", 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"), 0);
}
TEST_F(GlicMetricsTest, BasicVisible) {
controller_->showing_ = true;
controller_->attached_ = true;
metrics_->OnGlicWindowOpen(/*attached=*/true, InvocationSource::kOsButton);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped();
metrics_->OnResponseRated(/*positive=*/true);
metrics_->OnSessionTerminated();
metrics_->OnGlicWindowClose();
histogram_tester_.ExpectTotalCount("Glic.Response.StopTime", 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) {
controller_->showing_ = true;
metrics_->OnGlicWindowOpen(/*attached=*/false, InvocationSource::kFre);
for (int i = 0; i < 2; ++i) {
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped();
}
{
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>(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>(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);
tab_manager_->SetWebContents(web_contents.get());
controller_->showing_ = true;
metrics_->DidRequestContextFromFocusedTab();
metrics_->OnGlicWindowOpen(/*attached=*/false, InvocationSource::kFre);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped();
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);
}
tab_manager_->SetWebContents(nullptr);
}
TEST_F(GlicMetricsTest, SegmentationOsButtonAttachedText) {
controller_->showing_ = true;
controller_->attached_ = true;
metrics_->OnGlicWindowOpen(/*attached=*/true, InvocationSource::kOsButton);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kText);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped();
metrics_->OnGlicWindowClose();
histogram_tester_.ExpectTotalCount("Glic.Response.Segmentation", 1);
histogram_tester_.ExpectBucketCount("Glic.Response.Segmentation",
/*kOsButtonAttachedText=*/1,
/*expected_count=*/1);
}
TEST_F(GlicMetricsTest, SegmentationChroMenuDetachedAudio) {
controller_->showing_ = true;
controller_->attached_ = false;
metrics_->OnGlicWindowOpen(/*attached=*/false, InvocationSource::kChroMenu);
metrics_->OnUserInputSubmitted(mojom::WebClientMode::kAudio);
metrics_->OnResponseStarted();
metrics_->OnResponseStopped();
metrics_->OnGlicWindowClose();
histogram_tester_.ExpectTotalCount("Glic.Response.Segmentation", 1);
histogram_tester_.ExpectBucketCount("Glic.Response.Segmentation",
/*kChroMenuDetachedAudio=*/32,
/*expected_count=*/1);
}
TEST_F(GlicMetricsTest, ImpressionBeforeFre) {
profile_.GetPrefs()->SetBoolean(prefs::kGlicCompletedFre, false);
task_environment_.FastForwardBy(base::Minutes(16));
histogram_tester_.ExpectTotalCount("Glic.EntryPoint.Impression", 1);
histogram_tester_.ExpectBucketCount("Glic.EntryPoint.Impression",
/*kBeforeFre=*/0, /*expected_count=*/1);
}
TEST_F(GlicMetricsTest, ImpressionAfterFre) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{
features::kGlic,
features::kTabstripComboButton,
},
{});
task_environment_.FastForwardBy(base::Minutes(16));
histogram_tester_.ExpectTotalCount("Glic.EntryPoint.Impression", 1);
histogram_tester_.ExpectBucketCount("Glic.EntryPoint.Impression",
/*kAfterFreGlicEnabled=*/1,
/*expected_count=*/1);
}
TEST_F(GlicMetricsTest, EnablingChanged) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{
features::kGlic,
features::kTabstripComboButton,
},
{});
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 0);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 0);
profile_.GetPrefs()->SetInteger(
prefs::kGlicSettingsPolicy,
static_cast<int>(glic::prefs::SettingsPolicyState::kEnabled));
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 0);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 1);
profile_.GetPrefs()->SetInteger(
prefs::kGlicSettingsPolicy,
static_cast<int>(glic::prefs::SettingsPolicyState::kDisabled));
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Disabled"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("Glic.Enabled"), 1);
}
} // namespace
} // namespace glic