| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <cstdint> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind.h" |
| #include "base/timer/timer.h" |
| #include "chromeos/ui/base/app_types.h" |
| #include "chromeos/ui/base/window_properties.h" |
| #include "components/app_constants/constants.h" |
| #include "components/app_restore/app_launch_info.h" |
| #include "components/app_restore/app_restore_data.h" |
| #include "components/app_restore/full_restore_read_handler.h" |
| #include "components/app_restore/full_restore_save_handler.h" |
| #include "components/app_restore/full_restore_utils.h" |
| #include "components/app_restore/restore_data.h" |
| #include "components/app_restore/window_info.h" |
| #include "components/app_restore/window_properties.h" |
| #include "components/services/app_service/public/cpp/app_launch_util.h" |
| #include "components/services/app_service/public/cpp/intent.h" |
| #include "components/services/app_service/public/cpp/intent_util.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/test/aura_test_helper.h" |
| #include "ui/aura/test/test_windows.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/window_open_disposition.h" |
| #include "ui/display/types/display_constants.h" |
| #include "ui/views/test/test_views_delegate.h" |
| #include "url/gurl.h" |
| |
| namespace full_restore { |
| |
| namespace { |
| |
| using testing::ElementsAre; |
| using testing::Pair; |
| |
| constexpr char kAppId[] = "aaa"; |
| |
| constexpr int32_t kId1 = 100; |
| constexpr int32_t kId2 = 200; |
| constexpr int32_t kId3 = 300; |
| |
| constexpr int32_t kActivationIndex1 = 100; |
| constexpr int32_t kActivationIndex2 = 101; |
| |
| constexpr int32_t kArcSessionId1 = 1; |
| constexpr int32_t kArcSessionId2 = |
| app_restore::kArcSessionIdOffsetForRestoredLaunching + 1; |
| |
| constexpr int32_t kArcTaskId1 = 666; |
| constexpr int32_t kArcTaskId2 = 888; |
| |
| constexpr char kFilePath1[] = "path1"; |
| constexpr char kFilePath2[] = "path2"; |
| |
| constexpr char kHandlerId[] = "audio"; |
| |
| constexpr char kExampleUrl1[] = "https://www.example1.com"; |
| constexpr char kExampleUrl2[] = "https://www.example2.com"; |
| |
| // Randomly generated desk GUID to test saving removing desk GUID. |
| const base::Uuid kRemovingDeskGuid = base::Uuid::GenerateRandomV4(); |
| const base::Uuid kNonRemovingDeskGuid = base::Uuid::GenerateRandomV4(); |
| |
| } // namespace |
| |
| class FullRestoreReadHandlerTestApi { |
| public: |
| explicit FullRestoreReadHandlerTestApi(FullRestoreReadHandler* read_handler) |
| : read_handler_(read_handler) {} |
| |
| FullRestoreReadHandlerTestApi(const FullRestoreReadHandlerTestApi&) = delete; |
| FullRestoreReadHandlerTestApi& operator=( |
| const FullRestoreReadHandlerTestApi&) = delete; |
| ~FullRestoreReadHandlerTestApi() = default; |
| |
| const app_restore::ArcReadHandler* GetArcReadHander() const { |
| DCHECK(read_handler_); |
| return read_handler_->arc_read_handler_.get(); |
| } |
| |
| const std::map<int32_t, std::string>& GetArcWindowIdMap() const { |
| const auto* arc_read_handler = GetArcReadHander(); |
| DCHECK(arc_read_handler); |
| return arc_read_handler->window_id_to_app_id_; |
| } |
| |
| const std::map<int32_t, int32_t>& GetArcSessionIdMap() const { |
| const auto* arc_read_handler = GetArcReadHander(); |
| DCHECK(arc_read_handler); |
| return arc_read_handler->session_id_to_window_id_; |
| } |
| |
| const std::map<int32_t, int32_t>& GetArcTaskIdMap() const { |
| const auto* arc_read_handler = GetArcReadHander(); |
| DCHECK(arc_read_handler); |
| return arc_read_handler->task_id_to_window_id_; |
| } |
| |
| void ClearRestoreData() { |
| read_handler_->profile_path_to_restore_data_.clear(); |
| } |
| |
| private: |
| raw_ptr<FullRestoreReadHandler> read_handler_; |
| }; |
| |
| class FullRestoreSaveHandlerTestApi { |
| public: |
| explicit FullRestoreSaveHandlerTestApi(FullRestoreSaveHandler* save_handler) |
| : save_handler_(save_handler) {} |
| |
| FullRestoreSaveHandlerTestApi(const FullRestoreSaveHandlerTestApi&) = delete; |
| FullRestoreSaveHandlerTestApi& operator=( |
| const FullRestoreSaveHandlerTestApi&) = delete; |
| ~FullRestoreSaveHandlerTestApi() = default; |
| |
| const ArcSaveHandler* GetArcSaveHander() const { |
| DCHECK(save_handler_); |
| return save_handler_->arc_save_handler_.get(); |
| } |
| |
| const ArcSaveHandler::SessionIdMap& GetArcSessionIdMap() const { |
| const auto* arc_save_handler = GetArcSaveHander(); |
| DCHECK(arc_save_handler); |
| return arc_save_handler->session_id_to_app_launch_info_; |
| } |
| |
| const std::map<int32_t, std::string>& GetArcTaskIdMap() const { |
| const auto* arc_save_handler = GetArcSaveHander(); |
| DCHECK(arc_save_handler); |
| return arc_save_handler->task_id_to_app_id_; |
| } |
| |
| void ModifyLaunchTime(int32_t session_id) { |
| auto& session_id_to_app_launch_info = |
| arc_save_handler()->session_id_to_app_launch_info_; |
| auto it = session_id_to_app_launch_info.find(session_id); |
| if (it == session_id_to_app_launch_info.end()) |
| return; |
| |
| // If there is no task created for the session id in 600 seconds, the |
| // session id record is removed. So set the record time as 601 seconds ago, |
| // so that CheckTasksForAppLaunching can remove the session id record to |
| // simulate the task is not created for the session id. |
| it->second.second = it->second.second - base::Seconds(601); |
| } |
| |
| base::RepeatingTimer* GetArcCheckTimer() { |
| return &arc_save_handler()->check_timer_; |
| } |
| |
| void CheckArcTasks() { arc_save_handler()->CheckTasksForAppLaunching(); } |
| |
| void ClearRestoreData() { |
| save_handler_->profile_path_to_restore_data_.clear(); |
| } |
| |
| private: |
| ArcSaveHandler* arc_save_handler() { |
| DCHECK(save_handler_); |
| DCHECK(save_handler_->arc_save_handler_.get()); |
| return save_handler_->arc_save_handler_.get(); |
| } |
| |
| raw_ptr<FullRestoreSaveHandler> save_handler_; |
| }; |
| |
| // Unit tests for restore data. |
| class FullRestoreReadAndSaveTest : public testing::Test { |
| public: |
| FullRestoreReadAndSaveTest() = default; |
| ~FullRestoreReadAndSaveTest() override = default; |
| |
| FullRestoreReadAndSaveTest(const FullRestoreReadAndSaveTest&) = delete; |
| FullRestoreReadAndSaveTest& operator=(const FullRestoreReadAndSaveTest&) = |
| delete; |
| |
| void SetUp() override { |
| ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir()); |
| |
| aura_test_helper_.SetUp(); |
| } |
| |
| void TearDown() override { |
| FullRestoreSaveHandler::GetInstance()->ClearForTesting(); |
| } |
| |
| const base::FilePath& GetPath() { return tmp_dir_.GetPath(); } |
| |
| void ReadFromFile(const base::FilePath& file_path, bool clear_data = true) { |
| FullRestoreReadHandler* read_handler = |
| FullRestoreReadHandler::GetInstance(); |
| if (clear_data) |
| FullRestoreReadHandlerTestApi(read_handler).ClearRestoreData(); |
| |
| base::RunLoop run_loop; |
| |
| read_handler->ReadFromFile( |
| file_path, |
| base::BindLambdaForTesting( |
| [&](std::unique_ptr<app_restore::RestoreData> restore_data) { |
| run_loop.Quit(); |
| restore_data_ = std::move(restore_data); |
| })); |
| run_loop.Run(); |
| } |
| |
| FullRestoreSaveHandler* GetSaveHandler(bool start_save_timer = true) { |
| auto* save_handler = FullRestoreSaveHandler::GetInstance(); |
| save_handler->SetActiveProfilePath(GetPath()); |
| save_handler->AllowSave(); |
| return save_handler; |
| } |
| |
| const app_restore::RestoreData* GetRestoreData( |
| const base::FilePath& file_path) { |
| return restore_data_.get(); |
| } |
| |
| void AddAppLaunchInfo(const base::FilePath& file_path, int32_t id) { |
| SaveAppLaunchInfo(file_path, |
| std::make_unique<app_restore::AppLaunchInfo>(kAppId, id)); |
| } |
| |
| void AddArcAppLaunchInfo(const base::FilePath& file_path) { |
| SaveAppLaunchInfo(file_path, std::make_unique<app_restore::AppLaunchInfo>( |
| kAppId, /*event_flags=*/0, kArcSessionId1, |
| /*display_id*/ 0)); |
| } |
| |
| void AddBrowserLaunchInfo(const base::FilePath& file_path, |
| int32_t id, |
| const std::vector<GURL>& urls, |
| int32_t active_tab_index = 0) { |
| auto launch_info = std::make_unique<app_restore::AppLaunchInfo>( |
| app_constants::kChromeAppId, id); |
| launch_info->browser_extra_info.urls = urls; |
| launch_info->browser_extra_info.active_tab_index = active_tab_index; |
| SaveAppLaunchInfo(file_path, std::move(launch_info)); |
| } |
| |
| void AddChromeAppLaunchInfo(const base::FilePath& file_path) { |
| auto app_launch_info = std::make_unique<app_restore::AppLaunchInfo>( |
| kAppId, kHandlerId, |
| std::vector<base::FilePath>{base::FilePath(kFilePath1), |
| base::FilePath(kFilePath2)}); |
| app_launch_info->window_id = kId1; |
| SaveAppLaunchInfo(file_path, std::move(app_launch_info)); |
| } |
| |
| void SaveWindowInfo(aura::Window* window, int32_t activation_index) { |
| app_restore::WindowInfo window_info; |
| window_info.window = window; |
| window_info.activation_index = activation_index; |
| full_restore::SaveWindowInfo(window_info); |
| } |
| |
| std::unique_ptr<aura::Window> CreateWindowInfo( |
| int32_t id, |
| int32_t index, |
| chromeos::AppType app_type = chromeos::AppType::BROWSER, |
| base::Uuid desk_guid = base::Uuid()) { |
| std::unique_ptr<aura::Window> window = |
| aura::test::CreateTestWindow({.bounds = {100, 100}, .window_id = id}); |
| window->SetProperty(chromeos::kAppTypeKey, app_type); |
| window->SetProperty(app_restore::kWindowIdKey, id); |
| app_restore::WindowInfo window_info; |
| window_info.window = window.get(); |
| window_info.activation_index = index; |
| window_info.desk_guid = desk_guid; |
| full_restore::SaveWindowInfo(window_info); |
| return window; |
| } |
| |
| std::unique_ptr<app_restore::WindowInfo> GetArcWindowInfo( |
| int32_t restore_window_id) { |
| std::unique_ptr<aura::Window> window = aura::test::CreateTestWindow( |
| {.bounds = {100, 100}, .window_id = restore_window_id}); |
| window->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::ARC_APP); |
| window->SetProperty(app_restore::kRestoreWindowIdKey, restore_window_id); |
| return FullRestoreReadHandler::GetInstance()->GetWindowInfo(window.get()); |
| } |
| |
| void VerifyRestoreData(const base::FilePath& file_path, |
| int32_t id, |
| int32_t index) { |
| ReadFromFile(file_path); |
| |
| const auto* restore_data = GetRestoreData(file_path); |
| ASSERT_TRUE(restore_data != nullptr); |
| |
| const auto& launch_list = restore_data->app_id_to_launch_list(); |
| EXPECT_EQ(1u, launch_list.size()); |
| |
| // Verify for |kAppId|. |
| const auto launch_list_it = launch_list.find(kAppId); |
| EXPECT_TRUE(launch_list_it != launch_list.end()); |
| EXPECT_EQ(1u, launch_list_it->second.size()); |
| |
| // Verify for |id|. |
| const auto app_restore_data_it = launch_list_it->second.find(id); |
| EXPECT_TRUE(app_restore_data_it != launch_list_it->second.end()); |
| |
| const auto& data = app_restore_data_it->second; |
| EXPECT_THAT(data->window_info.activation_index, testing::Optional(index)); |
| } |
| |
| content::BrowserTaskEnvironment& task_environment() { |
| return task_environment_; |
| } |
| |
| private: |
| content::BrowserTaskEnvironment task_environment_; |
| |
| base::ScopedTempDir tmp_dir_; |
| |
| std::unique_ptr<app_restore::RestoreData> restore_data_; |
| |
| views::TestViewsDelegate test_views_delegate_; |
| |
| aura::test::AuraTestHelper aura_test_helper_; |
| }; |
| |
| TEST_F(FullRestoreReadAndSaveTest, ReadEmptyRestoreData) { |
| ReadFromFile(GetPath()); |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| ASSERT_TRUE(restore_data->app_id_to_launch_list().empty()); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, StopSavingWhenShutdown) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add app launch info, and verify the timer starts. |
| AddAppLaunchInfo(GetPath(), kId1); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| // Simulate timeout. |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Add one more app launch info, and verify the timer is running. |
| AddAppLaunchInfo(GetPath(), kId2); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| // Simulate shutdown. |
| save_handler->SetShutDown(); |
| |
| // Simulate timeout. |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| FullRestoreReadHandler* read_handler = FullRestoreReadHandler::GetInstance(); |
| FullRestoreReadHandlerTestApi(read_handler).ClearRestoreData(); |
| |
| // Add one more app launch info, to simulate a window is created during the |
| // system startup phase. |
| AddAppLaunchInfo(GetPath(), kId3); |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| ReadFromFile(GetPath(), /*clear_data=*/false); |
| |
| // Verify the restore data can be read correctly. |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| |
| const auto& launch_list = restore_data->app_id_to_launch_list(); |
| // Verify the restore data for `kAppId` exists, and that it contains data for |
| // `kId1` but none for `kId2` and `kId3`. |
| EXPECT_THAT(launch_list, |
| ElementsAre(Pair(kAppId, ElementsAre(Pair(kId1, testing::_))))); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, StartSaveTimer) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add app launch info, and verify the timer starts. |
| AddAppLaunchInfo(GetPath(), kId1); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| // Simulate timeout. |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Simulate the system reboots. |
| FullRestoreReadHandler* read_handler = FullRestoreReadHandler::GetInstance(); |
| FullRestoreReadHandlerTestApi(read_handler).ClearRestoreData(); |
| save_handler->ClearForTesting(); |
| |
| // Add one more app launch info, to simulate an app is launched during the |
| // system startup phase. |
| AddAppLaunchInfo(GetPath(), kId2); |
| |
| // Verify `timer` doesn't start. |
| EXPECT_FALSE(timer->IsRunning()); |
| |
| ReadFromFile(GetPath(), /*clear_data=*/false); |
| |
| // Verify the restore data can be read correctly. |
| auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| |
| // Verify the restore data for `kAppId` exists, and that it contains data for |
| // `kId1` but none for `kId2`. |
| auto& launch_list1 = restore_data->app_id_to_launch_list(); |
| EXPECT_THAT(launch_list1, |
| ElementsAre(Pair(kAppId, ElementsAre(Pair(kId1, testing::_))))); |
| |
| // Simulate the system reboots. |
| FullRestoreReadHandlerTestApi(read_handler).ClearRestoreData(); |
| save_handler->ClearForTesting(); |
| |
| ReadFromFile(GetPath(), /*clear_data=*/false); |
| |
| // Verify the original restore data can be read correctly. |
| restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| |
| // Verify the restore data for `kAppId` exists, and that it contains data for |
| // `kId1` but none for `kId2`. |
| auto& launch_list2 = restore_data->app_id_to_launch_list(); |
| EXPECT_THAT(launch_list2, |
| ElementsAre(Pair(kAppId, ElementsAre(Pair(kId1, testing::_))))); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, SaveAndReadRestoreData) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add app launch info, and verify the timer starts. |
| AddAppLaunchInfo(GetPath(), kId1); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| // Add one more app launch info, and verify the timer is still running. |
| AddAppLaunchInfo(GetPath(), kId2); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| std::unique_ptr<aura::Window> window1 = |
| CreateWindowInfo(kId2, kActivationIndex2); |
| |
| // Simulate timeout, and verify the timer stops. |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Modify the window info, and verify the timer starts. |
| std::unique_ptr<aura::Window> window2 = |
| CreateWindowInfo(kId1, kActivationIndex1); |
| EXPECT_TRUE(timer->IsRunning()); |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Verify that GetAppId() can get correct app id for |window1| and |window2|. |
| EXPECT_EQ(save_handler->GetAppId(window1.get()), kAppId); |
| EXPECT_EQ(save_handler->GetAppId(window2.get()), kAppId); |
| |
| // Modify the window id from `kId2` to `kId3` for `kAppId`. |
| save_handler->ModifyWindowId(GetPath(), kAppId, kId2, kId3); |
| EXPECT_TRUE(timer->IsRunning()); |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Verify now GetAppId() can still get correct id for |window1| whose |
| // app_restore::kWindowIdKey has changed. |
| EXPECT_EQ(save_handler->GetAppId(window1.get()), kAppId); |
| |
| ReadFromFile(GetPath()); |
| |
| // Verify the restore data can be read correctly. |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| |
| const auto& launch_list = restore_data->app_id_to_launch_list(); |
| EXPECT_EQ(1u, launch_list.size()); |
| |
| // Verify for |kAppId|. |
| const auto launch_list_it = launch_list.find(kAppId); |
| EXPECT_TRUE(launch_list_it != launch_list.end()); |
| EXPECT_EQ(2u, launch_list_it->second.size()); |
| |
| // Verify for |kId1|. |
| const auto app_restore_data_it1 = launch_list_it->second.find(kId1); |
| EXPECT_TRUE(app_restore_data_it1 != launch_list_it->second.end()); |
| |
| const auto& data1 = app_restore_data_it1->second; |
| EXPECT_THAT(data1->window_info.activation_index, |
| testing::Optional(kActivationIndex1)); |
| |
| // Verify the restore data for |kId2| doesn't exist. |
| EXPECT_FALSE(launch_list_it->second.contains(kId2)); |
| |
| // Verify the restore data for |kId2| is moved to |kId3|. |
| const auto app_restore_data_it3 = launch_list_it->second.find(kId3); |
| ASSERT_NE(app_restore_data_it3, launch_list_it->second.end()); |
| |
| const auto& data3 = app_restore_data_it3->second; |
| EXPECT_THAT(data3->window_info.activation_index, |
| testing::Optional(kActivationIndex2)); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, MultipleFilePaths) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| base::ScopedTempDir tmp_dir1; |
| base::ScopedTempDir tmp_dir2; |
| ASSERT_TRUE(tmp_dir1.CreateUniqueTempDir()); |
| ASSERT_TRUE(tmp_dir2.CreateUniqueTempDir()); |
| |
| save_handler->SetActiveProfilePath(tmp_dir1.GetPath()); |
| |
| // Add app launch info for |tmp_dir1|, and verify the timer starts. |
| AddAppLaunchInfo(tmp_dir1.GetPath(), kId1); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| // Add app launch info for |tmp_dir2|, and verify the timer is still running. |
| AddAppLaunchInfo(tmp_dir2.GetPath(), kId2); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| CreateWindowInfo(kId2, kActivationIndex2); |
| |
| // Simulate timeout, and verify the timer stops. |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| EXPECT_FALSE(timer->IsRunning()); |
| |
| // Modify the window info, and verify the timer starts. |
| CreateWindowInfo(kId1, kActivationIndex1); |
| EXPECT_TRUE(timer->IsRunning()); |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| VerifyRestoreData(tmp_dir1.GetPath(), kId1, kActivationIndex1); |
| |
| // Set the active profile path to `tmp_dir2` to simulate the user is switched. |
| save_handler->SetActiveProfilePath(tmp_dir2.GetPath()); |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| VerifyRestoreData(tmp_dir2.GetPath(), kId2, kActivationIndex2); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, ClearRestoreData) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| FullRestoreSaveHandlerTestApi test_api(save_handler); |
| |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add app launch info, and verify the timer starts. |
| AddAppLaunchInfo(GetPath(), kId1); |
| EXPECT_TRUE(timer->IsRunning()); |
| |
| // Simulate timeout. |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Read the restore data. |
| ReadFromFile(GetPath()); |
| |
| // Clear restore data to simulate the system reboot. |
| test_api.ClearRestoreData(); |
| |
| // Verify the restore data can be read correctly. |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| |
| // Verify the restore data for `kAppId` exists, and that it contains data for |
| // `kId1`. |
| auto& launch_list = restore_data->app_id_to_launch_list(); |
| EXPECT_THAT(launch_list, |
| ElementsAre(Pair(kAppId, ElementsAre(Pair(kId1, testing::_))))); |
| |
| // Simulate timeout to clear restore data. |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Read the restore data. |
| ReadFromFile(GetPath()); |
| |
| // Verify the restore data has been cleared. |
| ASSERT_TRUE(GetRestoreData(GetPath())); |
| ASSERT_TRUE(GetRestoreData(GetPath())->app_id_to_launch_list().empty()); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, ArcWindowSaving) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| FullRestoreSaveHandlerTestApi test_api(save_handler); |
| |
| save_handler->SetPrimaryProfilePath(GetPath()); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add an ARC app launch info. |
| AddArcAppLaunchInfo(GetPath()); |
| const ArcSaveHandler* arc_save_handler = test_api.GetArcSaveHander(); |
| ASSERT_TRUE(arc_save_handler); |
| const auto& arc_session_id_map = test_api.GetArcSessionIdMap(); |
| EXPECT_EQ(1u, arc_session_id_map.size()); |
| auto session_it = arc_session_id_map.find(kArcSessionId1); |
| EXPECT_TRUE(session_it != arc_session_id_map.end()); |
| |
| // Create a task. Since we have got the task, the arc session id map can be |
| // cleared. |
| save_handler->OnTaskCreated(kAppId, kArcTaskId1, kArcSessionId1); |
| EXPECT_TRUE(arc_session_id_map.empty()); |
| const auto& task_id_map = test_api.GetArcTaskIdMap(); |
| EXPECT_EQ(1u, task_id_map.size()); |
| auto task_id = task_id_map.find(kArcTaskId1); |
| EXPECT_TRUE(task_id != task_id_map.end()); |
| |
| // Create a window to associate with the task id. |
| std::unique_ptr<aura::Window> window = CreateWindowInfo( |
| kArcTaskId1, kActivationIndex1, chromeos::AppType::ARC_APP); |
| // Test that using ARC task id we can get the correct app id for the window. |
| EXPECT_EQ(save_handler->GetAppId(window.get()), kAppId); |
| |
| // Destroy the task. |
| save_handler->OnTaskDestroyed(kArcTaskId1); |
| EXPECT_TRUE(task_id_map.empty()); |
| |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| ReadFromFile(GetPath()); |
| |
| // Verify there is not restore data. |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| EXPECT_TRUE(restore_data->app_id_to_launch_list().empty()); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, ArcLaunchWithoutTask) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| FullRestoreSaveHandlerTestApi test_api(save_handler); |
| |
| save_handler->SetPrimaryProfilePath(GetPath()); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add an ARC app launch info. |
| AddArcAppLaunchInfo(GetPath()); |
| |
| // Verify the ARC app launch info is saved to `arc_session_id_map`. |
| const auto& arc_session_id_map = test_api.GetArcSessionIdMap(); |
| EXPECT_THAT(arc_session_id_map, |
| ElementsAre(Pair(kArcSessionId1, testing::_))); |
| |
| // Verify the ARC check timer starts running. |
| base::RepeatingTimer* arc_check_timer = test_api.GetArcCheckTimer(); |
| EXPECT_TRUE(arc_check_timer->IsRunning()); |
| |
| // Simulate more than 30 seconds have passed, OnTaskCreated is not called, and |
| // the ARC check timer is expired to remove the ARC app launch info. |
| test_api.ModifyLaunchTime(kArcSessionId1); |
| test_api.CheckArcTasks(); |
| EXPECT_TRUE(arc_session_id_map.empty()); |
| EXPECT_TRUE(test_api.GetArcTaskIdMap().empty()); |
| EXPECT_FALSE(arc_check_timer->IsRunning()); |
| |
| // Verify the timer in FullRestoreSaveHandler is not running, because there is |
| // no app launching info to save. |
| EXPECT_FALSE(timer->IsRunning()); |
| task_environment().RunUntilIdle(); |
| |
| ReadFromFile(GetPath()); |
| |
| // Verify there is not restore data. |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| EXPECT_TRUE(restore_data->app_id_to_launch_list().empty()); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, ArcWindowRestore) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| FullRestoreSaveHandlerTestApi save_test_api(save_handler); |
| |
| save_handler->SetPrimaryProfilePath(GetPath()); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add an ARC app launch info. |
| AddArcAppLaunchInfo(GetPath()); |
| const ArcSaveHandler* arc_save_handler = save_test_api.GetArcSaveHander(); |
| ASSERT_TRUE(arc_save_handler); |
| EXPECT_EQ(1u, save_test_api.GetArcSessionIdMap().size()); |
| |
| // Verify the ARC check timer starts running. |
| base::RepeatingTimer* arc_check_timer = save_test_api.GetArcCheckTimer(); |
| EXPECT_TRUE(arc_check_timer->IsRunning()); |
| |
| // Create a task. Since we have got the task, the arc session id map can be |
| // cleared. |
| save_handler->OnTaskCreated(kAppId, kArcTaskId1, kArcSessionId1); |
| EXPECT_TRUE(save_test_api.GetArcSessionIdMap().empty()); |
| EXPECT_EQ(1u, save_test_api.GetArcTaskIdMap().size()); |
| EXPECT_FALSE(arc_check_timer->IsRunning()); |
| |
| // Modify the window info. |
| CreateWindowInfo(kArcTaskId1, kActivationIndex1, chromeos::AppType::ARC_APP); |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| ReadFromFile(GetPath()); |
| |
| // Verify the restore data can be read correctly. |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| |
| FullRestoreReadHandler* read_handler = FullRestoreReadHandler::GetInstance(); |
| // The following is necessary for making `ShouldUseFullRestoreArcData()` and |
| // `read_handler->IsFullRestoreRunning()` return true; |
| read_handler->SetActiveProfilePath(GetPath()); |
| read_handler->SetStartTimeForProfile(GetPath()); |
| |
| FullRestoreReadHandlerTestApi read_test_api(read_handler); |
| ASSERT_TRUE(read_test_api.GetArcReadHander()); |
| EXPECT_EQ(1u, read_test_api.GetArcWindowIdMap().size()); |
| |
| // Verify the map from app ids to launch list. |
| const std::map<std::string, |
| std::map<int, std::unique_ptr<app_restore::AppRestoreData>>>& |
| launch_list = restore_data->app_id_to_launch_list(); |
| EXPECT_EQ(1u, launch_list.size()); |
| |
| // Verify the launch list for |kAppId|: |
| const auto launch_list_it = launch_list.find(kAppId); |
| EXPECT_TRUE(launch_list_it != launch_list.end()); |
| EXPECT_EQ(1u, launch_list_it->second.size()); |
| |
| // Verify that there is an AppRestoreData for the window id |kArcTaskId1|. |
| const auto app_restore_data_it = launch_list_it->second.find(kArcTaskId1); |
| EXPECT_TRUE(app_restore_data_it != launch_list_it->second.end()); |
| |
| // Verify the AppRestoreData. |
| const std::unique_ptr<app_restore::AppRestoreData>& data = |
| app_restore_data_it->second; |
| EXPECT_THAT(data->window_info.activation_index, |
| testing::Optional(kActivationIndex1)); |
| |
| // Simulate the ARC app launching, and set the arc session id kArcSessionId2 |
| // for the restore window id |kArcTaskId1|. |
| read_handler->SetArcSessionIdForWindowId(kArcSessionId2, kArcTaskId1); |
| EXPECT_EQ(1u, read_test_api.GetArcSessionIdMap().size()); |
| |
| // Before OnTaskCreated is called, return |kArcTaskId1| for |kArcSessionId2| |
| // to simulate the ghost window property setting. |
| EXPECT_EQ(kArcTaskId1, |
| app_restore::GetArcRestoreWindowIdForSessionId(kArcSessionId2)); |
| |
| // Before OnTaskCreated is called, return -1 to add the ARC app window to the |
| // hidden container. |
| EXPECT_EQ(app_restore::kParentToHiddenContainer, |
| app_restore::GetArcRestoreWindowIdForTaskId(kArcTaskId2)); |
| |
| // Call OnTaskCreated to simulate that the ARC app with |kAppId| has been |
| // launched, and the new task id |kArcTaskId2| has been created with |
| // |kArcSessionId2| returned. |
| read_handler->OnTaskCreated(kAppId, kArcTaskId2, kArcSessionId2); |
| EXPECT_EQ(1u, read_test_api.GetArcTaskIdMap().size()); |
| |
| // Since we have got the new task with |kArcSessionId2|, the arc session id |
| // map can be cleared. And verify that we can get the restore window id |
| // |kArcTaskId1| with the new |kArcTaskId2|. |
| EXPECT_TRUE(read_test_api.GetArcSessionIdMap().empty()); |
| EXPECT_EQ(kArcTaskId1, |
| app_restore::GetArcRestoreWindowIdForTaskId(kArcTaskId2)); |
| |
| // Verify |window_info| for |kArcTaskId1|. |
| auto window_info = GetArcWindowInfo(kArcTaskId1); |
| EXPECT_TRUE(window_info); |
| EXPECT_EQ(kActivationIndex1, window_info->activation_index); |
| |
| // Call OnTaskDestroyed to simulate the ARC app launching has been finished |
| // for |kArcTaskId2|, and verify the task id map is now empty and a invalid |
| // value is returned when trying to get the restore window id. |
| read_handler->OnTaskDestroyed(kArcTaskId2); |
| EXPECT_EQ(0, app_restore::GetArcRestoreWindowIdForTaskId(kArcTaskId2)); |
| EXPECT_TRUE(read_test_api.GetArcTaskIdMap().empty()); |
| EXPECT_TRUE(read_test_api.GetArcWindowIdMap().empty()); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, ReadBrowserRestoreData) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add browser launch info. |
| std::vector<GURL> urls = {GURL(kExampleUrl1), GURL(kExampleUrl2)}; |
| const int active_tab_index = 1; |
| AddBrowserLaunchInfo(GetPath(), kId1, urls, |
| /*active_tab_index=*/active_tab_index); |
| EXPECT_TRUE(timer->IsRunning()); |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Now read from the file. |
| ReadFromFile(GetPath()); |
| |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| const auto& launch_list = restore_data->app_id_to_launch_list(); |
| EXPECT_EQ(1u, launch_list.size()); |
| const auto launch_list_it = launch_list.find(app_constants::kChromeAppId); |
| EXPECT_TRUE(launch_list_it != launch_list.end()); |
| EXPECT_EQ(1u, launch_list_it->second.size()); |
| const auto app_restore_data_it = launch_list_it->second.find(kId1); |
| EXPECT_TRUE(app_restore_data_it != launch_list_it->second.end()); |
| |
| const app_restore::BrowserExtraInfo browser_info = |
| app_restore_data_it->second->browser_extra_info; |
| EXPECT_THAT(browser_info.urls, |
| ElementsAre(GURL(kExampleUrl1), GURL(kExampleUrl2))); |
| EXPECT_THAT(browser_info.active_tab_index, |
| testing::Optional(active_tab_index)); |
| } |
| |
| TEST_F(FullRestoreReadAndSaveTest, ReadChromeAppRestoreData) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add Chrome app launch info. |
| AddChromeAppLaunchInfo(GetPath()); |
| EXPECT_TRUE(timer->IsRunning()); |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| // Now read from the file. |
| ReadFromFile(GetPath()); |
| |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| const auto& launch_list = restore_data->app_id_to_launch_list(); |
| EXPECT_EQ(1u, launch_list.size()); |
| const auto launch_list_it = launch_list.find(kAppId); |
| EXPECT_TRUE(launch_list_it != launch_list.end()); |
| EXPECT_EQ(1u, launch_list_it->second.size()); |
| const auto app_restore_data_it = launch_list_it->second.find(kId1); |
| EXPECT_TRUE(app_restore_data_it != launch_list_it->second.end()); |
| |
| const auto& data = app_restore_data_it->second; |
| EXPECT_THAT(data->file_paths, ElementsAre(base::FilePath(kFilePath1), |
| base::FilePath(kFilePath2))); |
| EXPECT_TRUE(data->handler_id.has_value()); |
| EXPECT_EQ(kHandlerId, data->handler_id.value()); |
| } |
| |
| // prevent the windows in that desk from being restored. |
| TEST_F(FullRestoreReadAndSaveTest, PreventWindowsOnRemovingDeskFromRestoring) { |
| FullRestoreSaveHandler* save_handler = GetSaveHandler(); |
| base::OneShotTimer* timer = save_handler->GetTimerForTesting(); |
| |
| // Add app launch info, and verify the timer starts. |
| AddAppLaunchInfo(GetPath(), kId1); |
| ASSERT_TRUE(timer->IsRunning()); |
| |
| // Add one more app launch info, and verify the timer is still running. |
| AddAppLaunchInfo(GetPath(), kId2); |
| ASSERT_TRUE(timer->IsRunning()); |
| |
| // Create two windows. Establish that `window1` will be on the removing desk |
| // and `window2` will be on the non-removing desk. |
| std::unique_ptr<aura::Window> window1 = CreateWindowInfo( |
| kId1, kActivationIndex1, chromeos::AppType::BROWSER, kRemovingDeskGuid); |
| std::unique_ptr<aura::Window> window2 = |
| CreateWindowInfo(kId2, kActivationIndex2, chromeos::AppType::BROWSER, |
| kNonRemovingDeskGuid); |
| |
| // Establish that the desk with `kRemovingDeskGuid` as its GUID is being |
| // removed. |
| save_handler->SaveRemovingDeskGuid(kRemovingDeskGuid); |
| |
| // Simulate timeout, which should trigger a save, and verify the timer stops. |
| timer->FireNow(); |
| task_environment().RunUntilIdle(); |
| |
| ReadFromFile(GetPath()); |
| |
| // Verify the restore data can be read correctly. |
| const auto* restore_data = GetRestoreData(GetPath()); |
| ASSERT_TRUE(restore_data); |
| |
| // The launch list in `restore_data` should only have `window2`. |
| EXPECT_THAT(restore_data->app_id_to_launch_list(), |
| ElementsAre(Pair(kAppId, ElementsAre(Pair(kId2, testing::_))))); |
| } |
| |
| } // namespace full_restore |