blob: f7cefebe5623ebea9e90611e1467ddfe8b5deaa9 [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/autocomplete/chrome_aim_eligibility_service.h"
#include <algorithm>
#include <tuple>
#include <vector>
#include "base/callback_list.h"
#include "base/functional/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "chrome/browser/autocomplete/aim_eligibility_service_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/chrome_signin_client_test_util.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/scoped_browser_locale.h"
#include "chrome/test/base/search_test_utils.h"
#include "components/omnibox/browser/aim_eligibility_service.h"
#include "components/omnibox/browser/omnibox_prefs.h"
#include "components/prefs/pref_service.h"
#include "components/search/search.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_data.h"
#include "components/search_engines/template_url_service.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/variations/service/variations_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/url_loader_interceptor.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/omnibox_proto/aim_eligibility_response.pb.h"
// Helper function to provide eligibility response for intercepted requests.
bool OnRequest(omnibox::AimEligibilityResponse response,
content::URLLoaderInterceptor::RequestParams* params) {
const GURL& url = params->url_request.url;
if (!url.DomainIs("google.com") || url.path() != "/async/folae" ||
url.query() != "async=_fmt:pb") {
return false;
}
std::string response_string;
response.SerializeToString(&response_string);
content::URLLoaderInterceptor::WriteResponse(
"HTTP/1.1 200 OK\nContent-Type: application/x-protobuf\n\n",
response_string, params->client.get());
return true;
}
// Helper class to observe IdentityManager.
class IdentityManagerObserverHelper : public signin::IdentityManager::Observer {
public:
explicit IdentityManagerObserverHelper(
signin::IdentityManager* identity_manager) {
identity_manager_observation_.Observe(identity_manager);
}
// signin::IdentityManager::Observer:
void OnAccountsInCookieUpdated(
const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
const GoogleServiceAuthError& error) override {
if (!accounts_updated_future_.IsReady()) {
accounts_updated_future_.SetValue();
}
}
bool WaitForAccountsInCookieUpdated() {
return accounts_updated_future_.Wait();
}
private:
base::ScopedObservation<signin::IdentityManager,
signin::IdentityManager::Observer>
identity_manager_observation_{this};
base::test::TestFuture<void> accounts_updated_future_;
};
// Friend class to access private members of AimEligibilityService for testing.
class AimEligibilityServiceFriend {
public:
using EligibilityRequestStatus =
AimEligibilityService::EligibilityRequestStatus;
};
class ChromeAimEligibilityServiceBrowserTest
: public InProcessBrowserTest,
public ::testing::WithParamInterface<
std::tuple<std::string, std::string, bool, bool, bool, bool, bool>> {
public:
ChromeAimEligibilityServiceBrowserTest() = default;
~ChromeAimEligibilityServiceBrowserTest() override = default;
signin::IdentityTestEnvironment* identity_test_env() {
return identity_test_env_adaptor_->identity_test_env();
}
network::TestURLLoaderFactory* test_url_loader_factory() {
return signin_client_with_url_loader_helper_.test_url_loader_factory();
}
protected:
void SetUp() override {
auto [locale, country, server_eligibility_enabled_all, allowed_by_policy,
is_google_dse, is_server_eligible, is_pdf_upload_eligible] =
GetParam();
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
// Needed for bots with field trial testing configs explicitly disabled.
enabled_features.push_back(
omnibox::kAimServerEligibilityChangedNotification);
enabled_features.push_back(omnibox::kAimServerEligibilityEnabledEn);
if (server_eligibility_enabled_all) {
enabled_features.push_back(omnibox::kAimServerEligibilityEnabled);
} else {
disabled_features.push_back(omnibox::kAimServerEligibilityEnabled);
}
feature_list_.InitWithFeatures(enabled_features, disabled_features);
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
auto [locale, country, server_eligibility_enabled_all, allowed_by_policy,
is_google_dse, is_server_eligible, is_pdf_upload_eligible] =
GetParam();
// Set up locale and country.
scoped_browser_locale_ = std::make_unique<ScopedBrowserLocale>(locale);
g_browser_process->variations_service()->OverrideStoredPermanentCountry(
country);
// Set up the AIM policy pref; 0 = allowed, 1 = disallowed.
browser()->profile()->GetPrefs()->SetInteger(omnibox::kAIModeSettings,
allowed_by_policy ? 0 : 1);
// Set up the default search engine.
TemplateURLService* template_url_service =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service);
TemplateURLData template_url_data;
if (is_google_dse) {
template_url_data.SetShortName(u"Google");
template_url_data.SetKeyword(u"google.com");
template_url_data.SetURL("https://www.google.com/search?q={searchTerms}");
} else {
template_url_data.SetShortName(u"Bing");
template_url_data.SetKeyword(u"bing.com");
template_url_data.SetURL("https://www.bing.com/search?q={searchTerms}");
}
auto template_url = std::make_unique<TemplateURL>(template_url_data);
auto* template_url_ptr = template_url_service->Add(std::move(template_url));
template_url_service->SetUserSelectedDefaultSearchProvider(
template_url_ptr);
// Set the adaptor that supports signin::IdentityTestEnvironment.
identity_test_env_adaptor_ =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(
browser()->profile());
// Set the testing factory for AimEligibilityService.
AimEligibilityServiceFactory::GetInstance()->SetTestingFactory(
browser()->profile(),
base::BindOnce(AimEligibilityServiceFactory::GetDefaultFactory()));
InProcessBrowserTest::SetUpOnMainThread();
}
void TearDownOnMainThread() override {
scoped_browser_locale_.reset();
InProcessBrowserTest::TearDownOnMainThread();
}
void SetUpInProcessBrowserTestFixture() override {
signin_client_with_url_loader_helper_.SetUp();
create_services_subscription_ =
BrowserContextDependencyManager::GetInstance()
->RegisterCreateServicesCallbackForTesting(
base::BindRepeating(&ChromeAimEligibilityServiceBrowserTest::
OnWillCreateBrowserContextServices));
}
static void OnWillCreateBrowserContextServices(
content::BrowserContext* context) {
// Set up IdentityTestEnvironment.
IdentityTestEnvironmentProfileAdaptor::
SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
}
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<ScopedBrowserLocale> scoped_browser_locale_;
ChromeSigninClientWithURLLoaderHelper signin_client_with_url_loader_helper_;
base::CallbackListSubscription create_services_subscription_;
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
identity_test_env_adaptor_;
};
INSTANTIATE_TEST_SUITE_P(,
ChromeAimEligibilityServiceBrowserTest,
::testing::Combine(
// Values for the locale.
::testing::Values("en-US", "es-MX"),
// Values for the country.
::testing::Values("us", "ca"),
// Values for server eligibility enabled.
::testing::Values(true, false),
// Values for allowed by policy.
::testing::Values(true, false),
// Values for Google DSE.
::testing::Values(true, false),
// Values for server response eligibility.
::testing::Values(true, false),
// Values for Pdf server response eligibility.
::testing::Values(true, false)));
IN_PROC_BROWSER_TEST_P(ChromeAimEligibilityServiceBrowserTest,
ComprehensiveEligibilityTest) {
auto [locale, country, server_eligibility_enabled_all, allowed_by_policy,
is_google_dse, is_server_eligible, is_pdf_upload_eligible] = GetParam();
// Enabling `AimServerEligibilityEnabledEn` overrides server eligibility for
// English locales
const bool server_eligibility_enabled =
server_eligibility_enabled_all ||
base::StartsWith(locale, "en", base::CompareCase::SENSITIVE);
// Handle the eligibility request on startup with a custom response.
omnibox::AimEligibilityResponse response;
response.set_is_eligible(is_server_eligible);
response.set_is_pdf_upload_eligible(is_pdf_upload_eligible);
auto url_loader_interceptor = std::make_unique<content::URLLoaderInterceptor>(
base::BindRepeating(&OnRequest, response));
// Test service startup.
{
base::HistogramTester histogram_tester;
auto* service =
AimEligibilityServiceFactory::GetForProfile(browser()->profile());
base::test::TestFuture<void> eligibility_changed_future;
auto eligibility_subscription = service->RegisterEligibilityChangedCallback(
eligibility_changed_future.GetRepeatingCallback());
// Test country and locale detection.
EXPECT_TRUE(service->IsCountry(country));
EXPECT_TRUE(service->IsLanguage(locale.substr(0, 2)));
// Test IsServerEligibilityEnabled().
EXPECT_EQ(service->IsServerEligibilityEnabled(),
server_eligibility_enabled);
// Wait for the eligibility change callback to be invoked, if applicable.
if (is_google_dse) {
EXPECT_TRUE(eligibility_changed_future.Wait());
} else {
EXPECT_FALSE(eligibility_changed_future.IsReady());
}
// Test IsAimLocallyEligible().
bool expected_local_eligibility = is_google_dse && allowed_by_policy;
EXPECT_EQ(service->IsAimLocallyEligible(), expected_local_eligibility);
// Test IsAimEligible().
bool expected_eligible =
expected_local_eligibility &&
(!server_eligibility_enabled || is_server_eligible);
EXPECT_EQ(service->IsAimEligible(), expected_eligible);
// Test IsPdfUploadEligible().
bool expected_pdf_upload_eligible =
expected_eligible &&
(!server_eligibility_enabled || is_pdf_upload_eligible);
EXPECT_EQ(service->IsPdfUploadEligible(), expected_pdf_upload_eligible);
// Verify histograms for the request on startup.
if (is_google_dse) {
// Startup sliced histograms.
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityRequestStatus.Startup", 2);
histogram_tester.ExpectBucketCount(
"Omnibox.AimEligibility.EligibilityRequestStatus.Startup",
AimEligibilityServiceFriend::EligibilityRequestStatus::kSent, 1);
histogram_tester.ExpectBucketCount(
"Omnibox.AimEligibility.EligibilityRequestStatus.Startup",
AimEligibilityServiceFriend::EligibilityRequestStatus::kSuccess, 1);
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityResponseCode.Startup", 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponseCode.Startup", 200, 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponse.Startup.is_eligible",
is_server_eligible, 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponse.Startup.is_pdf_upload_"
"eligible",
is_pdf_upload_eligible, 1);
// Unsliced histograms.
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityRequestStatus", 2);
histogram_tester.ExpectBucketCount(
"Omnibox.AimEligibility.EligibilityRequestStatus",
AimEligibilityServiceFriend::EligibilityRequestStatus::kSent, 1);
histogram_tester.ExpectBucketCount(
"Omnibox.AimEligibility.EligibilityRequestStatus",
AimEligibilityServiceFriend::EligibilityRequestStatus::kSuccess, 1);
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityResponseCode", 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponseCode", 200, 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponse.is_eligible",
is_server_eligible, 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponse.is_pdf_upload_eligible",
is_pdf_upload_eligible, 1);
// Response change histograms.
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponseChange.is_eligible",
is_server_eligible, 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponseChange.is_pdf_upload_"
"eligible",
is_pdf_upload_eligible, 1);
} else {
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityRequestStatus.Startup", 0);
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityResponseCode.Startup", 0);
}
}
url_loader_interceptor.reset();
// Test changes to the accounts in the cookie jar.
{
base::HistogramTester histogram_tester;
// Handle the eligibility request with a custom response.
response.set_is_eligible(!is_server_eligible);
response.set_is_pdf_upload_eligible(!is_pdf_upload_eligible);
url_loader_interceptor = std::make_unique<content::URLLoaderInterceptor>(
base::BindRepeating(&OnRequest, response));
auto* service =
AimEligibilityServiceFactory::GetForProfile(browser()->profile());
base::test::TestFuture<void> eligibility_changed_future;
auto eligibility_subscription = service->RegisterEligibilityChangedCallback(
eligibility_changed_future.GetRepeatingCallback());
// Simulate a change to the account in the cookie jar.
auto* identity_manager = identity_test_env()->identity_manager();
IdentityManagerObserverHelper identity_observer(identity_manager);
signin::MakeAccountAvailable(
identity_manager,
signin::AccountAvailabilityOptionsBuilder(test_url_loader_factory())
.WithCookie()
.AsPrimary(signin::ConsentLevel::kSignin)
.Build("test@email.com"));
EXPECT_TRUE(identity_observer.WaitForAccountsInCookieUpdated());
// Wait for the eligibility change callback to be invoked, if applicable.
if (is_google_dse) {
EXPECT_TRUE(eligibility_changed_future.Wait());
} else {
EXPECT_FALSE(eligibility_changed_future.IsReady());
}
// Test IsAimLocallyEligible().
bool expected_local_eligibility = is_google_dse && allowed_by_policy;
EXPECT_EQ(service->IsAimLocallyEligible(), expected_local_eligibility);
// Test IsAimEligible().
bool expected_eligible =
expected_local_eligibility &&
(!server_eligibility_enabled || !is_server_eligible);
EXPECT_EQ(service->IsAimEligible(), expected_eligible);
// Test IsPdfUploadEligible().
bool expected_pdf_upload_eligible =
expected_eligible &&
(!server_eligibility_enabled || !is_pdf_upload_eligible);
EXPECT_EQ(service->IsPdfUploadEligible(), expected_pdf_upload_eligible);
// Verify histograms.
if (is_google_dse) {
// CookieChange sliced histograms.
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityRequestStatus.CookieChange", 2);
histogram_tester.ExpectBucketCount(
"Omnibox.AimEligibility.EligibilityRequestStatus.CookieChange",
AimEligibilityServiceFriend::EligibilityRequestStatus::kSent, 1);
histogram_tester.ExpectBucketCount(
"Omnibox.AimEligibility.EligibilityRequestStatus.CookieChange",
AimEligibilityServiceFriend::EligibilityRequestStatus::kSuccess, 1);
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityResponseCode.CookieChange", 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponseCode.CookieChange", 200,
1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponse.CookieChange.is_eligible",
!is_server_eligible, 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponse.CookieChange.is_pdf_"
"upload_eligible",
!is_pdf_upload_eligible, 1);
// Unsliced histograms.
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityRequestStatus", 2);
histogram_tester.ExpectBucketCount(
"Omnibox.AimEligibility.EligibilityRequestStatus",
AimEligibilityServiceFriend::EligibilityRequestStatus::kSent, 1);
histogram_tester.ExpectBucketCount(
"Omnibox.AimEligibility.EligibilityRequestStatus",
AimEligibilityServiceFriend::EligibilityRequestStatus::kSuccess, 1);
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityResponseCode", 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponseCode", 200, 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponse.is_eligible",
!is_server_eligible, 1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponse.is_pdf_upload_eligible",
!is_pdf_upload_eligible, 1);
// Response change histograms.
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponseChange.is_eligible", true,
1);
histogram_tester.ExpectUniqueSample(
"Omnibox.AimEligibility.EligibilityResponseChange.is_pdf_upload_"
"eligible",
true, 1);
} else {
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityRequestStatus.CookieChange", 0);
histogram_tester.ExpectTotalCount(
"Omnibox.AimEligibility.EligibilityResponseCode.CookieChange", 0);
}
}
}