chromium/ios/chrome/browser/browsing_data/model/tabs_closure_util_unittest.mm

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/browsing_data/model/tabs_closure_util.h"

#import "base/time/time.h"
#import "ios/chrome/browser/sessions/model/fake_tab_restore_service.h"
#import "ios/chrome/browser/sessions/model/ios_chrome_tab_restore_service_factory.h"
#import "ios/chrome/browser/sessions/model/session_restoration_service_factory.h"
#import "ios/chrome/browser/sessions/model/test_session_restoration_service.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/model/web_state_list/tab_group.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/signin/model/fake_authentication_service_delegate.h"
#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
#import "ios/web/public/test/fakes/fake_navigation_manager.h"
#import "ios/web/public/test/fakes/fake_web_frames_manager.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"

namespace {
using tabs_closure_util::CloseTabs;
using tabs_closure_util::GetTabGroupsWithTabsToClose;
using tabs_closure_util::GetTabsInfoForCache;
using tabs_closure_util::GetTabsToClose;
using tabs_closure_util::WebStateIDToTime;

// List all ContentWorlds. Necessary because calling SetWebFramesManager(...)
// with a kAllContentWorlds is not enough with FakeWebState.
constexpr web::ContentWorld kContentWorlds[] = {
    web::ContentWorld::kAllContentWorlds,
    web::ContentWorld::kPageContentWorld,
    web::ContentWorld::kIsolatedWorld,
};

// Session name used by the fake SceneState.
const char kSceneSessionID[] = "Identifier";

// Gets the WebStateIDs from `WebStateIDToTime`.
std::set<web::WebStateID> GetWebStateIDs(WebStateIDToTime tabs) {
  std::set<web::WebStateID> expected_web_state_ids;
  for (auto const& tab : tabs) {
    expected_web_state_ids.insert(tab.first);
  }
  return expected_web_state_ids;
}

}  // namespace

class TabsClosureUtilTest : public PlatformTest {
 public:
  TabsClosureUtilTest() {
    // Create a TestChromeBrowserState with required services.
    TestChromeBrowserState::Builder builder;
    builder.AddTestingFactory(
        AuthenticationServiceFactory::GetInstance(),
        AuthenticationServiceFactory::GetDefaultFactory());
    builder.AddTestingFactory(
        SessionRestorationServiceFactory::GetInstance(),
        TestSessionRestorationService::GetTestingFactory());
    builder.AddTestingFactory(IOSChromeTabRestoreServiceFactory::GetInstance(),
                              FakeTabRestoreService::GetTestingFactory());
    browser_state_ = std::move(builder).Build();

    // Initialize the AuthenticationService.
    AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
        browser_state_.get(),
        std::make_unique<FakeAuthenticationServiceDelegate>());

    scene_state_ = OCMClassMock([SceneState class]);
    OCMStub([scene_state_ sceneSessionID]).andReturn(@(kSceneSessionID));
    browser_ = Browser::Create(browser_state_.get(), scene_state_);
  }

  Browser* browser() { return browser_.get(); }

  web::WebState* web_state0() { return web_state0_; }
  web::WebState* web_state1() { return web_state1_; }

  // Appends two unrealized WebStates in `browser_` and sets `web_state0_` and
  // `web_state1_` with them. Returns a map with their WebState Ids and their
  // last_navigation_time;
  WebStateIDToTime AppendUnrealizedWebstates(base::Time last_navigation_time) {
    web_state0_ = AppendWebState(/*realized=*/false, last_navigation_time);
    web_state1_ = AppendWebState(/*realized=*/false, last_navigation_time);

    WebStateList* web_state_list = browser()->GetWebStateList();
    CHECK_EQ(web_state_list->count(), 2);
    CHECK_EQ(web_state_list->GetWebStateAt(0), web_state0_);
    CHECK_EQ(web_state_list->GetWebStateAt(1), web_state1_);

    return {{web_state0_->GetUniqueIdentifier(), last_navigation_time},
            {web_state1_->GetUniqueIdentifier(), last_navigation_time}};
  }

  // Appends a fake WebState in `browser_`.
  web::WebState* AppendWebState(bool realized,
                                base::Time last_navigation_time,
                                bool pinned = false) {
    const GURL url = GURL("https://example.com");
    auto navigation_manager = std::make_unique<web::FakeNavigationManager>();
    navigation_manager->AddItem(url, ui::PAGE_TRANSITION_LINK);
    web::NavigationItem* navigation_item =
        navigation_manager->GetItemAtIndex(0);
    navigation_item->SetTimestamp(last_navigation_time);
    navigation_manager->SetLastCommittedItem(navigation_item);

    auto web_state = std::make_unique<web::FakeWebState>();
    web_state->SetIsRealized(realized);
    web_state->SetVisibleURL(url);
    web_state->SetBrowserState(browser_->GetBrowserState());
    web_state->SetNavigationManager(std::move(navigation_manager));
    web_state->SetNavigationItemCount(1);

    for (const web::ContentWorld content_world : kContentWorlds) {
      web_state->SetWebFramesManager(
          content_world, std::make_unique<web::FakeWebFramesManager>());
    }

    WebStateList* web_state_list = browser_->GetWebStateList();
    // Force the insertion at the end. Otherwise, the opener will trigger logic
    // to move an inserted WebState close to its opener.
    const WebStateList::InsertionParams params =
        WebStateList::InsertionParams::AtIndex(web_state_list->count())
            .Pinned(pinned);
    const int insertion_index =
        web_state_list->InsertWebState(std::move(web_state), params);

    return web_state_list->GetWebStateAt(insertion_index);
  }

  // Appends a tab group to  `_browser` with the tabs in `indexes`. These
  // indexes in WebStateList need to have been populated beforehand.
  const TabGroup* AppendTabGroup(const std::set<int>& indexes) {
    tab_groups::TabGroupVisualData visualData = tab_groups::TabGroupVisualData(
        u"Group", tab_groups::TabGroupColorId::kPink);
    return browser_->GetWebStateList()->CreateGroup(
        indexes, visualData, tab_groups::TabGroupId::GenerateNew());
  }

 private:
  web::WebTaskEnvironment task_environment_;
  IOSChromeScopedTestingLocalState scoped_testing_local_state_;
  std::unique_ptr<ChromeBrowserState> browser_state_;
  __strong SceneState* scene_state_;
  std::unique_ptr<Browser> browser_;

  web::WebState* web_state0_;
  web::WebState* web_state1_;
};

// Tests `GetTabsInfoForCache` with several time ranges.
TEST_F(TabsClosureUtilTest, GetCountOfTabsToClose) {
  base::Time now = base::Time::Now();  // Current time for reference

  const std::pair<web::WebStateID, base::Time> tab0 = {
      web::WebStateID::NewUnique(),
      now - base::Hours(1)};  // Tab 0: Active 1 hour ago.
  const std::pair<web::WebStateID, base::Time> tab1 = {
      web::WebStateID::NewUnique(),
      now - base::Hours(3)};  // Tab 1: Active 3 hours ago.
  const std::pair<web::WebStateID, base::Time> tab2 = {
      web::WebStateID::NewUnique(),
      now - base::Minutes(15)};  // Tab 2: Active 15 minutes ago.
  const std::pair<web::WebStateID, base::Time> tab3 = {
      web::WebStateID::NewUnique(),
      now - base::Days(2)};  // Tab 3: Active 2 days ago.
  const std::pair<web::WebStateID, base::Time> tab4 = {
      web::WebStateID::NewUnique(),
      now + base::Hours(1)};  // Tab 4: Active in the future.

  const WebStateIDToTime tabs = {{tab0}, {tab1}, {tab2}, {tab3}, {tab4}};

  EXPECT_EQ(
      GetTabsInfoForCache(tabs, now - base::Hours(4), now - base::Hours(2)),
      WebStateIDToTime({{tab1}}));
  EXPECT_EQ(GetTabsInfoForCache(tabs, now - base::Hours(3), now),
            WebStateIDToTime({{tab0}, {tab1}, {tab2}}));
  EXPECT_EQ(GetTabsInfoForCache(tabs, now - base::Days(3), now),
            WebStateIDToTime({{tab0}, {tab1}, {tab2}, {tab3}}));
  EXPECT_EQ(GetTabsInfoForCache(tabs, now, now + base::Hours(2)),
            WebStateIDToTime({{tab4}}));
  EXPECT_EQ(GetTabsInfoForCache({}, now, now + base::Hours(2)),
            WebStateIDToTime({}));
}

// Tests that `CloseTabs` correctly closes the tabs within the time range, in
// this case, all tabs associated with the browser which are unrealized.
TEST_F(TabsClosureUtilTest, CloseTabs_RemoveAllTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));

  CloseTabs(web_state_list, begin_time, end_time, tabs);

  EXPECT_TRUE(web_state_list->empty());
}

// Tests that `GetTabsToClose` correctly return the tabs within the time range,
// in this case, all tabs associated with the browser which are unrealized.
TEST_F(TabsClosureUtilTest, GetTabsToClose_RemoveAllTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));

  std::set<web::WebStateID> web_state_ids =
      GetTabsToClose(web_state_list, begin_time, end_time, tabs);

  EXPECT_EQ(web_state_ids.size(), tabs.size());
  EXPECT_EQ(web_state_ids, GetWebStateIDs(tabs));
}

// Tests that `GetTabGroupsWithTabsToClose` correctly returns the tab groups
// with tabs within the time range, in this case, all tabs associated with the
// browser which are unrealized.
TEST_F(TabsClosureUtilTest, GetTabGroupsWithTabsToClose_RemoveAllTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));
  const TabGroup* group0 = AppendTabGroup({0});
  const TabGroup* group1 = AppendTabGroup({1});

  std::map<tab_groups::TabGroupId, std::set<int>> tab_group_ids =
      GetTabGroupsWithTabsToClose(web_state_list, begin_time, end_time, tabs);

  std::map<tab_groups::TabGroupId, std::set<int>> expected_tab_group_ids = {
      {group0->tab_group_id(), {0}}, {group1->tab_group_id(), {1}}};
  EXPECT_EQ(tab_group_ids.size(), 2u);
  EXPECT_EQ(tab_group_ids, expected_tab_group_ids);
}

// Tests that `GetTabGroupsWithTabsToClose` correctly returns the tab groups
// with tabs within the time range, in this case, all tabs associated with the
// browser which are unrealized.
TEST_F(TabsClosureUtilTest,
       GetTabGroupsWithTabsToClose_RemoveAllTabs_SameGroup) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));
  const TabGroup* group = AppendTabGroup({0, 1});

  std::map<tab_groups::TabGroupId, std::set<int>> tab_group_ids =
      GetTabGroupsWithTabsToClose(web_state_list, begin_time, end_time, tabs);

  std::map<tab_groups::TabGroupId, std::set<int>> expected_tab_group_ids = {
      {group->tab_group_id(), {0, 1}}};
  EXPECT_EQ(tab_group_ids.size(), 1u);
  EXPECT_EQ(tab_group_ids, expected_tab_group_ids);
}

// Tests that `GetTabGroupsWithTabsToClose` correctly returns the tab groups
// with tabs within the time range, in this case, no tabs within the time range
// are in a group.
TEST_F(TabsClosureUtilTest, GetTabGroupsWithTabsToClose_NoTabGroups) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));

  std::map<tab_groups::TabGroupId, std::set<int>> tab_group_ids =
      GetTabGroupsWithTabsToClose(web_state_list, begin_time, end_time, tabs);

  EXPECT_TRUE(tab_group_ids.empty());
}

// Tests that `CloseTabs` correctly closes all the tabs within the time frame,
// in this case, all tabs associated with the browser including the ones passed
// as cached information and the ones created after.
TEST_F(TabsClosureUtilTest, CloseTabs_NoMatchingTabsForDeletion) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));
  web::WebState* web_state3_ =
      AppendWebState(/*realized=*/true, end_time - base::Minutes(1));

  ASSERT_EQ(web_state_list->count(), 3);
  ASSERT_EQ(web_state_list->GetWebStateAt(0), web_state0());
  ASSERT_EQ(web_state_list->GetWebStateAt(1), web_state1());
  ASSERT_EQ(web_state_list->GetWebStateAt(2), web_state3_);

  // The unrelized webstates are passed direcly. The realized webstates will be
  // checked directly.
  CloseTabs(web_state_list, begin_time, end_time, tabs);

  EXPECT_TRUE(web_state_list->empty());
}

// Tests that `GetTabsToClose` correctly return the tabs within the time frame,
// in this case, all tabs associated with the browser including the ones passed
// as cached information and the ones created after.
TEST_F(TabsClosureUtilTest, GetTabsToClose_NoMatchingTabsForDeletion) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));
  web::WebState* web_state3_ =
      AppendWebState(/*realized=*/true, end_time - base::Minutes(1));

  ASSERT_EQ(web_state_list->count(), 3);
  ASSERT_EQ(web_state_list->GetWebStateAt(0), web_state0());
  ASSERT_EQ(web_state_list->GetWebStateAt(1), web_state1());
  ASSERT_EQ(web_state_list->GetWebStateAt(2), web_state3_);

  // The unrelized webstates are passed direcly. The realized webstates will be
  // checked directly.
  std::set<web::WebStateID> web_state_ids =
      GetTabsToClose(web_state_list, begin_time, end_time, tabs);

  EXPECT_EQ(web_state_ids.size(), 3u);
  std::set<web::WebStateID> expected_web_state_ids = GetWebStateIDs(tabs);
  expected_web_state_ids.insert(web_state3_->GetUniqueIdentifier());
  EXPECT_EQ(web_state_ids, expected_web_state_ids);
}

// Tests that `GetTabGroupsWithTabsToClose` correctly returns the tab groups
// with tabs within the time frame, in this case, all tabs associated with the
// browser including the ones passed as cached information and the ones created
// after.
TEST_F(TabsClosureUtilTest,
       GetTabGroupsWithTabsToClose_NoMatchingTabsForDeletion) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));
  web::WebState* web_state2 =
      AppendWebState(/*realized=*/true, end_time - base::Minutes(1));

  ASSERT_EQ(web_state_list->count(), 3);
  ASSERT_EQ(web_state_list->GetWebStateAt(0), web_state0());
  ASSERT_EQ(web_state_list->GetWebStateAt(1), web_state1());
  ASSERT_EQ(web_state_list->GetWebStateAt(2), web_state2);

  const TabGroup* group0 = AppendTabGroup({0});
  const TabGroup* group1 = AppendTabGroup({1});
  const TabGroup* group2 = AppendTabGroup({2});

  // The unrelized webstates are passed direcly. The realized webstates will be
  // checked directly.
  std::map<tab_groups::TabGroupId, std::set<int>> tab_group_ids =
      GetTabGroupsWithTabsToClose(web_state_list, begin_time, end_time, tabs);

  std::map<tab_groups::TabGroupId, std::set<int>> expected_tab_group_ids = {
      {group0->tab_group_id(), {0}},
      {group1->tab_group_id(), {1}},
      {group2->tab_group_id(), {2}}};
  EXPECT_EQ(tab_group_ids.size(), 3u);
  EXPECT_EQ(tab_group_ids, expected_tab_group_ids);
}

// Tests that `CloseTabs` correctly closes the cached unrealized tab, but not
// the non cached realized tab.
TEST_F(TabsClosureUtilTest, CloseTabs_OnlyOneTabForDeletion) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));

  CloseTabs(web_state_list, begin_time, end_time,
            {{tabs.begin()->first, tabs.begin()->second}});

  EXPECT_EQ(web_state_list->count(), 1);
  EXPECT_EQ(web_state_list->GetWebStateAt(0), web_state1());
}

// Tests that `GetTabsToClose` correctly return the cached unrealized tab, but
// not the non cached realized tab.
TEST_F(TabsClosureUtilTest, GetTabsToClose_OnlyOneTabForDeletion) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));

  std::set<web::WebStateID> web_state_ids =
      GetTabsToClose(web_state_list, begin_time, end_time,
                     {{tabs.begin()->first, tabs.begin()->second}});

  EXPECT_EQ(web_state_ids.size(), 1u);
  EXPECT_TRUE(web_state_ids.contains(web_state0()->GetUniqueIdentifier()));
}

// Tests that `GetTabGroupsWithTabsToClose` correctly returns the tab groups
// with tabs within the time frame, in this case, cached unrealized tab, but not
// the non cached realized tab.
TEST_F(TabsClosureUtilTest, GetTabGroupsWithTabsToClose_OnlyOneTabForDeletion) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));
  const TabGroup* group0 = AppendTabGroup({0});
  AppendTabGroup({1});

  std::map<tab_groups::TabGroupId, std::set<int>> tab_group_ids =
      GetTabGroupsWithTabsToClose(
          web_state_list, begin_time, end_time,
          {{tabs.begin()->first, tabs.begin()->second}});
  std::map<tab_groups::TabGroupId, std::set<int>> expected_tab_group_ids = {
      {group0->tab_group_id(), {0}}};

  EXPECT_EQ(tab_group_ids.size(), 1u);
  EXPECT_EQ(tab_group_ids, expected_tab_group_ids);
}

// Tests that `GetTabGroupsWithTabsToClose` correctly returns the tab groups
// with tabs within the time frame, in this case, the cached unrealized tab, but
// not the non cached realized tab. This test tests tabs all within the same
// group.
TEST_F(TabsClosureUtilTest,
       GetTabGroupsWithTabsToClose_OnlyOneTabForDeletion_SameGroup) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  WebStateIDToTime tabs =
      AppendUnrealizedWebstates(end_time - base::Minutes(1));
  const TabGroup* group0 = AppendTabGroup({0, 1});

  std::map<tab_groups::TabGroupId, std::set<int>> tab_group_ids =
      GetTabGroupsWithTabsToClose(
          web_state_list, begin_time, end_time,
          {{tabs.begin()->first, tabs.begin()->second}});
  std::map<tab_groups::TabGroupId, std::set<int>> expected_tab_group_ids = {
      {group0->tab_group_id(), {0}}};

  EXPECT_EQ(tab_group_ids.size(), 1u);
  EXPECT_EQ(tab_group_ids, expected_tab_group_ids);
}

// Tests that `CloseTabs` doesn't close unrealized tabs when none of the cached
// tabs for deletion matches with the ones in browser.
TEST_F(TabsClosureUtilTest, CloseTabs_UnrealizedAndNotMatchingTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  AppendUnrealizedWebstates(end_time - base::Minutes(1));

  CloseTabs(web_state_list, begin_time, end_time,
            {{web::WebStateID::NewUnique(), end_time - base::Minutes(1)}});

  EXPECT_EQ(web_state_list->count(), 2);
  EXPECT_EQ(web_state_list->GetWebStateAt(0), web_state0());
  EXPECT_EQ(web_state_list->GetWebStateAt(1), web_state1());
}

// Tests that `GetTabsToClose` correctly returns the unrealized tabs when none
// of the cached tabs for deletion matches with the ones in browser.
TEST_F(TabsClosureUtilTest, GetTabsToClose_UnrealizedAndNotMatchingTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  AppendUnrealizedWebstates(end_time - base::Minutes(1));

  std::set<web::WebStateID> web_state_ids = GetTabsToClose(
      web_state_list, begin_time, end_time,
      {{web::WebStateID::NewUnique(), end_time - base::Minutes(1)}});

  EXPECT_TRUE(web_state_ids.empty());
}

// Tests that `GetTabGroupsWithTabsToClose` correctly returns the tab groups
// with tabs within the time frame, in this case, the unrealized tabs when none
// of the cached tabs for deletion matches with the ones in browser. This test
// tests tabs all within the same group.
TEST_F(TabsClosureUtilTest,
       GetTabGroupsWithTabsToClose_UnrealizedAndNotMatchingTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  AppendUnrealizedWebstates(end_time - base::Minutes(1));
  AppendTabGroup({0, 1});

  std::map<tab_groups::TabGroupId, std::set<int>> tab_group_ids =
      GetTabGroupsWithTabsToClose(
          web_state_list, begin_time, end_time,
          {{web::WebStateID::NewUnique(), end_time - base::Minutes(1)}});

  EXPECT_TRUE(tab_group_ids.empty());
}

// Tests that `CloseTabs closes tabs within the range even if all are
// unrealized, none are cached but last active timestamp is within the selected
// range.
TEST_F(TabsClosureUtilTest, CloseTabs_UnrealizedNotCachedTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  web::FakeWebState* webstate = static_cast<web::FakeWebState*>(
      AppendWebState(/*realized=*/false, end_time - base::Minutes(1)));
  webstate->SetLastActiveTime(end_time - base::Minutes(1));

  CloseTabs(web_state_list, begin_time, end_time, {});

  EXPECT_TRUE(web_state_list->empty());
}

// Tests that `GetTabsToClose` returns tabs within the range even if all are
// unrealized, none are cached but last active timestamp is within the selected
// range.
TEST_F(TabsClosureUtilTest, GetTabsToClose_UnrealizedNotCachedTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  web::FakeWebState* webstate = static_cast<web::FakeWebState*>(
      AppendWebState(/*realized=*/false, end_time - base::Minutes(1)));
  webstate->SetLastActiveTime(end_time - base::Minutes(1));

  std::set<web::WebStateID> web_state_ids =
      GetTabsToClose(web_state_list, begin_time, end_time, {});

  EXPECT_EQ(web_state_ids.size(), 1u);
  EXPECT_TRUE(web_state_ids.contains(webstate->GetUniqueIdentifier()));
}

// Tests that `CloseTabs doesn't close tabs within the range unrelized but
// cached if they're pinned.
TEST_F(TabsClosureUtilTest, CloseTabs_UnrealizedCachedPinnedTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);
  base::Time last_navigation_time = end_time - base::Minutes(1);

  web::FakeWebState* webstate = static_cast<web::FakeWebState*>(AppendWebState(
      /*realized=*/false, last_navigation_time, /*pinned=*/true));

  CloseTabs(web_state_list, begin_time, end_time,
            {{webstate->GetUniqueIdentifier(), last_navigation_time}});

  EXPECT_EQ(web_state_list->count(), 1);
}

// Tests that `GetTabsToClose` doesn't return the tabs within the range
// unrealized but cached if they're pinned.
TEST_F(TabsClosureUtilTest, GetTabsToClose_UnrealizedCachedPinnedTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);
  base::Time last_navigation_time = end_time - base::Minutes(1);

  web::FakeWebState* webstate = static_cast<web::FakeWebState*>(AppendWebState(
      /*realized=*/false, last_navigation_time, /*pinned=*/true));

  std::set<web::WebStateID> web_state_ids =
      GetTabsToClose(web_state_list, begin_time, end_time,
                     {{webstate->GetUniqueIdentifier(), last_navigation_time}});

  EXPECT_TRUE(web_state_ids.empty());
}

// Tests that `CloseTabs doesn't close tabs within the range that are realized
// but not cached if they're pinned and realized.
TEST_F(TabsClosureUtilTest, CloseTabs_RealizedNotCachedPinnedTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);
  base::Time last_navigation_time = end_time - base::Minutes(1);

  web::FakeWebState* webstate = static_cast<web::FakeWebState*>(
      AppendWebState(/*realized=*/true, last_navigation_time, /*pinned=*/true));

  CloseTabs(web_state_list, begin_time, end_time,
            {{webstate->GetUniqueIdentifier(), last_navigation_time}});

  EXPECT_EQ(web_state_list->count(), 1);
}

// Tests that `GetTabsToClose` doesn't return the tabs within the range that are
// realized but not cached if they're pinned.
TEST_F(TabsClosureUtilTest, GetTabsToClose_RealizedNotCachedPinnedTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);
  base::Time last_navigation_time = end_time - base::Minutes(1);

  web::FakeWebState* webstate = static_cast<web::FakeWebState*>(AppendWebState(
      /*realized=*/true, last_navigation_time, /*pinned=*/true));

  std::set<web::WebStateID> web_state_ids =
      GetTabsToClose(web_state_list, begin_time, end_time,
                     {{webstate->GetUniqueIdentifier(), last_navigation_time}});

  EXPECT_TRUE(web_state_ids.empty());
}

// Tests that `GetTabGroupsWithTabsToClose` returns tab groups with tabs within
// the range even if all are unrealized, none are cached but last active
// timestamp is within the selected range.
TEST_F(TabsClosureUtilTest,
       GetTabGroupsWithTabsToClose_UnrealizedNotCachedTabs) {
  WebStateList* web_state_list = browser()->GetWebStateList();
  base::Time end_time = base::Time::Now();
  base::Time begin_time = end_time - base::Hours(1);

  web::FakeWebState* webstate = static_cast<web::FakeWebState*>(
      AppendWebState(/*realized=*/false, end_time - base::Minutes(1)));
  webstate->SetLastActiveTime(end_time - base::Minutes(1));
  const TabGroup* group = AppendTabGroup({0});

  std::map<tab_groups::TabGroupId, std::set<int>> tab_group_ids =
      GetTabGroupsWithTabsToClose(web_state_list, begin_time, end_time, {});
  std::map<tab_groups::TabGroupId, std::set<int>> expected_tab_group_ids = {
      {group->tab_group_id(), {0}}};

  EXPECT_EQ(tab_group_ids.size(), 1u);
  EXPECT_EQ(tab_group_ids, expected_tab_group_ids);
}