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.
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"
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;
77 using testing::Return;
79 namespace browser_sync {
83 class FakeProfileSyncService : public TestProfileSyncService {
85 FakeProfileSyncService(
86 ProfileSyncComponentsFactory* factory,
88 SigninManagerBase* signin,
89 ProfileOAuth2TokenService* oauth2_token_service,
90 ProfileSyncService::StartBehavior behavior)
91 : TestProfileSyncService(factory,
96 virtual ~FakeProfileSyncService() {}
98 virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE {
99 return scoped_ptr<DeviceInfo>(new DeviceInfo(base::GenerateGUID(),
103 sync_pb::SyncEnums::TYPE_WIN));
107 bool CompareMemoryToString(
108 const std::string& str,
109 const scoped_refptr<base::RefCountedMemory>& mem) {
110 if (mem->size() != str.size())
112 for (size_t i = 0; i <mem->size(); ++i) {
113 if (str[i] != *(mem->front() + i))
119 ACTION_P(ReturnSyncBackendHost, callback) {
120 return new browser_sync::SyncBackendHostForProfileSyncTest(
121 arg1, arg2, callback);
126 class ProfileSyncServiceSessionTest
127 : public BrowserWithTestWindowTest,
128 public content::NotificationObserver {
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(); }
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();
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());
160 virtual void Observe(int type,
161 const content::NotificationSource& source,
162 const content::NotificationDetails& details) OVERRIDE {
164 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
165 notified_of_update_ = true;
167 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL:
168 notified_of_refresh_ = true;
176 virtual void TearDown() {
177 sync_service_->Shutdown();
178 sync_service_.reset();
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());
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();
192 bool StartSyncService(const base::Closure& callback,
193 bool will_fail_association) {
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(
207 oauth2_token_service,
208 ProfileSyncService::AUTO_START));
209 EXPECT_CALL(*factory, CreateSyncBackendHost(_,_,_)).
210 WillOnce(ReturnSyncBackendHost(callback));
212 // Register the session data type.
213 SessionDataTypeController *dtc = new SessionDataTypeController(factory,
215 sync_service_.get());
216 sync_service_->RegisterDataTypeController(dtc);
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());
230 ProfileOAuth2TokenServiceFactory::GetForProfile(profile())
231 ->UpdateCredentials("test_user", "oauth2_login_token");
232 sync_service_->Initialize();
233 base::MessageLoop::current()->Run();
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_;
251 class CreateRootHelper {
253 explicit CreateRootHelper(ProfileSyncServiceSessionTest* test)
254 : callback_(base::Bind(&CreateRootHelper::CreateRootCallback,
255 base::Unretained(this), test)),
259 virtual ~CreateRootHelper() {}
261 const base::Closure& callback() const { return callback_; }
262 bool success() { return success_; }
265 void CreateRootCallback(ProfileSyncServiceSessionTest* test) {
266 success_ = syncer::TestUserShare::CreateRoot(
267 syncer::SESSIONS, test->sync_service()->GetUserShare());
270 base::Closure callback_;
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());
280 // Check that the DataTypeController associated the models.
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);
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());
302 // Crashes sometimes on Windows, particularly XP.
303 // See http://crbug.com/174951
305 #define MAYBE_WriteFilledSessionToNode DISABLED_WriteFilledSessionToNode
307 #define MAYBE_WriteFilledSessionToNode WriteFilledSessionToNode
308 #endif // defined(OS_WIN)
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());
317 // Check that the DataTypeController associated the models.
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"));
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);
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);
337 // Get the tabs for this machine from the node and check that they were
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());
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());
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());
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());
370 // Check that the DataTypeController associated the models.
372 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
373 ASSERT_TRUE(has_nodes);
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));
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());
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]));
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());
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());
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]));
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]);
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());
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]));
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]);
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());
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]));
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());
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]));
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());
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]));
507 // Write a foreign session to a node, with the tabs arriving first, and then
509 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeTabsFirst) {
510 CreateRootHelper create_root(this);
511 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
512 ASSERT_TRUE(create_root.success());
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));
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());
527 // Update associator with the session's meta node containing one window.
528 model_associator_->AssociateForeignSpecifics(meta, base::Time());
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]));
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());
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;
560 for (size_t i = 0; i < 2; ++i) {
561 helper_.BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
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());
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());
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());
585 // Close the second window.
586 meta.mutable_header()->clear_window();
587 helper_.AddWindowSpecifics(0, tab_list1, &meta);
589 // Update associator with the session's meta node containing one window.
590 model_associator_->AssociateForeignSpecifics(meta, base::Time());
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]));
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_);
611 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
612 change_processor_->ApplyChangesFromSyncModel(
614 ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
615 node_id, ChangeRecord::ACTION_UPDATE));
617 ASSERT_TRUE(notified_of_update_);
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());
626 int64 node_id = model_associator_->GetSyncIdFromSessionTag(
627 model_associator_->GetCurrentMachineTag());
628 ASSERT_FALSE(notified_of_update_);
630 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
631 change_processor_->ApplyChangesFromSyncModel(
633 ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
634 node_id, ChangeRecord::ACTION_ADD));
636 ASSERT_TRUE(notified_of_update_);
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());
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_);
651 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
652 change_processor_->ApplyChangesFromSyncModel(
654 ProfileSyncServiceTestHelper::MakeSingletonDeletionChangeRecordList(
655 node_id, deleted_specifics));
657 ASSERT_TRUE(notified_of_update_);
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());
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);
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]);
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());
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());
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);
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);
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]);
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());
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());
731 // Check that the DataTypeController associated the models.
733 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
734 ASSERT_TRUE(has_nodes);
736 // A foreign session's tag.
737 std::string tag = "tag1";
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_);
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));
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());
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]));
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));
776 // Verify that deleteForeignSession triggers the
777 // NOTIFICATION_FOREIGN_SESSION_DISABLED notification.
778 ASSERT_TRUE(notified_of_update_);
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());
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]);
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);
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);
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);
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());
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]));
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());
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));
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);
860 // Associate one of the tabs with a non-stale time.
861 model_associator_->AssociateForeignSpecifics(tabs1[0], base::Time::Now());
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());
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]));
877 // Crashes sometimes on Windows, particularly XP.
878 // See http://crbug.com/174951
880 #define MAYBE_ValidTabs DISABLED_ValidTabs
882 #define MAYBE_ValidTabs ValidTabs
883 #endif // defined(OS_WIN)
885 // Test that tabs with nothing but "chrome://*" and "file://*" navigations are
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());
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.
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());
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());
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_);
925 // Nothing should have changed since we don't have unapplied data.
926 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
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"));
936 CreateRootHelper create_root(this);
937 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
938 ASSERT_TRUE(create_root.success());
940 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
941 ASSERT_TRUE(has_nodes);
943 std::string machine_tag = model_associator_->GetCurrentMachineTag();
944 int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
945 ASSERT_NE(syncer::kInvalidId, sync_id);
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);
952 // Get the tabs for this machine from the node and check that they were
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());
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());
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();
983 error = model_associator_->DisassociateModels();
984 ASSERT_FALSE(error.IsSet());
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);
999 error = model_associator_->AssociateModels(NULL, NULL);
1000 ASSERT_FALSE(error.IsSet());
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();
1013 error = model_associator_->DisassociateModels();
1014 ASSERT_FALSE(error.IsSet());
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);
1030 error = model_associator_->AssociateModels(NULL, NULL);
1031 ASSERT_FALSE(error.IsSet());
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;
1043 error = model_associator_->DisassociateModels();
1044 ASSERT_FALSE(error.IsSet());
1046 // Create another sync node with neither header nor tab field and a foreign
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,
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);
1061 error = model_associator_->AssociateModels(NULL, NULL);
1062 ASSERT_FALSE(error.IsSet());
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;
1075 error = model_associator_->DisassociateModels();
1076 ASSERT_FALSE(error.IsSet());
1078 // Delete the first sync tab node.
1079 std::string tab_tag = TabNodePool::TabIdToTag(local_tag, 1);
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();
1089 error = model_associator_->AssociateModels(NULL, NULL);
1090 ASSERT_FALSE(error.IsSet());
1092 // Add some more tabs to ensure we don't conflict with the pre-existing tab
1094 AddTab(browser(), GURL("http://baz1"));
1095 AddTab(browser(), GURL("http://baz2"));
1098 TEST_F(ProfileSyncServiceSessionTest, Favicons) {
1099 CreateRootHelper create_root(this);
1100 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1101 ASSERT_TRUE(create_root.success());
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;
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));
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));
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));
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;
1151 error = model_associator_->DisassociateModels();
1152 ASSERT_FALSE(error.IsSet());
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);
1162 // Ensure we associate properly despite the pre-existing node with our local
1164 error = model_associator_->AssociateModels(NULL, NULL);
1165 ASSERT_FALSE(error.IsSet());
1168 TEST_F(ProfileSyncServiceSessionTest, CheckPrerenderedWebContentsSwap) {
1169 AddTab(browser(), GURL("http://foo1"));
1170 NavigateAndCommitActiveTab(GURL("http://foo2"));
1171 CreateRootHelper create_root(this);
1173 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1175 syncer::SyncError error;
1176 // Initial association.
1177 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
1178 ASSERT_FALSE(error.IsSet());
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();
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());
1194 // Swap the WebContents.
1196 browser()->tab_strip_model()->GetIndexOfWebContents(old_web_contents);
1197 browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents);
1199 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
1200 ASSERT_FALSE(error.IsSet());
1202 NavigateAndCommitActiveTab(GURL("http://bar2"));
1203 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
1204 ASSERT_FALSE(error.IsSet());
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());
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());
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());
1224 TEST_F(ProfileSyncServiceSessionTest, TabPoolFreeNodeLimits) {
1225 CreateRootHelper create_root(this);
1226 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1227 ASSERT_TRUE(create_root.success());
1229 // Allocate TabNodePool::kFreeNodesHighWatermark + 1 nodes and verify that
1230 // freeing the last node reduces the free node pool size to
1231 // kFreeNodesLowWatermark.
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);
1242 // Free all except one node.
1243 int last_sync_id = used_sync_ids.back();
1244 used_sync_ids.pop_back();
1246 for (size_t i = 0; i < used_sync_ids.size(); ++i) {
1247 model_associator_->local_tab_pool_.FreeTabNode(used_sync_ids[i]);
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());
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());
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
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.
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();
1286 tab_node.SetSessionSpecifics(specifics);
1289 error = model_associator_->DisassociateModels();
1290 ASSERT_FALSE(error.IsSet());
1291 error = model_associator_->AssociateModels(NULL, NULL);
1292 ASSERT_FALSE(error.IsSet());
1295 } // namespace browser_sync