blob: d433311db6b19d6e6a67f6402f13732cbcaf850c [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/segmentation_platform/segmentation_platform_service_factory.h"
#include "chrome/browser/segmentation_platform/ukm_data_manager_test_utils.h"
#include "chrome/browser/segmentation_platform/ukm_database_client.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_observer.h"
#include "components/prefs/pref_service.h"
#include "components/segmentation_platform/internal/constants.h"
#include "components/segmentation_platform/internal/execution/mock_model_provider.h"
#include "components/segmentation_platform/public/config.h"
#include "components/segmentation_platform/public/features.h"
#include "components/segmentation_platform/public/model_provider.h"
#include "components/segmentation_platform/public/segment_selection_result.h"
#include "components/segmentation_platform/public/segmentation_platform_service.h"
#include "components/ukm/ukm_service.h"
#include "content/public/test/browser_test.h"
namespace segmentation_platform {
using ::base::test::RunOnceCallback;
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SaveArg;
constexpr SegmentId kSegmentId =
SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT;
constexpr char kSqlFeatureQuery[] = "SELECT COUNT(*) from metrics";
class SegmentationPlatformTest : public InProcessBrowserTest {
public:
SegmentationPlatformTest() {
feature_list_.InitWithFeaturesAndParameters(
{base::test::FeatureRefAndParams(features::kSegmentationPlatformFeature,
{}),
base::test::FeatureRefAndParams(
features::kSegmentationStructuredMetricsFeature, {}),
base::test::FeatureRefAndParams(
features::kSegmentationPlatformUkmEngine, {}),
base::test::FeatureRefAndParams(
features::kSegmentationPlatformLowEngagementFeature,
{{"enable_default_model", "true"}})},
{});
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch("segmentation-platform-refresh-results");
}
bool HasResultPref(base::StringPiece segmentation_key) {
const base::Value::Dict& dictionary =
browser()->profile()->GetPrefs()->GetDict(kSegmentationResultPref);
return !!dictionary.FindByDottedPath(segmentation_key);
}
void OnResultPrefUpdated() {
if (!wait_for_pref_callback_.is_null() &&
HasResultPref(kChromeLowUserEngagementSegmentationKey)) {
std::move(wait_for_pref_callback_).Run();
}
}
void WaitForPrefUpdate() {
if (HasResultPref(kChromeLowUserEngagementSegmentationKey))
return;
base::RunLoop wait_for_pref;
wait_for_pref_callback_ = wait_for_pref.QuitClosure();
pref_registrar_.Init(browser()->profile()->GetPrefs());
pref_registrar_.Add(
kSegmentationResultPref,
base::BindRepeating(&SegmentationPlatformTest::OnResultPrefUpdated,
weak_ptr_factory_.GetWeakPtr()));
wait_for_pref.Run();
pref_registrar_.RemoveAll();
}
void WaitForPlatformInit() {
base::RunLoop wait_for_init;
SegmentationPlatformService* service = segmentation_platform::
SegmentationPlatformServiceFactory::GetForProfile(browser()->profile());
while (!service->IsPlatformInitialized()) {
wait_for_init.RunUntilIdle();
}
}
void ExpectSegmentSelectionResult(const std::string& segmentation_key,
bool result_expected) {
SegmentationPlatformService* service = segmentation_platform::
SegmentationPlatformServiceFactory::GetForProfile(browser()->profile());
base::RunLoop wait_for_segment;
service->GetSelectedSegment(
segmentation_key, base::BindOnce(
[](bool result_expected, base::OnceClosure quit,
const SegmentSelectionResult& result) {
EXPECT_EQ(result_expected, result.is_ready);
std::move(quit).Run();
},
result_expected, wait_for_segment.QuitClosure()));
wait_for_segment.Run();
}
protected:
base::test::ScopedFeatureList feature_list_;
PrefChangeRegistrar pref_registrar_;
base::OnceClosure wait_for_pref_callback_;
base::WeakPtrFactory<SegmentationPlatformTest> weak_ptr_factory_{this};
};
IN_PROC_BROWSER_TEST_F(SegmentationPlatformTest, PRE_RunDefaultModel) {
WaitForPlatformInit();
// The default model is executed and result stored in prefs.
WaitForPrefUpdate();
// The result from platform is not available since it only returns result from
// a previous session.
ExpectSegmentSelectionResult(kChromeLowUserEngagementSegmentationKey,
/*result_expected=*/false);
}
IN_PROC_BROWSER_TEST_F(SegmentationPlatformTest, RunDefaultModel) {
WaitForPlatformInit();
// Result is available from previous session's selection.
ExpectSegmentSelectionResult(kChromeLowUserEngagementSegmentationKey,
/*result_expected=*/true);
// This session runs default model and updates again.
WaitForPrefUpdate();
}
class SegmentationPlatformUkmModelTest : public SegmentationPlatformTest {
public:
SegmentationPlatformUkmModelTest() : utils_(&ukm_recorder_) {}
void CreatedBrowserMainParts(content::BrowserMainParts* parts) override {
InProcessBrowserTest::CreatedBrowserMainParts(parts);
utils_.PreProfileInit({kSegmentId});
}
void PreRunTestOnMainThread() override {
SegmentationPlatformTest::PreRunTestOnMainThread();
utils_.set_history_service(HistoryServiceFactory::GetForProfile(
browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS));
}
protected:
ukm::TestUkmRecorder ukm_recorder_;
UkmDataManagerTestUtils utils_;
};
// This test is disabled in CrOS because CrOS creates a signin profile that uses
// incognito mode. This disables the segmentation platform data collection.
// TODO(ssid): Fix this test for CrOS by waiting for signin profile to be
// deleted at startup before adding metrics.
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_PRE_RunUkmBasedModel DISABLED_PRE_RunUkmBasedModel
#define MAYBE_RunUkmBasedModel DISABLED_RunUkmBasedModel
#else
#define MAYBE_PRE_RunUkmBasedModel PRE_RunUkmBasedModel
#define MAYBE_RunUkmBasedModel RunUkmBasedModel
#endif
IN_PROC_BROWSER_TEST_F(SegmentationPlatformUkmModelTest,
MAYBE_PRE_RunUkmBasedModel) {
const GURL kUrl1("https://www.url1.com");
MockModelProvider* provider = utils_.GetDefaultOverride(kSegmentId);
EXPECT_CALL(*provider, ExecuteModelWithInput(_, _))
.WillRepeatedly(Invoke([&](const ModelProvider::Request& inputs,
ModelProvider::ExecutionCallback callback) {
// There are no UKM metrics written to the database, count = 0.
EXPECT_EQ(ModelProvider::Request({0}), inputs);
std::move(callback).Run(ModelProvider::Response(1, 0.5));
}));
WaitForPlatformInit();
utils_.WaitForModelRequestAndUpdateWith(
kSegmentId, utils_.GetSamplePageLoadMetadata(kSqlFeatureQuery));
// Wait for the default model to run and save results to prefs.
WaitForPrefUpdate();
// Record page load UKM that should be recorded in the database, persisted
// across sessions.
utils_.RecordPageLoadUkm(kUrl1, base::Time::Now());
while (!utils_.IsUrlInDatabase(kUrl1)) {
base::RunLoop().RunUntilIdle();
}
}
IN_PROC_BROWSER_TEST_F(SegmentationPlatformUkmModelTest,
MAYBE_RunUkmBasedModel) {
const GURL kUrl1("https://www.url1.com");
MockModelProvider* provider = utils_.GetDefaultOverride(kSegmentId);
EXPECT_CALL(*provider, ExecuteModelWithInput(_, _))
.WillRepeatedly(Invoke([](const ModelProvider::Request& inputs,
ModelProvider::ExecutionCallback callback) {
// Expected input is 2 since we recorded 2 UKM metrics in the previous
// session.
EXPECT_EQ(ModelProvider::Request({2}), inputs);
std::move(callback).Run(ModelProvider::Response(1, 0.5));
}));
WaitForPlatformInit();
// Verify that the URL recorded in last session is still in database.
EXPECT_TRUE(utils_.IsUrlInDatabase(kUrl1));
// Result is available from previous session's selection.
ExpectSegmentSelectionResult(kChromeLowUserEngagementSegmentationKey,
/*result_expected=*/true);
utils_.WaitForModelRequestAndUpdateWith(
kSegmentId, utils_.GetSamplePageLoadMetadata(kSqlFeatureQuery));
WaitForPrefUpdate();
}
} // namespace segmentation_platform