| // 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 |