chromium/chrome/browser/banners/android/ambient_badge_manager_browsertest.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "base/auto_reset.h"
#include "base/functional/callback_forward.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/banners/android/chrome_app_banner_manager_android.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/android/android_browser_test.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "components/segmentation_platform/public/segment_selection_result.h"
#include "components/segmentation_platform/public/testing/mock_segmentation_platform_service.h"
#include "components/site_engagement/content/site_engagement_score.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/webapps/browser/android/add_to_homescreen_params.h"
#include "components/webapps/browser/android/ambient_badge_manager.h"
#include "components/webapps/browser/android/app_banner_manager_android.h"
#include "components/webapps/browser/android/bottomsheet/pwa_bottom_sheet_controller.h"
#include "components/webapps/browser/banners/app_banner_settings_helper.h"
#include "components/webapps/browser/banners/install_banner_config.h"
#include "components/webapps/browser/features.h"
#include "components/webapps/browser/installable/installable_data.h"
#include "components/webapps/browser/installable/ml_installability_promoter.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/base/url_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"

using ::base::test::RunOnceCallback;
using testing::_;

namespace webapps {

class TestAmbientBadgeManager : public AmbientBadgeManager {
 public:
  explicit TestAmbientBadgeManager(
      content::WebContents* web_contents,
      segmentation_platform::SegmentationPlatformService*
          segmentation_platform_service,
      PrefService* prefs)
      : AmbientBadgeManager(*web_contents,
                            segmentation_platform_service,
                            *prefs) {}

  TestAmbientBadgeManager(const TestAmbientBadgeManager&) = delete;
  TestAmbientBadgeManager& operator=(const TestAmbientBadgeManager&) = delete;

  ~TestAmbientBadgeManager() override = default;

  void WaitForState(State target, base::OnceClosure on_done) {
    target_state_ = target;
    on_done_ = std::move(on_done);
  }

 protected:
  void UpdateState(State state) override {
    AmbientBadgeManager::UpdateState(state);
    if (state == target_state_ && on_done_) {
      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
          FROM_HERE, std::move(on_done_));
    }
  }

 private:
  State target_state_;
  base::OnceClosure on_done_;
};

class TestAppBannerManager : public AppBannerManagerAndroid {
 public:
  explicit TestAppBannerManager(content::WebContents* web_contents)
      : AppBannerManagerAndroid(
            web_contents,
            std::make_unique<ChromeAppBannerManagerAndroid>(*web_contents)) {}

  explicit TestAppBannerManager(
      content::WebContents* web_contents,
      segmentation_platform::SegmentationPlatformService*
          segmentation_platform_service)
      : AppBannerManagerAndroid(
            web_contents,
            std::make_unique<ChromeAppBannerManagerAndroid>(*web_contents)),
        mock_segmentation_(segmentation_platform_service) {}

  TestAppBannerManager(const TestAppBannerManager&) = delete;
  TestAppBannerManager& operator=(const TestAppBannerManager&) = delete;

  ~TestAppBannerManager() override = default;

  void WaitForAmbientBadgeState(AmbientBadgeManager::State target,
                                base::OnceClosure on_done) {
    target_badge_state_ = target;
    on_badge_done_ = std::move(on_done);
  }

  bool TriggeringDisabledForTesting() const override { return false; }

  TestAmbientBadgeManager* GetBadgeManagerForTest() {
    return ambient_badge_test_.get();
  }

 protected:
  Profile* profile() {
    return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
  }

  void MaybeShowAmbientBadge(
      const InstallBannerConfig& install_config) override {
    ambient_badge_test_ = std::make_unique<TestAmbientBadgeManager>(
        web_contents(), mock_segmentation_, profile()->GetPrefs());

    ambient_badge_test_->WaitForState(target_badge_state_,
                                      std::move(on_badge_done_));

    std::unique_ptr<AddToHomescreenParams> a2hs_params =
        AppBannerManagerAndroid::CreateAddToHomescreenParams(
            install_config, native_java_app_data_for_testing(),
            InstallableMetrics::GetInstallSource(
                &GetWebContents(), InstallTrigger::AMBIENT_BADGE));

    ambient_badge_test_->MaybeShow(
        install_config.validated_url, install_config.GetWebOrNativeAppName(),
        install_config.GetWebOrNativeAppIdentifier(), std::move(a2hs_params),
        // TODO(b/323192242): See if these callbacks can be merged.
        base::BindOnce(&AppBannerManagerAndroid::ShowBannerFromBadge,
                       GetAndroidWeakPtr(), install_config),
        // Create the params, then pass them to MaybeShow.
        base::BindOnce(&AppBannerManagerAndroid::CreateAddToHomescreenParams,
                       install_config, native_java_app_data_for_testing())
            .Then(base::BindOnce(
                &PwaBottomSheetController::MaybeShow, web_contents(),
                install_config.web_app_data, /*expand_sheet=*/false,
                base::BindRepeating(&TestAppBannerManager::OnInstallEvent,
                                    GetAndroidWeakPtr(),
                                    install_config.validated_url))));
  }

 private:
  std::unique_ptr<TestAmbientBadgeManager> ambient_badge_test_;
  raw_ptr<segmentation_platform::SegmentationPlatformService>
      mock_segmentation_;
  AmbientBadgeManager::State target_badge_state_;
  base::OnceClosure on_badge_done_;
};

class AmbientBadgeManagerBrowserTest : public AndroidBrowserTest {
 public:
  AmbientBadgeManagerBrowserTest()
      : disable_banner_trigger_(&test::g_disable_banner_triggering_for_testing,
                                true) {}

  AmbientBadgeManagerBrowserTest(const AmbientBadgeManagerBrowserTest&) =
      delete;
  AmbientBadgeManagerBrowserTest& operator=(
      const AmbientBadgeManagerBrowserTest&) = delete;

  ~AmbientBadgeManagerBrowserTest() override = default;

  void SetUpOnMainThread() override {
    ASSERT_TRUE(embedded_test_server()->Start());
    site_engagement::SiteEngagementScore::SetParamValuesForTesting();

    app_banner_manager_ = std::make_unique<TestAppBannerManager>(
        web_contents(), &mock_segmentation_service_);
    AndroidBrowserTest::SetUpOnMainThread();
  }

  segmentation_platform::ClassificationResult GetClassificationResult(
      std::string label) {
    segmentation_platform::ClassificationResult result(
        segmentation_platform::PredictionStatus::kSucceeded);
    result.ordered_labels.emplace_back(label);
    return result;
  }

 protected:
  content::WebContents* web_contents() {
    return chrome_test_utils::GetActiveWebContents(this);
  }

  AmbientBadgeManager* GetAmbientBadgeManager() {
    return app_banner_manager_->GetBadgeManagerForTest();
  }

  void ResetEngagementForUrl(const GURL& url, double score) {
    site_engagement::SiteEngagementService* service =
        site_engagement::SiteEngagementService::Get(
            Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
    service->ResetBaseScoreForURL(url, score);
  }

  void SetSegmentationResult(std::string label) {
    EXPECT_CALL(mock_segmentation_service_, GetClassificationResult(_, _, _, _))
        .WillOnce(RunOnceCallback<3>(GetClassificationResult(label)));
  }

  void RunTest(const GURL& url, AmbientBadgeManager::State expected_state) {
    ResetEngagementForUrl(url, 10);

    base::RunLoop waiter;

    app_banner_manager_->WaitForAmbientBadgeState(expected_state,
                                                  waiter.QuitClosure());
    ASSERT_TRUE(content::NavigateToURL(web_contents(), url));

    waiter.Run();
  }

 private:
  // Disable the banners in the browser so it won't interfere with the test.
  base::AutoReset<bool> disable_banner_trigger_;

  std::unique_ptr<TestAppBannerManager> app_banner_manager_;
  segmentation_platform::MockSegmentationPlatformService
      mock_segmentation_service_;
};

IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest, ShowAmbientBadge) {
  SetSegmentationResult(MLInstallabilityPromoter::kShowInstallPromptLabel);

  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
          AmbientBadgeManager::State::kShowing);
}

IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest,
                       BlockedBySegmentationResult) {
  SetSegmentationResult(MLInstallabilityPromoter::kDontShowLabel);

  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
          AmbientBadgeManager::State::kSegmentationBlock);
}

IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest, NoServiceWorker) {
  SetSegmentationResult(MLInstallabilityPromoter::kShowInstallPromptLabel);

  RunTest(embedded_test_server()->GetURL(
              "/banners/manifest_no_service_worker.html"),
          AmbientBadgeManager::State::kShowing);
}

IN_PROC_BROWSER_TEST_F(AmbientBadgeManagerBrowserTest,
                       BlockedIfDismissRecently) {
  SetSegmentationResult(MLInstallabilityPromoter::kShowInstallPromptLabel);
  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
          AmbientBadgeManager::State::kShowing);

  // Explicitly dismiss the badge.
  GetAmbientBadgeManager()->BadgeDismissed();  // IN-TEST
  EXPECT_EQ(AmbientBadgeManager::State::kDismissed,
            GetAmbientBadgeManager()->state());  // IN-TEST

  // Badge blocked because it was recently dismissed.
  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
          AmbientBadgeManager::State::kBlocked);

  // Badge can show again after 91 days.
  SetSegmentationResult(MLInstallabilityPromoter::kShowInstallPromptLabel);
  webapps::AppBannerManager::SetTimeDeltaForTesting(91);
  RunTest(embedded_test_server()->GetURL("/banners/manifest_test_page.html"),
          AmbientBadgeManager::State::kShowing);
}

}  // namespace webapps