// 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/omnibox_metrics_provider.h"

#include <memory>
#include <string>

#include "base/memory/scoped_refptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/omnibox/browser/actions/contextual_search_action.h"
#include "components/omnibox/browser/actions/cross_device_tab_action.h"
#include "components/omnibox/browser/actions/omnibox_action.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/autocomplete_match_type.h"
#include "components/omnibox/browser/autocomplete_provider.h"
#include "components/omnibox/browser/autocomplete_result.h"
#include "components/omnibox/browser/cross_device_tab_provider.h"
#include "components/omnibox/browser/fake_autocomplete_provider.h"
#include "components/omnibox/browser/match_compare.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "components/omnibox/browser/omnibox_log.h"
#include "components/omnibox/browser/omnibox_popup_selection.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "third_party/metrics_proto/omnibox_scoring_signals.pb.h"
#include "ui/base/window_open_disposition.h"

using ScoringSignals = ::metrics::OmniboxEventProto::Suggestion::ScoringSignals;
using OmniboxScoringSignals = ::metrics::OmniboxScoringSignals;

class OmniboxMetricsProviderTest : public testing::Test {
 public:
  OmniboxMetricsProviderTest() = default;
  ~OmniboxMetricsProviderTest() override = default;

  void SetUp() override {
    autocomplete_provider_ =
        new FakeAutocompleteProvider(AutocompleteProvider::TYPE_SEARCH);
    metrics_provider_ = std::make_unique<OmniboxMetricsProvider>();
  }

  void TearDown() override { metrics_provider_.reset(); }

  OmniboxLog BuildOmniboxLog(const AutocompleteResult& result,
                             size_t selected_index,
                             SessionData session_data) {
    return OmniboxLog(
        /*text=*/u"my text", /*just_deleted_text=*/false,
        /*input_type=*/metrics::OmniboxInputType::URL,
        /*in_keyword_mode=*/false,
        /*entry_method=*/
        metrics::OmniboxEventProto_KeywordModeEntryMethod_INVALID,
        /*is_popup_open=*/false,
        /*selection=*/OmniboxPopupSelection(selected_index),
        /*disposition=*/WindowOpenDisposition::CURRENT_TAB,
        /*is_paste_and_go=*/false,
        /*tab_id=*/SessionID::FromSerializedValue(1),
        /*current_page_classification=*/
        metrics::OmniboxEventProto_PageClassification_NTP_REALBOX,
        /*elapsed_time_since_user_first_modified_omnibox=*/base::TimeDelta(),
        /*completed_length=*/0,
        /*elapsed_time_since_last_change_to_default_match=*/base::TimeDelta(),
        /*result=*/result, /*destination_url=*/GURL("https://www.example.com/"),
        /*is_incognito=*/false,
        /*is_zero_suggest=*/false,
        /*session=*/session_data);
  }

  AutocompleteMatch BuildMatch(AutocompleteMatch::Type type) {
    return AutocompleteMatch(autocomplete_provider_.get(), /*relevance=*/0,
                             /*deletable=*/false, type);
  }

  void RecordMetrics(const OmniboxLog& log) {
    metrics_provider_->RecordMetrics(log);
  }

  void RecordContextualSearchPrecisionRecallUsage(const OmniboxLog& log) {
    metrics_provider_->RecordContextualSearchPrecisionRecallUsage(log);
  }

  void RecordLogAndVerifyScoringSignals(
      const OmniboxLog& log,
      OmniboxScoringSignals& expected_scoring_signals) {
    // Clear the event cache so we start with a clean slate.
    metrics_provider_->omnibox_events_cache.clear_omnibox_event();

    metrics_provider_->RecordOmniboxEvent(log);

    EXPECT_EQ(metrics_provider_->omnibox_events_cache.omnibox_event_size(), 1);
    const metrics::OmniboxEventProto& omnibox_event =
        metrics_provider_->omnibox_events_cache.omnibox_event(0);

    for (int i = 0; i < omnibox_event.suggestion_size(); i++) {
      const metrics::OmniboxEventProto::Suggestion& suggestion =
          omnibox_event.suggestion(i);
      // Scoring signals should not be logged when in incognito/off-the-record
      // mode, regardless of result type.
      if (log.is_incognito) {
        EXPECT_FALSE(suggestion.has_scoring_signals());
        continue;
      }

      // When not in incognito, scoring signals should only be logged for the
      // proper suggestion types. Check that the signals are logged correctly
      // for URL types and Search types, while not being logged for any others.
      if (suggestion.has_result_type()) {
        EXPECT_TRUE(suggestion.has_scoring_signals());

        if (suggestion.result_type() ==
            metrics::
                OmniboxEventProto_Suggestion_ResultType_SEARCH_WHAT_YOU_TYPED) {
          EXPECT_EQ(suggestion.scoring_signals().search_suggest_relevance(),
                    expected_scoring_signals.search_suggest_relevance());
          EXPECT_EQ(suggestion.scoring_signals().is_search_suggest_entity(),
                    expected_scoring_signals.is_search_suggest_entity());
        } else {
          EXPECT_EQ(
              suggestion.scoring_signals()
                  .first_bookmark_title_match_position(),
              expected_scoring_signals.first_bookmark_title_match_position());
          EXPECT_EQ(suggestion.scoring_signals().allowed_to_be_default_match(),
                    expected_scoring_signals.allowed_to_be_default_match());
          EXPECT_EQ(suggestion.scoring_signals().length_of_url(),
                    expected_scoring_signals.length_of_url());
        }
      } else {
        EXPECT_FALSE(suggestion.has_scoring_signals());
      }
    }

    // Clear the event cache.
    metrics_provider_->omnibox_events_cache.clear_omnibox_event();
  }

 protected:
  base::test::TaskEnvironment task_environment_;
  scoped_refptr<FakeAutocompleteProvider> autocomplete_provider_;
  std::unique_ptr<OmniboxMetricsProvider> metrics_provider_;
};

TEST_F(OmniboxMetricsProviderTest, RecordMetrics_SingleURL) {
  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteResult result;
    result.AppendMatches(
        {BuildMatch(AutocompleteMatch::Type::URL_WHAT_YOU_TYPED)});
    SessionData session;
    session.typed_suggestions_shown_in_session = true;
    session.typed_url_suggestions_shown_in_session = true;
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    log.ukm_source_id = ukm::NoURLSourceId();
    RecordMetrics(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionUsed.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl, /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType.ByPageContext.NTP_"
        "REALBOX",
        ClientSummarizedResultType::kUrl, /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.TypedSuggest.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl, /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.TypedSuggest.ClientSummarizedResultType."
        "ByPageContext.NTP_REALBOX",
        ClientSummarizedResultType::kUrl, /*expected_count=*/1);

    // Verify the UKM event.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kResultTypeGroupName,
        static_cast<uint64_t>(ClientSummarizedResultType::kUrl));
  }

  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteResult result;
    result.AppendMatches(
        {BuildMatch(AutocompleteMatch::Type::URL_WHAT_YOU_TYPED)});
    SessionData session;
    session.zero_prefix_suggestions_shown_in_session = true;
    session.zero_prefix_url_suggestions_shown_in_session = true;
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    log.text = u"";
    log.ukm_source_id = ukm::NoURLSourceId();
    RecordMetrics(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionUsed.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl, /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType.ByPageContext.NTP_"
        "REALBOX",
        ClientSummarizedResultType::kUrl, /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ZeroSuggest.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl, /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ZeroSuggest.ClientSummarizedResultType."
        "ByPageContext.NTP_REALBOX",
        ClientSummarizedResultType::kUrl, /*expected_count=*/1);

    // Verify the UKM event.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kResultTypeGroupName,
        static_cast<uint64_t>(ClientSummarizedResultType::kUrl));
  }
}

TEST_F(OmniboxMetricsProviderTest, RecordMetrics_SingleSearch) {
  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteResult result;
    result.AppendMatches({BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST)});
    SessionData session;
    session.typed_suggestions_shown_in_session = true;
    session.typed_search_suggestions_shown_in_session = true;
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    log.ukm_source_id = ukm::NoURLSourceId();
    RecordMetrics(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionUsed.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch, /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType.ByPageContext.NTP_"
        "REALBOX",
        ClientSummarizedResultType::kSearch, /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.TypedSuggest.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch, /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.TypedSuggest.ClientSummarizedResultType."
        "ByPageContext.NTP_REALBOX",
        ClientSummarizedResultType::kSearch, /*expected_count=*/1);

    // Verify the UKM event.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kResultTypeGroupName,
        static_cast<uint64_t>(ClientSummarizedResultType::kSearch));
  }

  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteResult result;
    result.AppendMatches({BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST)});
    SessionData session;
    session.zero_prefix_suggestions_shown_in_session = true;
    session.zero_prefix_search_suggestions_shown_in_session = true;
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    log.ukm_source_id = ukm::NoURLSourceId();
    RecordMetrics(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionUsed.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch, /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType.ByPageContext.NTP_"
        "REALBOX",
        ClientSummarizedResultType::kSearch, /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ZeroSuggest.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch, /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ZeroSuggest.ClientSummarizedResultType."
        "ByPageContext.NTP_REALBOX",
        ClientSummarizedResultType::kSearch, /*expected_count=*/1);

    // Verify the UKM event.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kResultTypeGroupName,
        static_cast<uint64_t>(ClientSummarizedResultType::kSearch));
  }
}

TEST_F(OmniboxMetricsProviderTest, RecordContextualSearchMetrics) {
  // Contextual search suggestion shown, but not selected.
  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteMatch match =
        BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST);
    match.subtypes.insert(omnibox::SUBTYPE_CONTEXTUAL_SEARCH);
    match.takeover_action =
        base::MakeRefCounted<ContextualSearchFulfillmentAction>(
            match.destination_url, match.type, true);

    AutocompleteResult result;
    result.AppendMatches(
        {BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST), match});

    SessionData session;
    session.contextual_search_suggestions_shown_in_session = true;

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordContextualSearchPrecisionRecallUsage(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectTotalCount(
        "Omnibox.ContextualSearchSuggestion.Precision", /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.ContextualSearchSuggestion.Precision", false,
        /*expected_count=*/1);

    histogram_tester.ExpectTotalCount(
        "Omnibox.ContextualSearchSuggestion.Recall",
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.ContextualSearchSuggestion.Recall", true,
        /*expected_count=*/1);

    histogram_tester.ExpectTotalCount(
        "Omnibox.ContextualSearchSuggestion.Usage",
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.ContextualSearchSuggestion.Usage", false,
        /*expected_count=*/1);

    RecordMetrics(log);

    // Verify the UKM event and the full set of metrics.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(entry,
                                   ukm::builders::Omnibox_SuggestionUsed::
                                       kZeroPrefixContextualSearchShownName,
                                   true);
    ukm_recorder.ExpectEntryMetric(
        entry,
        ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixLensActionShownName,
        false);
  }

  // Contextual search suggestion shown and selected.
  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteMatch match =
        BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST);
    match.subtypes.insert(omnibox::SUBTYPE_CONTEXTUAL_SEARCH);
    match.takeover_action =
        base::MakeRefCounted<ContextualSearchFulfillmentAction>(
            match.destination_url, match.type, true);

    AutocompleteResult result;
    result.AppendMatches(
        {BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST), match});

    SessionData session;
    session.contextual_search_suggestions_shown_in_session = true;

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/1,
                                     /*session_data=*/session);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordContextualSearchPrecisionRecallUsage(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectTotalCount(
        "Omnibox.ContextualSearchSuggestion.Precision", /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.ContextualSearchSuggestion.Precision", true,
        /*expected_count=*/1);

    histogram_tester.ExpectTotalCount(
        "Omnibox.ContextualSearchSuggestion.Recall",
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.ContextualSearchSuggestion.Recall", true,
        /*expected_count=*/1);

    histogram_tester.ExpectTotalCount(
        "Omnibox.ContextualSearchSuggestion.Usage",
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.ContextualSearchSuggestion.Usage", true, /*expected_count=*/1);

    RecordMetrics(log);

    // Verify the UKM event and the full set of metrics.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(entry,
                                   ukm::builders::Omnibox_SuggestionUsed::
                                       kZeroPrefixContextualSearchShownName,
                                   true);
    ukm_recorder.ExpectEntryMetric(
        entry,
        ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixLensActionShownName,
        false);
  }

  // Google Lens action shown, but not selected.
  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteMatch match =
        BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST);
    match.subtypes.insert(omnibox::SUBTYPE_CONTEXTUAL_SEARCH);
    match.takeover_action =
        base::MakeRefCounted<ContextualSearchOpenLensAction>();

    AutocompleteResult result;
    result.AppendMatches(
        {BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST), match});

    SessionData session;
    session.lens_action_shown_in_session = true;

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordContextualSearchPrecisionRecallUsage(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectTotalCount("Omnibox.LensAction.Precision", 1);
    histogram_tester.ExpectBucketCount("Omnibox.LensAction.Precision", false,
                                       /*expected_count=*/1);

    histogram_tester.ExpectTotalCount("Omnibox.LensAction.Recall", 1);
    histogram_tester.ExpectBucketCount("Omnibox.LensAction.Recall", true,
                                       /*expected_count=*/1);

    histogram_tester.ExpectTotalCount("Omnibox.LensAction.Usage", 1);
    histogram_tester.ExpectBucketCount("Omnibox.LensAction.Usage", false,
                                       /*expected_count=*/1);

    RecordMetrics(log);

    // Verify the UKM event and the full set of metrics.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(entry,
                                   ukm::builders::Omnibox_SuggestionUsed::
                                       kZeroPrefixContextualSearchShownName,
                                   false);
    ukm_recorder.ExpectEntryMetric(
        entry,
        ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixLensActionShownName,
        true);
  }

  // Google Lens action shown and selected.
  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteMatch match =
        BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST);
    match.subtypes.insert(omnibox::SUBTYPE_CONTEXTUAL_SEARCH);
    match.takeover_action =
        base::MakeRefCounted<ContextualSearchOpenLensAction>();

    AutocompleteResult result;
    result.AppendMatches(
        {BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST), match});

    SessionData session;
    session.lens_action_shown_in_session = true;

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/1,
                                     /*session_data=*/session);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordContextualSearchPrecisionRecallUsage(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectTotalCount("Omnibox.LensAction.Precision", 1);
    histogram_tester.ExpectBucketCount("Omnibox.LensAction.Precision", true,
                                       /*expected_count=*/1);

    histogram_tester.ExpectTotalCount("Omnibox.LensAction.Recall", 1);
    histogram_tester.ExpectBucketCount("Omnibox.LensAction.Recall", true,
                                       /*expected_count=*/1);

    histogram_tester.ExpectTotalCount("Omnibox.LensAction.Usage", 1);
    histogram_tester.ExpectBucketCount("Omnibox.LensAction.Usage", true,
                                       /*expected_count=*/1);

    RecordMetrics(log);

    // Verify the UKM event and the full set of metrics.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(entry,
                                   ukm::builders::Omnibox_SuggestionUsed::
                                       kZeroPrefixContextualSearchShownName,
                                   false);
    ukm_recorder.ExpectEntryMetric(
        entry,
        ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixLensActionShownName,
        true);
  }
}

TEST_F(OmniboxMetricsProviderTest, RecordMetrics_MultipleSearch) {
  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteResult result;
    result.AppendMatches(
        {BuildMatch(AutocompleteMatch::Type::URL_WHAT_YOU_TYPED),
         BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST),
         BuildMatch(AutocompleteMatch::Type::URL_WHAT_YOU_TYPED)});
    SessionData session;
    session.typed_suggestions_shown_in_session = true;
    session.typed_search_suggestions_shown_in_session = true;
    session.typed_url_suggestions_shown_in_session = true;
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/1,
                                     /*session_data=*/session);
    log.ukm_source_id = ukm::NoURLSourceId();
    log.elapsed_time_since_user_focused_omnibox = base::Milliseconds(10);
    RecordMetrics(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionUsed.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType.ByPageContext.NTP_"
        "REALBOX",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.TypedSuggest.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.TypedSuggest.ClientSummarizedResultType."
        "ByPageContext.NTP_REALBOX",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType.ByPageContext.NTP_"
        "REALBOX",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.TypedSuggest.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.TypedSuggest.ClientSummarizedResultType."
        "ByPageContext.NTP_REALBOX",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);

    // Verify the UKM event and the full set of metrics.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kPageClassificationName,
        static_cast<uint64_t>(
            metrics::OmniboxEventProto_PageClassification_NTP_REALBOX));
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kProviderTypeName,
        static_cast<uint64_t>(metrics::OmniboxEventProto_ProviderType_SEARCH));
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kResultTypeName,
        static_cast<uint64_t>(
            metrics::OmniboxEventProto_Suggestion::SEARCH_SUGGEST));
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kResultTypeGroupName,
        static_cast<uint64_t>(ClientSummarizedResultType::kSearch));
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kSelectedIndexName, 1ul);
    // With exponential bucketing scheme with a standard spacing of 2.0, 10
    // falls into the 8-16 bucket as the boundaries of the buckets increase
    // exponentially, e.g., 1, 2, 4, 8, 16, etc.
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kTimeSinceLastFocusMsName,
        8ul);
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kTypedLengthName, 7ul);
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kTypingDurationMsName,
        0ul);
    ukm_recorder.ExpectEntryMetric(
        entry,
        ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixSearchShownName,
        false);
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixUrlShownName,
        false);
    ukm_recorder.ExpectEntryMetric(entry,
                                   ukm::builders::Omnibox_SuggestionUsed::
                                       kZeroPrefixContextualSearchShownName,
                                   false);
    ukm_recorder.ExpectEntryMetric(
        entry,
        ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixLensActionShownName,
        false);
  }
  {
    base::HistogramTester histogram_tester;
    ukm::TestAutoSetUkmRecorder ukm_recorder;

    AutocompleteResult result;
    result.AppendMatches({BuildMatch(AutocompleteMatch::Type::HISTORY_URL),
                          BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST),
                          BuildMatch(AutocompleteMatch::Type::HISTORY_URL)});
    SessionData session;
    session.zero_prefix_suggestions_shown_in_session = true;
    session.zero_prefix_search_suggestions_shown_in_session = true;
    session.zero_prefix_url_suggestions_shown_in_session = true;
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/1,
                                     /*session_data=*/session);
    log.text = u"";
    log.ukm_source_id = ukm::NoURLSourceId();
    log.elapsed_time_since_user_focused_omnibox = base::Milliseconds(10);
    RecordMetrics(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionUsed.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType.ByPageContext.NTP_"
        "REALBOX",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ZeroSuggest.ClientSummarizedResultType",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ZeroSuggest.ClientSummarizedResultType."
        "ByPageContext.NTP_REALBOX",
        ClientSummarizedResultType::kSearch,
        /*expected_count=*/1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ClientSummarizedResultType.ByPageContext.NTP_"
        "REALBOX",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ZeroSuggest.ClientSummarizedResultType",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.SuggestionShown.ZeroSuggest.ClientSummarizedResultType."
        "ByPageContext.NTP_REALBOX",
        ClientSummarizedResultType::kUrl,
        /*expected_count=*/1);

    // Verify the UKM event and the full set of metrics.
    const char* entry_name = ukm::builders::Omnibox_SuggestionUsed::kEntryName;
    EXPECT_EQ(ukm_recorder.GetEntriesByName(entry_name).size(), 1ul);
    auto* entry = ukm_recorder.GetEntriesByName(entry_name)[0].get();
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kPageClassificationName,
        static_cast<uint64_t>(
            metrics::OmniboxEventProto_PageClassification_NTP_REALBOX));
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kProviderTypeName,
        static_cast<uint64_t>(metrics::OmniboxEventProto_ProviderType_SEARCH));
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kResultTypeName,
        static_cast<uint64_t>(
            metrics::OmniboxEventProto_Suggestion::SEARCH_SUGGEST));
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kResultTypeGroupName,
        static_cast<uint64_t>(ClientSummarizedResultType::kSearch));
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kSelectedIndexName, 1ul);
    // With exponential bucketing scheme with a standard spacing of 2.0, 10
    // falls into the 8-16 bucket as the boundaries of the buckets increase
    // exponentially, e.g., 1, 2, 4, 8, 16, etc.
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kTimeSinceLastFocusMsName,
        8ul);
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kTypedLengthName, 0ul);
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kTypingDurationMsName,
        0ul);
    ukm_recorder.ExpectEntryMetric(
        entry,
        ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixSearchShownName,
        true);
    ukm_recorder.ExpectEntryMetric(
        entry, ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixUrlShownName,
        true);
    ukm_recorder.ExpectEntryMetric(entry,
                                   ukm::builders::Omnibox_SuggestionUsed::
                                       kZeroPrefixContextualSearchShownName,
                                   false);
    ukm_recorder.ExpectEntryMetric(
        entry,
        ukm::builders::Omnibox_SuggestionUsed::kZeroPrefixLensActionShownName,
        false);
  }
}

TEST_F(OmniboxMetricsProviderTest, RecordMetrics_InvalidUkmSourceId) {
  base::HistogramTester histogram_tester;
  ukm::TestAutoSetUkmRecorder ukm_recorder;
  AutocompleteResult result;
  result.AppendMatches(
      {BuildMatch(AutocompleteMatch::Type::URL_WHAT_YOU_TYPED)});
  OmniboxLog log =
      BuildOmniboxLog(result, /*selected_index=*/0, /*session_data=*/{});
  RecordMetrics(log);

  // Verify the UMA histogram.
  histogram_tester.ExpectBucketCount(
      "Omnibox.SuggestionUsed.ClientSummarizedResultType",
      ClientSummarizedResultType::kUrl, 1);

  // Verify the UKM event was not logged due to invalid ukm source id.
  EXPECT_EQ(
      ukm_recorder
          .GetEntriesByName(ukm::builders::Omnibox_SuggestionUsed::kEntryName)
          .size(),
      0ul);
}

TEST_F(OmniboxMetricsProviderTest, RecordMetrics_ToolAndModelModes) {
  {
    base::HistogramTester histogram_tester;

    AutocompleteResult result;
    result.AppendMatches({BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST)});
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/{});
    log.ukm_source_id = ukm::NoURLSourceId();

    // Explicitly set the AIM state.
    log.input_state.active_tool = omnibox::TOOL_MODE_IMAGE_GEN;
    log.input_state.active_model = omnibox::MODEL_MODE_GEMINI_PRO;

    RecordMetrics(log);

    // Verify the UMA histograms. (SEARCH_SUGGEST is non-verbatim so precision
    // is true)
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.ImageGen", true, 1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByModel.GeminiPro", true, 1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.ImageGen.ByModel."
        "GeminiPro",
        true, 1);

    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.ImageGen", true, 1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByModel.GeminiPro", true, 1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.ImageGen.ByModel."
        "GeminiPro",
        true, 1);
  }

  {
    base::HistogramTester histogram_tester;

    AutocompleteResult result;
    // SEARCH_WHAT_YOU_TYPED is verbatim type.
    result.AppendMatches(
        {BuildMatch(AutocompleteMatch::Type::SEARCH_WHAT_YOU_TYPED)});

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/{});
    log.ukm_source_id = ukm::NoURLSourceId();

    // Explicitly set the AIM state summary with no non-trivial matches.
    log.input_state.active_tool = omnibox::TOOL_MODE_IMAGE_GEN;
    log.input_state.active_model = omnibox::MODEL_MODE_GEMINI_PRO;

    RecordMetrics(log);

    // Verify the UMA histograms.
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.ImageGen", false, 1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByModel.GeminiPro", false, 1);
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.ImageGen.ByModel."
        "GeminiPro",
        false, 1);

    // Precision shouldn't be recorded if not shown.
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.ImageGen", 0);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByModel.GeminiPro", 0);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.ImageGen.ByModel."
        "GeminiPro",
        0);
  }

  {
    base::HistogramTester histogram_tester;

    AutocompleteResult result;
    result.AppendMatches({BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST)});

    // Here we don't set an input state. It should default to Unspecified.
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/{});
    log.ukm_source_id = ukm::NoURLSourceId();
    RecordMetrics(log);

    // Verify the UMA histograms.
    // When Unspecified, we should NOT log the precision/recall metrics anymore.
    histogram_tester.ExpectTotalCount("Omnibox.NonTrivialSuggestions.Recall",
                                      0);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.Unspecified", 0);
    histogram_tester.ExpectTotalCount("Omnibox.NonTrivialSuggestions.Precision",
                                      0);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.Unspecified", 0);
  }

  {
    base::HistogramTester histogram_tester;

    AutocompleteResult result;
    result.AppendMatches({BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST)});
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/{});
    log.ukm_source_id = ukm::NoURLSourceId();

    // Only set the tool mode.
    log.input_state.active_tool = omnibox::TOOL_MODE_IMAGE_GEN;

    RecordMetrics(log);

    // Verify that only the tool metric is logged.
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.ImageGen", true, 1);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByModel.GeminiPro", 0);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.ImageGen.ByModel."
        "GeminiPro",
        0);

    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.ImageGen", true, 1);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByModel.GeminiPro", 0);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.ImageGen.ByModel."
        "GeminiPro",
        0);
  }

  {
    base::HistogramTester histogram_tester;

    AutocompleteResult result;
    result.AppendMatches({BuildMatch(AutocompleteMatch::Type::SEARCH_SUGGEST)});
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/{});
    log.ukm_source_id = ukm::NoURLSourceId();

    // Only set the model mode.
    log.input_state.active_model = omnibox::MODEL_MODE_GEMINI_PRO;

    RecordMetrics(log);

    // Verify that only the model metric is logged.
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.ImageGen", 0);
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByModel.GeminiPro", true, 1);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Recall.ByToolType.ImageGen.ByModel."
        "GeminiPro",
        0);

    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.ImageGen", 0);
    histogram_tester.ExpectBucketCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByModel.GeminiPro", true, 1);
    histogram_tester.ExpectTotalCount(
        "Omnibox.NonTrivialSuggestions.Precision.ByToolType.ImageGen.ByModel."
        "GeminiPro",
        0);
  }
}

TEST_F(OmniboxMetricsProviderTest, RecordMetrics_CrossDeviceTab) {
  class FakeCrossDeviceTabProvider : public CrossDeviceTabProvider {
   public:
    FakeCrossDeviceTabProvider() : CrossDeviceTabProvider(nullptr) {}
    void Start(const AutocompleteInput& input, bool minimal_changes) override {}

   protected:
    ~FakeCrossDeviceTabProvider() override = default;
  };

  scoped_refptr<FakeCrossDeviceTabProvider> cross_device_provider =
      base::MakeRefCounted<FakeCrossDeviceTabProvider>();

  base::Time tab_last_active_time = base::Time::Now() - base::Minutes(2);

  auto BuildCrossDeviceMatch = [&](bool with_action) {
    AutocompleteMatch match(cross_device_provider.get(), /*relevance=*/100,
                            /*deletable=*/false,
                            AutocompleteMatchType::CROSS_DEVICE_TAB);
    if (with_action) {
      match.actions.push_back(
          base::MakeRefCounted<CrossDeviceTabAction>(tab_last_active_time));
    }
    return match;
  };

  // Case 1: Match is shown and clicked.
  {
    base::HistogramTester histogram_tester;
    AutocompleteResult result;
    result.AppendMatches({BuildCrossDeviceMatch(/*with_action=*/true)});

    SessionData session;
    session.zero_prefix_suggestions_shown_in_session = true;
    session.zero_prefix_url_suggestions_shown_in_session = true;

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    log.selection.state = OmniboxPopupSelection::NORMAL;
    log.elapsed_time_since_user_focused_omnibox = base::Seconds(3);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordMetrics(log);

    // Verify Time metrics.
    histogram_tester.ExpectUniqueSample(
        "Omnibox.CrossDeviceTab.Match.FocusToOpenTime",
        base::Seconds(3).InMilliseconds(), 1);
    histogram_tester.ExpectUniqueSample("Omnibox.CrossDeviceTab.Match.ClickAge",
                                        base::Minutes(2).InMilliseconds(), 1);
    histogram_tester.ExpectUniqueSample("Omnibox.CrossDeviceTab.Match.ShowAge",
                                        base::Minutes(2).InMilliseconds(), 1);
  }

  // Case 2: Match shown but NOT clicked (another match clicked).
  {
    base::HistogramTester histogram_tester;
    AutocompleteResult result;
    result.AppendMatches(
        {BuildCrossDeviceMatch(/*with_action=*/true),
         BuildMatch(AutocompleteMatch::Type::URL_WHAT_YOU_TYPED)});

    SessionData session;
    session.zero_prefix_suggestions_shown_in_session = true;
    session.zero_prefix_url_suggestions_shown_in_session = true;

    // Select the second match (index 1), which is NOT cross device.
    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/1,
                                     /*session_data=*/session);
    log.selection.state = OmniboxPopupSelection::NORMAL;
    log.elapsed_time_since_user_focused_omnibox = base::Seconds(3);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordMetrics(log);

    // Verify NO focus/age metrics for cross device tab (since it wasn't
    // clicked).
    histogram_tester.ExpectTotalCount(
        "Omnibox.CrossDeviceTab.Match.FocusToOpenTime", 0);
    histogram_tester.ExpectTotalCount("Omnibox.CrossDeviceTab.Match.ClickAge",
                                      0);
    histogram_tester.ExpectUniqueSample("Omnibox.CrossDeviceTab.Match.ShowAge",
                                        base::Minutes(2).InMilliseconds(), 1);
  }

  // Case 3: Action clicked.
  {
    base::HistogramTester histogram_tester;
    AutocompleteResult result;
    result.AppendMatches({BuildCrossDeviceMatch(/*with_action=*/true)});

    SessionData session;
    session.zero_prefix_suggestions_shown_in_session = true;
    session.zero_prefix_url_suggestions_shown_in_session = true;

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    // Select the action on match 0.
    log.selection.state = OmniboxPopupSelection::FOCUSED_BUTTON_ACTION;
    log.selection.action_index = 0;
    log.elapsed_time_since_user_focused_omnibox = base::Seconds(5);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordMetrics(log);

    // Verify Action Time metrics.
    histogram_tester.ExpectUniqueSample(
        "Omnibox.CrossDeviceTab.Action.FocusToOpenTime",
        base::Seconds(5).InMilliseconds(), 1);
    histogram_tester.ExpectUniqueSample(
        "Omnibox.CrossDeviceTab.Action.ClickAge",
        base::Minutes(2).InMilliseconds(), 1);
    histogram_tester.ExpectUniqueSample("Omnibox.CrossDeviceTab.Match.ShowAge",
                                        base::Minutes(2).InMilliseconds(), 1);
  }

  // Case 4: Match clicked, with multiple actions (dummy action at index 0,
  // CrossDeviceTabAction at index 1).
  {
    base::HistogramTester histogram_tester;
    AutocompleteResult result;
    // Build match with multiple actions.
    AutocompleteMatch match = BuildCrossDeviceMatch(/*with_action=*/true);
    // Prepend a dummy action.
    match.actions.insert(
        match.actions.begin(),
        base::MakeRefCounted<OmniboxAction>(OmniboxAction::LabelStrings(),
                                            GURL("chrome://settings")));
    result.AppendMatches({match});

    SessionData session;
    session.zero_prefix_suggestions_shown_in_session = true;
    session.zero_prefix_url_suggestions_shown_in_session = true;

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    log.selection.state = OmniboxPopupSelection::NORMAL;
    log.elapsed_time_since_user_focused_omnibox = base::Seconds(3);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordMetrics(log);

    // Verify Time metrics.
    histogram_tester.ExpectUniqueSample(
        "Omnibox.CrossDeviceTab.Match.FocusToOpenTime",
        base::Seconds(3).InMilliseconds(), 1);
    // ClickAge and ShowAge should still be logged correctly, finding the action
    // at index 1.
    histogram_tester.ExpectUniqueSample("Omnibox.CrossDeviceTab.Match.ClickAge",
                                        base::Minutes(2).InMilliseconds(), 1);
    histogram_tester.ExpectUniqueSample("Omnibox.CrossDeviceTab.Match.ShowAge",
                                        base::Minutes(2).InMilliseconds(), 1);
  }

  // Case 5: Action clicked, with multiple actions (dummy action at index 0,
  // CrossDeviceTabAction at index 1).
  {
    base::HistogramTester histogram_tester;
    AutocompleteResult result;
    AutocompleteMatch match = BuildCrossDeviceMatch(/*with_action=*/true);
    match.actions.insert(
        match.actions.begin(),
        base::MakeRefCounted<OmniboxAction>(OmniboxAction::LabelStrings(),
                                            GURL("chrome://settings")));
    result.AppendMatches({match});

    SessionData session;
    session.zero_prefix_suggestions_shown_in_session = true;
    session.zero_prefix_url_suggestions_shown_in_session = true;

    OmniboxLog log = BuildOmniboxLog(result, /*selected_index=*/0,
                                     /*session_data=*/session);
    // Select the action at index 1 (the CrossDeviceTabAction).
    log.selection.state = OmniboxPopupSelection::FOCUSED_BUTTON_ACTION;
    log.selection.action_index = 1;
    log.elapsed_time_since_user_focused_omnibox = base::Seconds(5);
    log.ukm_source_id = ukm::NoURLSourceId();

    RecordMetrics(log);

    // Verify Action Time metrics (should find the action at index 1).
    histogram_tester.ExpectUniqueSample(
        "Omnibox.CrossDeviceTab.Action.FocusToOpenTime",
        base::Seconds(5).InMilliseconds(), 1);
    histogram_tester.ExpectUniqueSample(
        "Omnibox.CrossDeviceTab.Action.ClickAge",
        base::Minutes(2).InMilliseconds(), 1);
    histogram_tester.ExpectUniqueSample("Omnibox.CrossDeviceTab.Match.ShowAge",
                                        base::Minutes(2).InMilliseconds(), 1);
  }
}

// TODO(b/261895038): This test is flaky on android.  Currently scoring signals
// logging is only enabled on desktop, so disable for mobile.
#if !(BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID))
TEST_F(OmniboxMetricsProviderTest, LogScoringSignals) {
  // Enable feature flag to log scoring signals.
  OmniboxFieldTrial::ScopedMLConfigForTesting scoped_ml_config;
  scoped_ml_config.GetMLConfig().log_url_scoring_signals = true;

  // Populate a set of scoring signals with some test values. This will be used
  // to ensure the scoring signals are being propagated correctly.
  OmniboxScoringSignals expected_url_scoring_signals;
  expected_url_scoring_signals.set_first_bookmark_title_match_position(3);
  expected_url_scoring_signals.set_allowed_to_be_default_match(true);
  expected_url_scoring_signals.set_length_of_url(20);

  OmniboxScoringSignals expected_search_scoring_signals;
  expected_search_scoring_signals.set_search_suggest_relevance(1000);
  expected_search_scoring_signals.set_is_search_suggest_entity(true);

  // Create matches and populate the scoring signals. Signals should only be
  // logged for non-search suggestions.
  ACMatches matches = {
      BuildMatch(AutocompleteMatchType::Type::BOOKMARK_TITLE),
      BuildMatch(AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED)};
  for (auto& match : matches) {
    match.scoring_signals = AutocompleteMatch::IsSearchHistoryType(match.type)
                                ? expected_search_scoring_signals
                                : expected_url_scoring_signals;
  }
  AutocompleteResult result;
  result.AppendMatches(matches);

  // Create the log and call simulate logging.
  OmniboxLog log =
      BuildOmniboxLog(result, /*selected_index=*/1, /*session_data=*/{});
  RecordLogAndVerifyScoringSignals(log, *matches[0].scoring_signals);

  // Now, "turn on" incognito mode, scoring signals should not be logged.
  log.is_incognito = true;
  RecordLogAndVerifyScoringSignals(log, *matches[0].scoring_signals);
}
#endif  // !(BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID))
