Home | History | Annotate | Download | only in sessions
      1 // Copyright 2013 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 "chrome/browser/extensions/api/sessions/sessions_api.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/path_service.h"
      9 #include "base/strings/stringprintf.h"
     10 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
     11 #include "chrome/browser/extensions/extension_apitest.h"
     12 #include "chrome/browser/extensions/extension_function_test_utils.h"
     13 #include "chrome/browser/profiles/profile_manager.h"
     14 #include "chrome/browser/sync/open_tabs_ui_delegate.h"
     15 #include "chrome/browser/sync/profile_sync_service.h"
     16 #include "chrome/browser/sync/profile_sync_service_factory.h"
     17 #include "chrome/browser/sync/profile_sync_service_mock.h"
     18 #include "chrome/common/chrome_paths.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "chrome/test/base/in_process_browser_test.h"
     21 #include "chrome/test/base/test_switches.h"
     22 #include "chrome/test/base/testing_browser_process.h"
     23 #include "sync/api/attachments/attachment_id.h"
     24 #include "sync/api/attachments/attachment_service_proxy_for_test.h"
     25 #include "sync/api/fake_sync_change_processor.h"
     26 #include "sync/api/sync_error_factory_mock.h"
     27 
     28 #if defined(OS_CHROMEOS)
     29 #include "chromeos/chromeos_switches.h"
     30 #endif
     31 
     32 namespace utils = extension_function_test_utils;
     33 
     34 namespace extensions {
     35 
     36 namespace {
     37 
     38 // If more sessions are added to session tags, num sessions should be updated.
     39 const char* kSessionTags[] = {"tag0", "tag1", "tag2", "tag3", "tag4"};
     40 const size_t kNumSessions = 5;
     41 
     42 void BuildSessionSpecifics(const std::string& tag,
     43                            sync_pb::SessionSpecifics* meta) {
     44   meta->set_session_tag(tag);
     45   sync_pb::SessionHeader* header = meta->mutable_header();
     46   header->set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_LINUX);
     47   header->set_client_name(tag);
     48 }
     49 
     50 void BuildWindowSpecifics(int window_id,
     51                           const std::vector<int>& tab_list,
     52                           sync_pb::SessionSpecifics* meta) {
     53   sync_pb::SessionHeader* header = meta->mutable_header();
     54   sync_pb::SessionWindow* window = header->add_window();
     55   window->set_window_id(window_id);
     56   window->set_selected_tab_index(0);
     57   window->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
     58   for (std::vector<int>::const_iterator iter = tab_list.begin();
     59        iter != tab_list.end(); ++iter) {
     60     window->add_tab(*iter);
     61   }
     62 }
     63 
     64 void BuildTabSpecifics(const std::string& tag, int window_id, int tab_id,
     65                        sync_pb::SessionSpecifics* tab_base) {
     66   tab_base->set_session_tag(tag);
     67   tab_base->set_tab_node_id(0);
     68   sync_pb::SessionTab* tab = tab_base->mutable_tab();
     69   tab->set_tab_id(tab_id);
     70   tab->set_tab_visual_index(1);
     71   tab->set_current_navigation_index(0);
     72   tab->set_pinned(true);
     73   tab->set_extension_app_id("app_id");
     74   sync_pb::TabNavigation* navigation = tab->add_navigation();
     75   navigation->set_virtual_url("http://foo/1");
     76   navigation->set_referrer("referrer");
     77   navigation->set_title("title");
     78   navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
     79 }
     80 
     81 } // namespace
     82 
     83 class ExtensionSessionsTest : public InProcessBrowserTest {
     84  public:
     85   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
     86   virtual void SetUpOnMainThread() OVERRIDE;
     87  protected:
     88   void CreateTestProfileSyncService();
     89   void CreateTestExtension();
     90   void CreateSessionModels();
     91 
     92   template <class T>
     93   scoped_refptr<T> CreateFunction(bool has_callback) {
     94     scoped_refptr<T> fn(new T());
     95     fn->set_extension(extension_.get());
     96     fn->set_has_callback(has_callback);
     97     return fn;
     98   };
     99 
    100   Browser* browser_;
    101   scoped_refptr<extensions::Extension> extension_;
    102 };
    103 
    104 void ExtensionSessionsTest::SetUpCommandLine(CommandLine* command_line) {
    105 #if defined(OS_CHROMEOS)
    106   command_line->AppendSwitch(
    107       chromeos::switches::kIgnoreUserProfileMappingForTests);
    108 #endif
    109 }
    110 
    111 void ExtensionSessionsTest::SetUpOnMainThread() {
    112   CreateTestProfileSyncService();
    113   CreateTestExtension();
    114 }
    115 
    116 void ExtensionSessionsTest::CreateTestProfileSyncService() {
    117   ProfileManager* profile_manager = g_browser_process->profile_manager();
    118   base::FilePath path;
    119   PathService::Get(chrome::DIR_USER_DATA, &path);
    120   path = path.AppendASCII("test_profile");
    121   if (!base::PathExists(path))
    122     CHECK(base::CreateDirectory(path));
    123   Profile* profile =
    124       Profile::CreateProfile(path, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
    125   profile_manager->RegisterTestingProfile(profile, true, false);
    126   ProfileSyncServiceMock* service = static_cast<ProfileSyncServiceMock*>(
    127       ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
    128       profile, &ProfileSyncServiceMock::BuildMockProfileSyncService));
    129   browser_ = new Browser(Browser::CreateParams(
    130       profile, chrome::HOST_DESKTOP_TYPE_NATIVE));
    131 
    132   syncer::ModelTypeSet preferred_types;
    133   preferred_types.Put(syncer::SESSIONS);
    134   GoogleServiceAuthError no_error(GoogleServiceAuthError::NONE);
    135   ON_CALL(*service, IsSessionsDataTypeControllerRunning())
    136       .WillByDefault(testing::Return(true));
    137   ON_CALL(*service, GetRegisteredDataTypes())
    138       .WillByDefault(testing::Return(syncer::UserTypes()));
    139   ON_CALL(*service, GetPreferredDataTypes()).WillByDefault(
    140       testing::Return(preferred_types));
    141   EXPECT_CALL(*service, GetAuthError()).WillRepeatedly(
    142       testing::ReturnRef(no_error));
    143   ON_CALL(*service, GetActiveDataTypes()).WillByDefault(
    144       testing::Return(preferred_types));
    145   ON_CALL(*service, GetLocalDeviceInfoMock()).WillByDefault(
    146       testing::Return(new browser_sync::DeviceInfo(
    147           std::string(kSessionTags[0]),
    148           "machine name",
    149           "Chromium 10k",
    150           "Chrome 10k",
    151           sync_pb::SyncEnums_DeviceType_TYPE_LINUX)));
    152   ON_CALL(*service, GetLocalSyncCacheGUID()).WillByDefault(
    153       testing::Return(std::string(kSessionTags[0])));
    154   EXPECT_CALL(*service, AddObserver(testing::_)).Times(testing::AnyNumber());
    155   EXPECT_CALL(*service, RemoveObserver(testing::_)).Times(testing::AnyNumber());
    156 
    157   service->Initialize();
    158 }
    159 
    160 void ExtensionSessionsTest::CreateTestExtension() {
    161   scoped_ptr<base::DictionaryValue> test_extension_value(
    162       utils::ParseDictionary(
    163       "{\"name\": \"Test\", \"version\": \"1.0\", "
    164       "\"permissions\": [\"sessions\", \"tabs\"]}"));
    165   extension_ = utils::CreateExtension(test_extension_value.get());
    166 }
    167 
    168 void ExtensionSessionsTest::CreateSessionModels() {
    169   syncer::SyncDataList initial_data;
    170   for (size_t index = 0; index < kNumSessions; ++index) {
    171     // Fill an instance of session specifics with a foreign session's data.
    172     sync_pb::SessionSpecifics meta;
    173     BuildSessionSpecifics(kSessionTags[index], &meta);
    174     SessionID::id_type tab_nums1[] = {5, 10, 13, 17};
    175     std::vector<SessionID::id_type> tab_list1(
    176         tab_nums1, tab_nums1 + arraysize(tab_nums1));
    177     BuildWindowSpecifics(index, tab_list1, &meta);
    178     std::vector<sync_pb::SessionSpecifics> tabs1;
    179     tabs1.resize(tab_list1.size());
    180     for (size_t i = 0; i < tab_list1.size(); ++i) {
    181       BuildTabSpecifics(kSessionTags[index], 0, tab_list1[i], &tabs1[i]);
    182     }
    183 
    184     sync_pb::EntitySpecifics entity;
    185     entity.mutable_session()->CopyFrom(meta);
    186     initial_data.push_back(syncer::SyncData::CreateRemoteData(
    187         1,
    188         entity,
    189         base::Time(),
    190         syncer::AttachmentIdList(),
    191         syncer::AttachmentServiceProxyForTest::Create()));
    192     for (size_t i = 0; i < tabs1.size(); i++) {
    193       sync_pb::EntitySpecifics entity;
    194       entity.mutable_session()->CopyFrom(tabs1[i]);
    195       initial_data.push_back(syncer::SyncData::CreateRemoteData(
    196           i + 2,
    197           entity,
    198           base::Time(),
    199           syncer::AttachmentIdList(),
    200           syncer::AttachmentServiceProxyForTest::Create()));
    201     }
    202   }
    203 
    204   ProfileSyncServiceFactory::GetForProfile(browser_->profile())->
    205       GetSessionsSyncableService()->
    206           MergeDataAndStartSyncing(syncer::SESSIONS, initial_data,
    207       scoped_ptr<syncer::SyncChangeProcessor>(
    208           new syncer::FakeSyncChangeProcessor()),
    209       scoped_ptr<syncer::SyncErrorFactory>(
    210           new syncer::SyncErrorFactoryMock()));
    211 }
    212 
    213 IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, GetDevices) {
    214   CreateSessionModels();
    215 
    216   scoped_ptr<base::ListValue> result(utils::ToList(
    217       utils::RunFunctionAndReturnSingleResult(
    218           CreateFunction<SessionsGetDevicesFunction>(true).get(),
    219           "[{\"maxResults\": 0}]",
    220           browser_)));
    221   ASSERT_TRUE(result);
    222   base::ListValue* devices = result.get();
    223   EXPECT_EQ(5u, devices->GetSize());
    224   base::DictionaryValue* device = NULL;
    225   base::ListValue* sessions = NULL;
    226   for (size_t i = 0; i < devices->GetSize(); ++i) {
    227     EXPECT_TRUE(devices->GetDictionary(i, &device));
    228     EXPECT_EQ(kSessionTags[i], utils::GetString(device, "info"));
    229     EXPECT_EQ(kSessionTags[i], utils::GetString(device, "deviceName"));
    230     EXPECT_TRUE(device->GetList("sessions", &sessions));
    231     EXPECT_EQ(0u, sessions->GetSize());
    232   }
    233 }
    234 
    235 IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, GetDevicesMaxResults) {
    236   CreateSessionModels();
    237 
    238   scoped_ptr<base::ListValue> result(utils::ToList(
    239       utils::RunFunctionAndReturnSingleResult(
    240           CreateFunction<SessionsGetDevicesFunction>(true).get(),
    241           "[]",
    242           browser_)));
    243   ASSERT_TRUE(result);
    244   base::ListValue* devices = result.get();
    245   EXPECT_EQ(5u, devices->GetSize());
    246   base::DictionaryValue* device = NULL;
    247   base::ListValue* sessions = NULL;
    248   for (size_t i = 0; i < devices->GetSize(); ++i) {
    249     EXPECT_TRUE(devices->GetDictionary(i, &device));
    250     EXPECT_EQ(kSessionTags[i], utils::GetString(device, "info"));
    251     EXPECT_EQ(kSessionTags[i], utils::GetString(device, "deviceName"));
    252     EXPECT_TRUE(device->GetList("sessions", &sessions));
    253     EXPECT_EQ(1u, sessions->GetSize());
    254   }
    255 }
    256 
    257 IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, GetDevicesListEmpty) {
    258   scoped_ptr<base::ListValue> result(utils::ToList(
    259       utils::RunFunctionAndReturnSingleResult(
    260           CreateFunction<SessionsGetDevicesFunction>(true).get(),
    261           "[]",
    262           browser_)));
    263 
    264   ASSERT_TRUE(result);
    265   base::ListValue* devices = result.get();
    266   EXPECT_EQ(0u, devices->GetSize());
    267 }
    268 
    269 // Flaky timeout: http://crbug.com/278372
    270 IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest,
    271                        DISABLED_RestoreForeignSessionWindow) {
    272   CreateSessionModels();
    273 
    274   scoped_ptr<base::DictionaryValue> restored_window_session(utils::ToDictionary(
    275       utils::RunFunctionAndReturnSingleResult(
    276           CreateFunction<SessionsRestoreFunction>(true).get(),
    277           "[\"tag3.3\"]",
    278           browser_,
    279           utils::INCLUDE_INCOGNITO)));
    280   ASSERT_TRUE(restored_window_session);
    281 
    282   scoped_ptr<base::ListValue> result(utils::ToList(
    283       utils::RunFunctionAndReturnSingleResult(
    284           CreateFunction<WindowsGetAllFunction>(true).get(),
    285           "[]",
    286           browser_)));
    287   ASSERT_TRUE(result);
    288 
    289   base::ListValue* windows = result.get();
    290   EXPECT_EQ(2u, windows->GetSize());
    291   base::DictionaryValue* restored_window = NULL;
    292   EXPECT_TRUE(restored_window_session->GetDictionary("window",
    293                                                      &restored_window));
    294   base::DictionaryValue* window = NULL;
    295   int restored_id = utils::GetInteger(restored_window, "id");
    296   for (size_t i = 0; i < windows->GetSize(); ++i) {
    297     EXPECT_TRUE(windows->GetDictionary(i, &window));
    298     if (utils::GetInteger(window, "id") == restored_id)
    299       break;
    300   }
    301   EXPECT_EQ(restored_id, utils::GetInteger(window, "id"));
    302 }
    303 
    304 IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, RestoreForeignSessionInvalidId) {
    305   CreateSessionModels();
    306 
    307   EXPECT_TRUE(MatchPattern(utils::RunFunctionAndReturnError(
    308       CreateFunction<SessionsRestoreFunction>(true).get(),
    309       "[\"tag3.0\"]",
    310       browser_), "Invalid session id: \"tag3.0\"."));
    311 }
    312 
    313 IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, RestoreInIncognito) {
    314   CreateSessionModels();
    315 
    316   EXPECT_TRUE(MatchPattern(utils::RunFunctionAndReturnError(
    317       CreateFunction<SessionsRestoreFunction>(true).get(),
    318       "[\"1\"]",
    319       CreateIncognitoBrowser()),
    320       "Can not restore sessions in incognito mode."));
    321 }
    322 
    323 IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, GetRecentlyClosedIncognito) {
    324   scoped_ptr<base::ListValue> result(utils::ToList(
    325       utils::RunFunctionAndReturnSingleResult(
    326           CreateFunction<SessionsGetRecentlyClosedFunction>(true).get(),
    327           "[]",
    328           CreateIncognitoBrowser())));
    329   ASSERT_TRUE(result);
    330   base::ListValue* sessions = result.get();
    331   EXPECT_EQ(0u, sessions->GetSize());
    332 }
    333 
    334 // Flaky on ChromeOS, times out on OSX Debug http://crbug.com/251199
    335 #if defined(OS_CHROMEOS) || (defined(OS_MACOSX) && !defined(NDEBUG))
    336 #define MAYBE_SessionsApis DISABLED_SessionsApis
    337 #else
    338 #define MAYBE_SessionsApis SessionsApis
    339 #endif
    340 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_SessionsApis) {
    341 #if defined(OS_WIN) && defined(USE_ASH)
    342   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    343   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    344     return;
    345 #endif
    346 
    347   ASSERT_TRUE(RunExtensionSubtest("sessions",
    348                                   "sessions.html")) << message_;
    349 }
    350 
    351 }  // namespace extensions
    352