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