chromium/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc

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

#include "components/omnibox/browser/autocomplete_grouper_sections.h"

#include <iterator>
#include <memory>

#include "base/test/scoped_feature_list.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/omnibox_feature_configs.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/omnibox_proto/groups.pb.h"

namespace {
AutocompleteMatch CreateMatch(int relevance,
                              omnibox::GroupId group_id,
                              bool allowed_to_be_default = false) {}

void VerifyMatches(const ACMatches& matches,
                   std::vector<int> expected_relevances) {}

}  // namespace

// Tests rules for Section.
TEST(AutocompleteGrouperSectionsTest, Section) {}

// Tests rules for ZpsSection.
TEST(AutocompleteGrouperGroupsTest, ZpsSection) {}

// Tests the groups, limits, and rules for the Desktop NTP ZPS section.
TEST(AutocompleteGrouperSectionsTest, DesktopNTPZpsSection) {}

// Tests the groups, limits, and rules for the Desktop NTP ZPS section.
TEST(AutocompleteGrouperSectionsTest, DesktopNTPZpsSection_WithIPH) {}

// Tests the groups, limits, and rules for the Desktop non-ZPS section.
TEST(AutocompleteGrouperSectionsTest, DesktopNonZpsSection) {}

// Tests the groups, limits, and rules for the Android SRP ZPS section.
TEST(AutocompleteGrouperSectionsTest, AndroidSRPZpsSection) {}

// Tests the groups, limits, and rules for the Android Web ZPS section.
TEST(AutocompleteGrouperSectionsTest, AndroidWebZpsSection) {}

// Tests the groups, limits, and rules for the Android NTP ZPS + Inspire Me.
TEST(AutocompleteGrouperSectionsTest, AndroidNTPZpsSection_withInspireMe) {}

// Tests the groups, limits, and rules for the Android Web ZPS + MV tiles.
TEST(AutocompleteGrouperSectionsTest, AndroidWebZpsSection_mostVisitedTiles) {}

// Tests the groups, limits, and rules for the iOS NTP ZPS.
TEST(AutocompleteGrouperSectionsTest, IOSNTPZpsSection) {}

// Tests the groups and limits for DesktopSecondaryNTPZpsSection.
TEST(AutocompleteGrouperSectionsTest, DesktopSecondaryNTPZpsSection) {}

// Tests the behavior when DesktopNTPZpsSection and
// DesktopSecondaryNTPZpsSection are both created.
TEST(AutocompleteGrouperSectionsTest,
     DesktopNTPZpsSectionAndDesktopSecondaryNTPZpsSection) {}

// Test that (on Android) sections are grouped by Search vs URL.
#if BUILDFLAG(IS_ANDROID)
TEST(AutocompleteGrouperSectionsTest,
     AndroidNonZPSSection_groupsBySearchVsUrl) {
  auto test = [](bool show_only_search_suggestions, ACMatches matches,
                 std::vector<int> expected_relevances) {
    PSections sections;
    omnibox::GroupConfigMap group_configs;
    sections.push_back(std::make_unique<AndroidNonZPSSection>(
        show_only_search_suggestions, group_configs));
    auto out_matches = Section::GroupMatches(std::move(sections), matches);
    VerifyMatches(out_matches, expected_relevances);
  };

  auto make_search = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_SEARCH);
    match.type = AutocompleteMatchType::SEARCH_HISTORY;
    return match;
  };

  auto make_url = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_OTHER_NAVS);
    match.type = AutocompleteMatchType::NAVSUGGEST;
    return match;
  };

  constexpr bool kSearchesOnly = true;
  constexpr bool kSearchesAndUrls = false;

  {
    SCOPED_TRACE("No matches = no crashes.");
    test(kSearchesAndUrls, {}, {});
    test(kSearchesOnly, {}, {});
  }
  {
    SCOPED_TRACE("Grouping top section only w/ Search.");
    test(kSearchesAndUrls, {make_search(100)}, {100});
    test(kSearchesOnly, {make_search(100)}, {100});
  }
  {
    SCOPED_TRACE("Grouping top section only w/ URL.");
    test(kSearchesAndUrls, {make_url(100)}, {100});
    // Top URL is allowed.
    test(kSearchesOnly, {make_url(100)}, {100});
  }
  {
    SCOPED_TRACE("Grouping top section only w/ multiple URLs.");
    ACMatches matches{
        make_url(20),
        make_url(19),
        make_url(18),
    };
    test(kSearchesAndUrls, matches, {20, 19, 18});
    // Only top URL is allowed.
    test(kSearchesOnly, matches, {20});
  }
  {
    SCOPED_TRACE("Grouping top two sections.");
    ACMatches matches{
        make_url(20),    make_url(19),   make_url(18),
        make_search(10), make_search(9),
    };

    test(kSearchesAndUrls, matches,
         // 20     -- default match.
         // 10, 9  -- top searches.
         // 19, 18 -- top URLs.
         {20, 10, 9, 19, 18});

    test(kSearchesOnly, matches,
         // 20     -- default match (url).
         // 10, 9  -- top searches.
         {20, 10, 9});
  }
  {
    SCOPED_TRACE("Grouping all sections.");
    ACMatches matches{
        make_url(20),
        // top adaptive group
        make_url(19),
        make_url(18),
        make_search(10),
        make_search(9),
        make_url(17),
        // bottom adaptive group
        make_url(16),
        make_search(8),
        make_search(7),
        make_url(15),
        make_url(14),
        make_search(6),
        make_search(5),
        make_url(13),
        make_url(12),
    };

    test(kSearchesAndUrls, matches,
         {
             20,                             // the default match
             10, 9, 19, 18, 17,              // the top adaptive group
             8, 7, 6, 5, 16, 15, 14, 13, 12  // the bottom adaptive group.
         });

    test(kSearchesOnly, matches,
         {
             20,                 // Default match is URL
             10, 9, 8, 7, 6, 5,  // top adaptive group.
         });
  }
}

TEST(AutocompleteGrouperSectionsTest,
     AndroidNonZPSSection_richCardInFirstPosition) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeatureWithParameters(
      omnibox::kOmniboxAnswerActions,
      {{OmniboxFieldTrial::kAnswerActionsShowRichCard.name, "true"}});
  auto test = [](ACMatches matches, std::vector<int> expected_relevances) {
    PSections sections;
    omnibox::GroupConfigMap group_configs;
    AndroidNonZPSSection::set_num_visible_matches(5);
    sections.push_back(
        std::make_unique<AndroidNonZPSSection>(false, group_configs));
    auto out_matches = Section::GroupMatches(std::move(sections), matches);
    VerifyMatches(out_matches, expected_relevances);
  };

  auto make_search = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_SEARCH);
    match.type = AutocompleteMatchType::SEARCH_HISTORY;
    return match;
  };

  auto make_url = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_OTHER_NAVS);
    match.type = AutocompleteMatchType::NAVSUGGEST;
    return match;
  };

  auto make_rich_card = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_MOBILE_RICH_ANSWER);
    match.type = AutocompleteMatchType::SEARCH_HISTORY;
    SuggestionAnswer answer;
    match.answer = answer;
    return match;
  };

  {
    SCOPED_TRACE("No matches, no crashes.");
    test({}, {});
  }

  SCOPED_TRACE("Card in first position");
  test(
      {
          make_rich_card(20),
          make_url(19),
          make_url(18),
          make_search(10),
          make_search(9),
      },
      // 20     -- rich answer card
      // 10, 9  -- top searches.
      // 19, 18 -- top URLs.
      {20, 10, 9, 19, 18});
}

TEST(AutocompleteGrouperSectionsTest,
     AndroidNonZPSSection_richCardAboveKeyboard) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeatureWithParameters(
      omnibox::kOmniboxAnswerActions,
      {{OmniboxFieldTrial::kAnswerActionsShowRichCard.name, "true"},
       {OmniboxFieldTrial::kAnswerActionsShowAboveKeyboard.name, "true"},
       {OmniboxFieldTrial::kAnswerActionsShowIfUrlsPresent.name, "true"}});
  auto test = [](ACMatches matches, std::vector<int> expected_relevances) {
    PSections sections;
    omnibox::GroupConfigMap group_configs;
    AndroidNonZPSSection::set_num_visible_matches(5);
    sections.push_back(
        std::make_unique<AndroidNonZPSSection>(false, group_configs));
    auto out_matches = Section::GroupMatches(std::move(sections), matches);
    VerifyMatches(out_matches, expected_relevances);
  };

  auto make_search = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_SEARCH);
    match.type = AutocompleteMatchType::SEARCH_HISTORY;
    return match;
  };

  auto make_url = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_OTHER_NAVS);
    match.type = AutocompleteMatchType::NAVSUGGEST;
    return match;
  };

  auto make_rich_card = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_MOBILE_RICH_ANSWER);
    match.type = AutocompleteMatchType::SEARCH_HISTORY;
    omnibox::RichAnswerTemplate answer_template;
    match.answer_template = answer_template;
    return match;
  };

  {
    SCOPED_TRACE("No matches, no crashes.");
    test({}, {});
  }

  SCOPED_TRACE("Card in last position of visible matches");
  test(
      {
          make_url(19),
          make_url(18),
          make_rich_card(20),
          make_search(10),
          make_search(9),
          make_search(8),
          make_search(7),
      },
      // 19     -- default match url
      // 10, 9  -- top searches.
      // 18 -- remaining URL.
      // 20 -- rich answer card
      // 8, 7 -- below the fold matches
      {19, 10, 9, 18, 20, 8, 7});
}

TEST(AutocompleteGrouperSectionsTest,
     AndroidNonZPSSection_hideCardWhenUrlsPresent) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeatureWithParameters(
      omnibox::kOmniboxAnswerActions,
      {{OmniboxFieldTrial::kAnswerActionsShowRichCard.name, "true"},
       {OmniboxFieldTrial::kAnswerActionsShowAboveKeyboard.name, "true"},
       {OmniboxFieldTrial::kAnswerActionsShowIfUrlsPresent.name, "false"}});

  auto test = [](ACMatches matches, std::vector<int> expected_relevances) {
    PSections sections;
    omnibox::GroupConfigMap group_configs;
    AndroidNonZPSSection::set_num_visible_matches(5);
    sections.push_back(
        std::make_unique<AndroidNonZPSSection>(false, group_configs));
    auto out_matches = Section::GroupMatches(std::move(sections), matches);
    VerifyMatches(out_matches, expected_relevances);
  };

  auto make_search = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_SEARCH);
    match.type = AutocompleteMatchType::SEARCH_HISTORY;
    return match;
  };

  auto make_url = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_OTHER_NAVS);
    match.type = AutocompleteMatchType::NAVSUGGEST;
    return match;
  };

  auto make_rich_card = [](int score) {
    auto match = CreateMatch(score, omnibox::GROUP_MOBILE_RICH_ANSWER);
    match.type = AutocompleteMatchType::SEARCH_HISTORY;
    omnibox::RichAnswerTemplate answer_template;
    match.answer_template = answer_template;
    return match;
  };

  {
    SCOPED_TRACE("No matches, no crashes.");
    test({}, {});
  }

  SCOPED_TRACE("Card in last position of visible matches");
  test(
      {
          make_url(19),
          make_url(18),
          make_rich_card(20),
          make_search(10),
          make_search(9),
          make_search(8),
          make_search(7),
      },
      // 19     -- default match url
      // 20, 10 -- top searches.
      // Answer(20) counts as a plain search due to presence of urls.
      // 18 -- remaining URL.
      // 9, 8, 7 -- below the fold matches
      {19, 20, 10, 18, 9, 8, 7});
}

#endif