chromium/components/app_restore/full_restore_read_and_save_unittest.cc

// 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/test/scoped_feature_list.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/features.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";

constexpr char kLacrosWindowId[] = "123";

constexpr uint32_t kBrowserSessionId = 56;

// 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(); }

  const LacrosSaveHandler* GetLacrosSaveHander() const {
    DCHECK(save_handler_);
    return save_handler_->lacros_save_handler_.get();
  }

  const std::map<std::string, LacrosSaveHandler::WindowData>&
  GetLacrosWindowCandidates() const {
    const auto* lacros_save_handler = GetLacrosSaveHander();
    DCHECK(lacros_save_handler);
    return lacros_save_handler->window_candidates_;
  }

  const std::map<std::string, std::string>& GetLacrosWindowIdToAppIdMap()
      const {
    const auto* lacros_save_handler = GetLacrosSaveHander();
    DCHECK(lacros_save_handler);
    return lacros_save_handler->lacros_window_id_to_app_id_;
  }

  int32_t GetLacrosWindowId(std::string lacros_window_id) const {
    const auto& window_candidates = GetLacrosWindowCandidates();
    auto it = window_candidates.find(lacros_window_id);
    return it != window_candidates.end() ? it->second.window_id : -1;
  }

  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 {
    scoped_feature_list_.InitAndEnableFeature(features::kFullRestoreForLacros);
    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));
  }

  std::unique_ptr<views::Widget> CreateLacrosWidget(
      const std::string& lacros_window_id,
      int32_t restore_session_id,
      int32_t restore_window_id) {
    views::Widget::InitParams params(
        views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
        views::Widget::InitParams::TYPE_WINDOW);

    params.bounds = gfx::Rect(5, 5, 20, 20);
    params.context = aura_test_helper_.GetContext();

    params.init_properties_container.SetProperty(chromeos::kAppTypeKey,
                                                 chromeos::AppType::LACROS);
    params.init_properties_container.SetProperty(app_restore::kLacrosWindowId,
                                                 lacros_window_id);

    params.init_properties_container.SetProperty(app_restore::kWindowIdKey,
                                                 restore_session_id);
    params.init_properties_container.SetProperty(
        app_restore::kRestoreWindowIdKey, restore_window_id);

    auto widget = std::make_unique<views::Widget>(std::move(params));
    widget->Show();
    return widget;
  }

  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::CreateTestWindowWithId(id, nullptr));
    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::CreateTestWindowWithId(restore_window_id, nullptr));
    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_;

  base::test::ScopedFeatureList scoped_feature_list_;

  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(base::Contains(launch_list_it->second, 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());
}

// Verify the Lacros browser window is saved correctly when the window is
// created first, then OnLacrosWindowAdded is called.
TEST_F(FullRestoreReadAndSaveTest, LacrosBrowserWindowSavingCreateWindowFirst) {
  FullRestoreSaveHandler* save_handler = GetSaveHandler();
  FullRestoreSaveHandlerTestApi test_api(save_handler);

  save_handler->SetPrimaryProfilePath(GetPath());
  base::OneShotTimer* timer = save_handler->GetTimerForTesting();

  const LacrosSaveHandler* lacros_save_handler = test_api.GetLacrosSaveHander();
  ASSERT_TRUE(lacros_save_handler);

  // Create a browser window first, then OnLacrosWindowAdded is called later.
  auto widget = CreateLacrosWidget(kLacrosWindowId, kBrowserSessionId,
                                   /*restored_browser_session_id=*/0);
  auto* window = widget->GetNativeWindow();
  SaveWindowInfo(window, kActivationIndex1);

  // Verify the browser window is saved.
  EXPECT_EQ(app_constants::kLacrosAppId,
            save_handler->GetAppId(widget->GetNativeWindow()));
  auto window_info = save_handler->GetWindowInfo(
      GetPath(), app_constants::kLacrosAppId, kBrowserSessionId);
  EXPECT_EQ(kActivationIndex1, window_info->activation_index.value());

  // Modify the window info.
  SaveWindowInfo(window, kActivationIndex2);
  window_info = save_handler->GetWindowInfo(
      GetPath(), app_constants::kLacrosAppId, kBrowserSessionId);
  EXPECT_EQ(kActivationIndex2, window_info->activation_index.value());

  widget.reset();
  ASSERT_FALSE(save_handler->GetWindowInfo(
      GetPath(), app_constants::kLacrosAppId, kBrowserSessionId));

  timer->FireNow();
  // Wait for the restore data to be written to the full restore file.
  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());
}

// Verify the Lacros browser window is saved correctly when
// OnLacrosWindowAdded is called first, then the window is init later.
TEST_F(FullRestoreReadAndSaveTest,
       LacrosBrowserWindowSavingOnLacrosWindowAddedCalledFirst) {
  FullRestoreSaveHandler* save_handler = GetSaveHandler();
  FullRestoreSaveHandlerTestApi test_api(save_handler);

  save_handler->SetPrimaryProfilePath(GetPath());
  base::OneShotTimer* timer = save_handler->GetTimerForTesting();
  const LacrosSaveHandler* lacros_save_handler = test_api.GetLacrosSaveHander();
  ASSERT_TRUE(lacros_save_handler);

  // OnLacrosWindowAdded is called first, then init the browser window later.
  auto widget = CreateLacrosWidget(kLacrosWindowId, kBrowserSessionId,
                                   /*restored_browser_session_id=*/0);
  aura::Window* window = widget->GetNativeWindow();

  SaveWindowInfo(window, kActivationIndex1);

  // Verify the browser window is saved.
  EXPECT_EQ(app_constants::kLacrosAppId, save_handler->GetAppId(window));
  auto window_info = save_handler->GetWindowInfo(
      GetPath(), app_constants::kLacrosAppId, kBrowserSessionId);
  EXPECT_EQ(kActivationIndex1, window_info->activation_index.value());

  // Modify the window info.
  SaveWindowInfo(window, kActivationIndex2);
  window_info = save_handler->GetWindowInfo(
      GetPath(), app_constants::kLacrosAppId, kBrowserSessionId);
  EXPECT_EQ(kActivationIndex2, window_info->activation_index.value());

  widget.reset();

  // Wait for `save_handler` to fresh the full restore file.
  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());
}

// Verify the Lacros Chrome app window is saved correctly when the window is
// created first, then OnAppWindowAdded is called.
TEST_F(FullRestoreReadAndSaveTest,
       LacrosChromeAppWindowSavingCreateWindowFirst) {
  FullRestoreSaveHandler* save_handler = GetSaveHandler();
  FullRestoreSaveHandlerTestApi test_api(save_handler);

  save_handler->SetPrimaryProfilePath(GetPath());
  base::OneShotTimer* timer = save_handler->GetTimerForTesting();

  // Add a Chrome app launch info.
  SaveAppLaunchInfo(
      GetPath(), std::make_unique<app_restore::AppLaunchInfo>(
                     kAppId, apps::LaunchContainer::kLaunchContainerNone,
                     WindowOpenDisposition::UNKNOWN, display::kInvalidDisplayId,
                     std::vector<base::FilePath>{}, nullptr));
  const LacrosSaveHandler* lacros_save_handler = test_api.GetLacrosSaveHander();
  ASSERT_TRUE(lacros_save_handler);

  // Create a Chrome app window first, then the crosapi OnAppWindowAdded is
  // called later.
  auto widget = CreateLacrosWidget(kLacrosWindowId, kBrowserSessionId,
                                   /*restored_browser_session_id=*/0);
  auto* window = widget->GetNativeWindow();
  EXPECT_FALSE(test_api.GetLacrosWindowCandidates().empty());
  SaveWindowInfo(window, kActivationIndex1);
  OnLacrosChromeAppWindowAdded(kAppId, kLacrosWindowId);

  // Verify the Chrome app window is saved.
  EXPECT_TRUE(test_api.GetLacrosWindowIdToAppIdMap().empty());
  EXPECT_EQ(save_handler->GetAppId(widget->GetNativeWindow()), kAppId);
  auto window_info = save_handler->GetWindowInfo(
      GetPath(), kAppId, test_api.GetLacrosWindowId(kLacrosWindowId));
  EXPECT_EQ(kActivationIndex1, window_info->activation_index.value());

  // Modify the window info.
  SaveWindowInfo(window, kActivationIndex2);
  window_info = save_handler->GetWindowInfo(
      GetPath(), kAppId, test_api.GetLacrosWindowId(kLacrosWindowId));
  EXPECT_EQ(kActivationIndex2, window_info->activation_index.value());

  // Destroy the window first, then call the crosapi OnAppWindowRemoved.
  widget.reset();
  OnLacrosChromeAppWindowRemoved(kAppId, kLacrosWindowId);
  EXPECT_TRUE(test_api.GetLacrosWindowCandidates().empty());
  EXPECT_TRUE(test_api.GetLacrosWindowIdToAppIdMap().empty());

  // Wait for `save_handler` to fresh the full restore file.
  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());
}

// Verify the Lacros Chrome app window is saved correctly when OnAppWindowAdded
// is called first, then the window is created later.
TEST_F(FullRestoreReadAndSaveTest,
       LacrosChromeAppWindowSavingOnAppWindowCalledFirst) {
  FullRestoreSaveHandler* save_handler = GetSaveHandler();
  FullRestoreSaveHandlerTestApi test_api(save_handler);

  save_handler->SetPrimaryProfilePath(GetPath());
  base::OneShotTimer* timer = save_handler->GetTimerForTesting();

  // Add a Chrome app launch info.
  auto intent = std::make_unique<apps::Intent>(apps_util::kIntentActionSend);
  intent->activity_name = "activity_name";
  SaveAppLaunchInfo(
      GetPath(),
      std::make_unique<app_restore::AppLaunchInfo>(
          kAppId, apps::LaunchContainer::kLaunchContainerNone,
          WindowOpenDisposition::CURRENT_TAB, display::kInvalidDisplayId,
          std::vector<base::FilePath>{base::FilePath(kFilePath1),
                                      base::FilePath(kFilePath2)},
          std::move(intent)));
  const LacrosSaveHandler* lacros_save_handler = test_api.GetLacrosSaveHander();
  ASSERT_TRUE(lacros_save_handler);

  // The crosapi OnAppWindowAdded is called first, then create a Chrome app
  // window later.
  OnLacrosChromeAppWindowAdded(kAppId, kLacrosWindowId);
  EXPECT_FALSE(test_api.GetLacrosWindowIdToAppIdMap().empty());
  auto widget = CreateLacrosWidget(kLacrosWindowId, kBrowserSessionId,
                                   /*restored_browser_session_id=*/0);
  auto* window = widget->GetNativeWindow();
  EXPECT_FALSE(test_api.GetLacrosWindowCandidates().empty());
  SaveWindowInfo(window, kActivationIndex1);

  // Verify the Chrome app window is saved.
  EXPECT_EQ(save_handler->GetAppId(widget->GetNativeWindow()), kAppId);
  auto window_info = save_handler->GetWindowInfo(
      GetPath(), kAppId, test_api.GetLacrosWindowId(kLacrosWindowId));
  EXPECT_EQ(kActivationIndex1, window_info->activation_index.value());

  // Modify the window info.
  SaveWindowInfo(window, kActivationIndex2);
  window_info = save_handler->GetWindowInfo(
      GetPath(), kAppId, test_api.GetLacrosWindowId(kLacrosWindowId));
  EXPECT_EQ(kActivationIndex2, window_info->activation_index.value());

  // Call the crosapi OnAppWindowRemoved first, then destroy the window.
  OnLacrosChromeAppWindowRemoved(kAppId, kLacrosWindowId);
  widget.reset();
  EXPECT_TRUE(test_api.GetLacrosWindowCandidates().empty());
  EXPECT_TRUE(test_api.GetLacrosWindowIdToAppIdMap().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());
}

// Verifies that saving a removing desk's GUID in `RestoreData` allows for us to
// 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