chromium/ash/system/human_presence/snooping_protection_notification_blocker_unittest.cc

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

#include "ash/system/human_presence/snooping_protection_notification_blocker.h"

#include <memory>
#include <string>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "ash/public/cpp/test/test_system_tray_client.h"
#include "ash/session/session_controller_impl.h"
#include "ash/session/test_session_controller_client.h"
#include "ash/shell.h"
#include "ash/system/human_presence/snooping_protection_controller.h"
#include "ash/system/human_presence/snooping_protection_notification_blocker_internal.h"
#include "ash/system/notification_center/message_center_controller.h"
#include "ash/system/network/sms_observer.h"
#include "ash/system/notification_center/notification_center_tray.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/test/ash_test_base.h"
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/ash/components/dbus/hps/hps_service.pb.h"
#include "chromeos/ash/components/dbus/human_presence/fake_human_presence_dbus_client.h"
#include "chromeos/ash/components/dbus/human_presence/human_presence_dbus_client.h"
#include "components/account_id/account_id.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/app_update.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_types.h"
#include "ui/message_center/public/cpp/notifier_id.h"
#include "url/gurl.h"

namespace ash {

namespace {

// Enables or disables the user pref for the entire feature.
void SetSnoopingPref(bool enabled) {
  Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
      prefs::kSnoopingProtectionEnabled, enabled);
  base::RunLoop().RunUntilIdle();
}

// Enables or disables the user pref for notification blocking.
void SetBlockerPref(bool enabled) {
  Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
      prefs::kSnoopingProtectionNotificationSuppressionEnabled, enabled);
  base::RunLoop().RunUntilIdle();
}

// Add a notification to the message center. An empty notifier title creates a
// system notification.
void AddNotification(const std::string& notification_id,
                     const std::u16string& notifier_title) {
  const message_center::NotifierId notifier_id =
      notifier_title.empty()
          ? message_center::NotifierId(
                message_center::NotifierType::SYSTEM_COMPONENT, "system",
                NotificationCatalogName::kHPSNotify)
          : message_center::NotifierId(/*url=*/GURL(), notifier_title,
                                       /*web_app_id=*/std::nullopt);

  message_center::MessageCenter::Get()->AddNotification(
      std::make_unique<message_center::Notification>(
          message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
          u"test-title", u"test-message", /*icon=*/ui::ImageModel(),
          /*display_source=*/std::u16string(), /*origin_url=*/GURL(),
          notifier_id, message_center::RichNotificationData(),
          base::MakeRefCounted<message_center::NotificationDelegate>()));
}

// Removes the notification with the given ID.
void RemoveNotification(const std::string& notification_id) {
  message_center::MessageCenter::Get()->RemoveNotification(notification_id,
                                                           /*by_user=*/true);
}

// Returns the number of popup notifications that are currently visible in the
// message center.
size_t VisiblePopupCount() {
  return message_center::MessageCenter::Get()->GetPopupNotifications().size();
}

// Returns the number of (popup or non-popup) notifications that are currently
// visible in the message center queue.
size_t VisibleNotificationCount() {
  return message_center::MessageCenter::Get()->GetVisibleNotifications().size();
}

// Returns true if the HPS notify informational popup is popped-up.
bool InfoPopupVisible() {
  return message_center::MessageCenter::Get()->FindPopupNotificationById(
             SnoopingProtectionNotificationBlocker::kInfoNotificationId) !=
         nullptr;
}

// Returns the index at which the given substring appears in the informational
// popup's message, or npos otherwise.
size_t PositionInInfoPopupMessage(const std::u16string& substr) {
  const message_center::Notification* notification =
      message_center::MessageCenter::Get()->FindPopupNotificationById(
          SnoopingProtectionNotificationBlocker::kInfoNotificationId);
  return notification ? notification->message().find(substr)
                      : std::u16string::npos;
}

// A blocker that blocks only a popup with the given ID.
class IdPopupBlocker : public message_center::NotificationBlocker {
 public:
  explicit IdPopupBlocker(message_center::MessageCenter* message_center)
      : NotificationBlocker(message_center) {}
  IdPopupBlocker(const IdPopupBlocker&) = delete;
  IdPopupBlocker& operator=(const IdPopupBlocker&) = delete;
  ~IdPopupBlocker() override = default;

  void SetTargetId(const std::string& target_id) {
    target_id_ = target_id;
    NotifyBlockingStateChanged();
  }

  // message_center::NotificationBlocker:
  bool ShouldShowNotificationAsPopup(
      const message_center::Notification& notification) const override {
    return notification.id() != target_id_;
  }

 private:
  std::string target_id_;
};

// An app registry cache that can be used to register test app names.
class FakeAppRegistryCache {
 public:
  // The following methods match the interface of apps::AppRegistryCacheWrapper.

  static FakeAppRegistryCache& Get() {
    static base::NoDestructor<FakeAppRegistryCache> kInstance;
    return *kInstance;
  }

  // In the real class, returns the cache for the given user. We only maintain a
  // single fake cache.
  FakeAppRegistryCache* GetAppRegistryCache(const AccountId&) { return this; }

  template <typename FunctionType>
  bool ForOneApp(const std::string& app_id, FunctionType f) {
    for (const std::unique_ptr<apps::AppUpdate>& app : apps_) {
      if (app_id == app->AppId()) {
        f(*app);
        return true;
      }
    }

    return false;
  }

  // Test-only method to populate fake apps.
  void AddApp(std::unique_ptr<apps::AppUpdate> app) {
    apps_.push_back(std::move(app));
  }

 private:
  // Use pointers as we need a default constructor.
  std::vector<std::unique_ptr<apps::AppUpdate>> apps_;
};

// A test fixture that gives access to the HPS notify controller (to fake
// snooping events).
class SnoopingProtectionNotificationBlockerTest : public AshTestBase {
 public:
  SnoopingProtectionNotificationBlockerTest()
      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
    scoped_feature_list_.InitWithFeaturesAndParameters(
        {/*enabled_features=*/{
            ash::features::kSnoopingProtection,
            {
                {"SnoopingProtection_pos_window_ms", "4000"},
                {"SnoopingProtection_filter_config_case", "2"},
                {"SnoopingProtection_positive_count_threshold", "1"},
                {"SnoopingProtection_negative_count_threshold", "1"},
                {"SnoopingProtection_uncertain_count_threshold", "1"},
                {"SnoopingProtection_positive_score_threshold", "0"},
                {"SnoopingProtection_negative_score_threshold", "0"},
            }}},
        /*disabled_features=*/{ash::features::kQuickDim});
    scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
        switches::kHasHps);
  }

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

  ~SnoopingProtectionNotificationBlockerTest() override = default;

  // AshTestBase overrides:
  void SetUp() override {
    // Simulate a working DBus client.
    HumanPresenceDBusClient::InitializeFake();
    auto* dbus_client = FakeHumanPresenceDBusClient::Get();
    dbus_client->set_hps_service_is_available(true);
    hps::HpsResultProto state;
    state.set_value(hps::HpsResult::NEGATIVE);
    dbus_client->set_hps_notify_result(state);

    AshTestBase::SetUp();

    // The controller has now been initialized, part of which entails sending a
    // method to the DBus service. Here we wait for the service to
    // asynchronously respond.
    base::RunLoop().RunUntilIdle();

    // Make sure the controller is active by both logging in and enabling the
    // snooping protection pref.
    SetSnoopingPref(true);

    controller_ = Shell::Get()->snooping_protection_controller();
    message_center_ = message_center::MessageCenter::Get();
  }

  bool HasInfoNotification() {
    message_center::Notification* notification =
        message_center::MessageCenter::Get()->FindVisibleNotificationById(
            SnoopingProtectionNotificationBlocker::kInfoNotificationId);
    return notification != nullptr;
  }

  void SimulateClick(int button_index) {
    message_center::Notification* notification =
        message_center::MessageCenter::Get()->FindVisibleNotificationById(
            SnoopingProtectionNotificationBlocker::kInfoNotificationId);
    notification->delegate()->Click(button_index, std::nullopt);
  }

  int GetNumOsSmartPrivacySettingsOpened() {
    return GetSystemTrayClient()->show_os_smart_privacy_settings_count();
  }

 protected:
  raw_ptr<SnoopingProtectionController, DanglingUntriaged> controller_ =
      nullptr;
  raw_ptr<message_center::MessageCenter, DanglingUntriaged> message_center_ =
      nullptr;

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  base::test::ScopedCommandLine scoped_command_line_;
};

TEST_F(SnoopingProtectionNotificationBlockerTest, Snooping) {
  SetBlockerPref(true);

  // By default, no snooper detected.
  AddNotification("notification-1", u"notifier-1");
  EXPECT_EQ(VisiblePopupCount(), 1u);
  EXPECT_EQ(VisibleNotificationCount(), 1u);

  // Simulate snooper presence.
  hps::HpsResultProto state;
  state.set_value(hps::HpsResult::POSITIVE);
  controller_->OnHpsNotifyChanged(state);

  // When snooping is detected, the popup notification should be hidden but
  // remain in the notification queue. Note that, since the popup has been
  EXPECT_EQ(VisiblePopupCount(), 0u);
  EXPECT_EQ(VisibleNotificationCount(), 1u);

  // Add notifications while a snooper is present.
  AddNotification("notification-2", u"notifier-2");
  AddNotification("notification-3", u"notifier-3");
  EXPECT_EQ(VisiblePopupCount(), 1u);  // Only our info popup.
  EXPECT_TRUE(InfoPopupVisible());
  EXPECT_EQ(VisibleNotificationCount(), 4u);

  // Simulate snooper absence. We wait for a moment to bypass the controller's
  // hysteresis logic.
  state.set_value(hps::HpsResult::NEGATIVE);
  controller_->OnHpsNotifyChanged(state);
  task_environment()->FastForwardBy(base::Seconds(10));

  // The unshown popups should appear since snooper has left.
  EXPECT_EQ(VisiblePopupCount(), 2u);
  EXPECT_FALSE(InfoPopupVisible());
  EXPECT_EQ(VisibleNotificationCount(), 3u);
}

TEST_F(SnoopingProtectionNotificationBlockerTest, Pref) {
  SetBlockerPref(false);

  // Start with one notification that shouldn't be hidden.
  AddNotification("notification-1", u"notifier-1");
  EXPECT_EQ(VisiblePopupCount(), 1u);
  EXPECT_FALSE(InfoPopupVisible());
  EXPECT_EQ(VisibleNotificationCount(), 1u);

  // Simulate snooper presence.
  hps::HpsResultProto state;
  state.set_value(hps::HpsResult::POSITIVE);
  controller_->OnHpsNotifyChanged(state);

  // Notifications should be visible up until the user enables the feature.
  EXPECT_EQ(VisiblePopupCount(), 1u);
  EXPECT_FALSE(InfoPopupVisible());
  EXPECT_EQ(VisibleNotificationCount(), 1u);
  SetBlockerPref(true);

  // Since notification 1 has been shown, we aren't blocking any popups and our
  // info popup need not be shown.
  EXPECT_EQ(VisiblePopupCount(), 0u);
  EXPECT_FALSE(InfoPopupVisible());
  EXPECT_EQ(VisibleNotificationCount(), 1u);

  // Add notifications while the feature is disabled. This should cause our info
  // popup and no other popups to be shown.
  AddNotification("notification-2", u"notifier-2");
  AddNotification("notification-3", u"notifier-3");
  EXPECT_EQ(VisiblePopupCount(), 1u);
  EXPECT_TRUE(InfoPopupVisible());
  EXPECT_EQ(VisibleNotificationCount(), 4u);

  // Notifications should be shown if *either* the whole setting or the
  // subfeature is enabled.
  SetSnoopingPref(false);

  // The new popups should appear when the feature is disabled.
  EXPECT_EQ(VisiblePopupCount(), 2u);
  EXPECT_FALSE(InfoPopupVisible());
  EXPECT_EQ(VisibleNotificationCount(), 3u);
}

TEST_F(SnoopingProtectionNotificationBlockerTest, SystemNotification) {
  SetBlockerPref(true);

  // One regular notification, one important notification that should be
  // allowlisted, and one important notification that could contain sensitive
  // information (and should therefore still be blocked).
  AddNotification("notification-1", u"notifier-1");
  AddNotification("notification-2", /*notifier_title=*/u"");
  AddNotification(
      SmsObserver::kNotificationPrefix + std::string("-notification-3"),
      /*notifier_title=*/u"");
  EXPECT_EQ(VisiblePopupCount(), 3u);
  EXPECT_FALSE(InfoPopupVisible());
  EXPECT_EQ(VisibleNotificationCount(), 3u);

  // Simulate snooper presence.
  hps::HpsResultProto state;
  state.set_value(hps::HpsResult::POSITIVE);
  controller_->OnHpsNotifyChanged(state);

  // The safe notification shouldn't be suppressed, but the sensitive
  // notification should be.
  EXPECT_EQ(VisiblePopupCount(), 2u);
  EXPECT_TRUE(InfoPopupVisible());
  // Regular notification disappears because it was already shown before the
  // snooper arrived.
  EXPECT_EQ(PositionInInfoPopupMessage(u"notifier-1"), std::u16string::npos);
  // Check that the system notification is labled as such.
  EXPECT_NE(PositionInInfoPopupMessage(u"System"), std::u16string::npos);
  EXPECT_EQ(VisibleNotificationCount(), 4u);
}

TEST_F(SnoopingProtectionNotificationBlockerTest, InfoPopup) {
  SetBlockerPref(true);

  // Simulate snooper presence.
  hps::HpsResultProto state;
  state.set_value(hps::HpsResult::POSITIVE);
  controller_->OnHpsNotifyChanged(state);

  // Two notifications we're blocking.
  AddNotification("notification-1", u"notifier-1");
  AddNotification("notification-2", u"notifier-2");
  EXPECT_EQ(VisiblePopupCount(), 1u);  // Only our info popup.
  EXPECT_NE(PositionInInfoPopupMessage(u"notifier-1"), std::u16string::npos);
  EXPECT_NE(PositionInInfoPopupMessage(u"notifier-2"), std::u16string::npos);
  EXPECT_EQ(VisibleNotificationCount(), 3u);

  // Check that the user can remove the info popup and it will return.
  RemoveNotification(
      SnoopingProtectionNotificationBlocker::kInfoNotificationId);
  EXPECT_EQ(VisiblePopupCount(), 0u);
  AddNotification("notification-3", u"notifier-3");
  EXPECT_EQ(VisiblePopupCount(), 1u);  // Only our info popup.
  EXPECT_NE(PositionInInfoPopupMessage(u"notifier-2"), std::u16string::npos);
  EXPECT_NE(PositionInInfoPopupMessage(u"notifier-3"), std::u16string::npos);
}

// Test that we don't report the notifiers of popups that we (alone) aren't
// blocking.
TEST_F(SnoopingProtectionNotificationBlockerTest, InfoPopupOtherBlocker) {
  IdPopupBlocker other_blocker(message_center_);
  other_blocker.Init();
  other_blocker.SetTargetId("notification-2");

  SetBlockerPref(true);

  // Simulate snooper presence.
  hps::HpsResultProto state;
  state.set_value(hps::HpsResult::POSITIVE);
  controller_->OnHpsNotifyChanged(state);

  // One notification only we are blocking, and one notification that is also
  // blocked by another blocker.
  AddNotification("notification-1", u"notifier-1");
  AddNotification("notification-2", u"notifier-2");
  EXPECT_EQ(VisiblePopupCount(), 1u);
  EXPECT_NE(PositionInInfoPopupMessage(u"notifier-1"), std::u16string::npos);
  // Do not report that we're blocking a notification when it won't show up
  // after snooping ends.
  EXPECT_EQ(PositionInInfoPopupMessage(u"notifier-2"), std::u16string::npos);
  EXPECT_EQ(VisibleNotificationCount(), 3u);

  // Now update our other blocker not to block either notification.
  other_blocker.SetTargetId("notification-3");

  // We are now the sole blockers of both notifications, so should report both.
  EXPECT_EQ(VisiblePopupCount(), 1u);
  EXPECT_NE(PositionInInfoPopupMessage(u"notifier-1"), std::u16string::npos);
  EXPECT_NE(PositionInInfoPopupMessage(u"notifier-2"), std::u16string::npos);
  EXPECT_EQ(VisibleNotificationCount(), 3u);
}

// Test that the info popup message is changed as relevant notifications are
// added and removed.
TEST_F(SnoopingProtectionNotificationBlockerTest,
       InfoPopupChangingNotifications) {
  SetBlockerPref(true);

  // Simulate snooper presence.
  hps::HpsResultProto state;
  state.set_value(hps::HpsResult::POSITIVE);
  controller_->OnHpsNotifyChanged(state);

  // Newer notifiers should come before older ones.
  AddNotification("notification-1", u"notifier-1");
  AddNotification("notification-2", u"notifier-2");
  {
    const size_t pos_1 = PositionInInfoPopupMessage(u"notifier-1");
    const size_t pos_2 = PositionInInfoPopupMessage(u"notifier-2");
    EXPECT_LE(pos_2, pos_1);
    EXPECT_LE(pos_1, std::u16string::npos);
  }

  // Positions should be swapped if we see an old notifier again.
  AddNotification("notification-3", u"notifier-1");
  {
    const size_t pos_1 = PositionInInfoPopupMessage(u"notifier-1");
    const size_t pos_2 = PositionInInfoPopupMessage(u"notifier-2");
    EXPECT_LE(pos_1, pos_2);
    EXPECT_LE(pos_2, std::u16string::npos);
  }

  // Notifiers don't repeat.
  AddNotification("notification-4", u"notifier-1");
  {
    const size_t pos_1 = PositionInInfoPopupMessage(u"notifier-1");
    const size_t pos_2 = PositionInInfoPopupMessage(u"notifier-2");
    EXPECT_LE(pos_1, pos_2);
    EXPECT_LE(pos_2, std::u16string::npos);
  }

  // Notifiers are removed correctly.
  RemoveNotification("notification-4");
  RemoveNotification("notification-2");
  {
    const size_t pos_1 = PositionInInfoPopupMessage(u"notifier-1");
    const size_t pos_2 = PositionInInfoPopupMessage(u"notifier-2");
    EXPECT_NE(pos_1, std::u16string::npos);
    EXPECT_EQ(pos_2, std::u16string::npos);
  }
}

// Test that message center is visible when click Settings button.
TEST_F(SnoopingProtectionNotificationBlockerTest, SettingsButtonClicked) {
  SetBlockerPref(true);

  // Simulate snooper presence.
  hps::HpsResultProto state;
  state.set_value(hps::HpsResult::POSITIVE);
  controller_->OnHpsNotifyChanged(state);

  AddNotification("notification-1", u"notifier-1");
  AddNotification("notification-2", u"notifier-2");

  EXPECT_TRUE(HasInfoNotification());

  // Click on show button.
  SimulateClick(/*button_index=*/1);
  EXPECT_EQ(1, GetNumOsSmartPrivacySettingsOpened());
}

TEST(SnoopingProtectionNotificationBlockerInternalTest, WebsiteNotifierTitles) {
  // Website without title uses a generic "web" string.
  const message_center::NotifierId untrusted_notifier(
      GURL("http://untrusted.com:443"));
  const std::u16string untrusted_title =
      hps_internal::GetNotifierTitle<FakeAppRegistryCache>(untrusted_notifier,
                                                           AccountId());
  EXPECT_EQ(untrusted_title,
            l10n_util::GetStringUTF16(
                IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_WEB_TITLE_LOWER));

  // Website with a trusted title uses the title.
  const message_center::NotifierId trusted_notifier(
      GURL("https://trusted.com:443"), u"Trusted",
      /*web_app_id=*/std::nullopt);
  const std::u16string trusted_title =
      hps_internal::GetNotifierTitle<FakeAppRegistryCache>(trusted_notifier,
                                                           AccountId());
  EXPECT_EQ(trusted_title, u"Trusted");
}

TEST(SnoopingProtectionNotificationBlockerInternalTest, AppNotifierTitles) {
  // App without known title uses a generic "app" string.
  const message_center::NotifierId unknown_app_notifier(
      message_center::NotifierType::APPLICATION, "unknown-app");
  const std::u16string unknown_app_title =
      hps_internal::GetNotifierTitle<FakeAppRegistryCache>(unknown_app_notifier,
                                                           AccountId());
  EXPECT_EQ(unknown_app_title,
            l10n_util::GetStringUTF16(
                IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_APP_TITLE_LOWER));

  // Create an app cache with one used and one unused app.
  auto* app_cache =
      FakeAppRegistryCache::Get().GetAppRegistryCache(AccountId());
  apps::App crostini_app_state(apps::AppType::kCrostini, "crostini-app");
  crostini_app_state.name = "Signal Messenger";
  auto crostini_app = std::make_unique<apps::AppUpdate>(
      &crostini_app_state, /*delta=*/nullptr, AccountId());
  app_cache->AddApp(std::move(crostini_app));
  apps::App arc_app_state(apps::AppType::kArc, "arc-app");
  arc_app_state.name = "Discord";
  auto arc_app = std::make_unique<apps::AppUpdate>(
      &arc_app_state, /*delta=*/nullptr, AccountId());
  app_cache->AddApp(std::move(arc_app));

  // App with a registered name uses that name.
  const message_center::NotifierId crostini_app_notifier(
      message_center::NotifierType::CROSTINI_APPLICATION, "crostini-app");
  const std::u16string crostini_app_title =
      hps_internal::GetNotifierTitle<FakeAppRegistryCache>(
          crostini_app_notifier, AccountId());
  EXPECT_EQ(crostini_app_title, u"Signal Messenger");
}

TEST(SnoopingProtectionNotificationBlockerInternalTest, PopupMessage) {
  // Proper app names should be presented as-is.
  const std::vector<std::u16string> list_1 = {u"App title"};
  const std::u16string list_1_msg =
      hps_internal::GetTitlesBlockedMessage(list_1);
  EXPECT_TRUE(base::Contains(list_1_msg, u"App title"));

  // Improper app names should use a reasonable default. In this case, the
  // default should be capitalized since it is the first word in the message.
  const std::vector<std::u16string> list_2 = {l10n_util::GetStringUTF16(
      IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_WEB_TITLE_LOWER)};
  const std::u16string list_2_msg =
      hps_internal::GetTitlesBlockedMessage(list_2);
  EXPECT_TRUE(base::Contains(
      list_2_msg,
      l10n_util::GetStringUTF16(
          IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_WEB_TITLE_UPPER)));

  // Subsequent improper app names should not be capitalized.
  const std::vector<std::u16string> list_3 = {
      u"App title",
      l10n_util::GetStringUTF16(
          IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_WEB_TITLE_LOWER)};
  const std::u16string list_3_msg =
      hps_internal::GetTitlesBlockedMessage(list_3);
  EXPECT_FALSE(base::Contains(
      list_3_msg,
      l10n_util::GetStringUTF16(
          IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_WEB_TITLE_UPPER)));
}

}  // namespace

}  // namespace ash