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