profile_sync_service_session_unittest.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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 <map>
6#include <string>
7
8#include "base/bind.h"
9#include "base/bind_helpers.h"
10#include "base/callback.h"
11#include "base/compiler_specific.h"
12#include "base/files/scoped_temp_dir.h"
13#include "base/location.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/message_loop.h"
16#include "base/stl_util.h"
17#include "base/time.h"
18#include "base/utf_string_conversions.h"
19#include "chrome/browser/signin/signin_manager.h"
20#include "chrome/browser/signin/signin_manager_factory.h"
21#include "chrome/browser/signin/token_service_factory.h"
22#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
23#include "chrome/browser/sync/glue/device_info.h"
24#include "chrome/browser/sync/glue/session_change_processor.h"
25#include "chrome/browser/sync/glue/session_data_type_controller.h"
26#include "chrome/browser/sync/glue/session_model_associator.h"
27#include "chrome/browser/sync/glue/sync_backend_host.h"
28#include "chrome/browser/sync/glue/synced_device_tracker.h"
29#include "chrome/browser/sync/glue/synced_tab_delegate.h"
30#include "chrome/browser/sync/glue/tab_node_pool.h"
31#include "chrome/browser/sync/profile_sync_components_factory_mock.h"
32#include "chrome/browser/sync/profile_sync_service_factory.h"
33#include "chrome/browser/sync/profile_sync_test_util.h"
34#include "chrome/browser/sync/test_profile_sync_service.h"
35#include "chrome/common/chrome_notification_types.h"
36#include "chrome/test/base/browser_with_test_window_test.h"
37#include "chrome/test/base/testing_profile.h"
38#include "content/public/browser/navigation_entry.h"
39#include "content/public/browser/notification_observer.h"
40#include "content/public/browser/notification_registrar.h"
41#include "content/public/browser/notification_service.h"
42#include "content/public/test/test_browser_thread.h"
43#include "google_apis/gaia/gaia_constants.h"
44#include "googleurl/src/gurl.h"
45#include "sync/internal_api/public/base/model_type.h"
46#include "sync/internal_api/public/change_record.h"
47#include "sync/internal_api/public/read_node.h"
48#include "sync/internal_api/public/read_transaction.h"
49#include "sync/internal_api/public/test/test_user_share.h"
50#include "sync/internal_api/public/write_node.h"
51#include "sync/internal_api/public/write_transaction.h"
52#include "sync/protocol/session_specifics.pb.h"
53#include "sync/protocol/sync.pb.h"
54#include "testing/gmock/include/gmock/gmock.h"
55#include "testing/gtest/include/gtest/gtest.h"
56#include "ui/base/ui_base_types.h"
57
58using browser_sync::SessionChangeProcessor;
59using browser_sync::SessionDataTypeController;
60using browser_sync::SessionModelAssociator;
61using browser_sync::SyncBackendHost;
62using content::BrowserThread;
63using syncer::ChangeRecord;
64using testing::_;
65using testing::Return;
66
67namespace browser_sync {
68
69namespace {
70
71class FakeProfileSyncService : public TestProfileSyncService {
72 public:
73  FakeProfileSyncService(
74      ProfileSyncComponentsFactory* factory,
75      Profile* profile,
76      SigninManagerBase* signin,
77      ProfileSyncService::StartBehavior behavior,
78      bool synchronous_backend_initialization)
79      : TestProfileSyncService(factory,
80                               profile,
81                               signin,
82                               behavior,
83                               synchronous_backend_initialization) {}
84  virtual ~FakeProfileSyncService() {}
85
86  virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE {
87    return scoped_ptr<DeviceInfo>(new DeviceInfo("client_name",
88                                                 std::string(),
89                                                 std::string(),
90                                                 sync_pb::SyncEnums::TYPE_WIN));
91  }
92};
93
94void BuildSessionSpecifics(const std::string& tag,
95                           sync_pb::SessionSpecifics* meta) {
96  meta->set_session_tag(tag);
97  sync_pb::SessionHeader* header = meta->mutable_header();
98  header->set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_LINUX);
99  header->set_client_name("name");
100}
101
102void AddWindowSpecifics(int window_id,
103                        const std::vector<int>& tab_list,
104                        sync_pb::SessionSpecifics* meta) {
105  sync_pb::SessionHeader* header = meta->mutable_header();
106  sync_pb::SessionWindow* window = header->add_window();
107  window->set_window_id(window_id);
108  window->set_selected_tab_index(0);
109  window->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
110  for (std::vector<int>::const_iterator iter = tab_list.begin();
111       iter != tab_list.end(); ++iter) {
112    window->add_tab(*iter);
113  }
114}
115
116void BuildTabSpecifics(const std::string& tag, int window_id, int tab_id,
117                       sync_pb::SessionSpecifics* tab_base) {
118  tab_base->set_session_tag(tag);
119  sync_pb::SessionTab* tab = tab_base->mutable_tab();
120  tab->set_tab_id(tab_id);
121  tab->set_tab_visual_index(1);
122  tab->set_current_navigation_index(0);
123  tab->set_pinned(true);
124  tab->set_extension_app_id("app_id");
125  sync_pb::TabNavigation* navigation = tab->add_navigation();
126  navigation->set_virtual_url("http://foo/1");
127  navigation->set_referrer("referrer");
128  navigation->set_title("title");
129  navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
130}
131
132// Verifies number of windows, number of tabs, and basic fields.
133void VerifySyncedSession(
134    const std::string& tag,
135    const std::vector<std::vector<SessionID::id_type> >& windows,
136    const SyncedSession& session) {
137  ASSERT_EQ(tag, session.session_tag);
138  ASSERT_EQ(SyncedSession::TYPE_LINUX, session.device_type);
139  ASSERT_EQ("name", session.session_name);
140  ASSERT_EQ(windows.size(), session.windows.size());
141
142  // We assume the window id's are in increasing order.
143  int i = 0;
144  for (std::vector<std::vector<int> >::const_iterator win_iter =
145           windows.begin();
146       win_iter != windows.end(); ++win_iter, ++i) {
147    SessionWindow* win_ptr;
148    SyncedSession::SyncedWindowMap::const_iterator map_iter =
149        session.windows.find(i);
150    if (map_iter != session.windows.end())
151      win_ptr = map_iter->second;
152    else
153      FAIL();
154    ASSERT_EQ(win_iter->size(), win_ptr->tabs.size());
155    ASSERT_EQ(0, win_ptr->selected_tab_index);
156    ASSERT_EQ(1, win_ptr->type);
157    int j = 0;
158    for (std::vector<int>::const_iterator tab_iter = (*win_iter).begin();
159         tab_iter != (*win_iter).end(); ++tab_iter, ++j) {
160      SessionTab* tab = win_ptr->tabs[j];
161      ASSERT_EQ(*tab_iter, tab->tab_id.id());
162      ASSERT_EQ(1U, tab->navigations.size());
163      ASSERT_EQ(1, tab->tab_visual_index);
164      ASSERT_EQ(0, tab->current_navigation_index);
165      ASSERT_TRUE(tab->pinned);
166      ASSERT_EQ("app_id", tab->extension_app_id);
167      ASSERT_EQ(1U, tab->navigations.size());
168      ASSERT_EQ(tab->navigations[0].virtual_url(), GURL("http://foo/1"));
169      ASSERT_EQ(tab->navigations[0].referrer().url, GURL("referrer"));
170      ASSERT_EQ(tab->navigations[0].title(), string16(ASCIIToUTF16("title")));
171      ASSERT_EQ(tab->navigations[0].transition_type(),
172                content::PAGE_TRANSITION_TYPED);
173    }
174  }
175}
176
177bool CompareMemoryToString(
178    const std::string& str,
179    const scoped_refptr<base::RefCountedMemory>& mem) {
180  if (mem->size() != str.size())
181    return false;
182  for (size_t i = 0; i <mem->size(); ++i) {
183    if (str[i] != *(mem->front() + i))
184      return false;
185  }
186  return true;
187}
188
189}  // namespace
190
191class ProfileSyncServiceSessionTest
192    : public BrowserWithTestWindowTest,
193      public content::NotificationObserver {
194 public:
195  ProfileSyncServiceSessionTest()
196      : io_thread_(BrowserThread::IO),
197        window_bounds_(0, 1, 2, 3),
198        notified_of_update_(false),
199        notified_of_refresh_(false) {}
200  ProfileSyncService* sync_service() { return sync_service_.get(); }
201
202 protected:
203  virtual TestingProfile* CreateProfile() OVERRIDE {
204    TestingProfile* profile = new TestingProfile();
205    // Don't want the profile to create a real ProfileSyncService.
206    ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(profile,
207                                                                NULL);
208    return profile;
209  }
210
211  virtual void SetUp() {
212    // BrowserWithTestWindowTest implementation.
213    BrowserWithTestWindowTest::SetUp();
214    io_thread_.StartIOThread();
215    profile()->CreateRequestContext();
216    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
217    registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
218        content::NotificationService::AllSources());
219    registrar_.Add(this, chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
220        content::NotificationService::AllSources());
221  }
222
223  virtual void Observe(int type,
224      const content::NotificationSource& source,
225      const content::NotificationDetails& details) OVERRIDE {
226    switch (type) {
227      case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
228        notified_of_update_ = true;
229        break;
230      case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL:
231        notified_of_refresh_ = true;
232        break;
233      default:
234        NOTREACHED();
235        break;
236    }
237  }
238
239  virtual void TearDown() {
240    sync_service_->Shutdown();
241    sync_service_.reset();
242    profile()->ResetRequestContext();
243
244    // We need to destroy the profile before shutting down the threads, because
245    // some of the ref counted objects in the profile depend on their
246    // destruction on the io thread.
247    DestroyBrowserAndProfile();
248    ASSERT_FALSE(profile());
249
250    // Pump messages posted by the sync core thread (which may end up
251    // posting on the IO thread).
252    base::MessageLoop::current()->RunUntilIdle();
253    io_thread_.Stop();
254    base::MessageLoop::current()->RunUntilIdle();
255    BrowserWithTestWindowTest::TearDown();
256  }
257
258  bool StartSyncService(const base::Closure& callback,
259                        bool will_fail_association) {
260    if (sync_service_)
261      return false;
262    SigninManagerBase* signin =
263        SigninManagerFactory::GetForProfile(profile());
264    signin->SetAuthenticatedUsername("test_user");
265    ProfileSyncComponentsFactoryMock* factory =
266        new ProfileSyncComponentsFactoryMock();
267    sync_service_.reset(new FakeProfileSyncService(
268        factory,
269        profile(),
270        signin,
271        ProfileSyncService::AUTO_START,
272        false));
273    sync_service_->set_backend_init_callback(callback);
274
275    // Register the session data type.
276    SessionDataTypeController *dtc = new SessionDataTypeController(factory,
277                                         profile(),
278                                         sync_service_.get());
279    sync_service_->RegisterDataTypeController(dtc);
280
281    model_associator_ =
282        new SessionModelAssociator(sync_service_.get(),
283                                   true /* setup_for_test */);
284    change_processor_ = new SessionChangeProcessor(
285        dtc, model_associator_,
286        true /* setup_for_test */);
287    EXPECT_CALL(*factory, CreateSessionSyncComponents(_, _)).
288        WillOnce(Return(ProfileSyncComponentsFactory::SyncComponents(
289            model_associator_, change_processor_)));
290    EXPECT_CALL(*factory, CreateDataTypeManager(_, _, _, _, _)).
291        WillOnce(ReturnNewDataTypeManager());
292
293    TokenServiceFactory::GetForProfile(profile())->IssueAuthTokenForTest(
294        GaiaConstants::kSyncService, "token");
295    sync_service_->Initialize();
296    base::MessageLoop::current()->Run();
297    return true;
298  }
299
300  content::TestBrowserThread io_thread_;
301  // Path used in testing.
302  base::ScopedTempDir temp_dir_;
303  SessionModelAssociator* model_associator_;
304  SessionChangeProcessor* change_processor_;
305  SessionID window_id_;
306  scoped_ptr<TestProfileSyncService> sync_service_;
307  const gfx::Rect window_bounds_;
308  bool notified_of_update_;
309  bool notified_of_refresh_;
310  content::NotificationRegistrar registrar_;
311};
312
313class CreateRootHelper {
314 public:
315  explicit CreateRootHelper(ProfileSyncServiceSessionTest* test)
316      : callback_(base::Bind(&CreateRootHelper::CreateRootCallback,
317                             base::Unretained(this), test)),
318        success_(false) {
319  }
320
321  virtual ~CreateRootHelper() {}
322
323  const base::Closure& callback() const { return callback_; }
324  bool success() { return success_; }
325
326 private:
327  void CreateRootCallback(ProfileSyncServiceSessionTest* test) {
328    success_ = syncer::TestUserShare::CreateRoot(
329        syncer::SESSIONS, test->sync_service()->GetUserShare());
330  }
331
332  base::Closure callback_;
333  bool success_;
334};
335
336// Test that we can write this machine's session to a node and retrieve it.
337TEST_F(ProfileSyncServiceSessionTest, WriteSessionToNode) {
338  CreateRootHelper create_root(this);
339  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
340  ASSERT_TRUE(create_root.success());
341
342  // Check that the DataTypeController associated the models.
343  bool has_nodes;
344  ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
345  ASSERT_TRUE(has_nodes);
346  std::string machine_tag = model_associator_->GetCurrentMachineTag();
347  int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
348  ASSERT_NE(syncer::kInvalidId, sync_id);
349
350  // Check that we can get the correct session specifics back from the node.
351  syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
352  syncer::ReadNode node(&trans);
353  ASSERT_EQ(syncer::BaseNode::INIT_OK,
354            node.InitByClientTagLookup(syncer::SESSIONS, machine_tag));
355  const sync_pb::SessionSpecifics& specifics(node.GetSessionSpecifics());
356  ASSERT_EQ(machine_tag, specifics.session_tag());
357  ASSERT_TRUE(specifics.has_header());
358  const sync_pb::SessionHeader& header_s = specifics.header();
359  ASSERT_TRUE(header_s.has_device_type());
360  ASSERT_EQ("client_name", header_s.client_name());
361  ASSERT_EQ(0, header_s.window_size());
362}
363
364// Crashes sometimes on Windows, particularly XP.
365// See http://crbug.com/174951
366#if defined(OS_WIN)
367#define MAYBE_WriteFilledSessionToNode DISABLED_WriteFilledSessionToNode
368#else
369#define MAYBE_WriteFilledSessionToNode WriteFilledSessionToNode
370#endif  // defined(OS_WIN)
371
372// Test that we can fill this machine's session, write it to a node,
373// and then retrieve it.
374TEST_F(ProfileSyncServiceSessionTest, MAYBE_WriteFilledSessionToNode) {
375  CreateRootHelper create_root(this);
376  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
377  ASSERT_TRUE(create_root.success());
378
379  // Check that the DataTypeController associated the models.
380  bool has_nodes;
381  ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
382  ASSERT_TRUE(has_nodes);
383  AddTab(browser(), GURL("http://foo/1"));
384  NavigateAndCommitActiveTab(GURL("http://foo/2"));
385  AddTab(browser(), GURL("http://bar/1"));
386  NavigateAndCommitActiveTab(GURL("http://bar/2"));
387
388  ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
389  ASSERT_TRUE(has_nodes);
390  std::string machine_tag = model_associator_->GetCurrentMachineTag();
391  int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
392  ASSERT_NE(syncer::kInvalidId, sync_id);
393
394  // Check that this machine's data is not included in the foreign windows.
395  std::vector<const SyncedSession*> foreign_sessions;
396  ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
397  ASSERT_EQ(foreign_sessions.size(), 0U);
398
399  // Get the tabs for this machine from the node and check that they were
400  // filled.
401  SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_;
402  ASSERT_EQ(2U, tab_map.size());
403  // Tabs are ordered by sessionid in tab_map, so should be able to traverse
404  // the tree based on order of tabs created
405  SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
406  ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
407  ASSERT_EQ(GURL("http://foo/1"), iter->second->tab()->
408          GetEntryAtIndex(0)->GetVirtualURL());
409  ASSERT_EQ(GURL("http://foo/2"), iter->second->tab()->
410          GetEntryAtIndex(1)->GetVirtualURL());
411  iter++;
412  ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
413  ASSERT_EQ(GURL("http://bar/1"), iter->second->tab()->
414      GetEntryAtIndex(0)->GetVirtualURL());
415  ASSERT_EQ(GURL("http://bar/2"), iter->second->tab()->
416      GetEntryAtIndex(1)->GetVirtualURL());
417}
418
419// Test that we fail on a failed model association.
420TEST_F(ProfileSyncServiceSessionTest, FailModelAssociation) {
421  ASSERT_TRUE(StartSyncService(base::Closure(), true));
422  ASSERT_TRUE(sync_service_->HasUnrecoverableError());
423}
424
425// Write a foreign session to a node, and then retrieve it.
426TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) {
427  CreateRootHelper create_root(this);
428  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
429  ASSERT_TRUE(create_root.success());
430
431  // Check that the DataTypeController associated the models.
432  bool has_nodes;
433  ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
434  ASSERT_TRUE(has_nodes);
435
436  // Fill an instance of session specifics with a foreign session's data.
437  std::string tag = "tag1";
438  sync_pb::SessionSpecifics meta;
439  BuildSessionSpecifics(tag, &meta);
440  SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
441  std::vector<SessionID::id_type> tab_list1(
442      tab_nums1, tab_nums1 + arraysize(tab_nums1));
443  AddWindowSpecifics(0, tab_list1, &meta);
444  std::vector<sync_pb::SessionSpecifics> tabs1;
445  tabs1.resize(tab_list1.size());
446  for (size_t i = 0; i < tab_list1.size(); ++i) {
447    BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
448  }
449
450  // Update associator with the session's meta node containing one window.
451  model_associator_->AssociateForeignSpecifics(meta, base::Time());
452  // Add tabs for the window.
453  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
454       iter != tabs1.end(); ++iter) {
455    model_associator_->AssociateForeignSpecifics(*iter, base::Time());
456  }
457
458  // Check that the foreign session was associated and retrieve the data.
459  std::vector<const SyncedSession*> foreign_sessions;
460  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
461  ASSERT_EQ(1U, foreign_sessions.size());
462  std::vector<std::vector<SessionID::id_type> > session_reference;
463  session_reference.push_back(tab_list1);
464  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
465}
466
467// Write a foreign session with one window to a node. Sync, then add a window.
468// Sync, then add a third window. Close the two windows.
469TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeThreeWindows) {
470  CreateRootHelper create_root(this);
471  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
472  ASSERT_TRUE(create_root.success());
473
474  // Build a foreign session with one window and four tabs.
475  std::string tag = "tag1";
476  sync_pb::SessionSpecifics meta;
477  BuildSessionSpecifics(tag, &meta);
478  SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
479  std::vector<SessionID::id_type> tab_list1(
480      tab_nums1, tab_nums1 + arraysize(tab_nums1));
481  AddWindowSpecifics(0, tab_list1, &meta);
482  std::vector<sync_pb::SessionSpecifics> tabs1;
483  tabs1.resize(tab_list1.size());
484  for (size_t i = 0; i < tab_list1.size(); ++i) {
485    BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
486  }
487  // Update associator with the session's meta node containing one window.
488  model_associator_->AssociateForeignSpecifics(meta, base::Time());
489  // Add tabs for first window.
490  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
491       iter != tabs1.end(); ++iter) {
492    model_associator_->AssociateForeignSpecifics(*iter, base::Time());
493  }
494
495  // Verify first window
496  std::vector<const SyncedSession*> foreign_sessions;
497  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
498  std::vector<std::vector<SessionID::id_type> > session_reference;
499  session_reference.push_back(tab_list1);
500  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
501
502  // Add a second window.
503  SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
504  std::vector<SessionID::id_type> tab_list2(
505      tab_nums2, tab_nums2 + arraysize(tab_nums2));
506  AddWindowSpecifics(1, tab_list2, &meta);
507  std::vector<sync_pb::SessionSpecifics> tabs2;
508  tabs2.resize(tab_list2.size());
509  for (size_t i = 0; i < tab_list2.size(); ++i) {
510    BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
511  }
512  // Update associator with the session's meta node containing two windows.
513  model_associator_->AssociateForeignSpecifics(meta, base::Time());
514  // Add tabs for second window.
515  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin();
516       iter != tabs2.end(); ++iter) {
517    model_associator_->AssociateForeignSpecifics(*iter, base::Time());
518  }
519
520  // Verify the two windows.
521  foreign_sessions.clear();
522  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
523  ASSERT_EQ(1U, foreign_sessions.size());
524  session_reference.push_back(tab_list2);
525  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
526
527  // Add a third window.
528  SessionID::id_type tab_nums3[] = {8, 16, 19, 21};
529  std::vector<SessionID::id_type> tab_list3(
530      tab_nums3, tab_nums3 + arraysize(tab_nums3));
531  AddWindowSpecifics(2, tab_list3, &meta);
532  std::vector<sync_pb::SessionSpecifics> tabs3;
533  tabs3.resize(tab_list3.size());
534  for (size_t i = 0; i < tab_list3.size(); ++i) {
535    BuildTabSpecifics(tag, 0, tab_list3[i], &tabs3[i]);
536  }
537  // Update associator with the session's meta node containing three windows.
538  model_associator_->AssociateForeignSpecifics(meta, base::Time());
539  // Add tabs for third window.
540  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs3.begin();
541       iter != tabs3.end(); ++iter) {
542    model_associator_->AssociateForeignSpecifics(*iter, base::Time());
543  }
544
545  // Verify the three windows
546  foreign_sessions.clear();
547  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
548  ASSERT_EQ(1U, foreign_sessions.size());
549  session_reference.push_back(tab_list3);
550  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
551
552  // Close third window (by clearing and then not adding it back).
553  meta.mutable_header()->clear_window();
554  AddWindowSpecifics(0, tab_list1, &meta);
555  AddWindowSpecifics(1, tab_list2, &meta);
556  // Update associator with just the meta node, now containing only two windows.
557  model_associator_->AssociateForeignSpecifics(meta, base::Time());
558
559  // Verify first two windows are still there.
560  foreign_sessions.clear();
561  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
562  ASSERT_EQ(1U, foreign_sessions.size());
563  session_reference.pop_back();  // Pop off the data for the third window.
564  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
565
566  // Close second window (by clearing and then not adding it back).
567  meta.mutable_header()->clear_window();
568  AddWindowSpecifics(0, tab_list1, &meta);
569  // Update associator with just the meta node, now containing only one windows.
570  model_associator_->AssociateForeignSpecifics(meta, base::Time());
571
572  // Verify first window is still there.
573  foreign_sessions.clear();
574  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
575  ASSERT_EQ(1U, foreign_sessions.size());
576  session_reference.pop_back();  // Pop off the data for the second window.
577  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
578}
579
580// Write a foreign session to a node, with the tabs arriving first, and then
581// retrieve it.
582TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeTabsFirst) {
583  CreateRootHelper create_root(this);
584  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
585  ASSERT_TRUE(create_root.success());
586
587  // Fill an instance of session specifics with a foreign session's data.
588  std::string tag = "tag1";
589  sync_pb::SessionSpecifics meta;
590  BuildSessionSpecifics(tag, &meta);
591  SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
592  std::vector<SessionID::id_type> tab_list1(
593      tab_nums1, tab_nums1 + arraysize(tab_nums1));
594  AddWindowSpecifics(0, tab_list1, &meta);
595  std::vector<sync_pb::SessionSpecifics> tabs1;
596  tabs1.resize(tab_list1.size());
597  for (size_t i = 0; i < tab_list1.size(); ++i) {
598    BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
599  }
600
601  // Add tabs for first window.
602  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
603       iter != tabs1.end(); ++iter) {
604    model_associator_->AssociateForeignSpecifics(*iter, base::Time());
605  }
606  // Update associator with the session's meta node containing one window.
607  model_associator_->AssociateForeignSpecifics(meta, base::Time());
608
609  // Check that the foreign session was associated and retrieve the data.
610  std::vector<const SyncedSession*> foreign_sessions;
611  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
612  ASSERT_EQ(1U, foreign_sessions.size());
613  std::vector<std::vector<SessionID::id_type> > session_reference;
614  session_reference.push_back(tab_list1);
615  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
616}
617
618// Write a foreign session to a node with some tabs that never arrive.
619TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeMissingTabs) {
620  CreateRootHelper create_root(this);
621  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
622  ASSERT_TRUE(create_root.success());
623
624  // Fill an instance of session specifics with a foreign session's data.
625  std::string tag = "tag1";
626  sync_pb::SessionSpecifics meta;
627  BuildSessionSpecifics(tag, &meta);
628  SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
629  std::vector<SessionID::id_type> tab_list1(
630      tab_nums1, tab_nums1 + arraysize(tab_nums1));
631  AddWindowSpecifics(0, tab_list1, &meta);
632  std::vector<sync_pb::SessionSpecifics> tabs1;
633  tabs1.resize(tab_list1.size());  // First window has all the tabs
634  for (size_t i = 0; i < tab_list1.size(); ++i) {
635    BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
636  }
637  // Add a second window, but this time only create two tab nodes, despite the
638  // window expecting four tabs.
639  SessionID::id_type tab_nums2[] = {7, 15, 18, 20};
640  std::vector<SessionID::id_type> tab_list2(
641      tab_nums2, tab_nums2 + arraysize(tab_nums2));
642  AddWindowSpecifics(1, tab_list2, &meta);
643  std::vector<sync_pb::SessionSpecifics> tabs2;
644  tabs2.resize(2);
645  for (size_t i = 0; i < 2; ++i) {
646    BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
647  }
648
649  // Update associator with the session's meta node containing two windows.
650  model_associator_->AssociateForeignSpecifics(meta, base::Time());
651  // Add tabs for first window.
652  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
653       iter != tabs1.end(); ++iter) {
654    model_associator_->AssociateForeignSpecifics(*iter, base::Time());
655  }
656  // Add tabs for second window.
657  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin();
658       iter != tabs2.end(); ++iter) {
659    model_associator_->AssociateForeignSpecifics(*iter, base::Time());
660  }
661
662  // Check that the foreign session was associated and retrieve the data.
663  std::vector<const SyncedSession*> foreign_sessions;
664  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
665  ASSERT_EQ(1U, foreign_sessions.size());
666  ASSERT_EQ(2U, foreign_sessions[0]->windows.size());
667  ASSERT_EQ(4U, foreign_sessions[0]->windows.find(0)->second->tabs.size());
668  ASSERT_EQ(4U, foreign_sessions[0]->windows.find(1)->second->tabs.size());
669
670  // Close the second window.
671  meta.mutable_header()->clear_window();
672  AddWindowSpecifics(0, tab_list1, &meta);
673
674  // Update associator with the session's meta node containing one window.
675  model_associator_->AssociateForeignSpecifics(meta, base::Time());
676
677  // Check that the foreign session was associated and retrieve the data.
678  foreign_sessions.clear();
679  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
680  ASSERT_EQ(1U, foreign_sessions.size());
681  ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
682  std::vector<std::vector<SessionID::id_type> > session_reference;
683  session_reference.push_back(tab_list1);
684  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
685}
686
687// Test the DataTypeController on update.
688TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionUpdate) {
689  CreateRootHelper create_root(this);
690  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
691  ASSERT_TRUE(create_root.success());
692  int64 node_id = model_associator_->GetSyncIdFromSessionTag(
693      model_associator_->GetCurrentMachineTag());
694  ASSERT_FALSE(notified_of_update_);
695  {
696    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
697    change_processor_->ApplyChangesFromSyncModel(
698        &trans, 0,
699        ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
700            node_id, ChangeRecord::ACTION_UPDATE));
701  }
702  ASSERT_TRUE(notified_of_update_);
703}
704
705// Test the DataTypeController on add.
706TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionAdd) {
707  CreateRootHelper create_root(this);
708  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
709  ASSERT_TRUE(create_root.success());
710
711  int64 node_id = model_associator_->GetSyncIdFromSessionTag(
712      model_associator_->GetCurrentMachineTag());
713  ASSERT_FALSE(notified_of_update_);
714  {
715    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
716    change_processor_->ApplyChangesFromSyncModel(
717        &trans, 0,
718        ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
719            node_id, ChangeRecord::ACTION_ADD));
720  }
721  ASSERT_TRUE(notified_of_update_);
722}
723
724// Test the DataTypeController on delete.
725TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionDelete) {
726  CreateRootHelper create_root(this);
727  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
728  ASSERT_TRUE(create_root.success());
729
730  int64 node_id = model_associator_->GetSyncIdFromSessionTag(
731      model_associator_->GetCurrentMachineTag());
732  sync_pb::EntitySpecifics deleted_specifics;
733  deleted_specifics.mutable_session()->set_session_tag("tag");
734  ASSERT_FALSE(notified_of_update_);
735  {
736    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
737    change_processor_->ApplyChangesFromSyncModel(
738        &trans, 0,
739        ProfileSyncServiceTestHelper::MakeSingletonDeletionChangeRecordList(
740            node_id, deleted_specifics));
741  }
742  ASSERT_TRUE(notified_of_update_);
743}
744// Test the TabNodePool when it starts off empty.
745TEST_F(ProfileSyncServiceSessionTest, TabNodePoolEmpty) {
746  CreateRootHelper create_root(this);
747  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
748  ASSERT_TRUE(create_root.success());
749
750  std::vector<int64> node_ids;
751  ASSERT_EQ(0U, model_associator_->tab_pool_.capacity());
752  ASSERT_TRUE(model_associator_->tab_pool_.empty());
753  ASSERT_TRUE(model_associator_->tab_pool_.full());
754  const size_t num_ids = 10;
755  for (size_t i = 0; i < num_ids; ++i) {
756    int64 id = model_associator_->tab_pool_.GetFreeTabNode();
757    ASSERT_GT(id, -1);
758    node_ids.push_back(id);
759  }
760  ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
761  ASSERT_TRUE(model_associator_->tab_pool_.empty());
762  ASSERT_FALSE(model_associator_->tab_pool_.full());
763  for (size_t i = 0; i < num_ids; ++i) {
764    model_associator_->tab_pool_.FreeTabNode(node_ids[i]);
765  }
766  ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
767  ASSERT_FALSE(model_associator_->tab_pool_.empty());
768  ASSERT_TRUE(model_associator_->tab_pool_.full());
769}
770
771// TODO(jhorwich): Re-enable when crbug.com/121487 addressed
772TEST_F(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty) {
773  CreateRootHelper create_root(this);
774  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
775  ASSERT_TRUE(create_root.success());
776
777  const size_t num_starting_nodes = 3;
778  for (size_t i = 0; i < num_starting_nodes; ++i) {
779    model_associator_->tab_pool_.AddTabNode(i);
780  }
781
782  std::vector<int64> node_ids;
783  ASSERT_EQ(num_starting_nodes, model_associator_->tab_pool_.capacity());
784  ASSERT_FALSE(model_associator_->tab_pool_.empty());
785  ASSERT_TRUE(model_associator_->tab_pool_.full());
786  const size_t num_ids = 10;
787  for (size_t i = 0; i < num_ids; ++i) {
788    int64 id = model_associator_->tab_pool_.GetFreeTabNode();
789    ASSERT_GT(id, -1);
790    node_ids.push_back(id);
791  }
792  ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
793  ASSERT_TRUE(model_associator_->tab_pool_.empty());
794  ASSERT_FALSE(model_associator_->tab_pool_.full());
795  for (size_t i = 0; i < num_ids; ++i) {
796    model_associator_->tab_pool_.FreeTabNode(node_ids[i]);
797  }
798  ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
799  ASSERT_FALSE(model_associator_->tab_pool_.empty());
800  ASSERT_TRUE(model_associator_->tab_pool_.full());
801}
802
803// Write a foreign session to a node, and then delete it.
804TEST_F(ProfileSyncServiceSessionTest, DeleteForeignSession) {
805  CreateRootHelper create_root(this);
806  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
807  ASSERT_TRUE(create_root.success());
808
809  // Check that the DataTypeController associated the models.
810  bool has_nodes;
811  ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
812  ASSERT_TRUE(has_nodes);
813
814  // A foreign session's tag.
815  std::string tag = "tag1";
816
817  // Should do nothing if the foreign session doesn't exist.
818  std::vector<const SyncedSession*> foreign_sessions;
819  ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
820  model_associator_->DeleteForeignSession(tag);
821  ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
822
823  // Fill an instance of session specifics with a foreign session's data.
824  sync_pb::SessionSpecifics meta;
825  BuildSessionSpecifics(tag, &meta);
826  SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
827  std::vector<SessionID::id_type> tab_list1(
828      tab_nums1, tab_nums1 + arraysize(tab_nums1));
829  AddWindowSpecifics(0, tab_list1, &meta);
830  std::vector<sync_pb::SessionSpecifics> tabs1;
831  tabs1.resize(tab_list1.size());
832  for (size_t i = 0; i < tab_list1.size(); ++i) {
833    BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
834  }
835
836  // Update associator with the session's meta node containing one window.
837  model_associator_->AssociateForeignSpecifics(meta, base::Time());
838  // Add tabs for the window.
839  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
840       iter != tabs1.end(); ++iter) {
841    model_associator_->AssociateForeignSpecifics(*iter, base::Time());
842  }
843
844  // Check that the foreign session was associated and retrieve the data.
845  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
846  ASSERT_EQ(1U, foreign_sessions.size());
847  std::vector<std::vector<SessionID::id_type> > session_reference;
848  session_reference.push_back(tab_list1);
849  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
850
851  // Now delete the foreign session.
852  model_associator_->DeleteForeignSession(tag);
853  ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
854}
855
856// Associate both a non-stale foreign session and a stale foreign session.
857// Ensure only the stale session gets deleted.
858TEST_F(ProfileSyncServiceSessionTest, DeleteStaleSessions) {
859  CreateRootHelper create_root(this);
860  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
861  ASSERT_TRUE(create_root.success());
862
863  // Fill two instances of session specifics with a foreign session's data.
864  std::string tag = "tag1";
865  sync_pb::SessionSpecifics meta;
866  BuildSessionSpecifics(tag, &meta);
867  SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
868  std::vector<SessionID::id_type> tab_list1(
869      tab_nums1, tab_nums1 + arraysize(tab_nums1));
870  AddWindowSpecifics(0, tab_list1, &meta);
871  std::vector<sync_pb::SessionSpecifics> tabs1;
872  tabs1.resize(tab_list1.size());
873  for (size_t i = 0; i < tab_list1.size(); ++i) {
874    BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
875  }
876  std::string tag2 = "tag2";
877  sync_pb::SessionSpecifics meta2;
878  BuildSessionSpecifics(tag2, &meta2);
879  SessionID::id_type tab_nums2[] = {8, 15, 18, 20};
880  std::vector<SessionID::id_type> tab_list2(
881      tab_nums2, tab_nums2 + arraysize(tab_nums2));
882  AddWindowSpecifics(0, tab_list2, &meta2);
883  std::vector<sync_pb::SessionSpecifics> tabs2;
884  tabs2.resize(tab_list2.size());
885  for (size_t i = 0; i < tab_list2.size(); ++i) {
886    BuildTabSpecifics(tag2, 0, tab_list2[i], &tabs2[i]);
887  }
888
889  // Set the modification time for tag1 to be 21 days ago, tag2 to 5 days ago.
890  base::Time tag1_time = base::Time::Now() - base::TimeDelta::FromDays(21);
891  base::Time tag2_time = base::Time::Now() - base::TimeDelta::FromDays(5);
892
893  // Associate specifics.
894  model_associator_->AssociateForeignSpecifics(meta, tag1_time);
895  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
896       iter != tabs1.end(); ++iter) {
897    model_associator_->AssociateForeignSpecifics(*iter, tag1_time);
898  }
899  model_associator_->AssociateForeignSpecifics(meta2, tag2_time);
900  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs2.begin();
901       iter != tabs2.end(); ++iter) {
902    model_associator_->AssociateForeignSpecifics(*iter, tag2_time);
903  }
904
905  // Check that the foreign session was associated and retrieve the data.
906  std::vector<const SyncedSession*> foreign_sessions;
907  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
908  ASSERT_EQ(2U, foreign_sessions.size());
909
910  // Now delete the stale session and verify the non-stale one is still there.
911  model_associator_->DeleteStaleSessions();
912  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
913  ASSERT_EQ(1U, foreign_sessions.size());
914  std::vector<std::vector<SessionID::id_type> > session_reference;
915  session_reference.push_back(tab_list2);
916  VerifySyncedSession(tag2, session_reference, *(foreign_sessions[0]));
917}
918
919// Write a stale foreign session to a node. Then update one of its tabs so
920// the session is no longer stale. Ensure it doesn't get deleted.
921TEST_F(ProfileSyncServiceSessionTest, StaleSessionRefresh) {
922  CreateRootHelper create_root(this);
923  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
924  ASSERT_TRUE(create_root.success());
925
926  std::string tag = "tag1";
927  sync_pb::SessionSpecifics meta;
928  BuildSessionSpecifics(tag, &meta);
929  SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
930  std::vector<SessionID::id_type> tab_list1(
931      tab_nums1, tab_nums1 + arraysize(tab_nums1));
932  AddWindowSpecifics(0, tab_list1, &meta);
933  std::vector<sync_pb::SessionSpecifics> tabs1;
934  tabs1.resize(tab_list1.size());
935  for (size_t i = 0; i < tab_list1.size(); ++i) {
936    BuildTabSpecifics(tag, 0, tab_list1[i], &tabs1[i]);
937  }
938
939  // Associate.
940  base::Time stale_time = base::Time::Now() - base::TimeDelta::FromDays(21);
941  model_associator_->AssociateForeignSpecifics(meta, stale_time);
942  for (std::vector<sync_pb::SessionSpecifics>::iterator iter = tabs1.begin();
943       iter != tabs1.end(); ++iter) {
944    model_associator_->AssociateForeignSpecifics(*iter, stale_time);
945  }
946
947  // Associate one of the tabs with a non-stale time.
948  model_associator_->AssociateForeignSpecifics(tabs1[0], base::Time::Now());
949
950  // Check that the foreign session was associated and retrieve the data.
951  std::vector<const SyncedSession*> foreign_sessions;
952  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
953  ASSERT_EQ(1U, foreign_sessions.size());
954
955  // Verify the now non-stale session does not get deleted.
956  model_associator_->DeleteStaleSessions();
957  ASSERT_TRUE(model_associator_->GetAllForeignSessions(&foreign_sessions));
958  ASSERT_EQ(1U, foreign_sessions.size());
959  std::vector<std::vector<SessionID::id_type> > session_reference;
960  session_reference.push_back(tab_list1);
961  VerifySyncedSession(tag, session_reference, *(foreign_sessions[0]));
962}
963
964// Crashes sometimes on Windows, particularly XP.
965// See http://crbug.com/174951
966#if defined(OS_WIN)
967#define MAYBE_ValidTabs DISABLED_ValidTabs
968#else
969#define MAYBE_ValidTabs ValidTabs
970#endif  // defined(OS_WIN)
971
972// Test that tabs with nothing but "chrome://*" and "file://*" navigations are
973// not be synced.
974TEST_F(ProfileSyncServiceSessionTest, MAYBE_ValidTabs) {
975  CreateRootHelper create_root(this);
976  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
977  ASSERT_TRUE(create_root.success());
978
979  AddTab(browser(), GURL("chrome://bla1/"));
980  NavigateAndCommitActiveTab(GURL("chrome://bla2"));
981  AddTab(browser(), GURL("file://bla3/"));
982  AddTab(browser(), GURL("bla://bla"));
983  // Note: chrome://newtab has special handling which crashes in unit tests.
984
985  // Get the tabs for this machine. Only the bla:// url should be synced.
986  SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_;
987  ASSERT_EQ(1U, tab_map.size());
988  SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
989  ASSERT_EQ(1, iter->second->tab()->GetEntryCount());
990  ASSERT_EQ(GURL("bla://bla"), iter->second->tab()->
991      GetEntryAtIndex(0)->GetVirtualURL());
992}
993
994// Verify that AttemptSessionsDataRefresh triggers the
995// NOTIFICATION_SYNC_REFRESH_LOCAL notification.
996// TODO(zea): Once we can have unit tests that are able to open to the NTP,
997// test that the NTP/#opentabs URL triggers a refresh as well (but only when
998// it is the active tab).
999TEST_F(ProfileSyncServiceSessionTest, SessionsRefresh) {
1000  CreateRootHelper create_root(this);
1001  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1002  ASSERT_TRUE(create_root.success());
1003
1004  // Empty, so returns false.
1005  std::vector<const SyncedSession*> foreign_sessions;
1006  ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
1007  ASSERT_FALSE(notified_of_refresh_);
1008  model_associator_->AttemptSessionsDataRefresh();
1009  ASSERT_TRUE(notified_of_refresh_);
1010
1011  // Nothing should have changed since we don't have unapplied data.
1012  ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
1013}
1014
1015// Ensure model association associates the pre-existing tabs.
1016TEST_F(ProfileSyncServiceSessionTest, ExistingTabs) {
1017  AddTab(browser(), GURL("http://foo1"));
1018  NavigateAndCommitActiveTab(GURL("http://foo2"));
1019  AddTab(browser(), GURL("http://bar1"));
1020  NavigateAndCommitActiveTab(GURL("http://bar2"));
1021
1022  CreateRootHelper create_root(this);
1023  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1024  ASSERT_TRUE(create_root.success());
1025  bool has_nodes;
1026  ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
1027  ASSERT_TRUE(has_nodes);
1028
1029  std::string machine_tag = model_associator_->GetCurrentMachineTag();
1030  int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
1031  ASSERT_NE(syncer::kInvalidId, sync_id);
1032
1033  // Check that this machine's data is not included in the foreign windows.
1034  std::vector<const SyncedSession*> foreign_sessions;
1035  ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
1036  ASSERT_EQ(foreign_sessions.size(), 0U);
1037
1038  // Get the tabs for this machine from the node and check that they were
1039  // filled.
1040  SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_;
1041  ASSERT_EQ(2U, tab_map.size());
1042  // Tabs are ordered by sessionid in tab_map, so should be able to traverse
1043  // the tree based on order of tabs created
1044  SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
1045  ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
1046  ASSERT_EQ(GURL("http://foo1"), iter->second->tab()->
1047          GetEntryAtIndex(0)->GetVirtualURL());
1048  ASSERT_EQ(GURL("http://foo2"), iter->second->tab()->
1049          GetEntryAtIndex(1)->GetVirtualURL());
1050  iter++;
1051  ASSERT_EQ(2, iter->second->tab()->GetEntryCount());
1052  ASSERT_EQ(GURL("http://bar1"), iter->second->tab()->
1053      GetEntryAtIndex(0)->GetVirtualURL());
1054  ASSERT_EQ(GURL("http://bar2"), iter->second->tab()->
1055      GetEntryAtIndex(1)->GetVirtualURL());
1056}
1057
1058TEST_F(ProfileSyncServiceSessionTest, MissingHeaderAndTab) {
1059  AddTab(browser(), GURL("http://foo1"));
1060  NavigateAndCommitActiveTab(GURL("http://foo2"));
1061  AddTab(browser(), GURL("http://bar1"));
1062  NavigateAndCommitActiveTab(GURL("http://bar2"));
1063  CreateRootHelper create_root(this);
1064  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1065  syncer::SyncError error;
1066  std::string local_tag = model_associator_->GetCurrentMachineTag();
1067
1068  error = model_associator_->DisassociateModels();
1069  ASSERT_FALSE(error.IsSet());
1070  {
1071    // Create a sync node with the local tag but neither header nor tab field.
1072    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
1073    syncer::ReadNode root(&trans);
1074    root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS));
1075    syncer::WriteNode extra_header(&trans);
1076    syncer::WriteNode::InitUniqueByCreationResult result =
1077        extra_header.InitUniqueByCreation(syncer::SESSIONS, root, "new_tag");
1078    ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result);
1079    sync_pb::SessionSpecifics specifics;
1080    specifics.set_session_tag(local_tag);
1081    extra_header.SetSessionSpecifics(specifics);
1082  }
1083
1084  error = model_associator_->AssociateModels(NULL, NULL);
1085  ASSERT_FALSE(error.IsSet());
1086}
1087
1088TEST_F(ProfileSyncServiceSessionTest, MultipleHeaders) {
1089  AddTab(browser(), GURL("http://foo1"));
1090  NavigateAndCommitActiveTab(GURL("http://foo2"));
1091  AddTab(browser(), GURL("http://bar1"));
1092  NavigateAndCommitActiveTab(GURL("http://bar2"));
1093  CreateRootHelper create_root(this);
1094  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1095  syncer::SyncError error;
1096  std::string local_tag = model_associator_->GetCurrentMachineTag();
1097
1098  error = model_associator_->DisassociateModels();
1099  ASSERT_FALSE(error.IsSet());
1100  {
1101    // Create another sync node with a header field and the local tag.
1102    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
1103    syncer::ReadNode root(&trans);
1104    root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS));
1105    syncer::WriteNode extra_header(&trans);
1106    syncer::WriteNode::InitUniqueByCreationResult result =
1107        extra_header.InitUniqueByCreation(syncer::SESSIONS,
1108                                          root, local_tag + "_");
1109    ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result);
1110    sync_pb::SessionSpecifics specifics;
1111    specifics.set_session_tag(local_tag);
1112    specifics.mutable_header();
1113    extra_header.SetSessionSpecifics(specifics);
1114  }
1115  error = model_associator_->AssociateModels(NULL, NULL);
1116  ASSERT_FALSE(error.IsSet());
1117}
1118
1119TEST_F(ProfileSyncServiceSessionTest, CorruptedForeign) {
1120  AddTab(browser(), GURL("http://foo1"));
1121  NavigateAndCommitActiveTab(GURL("http://foo2"));
1122  AddTab(browser(), GURL("http://bar1"));
1123  NavigateAndCommitActiveTab(GURL("http://bar2"));
1124  CreateRootHelper create_root(this);
1125  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1126  syncer::SyncError error;
1127
1128  error = model_associator_->DisassociateModels();
1129  ASSERT_FALSE(error.IsSet());
1130  {
1131    // Create another sync node with neither header nor tab field and a foreign
1132    // tag.
1133    std::string foreign_tag = "foreign_tag";
1134    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
1135    syncer::ReadNode root(&trans);
1136    root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS));
1137    syncer::WriteNode extra_header(&trans);
1138    syncer::WriteNode::InitUniqueByCreationResult result =
1139        extra_header.InitUniqueByCreation(syncer::SESSIONS,
1140                                          root, foreign_tag);
1141    ASSERT_EQ(syncer::WriteNode::INIT_SUCCESS, result);
1142    sync_pb::SessionSpecifics specifics;
1143    specifics.set_session_tag(foreign_tag);
1144    extra_header.SetSessionSpecifics(specifics);
1145  }
1146  error = model_associator_->AssociateModels(NULL, NULL);
1147  ASSERT_FALSE(error.IsSet());
1148}
1149
1150TEST_F(ProfileSyncServiceSessionTest, MissingLocalTabNode) {
1151  AddTab(browser(), GURL("http://foo1"));
1152  NavigateAndCommitActiveTab(GURL("http://foo2"));
1153  AddTab(browser(), GURL("http://bar1"));
1154  NavigateAndCommitActiveTab(GURL("http://bar2"));
1155  CreateRootHelper create_root(this);
1156  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1157  std::string local_tag = model_associator_->GetCurrentMachineTag();
1158  syncer::SyncError error;
1159
1160  error = model_associator_->DisassociateModels();
1161  ASSERT_FALSE(error.IsSet());
1162  {
1163    // Delete the first sync tab node.
1164    std::string tab_tag = TabNodePool::TabIdToTag(local_tag, 0);
1165
1166    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
1167    syncer::ReadNode root(&trans);
1168    root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS));
1169    syncer::WriteNode tab_node(&trans);
1170    ASSERT_EQ(syncer::BaseNode::INIT_OK,
1171              tab_node.InitByClientTagLookup(syncer::SESSIONS, tab_tag));
1172    tab_node.Tombstone();
1173  }
1174  error = model_associator_->AssociateModels(NULL, NULL);
1175  ASSERT_FALSE(error.IsSet());
1176
1177  // Add some more tabs to ensure we don't conflict with the pre-existing tab
1178  // node.
1179  AddTab(browser(), GURL("http://baz1"));
1180  AddTab(browser(), GURL("http://baz2"));
1181}
1182
1183TEST_F(ProfileSyncServiceSessionTest, Favicons) {
1184    CreateRootHelper create_root(this);
1185  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1186  ASSERT_TRUE(create_root.success());
1187
1188  // Build a foreign session with one window and one tab.
1189  std::string tag = "tag1";
1190  sync_pb::SessionSpecifics meta;
1191  BuildSessionSpecifics(tag, &meta);
1192  std::vector<SessionID::id_type> tab_list;
1193  tab_list.push_back(5);
1194  AddWindowSpecifics(0, tab_list, &meta);
1195  sync_pb::SessionSpecifics tab;
1196  BuildTabSpecifics(tag, 0, tab_list[0], &tab);
1197  std::string url = tab.tab().navigation(0).virtual_url();
1198  scoped_refptr<base::RefCountedMemory> favicon;
1199
1200  // Update associator.
1201  model_associator_->AssociateForeignSpecifics(meta, base::Time());
1202  model_associator_->AssociateForeignSpecifics(tab, base::Time());
1203  base::MessageLoop::current()->RunUntilIdle();
1204  ASSERT_FALSE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon));
1205
1206  // Now add a favicon.
1207  tab.mutable_tab()->set_favicon_source("http://favicon_source.com/png.ico");
1208  tab.mutable_tab()->set_favicon_type(sync_pb::SessionTab::TYPE_WEB_FAVICON);
1209  tab.mutable_tab()->set_favicon("data");
1210  model_associator_->AssociateForeignSpecifics(tab, base::Time());
1211  base::MessageLoop::current()->RunUntilIdle();
1212  ASSERT_TRUE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon));
1213  ASSERT_TRUE(CompareMemoryToString("data", favicon));
1214
1215  // Simulate navigating away. The associator should not delete the favicon.
1216  tab.mutable_tab()->clear_navigation();
1217  tab.mutable_tab()->add_navigation()->set_virtual_url("http://new_url.com");
1218  tab.mutable_tab()->clear_favicon_source();
1219  tab.mutable_tab()->clear_favicon_type();
1220  tab.mutable_tab()->clear_favicon();
1221  model_associator_->AssociateForeignSpecifics(tab, base::Time());
1222  base::MessageLoop::current()->RunUntilIdle();
1223  ASSERT_TRUE(model_associator_->GetSyncedFaviconForPageURL(url, &favicon));
1224}
1225
1226TEST_F(ProfileSyncServiceSessionTest, CorruptedLocalHeader) {
1227  AddTab(browser(), GURL("http://foo1"));
1228  NavigateAndCommitActiveTab(GURL("http://foo2"));
1229  AddTab(browser(), GURL("http://bar1"));
1230  NavigateAndCommitActiveTab(GURL("http://bar2"));
1231  CreateRootHelper create_root(this);
1232  ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1233  std::string local_tag = model_associator_->GetCurrentMachineTag();
1234  syncer::SyncError error;
1235
1236  error = model_associator_->DisassociateModels();
1237  ASSERT_FALSE(error.IsSet());
1238  {
1239    // Load the header node and clear it.
1240    syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
1241    syncer::WriteNode header(&trans);
1242    ASSERT_EQ(syncer::BaseNode::INIT_OK,
1243              header.InitByClientTagLookup(syncer::SESSIONS, local_tag));
1244    sync_pb::SessionSpecifics specifics;
1245    header.SetSessionSpecifics(specifics);
1246  }
1247  // Ensure we associate properly despite the pre-existing node with our local
1248  // tag.
1249  error = model_associator_->AssociateModels(NULL, NULL);
1250  ASSERT_FALSE(error.IsSet());
1251}
1252
1253}  // namespace browser_sync
1254