Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <string>
      6 #include <vector>
      7 
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "chrome/browser/chrome_notification_types.h"
     11 #include "chrome/browser/sessions/session_types.h"
     12 #include "chrome/browser/sync/glue/session_model_associator.h"
     13 #include "chrome/browser/sync/glue/synced_tab_delegate.h"
     14 #include "chrome/browser/sync/profile_sync_service_mock.h"
     15 #include "chrome/common/url_constants.h"
     16 #include "chrome/test/base/profile_mock.h"
     17 #include "components/sessions/serialized_navigation_entry_test_helper.h"
     18 #include "content/public/browser/navigation_entry.h"
     19 #include "content/public/browser/notification_details.h"
     20 #include "content/public/browser/notification_service.h"
     21 #include "content/public/common/page_transition_types.h"
     22 #include "content/public/test/test_browser_thread.h"
     23 #include "sync/protocol/session_specifics.pb.h"
     24 #include "sync/util/time.h"
     25 #include "testing/gmock/include/gmock/gmock.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 #include "url/gurl.h"
     28 
     29 using content::BrowserThread;
     30 using sessions::SerializedNavigationEntry;
     31 using sessions::SerializedNavigationEntryTestHelper;
     32 using testing::NiceMock;
     33 using testing::Return;
     34 using testing::StrictMock;
     35 using testing::_;
     36 
     37 namespace browser_sync {
     38 
     39 class SyncSessionModelAssociatorTest : public testing::Test {
     40  protected:
     41   SyncSessionModelAssociatorTest()
     42       : ui_thread_(BrowserThread::UI, &message_loop_),
     43         sync_service_(&profile_),
     44         model_associator_(&sync_service_, true) {}
     45 
     46   void LoadTabFavicon(const sync_pb::SessionTab& tab) {
     47     model_associator_.LoadForeignTabFavicon(tab);
     48     message_loop_.RunUntilIdle();
     49   }
     50 
     51   static GURL GetCurrentVirtualURL(const SyncedTabDelegate& tab_delegate) {
     52     return SessionModelAssociator::GetCurrentVirtualURL(tab_delegate);
     53   }
     54 
     55   static void SetSessionTabFromDelegate(
     56       const SyncedTabDelegate& tab_delegate,
     57       base::Time mtime,
     58       SessionTab* session_tab) {
     59     SessionModelAssociator::SetSessionTabFromDelegate(
     60         tab_delegate,
     61         mtime,
     62         session_tab);
     63   }
     64 
     65   bool FaviconEquals(const GURL page_url,
     66                      std::string expected_bytes) {
     67     FaviconCache* cache = model_associator_.GetFaviconCache();
     68     GURL gurl(page_url);
     69     scoped_refptr<base::RefCountedMemory> favicon;
     70     if (!cache->GetSyncedFaviconForPageURL(gurl, &favicon))
     71       return expected_bytes.empty();
     72     if (favicon->size() != expected_bytes.size())
     73       return false;
     74     for (size_t i = 0; i < favicon->size(); ++i) {
     75       if (expected_bytes[i] != *(favicon->front() + i))
     76         return false;
     77     }
     78     return true;
     79   }
     80 
     81  private:
     82   base::MessageLoopForUI message_loop_;
     83   content::TestBrowserThread ui_thread_;
     84   NiceMock<ProfileMock> profile_;
     85   NiceMock<ProfileSyncServiceMock> sync_service_;
     86 
     87  protected:
     88   SessionModelAssociator model_associator_;
     89 };
     90 
     91 namespace {
     92 
     93 TEST_F(SyncSessionModelAssociatorTest, SessionWindowHasNoTabsToSync) {
     94   SessionWindow win;
     95   ASSERT_TRUE(SessionWindowHasNoTabsToSync(win));
     96   scoped_ptr<SessionTab> tab(new SessionTab());
     97   win.tabs.push_back(tab.release());
     98   ASSERT_TRUE(SessionWindowHasNoTabsToSync(win));
     99   SerializedNavigationEntry nav =
    100       SerializedNavigationEntryTestHelper::CreateNavigation("about:bubba",
    101                                                             "title");
    102   win.tabs[0]->navigations.push_back(nav);
    103   ASSERT_FALSE(SessionWindowHasNoTabsToSync(win));
    104 }
    105 
    106 TEST_F(SyncSessionModelAssociatorTest, ShouldSyncSessionTab) {
    107   SessionTab tab;
    108   ASSERT_FALSE(ShouldSyncSessionTab(tab));
    109   SerializedNavigationEntry nav =
    110       SerializedNavigationEntryTestHelper::CreateNavigation(
    111           chrome::kChromeUINewTabURL, "title");
    112   tab.navigations.push_back(nav);
    113   // NewTab does not count as valid if it's the only navigation.
    114   ASSERT_FALSE(ShouldSyncSessionTab(tab));
    115   SerializedNavigationEntry nav2 =
    116       SerializedNavigationEntryTestHelper::CreateNavigation("about:bubba",
    117                                                             "title");
    118   tab.navigations.push_back(nav2);
    119   // Once there's another navigation, the tab is valid.
    120   ASSERT_TRUE(ShouldSyncSessionTab(tab));
    121 }
    122 
    123 TEST_F(SyncSessionModelAssociatorTest,
    124        ShouldSyncSessionTabIgnoresFragmentForNtp) {
    125   SessionTab tab;
    126   ASSERT_FALSE(ShouldSyncSessionTab(tab));
    127   SerializedNavigationEntry nav =
    128       SerializedNavigationEntryTestHelper::CreateNavigation(
    129           std::string(chrome::kChromeUINewTabURL) + "#bookmarks", "title");
    130   tab.navigations.push_back(nav);
    131   // NewTab does not count as valid if it's the only navigation.
    132   ASSERT_FALSE(ShouldSyncSessionTab(tab));
    133 }
    134 
    135 }  // namespace
    136 
    137 TEST_F(SyncSessionModelAssociatorTest, PopulateSessionHeader) {
    138   sync_pb::SessionHeader header_s;
    139   header_s.set_client_name("Client 1");
    140   header_s.set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_WIN);
    141 
    142   SyncedSession session;
    143   base::Time time = base::Time::Now();
    144   SessionModelAssociator::PopulateSessionHeaderFromSpecifics(
    145       header_s, time, &session);
    146   ASSERT_EQ("Client 1", session.session_name);
    147   ASSERT_EQ(SyncedSession::TYPE_WIN, session.device_type);
    148   ASSERT_EQ(time, session.modified_time);
    149 }
    150 
    151 TEST_F(SyncSessionModelAssociatorTest, PopulateSessionWindow) {
    152   sync_pb::SessionWindow window_s;
    153   window_s.add_tab(0);
    154   window_s.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
    155   window_s.set_selected_tab_index(1);
    156 
    157   std::string tag = "tag";
    158   SyncedSessionTracker tracker;
    159   SyncedSession* session = tracker.GetSession(tag);
    160   tracker.PutWindowInSession(tag, 0);
    161   SessionModelAssociator::PopulateSessionWindowFromSpecifics(
    162       tag, window_s, base::Time(), session->windows[0], &tracker);
    163   ASSERT_EQ(1U, session->windows[0]->tabs.size());
    164   ASSERT_EQ(1, session->windows[0]->selected_tab_index);
    165   ASSERT_EQ(1, session->windows[0]->type);
    166   ASSERT_EQ(1U, tracker.num_synced_sessions());
    167   ASSERT_EQ(1U, tracker.num_synced_tabs(std::string("tag")));
    168 }
    169 
    170 namespace {
    171 
    172 class SyncedTabDelegateMock : public SyncedTabDelegate {
    173  public:
    174   SyncedTabDelegateMock() {}
    175   virtual ~SyncedTabDelegateMock() {}
    176 
    177   MOCK_CONST_METHOD0(GetWindowId, SessionID::id_type());
    178   MOCK_CONST_METHOD0(GetSessionId, SessionID::id_type());
    179   MOCK_CONST_METHOD0(IsBeingDestroyed, bool());
    180   MOCK_CONST_METHOD0(profile, Profile*());
    181   MOCK_CONST_METHOD0(GetExtensionAppId, std::string());
    182   MOCK_CONST_METHOD0(GetCurrentEntryIndex, int());
    183   MOCK_CONST_METHOD0(GetEntryCount, int());
    184   MOCK_CONST_METHOD0(GetPendingEntryIndex, int());
    185   MOCK_CONST_METHOD0(GetPendingEntry, content::NavigationEntry*());
    186   MOCK_CONST_METHOD1(GetEntryAtIndex, content::NavigationEntry*(int i));
    187   MOCK_CONST_METHOD0(GetActiveEntry, content::NavigationEntry*());
    188   MOCK_CONST_METHOD0(ProfileIsManaged, bool());
    189   MOCK_CONST_METHOD0(GetBlockedNavigations,
    190                      const std::vector<const content::NavigationEntry*>*());
    191   MOCK_CONST_METHOD0(IsPinned, bool());
    192   MOCK_CONST_METHOD0(HasWebContents, bool());
    193   MOCK_CONST_METHOD0(GetSyncId, int());
    194   MOCK_METHOD1(SetSyncId, void(int));
    195 };
    196 
    197 class SyncRefreshListener : public content::NotificationObserver {
    198  public:
    199   SyncRefreshListener() : notified_of_refresh_(false) {
    200     registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
    201         content::NotificationService::AllSources());
    202   }
    203 
    204   virtual void Observe(int type,
    205                        const content::NotificationSource& source,
    206                        const content::NotificationDetails& details) OVERRIDE {
    207     if (type == chrome::NOTIFICATION_SYNC_REFRESH_LOCAL) {
    208       notified_of_refresh_ = true;
    209     }
    210   }
    211 
    212   bool notified_of_refresh() const { return notified_of_refresh_; }
    213 
    214  private:
    215   bool notified_of_refresh_;
    216   content::NotificationRegistrar registrar_;
    217 };
    218 
    219 // Test that AttemptSessionsDataRefresh() triggers the
    220 // NOTIFICATION_SYNC_REFRESH_LOCAL notification.
    221 TEST_F(SyncSessionModelAssociatorTest, TriggerSessionRefresh) {
    222   SyncRefreshListener refresh_listener;
    223 
    224   EXPECT_FALSE(refresh_listener.notified_of_refresh());
    225   model_associator_.AttemptSessionsDataRefresh();
    226   EXPECT_TRUE(refresh_listener.notified_of_refresh());
    227 }
    228 
    229 // Test that we exclude tabs with only chrome:// and file:// schemed navigations
    230 // from ShouldSyncTab(..).
    231 TEST_F(SyncSessionModelAssociatorTest, ValidTabs) {
    232   NiceMock<SyncedTabDelegateMock> tab_mock;
    233 
    234   // A null entry shouldn't crash.
    235   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
    236   EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
    237       Return((content::NavigationEntry *)NULL));
    238   EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(1));
    239   EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
    240   EXPECT_FALSE(model_associator_.ShouldSyncTab(tab_mock));
    241 
    242   // A chrome:// entry isn't valid.
    243   scoped_ptr<content::NavigationEntry> entry(
    244       content::NavigationEntry::Create());
    245   entry->SetVirtualURL(GURL("chrome://preferences/"));
    246   testing::Mock::VerifyAndClearExpectations(&tab_mock);
    247   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
    248   EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(Return(entry.get()));
    249   EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(1));
    250   EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
    251   EXPECT_FALSE(model_associator_.ShouldSyncTab(tab_mock));
    252 
    253   // A file:// entry isn't valid, even in addition to another entry.
    254   scoped_ptr<content::NavigationEntry> entry2(
    255       content::NavigationEntry::Create());
    256   entry2->SetVirtualURL(GURL("file://bla"));
    257   testing::Mock::VerifyAndClearExpectations(&tab_mock);
    258   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
    259   EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(Return(entry.get()));
    260   EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
    261       Return(entry2.get()));
    262   EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(2));
    263   EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
    264   EXPECT_FALSE(model_associator_.ShouldSyncTab(tab_mock));
    265 
    266   // Add a valid scheme entry to tab, making the tab valid.
    267   scoped_ptr<content::NavigationEntry> entry3(
    268       content::NavigationEntry::Create());
    269   entry3->SetVirtualURL(GURL("http://www.google.com"));
    270   testing::Mock::VerifyAndClearExpectations(&tab_mock);
    271   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
    272   EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
    273       Return(entry.get()));
    274   EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
    275       Return(entry2.get()));
    276   EXPECT_CALL(tab_mock, GetEntryAtIndex(2)).WillRepeatedly(
    277       Return(entry3.get()));
    278   EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(3));
    279   EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
    280   EXPECT_TRUE(model_associator_.ShouldSyncTab(tab_mock));
    281 }
    282 
    283 // TODO(akalin): We should really use a fake for SyncedTabDelegate.
    284 
    285 // Make sure GetCurrentVirtualURL() returns the virtual URL of the pending
    286 // entry if the current entry is pending.
    287 TEST_F(SyncSessionModelAssociatorTest, GetCurrentVirtualURLPending) {
    288   StrictMock<SyncedTabDelegateMock> tab_mock;
    289   scoped_ptr<content::NavigationEntry> entry(
    290       content::NavigationEntry::Create());
    291   entry->SetVirtualURL(GURL("http://www.google.com"));
    292   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillOnce(Return(0));
    293   EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillOnce(Return(0));
    294   EXPECT_CALL(tab_mock, GetPendingEntry()).WillOnce(Return(entry.get()));
    295   EXPECT_EQ(entry->GetVirtualURL(), GetCurrentVirtualURL(tab_mock));
    296 }
    297 
    298 // Make sure GetCurrentVirtualURL() returns the virtual URL of the current
    299 // entry if the current entry is non-pending.
    300 TEST_F(SyncSessionModelAssociatorTest, GetCurrentVirtualURLNonPending) {
    301   StrictMock<SyncedTabDelegateMock> tab_mock;
    302   scoped_ptr<content::NavigationEntry> entry(
    303       content::NavigationEntry::Create());
    304   entry->SetVirtualURL(GURL("http://www.google.com"));
    305   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillOnce(Return(0));
    306   EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillOnce(Return(-1));
    307   EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillOnce(Return(entry.get()));
    308   EXPECT_EQ(entry->GetVirtualURL(), GetCurrentVirtualURL(tab_mock));
    309 }
    310 
    311 const base::Time kTime1 = base::Time::FromInternalValue(100);
    312 const base::Time kTime2 = base::Time::FromInternalValue(105);
    313 const base::Time kTime3 = base::Time::FromInternalValue(110);
    314 const base::Time kTime4 = base::Time::FromInternalValue(120);
    315 const base::Time kTime5 = base::Time::FromInternalValue(130);
    316 
    317 // Populate the mock tab delegate with some data and navigation
    318 // entries and make sure that setting a SessionTab from it preserves
    319 // those entries (and clobbers any existing data).
    320 TEST_F(SyncSessionModelAssociatorTest, SetSessionTabFromDelegate) {
    321   // Create a tab with three valid entries.
    322   NiceMock<SyncedTabDelegateMock> tab_mock;
    323   EXPECT_CALL(tab_mock, GetSessionId()).WillRepeatedly(Return(0));
    324   scoped_ptr<content::NavigationEntry> entry1(
    325       content::NavigationEntry::Create());
    326   entry1->SetVirtualURL(GURL("http://www.google.com"));
    327   entry1->SetTimestamp(kTime1);
    328   scoped_ptr<content::NavigationEntry> entry2(
    329       content::NavigationEntry::Create());
    330   entry2->SetVirtualURL(GURL("http://www.noodle.com"));
    331   entry2->SetTimestamp(kTime2);
    332   scoped_ptr<content::NavigationEntry> entry3(
    333       content::NavigationEntry::Create());
    334   entry3->SetVirtualURL(GURL("http://www.doodle.com"));
    335   entry3->SetTimestamp(kTime3);
    336   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(2));
    337   EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
    338       Return(entry1.get()));
    339   EXPECT_CALL(tab_mock, GetEntryAtIndex(1)).WillRepeatedly(
    340       Return(entry2.get()));
    341   EXPECT_CALL(tab_mock, GetEntryAtIndex(2)).WillRepeatedly(
    342       Return(entry3.get()));
    343   EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(3));
    344   EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
    345   EXPECT_CALL(tab_mock, ProfileIsManaged()).WillRepeatedly(Return(false));
    346 
    347   SessionTab session_tab;
    348   session_tab.window_id.set_id(1);
    349   session_tab.tab_id.set_id(1);
    350   session_tab.tab_visual_index = 1;
    351   session_tab.current_navigation_index = 1;
    352   session_tab.pinned = true;
    353   session_tab.extension_app_id = "app id";
    354   session_tab.user_agent_override = "override";
    355   session_tab.timestamp = kTime5;
    356   session_tab.navigations.push_back(
    357       SerializedNavigationEntryTestHelper::CreateNavigation(
    358           "http://www.example.com", "Example"));
    359   session_tab.session_storage_persistent_id = "persistent id";
    360   SetSessionTabFromDelegate(tab_mock, kTime4, &session_tab);
    361 
    362   EXPECT_EQ(0, session_tab.window_id.id());
    363   EXPECT_EQ(0, session_tab.tab_id.id());
    364   EXPECT_EQ(0, session_tab.tab_visual_index);
    365   EXPECT_EQ(2, session_tab.current_navigation_index);
    366   EXPECT_FALSE(session_tab.pinned);
    367   EXPECT_TRUE(session_tab.extension_app_id.empty());
    368   EXPECT_TRUE(session_tab.user_agent_override.empty());
    369   EXPECT_EQ(kTime4, session_tab.timestamp);
    370   ASSERT_EQ(3u, session_tab.navigations.size());
    371   EXPECT_EQ(entry1->GetVirtualURL(),
    372             session_tab.navigations[0].virtual_url());
    373   EXPECT_EQ(entry2->GetVirtualURL(),
    374             session_tab.navigations[1].virtual_url());
    375   EXPECT_EQ(entry3->GetVirtualURL(),
    376             session_tab.navigations[2].virtual_url());
    377   EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
    378   EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
    379   EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
    380   EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
    381             session_tab.navigations[0].blocked_state());
    382   EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
    383             session_tab.navigations[1].blocked_state());
    384   EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
    385             session_tab.navigations[2].blocked_state());
    386   EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
    387 }
    388 
    389 // Tests that for managed users blocked navigations are recorded and marked as
    390 // such, while regular navigations are marked as allowed.
    391 TEST_F(SyncSessionModelAssociatorTest, BlockedNavigations) {
    392   NiceMock<SyncedTabDelegateMock> tab_mock;
    393   EXPECT_CALL(tab_mock, GetSessionId()).WillRepeatedly(Return(0));
    394   scoped_ptr<content::NavigationEntry> entry1(
    395       content::NavigationEntry::Create());
    396   entry1->SetVirtualURL(GURL("http://www.google.com"));
    397   entry1->SetTimestamp(kTime1);
    398   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(0));
    399   EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
    400       Return(entry1.get()));
    401   EXPECT_CALL(tab_mock, GetEntryCount()).WillRepeatedly(Return(1));
    402   EXPECT_CALL(tab_mock, GetPendingEntryIndex()).WillRepeatedly(Return(-1));
    403 
    404   content::NavigationEntry* entry2 = content::NavigationEntry::Create();
    405   entry2->SetVirtualURL(GURL("http://blocked.com/foo"));
    406   entry2->SetTimestamp(kTime2);
    407   content::NavigationEntry* entry3 = content::NavigationEntry::Create();
    408   entry3->SetVirtualURL(GURL("http://evil.com"));
    409   entry3->SetTimestamp(kTime3);
    410   ScopedVector<const content::NavigationEntry> blocked_navigations;
    411   blocked_navigations.push_back(entry2);
    412   blocked_navigations.push_back(entry3);
    413 
    414   EXPECT_CALL(tab_mock, ProfileIsManaged()).WillRepeatedly(Return(true));
    415   EXPECT_CALL(tab_mock, GetBlockedNavigations()).WillRepeatedly(
    416       Return(&blocked_navigations.get()));
    417 
    418   SessionTab session_tab;
    419   session_tab.window_id.set_id(1);
    420   session_tab.tab_id.set_id(1);
    421   session_tab.tab_visual_index = 1;
    422   session_tab.current_navigation_index = 1;
    423   session_tab.pinned = true;
    424   session_tab.extension_app_id = "app id";
    425   session_tab.user_agent_override = "override";
    426   session_tab.timestamp = kTime5;
    427   session_tab.navigations.push_back(
    428       SerializedNavigationEntryTestHelper::CreateNavigation(
    429           "http://www.example.com", "Example"));
    430   session_tab.session_storage_persistent_id = "persistent id";
    431   SetSessionTabFromDelegate(tab_mock, kTime4, &session_tab);
    432 
    433   EXPECT_EQ(0, session_tab.window_id.id());
    434   EXPECT_EQ(0, session_tab.tab_id.id());
    435   EXPECT_EQ(0, session_tab.tab_visual_index);
    436   EXPECT_EQ(0, session_tab.current_navigation_index);
    437   EXPECT_FALSE(session_tab.pinned);
    438   EXPECT_TRUE(session_tab.extension_app_id.empty());
    439   EXPECT_TRUE(session_tab.user_agent_override.empty());
    440   EXPECT_EQ(kTime4, session_tab.timestamp);
    441   ASSERT_EQ(3u, session_tab.navigations.size());
    442   EXPECT_EQ(entry1->GetVirtualURL(),
    443             session_tab.navigations[0].virtual_url());
    444   EXPECT_EQ(entry2->GetVirtualURL(),
    445             session_tab.navigations[1].virtual_url());
    446   EXPECT_EQ(entry3->GetVirtualURL(),
    447             session_tab.navigations[2].virtual_url());
    448   EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
    449   EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
    450   EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
    451   EXPECT_EQ(SerializedNavigationEntry::STATE_ALLOWED,
    452             session_tab.navigations[0].blocked_state());
    453   EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
    454             session_tab.navigations[1].blocked_state());
    455   EXPECT_EQ(SerializedNavigationEntry::STATE_BLOCKED,
    456             session_tab.navigations[2].blocked_state());
    457   EXPECT_TRUE(session_tab.session_storage_persistent_id.empty());
    458 }
    459 
    460 // Create tab specifics with an empty favicon. Ensure it gets ignored and not
    461 // stored into the synced favicon lookups.
    462 TEST_F(SyncSessionModelAssociatorTest, LoadEmptyFavicon) {
    463   std::string favicon;
    464   std::string favicon_url = "http://www.faviconurl.com/favicon.ico";
    465   std::string page_url = "http://www.faviconurl.com/page.html";
    466   sync_pb::SessionTab tab;
    467   tab.set_favicon(favicon);
    468   tab.set_favicon_source(favicon_url);
    469   tab.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
    470   sync_pb::TabNavigation* navigation = tab.add_navigation();
    471   navigation->set_virtual_url(page_url);
    472   tab.set_current_navigation_index(0);
    473 
    474   EXPECT_TRUE(FaviconEquals(GURL(page_url), std::string()));
    475   LoadTabFavicon(tab);
    476   EXPECT_TRUE(FaviconEquals(GURL(page_url), std::string()));
    477 }
    478 
    479 // Create tab specifics with a non-web favicon. Ensure it gets ignored and not
    480 // stored into the synced favicon lookups.
    481 TEST_F(SyncSessionModelAssociatorTest, LoadNonWebFavicon) {
    482   std::string favicon = "icon bytes";
    483   std::string favicon_url = "http://www.faviconurl.com/favicon.ico";
    484   std::string page_url = "http://www.faviconurl.com/page.html";
    485   sync_pb::SessionTab tab;
    486   tab.set_favicon(favicon);
    487   tab.set_favicon_source(favicon_url);
    488   // Set favicon type to an unsupported value (1 == WEB_FAVICON).
    489   tab.mutable_unknown_fields()->AddVarint(9, 2);
    490   sync_pb::TabNavigation* navigation = tab.add_navigation();
    491   navigation->set_virtual_url(page_url);
    492   tab.set_current_navigation_index(0);
    493 
    494   EXPECT_TRUE(FaviconEquals(GURL(page_url), std::string()));
    495   LoadTabFavicon(tab);
    496   EXPECT_TRUE(FaviconEquals(GURL(page_url), std::string()));
    497 }
    498 
    499 // Create tab specifics with a valid favicon. Ensure it gets stored in the
    500 // synced favicon lookups and is accessible by the page url.
    501 TEST_F(SyncSessionModelAssociatorTest, LoadValidFavicon) {
    502   std::string favicon = "icon bytes";
    503   std::string favicon_url = "http://www.faviconurl.com/favicon.ico";
    504   std::string page_url = "http://www.faviconurl.com/page.html";
    505   sync_pb::SessionTab tab;
    506   tab.set_favicon(favicon);
    507   tab.set_favicon_source(favicon_url);
    508   tab.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
    509   sync_pb::TabNavigation* navigation = tab.add_navigation();
    510   navigation->set_virtual_url(page_url);
    511   tab.set_current_navigation_index(0);
    512 
    513   EXPECT_TRUE(FaviconEquals(GURL(page_url), std::string()));
    514   LoadTabFavicon(tab);
    515   EXPECT_TRUE(FaviconEquals(GURL(page_url), favicon));
    516 }
    517 
    518 // Create tab specifics with a valid favicon, load it, then load tab specifics
    519 // with a new favicon for the same favicon source but different page. Ensure the
    520 // old favicon remains.
    521 TEST_F(SyncSessionModelAssociatorTest, UpdateValidFavicon) {
    522   std::string favicon_url = "http://www.faviconurl.com/favicon.ico";
    523 
    524   std::string favicon = "icon bytes";
    525   std::string page_url = "http://www.faviconurl.com/page.html";
    526   sync_pb::SessionTab tab;
    527   tab.set_favicon(favicon);
    528   tab.set_favicon_source(favicon_url);
    529   tab.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
    530   sync_pb::TabNavigation* navigation = tab.add_navigation();
    531   navigation->set_virtual_url(page_url);
    532   tab.set_current_navigation_index(0);
    533 
    534   EXPECT_TRUE(FaviconEquals(GURL(page_url), std::string()));
    535   LoadTabFavicon(tab);
    536   EXPECT_TRUE(FaviconEquals(GURL(page_url), favicon));
    537 
    538   // Now have a new page with same favicon source but newer favicon data.
    539   std::string favicon2 = "icon bytes 2";
    540   std::string page_url2 = "http://www.faviconurl.com/page2.html";
    541   sync_pb::SessionTab tab2;
    542   tab2.set_favicon(favicon2);
    543   tab2.set_favicon_source(favicon_url);
    544   tab2.set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
    545   sync_pb::TabNavigation* navigation2 = tab2.add_navigation();
    546   navigation2->set_virtual_url(page_url2);
    547   tab2.set_current_navigation_index(0);
    548 
    549   // The new page should be mapped to the old favicon data.
    550   EXPECT_TRUE(FaviconEquals(GURL(page_url2), std::string()));
    551   LoadTabFavicon(tab2);
    552   EXPECT_TRUE(FaviconEquals(GURL(page_url), favicon));
    553   EXPECT_TRUE(FaviconEquals(GURL(page_url2), favicon));
    554 }
    555 
    556 }  // namespace
    557 
    558 }  // namespace browser_sync
    559