blob: 9915dbd2ba34f5ea7906aa52c8955849cc43b495 [file] [log] [blame]
// Copyright 2020 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/web_applications/web_app_prefs_utils.h"
#include "base/json/json_reader.h"
#include "base/json/values_util.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace web_app {
namespace {
const webapps::AppId app_id = "test_app";
const webapps::AppId app_id_2 = "test_app_2";
const base::Time time_before_app_mute =
base::Time::Now() - base::Days(kIphAppSpecificMuteTimeSpanDays) -
base::Hours(1);
const base::Time time_before_global_mute =
base::Time::Now() - base::Days(kIphAppAgnosticMuteTimeSpanDays) -
base::Hours(1);
} // namespace
class WebAppPrefsUtilsTest : public testing::Test {
public:
WebAppPrefsUtilsTest() {
WebAppPrefsUtilsRegisterProfilePrefs(prefs_.registry());
}
sync_preferences::TestingPrefServiceSyncable* prefs() { return &prefs_; }
protected:
content::BrowserTaskEnvironment task_environment_;
private:
sync_preferences::TestingPrefServiceSyncable prefs_;
};
TEST_F(WebAppPrefsUtilsTest, TestIphIgnoreRecorded) {
EXPECT_FALSE(GetIntWebAppPref(prefs(), kIphIgnoreCount, app_id).has_value());
EXPECT_FALSE(
GetTimeWebAppPref(prefs(), app_id, kIphLastIgnoreTime).has_value());
RecordInstallIphIgnored(prefs(), app_id, base::Time::Now());
EXPECT_EQ(GetIntWebAppPref(prefs(), app_id, kIphIgnoreCount).value_or(0), 1);
auto last_ignore_time =
GetTimeWebAppPref(prefs(), app_id, kIphLastIgnoreTime);
EXPECT_TRUE(last_ignore_time.has_value());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticIphState);
EXPECT_EQ(dict.FindInt(kIphIgnoreCount).value_or(0), 1);
EXPECT_EQ(base::ValueToTime(dict.Find(kIphLastIgnoreTime)),
last_ignore_time.value());
}
}
TEST_F(WebAppPrefsUtilsTest, TestIphIgnoreRecordUpdated) {
RecordInstallIphIgnored(prefs(), app_id, base::Time::Now());
auto last_ignore_time =
GetTimeWebAppPref(prefs(), app_id, kIphLastIgnoreTime);
EXPECT_TRUE(last_ignore_time.has_value());
RecordInstallIphIgnored(prefs(), app_id, base::Time::Now());
EXPECT_EQ(GetIntWebAppPref(prefs(), app_id, kIphIgnoreCount).value_or(0), 2);
EXPECT_NE(GetTimeWebAppPref(prefs(), app_id, kIphLastIgnoreTime).value(),
last_ignore_time.value());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticIphState);
EXPECT_EQ(dict.FindInt(kIphIgnoreCount).value_or(0), 2);
EXPECT_NE(base::ValueToTime(dict.Find(kIphLastIgnoreTime)),
last_ignore_time.value());
}
}
TEST_F(WebAppPrefsUtilsTest, TestIphInstallResetCounters) {
RecordInstallIphIgnored(prefs(), app_id, base::Time::Now());
EXPECT_EQ(GetIntWebAppPref(prefs(), app_id, kIphIgnoreCount).value_or(0), 1);
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticIphState);
EXPECT_EQ(dict.FindInt(kIphIgnoreCount).value_or(0), 1);
}
RecordInstallIphInstalled(prefs(), app_id);
EXPECT_EQ(GetIntWebAppPref(prefs(), app_id, kIphIgnoreCount).value_or(0), 0);
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticIphState);
EXPECT_EQ(dict.FindInt(kIphIgnoreCount).value_or(0), 0);
}
}
TEST_F(WebAppPrefsUtilsTest, TestIphAppIgnoredRecently) {
EXPECT_TRUE(ShouldShowIph(prefs(), app_id));
RecordInstallIphIgnored(prefs(), app_id, base::Time::Now());
EXPECT_FALSE(ShouldShowIph(prefs(), app_id));
}
TEST_F(WebAppPrefsUtilsTest, TestIphGlobalIgnoredRecently) {
EXPECT_TRUE(ShouldShowIph(prefs(), app_id));
RecordInstallIphIgnored(prefs(), app_id_2, base::Time::Now());
EXPECT_FALSE(ShouldShowIph(prefs(), app_id));
}
TEST_F(WebAppPrefsUtilsTest, TestIphGlobalIgnoredPassedMuteTime) {
RecordInstallIphIgnored(prefs(), app_id_2, time_before_global_mute);
EXPECT_TRUE(ShouldShowIph(prefs(), app_id));
}
TEST_F(WebAppPrefsUtilsTest, TestIphAppIgnoredPassedMuteTime) {
RecordInstallIphIgnored(prefs(), app_id, time_before_app_mute);
EXPECT_TRUE(ShouldShowIph(prefs(), app_id));
}
TEST_F(WebAppPrefsUtilsTest, TestIphConsecutiveAppIgnore) {
RecordInstallIphIgnored(prefs(), app_id, time_before_app_mute);
UpdateIntWebAppPref(prefs(), app_id, kIphIgnoreCount,
kIphMuteAfterConsecutiveAppSpecificIgnores);
EXPECT_FALSE(ShouldShowIph(prefs(), app_id));
}
TEST_F(WebAppPrefsUtilsTest, TestGlobalConsecutiveAppIgnore) {
RecordInstallIphIgnored(prefs(), app_id_2, time_before_global_mute);
{
ScopedDictPrefUpdate update(prefs(), prefs::kWebAppsAppAgnosticIphState);
update->Set(kIphIgnoreCount, kIphMuteAfterConsecutiveAppAgnosticIgnores);
}
EXPECT_FALSE(ShouldShowIph(prefs(), app_id));
}
TEST_F(WebAppPrefsUtilsTest, TestTakeAllWebAppInstallSources) {
base::Value old = *base::JSONReader::Read(R"({
"app1": {},
"app2": { "latest_web_app_install_source": 2 },
"app3": {
"latest_web_app_install_source": 3,
"IPH_last_ignore_time": "123345567"
}
})");
prefs()->Set(prefs::kWebAppsPreferences, std::move(old));
EXPECT_TRUE(GetWebAppInstallSourceDeprecated(prefs(), "app2"));
EXPECT_TRUE(GetWebAppInstallSourceDeprecated(prefs(), "app3"));
std::map<webapps::AppId, int> values = TakeAllWebAppInstallSources(prefs());
// Verify the returned map.
ASSERT_EQ(2u, values.size());
auto app1 = values.find("app1");
ASSERT_TRUE(app1 == values.end());
auto app2 = values.find("app2");
ASSERT_FALSE(app2 == values.end());
EXPECT_EQ(2, app2->second);
auto app3 = values.find("app3");
ASSERT_FALSE(app3 == values.end());
EXPECT_EQ(3, app3->second);
EXPECT_FALSE(GetWebAppInstallSourceDeprecated(prefs(), "app2"));
EXPECT_FALSE(GetWebAppInstallSourceDeprecated(prefs(), "app3"));
// Verify what's left behind in prefs.
const base::Value* web_apps_prefs =
prefs()->GetUserPrefValue(prefs::kWebAppsPreferences);
ASSERT_TRUE(web_apps_prefs);
ASSERT_TRUE(web_apps_prefs->is_dict());
const base::Value::Dict& web_apps_pref_dict = web_apps_prefs->GetDict();
EXPECT_EQ(1u, web_apps_pref_dict.size());
EXPECT_FALSE(web_apps_pref_dict.Find("app1"));
EXPECT_FALSE(web_apps_pref_dict.Find("app2"));
EXPECT_TRUE(web_apps_pref_dict.Find("app3"));
EXPECT_FALSE(web_apps_pref_dict.FindByDottedPath(
"app3.latest_web_app_install_source"));
}
TEST_F(WebAppPrefsUtilsTest, MLInstallIgnored) {
EXPECT_FALSE(GetTimeWebAppPref(prefs(), app_id, kLastTimeMlInstallIgnored)
.has_value());
EXPECT_FALSE(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.has_value());
RecordMlInstallIgnored(prefs(), app_id, base::Time::Now());
EXPECT_EQ(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.value_or(0),
1);
auto last_ignore_time =
GetTimeWebAppPref(prefs(), app_id, kLastTimeMlInstallIgnored);
EXPECT_TRUE(last_ignore_time.has_value());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
1);
EXPECT_EQ(base::ValueToTime(dict.Find(kLastTimeMlInstallIgnored)),
last_ignore_time.value());
}
}
TEST_F(WebAppPrefsUtilsTest, MLInstallDismissed) {
EXPECT_FALSE(GetTimeWebAppPref(prefs(), app_id, kLastTimeMlInstallDismissed)
.has_value());
EXPECT_FALSE(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.has_value());
RecordMlInstallDismissed(prefs(), app_id, base::Time::Now());
EXPECT_EQ(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.value_or(0),
1);
auto last_dismissed_time =
GetTimeWebAppPref(prefs(), app_id, kLastTimeMlInstallDismissed);
EXPECT_TRUE(last_dismissed_time.has_value());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
1);
EXPECT_EQ(base::ValueToTime(dict.Find(kLastTimeMlInstallDismissed)),
last_dismissed_time.value());
}
}
TEST_F(WebAppPrefsUtilsTest, MLAcceptResetsCounters) {
RecordMlInstallIgnored(prefs(), app_id, base::Time::Now());
EXPECT_EQ(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.value_or(0),
1);
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
1);
}
RecordMlInstallAccepted(prefs(), app_id, base::Time::Now());
EXPECT_EQ(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.value_or(0),
0);
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
0);
}
}
TEST_F(WebAppPrefsUtilsTest, MLGuardrailConsecutiveAppSpecificIgnores) {
RecordMlInstallIgnored(prefs(), app_id, base::Time::Now());
EXPECT_EQ(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.value_or(0),
1);
RecordMlInstallIgnored(prefs(), app_id, base::Time::Now());
EXPECT_EQ(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.value_or(0),
2);
RecordMlInstallIgnored(prefs(), app_id, base::Time::Now());
EXPECT_EQ(
GetIntWebAppPref(prefs(), app_id, kConsecutiveMlInstallNotAcceptedCount)
.value_or(0),
3);
EXPECT_TRUE(IsMlPromotionBlockedByHistoryGuardrail(prefs(), app_id));
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(*dict.FindString(kMLPromotionGuardrailBlockReason),
"app_specific_not_accept_count_exceeded");
}
}
TEST_F(WebAppPrefsUtilsTest, MLGuardrailAppSpecificIgnoreForDays) {
base::Time now_time = base::Time::Now();
RecordMlInstallIgnored(prefs(), app_id, now_time);
auto ignore_time =
GetTimeWebAppPref(prefs(), app_id, kLastTimeMlInstallIgnored);
EXPECT_TRUE(ignore_time.has_value());
EXPECT_EQ(now_time, ignore_time);
base::Time forwarded_time = base::Time::Now() + base::Days(1);
RecordMlInstallIgnored(prefs(), app_id, forwarded_time);
auto ignore_time_new =
GetTimeWebAppPref(prefs(), app_id, kLastTimeMlInstallIgnored);
EXPECT_TRUE(ignore_time_new.has_value());
EXPECT_EQ(forwarded_time, ignore_time_new);
EXPECT_TRUE(IsMlPromotionBlockedByHistoryGuardrail(prefs(), app_id));
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(*dict.FindString(kMLPromotionGuardrailBlockReason),
"app_specific_ml_install_ignore_days_hit");
}
}
TEST_F(WebAppPrefsUtilsTest, MLGuardrailAppSpecificDismissForDays) {
base::Time now_time = base::Time::Now();
RecordMlInstallDismissed(prefs(), app_id, now_time);
auto dismiss_time =
GetTimeWebAppPref(prefs(), app_id, kLastTimeMlInstallDismissed);
EXPECT_TRUE(dismiss_time.has_value());
EXPECT_EQ(now_time, dismiss_time);
// Dismissing the same app within 14 days should trigger the guardrail
// response.
int randDays = base::RandInt(1, 13);
base::Time forwarded_time = base::Time::Now() + base::Days(randDays);
RecordMlInstallDismissed(prefs(), app_id, forwarded_time);
auto dismiss_time_new =
GetTimeWebAppPref(prefs(), app_id, kLastTimeMlInstallDismissed);
EXPECT_TRUE(dismiss_time_new.has_value());
EXPECT_EQ(forwarded_time, dismiss_time_new);
EXPECT_TRUE(IsMlPromotionBlockedByHistoryGuardrail(prefs(), app_id));
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(*dict.FindString(kMLPromotionGuardrailBlockReason),
"app_specific_ml_install_dismiss_days_hit");
}
}
TEST_F(WebAppPrefsUtilsTest, MLGuardrailConsecutiveAppAgnosticIgnores) {
const webapps::AppId& app_id1 = "app1";
const webapps::AppId& app_id2 = "app2";
const webapps::AppId& app_id3 = "app3";
const webapps::AppId& app_id4 = "app4";
const webapps::AppId& app_id5 = "app5";
RecordMlInstallIgnored(prefs(), app_id1, base::Time::Now());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
1);
}
RecordMlInstallDismissed(prefs(), app_id2, base::Time::Now());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
2);
}
RecordMlInstallDismissed(prefs(), app_id3, base::Time::Now());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
3);
}
RecordMlInstallDismissed(prefs(), app_id4, base::Time::Now());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
4);
}
RecordMlInstallIgnored(prefs(), app_id5, base::Time::Now());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(dict.FindInt(kConsecutiveMlInstallNotAcceptedCount).value_or(0),
5);
}
EXPECT_TRUE(IsMlPromotionBlockedByHistoryGuardrail(prefs(), app_id));
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(*dict.FindString(kMLPromotionGuardrailBlockReason),
"app_agnostic_not_accept_count_exceeded");
}
}
TEST_F(WebAppPrefsUtilsTest, MLGuardrailConsecutiveAppAgnosticIgnoreDays) {
const webapps::AppId& app_id1 = "app1";
RecordMlInstallIgnored(prefs(), app_id1, base::Time::Now());
auto last_ignored_time =
GetTimeWebAppPref(prefs(), app_id1, kLastTimeMlInstallIgnored);
EXPECT_TRUE(last_ignored_time.has_value());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(base::ValueToTime(dict.Find(kLastTimeMlInstallIgnored)),
last_ignored_time.value());
}
EXPECT_TRUE(IsMlPromotionBlockedByHistoryGuardrail(prefs(), app_id));
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(*dict.FindString(kMLPromotionGuardrailBlockReason),
"app_agnostic_ml_install_ignore_days_hit");
}
}
TEST_F(WebAppPrefsUtilsTest, MLGuardrailConsecutiveAppAgnosticDismissDays) {
const webapps::AppId& app_id1 = "app1";
// Dismissing any app within the last 7 days should trigger the app agnostic
// dismiss guardrail response.
int randDays = base::RandInt(0, 6);
RecordMlInstallDismissed(prefs(), app_id1,
base::Time::Now() - base::Days(randDays));
auto last_dismissed_time =
GetTimeWebAppPref(prefs(), app_id1, kLastTimeMlInstallDismissed);
EXPECT_TRUE(last_dismissed_time.has_value());
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(base::ValueToTime(dict.Find(kLastTimeMlInstallDismissed)),
last_dismissed_time.value());
}
EXPECT_TRUE(IsMlPromotionBlockedByHistoryGuardrail(prefs(), app_id));
{
const auto& dict = prefs()->GetDict(prefs::kWebAppsAppAgnosticMlState);
EXPECT_EQ(*dict.FindString(kMLPromotionGuardrailBlockReason),
"app_agnostic_ml_install_dismiss_days_hit");
}
}
} // namespace web_app