diff options
Diffstat (limited to 'chrome/browser/rlz/rlz_unittest.cc')
-rw-r--r-- | chrome/browser/rlz/rlz_unittest.cc | 547 |
1 files changed, 516 insertions, 31 deletions
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)); } |