• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <map>
6 #include <string>
7 
8 #include "base/memory/scoped_ptr.h"
9 #include "base/memory/scoped_temp_dir.h"
10 #include "base/message_loop.h"
11 #include "base/stl_util-inl.h"
12 #include "base/task.h"
13 #include "chrome/browser/sessions/session_service.h"
14 #include "chrome/browser/sessions/session_service_test_helper.h"
15 #include "chrome/browser/sync/abstract_profile_sync_service_test.h"
16 #include "chrome/browser/sync/engine/syncapi.h"
17 #include "chrome/browser/sync/glue/session_change_processor.h"
18 #include "chrome/browser/sync/glue/session_data_type_controller.h"
19 #include "chrome/browser/sync/glue/session_model_associator.h"
20 #include "chrome/browser/sync/glue/sync_backend_host.h"
21 #include "chrome/browser/sync/profile_sync_factory_mock.h"
22 #include "chrome/browser/sync/profile_sync_test_util.h"
23 #include "chrome/browser/sync/protocol/session_specifics.pb.h"
24 #include "chrome/browser/sync/protocol/sync.pb.h"
25 #include "chrome/browser/sync/syncable/directory_manager.h"
26 #include "chrome/browser/sync/syncable/model_type.h"
27 #include "chrome/browser/sync/syncable/syncable.h"
28 #include "chrome/browser/sync/test_profile_sync_service.h"
29 #include "chrome/common/net/gaia/gaia_constants.h"
30 #include "chrome/test/browser_with_test_window_test.h"
31 #include "chrome/test/file_test_utils.h"
32 #include "chrome/test/profile_mock.h"
33 #include "chrome/test/sync/engine/test_id_factory.h"
34 #include "chrome/test/testing_profile.h"
35 #include "content/common/notification_observer.h"
36 #include "content/common/notification_registrar.h"
37 #include "content/common/notification_service.h"
38 #include "testing/gmock/include/gmock/gmock.h"
39 #include "testing/gtest/include/gtest/gtest.h"
40 
41 using browser_sync::SessionChangeProcessor;
42 using browser_sync::SessionDataTypeController;
43 using browser_sync::SessionModelAssociator;
44 using browser_sync::SyncBackendHost;
45 using sync_api::SyncManager;
46 using testing::_;
47 using testing::Return;
48 using browser_sync::TestIdFactory;
49 
50 namespace browser_sync {
51 
52 class ProfileSyncServiceSessionTest
53     : public BrowserWithTestWindowTest,
54       public NotificationObserver {
55  public:
ProfileSyncServiceSessionTest()56   ProfileSyncServiceSessionTest()
57       : window_bounds_(0, 1, 2, 3),
58         notified_of_update_(false) {}
sync_service()59   ProfileSyncService* sync_service() { return sync_service_.get(); }
60 
ids()61   TestIdFactory* ids() { return sync_service_->id_factory(); }
62 
63  protected:
service()64   SessionService* service() { return helper_.service(); }
65 
SetUp()66   virtual void SetUp() {
67     // BrowserWithTestWindowTest implementation.
68     BrowserWithTestWindowTest::SetUp();
69     profile()->CreateRequestContext();
70     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
71     SessionService* session_service = new SessionService(temp_dir_.path());
72     helper_.set_service(session_service);
73     service()->SetWindowType(window_id_, Browser::TYPE_NORMAL);
74     service()->SetWindowBounds(window_id_, window_bounds_, false);
75     registrar_.Add(this, NotificationType::FOREIGN_SESSION_UPDATED,
76         NotificationService::AllSources());
77   }
78 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)79   void Observe(NotificationType type,
80       const NotificationSource& source,
81       const NotificationDetails& details) {
82     switch (type.value) {
83       case NotificationType::FOREIGN_SESSION_UPDATED:
84         notified_of_update_ = true;
85         break;
86       default:
87         NOTREACHED();
88         break;
89     }
90   }
91 
TearDown()92   virtual void TearDown() {
93     helper_.set_service(NULL);
94     profile()->set_session_service(NULL);
95     sync_service_.reset();
96     {
97       // The request context gets deleted on the I/O thread. To prevent a leak
98       // supply one here.
99       BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
100       profile()->ResetRequestContext();
101     }
102     MessageLoop::current()->RunAllPending();
103   }
104 
StartSyncService(Task * task,bool will_fail_association)105   bool StartSyncService(Task* task, bool will_fail_association) {
106     if (sync_service_.get())
107       return false;
108     sync_service_.reset(new TestProfileSyncService(
109         &factory_, profile(), "test user", false, task));
110     profile()->set_session_service(helper_.service());
111 
112     // Register the session data type.
113     model_associator_ =
114         new SessionModelAssociator(sync_service_.get(),
115                                    true /* setup_for_test */);
116     change_processor_ = new SessionChangeProcessor(
117         sync_service_.get(), model_associator_,
118         true /* setup_for_test */);
119     EXPECT_CALL(factory_, CreateSessionSyncComponents(_, _)).
120         WillOnce(Return(ProfileSyncFactory::SyncComponents(
121             model_associator_, change_processor_)));
122     EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
123         WillOnce(ReturnNewDataTypeManager());
124     sync_service_->RegisterDataTypeController(
125         new SessionDataTypeController(&factory_,
126                                       profile(),
127                                       sync_service_.get()));
128     profile()->GetTokenService()->IssueAuthTokenForTest(
129         GaiaConstants::kSyncService, "token");
130     sync_service_->Initialize();
131     MessageLoop::current()->Run();
132     return true;
133   }
134 
135   // Path used in testing.
136   ScopedTempDir temp_dir_;
137   SessionServiceTestHelper helper_;
138   SessionModelAssociator* model_associator_;
139   SessionChangeProcessor* change_processor_;
140   SessionID window_id_;
141   ProfileSyncFactoryMock factory_;
142   scoped_ptr<TestProfileSyncService> sync_service_;
143   const gfx::Rect window_bounds_;
144   bool notified_of_update_;
145   NotificationRegistrar registrar_;
146 };
147 
148 class CreateRootTask : public Task {
149  public:
CreateRootTask(ProfileSyncServiceSessionTest * test)150   explicit CreateRootTask(ProfileSyncServiceSessionTest* test)
151       : test_(test), success_(false) {
152   }
153 
~CreateRootTask()154   virtual ~CreateRootTask() {}
Run()155   virtual void Run() {
156     success_ = ProfileSyncServiceTestHelper::CreateRoot(
157         syncable::SESSIONS,
158         test_->sync_service()->GetUserShare(),
159         test_->ids());
160   }
161 
success()162   bool success() { return success_; }
163 
164  private:
165   ProfileSyncServiceSessionTest* test_;
166   bool success_;
167 };
168 
169 // Test that we can write this machine's session to a node and retrieve it.
TEST_F(ProfileSyncServiceSessionTest,WriteSessionToNode)170 TEST_F(ProfileSyncServiceSessionTest, WriteSessionToNode) {
171   CreateRootTask task(this);
172   ASSERT_TRUE(StartSyncService(&task, false));
173   ASSERT_TRUE(task.success());
174   ASSERT_EQ(model_associator_->GetSessionService(), helper_.service());
175 
176   // Check that the DataTypeController associated the models.
177   bool has_nodes;
178   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
179   ASSERT_TRUE(has_nodes);
180   std::string machine_tag = model_associator_->GetCurrentMachineTag();
181   int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
182   ASSERT_NE(sync_api::kInvalidId, sync_id);
183 
184   // Check that we can get the correct session specifics back from the node.
185   sync_api::ReadTransaction trans(sync_service_->GetUserShare());
186   sync_api::ReadNode node(&trans);
187   ASSERT_TRUE(node.InitByClientTagLookup(syncable::SESSIONS,
188       machine_tag));
189   const sync_pb::SessionSpecifics& specifics(node.GetSessionSpecifics());
190   ASSERT_EQ(machine_tag, specifics.session_tag());
191   ASSERT_TRUE(specifics.has_header());
192   const sync_pb::SessionHeader& header_s = specifics.header();
193   ASSERT_EQ(0, header_s.window_size());
194 }
195 
196 // Test that we can fill this machine's session, write it to a node,
197 // and then retrieve it.
TEST_F(ProfileSyncServiceSessionTest,WriteFilledSessionToNode)198 TEST_F(ProfileSyncServiceSessionTest, WriteFilledSessionToNode) {
199   CreateRootTask task(this);
200   ASSERT_TRUE(StartSyncService(&task, false));
201   ASSERT_TRUE(task.success());
202 
203   // Check that the DataTypeController associated the models.
204   bool has_nodes;
205   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
206   ASSERT_TRUE(has_nodes);
207   AddTab(browser(), GURL("http://foo/1"));
208   NavigateAndCommitActiveTab(GURL("http://foo/2"));
209   AddTab(browser(), GURL("http://bar/1"));
210   NavigateAndCommitActiveTab(GURL("http://bar/2"));
211 
212   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
213   ASSERT_TRUE(has_nodes);
214   std::string machine_tag = model_associator_->GetCurrentMachineTag();
215   int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
216   ASSERT_NE(sync_api::kInvalidId, sync_id);
217 
218   // Check that this machine's data is not included in the foreign windows.
219   std::vector<const ForeignSession*> foreign_sessions;
220   model_associator_->GetAllForeignSessions(&foreign_sessions);
221   ASSERT_EQ(foreign_sessions.size(), 0U);
222 
223   // Get the tabs for this machine from the node and check that they were
224   // filled.
225   SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_;
226   ASSERT_EQ(2U, tab_map.size());
227   // Tabs are ordered by sessionid in tab_map, so should be able to traverse
228   // the tree based on order of tabs created
229   SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
230   ASSERT_EQ(2, iter->second.tab()->controller().entry_count());
231   ASSERT_EQ(GURL("http://foo/1"), iter->second.tab()->controller().
232           GetEntryAtIndex(0)->virtual_url());
233   ASSERT_EQ(GURL("http://foo/2"), iter->second.tab()->controller().
234           GetEntryAtIndex(1)->virtual_url());
235   iter++;
236   ASSERT_EQ(2, iter->second.tab()->controller().entry_count());
237   ASSERT_EQ(GURL("http://bar/1"), iter->second.tab()->controller().
238       GetEntryAtIndex(0)->virtual_url());
239   ASSERT_EQ(GURL("http://bar/2"), iter->second.tab()->controller().
240       GetEntryAtIndex(1)->virtual_url());
241 }
242 
243 // Test that we fail on a failed model association.
TEST_F(ProfileSyncServiceSessionTest,FailModelAssociation)244 TEST_F(ProfileSyncServiceSessionTest, FailModelAssociation) {
245   ASSERT_TRUE(StartSyncService(NULL, true));
246   ASSERT_TRUE(sync_service_->unrecoverable_error_detected());
247 }
248 
249 // Write a foreign session to a node, and then retrieve it.
TEST_F(ProfileSyncServiceSessionTest,WriteForeignSessionToNode)250 TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) {
251   CreateRootTask task(this);
252   ASSERT_TRUE(StartSyncService(&task, false));
253   ASSERT_TRUE(task.success());
254 
255   // Check that the DataTypeController associated the models.
256   bool has_nodes;
257   ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
258   ASSERT_TRUE(has_nodes);
259 
260   // Fill an instance of session specifics with a foreign session's data.
261   sync_pb::SessionSpecifics meta_specifics;
262   std::string machine_tag = "session_sync123";
263   meta_specifics.set_session_tag(machine_tag);
264   sync_pb::SessionHeader* header_s = meta_specifics.mutable_header();
265   sync_pb::SessionWindow* window_s = header_s->add_window();
266   window_s->add_tab(0);
267   window_s->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
268   window_s->set_selected_tab_index(1);
269 
270   sync_pb::SessionSpecifics tab_specifics;
271   tab_specifics.set_session_tag(machine_tag);
272   sync_pb::SessionTab* tab = tab_specifics.mutable_tab();
273   tab->set_tab_visual_index(13);
274   tab->set_current_navigation_index(3);
275   tab->set_pinned(true);
276   tab->set_extension_app_id("app_id");
277   sync_pb::TabNavigation* navigation = tab->add_navigation();
278   navigation->set_index(12);
279   navigation->set_virtual_url("http://foo/1");
280   navigation->set_referrer("referrer");
281   navigation->set_title("title");
282   navigation->set_page_transition(sync_pb::TabNavigation_PageTransition_TYPED);
283 
284   // Update the server with the session specifics.
285   {
286     model_associator_->AssociateForeignSpecifics(meta_specifics, 0);
287     model_associator_->AssociateForeignSpecifics(tab_specifics, 0);
288   }
289 
290   // Check that the foreign session was associated and retrieve the data.
291   std::vector<const ForeignSession*> foreign_sessions;
292   model_associator_->GetAllForeignSessions(&foreign_sessions);
293   ASSERT_EQ(1U, foreign_sessions.size());
294   ASSERT_EQ(machine_tag, foreign_sessions[0]->foreign_session_tag);
295   ASSERT_EQ(1U,  foreign_sessions[0]->windows.size());
296   ASSERT_EQ(1U, foreign_sessions[0]->windows[0]->tabs.size());
297   ASSERT_EQ(1U, foreign_sessions[0]->windows[0]->tabs[0]->navigations.size());
298   ASSERT_EQ(foreign_sessions[0]->foreign_session_tag, machine_tag);
299   ASSERT_EQ(1, foreign_sessions[0]->windows[0]->selected_tab_index);
300   ASSERT_EQ(1, foreign_sessions[0]->windows[0]->type);
301   ASSERT_EQ(13, foreign_sessions[0]->windows[0]->tabs[0]->tab_visual_index);
302   ASSERT_EQ(3,
303       foreign_sessions[0]->windows[0]->tabs[0]->current_navigation_index);
304   ASSERT_TRUE(foreign_sessions[0]->windows[0]->tabs[0]->pinned);
305   ASSERT_EQ("app_id",
306       foreign_sessions[0]->windows[0]->tabs[0]->extension_app_id);
307   ASSERT_EQ(12,
308       foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].index());
309   ASSERT_EQ(GURL("referrer"),
310       foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].referrer());
311   ASSERT_EQ(string16(ASCIIToUTF16("title")),
312       foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].title());
313   ASSERT_EQ(PageTransition::TYPED,
314       foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].transition());
315   ASSERT_EQ(GURL("http://foo/1"),
316       foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].virtual_url());
317 }
318 
319 // Test the DataTypeController on update.
TEST_F(ProfileSyncServiceSessionTest,UpdatedSyncNodeActionUpdate)320 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionUpdate) {
321   CreateRootTask task(this);
322   ASSERT_TRUE(StartSyncService(&task, false));
323   ASSERT_TRUE(task.success());
324   int64 node_id = model_associator_->GetSyncIdFromSessionTag(
325       model_associator_->GetCurrentMachineTag());
326   scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord);
327   record->action = SyncManager::ChangeRecord::ACTION_UPDATE;
328   record->id = node_id;
329   ASSERT_FALSE(notified_of_update_);
330   {
331     sync_api::WriteTransaction trans(sync_service_->GetUserShare());
332     change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1);
333   }
334   ASSERT_TRUE(notified_of_update_);
335 }
336 
337 // Test the DataTypeController on add.
TEST_F(ProfileSyncServiceSessionTest,UpdatedSyncNodeActionAdd)338 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionAdd) {
339   CreateRootTask task(this);
340   ASSERT_TRUE(StartSyncService(&task, false));
341   ASSERT_TRUE(task.success());
342 
343   int64 node_id = model_associator_->GetSyncIdFromSessionTag(
344       model_associator_->GetCurrentMachineTag());
345   scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord);
346   record->action = SyncManager::ChangeRecord::ACTION_ADD;
347   record->id = node_id;
348   ASSERT_FALSE(notified_of_update_);
349   {
350     sync_api::WriteTransaction trans(sync_service_->GetUserShare());
351     change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1);
352   }
353   ASSERT_TRUE(notified_of_update_);
354 }
355 
356 // Test the DataTypeController on delete.
TEST_F(ProfileSyncServiceSessionTest,UpdatedSyncNodeActionDelete)357 TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionDelete) {
358   CreateRootTask task(this);
359   ASSERT_TRUE(StartSyncService(&task, false));
360   ASSERT_TRUE(task.success());
361 
362   int64 node_id = model_associator_->GetSyncIdFromSessionTag(
363       model_associator_->GetCurrentMachineTag());
364   scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord);
365   record->action = SyncManager::ChangeRecord::ACTION_DELETE;
366   record->id = node_id;
367   ASSERT_FALSE(notified_of_update_);
368   {
369     sync_api::WriteTransaction trans(sync_service_->GetUserShare());
370     change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1);
371   }
372   ASSERT_TRUE(notified_of_update_);
373 }
374 // Test the TabNodePool when it starts off empty.
TEST_F(ProfileSyncServiceSessionTest,TabNodePoolEmpty)375 TEST_F(ProfileSyncServiceSessionTest, TabNodePoolEmpty) {
376   CreateRootTask task(this);
377   ASSERT_TRUE(StartSyncService(&task, false));
378   ASSERT_TRUE(task.success());
379 
380   std::vector<int64> node_ids;
381   ASSERT_EQ(0U, model_associator_->tab_pool_.capacity());
382   ASSERT_TRUE(model_associator_->tab_pool_.empty());
383   ASSERT_TRUE(model_associator_->tab_pool_.full());
384   const size_t num_ids = 10;
385   for (size_t i = 0; i < num_ids; ++i) {
386     int64 id = model_associator_->tab_pool_.GetFreeTabNode();
387     ASSERT_GT(id, -1);
388     node_ids.push_back(id);
389   }
390   ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
391   ASSERT_TRUE(model_associator_->tab_pool_.empty());
392   ASSERT_FALSE(model_associator_->tab_pool_.full());
393   for (size_t i = 0; i < num_ids; ++i) {
394     model_associator_->tab_pool_.FreeTabNode(node_ids[i]);
395   }
396   ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
397   ASSERT_FALSE(model_associator_->tab_pool_.empty());
398   ASSERT_TRUE(model_associator_->tab_pool_.full());
399 }
400 
401 // Test the TabNodePool when it starts off with nodes
TEST_F(ProfileSyncServiceSessionTest,TabNodePoolNonEmpty)402 TEST_F(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty) {
403   CreateRootTask task(this);
404   ASSERT_TRUE(StartSyncService(&task, false));
405   ASSERT_TRUE(task.success());
406 
407   const size_t num_starting_nodes = 3;
408   for (size_t i = 0; i < num_starting_nodes; ++i) {
409     model_associator_->tab_pool_.AddTabNode(i);
410   }
411 
412   std::vector<int64> node_ids;
413   ASSERT_EQ(num_starting_nodes, model_associator_->tab_pool_.capacity());
414   ASSERT_FALSE(model_associator_->tab_pool_.empty());
415   ASSERT_TRUE(model_associator_->tab_pool_.full());
416   const size_t num_ids = 10;
417   for (size_t i = 0; i < num_ids; ++i) {
418     int64 id = model_associator_->tab_pool_.GetFreeTabNode();
419     ASSERT_GT(id, -1);
420     node_ids.push_back(id);
421   }
422   ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
423   ASSERT_TRUE(model_associator_->tab_pool_.empty());
424   ASSERT_FALSE(model_associator_->tab_pool_.full());
425   for (size_t i = 0; i < num_ids; ++i) {
426     model_associator_->tab_pool_.FreeTabNode(node_ids[i]);
427   }
428   ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
429   ASSERT_FALSE(model_associator_->tab_pool_.empty());
430   ASSERT_TRUE(model_associator_->tab_pool_.full());
431 }
432 
433 }  // namespace browser_sync
434