chromium/ash/wm/overview/birch/birch_bar_unittest.cc

// 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.

#include "ash/birch/birch_client.h"
#include "ash/birch/birch_data_provider.h"
#include "ash/birch/birch_item.h"
#include "ash/birch/birch_item_remover.h"
#include "ash/birch/birch_model.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/public/cpp/shelf_prefs.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/test/test_image_downloader.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/style/switch.h"
#include "ash/system/time/calendar_unittest_utils.h"
#include "ash/test/ash_test_base.h"
#include "ash/wallpaper/views/wallpaper_widget_controller.h"
#include "ash/wm/overview/birch/birch_bar_context_menu_model.h"
#include "ash/wm/overview/birch/birch_bar_controller.h"
#include "ash/wm/overview/birch/birch_bar_menu_model_adapter.h"
#include "ash/wm/overview/birch/birch_bar_view.h"
#include "ash/wm/overview/birch/birch_chip_button.h"
#include "ash/wm/overview/birch/birch_chip_context_menu_model.h"
#include "ash/wm/overview/overview_grid.h"
#include "ash/wm/overview/overview_grid_test_api.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/overview/overview_test_util.h"
#include "ash/wm/overview/overview_utils.h"
#include "ash/wm/splitview/split_view_types.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback_helpers.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_mock_clock_override.h"
#include "base/types/cxx23_to_underlying.h"
#include "ui/base/models/image_model.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/test/layer_animation_stopped_waiter.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/view_utils.h"

namespace ash {

namespace {

// Returns the pref service to use for Birch bar prefs.
PrefService* GetPrefService() {
  return Shell::Get()->session_controller()->GetPrimaryUserPrefService();
}

// Check if given suggestion types are shown in the bar chips.
bool HasSuggestionTypes(
    const std::vector<BirchItemType>& types,
    const std::vector<raw_ptr<BirchChipButtonBase>>& chips) {
  return base::ranges::all_of(types, [&](BirchItemType type) {
    return base::ranges::any_of(chips, [&](raw_ptr<BirchChipButtonBase> chip) {
      return chip->GetItem()->GetType() == type;
    });
  });
}

////////////////////////////////////////////////////////////////////////////////
// TestBirchItem:
class TestBirchItem : public BirchItem {
 public:
  TestBirchItem(const std::u16string& title,
                const std::u16string& subtitle,
                const std::optional<std::u16string>& addon_label,
                float ranking = 1.0f)
      : BirchItem(title, subtitle) {
    set_ranking(ranking);
    if (addon_label) {
      set_addon_label(*addon_label);
    }
  }
  TestBirchItem(const BirchItem&) = delete;
  const BirchItem& operator=(const BirchItem&) = delete;
  ~TestBirchItem() override = default;

  // BirchItem:
  BirchItemType GetType() const override { return BirchItemType::kTest; }
  std::string ToString() const override {
    return std::string("Test item ") + base::UTF16ToUTF8(title());
  }
  void PerformAction() override {}
  void LoadIcon(LoadIconCallback callback) const override {
    std::move(callback).Run(
        ui::ImageModel::FromVectorIcon(kSettingsIcon, SK_ColorBLACK, 20),
        SecondaryIconType::kNoIcon);
  }
};

////////////////////////////////////////////////////////////////////////////////
// TestBirchDataProvider:
// A test birch data provider that runs the data fetched callback with saved
// items when receives a data fetch request.
template <typename T>
class TestBirchDataProvider : public BirchDataProvider {
 public:
  using DataFetchedCallback =
      base::RepeatingCallback<void(const std::vector<T>&)>;

  TestBirchDataProvider(DataFetchedCallback data_fetched_callback,
                        const std::string& pref_name)
      : data_fetched_callback_(data_fetched_callback), pref_name_(pref_name) {}
  TestBirchDataProvider(const TestBirchDataProvider&) = delete;
  TestBirchDataProvider& operator=(const TestBirchDataProvider&) = delete;
  ~TestBirchDataProvider() override = default;

  void set_items(const std::vector<T>& items) { items_ = items; }

  void ClearItems() { items_.clear(); }

  // Trigger data provider changed callback.
  void RunDataProviderChangedCallback() { NotifyDataProviderChanged(); }

  // BirchDataProvider:
  void RequestBirchDataFetch() override {
    data_fetched_callback_.Run(
        (pref_name_.empty() || GetPrefService()->GetBoolean(pref_name_))
            ? items_
            : std::vector<T>());
  }

 private:
  DataFetchedCallback data_fetched_callback_;
  const std::string pref_name_;
  std::vector<T> items_;
};

////////////////////////////////////////////////////////////////////////////////
// TestBirchClient:
// A test birch client that returns the specific items to birch model.
class TestBirchClient : public BirchClient {
 public:
  explicit TestBirchClient(BirchModel* birch_model) {
    calendar_provider_ =
        std::make_unique<TestBirchDataProvider<BirchCalendarItem>>(
            base::BindRepeating(&TestBirchClient::HandleCalendarFetch,
                                base::Unretained(this)),
            prefs::kBirchUseCalendar);
    file_provider_ = std::make_unique<TestBirchDataProvider<BirchFileItem>>(
        base::BindRepeating(&BirchModel::SetFileSuggestItems,
                            base::Unretained(birch_model)),
        prefs::kBirchUseFileSuggest);
    tab_provider_ = std::make_unique<TestBirchDataProvider<BirchTabItem>>(
        base::BindRepeating(&BirchModel::SetRecentTabItems,
                            base::Unretained(birch_model)),
        prefs::kBirchUseChromeTabs);
    last_active_provider_ =
        std::make_unique<TestBirchDataProvider<BirchLastActiveItem>>(
            base::BindRepeating(&BirchModel::SetLastActiveItems,
                                base::Unretained(birch_model)),
            prefs::kBirchUseChromeTabs);
    most_visited_provider_ =
        std::make_unique<TestBirchDataProvider<BirchMostVisitedItem>>(
            base::BindRepeating(&BirchModel::SetMostVisitedItems,
                                base::Unretained(birch_model)),
            prefs::kBirchUseChromeTabs);
    self_share_provider_ =
        std::make_unique<TestBirchDataProvider<BirchSelfShareItem>>(
            base::BindRepeating(&BirchModel::SetSelfShareItems,
                                base::Unretained(birch_model)),
            prefs::kBirchUseChromeTabs);
    lost_media_provider_ =
        std::make_unique<TestBirchDataProvider<BirchLostMediaItem>>(
            base::BindRepeating(&BirchModel::SetLostMediaItems,
                                base::Unretained(birch_model)),
            prefs::kBirchUseLostMedia);
    release_notes_provider_ =
        std::make_unique<TestBirchDataProvider<BirchReleaseNotesItem>>(
            base::BindRepeating(&BirchModel::SetReleaseNotesItems,
                                base::Unretained(birch_model)),
            std::string());
    EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
  }
  TestBirchClient(const TestBirchClient&) = delete;
  TestBirchClient& operator=(const TestBirchClient&) = delete;
  ~TestBirchClient() override = default;

  void SetCalendarItems(const std::vector<BirchCalendarItem>& items) {
    calendar_provider_->set_items(items);
  }

  void SetFileSuggestItems(const std::vector<BirchFileItem>& items) {
    file_provider_->set_items(items);
  }

  void SetRecentTabsItems(const std::vector<BirchTabItem>& items) {
    tab_provider_->set_items(items);
  }

  void SetLastActiveItems(const std::vector<BirchLastActiveItem>& items) {
    last_active_provider_->set_items(items);
  }

  void SetMostVisitedItems(const std::vector<BirchMostVisitedItem>& items) {
    most_visited_provider_->set_items(items);
  }

  void SetReleaseNotesItems(const std::vector<BirchReleaseNotesItem>& items) {
    release_notes_provider_->set_items(items);
  }

  void SetSelfShareItems(const std::vector<BirchSelfShareItem>& items) {
    self_share_provider_->set_items(items);
  }

  void SetLostMediaItems(const std::vector<BirchLostMediaItem>& items) {
    lost_media_provider_->set_items(items);
  }

  // Clear all items.
  void Reset() {
    calendar_provider_->ClearItems();
    file_provider_->ClearItems();
    tab_provider_->ClearItems();
    last_active_provider_->ClearItems();
    release_notes_provider_->ClearItems();
    self_share_provider_->ClearItems();
    lost_media_provider_->ClearItems();
  }

  // BirchClient:
  BirchDataProvider* GetCalendarProvider() override {
    return calendar_provider_.get();
  }
  BirchDataProvider* GetFileSuggestProvider() override {
    return file_provider_.get();
  }
  BirchDataProvider* GetRecentTabsProvider() override {
    return tab_provider_.get();
  }
  BirchDataProvider* GetLastActiveProvider() override {
    return last_active_provider_.get();
  }
  BirchDataProvider* GetMostVisitedProvider() override {
    return most_visited_provider_.get();
  }
  BirchDataProvider* GetSelfShareProvider() override {
    return self_share_provider_.get();
  }
  BirchDataProvider* GetLostMediaProvider() override {
    return lost_media_provider_.get();
  }
  BirchDataProvider* GetReleaseNotesProvider() override {
    return release_notes_provider_.get();
  }
  void WaitForRefreshTokens(base::OnceClosure callback) override {
    std::move(callback).Run();
  }

  base::FilePath GetRemovedItemsFilePath() override {
    return test_dir_.GetPath();
  }

  void RemoveFileItemFromLauncher(const base::FilePath& path) override {}

  void GetFaviconImage(
      const GURL& url,
      const bool is_page_url,
      base::OnceCallback<void(const ui::ImageModel&)> callback) override {}

 private:
  void HandleCalendarFetch(const std::vector<BirchCalendarItem>& items) {
    // The production calendar provider sets both calendar items and attachment
    // items. Set both so the fetch can complete.
    Shell::Get()->birch_model()->SetCalendarItems(items);
    Shell::Get()->birch_model()->SetAttachmentItems({});
  }

  std::unique_ptr<TestBirchDataProvider<BirchCalendarItem>> calendar_provider_;
  std::unique_ptr<TestBirchDataProvider<BirchFileItem>> file_provider_;
  std::unique_ptr<TestBirchDataProvider<BirchTabItem>> tab_provider_;
  std::unique_ptr<TestBirchDataProvider<BirchLastActiveItem>>
      last_active_provider_;
  std::unique_ptr<TestBirchDataProvider<BirchMostVisitedItem>>
      most_visited_provider_;
  std::unique_ptr<TestBirchDataProvider<BirchSelfShareItem>>
      self_share_provider_;
  std::unique_ptr<TestBirchDataProvider<BirchLostMediaItem>>
      lost_media_provider_;
  std::unique_ptr<TestBirchDataProvider<BirchReleaseNotesItem>>
      release_notes_provider_;
  base::ScopedTempDir test_dir_;
};

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// BirchBarTest:
// The test class of birch bar with Forest feature enabled by default.
class BirchBarTest : public AshTestBase {
 public:
  BirchBarTest() {
    feature_list_.InitWithFeatures(
        {features::kForestFeature, features::kBirchWeather,
         features::kBirchCoral},
        {});
  }

  BirchBarTest(const BirchBarTest&) = delete;
  BirchBarTest& operator=(const BirchBarTest&) = delete;
  ~BirchBarTest() override = default;

  void SetUp() override {
    AshTestBase::SetUp();

    image_downloader_ = std::make_unique<ash::TestImageDownloader>();

    // Set prefs of all suggestion types and show suggestions enabled.
    for (const auto& pref_name :
         {prefs::kBirchShowSuggestions, prefs::kBirchUseCalendar,
          prefs::kBirchUseWeather, prefs::kBirchUseFileSuggest,
          prefs::kBirchUseChromeTabs, prefs::kBirchUseLostMedia,
          prefs::kBirchUseReleaseNotes, prefs::kBirchUseCoral}) {
      GetPrefService()->SetBoolean(pref_name, true);
    }

    // Create test birch client and weather provider.
    auto* birch_model = Shell::Get()->birch_model();
    birch_client_ = std::make_unique<TestBirchClient>(birch_model);
    birch_model->SetClientAndInit(birch_client_.get());
    auto weather_provider =
        std::make_unique<TestBirchDataProvider<BirchWeatherItem>>(
            base::BindRepeating(&BirchModel::SetWeatherItems,
                                base::Unretained(birch_model)),
            prefs::kBirchUseWeather);
    weather_provider_ = weather_provider.get();
    birch_model->OverrideWeatherProviderForTest(std::move(weather_provider));

    // TODO(make coral provider)
    auto coral_provider =
        std::make_unique<TestBirchDataProvider<BirchCoralItem>>(
            base::BindRepeating(&BirchModel::SetCoralItems,
                                base::Unretained(birch_model)),
            prefs::kBirchUseCoral);
    coral_provider_ = coral_provider.get();
    birch_model->OverrideCoralProviderForTest(std::move(coral_provider));

    base::RunLoop run_loop;
    Shell::Get()
        ->birch_model()
        ->GetItemRemoverForTest()
        ->SetProtoInitCallbackForTest(run_loop.QuitClosure());
    run_loop.Run();

    // Prepare a file item for test.
    SetFileItems(/*num=*/1);
  }

  void TearDown() override {
    Shell::Get()->birch_model()->SetClientAndInit(nullptr);
    weather_provider_ = nullptr;
    coral_provider_ = nullptr;
    birch_client_.reset();
    image_downloader_.reset();
    AshTestBase::TearDown();
  }

  std::unique_ptr<TestImageDownloader> image_downloader_;

 protected:
  // Adds a number of `num` file birch items to data source.
  void SetFileItems(size_t num) {
    std::vector<BirchFileItem> item_list;
    for (size_t i = 0; i < num; i++) {
      item_list.emplace_back(
          /*file_path=*/base::FilePath(base::StringPrintf("test path %lu", i)),
          "title",
          /*justification=*/u"suggestion",
          /*timestamp=*/base::Time(),
          /*file_id=*/base::StringPrintf("file_id_%lu", i),
          /*icon_url=*/"icon_url");
      item_list.back().set_ranking(1.0f);
    }
    birch_client_->SetFileSuggestItems(item_list);
  }

  // Adds a number of `num` calendar birch items to data source.
  void SetCalendarItems(size_t num) {
    std::vector<BirchCalendarItem> item_list;
    for (size_t i = 0; i < num; i++) {
      item_list.emplace_back(
          /*title=*/u"Event " + base::NumberToString16(i),
          /*start_time=*/base::Time(),
          /*end_time=*/base::Time(),
          /*calendar_url=*/GURL(),
          /*conference_url=*/GURL(),
          /*event_id=*/base::StringPrintf("event_id_%ld", i),
          /*all_day_event=*/false);
      item_list.back().set_ranking(1.0f);
    }
    birch_client_->SetCalendarItems(item_list);
  }

  // Adds a number of `num` tab birch items to data source.
  void SetTabItems(size_t num) {
    std::vector<BirchTabItem> item_list;
    for (size_t i = 0; i < num; i++) {
      item_list.emplace_back(
          /*title=*/u"tab", /*url*/ GURL("https://www.example.com/"),
          /*timestamp=*/base::Time(),
          /*favicon_url=*/GURL("https://www.favicon.com/"),
          /*session_name=*/"session",
          /*form_factor=*/BirchTabItem::DeviceFormFactor::kDesktop);
      item_list.back().set_ranking(1.0f);
    }
    birch_client_->SetRecentTabsItems(item_list);
  }

  // Adds `num` last active birch items to data source.
  void SetLastActiveItems(size_t num) {
    std::vector<BirchLastActiveItem> item_list;
    for (size_t i = 0; i < num; ++i) {
      item_list.emplace_back(u"last active", GURL("https://yahoo.com/"),
                             base::Time());
      item_list.back().set_ranking(1.0f);
    }
    birch_client_->SetLastActiveItems(item_list);
  }

  // Adds `num` most visited birch items to data source.
  void SetMostVisitedItems(size_t num) {
    std::vector<BirchMostVisitedItem> item_list;
    for (size_t i = 0; i < num; ++i) {
      item_list.emplace_back(u"most visited", GURL("https://google.com/"));
      item_list.back().set_ranking(1.0f);
    }
    birch_client_->SetMostVisitedItems(item_list);
  }

  // Adds a number of `num` self share birch items to data source.
  void SetSelfShareItems(size_t num) {
    std::vector<BirchSelfShareItem> item_list;
    for (size_t i = 0; i < num; i++) {
      item_list.emplace_back(
          /*guid=*/u"self share guid", /*title*/ u"self share tab",
          /*url=*/GURL("https://www.exampletwo.com/"),
          /*shared_time=*/base::Time(), /*device_name=*/u"my device",
          /*secondary_icon_type=*/SecondaryIconType::kTabFromDesktop,
          /*activation_callback=*/base::DoNothing());
      item_list.back().set_ranking(1.0f);
    }
    birch_client_->SetSelfShareItems(item_list);
  }

  // Adds `num` lost media items to data source.
  void SetLostMediaItems(size_t num,
                         const std::u16string& media_title = u"media title") {
    std::vector<BirchLostMediaItem> item_list;
    for (size_t i = 0; i < num; i++) {
      item_list.emplace_back(
          /*source_url=*/GURL("https://www.source.com/"),
          /*media_title=*/media_title,
          /*backup_icon=*/std::nullopt,
          /*secondary_icon_type=*/SecondaryIconType::kLostMediaVideo,
          /*activation_callback=*/base::DoNothing());
      item_list.back().set_ranking(1.0f);
    }
    birch_client_->SetLostMediaItems(item_list);
  }

  // Adds a number of `num` release notes birch items to data source.
  void SetReleaseNotesItems(size_t num) {
    std::vector<BirchReleaseNotesItem> item_list;
    for (size_t i = 0; i < num; i++) {
      item_list.emplace_back(/*release_notes_title=*/u"note",
                             /*release_notes_text=*/u"explore",
                             /*url=*/GURL("https://www.example.com/"),
                             /*first_seen=*/base::Time());
      item_list.back().set_ranking(1.0f);
    }
    birch_client_->SetReleaseNotesItems(item_list);
  }

  // Adds a number of `num` weather birch items to data source.
  void SetWeatherItems(size_t num) {
    std::vector<BirchWeatherItem> item_list;
    for (size_t i = 0; i < num; i++) {
      item_list.emplace_back(/*weather_description=*/u"cloudy",
                             /*temperature=*/72.f,
                             /*icon_url=*/GURL("http://icon.com/"));
      item_list.back().set_ranking(1.0f);
    }
    weather_provider_->set_items(item_list);
  }

  // Adds `num` coral items to data source.
  void SetCoralItems(size_t num) {
    std::vector<BirchCoralItem> item_list;
    for (size_t i = 0; i < num; i++) {
      item_list.emplace_back(
          /*coral_title=*/u"coral_title",
          /*coral_text=*/u"coral_text");
      item_list.back().set_ranking(1.0f);
    }
    coral_provider_->set_items(item_list);
  }
  std::unique_ptr<TestBirchClient> birch_client_;
  raw_ptr<TestBirchDataProvider<BirchWeatherItem>> weather_provider_;
  raw_ptr<TestBirchDataProvider<BirchCoralItem>> coral_provider_;

 private:
  base::test::ScopedFeatureList feature_list_;
  // Ensure base::Time::Now() is a fixed value.
  base::ScopedMockClockOverride mock_clock_override_;
};

// Tests that the birch bar will be shown in the normal Overview.
TEST_F(BirchBarTest, ShowBirchBar) {
  EnterOverview();
  EXPECT_TRUE(
      OverviewGridTestApi(Shell::GetPrimaryRootWindow()).birch_bar_view());
}

TEST_F(BirchBarTest, DoNotShowBirchBarForSecondaryUser) {
  // Sign in a secondary user.
  SimulateUserLogin("[email protected]");
  ASSERT_FALSE(Shell::Get()->session_controller()->IsUserPrimary());

  EnterOverview();
  EXPECT_FALSE(
      OverviewGridTestApi(Shell::GetPrimaryRootWindow()).birch_bar_view());
}

TEST_F(BirchBarTest, RecordsHistogramWhenChipsShown) {
  // Ensure a consistent timezone for this test.
  calendar_test_utils::ScopedLibcTimeZone scoped_timezone(
      "America/Los_Angeles");

  base::HistogramTester histograms;

  // Add an ongoing calendar event at the current time. This will create a
  // suggestion chip. Note that SetUp() also adds a file item.
  std::vector<BirchCalendarItem> items;
  items.emplace_back(u"Event", base::Time::Now() - base::Minutes(30),
                     base::Time::Now() + base::Minutes(30), GURL(), GURL(),
                     std::string(), /*all_day_event=*/false);
  birch_client_->SetCalendarItems(items);

  // Entering overview shows the birch bar.
  EnterOverview();
  base::RunLoop().RunUntilIdle();  // Wait for data fetch callback.

  // One impression was recorded for the birch bar.
  histograms.ExpectBucketCount("Ash.Birch.Bar.Impression", true, 1);

  // Two chips were shown.
  histograms.ExpectBucketCount("Ash.Birch.ChipCount", 2, 1);

  // One impression was recorded for each chip type.
  histograms.ExpectBucketCount("Ash.Birch.Chip.Impression",
                               BirchItemType::kFile, 1);
  histograms.ExpectBucketCount("Ash.Birch.Chip.Impression",
                               BirchItemType::kCalendar, 1);

  // Two rankings were recorded for the current time slot histogram.
  histograms.ExpectBucketCount("Ash.Birch.Ranking.1200to1700", 1, 1);
  histograms.ExpectBucketCount("Ash.Birch.Ranking.1200to1700", 12, 1);

  // The same ranking were recorded for the all-day total histogram.
  histograms.ExpectBucketCount("Ash.Birch.Ranking.Total", 1, 1);
  histograms.ExpectBucketCount("Ash.Birch.Ranking.Total", 12, 1);
}

// Tests that the birch bar will be hidden in the partial Overview with a split
// screen.
TEST_F(BirchBarTest, HideBirchBarInPartialSplitScreen) {
  // Create two windows.
  auto window_1 = CreateAppWindow(gfx::Rect(100, 100));
  // Need another window to keep partial Overview when `window_1` is snapped in
  // Overview session.
  auto window_2 = CreateAppWindow(gfx::Rect(100, 200));

  EnterOverview();

  // The birch bar should be shown in the normal Overview.
  aura::Window* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);
  EXPECT_TRUE(grid_test_api.birch_bar_view());

  // Snap window 1 to create a split screen and the birch bar should be hidden.
  SplitViewController::Get(root_window)
      ->SnapWindow(window_1.get(), SnapPosition::kPrimary);
  EXPECT_FALSE(grid_test_api.birch_bar_view());

  // Dismiss the split screen, the birch bar should be shown.
  window_1.reset();
  EXPECT_TRUE(grid_test_api.birch_bar_view());
}

TEST_F(BirchBarTest, ShowBirchBarInTabletMode) {
  EnterOverview();
  // Convert to Tablet mode, the birch bar should be shown in Overview mode.
  auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
  tablet_mode_controller->SetEnabledForTest(true);

  EnterOverview();
  EXPECT_TRUE(
      OverviewGridTestApi(Shell::GetPrimaryRootWindow()).birch_bar_view());
}

// Test that keyboard traversal on the birch bar works.
TEST_F(BirchBarTest, KeyboardTraversal) {
  SetCalendarItems(/*num=*/1);

  EnterOverview();
  auto birch_chips =
      OverviewGridTestApi(Shell::GetPrimaryRootWindow()).GetBirchChips();
  ASSERT_EQ(2u, birch_chips.size());

  // Tab through the default desk button and new desk button.
  PressAndReleaseKey(ui::VKEY_TAB);
  PressAndReleaseKey(ui::VKEY_TAB);

  // Tab through and verify the chips are focused.
  PressAndReleaseKey(ui::VKEY_TAB);
  EXPECT_TRUE(birch_chips[0]->HasFocus());
  PressAndReleaseKey(ui::VKEY_TAB);
  EXPECT_TRUE(birch_chips[1]->HasFocus());
}

// Test that there is no crash when SetIconImage was called after shutting down
// the chips.
TEST_F(BirchBarTest, NoCrashOnSettingIconAfterShutdown) {
  EnterOverview();
  const auto& chips =
      OverviewGridTestApi(Shell::GetPrimaryRootWindow()).GetBirchChips();
  ASSERT_EQ(1u, chips.size());

  BirchChipButton* chip = views::AsViewClass<BirchChipButton>(chips[0].get());

  ui::ScopedAnimationDurationScaleMode non_zero_duration(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);

  // Create a set icon callback to simulate the case of setting icon after
  // shutting down the chip.
  auto set_icon = base::BindOnce(&BirchChipButton::SetIconImage,
                                 chip->weak_factory_.GetWeakPtr(),
                                 ui::ImageModel(), SecondaryIconType::kNoIcon);

  ExitOverview();

  // The chip is shut down.
  EXPECT_FALSE(!!chip->GetItem());

  // Trigger setting icon.
  std::move(set_icon).Run();
}

// Tests that the lost media chip will get updated once the lost media data
// provider changed.
TEST_F(BirchBarTest, UpdateLostMediaChip) {
  // Clear all items and only add one lost media item.
  birch_client_->Reset();
  SetLostMediaItems(/*num=*/1, /*media_title=*/u"media 1");
  EnterOverview();

  // The bar have the only most media chip.
  const auto& chips =
      OverviewGridTestApi(Shell::GetPrimaryRootWindow()).GetBirchChips();
  ASSERT_EQ(1u, chips.size());

  auto* lost_media_chip = views::AsViewClass<BirchChipButton>(chips[0]);
  ASSERT_EQ(BirchItemType::kLostMedia, lost_media_chip->GetItem()->GetType());

  // The chip has expected title string.
  EXPECT_EQ(u"media 1", lost_media_chip->title_->GetText());

  // Update media item with a new title and notify the data change.
  SetLostMediaItems(/*num=*/1, /*media_title=*/u"media 2");
  auto* lost_media_provider =
      static_cast<TestBirchDataProvider<BirchLostMediaItem>*>(
          birch_client_->GetLostMediaProvider());
  lost_media_provider->RunDataProviderChangedCallback();

  // The chip title string should get updated.
  EXPECT_EQ(u"media 2", lost_media_chip->title_->GetText());

  // Clear the media item and notify data change.
  SetLostMediaItems(/*num=*/0);
  lost_media_provider->RunDataProviderChangedCallback();

  // The lost media chip should also be removed.
  EXPECT_EQ(0u, chips.size());
}

////////////////////////////////////////////////////////////////////////////////
// BirchBarMenuTest:
// The test class of birch bar context menu.
class BirchBarMenuTest : public BirchBarTest {
 public:
  BirchBarMenuTest() = default;
  BirchBarMenuTest(const BirchBarMenuTest&) = delete;
  BirchBarMenuTest& operator=(const BirchBarMenuTest&) = delete;
  ~BirchBarMenuTest() override = default;

  // BirchBarTest:
  void SetUp() override {
    BirchBarTest::SetUp();
    // Clear existing items.
    birch_client_->Reset();
    // Ensure screen is large enough to be able to click on all menu items.
    UpdateDisplay("1600x1200");
  }

 protected:
  BirchBarMenuModelAdapter* GetBirchBarChipMenuModelAdaper() {
    auto* overview_session = GetOverviewSession();
    CHECK(overview_session);
    return overview_session->birch_bar_controller()
        ->chip_menu_model_adapter_.get();
  }

  const std::vector<std::unique_ptr<BirchItem>>& GetBirchItemsInController()
      const {
    auto* overview_session = GetOverviewSession();
    CHECK(overview_session);
    return overview_session->birch_bar_controller()->items_;
  }
};

// Tests that removing a suggestion from context menu.
TEST_F(BirchBarMenuTest, RemoveChip) {
  // Create 5 suggestions with different item types.
  SetWeatherItems(/*num=*/1);
  SetCalendarItems(/*num=*/2);
  SetFileItems(/*num=*/1);
  SetTabItems(/*num=*/1);

  // Add another screen such that we will have two synchronized bar views.
  UpdateDisplay("1000x800,1000x800");

  // Enter Overview and check the two bar views are created.
  EnterOverview();

  aura::Window::Windows root_windows = Shell::Get()->GetAllRootWindows();
  ASSERT_EQ(root_windows.size(), 2u);
  auto* root_window_1 = root_windows[0].get();
  auto grid_test_api_1 = OverviewGridTestApi(root_window_1);
  auto* root_window_2 = root_windows[1].get();
  auto grid_test_api_2 = OverviewGridTestApi(root_window_2);

  EXPECT_TRUE(grid_test_api_1.birch_bar_view());
  EXPECT_TRUE(grid_test_api_2.birch_bar_view());

  // Cache the chips on two bar views.
  const auto& bar_1_chips = grid_test_api_1.GetBirchChips();
  const auto& bar_2_chips = grid_test_api_2.GetBirchChips();

  // Cached items in birch bar controller.
  const auto& cached_items = GetBirchItemsInController();

  // A functor to check if the current number of items are as expected and the
  // chips showing on the bar match the items
  auto chips_match_items = [&](size_t expected_num) {
    for (const auto& chips : {bar_1_chips, bar_2_chips}) {
      size_t item_num = cached_items.size();
      EXPECT_EQ(item_num, expected_num);
      EXPECT_EQ(
          chips.size(),
          std::min(static_cast<size_t>(BirchBarView::kMaxChipsNum), item_num));
      for (size_t i = 0; i < chips.size(); i++) {
        EXPECT_EQ(chips[i]->GetItem(), cached_items[i].get());
      }
    }
  };

  // Initially, we should have all 5 items in controller.
  chips_match_items(5);

  // Remove the third chip on the first bar view.
  // Right clicking on the second chip of first bar view to show the context
  // menu.
  RightClickOn(bar_1_chips[2]);

  auto* model_adapter = GetBirchBarChipMenuModelAdaper();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  // Hiding the third suggestion by selecting the corresponding menu item.
  const auto* hide_suggestion_item =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(0);
  EXPECT_EQ(hide_suggestion_item->GetCommand(),
            base::to_underlying(
                BirchChipContextMenuModel::CommandId::kHideSuggestion));

  LeftClickOn(hide_suggestion_item);

  // Check if the item is removed and the chips on both bars get updated.
  chips_match_items(4);

  // Remove the third chips on the second bar.
  RightClickOn(bar_2_chips[2]);

  model_adapter = GetBirchBarChipMenuModelAdaper();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  hide_suggestion_item =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(0);

  LeftClickOn(hide_suggestion_item);
  // Check if the item is removed and the chips on both bars get updated.
  chips_match_items(3);
}

// Tests that there is no crash when removing a chip.
TEST_F(BirchBarMenuTest, NoCrashOnRemovingChip) {
  // Create 4 suggestions with different item types.
  SetWeatherItems(/*num=*/1);
  SetCalendarItems(/*num=*/1);
  SetFileItems(/*num=*/1);
  SetTabItems(/*num=*/1);

  // Enter Overview and check the two bar views are created.
  EnterOverview();

  aura::Window* root_window = Shell::Get()->GetPrimaryRootWindow();
  OverviewGridTestApi grid_test_api(root_window);

  ASSERT_TRUE(grid_test_api.birch_bar_view());

  // Cache the chips on the bar.
  const auto& chips = grid_test_api.GetBirchChips();

  // There should be 4 chips on the bar.
  EXPECT_EQ(4u, chips.size());

  // Remove the third chip on the first bar view.
  // Right clicking on the second chip of first bar view to show the context
  // menu.
  RightClickOn(chips[2]);

  auto* model_adapter = GetBirchBarChipMenuModelAdaper();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  // Hiding the third suggestion by selecting the corresponding menu item.
  const auto* hide_suggestion_item =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(0);
  EXPECT_EQ(hide_suggestion_item->GetCommand(),
            base::to_underlying(
                BirchChipContextMenuModel::CommandId::kHideSuggestion));

  ui::LayerAnimationStoppedWaiter animation_waiter;
  ui::ScopedAnimationDurationScaleMode non_zero_duration(
      ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  LeftClickOn(hide_suggestion_item);
  animation_waiter.Wait(chips[2]->layer());

  // There should be 3 chips on the bar after animation without crash.
  EXPECT_EQ(3u, chips.size());
}

// Regression test to confirm there is no crash when removing a chip from a two
// rows bar.
TEST_F(BirchBarMenuTest, NoCrashOnRemovingChipFromTwoRowsBar) {
  // Set a narrow display whose short side can only hold two chips.
  UpdateDisplay("640x1080");

  // Add 5 calendar items.
  SetCalendarItems(/*num=*/5);

  EnterOverview();
  OverviewGridTestApi test_api =
      OverviewGridTestApi(Shell::GetPrimaryRootWindow());
  auto* birch_bar_view = test_api.birch_bar_view();

  // There should be 4 chips on two rows.
  EXPECT_EQ(4, birch_bar_view->GetChipsNum());
  EXPECT_EQ(2u, birch_bar_view->children().size());

  // Remove the second chip.
  RightClickOn(test_api.GetBirchChips()[1]);

  auto* model_adapter = GetBirchBarChipMenuModelAdaper();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  // Hiding the third suggestion by selecting the corresponding menu item.
  const auto* hide_suggestion_item =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(0);
  EXPECT_EQ(hide_suggestion_item->GetCommand(),
            base::to_underlying(
                BirchChipContextMenuModel::CommandId::kHideSuggestion));

  LeftClickOn(hide_suggestion_item);

  // There should still be 4 chips on two rows since the 5th item is filled in.
  EXPECT_EQ(4, birch_bar_view->GetChipsNum());
  EXPECT_EQ(2u, birch_bar_view->children().size());

  // Continue to remove the second chip.
  RightClickOn(test_api.GetBirchChips()[1]);

  model_adapter = GetBirchBarChipMenuModelAdaper();
  LeftClickOn(
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(0));

  // There should be 3 chips on two rows.
  EXPECT_EQ(3, birch_bar_view->GetChipsNum());
  EXPECT_EQ(2u, birch_bar_view->children().size());
}

// Tests showing/hiding suggestions from context menu.
TEST_F(BirchBarMenuTest, ShowHideBar) {
  // Create a suggestion for test.
  SetFileItems(/*num=*/1);

  // Add another screen such that we will have two synchronized bar views.
  UpdateDisplay("1000x800,1000x800");

  // Enter Overview and check the two bar views are created.
  EnterOverview();

  aura::Window::Windows root_windows = Shell::Get()->GetAllRootWindows();
  ASSERT_EQ(root_windows.size(), 2u);
  auto* root_window_1 = root_windows[0].get();
  auto grid_test_api_1 = std::make_unique<OverviewGridTestApi>(root_window_1);
  auto* root_window_2 = root_windows[1].get();
  auto grid_test_api_2 = std::make_unique<OverviewGridTestApi>(root_window_2);

  // The birch bars should be shown on both displays.
  EXPECT_TRUE(grid_test_api_1->birch_bar_view());
  EXPECT_TRUE(grid_test_api_2->birch_bar_view());

  auto* root_window_controller_1 =
      RootWindowController::ForWindow(root_window_1);
  // Right clicking on the wallpaper of the first display to show the context
  // menu.
  RightClickOn(root_window_controller_1->wallpaper_widget_controller()
                   ->GetWidget()
                   ->GetContentsView());

  auto* model_adapter =
      root_window_controller_1->menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  // Hiding the suggestions by clicking to the switch button.
  auto* hide_suggestions_item =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(0);
  auto switch_container = hide_suggestions_item->children()[0];
  Switch* switch_button = AsViewClass<Switch>(switch_container->children()[2]);
  EXPECT_TRUE(!!switch_button);
  EXPECT_TRUE(switch_button->GetIsOn());

  // Toggle the switch button to hide the suggestions.
  LeftClickOn(switch_button);

  // The birch bars should be hidden on both displays.
  EXPECT_FALSE(grid_test_api_1->birch_bar_view());
  EXPECT_FALSE(grid_test_api_2->birch_bar_view());

  // Exit and re-enter Overview. The birch bars should be hidden.
  grid_test_api_1.reset();
  grid_test_api_2.reset();
  ExitOverview();
  EnterOverview();

  grid_test_api_1 = std::make_unique<OverviewGridTestApi>(root_window_1);
  grid_test_api_2 = std::make_unique<OverviewGridTestApi>(root_window_2);

  // The birch bars should be shown on both displays.
  EXPECT_FALSE(grid_test_api_1->birch_bar_view());
  EXPECT_FALSE(grid_test_api_2->birch_bar_view());

  auto* root_window_controller_2 =
      RootWindowController::ForWindow(root_window_1);
  // Right clicking on the wallpaper of the second display to show the context
  // menu.
  RightClickOn(root_window_controller_2->wallpaper_widget_controller()
                   ->GetWidget()
                   ->GetContentsView());

  model_adapter = root_window_controller_2->menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  // Showing the suggestions by clicking to the switch button.
  hide_suggestions_item =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(0);
  switch_container = hide_suggestions_item->children()[0];
  switch_button = AsViewClass<Switch>(switch_container->children()[2]);
  EXPECT_FALSE(switch_button->GetIsOn());

  // Toggle the switch button to show the suggestions.
  LeftClickOn(switch_button);

  // The birch bars should be hidden on both displays.
  EXPECT_TRUE(grid_test_api_1->birch_bar_view());
  EXPECT_TRUE(grid_test_api_2->birch_bar_view());
}

// Tests customizing suggestions from context menu.
TEST_F(BirchBarMenuTest, CustomizeSuggestions) {
  // Create 4 suggestions, as the bar shows a maximum of 4 chips.
  SetWeatherItems(/*num=*/1);
  SetCalendarItems(/*num=*/1);
  SetFileItems(/*num=*/1);
  SetTabItems(/*num=*/1);

  // Set show suggestions initially.
  GetPrefService()->SetBoolean(prefs::kBirchShowSuggestions, true);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);

  // The birch bars should be shown.
  EXPECT_TRUE(grid_test_api.birch_bar_view());

  // Cache the chips.
  const auto& bar_chips = grid_test_api.GetBirchChips();

  // At the beginning, all types should be shown on the bar.
  EXPECT_TRUE(
      HasSuggestionTypes({BirchItemType::kWeather, BirchItemType::kCalendar,
                          BirchItemType::kFile, BirchItemType::kTab},
                         bar_chips));

  auto* root_window_controller = RootWindowController::ForWindow(root_window);
  // Right clicking on the wallpaper of the first display to show the context
  // menu.
  RightClickOn(root_window_controller->wallpaper_widget_controller()
                   ->GetWidget()
                   ->GetContentsView());

  auto* model_adapter =
      root_window_controller->menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  base::flat_map<BirchItemType, views::MenuItemView*> type_to_item;
  auto* sub_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* weather_item = sub_menu->GetMenuItemAt(1);
  EXPECT_EQ(weather_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kWeatherSuggestions));
  type_to_item[BirchItemType::kWeather] = weather_item;

  auto* calendar_item = sub_menu->GetMenuItemAt(2);
  EXPECT_EQ(calendar_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kCalendarSuggestions));
  type_to_item[BirchItemType::kCalendar] = calendar_item;

  auto* file_item = sub_menu->GetMenuItemAt(3);
  EXPECT_EQ(file_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kDriveSuggestions));
  type_to_item[BirchItemType::kFile] = file_item;

  auto* tab_item = sub_menu->GetMenuItemAt(4);
  EXPECT_EQ(tab_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kChromeTabSuggestions));
  type_to_item[BirchItemType::kTab] = tab_item;

  // Deselect all types of suggestions one by one.
  for (auto type : {BirchItemType::kWeather, BirchItemType::kCalendar,
                    BirchItemType::kFile, BirchItemType::kTab}) {
    LeftClickOn(type_to_item[type]);
    EXPECT_FALSE(HasSuggestionTypes({type}, bar_chips));
  }

  // There is no suggestions showing on the bar.
  EXPECT_TRUE(bar_chips.empty());

  // Re-select all types of suggestions one by one.
  std::vector<BirchItemType> new_types;
  for (auto type : {BirchItemType::kWeather, BirchItemType::kCalendar,
                    BirchItemType::kFile, BirchItemType::kTab}) {
    LeftClickOn(type_to_item[type]);
    EXPECT_TRUE(HasSuggestionTypes(new_types, bar_chips));
  }
}

// The bar shows a maximum of 4 suggestion chips. The above test verifies
// customizing the first 4 suggestion types; this test verifies the rest.
TEST_F(BirchBarMenuTest, CustomizeSuggestionsExtended) {
  SetLastActiveItems(/*num=*/1);
  SetMostVisitedItems(/*num=*/1);
  SetSelfShareItems(/*num=*/1);
  SetLostMediaItems(/*num=*/1);

  // Set show suggestions initially.
  GetPrefService()->SetBoolean(prefs::kBirchShowSuggestions, true);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);
  const auto& bar_chips = grid_test_api.GetBirchChips();

  // At the beginning, all types should be shown on the bar.
  EXPECT_TRUE(HasSuggestionTypes(
      {BirchItemType::kLastActive, BirchItemType::kMostVisited,
       BirchItemType::kSelfShare, BirchItemType::kLostMedia},
      bar_chips));

  auto* root_window_controller = RootWindowController::ForWindow(root_window);
  // Right clicking on the wallpaper of the first display to show the context
  // menu.
  RightClickOn(root_window_controller->wallpaper_widget_controller()
                   ->GetWidget()
                   ->GetContentsView());
  auto* model_adapter =
      root_window_controller->menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  auto* sub_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* tab_item = sub_menu->GetMenuItemAt(4);
  EXPECT_EQ(tab_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kChromeTabSuggestions));

  // Deselect tab suggestions.
  LeftClickOn(tab_item);

  // Only media is on the bar.
  EXPECT_TRUE(HasSuggestionTypes({BirchItemType::kLostMedia}, bar_chips));
  EXPECT_FALSE(HasSuggestionTypes(
      {BirchItemType::kLastActive, BirchItemType::kMostVisited,
       BirchItemType::kSelfShare},
      bar_chips));

  // Find the media suggestions menu item.
  auto* media_item = sub_menu->GetMenuItemAt(5);
  EXPECT_EQ(media_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kMediaSuggestions));

  // Deselect media suggestions.
  LeftClickOn(media_item);

  // There are no suggestions showing on the bar.
  EXPECT_TRUE(bar_chips.empty());

  // Re-select media suggestions.
  LeftClickOn(media_item);
  EXPECT_TRUE(HasSuggestionTypes({BirchItemType::kLostMedia}, bar_chips));

  // Re-select tab suggestions.
  LeftClickOn(tab_item);
  EXPECT_TRUE(HasSuggestionTypes(
      {BirchItemType::kLastActive, BirchItemType::kMostVisited,
       BirchItemType::kSelfShare, BirchItemType::kLostMedia},
      bar_chips));
}

// The bar shows a maximum of 4 suggestion chips. The above tests verifies
// customizing the first 8 suggestion types; this test verifies the rest.
TEST_F(BirchBarMenuTest, CustomizeSuggestionsExtended2) {
  SetCoralItems(/*num=*/1);

  // Set show suggestions initially.
  GetPrefService()->SetBoolean(prefs::kBirchShowSuggestions, true);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);
  const auto& bar_chips = grid_test_api.GetBirchChips();

  // At the beginning, all types should be shown on the bar.
  EXPECT_TRUE(HasSuggestionTypes({BirchItemType::kCoral}, bar_chips));

  auto* root_window_controller = RootWindowController::ForWindow(root_window);
  // Right clicking on the wallpaper of the first display to show the context
  // menu.
  RightClickOn(root_window_controller->wallpaper_widget_controller()
                   ->GetWidget()
                   ->GetContentsView());
  auto* model_adapter =
      root_window_controller->menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  auto* sub_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* coral_item = sub_menu->GetMenuItemAt(6);
  EXPECT_EQ(coral_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kCoralSuggestions));

  // Deselect coral suggestion.
  LeftClickOn(coral_item);
  EXPECT_FALSE(HasSuggestionTypes({BirchItemType::kCoral}, bar_chips));

  // There is no suggestions showing on the bar.
  EXPECT_TRUE(bar_chips.empty());

  // Re-select coral suggestion.
  LeftClickOn(coral_item);
  EXPECT_TRUE(HasSuggestionTypes({BirchItemType::kCoral}, bar_chips));
}

// Tests resetting suggestions from context menu.
TEST_F(BirchBarMenuTest, ResetSuggestions) {
  // Create 4 suggestions, one for each customizable suggestion type.
  SetCalendarItems(/*num=*/1);
  SetFileItems(/*num=*/1);
  SetTabItems(/*num=*/1);
  SetSelfShareItems(/*num*/ 1);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);

  // The birch bars should be shown.
  EXPECT_TRUE(grid_test_api.birch_bar_view());

  // Cache the chips.
  const auto& bar_chips = grid_test_api.GetBirchChips();

  // The functor to check if given suggestion types are shown in the bar chips.
  auto has_suggestion_types =
      [](const std::vector<BirchItemType>& types,
         const std::vector<raw_ptr<BirchChipButtonBase>>& chips) -> bool {
    return base::ranges::all_of(types, [&](BirchItemType type) {
      return base::ranges::any_of(chips,
                                  [&](raw_ptr<BirchChipButtonBase> chip) {
                                    return chip->GetItem()->GetType() == type;
                                  });
    });
  };

  // Disable the calendar and file suggestions such that only tab and
  // self share suggestions are shown.
  auto* pref_service = GetPrefService();
  pref_service->SetBoolean(prefs::kBirchUseCalendar, false);
  pref_service->SetBoolean(prefs::kBirchUseFileSuggest, false);

  EXPECT_EQ(2u, bar_chips.size());
  EXPECT_TRUE(has_suggestion_types(
      {BirchItemType::kTab, BirchItemType::kSelfShare}, bar_chips));

  auto* root_window_controller = RootWindowController::ForWindow(root_window);
  // Right clicking on the wallpaper of the first display to show the context
  // menu.
  RightClickOn(root_window_controller->wallpaper_widget_controller()
                   ->GetWidget()
                   ->GetContentsView());

  auto* model_adapter =
      root_window_controller->menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  auto* sub_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* reset_item = sub_menu->GetMenuItemAt(7);
  EXPECT_EQ(reset_item->GetCommand(),
            base::to_underlying(BirchBarContextMenuModel::CommandId::kReset));

  // Clicking on the reset button to enable all suggestions pref and all four
  // types of suggestion chips should be shown on the bar.
  LeftClickOn(reset_item);
  EXPECT_TRUE(pref_service->GetBoolean(prefs::kBirchUseCalendar));
  EXPECT_TRUE(pref_service->GetBoolean(prefs::kBirchUseFileSuggest));
  EXPECT_TRUE(pref_service->GetBoolean(prefs::kBirchUseChromeTabs));

  EXPECT_EQ(4u, bar_chips.size());
  EXPECT_TRUE(
      has_suggestion_types({BirchItemType::kCalendar, BirchItemType::kFile,
                            BirchItemType::kTab, BirchItemType::kSelfShare},
                           bar_chips));
}

// The bar shows a maximum of 4 suggestion chips. The above test verifies
// resetting the first 4 suggestion types; this test verifies the rest.
TEST_F(BirchBarMenuTest, ResetSuggestionsExtended) {
  SetLastActiveItems(/*num=*/1);
  SetMostVisitedItems(/*num=*/1);
  SetLostMediaItems(/*num=*/1);
  SetCoralItems(/*num=*/1);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);
  const auto& bar_chips = grid_test_api.GetBirchChips();

  // Disable the Chrome Tabs and media suggestions such that nothing is shown.
  auto* pref_service = GetPrefService();
  pref_service->SetBoolean(prefs::kBirchUseChromeTabs, false);
  pref_service->SetBoolean(prefs::kBirchUseLostMedia, false);
  pref_service->SetBoolean(prefs::kBirchUseCoral, false);

  EXPECT_EQ(0u, bar_chips.size());

  auto* root_window_controller = RootWindowController::ForWindow(root_window);
  // Right clicking on the wallpaper of the first display to show the context
  // menu.
  RightClickOn(root_window_controller->wallpaper_widget_controller()
                   ->GetWidget()
                   ->GetContentsView());

  auto* model_adapter =
      root_window_controller->menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  auto* sub_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* reset_item = sub_menu->GetMenuItemAt(7);
  EXPECT_EQ(reset_item->GetCommand(),
            base::to_underlying(BirchBarContextMenuModel::CommandId::kReset));

  // Clicking on the reset button to enable all suggestions pref and all types
  // of suggestion chips should be shown on the bar.
  LeftClickOn(reset_item);
  EXPECT_TRUE(pref_service->GetBoolean(prefs::kBirchUseChromeTabs));
  EXPECT_TRUE(pref_service->GetBoolean(prefs::kBirchUseLostMedia));
  EXPECT_TRUE(pref_service->GetBoolean(prefs::kBirchUseCoral));

  EXPECT_EQ(4u, bar_chips.size());
  EXPECT_TRUE(HasSuggestionTypes(
      {BirchItemType::kLastActive, BirchItemType::kMostVisited,
       BirchItemType::kLostMedia, BirchItemType::kCoral},
      bar_chips));
}

TEST_F(BirchBarMenuTest, ToggleFahrenheitCelsiusPref) {
  // The pref defaults to Fahrenheit.
  EXPECT_FALSE(GetPrefService()->GetBoolean(prefs::kBirchUseCelsius));

  // Show the birch bar with a weather item.
  SetWeatherItems(/*num=*/1);
  EnterOverview();

  // Get the overview grid test api.
  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);
  EXPECT_TRUE(grid_test_api.birch_bar_view());

  // Get the weather chip.
  raw_ptr<BirchChipButtonBase> chip = grid_test_api.GetBirchChips()[0];
  ASSERT_TRUE(chip);

  // Right-click on the chip to show the context menu.
  RightClickOn(chip);
  chip = nullptr;  // Avoid dangling pointer later.
  auto* model_adapter =
      BirchBarController::Get()->chip_menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  // Click on the menu item to toggle temperature units.
  auto* chip_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* toggle_temperature_units = chip_menu->GetMenuItemAt(2);
  EXPECT_EQ(toggle_temperature_units->GetCommand(),
            base::to_underlying(
                BirchChipContextMenuModel::CommandId::kToggleTemperatureUnits));
  LeftClickOn(toggle_temperature_units);

  // The pref is now set to use celsius.
  EXPECT_TRUE(GetPrefService()->GetBoolean(prefs::kBirchUseCelsius));
}

// Tests that there is no crash if hiding the suggestions by toggle the switch
// button in chip's submenu.
TEST_F(BirchBarMenuTest, NoCrashHideSuggestionsByChipSubmenu) {
  // Set show suggestions initially.
  GetPrefService()->SetBoolean(prefs::kBirchShowSuggestions, true);

  // Create a chip.
  SetCalendarItems(/*num=*/1);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);

  // The birch bars should be shown.
  EXPECT_TRUE(grid_test_api.birch_bar_view());

  // Right clicking on a chip to show the chip menu.
  RightClickOn(grid_test_api.GetBirchChips()[0]);
  auto* model_adapter =
      BirchBarController::Get()->chip_menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  auto* chip_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* customize_suggestions_item = chip_menu->GetMenuItemAt(2);
  LeftClickOn(customize_suggestions_item);

  auto* sub_menu = customize_suggestions_item->GetSubmenu();
  auto* sub_show_suggestions_item = sub_menu->GetMenuItemAt(0);
  EXPECT_EQ(sub_show_suggestions_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kShowSuggestions));
  auto switch_container = sub_show_suggestions_item->children()[0];
  auto* switch_button = AsViewClass<Switch>(switch_container->children()[2]);
  LeftClickOn(switch_button);
  EXPECT_FALSE(grid_test_api.birch_bar_view());
}

// Tests that there is no crash if customizing the suggestions by selecting the
// checkboxes in chip's submenu.
TEST_F(BirchBarMenuTest, NoCrashCustomizeSuggestionsByChipSubmenu) {
  // Set show suggestions and enable weather suggestions initially.
  GetPrefService()->SetBoolean(prefs::kBirchShowSuggestions, true);

  // Create 3 suggestions, one for each customizable suggestion type.
  SetCalendarItems(/*num=*/1);
  SetFileItems(/*num=*/1);
  SetTabItems(/*num=*/1);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);

  // The birch bars should be shown.
  EXPECT_TRUE(grid_test_api.birch_bar_view());

  // Right clicking on a chip to show the chip menu.
  RightClickOn(grid_test_api.GetBirchChips()[0]);
  auto* model_adapter =
      BirchBarController::Get()->chip_menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  auto* chip_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* customize_suggestions_item = chip_menu->GetMenuItemAt(2);
  LeftClickOn(customize_suggestions_item);

  auto* sub_menu = customize_suggestions_item->GetSubmenu();
  auto* calendar_item = sub_menu->GetMenuItemAt(2);
  EXPECT_EQ(calendar_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kCalendarSuggestions));
  auto* calendar_checkbox =
      views::AsViewClass<Checkbox>(calendar_item->children()[0]);

  // Deselect the calendar.
  LeftClickOn(calendar_checkbox);
  EXPECT_FALSE(GetPrefService()->GetBoolean(prefs::kBirchUseCalendar));

  // The menu will be closed after selection.
  EXPECT_FALSE(
      BirchBarController::Get()->chip_menu_model_adapter_for_testing());

  RightClickOn(grid_test_api.GetBirchChips()[0]);
  model_adapter =
      BirchBarController::Get()->chip_menu_model_adapter_for_testing();
  EXPECT_TRUE(model_adapter->IsShowingMenu());

  customize_suggestions_item =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(2);
  LeftClickOn(customize_suggestions_item);

  calendar_item = customize_suggestions_item->GetSubmenu()->GetMenuItemAt(2);
  calendar_checkbox =
      views::AsViewClass<Checkbox>(calendar_item->children()[0]);

  // Select the calendar.
  LeftClickOn(calendar_checkbox);
  EXPECT_TRUE(GetPrefService()->GetBoolean(prefs::kBirchUseCalendar));
}

// Tests hiding certain types of suggestions from context menu.
TEST_F(BirchBarMenuTest, HideSuggestionTypes) {
  // Create suggestions, at least one for each customizable suggestion type.
  SetWeatherItems(/*num=*/1);
  SetCalendarItems(/*num=*/2);
  SetFileItems(/*num=*/2);
  SetTabItems(/*num=*/2);
  SetLostMediaItems(/*num=*/2);

  // Set show suggestions initially.
  GetPrefService()->SetBoolean(prefs::kBirchShowSuggestions, true);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);

  // The birch bars should be shown.
  ASSERT_TRUE(grid_test_api.birch_bar_view());

  // Cache the chips.
  const auto& bar_chips = grid_test_api.GetBirchChips();

  while (bar_chips.size()) {
    BirchChipButtonBase* chip = bar_chips[0];

    SCOPED_TRACE(testing::Message()
                 << "Hide type of suggestion: " << chip->GetItem()->ToString());

    // Manually set birch bar bounds to trigger layout.
    views::test::RunScheduledLayout(grid_test_api.birch_bar_view());

    // Right clicking on a chip to show the chip menu.
    RightClickOn(chip);
    auto* model_adapter =
        BirchBarController::Get()->chip_menu_model_adapter_for_testing();
    EXPECT_TRUE(model_adapter->IsShowingMenu());

    auto* chip_menu = model_adapter->root_for_testing()->GetSubmenu();

    const BirchItemType item_type = chip->GetItem()->GetType();
    int hide_suggestions_type_idx = 1;
    if (item_type == BirchItemType::kWeather ||
        item_type == BirchItemType::kLostMedia) {
      hide_suggestions_type_idx = 0;
    }

    auto* hide_suggestions_item =
        chip_menu->GetMenuItemAt(hide_suggestions_type_idx);

    int hide_suggestions_item_id = -1;
    std::string pref_name;
    switch (item_type) {
      case BirchItemType::kWeather:
        hide_suggestions_item_id = base::to_underlying(
            BirchChipContextMenuModel::CommandId::kHideWeatherSuggestions);
        pref_name = prefs::kBirchUseWeather;
        break;
      case BirchItemType::kCalendar:
        hide_suggestions_item_id = base::to_underlying(
            BirchChipContextMenuModel::CommandId::kHideCalendarSuggestions);
        pref_name = prefs::kBirchUseCalendar;
        break;
      case BirchItemType::kFile:
        hide_suggestions_item_id = base::to_underlying(
            BirchChipContextMenuModel::CommandId::kHideDriveSuggestions);
        pref_name = prefs::kBirchUseFileSuggest;
        break;
      case BirchItemType::kTab:
        hide_suggestions_item_id = base::to_underlying(
            BirchChipContextMenuModel::CommandId::kHideChromeTabSuggestions);
        pref_name = prefs::kBirchUseChromeTabs;
        break;
      case BirchItemType::kLostMedia:
        hide_suggestions_item_id = base::to_underlying(
            BirchChipContextMenuModel::CommandId::kHideMediaSuggestions);
        pref_name = prefs::kBirchUseLostMedia;
        break;
      default:
        break;
    }

    EXPECT_EQ(hide_suggestions_item_id, hide_suggestions_item->GetCommand());
    EXPECT_TRUE(GetPrefService()->GetBoolean(pref_name));

    LeftClickOn(hide_suggestions_item);

    // Corresponding type of suggestions should be hidden from the bar.
    EXPECT_FALSE(HasSuggestionTypes({item_type}, bar_chips));
    // Corresponding type of user prefs should be disabled.
    EXPECT_FALSE(GetPrefService()->GetBoolean(pref_name));
  }
}

// Tests that the checkboxes in the context menu have the correct accessible
// name. Regression test for http://b/354925434.
TEST_F(BirchBarMenuTest, CheckboxAccessibleName) {
  EnterOverview();

  auto* root_window_controller = Shell::GetPrimaryRootWindowController();
  // Right click on the wallpaper of the first display to show the context menu.
  RightClickOn(root_window_controller->wallpaper_widget_controller()
                   ->GetWidget()
                   ->GetContentsView());

  auto* model_adapter =
      root_window_controller->menu_model_adapter_for_testing();
  ASSERT_TRUE(model_adapter->IsShowingMenu());

  // Ensure that the second item is a checkbox.
  views::MenuItemView* item_view =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(1);
  ASSERT_TRUE(views::IsViewClass<Checkbox>(item_view->children()[0]));

  // `views::MenuItemView` calculates its accessible name by calling
  // `GetAccessibleNodeData()`. Test that it returns the correct string.
  ui::AXNodeData node_data;
  item_view->GetAccessibleNodeData(&node_data);
  EXPECT_EQ(u"Weather",
            node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
}

// Tests that hide the birch bar from chip context menu with tapping works.
TEST_F(BirchBarMenuTest, HideSuggestionsByTappingChipMenu) {
  // Set show suggestions initially.
  GetPrefService()->SetBoolean(prefs::kBirchShowSuggestions, true);

  // Create a chip.
  SetCalendarItems(/*num=*/1);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);

  // The birch bars should be shown.
  ASSERT_TRUE(grid_test_api.birch_bar_view());

  // Right clicking on a chip to show the chip menu.
  RightClickOn(grid_test_api.GetBirchChips()[0]);
  auto* model_adapter =
      BirchBarController::Get()->chip_menu_model_adapter_for_testing();
  ASSERT_TRUE(model_adapter->IsShowingMenu());

  auto* chip_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* customize_suggestions_item = chip_menu->GetMenuItemAt(2);
  GestureTapOn(customize_suggestions_item);

  auto* sub_menu = customize_suggestions_item->GetSubmenu();
  auto* sub_show_suggestions_item = sub_menu->GetMenuItemAt(0);
  EXPECT_EQ(sub_show_suggestions_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kShowSuggestions));
  auto switch_container = sub_show_suggestions_item->children()[0];
  auto* switch_button = AsViewClass<Switch>(switch_container->children()[2]);
  GestureTapOn(switch_button);
  EXPECT_FALSE(grid_test_api.birch_bar_view());
}

// Tests that customize the birch bar from chip context menu with tapping works.
TEST_F(BirchBarMenuTest, CustomizeSuggestionsByTappingChipMenu) {
  // Set show suggestions and enable weather suggestions initially.
  GetPrefService()->SetBoolean(prefs::kBirchShowSuggestions, true);

  // Create 3 suggestions, one for each customizable suggestion type.
  SetCalendarItems(/*num=*/1);
  SetFileItems(/*num=*/1);
  SetTabItems(/*num=*/1);

  // Enter Overview and check a bar view is created.
  EnterOverview();

  auto* root_window = Shell::GetPrimaryRootWindow();
  auto grid_test_api = OverviewGridTestApi(root_window);

  // The birch bars should be shown.
  ASSERT_TRUE(grid_test_api.birch_bar_view());

  // Right clicking on a chip to show the chip menu.
  RightClickOn(grid_test_api.GetBirchChips()[0]);
  auto* model_adapter =
      BirchBarController::Get()->chip_menu_model_adapter_for_testing();
  ASSERT_TRUE(model_adapter->IsShowingMenu());

  auto* chip_menu = model_adapter->root_for_testing()->GetSubmenu();
  auto* customize_suggestions_item = chip_menu->GetMenuItemAt(2);
  GestureTapOn(customize_suggestions_item);

  auto* sub_menu = customize_suggestions_item->GetSubmenu();
  auto* calendar_item = sub_menu->GetMenuItemAt(2);
  EXPECT_EQ(calendar_item->GetCommand(),
            base::to_underlying(
                BirchBarContextMenuModel::CommandId::kCalendarSuggestions));
  auto* calendar_checkbox =
      views::AsViewClass<Checkbox>(calendar_item->children()[0]);

  // Deselect the calendar.
  GestureTapOn(calendar_checkbox);
  EXPECT_FALSE(GetPrefService()->GetBoolean(prefs::kBirchUseCalendar));

  // The menu will be closed after selection.
  EXPECT_FALSE(
      BirchBarController::Get()->chip_menu_model_adapter_for_testing());

  RightClickOn(grid_test_api.GetBirchChips()[0]);
  model_adapter =
      BirchBarController::Get()->chip_menu_model_adapter_for_testing();
  ASSERT_TRUE(model_adapter->IsShowingMenu());

  customize_suggestions_item =
      model_adapter->root_for_testing()->GetSubmenu()->GetMenuItemAt(2);
  GestureTapOn(customize_suggestions_item);

  calendar_item = customize_suggestions_item->GetSubmenu()->GetMenuItemAt(2);
  calendar_checkbox =
      views::AsViewClass<Checkbox>(calendar_item->children()[0]);

  // Select the calendar.
  GestureTapOn(calendar_checkbox);
  EXPECT_TRUE(GetPrefService()->GetBoolean(prefs::kBirchUseCalendar));
}

// The parameter structure for birch bar responsive layout tests.
struct LayoutTestParams {
  gfx::Size display_size;
  ShelfAlignment shelf_alignment;
  //  Expected birch bar bounds with 1 to 4 chips in landscape mode.
  std::vector<gfx::Rect> expected_landscape_bounds;
  // Expected birch bar bounds with 1 to 4 chips in portrait mode.
  std::vector<gfx::Rect> expected_portrait_bounds;
};

////////////////////////////////////////////////////////////////////////////////
// BirchBarLayoutTest:
// The test class of birch bar layout.
class BirchBarLayoutTest
    : public BirchBarTest,
      public testing::WithParamInterface<LayoutTestParams> {
 public:
  BirchBarLayoutTest() = default;
  BirchBarLayoutTest(const BirchBarLayoutTest&) = delete;
  BirchBarLayoutTest& operator=(const BirchBarLayoutTest&) = delete;
  ~BirchBarLayoutTest() override = default;

  // BirchBarTest:
  void SetUp() override {
    BirchBarTest::SetUp();

    // Clear existing items.
    birch_client_->Reset();

    // Set display size and shelf alignment according to the parameter.
    const LayoutTestParams params = GetParam();
    UpdateDisplay(params.display_size.ToString());

    // Here, we simulate changing the shelf alignment from context menu which
    // will update the user's pref. Otherwise, it will exit the Overview and
    // reset shelf alignment when we rotate the display.
    const int64_t display_id =
        display::Screen::GetScreen()->GetPrimaryDisplay().id();
    scoped_internal_display_id_ =
        std::make_unique<display::test::ScopedSetInternalDisplayId>(
            Shell::Get()->display_manager(), display_id);
    SetShelfAlignmentPref(
        Shell::Get()->session_controller()->GetPrimaryUserPrefService(),
        display_id, params.shelf_alignment);
  }

 private:
  std::unique_ptr<display::test::ScopedSetInternalDisplayId>
      scoped_internal_display_id_;
};

const LayoutTestParams kLayoutTestParams[] = {
    // The narrow display whose shorter side can only hold up to 2 chips.
    {/*display_size=*/gfx::Size(1080, 640),
     ShelfAlignment::kBottom,
     /*expected_landscape_bounds=*/
     {gfx::Rect(416, 512, 248, 64), gfx::Rect(288, 512, 504, 64),
      gfx::Rect(160, 512, 760, 64), gfx::Rect(32, 512, 1016, 64)},
     /*expected_portrait_bounds=*/
     {gfx::Rect(196, 952, 248, 64), gfx::Rect(68, 952, 504, 64),
      gfx::Rect(68, 880, 504, 136), gfx::Rect(68, 880, 504, 136)}},

    // The narrow display with shelf left aligned.
    {/*display_size=*/gfx::Size(1080, 640),
     ShelfAlignment::kLeft,
     /*expected_landscape_bounds=*/
     {gfx::Rect(436, 560, 240, 64), gfx::Rect(312, 560, 488, 64),
      gfx::Rect(188, 560, 736, 64), gfx::Rect(64, 560, 984, 64)},
     /*expected_portrait_bounds=*/
     {gfx::Rect(216, 1000, 240, 64), gfx::Rect(92, 1000, 488, 64),
      gfx::Rect(92, 928, 488, 136), gfx::Rect(92, 928, 488, 136)}},

    // The nearly squared display whose shorter side can hold up to 3 chips.
    {/*display_size=*/gfx::Size(1200, 1000),
     ShelfAlignment::kBottom,
     /*expected_landscape_bounds=*/
     {gfx::Rect(461, 872, 278, 64), gfx::Rect(318, 872, 564, 64),
      gfx::Rect(175, 872, 850, 64), gfx::Rect(32, 872, 1136, 64)},
     /*expected_portrait_bounds=*/
     {gfx::Rect(361, 1072, 278, 64), gfx::Rect(218, 1072, 564, 64),
      gfx::Rect(75, 1072, 850, 64), gfx::Rect(218, 1000, 564, 136)}},

    // The nearly squared display with shelf right aligned.
    {/*display_size=*/gfx::Size(1200, 1000),
     ShelfAlignment::kRight,
     /*expected_landscape_bounds=*/
     {gfx::Rect(449, 920, 270, 64), gfx::Rect(310, 920, 548, 64),
      gfx::Rect(171, 920, 826, 64), gfx::Rect(32, 920, 1104, 64)},
     /*expected_portrait_bounds=*/
     {gfx::Rect(349, 1120, 270, 64), gfx::Rect(210, 1120, 548, 64),
      gfx::Rect(71, 1120, 826, 64), gfx::Rect(210, 1048, 548, 136)}},

    // The wide display with width > 1450, which always use the optimal chip
    // size (278, 64).
    {/*display_size=*/gfx::Size(1600, 800),
     ShelfAlignment::kBottom,
     /*expected_landscape_bounds=*/
     {gfx::Rect(661, 672, 278, 64), gfx::Rect(518, 672, 564, 64),
      gfx::Rect(375, 672, 850, 64), gfx::Rect(232, 672, 1136, 64)},
     /*expected_portrait_bounds=*/
     {gfx::Rect(261, 1472, 278, 64), gfx::Rect(118, 1472, 564, 64),
      gfx::Rect(118, 1400, 564, 136), gfx::Rect(118, 1400, 564, 136)}},
};

INSTANTIATE_TEST_SUITE_P(
    All,
    BirchBarLayoutTest,
    testing::ValuesIn(kLayoutTestParams),
    [](const testing::TestParamInfo<BirchBarLayoutTest::ParamType>& info) {
      std::string test_name = info.param.display_size.ToString();

      switch (info.param.shelf_alignment) {
        case ShelfAlignment::kLeft:
          test_name += "_ShelfLeft";
          break;
        case ShelfAlignment::kRight:
          test_name = "_ShelfRight";
          break;
        case ShelfAlignment::kBottom:
        case ShelfAlignment::kBottomLocked:
          test_name += "_ShelfBottom";
      }
      return test_name;
    });

// Tests the responsive layout of a birch bar when converting from landscape
// mode to portrait mode with different number of chips.
TEST_P(BirchBarLayoutTest, ResponsiveLayout) {
  EnterOverview();

  aura::Window* root = Shell::GetPrimaryRootWindow();
  BirchBarView* birch_bar_view = OverviewGridTestApi(root).birch_bar_view();
  ASSERT_TRUE(birch_bar_view);

  const LayoutTestParams& params = GetParam();
  const views::Widget* birch_bar_widget =
      OverviewGridTestApi(root).birch_bar_widget();

  // Add test chips to the bar in landscape mode.
  std::vector<std::unique_ptr<BirchItem>> items_;
  for (int i = 0; i < 4; i++) {
    std::optional<std::u16string> addon_label;
    if (i % 2) {
      addon_label = u"add-on";
    }
    auto item =
        std::make_unique<TestBirchItem>(u"title", u"subtitle", addon_label);
    birch_bar_view->AddChip(item.get());
    items_.emplace_back(std::move(item));
    EXPECT_EQ(birch_bar_widget->GetWindowBoundsInScreen(),
              params.expected_landscape_bounds[i]);
  }

  // Convert to portrait mode.
  ScreenOrientationControllerTestApi screen_rotation_test_api(
      Shell::Get()->screen_orientation_controller());
  screen_rotation_test_api.SetDisplayRotation(
      display::Display::ROTATE_90, display::Display::RotationSource::ACTIVE);

  // Removing chips from the bar in portrait mode.
  for (int i = 4; i > 0; i--) {
    EXPECT_EQ(birch_bar_widget->GetWindowBoundsInScreen(),
              params.expected_portrait_bounds[i - 1]);
    birch_bar_view->RemoveChip(items_[i - 1].get());
  }
}

}  // namespace ash