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/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"
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;
82 namespace browser_sync {
86 class FakeProfileSyncService : public TestProfileSyncService {
88 FakeProfileSyncService(
89 ProfileSyncComponentsFactory* factory,
91 SigninManagerBase* signin,
92 ProfileOAuth2TokenService* oauth2_token_service,
93 ProfileSyncServiceStartBehavior behavior)
94 : TestProfileSyncService(factory,
99 virtual ~FakeProfileSyncService() {}
101 virtual scoped_ptr<DeviceInfo> GetLocalDeviceInfo() const OVERRIDE {
102 return scoped_ptr<DeviceInfo>(new DeviceInfo(base::GenerateGUID(),
106 sync_pb::SyncEnums::TYPE_WIN));
110 bool CompareMemoryToString(
111 const std::string& str,
112 const scoped_refptr<base::RefCountedMemory>& mem) {
113 if (mem->size() != str.size())
115 for (size_t i = 0; i <mem->size(); ++i) {
116 if (str[i] != *(mem->front() + i))
122 ACTION_P(ReturnSyncBackendHost, callback) {
123 return new browser_sync::SyncBackendHostForProfileSyncTest(
124 arg1, arg2, callback);
129 class ProfileSyncServiceSessionTest
130 : public BrowserWithTestWindowTest,
131 public content::NotificationObserver {
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(); }
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();
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");
164 virtual void Observe(int type,
165 const content::NotificationSource& source,
166 const content::NotificationDetails& details) OVERRIDE {
168 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
169 notified_of_update_ = true;
171 case chrome::NOTIFICATION_SYNC_REFRESH_LOCAL:
172 notified_of_refresh_ = true;
180 virtual void TearDown() {
181 sync_service_->Shutdown();
182 sync_service_.reset();
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());
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();
196 bool StartSyncService(const base::Closure& callback,
197 bool will_fail_association) {
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(
211 oauth2_token_service,
212 browser_sync::AUTO_START));
213 EXPECT_CALL(*factory, CreateSyncBackendHost(_,_,_)).
214 WillOnce(ReturnSyncBackendHost(callback));
216 // Register the session data type.
217 SessionDataTypeController *dtc = new SessionDataTypeController(factory,
219 sync_service_.get());
220 sync_service_->RegisterDataTypeController(dtc);
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());
234 ProfileOAuth2TokenServiceFactory::GetForProfile(profile())
235 ->UpdateCredentials("test_user", "oauth2_login_token");
236 sync_service_->Initialize();
237 base::MessageLoop::current()->Run();
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_;
255 class CreateRootHelper {
257 explicit CreateRootHelper(ProfileSyncServiceSessionTest* test)
258 : callback_(base::Bind(&CreateRootHelper::CreateRootCallback,
259 base::Unretained(this), test)),
263 virtual ~CreateRootHelper() {}
265 const base::Closure& callback() const { return callback_; }
266 bool success() { return success_; }
269 void CreateRootCallback(ProfileSyncServiceSessionTest* test) {
270 success_ = syncer::TestUserShare::CreateRoot(
271 syncer::SESSIONS, test->sync_service()->GetUserShare());
274 base::Closure callback_;
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());
284 // Check that the DataTypeController associated the models.
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);
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());
306 // Crashes sometimes on Windows, particularly XP.
307 // See http://crbug.com/174951
309 #define MAYBE_WriteFilledSessionToNode DISABLED_WriteFilledSessionToNode
311 #define MAYBE_WriteFilledSessionToNode WriteFilledSessionToNode
312 #endif // defined(OS_WIN)
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());
321 // Check that the DataTypeController associated the models.
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"));
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);
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);
341 // Get the tabs for this machine from the node and check that they were
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());
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());
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());
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());
374 // Check that the DataTypeController associated the models.
376 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
377 ASSERT_TRUE(has_nodes);
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));
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());
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]));
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());
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());
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]));
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]);
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());
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]));
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]);
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());
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]));
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());
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]));
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());
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]));
511 // Write a foreign session to a node, with the tabs arriving first, and then
513 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNodeTabsFirst) {
514 CreateRootHelper create_root(this);
515 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
516 ASSERT_TRUE(create_root.success());
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));
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());
531 // Update associator with the session's meta node containing one window.
532 model_associator_->AssociateForeignSpecifics(meta, base::Time());
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]));
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());
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;
564 for (size_t i = 0; i < 2; ++i) {
565 helper_.BuildTabSpecifics(tag, 0, tab_list2[i], &tabs2[i]);
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());
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());
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());
589 // Close the second window.
590 meta.mutable_header()->clear_window();
591 helper_.AddWindowSpecifics(0, tab_list1, &meta);
593 // Update associator with the session's meta node containing one window.
594 model_associator_->AssociateForeignSpecifics(meta, base::Time());
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]));
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_);
615 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
616 change_processor_->ApplyChangesFromSyncModel(
618 ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
619 node_id, ChangeRecord::ACTION_UPDATE));
621 ASSERT_TRUE(notified_of_update_);
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());
630 int64 node_id = model_associator_->GetSyncIdFromSessionTag(
631 model_associator_->GetCurrentMachineTag());
632 ASSERT_FALSE(notified_of_update_);
634 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
635 change_processor_->ApplyChangesFromSyncModel(
637 ProfileSyncServiceTestHelper::MakeSingletonChangeRecordList(
638 node_id, ChangeRecord::ACTION_ADD));
640 ASSERT_TRUE(notified_of_update_);
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());
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_);
655 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
656 change_processor_->ApplyChangesFromSyncModel(
658 ProfileSyncServiceTestHelper::MakeSingletonDeletionChangeRecordList(
659 node_id, deleted_specifics));
661 ASSERT_TRUE(notified_of_update_);
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());
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);
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]);
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());
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());
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);
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);
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]);
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());
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());
735 // Check that the DataTypeController associated the models.
737 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
738 ASSERT_TRUE(has_nodes);
740 // A foreign session's tag.
741 std::string tag = "tag1";
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_);
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));
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());
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]));
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));
780 // Verify that deleteForeignSession triggers the
781 // NOTIFICATION_FOREIGN_SESSION_DISABLED notification.
782 ASSERT_TRUE(notified_of_update_);
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());
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]);
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);
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);
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);
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());
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]));
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());
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));
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);
864 // Associate one of the tabs with a non-stale time.
865 model_associator_->AssociateForeignSpecifics(tabs1[0], base::Time::Now());
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());
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]));
881 // Crashes sometimes on Windows, particularly XP.
882 // See http://crbug.com/174951
884 #define MAYBE_ValidTabs DISABLED_ValidTabs
886 #define MAYBE_ValidTabs ValidTabs
887 #endif // defined(OS_WIN)
889 // Test that tabs with nothing but "chrome://*" and "file://*" navigations are
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());
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.
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());
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());
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_);
929 // Nothing should have changed since we don't have unapplied data.
930 ASSERT_FALSE(model_associator_->GetAllForeignSessions(&foreign_sessions));
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"));
940 CreateRootHelper create_root(this);
941 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
942 ASSERT_TRUE(create_root.success());
944 ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
945 ASSERT_TRUE(has_nodes);
947 std::string machine_tag = model_associator_->GetCurrentMachineTag();
948 int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
949 ASSERT_NE(syncer::kInvalidId, sync_id);
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);
956 // Get the tabs for this machine from the node and check that they were
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());
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());
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();
987 error = model_associator_->DisassociateModels();
988 ASSERT_FALSE(error.IsSet());
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);
1003 error = model_associator_->AssociateModels(NULL, NULL);
1004 ASSERT_FALSE(error.IsSet());
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();
1017 error = model_associator_->DisassociateModels();
1018 ASSERT_FALSE(error.IsSet());
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);
1034 error = model_associator_->AssociateModels(NULL, NULL);
1035 ASSERT_FALSE(error.IsSet());
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;
1047 error = model_associator_->DisassociateModels();
1048 ASSERT_FALSE(error.IsSet());
1050 // Create another sync node with neither header nor tab field and a foreign
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,
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);
1065 error = model_associator_->AssociateModels(NULL, NULL);
1066 ASSERT_FALSE(error.IsSet());
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;
1079 error = model_associator_->DisassociateModels();
1080 ASSERT_FALSE(error.IsSet());
1082 // Delete the first sync tab node.
1083 std::string tab_tag = TabNodePool::TabIdToTag(local_tag, 1);
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();
1093 error = model_associator_->AssociateModels(NULL, NULL);
1094 ASSERT_FALSE(error.IsSet());
1096 // Add some more tabs to ensure we don't conflict with the pre-existing tab
1098 AddTab(browser(), GURL("http://baz1"));
1099 AddTab(browser(), GURL("http://baz2"));
1102 TEST_F(ProfileSyncServiceSessionTest, Favicons) {
1103 CreateRootHelper create_root(this);
1104 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1105 ASSERT_TRUE(create_root.success());
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;
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));
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));
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));
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;
1155 error = model_associator_->DisassociateModels();
1156 ASSERT_FALSE(error.IsSet());
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);
1166 // Ensure we associate properly despite the pre-existing node with our local
1168 error = model_associator_->AssociateModels(NULL, NULL);
1169 ASSERT_FALSE(error.IsSet());
1172 TEST_F(ProfileSyncServiceSessionTest, CheckPrerenderedWebContentsSwap) {
1173 AddTab(browser(), GURL("http://foo1"));
1174 NavigateAndCommitActiveTab(GURL("http://foo2"));
1175 CreateRootHelper create_root(this);
1177 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1179 syncer::SyncError error;
1180 // Initial association.
1181 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
1182 ASSERT_FALSE(error.IsSet());
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();
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());
1198 // Swap the WebContents.
1200 browser()->tab_strip_model()->GetIndexOfWebContents(old_web_contents);
1201 browser()->tab_strip_model()->ReplaceWebContentsAt(index, new_web_contents);
1203 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
1204 ASSERT_FALSE(error.IsSet());
1206 NavigateAndCommitActiveTab(GURL("http://bar2"));
1207 EXPECT_TRUE(model_associator_->AssociateWindows(true, &error));
1208 ASSERT_FALSE(error.IsSet());
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());
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());
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());
1228 TEST_F(ProfileSyncServiceSessionTest, TabPoolFreeNodeLimits) {
1229 CreateRootHelper create_root(this);
1230 ASSERT_TRUE(StartSyncService(create_root.callback(), false));
1231 ASSERT_TRUE(create_root.success());
1233 // Allocate TabNodePool::kFreeNodesHighWatermark + 1 nodes and verify that
1234 // freeing the last node reduces the free node pool size to
1235 // kFreeNodesLowWatermark.
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);
1246 // Free all except one node.
1247 int last_sync_id = used_sync_ids.back();
1248 used_sync_ids.pop_back();
1250 for (size_t i = 0; i < used_sync_ids.size(); ++i) {
1251 model_associator_->local_tab_pool_.FreeTabNode(used_sync_ids[i]);
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());
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());
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
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.
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();
1290 tab_node.SetSessionSpecifics(specifics);
1293 error = model_associator_->DisassociateModels();
1294 ASSERT_FALSE(error.IsSet());
1295 error = model_associator_->AssociateModels(NULL, NULL);
1296 ASSERT_FALSE(error.IsSet());
1299 } // namespace browser_sync