summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordbeam@chromium.org <dbeam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-17 06:15:04 +0000
committerdbeam@chromium.org <dbeam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-17 06:15:04 +0000
commit728171342522b70993ac59a956a678d375d73fda (patch)
treeeee15bf88a9e802e2269161fe929a97c994b4cc9
parent8d944b369dc0979cc9926cdda7673682bf8e5bef (diff)
downloadchromium_src-728171342522b70993ac59a956a678d375d73fda.zip
chromium_src-728171342522b70993ac59a956a678d375d73fda.tar.gz
chromium_src-728171342522b70993ac59a956a678d375d73fda.tar.bz2
Sign In Promo: Roll out the new sign in promo gradually using a server check
R=achuith@chromium.org,mirandac@chromium.org BUG=94242 TEST=SyncPromoTest.* Review URL: http://codereview.chromium.org/7983046 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@105771 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/resources/ntp4/new_tab.js25
-rw-r--r--chrome/browser/ui/webui/ntp/ntp_resource_cache.cc7
-rw-r--r--chrome/browser/ui/webui/sync_promo_ui.cc5
-rw-r--r--chrome/browser/web_resource/promo_resource_service.cc97
-rw-r--r--chrome/browser/web_resource/promo_resource_service.h21
-rw-r--r--chrome/browser/web_resource/promo_resource_service_unittest.cc195
-rw-r--r--chrome/common/pref_names.cc7
-rw-r--r--chrome/common/pref_names.h2
8 files changed, 347 insertions, 12 deletions
diff --git a/chrome/browser/resources/ntp4/new_tab.js b/chrome/browser/resources/ntp4/new_tab.js
index 477e91f5..d374a6d 100644
--- a/chrome/browser/resources/ntp4/new_tab.js
+++ b/chrome/browser/resources/ntp4/new_tab.js
@@ -235,13 +235,8 @@ cr.define('ntp4', function() {
chrome.send('notificationPromoViewed');
}
- chrome.send('initializeSyncLogin');
- sliderFrame.classList.add('showing-sync-promo');
- $('login-container').addEventListener('click', function() {
- var rect = $('login-container').getBoundingClientRect();
- chrome.send('showSyncLoginUI',
- [rect.left, rect.top, rect.width, rect.height]);
- });
+ if (templateData.showSyncPromo)
+ showSyncPromo();
}
/**
@@ -848,6 +843,21 @@ cr.define('ntp4', function() {
$('recently-closed-menu-button').dataItems = dataItems;
}
+ /**
+ * Visually shows the sync promo on the NTP.
+ */
+ function showSyncPromo() {
+ var loginContainer = getRequiredElement('login-container');
+ if (loginContainer.hidden) {
+ chrome.send('initializeSyncLogin');
+ loginContainer.addEventListener('click', function() {
+ var rect = loginContainer.getBoundingClientRect();
+ chrome.send('showSyncLoginUI',
+ [rect.left, rect.top, rect.width, rect.height]);
+ });
+ }
+ }
+
function setMostVisitedPages(data, hasBlacklistedUrls) {
mostVisitedPage.data = data;
}
@@ -960,6 +970,7 @@ cr.define('ntp4', function() {
setRecentlyClosedTabs: setRecentlyClosedTabs,
setStripeColor: setStripeColor,
showNotification: showNotification,
+ showSyncPromo: showSyncPromo,
themeChanged: themeChanged,
updateLogin: updateLogin,
updateOfflineEnabledApps: updateOfflineEnabledApps
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 076877b..9f3efdb 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -10,8 +10,8 @@
#include "base/file_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/string16.h"
-#include "base/stringprintf.h"
#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
@@ -26,6 +26,7 @@
#include "chrome/browser/ui/webui/ntp/new_tab_page_handler.h"
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
#include "chrome/browser/ui/webui/ntp/shown_sections_handler.h"
+#include "chrome/browser/ui/webui/sync_promo_ui.h"
#include "chrome/browser/ui/webui/sync_setup_handler.h"
#include "chrome/browser/web_resource/promo_resource_service.h"
#include "chrome/common/chrome_notification_types.h"
@@ -421,6 +422,10 @@ void NTPResourceCache::CreateNewTabHTML() {
UserMetrics::RecordAction(UserMetricsAction("NTPPromoShown"));
}
+ // Tell the NTP whether or not it should show the sync promotion.
+ localized_strings.SetBoolean("showSyncPromo",
+ SyncPromoUI::ShouldShowSyncPromo(profile_));
+
// Enable or disable bookmark features based on an about flag.
localized_strings.SetString("bookmark_features",
NewTabUI::NTP4BookmarkFeaturesEnabled() ? "true" : "false");
diff --git a/chrome/browser/ui/webui/sync_promo_ui.cc b/chrome/browser/ui/webui/sync_promo_ui.cc
index 58ee4cb..b2a0734 100644
--- a/chrome/browser/ui/webui/sync_promo_ui.cc
+++ b/chrome/browser/ui/webui/sync_promo_ui.cc
@@ -15,6 +15,7 @@
#include "chrome/browser/ui/webui/options/core_options_handler.h"
#include "chrome/browser/ui/webui/sync_promo_handler.h"
#include "chrome/browser/ui/webui/theme_source.h"
+#include "chrome/browser/web_resource/promo_resource_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
@@ -90,7 +91,9 @@ bool SyncPromoUI::ShouldShowSyncPromo(Profile* profile) {
if (!service || service->HasSyncSetupCompleted())
return false;
- return true;
+ // If we're not excluded from showing sync, let's check to see if the remote
+ // data from the promo server says we should show a promo now.
+ return PromoResourceService::CanShowSyncPromo(profile);
}
// static
diff --git a/chrome/browser/web_resource/promo_resource_service.cc b/chrome/browser/web_resource/promo_resource_service.cc
index 14c1a30..7c9ff0d 100644
--- a/chrome/browser/web_resource/promo_resource_service.cc
+++ b/chrome/browser/web_resource/promo_resource_service.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/web_resource/promo_resource_service.h"
#include "base/command_line.h"
+#include "base/rand_util.h"
#include "base/string_number_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/time.h"
@@ -36,6 +37,10 @@ static const int kTestCacheUpdateDelay = 3 * 60 * 1000;
// to versions with different types of promos).
static const int kPromoServiceVersion = 2;
+// The number of groups sync promo users will be divided into (which gives us a
+// 1/N granularity when targeting more groups).
+static const int kSyncPromoNumberOfGroups = 100;
+
// Properties used by the server.
static const char kAnswerIdProperty[] = "answer_id";
static const char kWebStoreHeaderProperty[] = "question";
@@ -79,6 +84,12 @@ void PromoResourceService::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterDoublePref(prefs::kNTPCustomLogoEnd,
0,
PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterIntegerPref(prefs::kNTPSyncPromoGroup,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
+ prefs->RegisterIntegerPref(prefs::kNTPSyncPromoGroupMax,
+ 0,
+ PrefService::UNSYNCABLE_PREF);
NotificationPromo::RegisterUserPrefs(prefs);
}
@@ -92,8 +103,11 @@ chrome::VersionInfo::Channel PromoResourceService::GetChannel() {
// static
bool PromoResourceService::IsBuildTargeted(chrome::VersionInfo::Channel channel,
int builds_allowed) {
- if (builds_allowed == NO_BUILD)
+ if (builds_allowed == NO_BUILD ||
+ builds_allowed < 0 ||
+ builds_allowed > ALL_BUILDS) {
return false;
+ }
switch (channel) {
case chrome::VersionInfo::CHANNEL_CANARY:
return (CANARY_BUILD & builds_allowed) != 0;
@@ -140,6 +154,7 @@ void PromoResourceService::Unpack(const DictionaryValue& parsed_json) {
UnpackLogoSignal(parsed_json);
UnpackNotificationSignal(parsed_json);
UnpackWebStoreSignal(parsed_json);
+ UnpackSyncPromoSignal(parsed_json);
}
void PromoResourceService::OnNewNotification(double start, double end) {
@@ -359,3 +374,83 @@ void PromoResourceService::UnpackLogoSignal(
NotificationService::NoDetails());
}
}
+
+void PromoResourceService::UnpackSyncPromoSignal(
+ const DictionaryValue& parsed_json) {
+#if defined(OS_CHROMEOS)
+ // Don't bother with this signal on ChromeOS. Users are already synced.
+ return;
+#endif
+
+ DictionaryValue* topic_dict;
+ if (!parsed_json.GetDictionary("topic", &topic_dict))
+ return;
+
+ ListValue* answer_list;
+ if (!topic_dict->GetList("answers", &answer_list))
+ return;
+
+ std::string question;
+ for (ListValue::const_iterator answer_iter = answer_list->begin();
+ answer_iter != answer_list->end(); ++answer_iter) {
+ if (!(*answer_iter)->IsType(Value::TYPE_DICTIONARY))
+ continue;
+ DictionaryValue* a_dic = static_cast<DictionaryValue*>(*answer_iter);
+ std::string name;
+ if (a_dic->GetString("name", &name) && name == "sync_promo") {
+ a_dic->GetString("question", &question);
+ break;
+ }
+ }
+
+ int new_build;
+ int new_group_max;
+ size_t build_index = question.find(":");
+ if (std::string::npos == build_index ||
+ !base::StringToInt(question.substr(0, build_index), &new_build) ||
+ !IsBuildTargeted(new_build) ||
+ !base::StringToInt(question.substr(build_index + 1), &new_group_max)) {
+ // If anything about the response was invalid or this build is no longer
+ // targeted and there are existing prefs, clear them and notify.
+ if (prefs_->HasPrefPath(prefs::kNTPSyncPromoGroup) ||
+ prefs_->HasPrefPath(prefs::kNTPSyncPromoGroupMax)) {
+ // Make sure we clear first, as the following notification may possibly
+ // depend on calling CanShowSyncPromo synchronously.
+ prefs_->ClearPref(prefs::kNTPSyncPromoGroup);
+ prefs_->ClearPref(prefs::kNTPSyncPromoGroupMax);
+ // Notify the NTP resource cache if the promo has been disabled.
+ NotificationService::current()->Notify(
+ chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED,
+ Source<WebResourceService>(this),
+ NotificationService::NoDetails());
+ }
+ return;
+ }
+
+ // TODO(dbeam): Add automagic hour group bumper to parsing?
+
+ // If we successfully parsed a response and it differs from our user prefs,
+ // set pref for next time to compare.
+ if (new_group_max != prefs_->GetInteger(prefs::kNTPSyncPromoGroupMax))
+ prefs_->SetInteger(prefs::kNTPSyncPromoGroupMax, new_group_max);
+}
+
+// static
+bool PromoResourceService::CanShowSyncPromo(Profile* profile) {
+ DCHECK(profile);
+ PrefService* prefs = profile->GetPrefs();
+
+ if (!prefs->HasPrefPath(prefs::kNTPSyncPromoGroupMax))
+ return false;
+
+ // If there's a max group set and the user hasn't been bucketed yet, do it.
+ if (!prefs->HasPrefPath(prefs::kNTPSyncPromoGroup)) {
+ prefs->SetInteger(prefs::kNTPSyncPromoGroup,
+ base::RandInt(1, kSyncPromoNumberOfGroups));
+ }
+
+ // A response is not kept if the build wasn't targeted, so the only thing
+ // required to check is the group this client has been tagged in.
+ return prefs->GetInteger(prefs::kNTPSyncPromoGroupMax) >=
+ prefs->GetInteger(prefs::kNTPSyncPromoGroup);
+}
diff --git a/chrome/browser/web_resource/promo_resource_service.h b/chrome/browser/web_resource/promo_resource_service.h
index 1077cbf..fd97425 100644
--- a/chrome/browser/web_resource/promo_resource_service.h
+++ b/chrome/browser/web_resource/promo_resource_service.h
@@ -44,6 +44,9 @@ class PromoResourceService
// Checks for conditions to show promo: start/end times, channel, etc.
static bool CanShowNotificationPromo(Profile* profile);
+ // Checks if this user is in a group for sync promo roll-out.
+ static bool CanShowSyncPromo(Profile* profile);
+
static void RegisterPrefs(PrefService* local_state);
static void RegisterUserPrefs(PrefService* prefs);
@@ -57,6 +60,7 @@ class PromoResourceService
static const char* kDefaultPromoResourceServer;
private:
+ friend class SyncPromoTest;
FRIEND_TEST_ALL_PREFIXES(PromoResourceServiceTest, IsBuildTargetedTest);
FRIEND_TEST_ALL_PREFIXES(PromoResourceServiceTest, UnpackLogoSignal);
FRIEND_TEST_ALL_PREFIXES(PromoResourceServiceTest, UnpackWebStoreSignal);
@@ -205,6 +209,23 @@ class PromoResourceService
// answer_id: the promo's id
void UnpackWebStoreSignal(const base::DictionaryValue& parsed_json);
+ // Unpack the sync promo. Expects JSON delivery in the following format:
+ // {
+ // "topic": {
+ // "answers": [
+ // ...
+ // {
+ // "answer_id": "XXXXXXX",
+ // "name": "sync_promo",
+ // "question": "1:5"
+ // }
+ // ]
+ // }
+ // }
+ //
+ // The question is in the form of "<build>:<group_max>".
+ void UnpackSyncPromoSignal(const base::DictionaryValue& parsed_json);
+
// NotificationPromo::Delegate override.
virtual void OnNewNotification(double start, double end) OVERRIDE;
diff --git a/chrome/browser/web_resource/promo_resource_service_unittest.cc b/chrome/browser/web_resource/promo_resource_service_unittest.cc
index 6b6a8ed..b4a2501 100644
--- a/chrome/browser/web_resource/promo_resource_service_unittest.cc
+++ b/chrome/browser/web_resource/promo_resource_service_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/json/json_reader.h"
+#include "base/string_number_conversions.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
@@ -12,10 +13,12 @@
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/web_resource/notification_promo.h"
#include "chrome/browser/web_resource/promo_resource_service.h"
+#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_pref_service.h"
#include "chrome/test/base/testing_profile.h"
+#include "content/common/notification_registrar.h"
#include "content/test/test_url_fetcher_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -33,6 +36,75 @@ class PromoResourceServiceTest : public testing::Test {
MessageLoop loop_;
};
+class SyncPromoTest : public PromoResourceServiceTest,
+ public NotificationObserver {
+ public:
+ SyncPromoTest() : PromoResourceServiceTest(), notifications_allowed_(false) {
+ web_resource_service_->set_channel(chrome::VersionInfo::CHANNEL_DEV);
+ registrar_.Add(this,
+ chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED,
+ NotificationService::AllSources());
+ }
+
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // If we get any unexpected notifications we should fail.
+ EXPECT_TRUE(notifications_allowed_);
+ }
+
+ void allow_notifications(bool allowed) { notifications_allowed_ = allowed; }
+
+ protected:
+ void ClearSyncPromoPrefs() {
+ PrefService* prefs = profile_.GetPrefs();
+ prefs->ClearPref(prefs::kNTPSyncPromoGroup);
+ prefs->ClearPref(prefs::kNTPSyncPromoGroupMax);
+ ASSERT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroup));
+ ASSERT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroupMax));
+ }
+
+ void InvalidTestCase(const std::string& question) {
+ PrefService* prefs = profile_.GetPrefs();
+ ASSERT_TRUE(prefs != NULL);
+ prefs->SetInteger(prefs::kNTPSyncPromoGroup, 50);
+ prefs->SetInteger(prefs::kNTPSyncPromoGroupMax, 75);
+ UnpackSyncPromo(question);
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroup));
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroupMax));
+ }
+
+ void SetupSyncPromoCase(int build, int max_group) {
+ std::string question = base::IntToString(build) + ":" +
+ base::IntToString(max_group);
+ UnpackSyncPromo(question);
+ }
+
+ void UnpackSyncPromo(const std::string& question) {
+ std::string json_header =
+ "{ "
+ " \"topic\": {"
+ " \"answers\": ["
+ " {"
+ " \"name\": \"sync_promo\","
+ " \"question\": \"";
+
+ std::string json_footer = "\""
+ " }"
+ " ]"
+ " }"
+ "}";
+
+ scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>(
+ base::JSONReader::Read(json_header + question + json_footer, false)));
+ web_resource_service_->UnpackSyncPromoSignal(*(test_json.get()));
+ }
+
+ private:
+ bool notifications_allowed_;
+ NotificationRegistrar registrar_;
+};
+
// Verifies that custom dates read from a web resource server are written to
// the preferences file.
TEST_F(PromoResourceServiceTest, UnpackLogoSignal) {
@@ -51,8 +123,8 @@ TEST_F(PromoResourceServiceTest, UnpackLogoSignal) {
" ]"
" }"
"}";
- scoped_ptr<DictionaryValue> test_json(static_cast<DictionaryValue*>(
- base::JSONReader::Read(json, false)));
+ scoped_ptr<DictionaryValue> test_json(
+ static_cast<DictionaryValue*>(base::JSONReader::Read(json, false)));
// Check that prefs are set correctly.
web_resource_service_->UnpackLogoSignal(*(test_json.get()));
@@ -669,6 +741,121 @@ TEST_F(PromoResourceServiceTest, UnpackWebStoreSignalHttpLogo) {
EXPECT_EQ(GURL(""), AppsPromo::GetSourcePromoLogoURL());
}
+// Don't run sync promo unpacking tests on ChromeOS as on that plaform
+// PromoResourceService::UnpackSyncPromoSignal() basically just no-ops.
+#if !defined(OS_CHROMEOS)
+TEST_F(SyncPromoTest, UnpackSyncPromoSignal) {
+ PrefService* prefs = profile_.GetPrefs();
+ ASSERT_TRUE(prefs != NULL);
+
+ // It's OK if we get notifications now, so just allow all.
+ allow_notifications(true);
+
+ // Test on by default (currently should be false).
+ EXPECT_FALSE(PromoResourceService::CanShowSyncPromo(&profile_));
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroup));
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroupMax));
+
+ // Non-targeted build.
+ ClearSyncPromoPrefs();
+ SetupSyncPromoCase(2, 50);
+ EXPECT_FALSE(PromoResourceService::CanShowSyncPromo(&profile_));
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroup));
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroupMax));
+
+ // Targeted build, doesn't create bucket and doesn't show promo because
+ // groupMax < group.
+ ClearSyncPromoPrefs();
+ SetupSyncPromoCase(1, 0);
+ EXPECT_FALSE(PromoResourceService::CanShowSyncPromo(&profile_));
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroupMax), 0);
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroup));
+
+ // Targeted build, max_group = 50, ensure group pref created and within the
+ // group bounds.
+ ClearSyncPromoPrefs();
+ SetupSyncPromoCase(1, 50);
+ PromoResourceService::CanShowSyncPromo(&profile_);
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroupMax), 50);
+ EXPECT_TRUE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroup));
+ EXPECT_GT(prefs->GetInteger(prefs::kNTPSyncPromoGroup), 0);
+ EXPECT_LE(prefs->GetInteger(prefs::kNTPSyncPromoGroup), 100);
+
+ // Set user group = 50, now shows promo.
+ prefs->SetInteger(prefs::kNTPSyncPromoGroup, 50);
+ EXPECT_TRUE(PromoResourceService::CanShowSyncPromo(&profile_));
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroup), 50);
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroupMax), 50);
+
+ // Bump user group, ensure that we should not show promo.
+ prefs->SetInteger(prefs::kNTPSyncPromoGroup, 51);
+ EXPECT_FALSE(PromoResourceService::CanShowSyncPromo(&profile_));
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroup), 51);
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroupMax), 50);
+
+ // If the max group gets bumped to the user's group (or above), it should
+ // show.
+ prefs->SetInteger(prefs::kNTPSyncPromoGroupMax, 51);
+ EXPECT_TRUE(PromoResourceService::CanShowSyncPromo(&profile_));
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroup), 51);
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroupMax), 51);
+
+ // Reduce max group.
+ prefs->SetInteger(prefs::kNTPSyncPromoGroupMax, 49);
+ EXPECT_FALSE(PromoResourceService::CanShowSyncPromo(&profile_));
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroup), 51);
+ EXPECT_EQ(prefs->GetInteger(prefs::kNTPSyncPromoGroupMax), 49);
+
+ // Ignore non-targeted builds.
+ prefs->SetInteger(prefs::kNTPSyncPromoGroup, 50);
+ prefs->SetInteger(prefs::kNTPSyncPromoGroupMax, 75);
+ EXPECT_TRUE(PromoResourceService::CanShowSyncPromo(&profile_));
+ SetupSyncPromoCase(2, 25);
+ // Make sure the prefs are deleted.
+ EXPECT_FALSE(PromoResourceService::CanShowSyncPromo(&profile_));
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroup));
+ EXPECT_FALSE(prefs->HasPrefPath(prefs::kNTPSyncPromoGroupMax));
+}
+
+// Throw random stuff at UnpackSyncPromoSignal and make sure no segfaults or
+// other issues and that the prefs were cleared.
+TEST_F(SyncPromoTest, UnpackSyncPromoSignalInvalid) {
+ // We're not testing these here, so ignore them.
+ allow_notifications(true);
+
+ // Empty.
+ InvalidTestCase("");
+
+ // Negative numbers.
+ InvalidTestCase("-5:-6");
+
+ // An extra field.
+ InvalidTestCase("1:0:1");
+
+ // A ton of separators.
+ InvalidTestCase("::::::");
+
+ // Really big numbers.
+ InvalidTestCase("68719476737:68719476737");
+
+ // UTF-8 chars.
+ InvalidTestCase("だからって馬鹿に:してるの?怒る友人");
+}
+
+TEST_F(SyncPromoTest, UnpackSyncPromoSignalNotify) {
+ // Ensure no notifications are sent.
+ ClearSyncPromoPrefs();
+ allow_notifications(false);
+ SetupSyncPromoCase(2, 50);
+ SetupSyncPromoCase(1, 0);
+ SetupSyncPromoCase(1, 100);
+
+ // Expect a notification to be called when the promo is disabled.
+ allow_notifications(true);
+ SetupSyncPromoCase(2, 0);
+}
+#endif // !defined(OS_CHROMEOS)
+
TEST_F(PromoResourceServiceTest, IsBuildTargetedTest) {
// canary
const chrome::VersionInfo::Channel canary =
@@ -710,4 +897,8 @@ TEST_F(PromoResourceServiceTest, IsBuildTargetedTest) {
EXPECT_FALSE(PromoResourceService::IsBuildTargeted(stable, 8));
EXPECT_FALSE(PromoResourceService::IsBuildTargeted(stable, 11));
EXPECT_TRUE(PromoResourceService::IsBuildTargeted(stable, 12));
+
+ // invalid
+ EXPECT_FALSE(PromoResourceService::IsBuildTargeted(stable, -1));
+ EXPECT_FALSE(PromoResourceService::IsBuildTargeted(stable, INT_MAX));
}
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 0176499..c5efb78 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1300,6 +1300,13 @@ const char kNTPPromoLine[] = "ntp.promo_line";
const char kNTPPromoStart[] = "ntp.promo_start";
const char kNTPPromoEnd[] = "ntp.promo_end";
+// A randomly generated group created to control the number of users we show the
+// sync promo to on the NTP.
+const char kNTPSyncPromoGroup[] = "ntp.sync_promo.group";
+
+// The maximum allowable group that can be shown the sync promotion on the NTP.
+const char kNTPSyncPromoGroupMax[] = "ntp.sync_promo.group_max";
+
// Boolean indicating whether the web store is active for the current locale.
const char kNTPWebStoreEnabled[] = "ntp.webstore_enabled";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 676a37a..f5e7c50 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -464,6 +464,8 @@ extern const char kNTPPromoGroupMax[];
extern const char kNTPPromoViews[];
extern const char kNTPPromoViewsMax[];
extern const char kNTPPromoBuild[];
+extern const char kNTPSyncPromoGroup[];
+extern const char kNTPSyncPromoGroupMax[];
extern const char kNTPWebStoreEnabled[];
extern const char kNTPWebStorePromoLastId[];
extern const char kNTPWebStorePromoId[];