summaryrefslogtreecommitdiffstats
path: root/chrome/browser/rlz
diff options
context:
space:
mode:
authorrogerta@chromium.org <rogerta@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-30 04:41:11 +0000
committerrogerta@chromium.org <rogerta@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-30 04:41:11 +0000
commit48d28bae330ad492e76a340e3d0b7c02cd049bb1 (patch)
tree0cbd67e5fac6804048be460d81db56231408fba1 /chrome/browser/rlz
parent911f3188a95bda8d6b259e590e8dce22c83eb92a (diff)
downloadchromium_src-48d28bae330ad492e76a340e3d0b7c02cd049bb1.zip
chromium_src-48d28bae330ad492e76a340e3d0b7c02cd049bb1.tar.gz
chromium_src-48d28bae330ad492e76a340e3d0b7c02cd049bb1.tar.bz2
Adding unit tests to RLZ code. Refactoring RLZ code to make it more testable.
There is one new functionality, which is to support the CHROME_HOME_PAGE access point, but in this CL that new function is disabled. However, it is unit tested. BUG=None TEST=See new unit tests Review URL: http://codereview.chromium.org/7736001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98775 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/rlz')
-rw-r--r--chrome/browser/rlz/rlz.cc520
-rw-r--r--chrome/browser/rlz/rlz.h122
-rw-r--r--chrome/browser/rlz/rlz_unittest.cc547
3 files changed, 893 insertions, 296 deletions
diff --git a/chrome/browser/rlz/rlz.cc b/chrome/browser/rlz/rlz.cc
index acdaf7f..1af7d7b 100644
--- a/chrome/browser/rlz/rlz.cc
+++ b/chrome/browser/rlz/rlz.cc
@@ -31,23 +31,78 @@
#include "chrome/common/env_vars.h"
#include "chrome/installer/util/google_update_settings.h"
#include "content/browser/browser_thread.h"
-#include "content/common/notification_registrar.h"
+#include "content/browser/tab_contents/navigation_entry.h"
#include "content/common/notification_service.h"
namespace {
-enum {
- ACCESS_VALUES_STALE, // Possibly new values available.
- ACCESS_VALUES_FRESH // The cached values are current.
-};
+// Organic brands all start with GG, such as GGCM.
+static bool is_organic(const std::wstring& brand) {
+ return (brand.size() < 2) ? false : (brand.substr(0, 2) == L"GG");
+}
+
+void RecordProductEvents(bool first_run, bool google_default_search,
+ bool google_default_homepage, bool already_ran,
+ bool omnibox_used, bool homepage_used) {
+ // Record the installation of chrome. We call this all the time but the rlz
+ // lib should ingore all but the first one.
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ rlz_lib::CHROME_OMNIBOX,
+ rlz_lib::INSTALL);
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ rlz_lib::CHROME_HOME_PAGE,
+ rlz_lib::INSTALL);
+
+ if (!already_ran) {
+ // Do the initial event recording if is the first run or if we have an
+ // empty rlz which means we haven't got a chance to do it.
+ char omnibox_rlz[rlz_lib::kMaxRlzLength + 1];
+ if (!rlz_lib::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, omnibox_rlz,
+ rlz_lib::kMaxRlzLength, NULL)) {
+ omnibox_rlz[0] = 0;
+ }
-// Tracks if we have tried and succeeded sending the ping. This helps us
-// decide if we need to refresh the cached RLZ string.
-volatile int access_values_state = ACCESS_VALUES_STALE;
-base::Lock rlz_lock;
+ // Record if google is the initial search provider and/or home page.
+ if ((first_run || omnibox_rlz[0] == 0) && google_default_search) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ rlz_lib::CHROME_OMNIBOX,
+ rlz_lib::SET_TO_GOOGLE);
+ }
-bool SendFinancialPing(const std::wstring& brand, const std::wstring& lang,
- const std::wstring& referral, bool exclude_id) {
+ char homepage_rlz[rlz_lib::kMaxRlzLength + 1];
+ if (!rlz_lib::GetAccessPointRlz(rlz_lib::CHROME_HOME_PAGE, homepage_rlz,
+ rlz_lib::kMaxRlzLength, NULL)) {
+ homepage_rlz[0] = 0;
+ }
+
+ if ((first_run || homepage_rlz[0] == 0) && google_default_homepage) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ rlz_lib::CHROME_HOME_PAGE,
+ rlz_lib::SET_TO_GOOGLE);
+ }
+ }
+
+ // Record first user interaction with the omnibox. We call this all the
+ // time but the rlz lib should ingore all but the first one.
+ if (omnibox_used) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ rlz_lib::CHROME_OMNIBOX,
+ rlz_lib::FIRST_SEARCH);
+ }
+
+ // Record first user interaction with the home page. We call this all the
+ // time but the rlz lib should ingore all but the first one.
+ if (homepage_used) {
+ rlz_lib::RecordProductEvent(rlz_lib::CHROME,
+ rlz_lib::CHROME_HOME_PAGE,
+ rlz_lib::FIRST_SEARCH);
+ }
+}
+
+bool SendFinancialPing(const std::wstring& brand,
+ const std::wstring& lang,
+ const std::wstring& referral,
+ bool exclude_id) {
rlz_lib::AccessPoint points[] = {rlz_lib::CHROME_OMNIBOX,
rlz_lib::CHROME_HOME_PAGE,
rlz_lib::NO_ACCESS_POINT};
@@ -57,7 +112,7 @@ bool SendFinancialPing(const std::wstring& brand, const std::wstring& lang,
// If chrome has been reactivated, send a ping for this brand as well.
// We ignore the return value of SendFinancialPing() since we'll try again
- // later anyway. Callers of this function are only interested in whether
+ // later anyway. Callers of this function are only interested in whether
// the ping for the main brand succeeded or not.
std::wstring reactivation_brand;
if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand)) {
@@ -74,248 +129,196 @@ bool SendFinancialPing(const std::wstring& brand, const std::wstring& lang,
lang_ascii.c_str(), exclude_id, NULL, true);
}
-// This class leverages the AutocompleteEditModel notification to know when
-// the user first interacted with the omnibox and set a global accordingly.
-class OmniBoxUsageObserver : public NotificationObserver {
- public:
- OmniBoxUsageObserver(bool first_run, bool send_ping_immediately,
- bool google_default_search)
- : first_run_(first_run),
- send_ping_immediately_(send_ping_immediately),
- google_default_search_(google_default_search) {
- registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
- NotificationService::AllSources());
- // If instant is enabled we'll start searching as soon as the user starts
- // typing in the omnibox (which triggers INSTANT_CONTROLLER_UPDATED).
- registrar_.Add(this, chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
- NotificationService::AllSources());
- omnibox_used_ = false;
- DCHECK(!instance_);
- instance_ = this;
- }
-
- virtual void Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details);
-
- static bool used() {
- return omnibox_used_;
- }
-
- // Deletes the single instance of OmniBoxUsageObserver.
- static void DeleteInstance() {
- delete instance_;
- }
+} // namespace
- private:
- // Dtor is private so the object cannot be created on the stack.
- ~OmniBoxUsageObserver() {
- instance_ = NULL;
- }
+RLZTracker* RLZTracker::tracker_ = NULL;
- static bool omnibox_used_;
+// static
+RLZTracker* RLZTracker::GetInstance() {
+ return tracker_ ? tracker_ : Singleton<RLZTracker>::get();
+}
- // There should only be one instance created at a time, and instance_ points
- // to that instance.
- // NOTE: this is only non-null for the amount of time it is needed. Once the
- // instance_ is no longer needed (or Chrome is exiting), this is null.
- static OmniBoxUsageObserver* instance_;
+RLZTracker::RLZTracker()
+ : first_run_(false),
+ send_ping_immediately_(false),
+ google_default_search_(false),
+ already_ran_(false),
+ omnibox_used_(false),
+ homepage_used_(false) {
+}
- NotificationRegistrar registrar_;
- bool first_run_;
- bool send_ping_immediately_;
- bool google_default_search_;
-};
+RLZTracker::~RLZTracker() {
+}
-bool OmniBoxUsageObserver::omnibox_used_ = false;
-OmniBoxUsageObserver* OmniBoxUsageObserver::instance_ = NULL;
+bool RLZTracker::InitRlzDelayed(bool first_run, int delay,
+ bool google_default_search,
+ bool google_default_homepage) {
+ return GetInstance()->Init(first_run, delay, google_default_search,
+ google_default_homepage);
+}
-// This task is run in the file thread, so to not block it for a long time
-// we use a throwaway thread to do the blocking url request.
-class DailyPingTask : public Task {
- public:
- virtual ~DailyPingTask() {
- }
- virtual void Run() {
- // We use a transient thread because we have no guarantees about
- // how long the RLZ lib can block us.
- _beginthread(PingNow, 0, NULL);
- }
+bool RLZTracker::Init(bool first_run, int delay, bool google_default_search,
+ bool google_default_homepage) {
+ first_run_ = first_run;
+ google_default_search_ = google_default_search;
+ google_default_homepage_ = google_default_homepage;
- private:
- // Causes a ping to the server using WinInet.
- static void _cdecl PingNow(void*) {
- // Needs to be evaluated. See http://crbug.com/62328.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
-
- std::wstring lang;
- GoogleUpdateSettings::GetLanguage(&lang);
- if (lang.empty())
- lang = L"en";
- std::wstring brand;
- GoogleUpdateSettings::GetBrand(&brand);
- std::wstring referral;
- GoogleUpdateSettings::GetReferral(&referral);
- if (SendFinancialPing(brand, lang, referral, is_organic(brand))) {
- base::AutoLock lock(rlz_lock);
- access_values_state = ACCESS_VALUES_STALE;
- GoogleUpdateSettings::ClearReferral();
- }
+ // A negative delay means that a financial ping should be sent immediately
+ // after a first search is recorded, without waiting for the next restart
+ // of chrome. However, we only want this behaviour on first run.
+ send_ping_immediately_ = false;
+ if (delay < 0) {
+ send_ping_immediately_ = true;
+ delay = -delay;
}
- // Organic brands all start with GG, such as GGCM.
- static bool is_organic(const std::wstring& brand) {
- return (brand.size() < 2) ? false : (brand.substr(0, 2) == L"GG");
- }
-};
-
-// Performs late RLZ initialization and RLZ event recording for chrome.
-// This task needs to run on the UI thread.
-class DelayedInitTask : public Task {
- public:
- DelayedInitTask(bool first_run, bool google_default_search)
- : first_run_(first_run),
- google_default_search_(google_default_search) {
- }
- virtual ~DelayedInitTask() {
- }
- virtual void Run() {
- // For non-interactive tests we don't do the rest of the initialization
- // because sometimes the very act of loading the dll causes QEMU to crash.
- if (::GetEnvironmentVariableW(ASCIIToWide(env_vars::kHeadless).c_str(),
- NULL, 0)) {
- return;
- }
- // For organic brandcodes do not use rlz at all. Empty brandcode usually
- // means a chromium install. This is ok.
- std::wstring brand;
- if (!GoogleUpdateSettings::GetBrand(&brand) || brand.empty() ||
- GoogleUpdateSettings::IsOrganic(brand))
- return;
-
- RecordProductEvents(first_run_, google_default_search_, already_ran_);
-
- // If chrome has been reactivated, record the events for this brand
- // as well.
- std::wstring reactivation_brand;
- if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand)) {
- rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str());
- RecordProductEvents(first_run_, google_default_search_, already_ran_);
- }
+ // Maximum and minimum delay we would allow to be set through master
+ // preferences. Somewhat arbitrary, may need to be adjusted in future.
+ const int kMaxDelay = 200 * 1000;
+ const int kMinDelay = 20 * 1000;
- already_ran_ = true;
+ delay *= 1000;
+ delay = (delay < kMinDelay) ? kMinDelay : delay;
+ delay = (delay > kMaxDelay) ? kMaxDelay : delay;
- // Schedule the daily RLZ ping.
- MessageLoop::current()->PostTask(FROM_HERE, new DailyPingTask());
- }
+ // Register for notifications from the omnibox so that we can record when
+ // the user performs a first search.
+ registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
+ NotificationService::AllSources());
+ // If instant is enabled we'll start searching as soon as the user starts
+ // typing in the omnibox (which triggers INSTANT_CONTROLLER_UPDATED).
+ registrar_.Add(this, chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
+ NotificationService::AllSources());
+
+ // Register for notifications from navigations, to see if the user has used
+ // the home page.
+ registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
+ NotificationService::AllSources());
+
+ ScheduleDelayedInit(delay);
+ return true;
+}
- private:
- static void RecordProductEvents(bool first_run, bool google_default_search,
- bool already_ran) {
- // Record the installation of chrome. We call this all the time but the rlz
- // lib should ingore all but the first one.
- rlz_lib::RecordProductEvent(rlz_lib::CHROME,
- rlz_lib::CHROME_OMNIBOX,
- rlz_lib::INSTALL);
- rlz_lib::RecordProductEvent(rlz_lib::CHROME,
- rlz_lib::CHROME_HOME_PAGE,
- rlz_lib::INSTALL);
+void RLZTracker::ScheduleDelayedInit(int delay) {
+ BrowserThread::PostDelayedTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ NewRunnableMethod(this, &RLZTracker::DelayedInit),
+ delay);
+}
- // Do the initial event recording if is the first run or if we have an
- // empty rlz which means we haven't got a chance to do it.
- char omnibox_rlz[rlz_lib::kMaxRlzLength + 1];
- if (!rlz_lib::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, omnibox_rlz,
- rlz_lib::kMaxRlzLength, NULL)) {
- omnibox_rlz[0] = 0;
- }
+void RLZTracker::DelayedInit() {
+ // For organic brandcodes do not use rlz at all. Empty brandcode usually
+ // means a chromium install. This is ok.
+ std::wstring brand;
+ if (!GoogleUpdateSettings::GetBrand(&brand) || brand.empty() ||
+ GoogleUpdateSettings::IsOrganic(brand))
+ return;
- // Record if google is the initial search provider.
- if ((first_run || omnibox_rlz[0] == 0) && google_default_search &&
- !already_ran) {
- rlz_lib::RecordProductEvent(rlz_lib::CHROME,
- rlz_lib::CHROME_OMNIBOX,
- rlz_lib::SET_TO_GOOGLE);
- }
+ RecordProductEvents(first_run_, google_default_search_,
+ google_default_homepage_, already_ran_,
+ omnibox_used_, homepage_used_);
- // Record first user interaction with the omnibox. We call this all the
- // time but the rlz lib should ingore all but the first one.
- if (OmniBoxUsageObserver::used()) {
- rlz_lib::RecordProductEvent(rlz_lib::CHROME,
- rlz_lib::CHROME_OMNIBOX,
- rlz_lib::FIRST_SEARCH);
- }
+ // If chrome has been reactivated, record the events for this brand
+ // as well.
+ std::wstring reactivation_brand;
+ if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand)) {
+ rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str());
+ RecordProductEvents(first_run_, google_default_search_,
+ google_default_homepage_, already_ran_,
+ omnibox_used_, homepage_used_);
}
- // Flag that remembers if the delayed task already ran or not. This is
- // needed only in the first_run case, since we don't want to record the
- // set-to-google event more than once. We need to worry about this event
- // (and not the others) because it is not a stateful RLZ event.
- static bool already_ran_;
-
- bool first_run_;
+ already_ran_ = true;
- // True if Google is the default search engine for the first profile starting
- // in a browser during first run.
- bool google_default_search_;
+ ScheduleFinancialPing();
+}
-};
+void RLZTracker::ScheduleFinancialPing() {
+ _beginthread(PingNow, 0, this);
+}
-bool DelayedInitTask::already_ran_ = false;
+// static
+void _cdecl RLZTracker::PingNow(void* arg) {
+ RLZTracker* tracker = reinterpret_cast<RLZTracker*>(arg);
+ tracker->PingNowImpl();
+}
-void OmniBoxUsageObserver::Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) {
+void RLZTracker::PingNowImpl() {
// Needs to be evaluated. See http://crbug.com/62328.
base::ThreadRestrictions::ScopedAllowIO allow_io;
- // Try to record event now, else set the flag to try later when we
- // attempt the ping.
- if (!RLZTracker::RecordProductEvent(rlz_lib::CHROME,
- rlz_lib::CHROME_OMNIBOX,
- rlz_lib::FIRST_SEARCH))
- omnibox_used_ = true;
- else if (send_ping_immediately_) {
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE, new DelayedInitTask(first_run_,
- google_default_search_));
+ std::wstring lang;
+ GoogleUpdateSettings::GetLanguage(&lang);
+ if (lang.empty())
+ lang = L"en";
+ std::wstring brand;
+ GoogleUpdateSettings::GetBrand(&brand);
+ std::wstring referral;
+ GoogleUpdateSettings::GetReferral(&referral);
+ if (SendFinancialPing(brand, lang, referral, is_organic(brand))) {
+ GoogleUpdateSettings::ClearReferral();
+ base::AutoLock lock(cache_lock_);
+ rlz_cache_.clear();
}
+}
- delete this;
+bool RLZTracker::SendFinancialPing(const std::wstring& brand,
+ const std::wstring& lang,
+ const std::wstring& referral,
+ bool exclude_id) {
+ return ::SendFinancialPing(brand, lang, referral, exclude_id);
}
-} // namespace
+void RLZTracker::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // Needs to be evaluated. See http://crbug.com/62328.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
-bool RLZTracker::InitRlzDelayed(bool first_run, int delay,
- bool google_default_search) {
- // A negative delay means that a financial ping should be sent immediately
- // after a first search is recorded, without waiting for the next restart
- // of chrome. However, we only want this behaviour on first run.
- bool send_ping_immediately = false;
- if (delay < 0) {
- send_ping_immediately = true;
- delay = -delay;
+ rlz_lib::AccessPoint point;
+ bool* record_used = NULL;
+ bool call_record = false;
+
+ switch (type) {
+ case chrome::NOTIFICATION_OMNIBOX_OPENED_URL:
+ case chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED:
+ point = rlz_lib::CHROME_OMNIBOX;
+ record_used = &omnibox_used_;
+ call_record = true;
+
+ registrar_.Remove(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
+ NotificationService::AllSources());
+ registrar_.Remove(this, chrome::NOTIFICATION_INSTANT_CONTROLLER_UPDATED,
+ NotificationService::AllSources());
+ break;
+ case content::NOTIFICATION_NAV_ENTRY_PENDING: {
+ const NavigationEntry* entry = Details<NavigationEntry>(details).ptr();
+ if (entry != NULL &&
+ ((entry->transition_type() & RLZ_PAGETRANSITION_HOME_PAGE) != 0)) {
+ point = rlz_lib::CHROME_HOME_PAGE;
+ record_used = &homepage_used_;
+ call_record = true;
+
+ registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_PENDING,
+ NotificationService::AllSources());
+ }
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
}
- // Maximum and minimum delay we would allow to be set through master
- // preferences. Somewhat arbitrary, may need to be adjusted in future.
- const int kMaxDelay = 200 * 1000;
- const int kMinDelay = 20 * 1000;
-
- delay *= 1000;
- delay = (delay < kMinDelay) ? kMinDelay : delay;
- delay = (delay > kMaxDelay) ? kMaxDelay : delay;
-
- if (!OmniBoxUsageObserver::used())
- new OmniBoxUsageObserver(first_run, send_ping_immediately,
- google_default_search);
-
- // Schedule the delayed init items.
- BrowserThread::PostDelayedTask(
- BrowserThread::FILE,
- FROM_HERE,
- new DelayedInitTask(first_run, google_default_search),
- delay);
- return true;
+ if (call_record) {
+ // Try to record event now, else set the flag to try later when we
+ // attempt the ping.
+ if (!RecordProductEvent(rlz_lib::CHROME, point, rlz_lib::FIRST_SEARCH))
+ *record_used = true;
+ else if (send_ping_immediately_ && point == rlz_lib::CHROME_OMNIBOX) {
+ ScheduleDelayedInit(0);
+ }
+ }
}
bool RLZTracker::RecordProductEvent(rlz_lib::Product product,
@@ -333,52 +336,59 @@ bool RLZTracker::RecordProductEvent(rlz_lib::Product product,
return ret;
}
-// We implement caching of the answer of get_access_point() if the request
-// is for CHROME_OMNIBOX. If we had a successful ping, then we update the
-// cached value.
-
+// GetAccessPointRlz() caches RLZ strings for all access points. If we had
+// a successful ping, then we update the cached value.
bool RLZTracker::GetAccessPointRlz(rlz_lib::AccessPoint point,
std::wstring* rlz) {
- static std::wstring cached_ommibox_rlz;
- if (rlz_lib::CHROME_OMNIBOX == point) {
- base::AutoLock lock(rlz_lock);
- if (access_values_state == ACCESS_VALUES_FRESH) {
- *rlz = cached_ommibox_rlz;
+ return GetInstance()->GetAccessPointRlzImpl(point, rlz);
+}
+
+// GetAccessPointRlz() caches RLZ strings for all access points. If we had
+// a successful ping, then we update the cached value.
+bool RLZTracker::GetAccessPointRlzImpl(rlz_lib::AccessPoint point,
+ std::wstring* rlz) {
+ // If the RLZ string for the specified access point is already cached,
+ // simply return its value.
+ {
+ base::AutoLock lock(cache_lock_);
+ if (rlz_cache_.find(point) != rlz_cache_.end()) {
+ if (rlz)
+ *rlz = rlz_cache_[point];
return true;
}
}
- // Make sure we don't access disk outside of the file context.
+ // Make sure we don't access disk outside of the I/O thread.
// In such case we repost the task on the right thread and return error.
- if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
- // Caching of access points is now only implemented for the CHROME_OMNIBOX.
- // Thus it is not possible to call this function on another thread for
- // other access points until proper caching for these has been implemented
- // and the code that calls this function can handle synchronous fetching
- // of the access point.
- DCHECK_EQ(rlz_lib::CHROME_OMNIBOX, point);
-
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- NewRunnableFunction(&RLZTracker::GetAccessPointRlz,
- point, &cached_ommibox_rlz));
- rlz->erase();
- return false;
- }
+ if (ScheduleGetAccessPointRlz(point))
+ return false;
char str_rlz[rlz_lib::kMaxRlzLength + 1];
if (!rlz_lib::GetAccessPointRlz(point, str_rlz, rlz_lib::kMaxRlzLength, NULL))
return false;
- *rlz = ASCIIToWide(std::string(str_rlz));
- if (rlz_lib::CHROME_OMNIBOX == point) {
- base::AutoLock lock(rlz_lock);
- cached_ommibox_rlz.assign(*rlz);
- access_values_state = ACCESS_VALUES_FRESH;
- }
+
+ std::wstring rlz_local(ASCIIToWide(std::string(str_rlz)));
+ if (rlz)
+ *rlz = rlz_local;
+
+ base::AutoLock lock(cache_lock_);
+ rlz_cache_[point] = rlz_local;
+ return true;
+}
+
+bool RLZTracker::ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::FILE))
+ return false;
+
+ std::wstring* not_used = NULL;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(&RLZTracker::GetAccessPointRlz, point, not_used));
return true;
}
// static
void RLZTracker::CleanupRlz() {
- OmniBoxUsageObserver::DeleteInstance();
+ GetInstance()->rlz_cache_.clear();
+ GetInstance()->registrar_.RemoveAll();
}
diff --git a/chrome/browser/rlz/rlz.h b/chrome/browser/rlz/rlz.h
index ca2658d..cbbec70 100644
--- a/chrome/browser/rlz/rlz.h
+++ b/chrome/browser/rlz/rlz.h
@@ -10,9 +10,15 @@
#if defined(OS_WIN)
+#include <map>
#include <string>
#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/task.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
#include "rlz/win/lib/rlz_lib.h"
// RLZ is a library which is used to measure distribution scenarios.
@@ -25,18 +31,16 @@
// For partner or bundled installs, the RLZ might send more information
// according to the terms disclosed in the EULA.
-class RLZTracker {
-
+class RLZTracker : public NotificationObserver {
public:
- // Like InitRlz() this function initializes the RLZ library services for use
- // in chrome. Besides binding the dll, it schedules a delayed task (delayed
- // by |delay| seconds) that performs the ping and registers some events
- // when 'first-run' is true.
+ // Initializes the RLZ library services for use in chrome. Schedules a
+ // delayed task (delayed by |delay| seconds) that performs the ping and
+ // registers some events when 'first-run' is true.
//
- // If the chrome brand is organic (no partners) then the RLZ library is not
- // loaded or initialized and the pings don't ocurr.
+ // If the chrome brand is organic (no partners) then the pings don't occur.
static bool InitRlzDelayed(bool first_run, int delay,
- bool google_default_search);
+ bool google_default_search,
+ bool google_default_homepage);
// Records an RLZ event. Some events can be access point independent.
// Returns false it the event could not be recorded. Requires write access
@@ -53,10 +57,108 @@ class RLZTracker {
// Invoked during shutdown to clean up any state created by RLZTracker.
static void CleanupRlz();
+ // This method is public for use by the Singleton class.
+ static RLZTracker* GetInstance();
+
+ // The following methods are made protected so that they can be used for
+ // testing purposes. Production code should never need to call these.
+ protected:
+ RLZTracker();
+ ~RLZTracker();
+
+ // This is a temporary constant used here until the home page change is
+ // committed, which will happen before 2011-09-01. This constant will be
+ // replaced with PageTransition::HOME_PAGE.
+ static const int RLZ_PAGETRANSITION_HOME_PAGE = 0x02000000;
+
+ // Thread function entry point, see ScheduleFinancialPing(). Assumes argument
+ // is a pointer to an RLZTracker.
+ static void _cdecl PingNow(void* tracker);
+
+ // Performs initialization of RLZ tracker that is purposefully delayed so
+ // that it does not interfere with chrome startup time.
+ virtual void DelayedInit();
+
+ // NotificationObserver implementation:
+ virtual void Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ // Used by test code to override the default RLZTracker instance returned
+ // by GetInstance().
+ void set_tracker(RLZTracker* tracker) {
+ tracker_ = tracker;
+ }
+
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(RLZTracker);
+ friend struct DefaultSingletonTraits<RLZTracker>;
+ friend class base::RefCountedThreadSafe<RLZTracker>;
+
+ // Implementation called from InitRlzDelayed() static method.
+ bool Init(bool first_run, int delay, bool google_default_search,
+ bool google_default_homepage);
+
+ // Implementation called from RecordProductEvent() static method.
+ bool GetAccessPointRlzImpl(rlz_lib::AccessPoint point, std::wstring* rlz);
+
+ // Schedules the delayed initialization. This method is virtual to allow
+ // tests to override how the scheduling is done.
+ virtual void ScheduleDelayedInit(int delay);
+
+ // Schedules a call to rlz_lib::SendFinancialPing(). This method is virtual
+ // to allow tests to override how the scheduling is done.
+ virtual void ScheduleFinancialPing();
+
+ // Schedules a call to GetAccessPointRlz() on the I/O thread if the current
+ // thread is not already the I/O thread, otherwise does nothing. Returns
+ // true if the call was scheduled, and false otherwise. This method is
+ // virtual to allow tests to override how the scheduling is done.
+ virtual bool ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point);
+
+ // Sends the financial ping to the RLZ servers and invalidates the RLZ string
+ // cache since the response from the RLZ server may have changed then.
+ void PingNowImpl();
+
+ // Sends the financial ping to the RLZ servers. This method is virtual to
+ // allow tests to override.
+ virtual bool SendFinancialPing(const std::wstring& brand,
+ const std::wstring& lang,
+ const std::wstring& referral,
+ bool exclude_id);
+
+ // Tracker used for testing purposes only. If this value is non-NULL, it
+ // will be returned from GetInstance() instead of the regular singleton.
+ static RLZTracker* tracker_;
+
+ // Configuation data for RLZ tracker. Set by call to Init().
+ bool first_run_;
+ bool send_ping_immediately_;
+ bool google_default_search_;
+ bool google_default_homepage_;
+
+ // Keeps track if the RLZ tracker has already performed its delayed
+ // initialization.
+ bool already_ran_;
+
+ // Keeps a cache of RLZ access point strings, since they rarely change.
+ // The cache must be protected by a lock since it may be accessed from
+ // the UI thread for reading and the IO thread for reading and/or writing.
+ base::Lock cache_lock_;
+ std::map<rlz_lib::AccessPoint, std::wstring> rlz_cache_;
+
+ // Keeps track of whether the omnibox or host page have been used.
+ bool omnibox_used_;
+ bool homepage_used_;
+
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(RLZTracker);
};
+// The RLZTracker is a singleton object that outlives any runnable tasks
+// that will be queued up.
+DISABLE_RUNNABLE_METHOD_REFCOUNT(RLZTracker);
+
#endif // defined(OS_WIN)
#endif // CHROME_BROWSER_RLZ_RLZ_H_
diff --git a/chrome/browser/rlz/rlz_unittest.cc b/chrome/browser/rlz/rlz_unittest.cc
index 6a5e85d..96816a4 100644
--- a/chrome/browser/rlz/rlz_unittest.cc
+++ b/chrome/browser/rlz/rlz_unittest.cc
@@ -4,48 +4,533 @@
#include "chrome/browser/rlz/rlz.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stringprintf.h"
#include "base/path_service.h"
+#include "base/test/test_reg_util_win.h"
+#include "base/utf_string_conversions.h"
#include "base/win/registry.h"
+#include "chrome/browser/autocomplete/autocomplete.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/env_vars.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/google_update_constants.h"
+#include "content/browser/tab_contents/navigation_entry.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_source.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::win::RegKey;
+using registry_util::RegistryOverrideManager;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+using testing::AssertionFailure;
namespace {
-// Gets rid of registry leftovers from testing. Returns false if there
-// is nothing to clean.
-bool CleanValue(const wchar_t* key_name, const wchar_t* value) {
- RegKey key;
- if (key.Open(HKEY_CURRENT_USER, key_name, KEY_READ | KEY_WRITE) !=
- ERROR_SUCCESS)
- return false;
- EXPECT_EQ(ERROR_SUCCESS, key.DeleteValue(value));
- return true;
+// Registry path to overridden hive.
+const wchar_t kRlzTempHkcu[] = L"rlz_hkcu";
+const wchar_t kRlzTempHklm[] = L"rlz_hklm";
+
+// Dummy RLZ string for the access points.
+const char kOmniboxRlzString[] = "test_omnibox";
+const char kHomepageRlzString[] = "test_homepage";
+
+// Some helper macros to test it a string contains/does not contain a substring.
+
+AssertionResult CmpHelperSTRC(const char* str_expression,
+ const char* substr_expression,
+ const char* str,
+ const char* substr) {
+ if (NULL != strstr(str, substr)) {
+ return AssertionSuccess();
+ }
+
+ return AssertionFailure() << "Expected: (" << substr_expression << ") in ("
+ << str_expression << "), actual: '"
+ << substr << "' not in '" << str << "'";
}
-// The chrome events RLZ key lives here.
-const wchar_t kKeyName[] = L"Software\\Google\\Common\\Rlz\\Events\\C";
+AssertionResult CmpHelperSTRNC(const char* str_expression,
+ const char* substr_expression,
+ const char* str,
+ const char* substr) {
+ if (NULL == strstr(str, substr)) {
+ return AssertionSuccess();
+ }
+
+ return AssertionFailure() << "Expected: (" << substr_expression
+ << ") not in (" << str_expression << "), actual: '"
+ << substr << "' in '" << str << "'";
+}
+
+#define EXPECT_STR_CONTAINS(str, substr) \
+ EXPECT_PRED_FORMAT2(CmpHelperSTRC, str, substr)
+
+#define EXPECT_STR_NOT_CONTAIN(str, substr) \
+ EXPECT_PRED_FORMAT2(CmpHelperSTRNC, str, substr)
} // namespace
-TEST(RlzLibTest, RecordProductEvent) {
- DWORD recorded_value = 0;
- EXPECT_TRUE(RLZTracker::RecordProductEvent(rlz_lib::CHROME,
- rlz_lib::CHROME_OMNIBOX, rlz_lib::FIRST_SEARCH));
- const wchar_t kEvent1[] = L"C1F";
- RegKey key1;
- EXPECT_EQ(ERROR_SUCCESS, key1.Open(HKEY_CURRENT_USER, kKeyName, KEY_READ));
- EXPECT_EQ(ERROR_SUCCESS, key1.ReadValueDW(kEvent1, &recorded_value));
- EXPECT_EQ(1, recorded_value);
- EXPECT_TRUE(CleanValue(kKeyName, kEvent1));
-
- EXPECT_TRUE(RLZTracker::RecordProductEvent(rlz_lib::CHROME,
- rlz_lib::CHROME_HOME_PAGE, rlz_lib::SET_TO_GOOGLE));
- const wchar_t kEvent2[] = L"C2S";
- RegKey key2;
- EXPECT_EQ(ERROR_SUCCESS, key2.Open(HKEY_CURRENT_USER, kKeyName, KEY_READ));
- DWORD value = 0;
- EXPECT_EQ(ERROR_SUCCESS, key2.ReadValueDW(kEvent2, &recorded_value));
- EXPECT_EQ(1, recorded_value);
- EXPECT_TRUE(CleanValue(kKeyName, kEvent2));
+// Test class for RLZ tracker. Makes some member functions public and
+// overrides others to make it easier to test.
+class TestRLZTracker : public RLZTracker {
+ public:
+ using RLZTracker::DelayedInit;
+ using RLZTracker::Observe;
+ using RLZTracker::RLZ_PAGETRANSITION_HOME_PAGE;
+
+ TestRLZTracker() : pingnow_called_(false), assume_io_thread_(false) {
+ set_tracker(this);
+ }
+
+ virtual ~TestRLZTracker() {
+ set_tracker(NULL);
+ }
+
+ bool pingnow_called() const {
+ return pingnow_called_;
+ }
+
+ bool assume_io_thread() const {
+ return assume_io_thread_;
+ }
+
+ void set_assume_io_thread(bool assume_io_thread) {
+ assume_io_thread_ = assume_io_thread;
+ }
+
+ private:
+ virtual void ScheduleDelayedInit(int delay) OVERRIDE {
+ // If the delay is 0, invoke the delayed init now. Otherwise,
+ // don't schedule anything, it will be manually called during tests.
+ if (delay == 0)
+ DelayedInit();
+ }
+
+ virtual void ScheduleFinancialPing() OVERRIDE {
+ PingNow(this);
+ }
+
+ virtual bool ScheduleGetAccessPointRlz(rlz_lib::AccessPoint point) OVERRIDE {
+ return !assume_io_thread_;
+ }
+
+ virtual bool SendFinancialPing(const std::wstring& brand,
+ const std::wstring& lang,
+ const std::wstring& referral,
+ bool exclude_id) OVERRIDE {
+ // Don't ping the server during tests.
+ pingnow_called_ = true;
+ return true;
+ }
+
+ bool pingnow_called_;
+ bool assume_io_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestRLZTracker);
+};
+
+class RlzLibTest : public testing::Test {
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ protected:
+ void SimulateOmniboxUsage();
+ void SimulateHomepageUsage();
+ void InvokeDelayedInit();
+
+ void ExpectEventRecorded(const char* event_name, bool expected);
+ void ExpectRlzPingSent(bool expected);
+
+ TestRLZTracker tracker_;
+ RegistryOverrideManager override_manager_;
+};
+
+void RlzLibTest::SetUp() {
+ testing::Test::SetUp();
+
+ // Before overriding HKLM for the tests, we need to set it up correctly
+ // so that the rlz_lib calls work. This needs to be done before we do the
+ // override.
+
+ std::wstring temp_hklm_path = base::StringPrintf(
+ L"%ls\\%ls",
+ RegistryOverrideManager::kTempTestKeyPath,
+ kRlzTempHklm);
+
+ base::win::RegKey hklm;
+ ASSERT_EQ(ERROR_SUCCESS, hklm.Create(HKEY_CURRENT_USER,
+ temp_hklm_path.c_str(),
+ KEY_READ));
+
+ std::wstring temp_hkcu_path = base::StringPrintf(
+ L"%ls\\%ls",
+ RegistryOverrideManager::kTempTestKeyPath,
+ kRlzTempHkcu);
+
+ base::win::RegKey hkcu;
+ ASSERT_EQ(ERROR_SUCCESS, hkcu.Create(HKEY_CURRENT_USER,
+ temp_hkcu_path.c_str(),
+ KEY_READ));
+
+ rlz_lib::InitializeTempHivesForTesting(hklm, hkcu);
+
+ // Its important to override HKLM before HKCU because of the registry
+ // initialization performed above.
+ override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE, kRlzTempHklm);
+ override_manager_.OverrideRegistry(HKEY_CURRENT_USER, kRlzTempHkcu);
+
+ // Make sure a non-organic brand code is set in the registry or the RLZTracker
+ // is pretty much a no-op.
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ std::wstring reg_path = dist->GetStateKey();
+ RegKey key(HKEY_CURRENT_USER, reg_path.c_str(), KEY_SET_VALUE);
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(google_update::kRegRLZBrandField,
+ L"TEST"));
+}
+
+void RlzLibTest::TearDown() {
+ testing::Test::TearDown();
+}
+
+void RlzLibTest::SimulateOmniboxUsage() {
+ tracker_.Observe(chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
+ NotificationService::AllSources(),
+ Details<AutocompleteLog>(NULL));
+}
+
+void RlzLibTest::SimulateHomepageUsage() {
+ NavigationEntry entry(NULL, 0, GURL(), GURL(), string16(),
+ TestRLZTracker::RLZ_PAGETRANSITION_HOME_PAGE);
+ tracker_.Observe(content::NOTIFICATION_NAV_ENTRY_PENDING,
+ NotificationService::AllSources(),
+ Details<NavigationEntry>(&entry));
+}
+
+void RlzLibTest::InvokeDelayedInit() {
+ tracker_.DelayedInit();
+}
+
+void RlzLibTest::ExpectEventRecorded(const char* event_name, bool expected) {
+ char cgi[rlz_lib::kMaxCgiLength];
+ GetProductEventsAsCgi(rlz_lib::CHROME, cgi, arraysize(cgi));
+ if (expected) {
+ EXPECT_STR_CONTAINS(cgi, event_name);
+ } else {
+ EXPECT_STR_NOT_CONTAIN(cgi, event_name);
+ }
+}
+
+void RlzLibTest::ExpectRlzPingSent(bool expected) {
+ EXPECT_EQ(expected, tracker_.pingnow_called());
+}
+
+TEST_F(RlzLibTest, RecordProductEvent) {
+ RLZTracker::RecordProductEvent(rlz_lib::CHROME, rlz_lib::CHROME_OMNIBOX,
+ rlz_lib::FIRST_SEARCH);
+
+ ExpectEventRecorded("C1F", true);
+}
+
+// The events that affect the different RLZ scenarios are the following:
+//
+// A: the user starts chrome for the first time
+// B: the user stops chrome
+// C: the user start a subsequent time
+// D: the user stops chrome again
+// I: the RLZTracker::DelayedInit() method is invoked
+// X: the user performs a search using the omnibox
+// Y: the user performs a search using the home page
+//
+// The events A to D happen in chronological order, but the other events
+// may happen at any point between A-B or C-D, in no particular order.
+//
+// The visible results of the scenarios are:
+//
+// C1I event is recorded
+// C2I event is recorded
+// C1F event is recorded
+// C2F event is recorded
+// C1S event is recorded
+// C2S event is recorded
+// RLZ ping sent
+//
+// Variations on the above scenarios:
+//
+// - if the delay specified to InitRlzDelayed() is negative, then the RLZ
+// ping should be sent out at the time of event X and not wait for I
+//
+// Also want to test that pre-warming the RLZ string cache works correctly.
+
+TEST_F(RlzLibTest, QuickStopAfterStart) {
+ RLZTracker::InitRlzDelayed(true, 20, true, true);
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", false);
+ ExpectEventRecorded("C1S", false);
+ ExpectEventRecorded("C1F", false);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", false);
+ ExpectEventRecorded("C2S", false);
+ ExpectEventRecorded("C2F", false);
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnly) {
+ RLZTracker::InitRlzDelayed(true, 20, true, true);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", true);
+ ExpectEventRecorded("C1S", true);
+ ExpectEventRecorded("C1F", false);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", true);
+ ExpectEventRecorded("C2S", true);
+ ExpectEventRecorded("C2F", false);
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnlyNoFirstRunNoRlzStrings) {
+ RLZTracker::InitRlzDelayed(false, 20, true, true);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", true);
+ ExpectEventRecorded("C1S", true);
+ ExpectEventRecorded("C1F", false);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", true);
+ ExpectEventRecorded("C2S", true);
+ ExpectEventRecorded("C2F", false);
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnlyNoFirstRun) {
+ // Set some dummy RLZ strings to simulate that we already ran before and
+ // performed a successful ping to the RLZ server.
+ rlz_lib::SetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, kOmniboxRlzString);
+ rlz_lib::SetAccessPointRlz(rlz_lib::CHROME_HOME_PAGE, kHomepageRlzString);
+
+ RLZTracker::InitRlzDelayed(false, 20, true, true);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", true);
+ ExpectEventRecorded("C1S", false);
+ ExpectEventRecorded("C1F", false);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", true);
+ ExpectEventRecorded("C2S", false);
+ ExpectEventRecorded("C2F", false);
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, DelayedInitOnlyNoGoogleDefaultSearchOrHomepage) {
+ RLZTracker::InitRlzDelayed(true, 20, false, false);
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", true);
+ ExpectEventRecorded("C1S", false);
+ ExpectEventRecorded("C1F", false);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", true);
+ ExpectEventRecorded("C2S", false);
+ ExpectEventRecorded("C2F", false);
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, OmniboxUsageOnly) {
+ RLZTracker::InitRlzDelayed(true, 20, true, true);
+ SimulateOmniboxUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", false);
+ ExpectEventRecorded("C1S", false);
+ ExpectEventRecorded("C1F", true);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", false);
+ ExpectEventRecorded("C2S", false);
+ ExpectEventRecorded("C2F", false);
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, HomepageUsageOnly) {
+ RLZTracker::InitRlzDelayed(true, 20, true, true);
+ SimulateHomepageUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", false);
+ ExpectEventRecorded("C1S", false);
+ ExpectEventRecorded("C1F", false);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", false);
+ ExpectEventRecorded("C2S", false);
+ ExpectEventRecorded("C2F", true);
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, UsageBeforeDelayedInit) {
+ RLZTracker::InitRlzDelayed(true, 20, true, true);
+ SimulateOmniboxUsage();
+ SimulateHomepageUsage();
+ InvokeDelayedInit();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", true);
+ ExpectEventRecorded("C1S", true);
+ ExpectEventRecorded("C1F", true);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", true);
+ ExpectEventRecorded("C2S", true);
+ ExpectEventRecorded("C2F", true);
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, OmniboxUsageAfterDelayedInit) {
+ RLZTracker::InitRlzDelayed(true, 20, true, true);
+ InvokeDelayedInit();
+ SimulateOmniboxUsage();
+ SimulateHomepageUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", true);
+ ExpectEventRecorded("C1S", true);
+ ExpectEventRecorded("C1F", true);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", true);
+ ExpectEventRecorded("C2S", true);
+ ExpectEventRecorded("C2F", true);
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, OmniboxUsageSendsPingWhenDelayNegative) {
+ RLZTracker::InitRlzDelayed(true, -20, true, true);
+ SimulateOmniboxUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", true);
+ ExpectEventRecorded("C1S", true);
+ ExpectEventRecorded("C1F", true);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", true);
+ ExpectEventRecorded("C2S", true);
+ ExpectEventRecorded("C2F", false);
+
+ ExpectRlzPingSent(true);
+}
+
+TEST_F(RlzLibTest, HomepageUsageDoesNotSendPingWhenDelayNegative) {
+ RLZTracker::InitRlzDelayed(true, -20, true, true);
+ SimulateHomepageUsage();
+
+ // Omnibox events.
+ ExpectEventRecorded("C1I", false);
+ ExpectEventRecorded("C1S", false);
+ ExpectEventRecorded("C1F", false);
+
+ // Home page events.
+ ExpectEventRecorded("C2I", false);
+ ExpectEventRecorded("C2S", false);
+ ExpectEventRecorded("C2F", true);
+
+ ExpectRlzPingSent(false);
+}
+
+TEST_F(RlzLibTest, GetAccessPointRlzOnIoThread) {
+ // Set dummy RLZ string.
+ rlz_lib::SetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, kOmniboxRlzString);
+
+ std::wstring rlz;
+
+ tracker_.set_assume_io_thread(true);
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, WideToUTF8(rlz).c_str());
+}
+
+TEST_F(RlzLibTest, GetAccessPointRlzNotOnIoThread) {
+ // Set dummy RLZ string.
+ rlz_lib::SetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, kOmniboxRlzString);
+
+ std::wstring rlz;
+
+ tracker_.set_assume_io_thread(false);
+ EXPECT_FALSE(RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz));
+}
+
+TEST_F(RlzLibTest, GetAccessPointRlzIsCached) {
+ // Set dummy RLZ string.
+ rlz_lib::SetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, kOmniboxRlzString);
+
+ std::wstring rlz;
+
+ tracker_.set_assume_io_thread(false);
+ EXPECT_FALSE(RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz));
+
+ tracker_.set_assume_io_thread(true);
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, WideToUTF8(rlz).c_str());
+
+ tracker_.set_assume_io_thread(false);
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, WideToUTF8(rlz).c_str());
+}
+
+TEST_F(RlzLibTest, PingInvalidatesRlzCache) {
+ // Set dummy RLZ string.
+ rlz_lib::SetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, kOmniboxRlzString);
+
+ std::wstring rlz;
+
+ // Prime the cache.
+ tracker_.set_assume_io_thread(true);
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, WideToUTF8(rlz).c_str());
+
+ // Make sure cache is valid.
+ tracker_.set_assume_io_thread(false);
+ EXPECT_TRUE(RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz));
+ EXPECT_STREQ(kOmniboxRlzString, WideToUTF8(rlz).c_str());
+
+ // Perform ping.
+ RLZTracker::InitRlzDelayed(true, 20, true, true);
+ InvokeDelayedInit();
+ ExpectRlzPingSent(true);
+
+ // Make sure cache is now invalid.
+ EXPECT_FALSE(RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz));
+}
+
+TEST_F(RlzLibTest, ObserveHandlesBadArgs) {
+ NavigationEntry entry(NULL, 0, GURL(), GURL(), string16(),
+ PageTransition::LINK);
+ tracker_.Observe(content::NOTIFICATION_NAV_ENTRY_PENDING,
+ NotificationService::AllSources(),
+ Details<NavigationEntry>(NULL));
+ tracker_.Observe(content::NOTIFICATION_NAV_ENTRY_PENDING,
+ NotificationService::AllSources(),
+ Details<NavigationEntry>(&entry));
}