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