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

#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/memory/raw_ref.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "components/omnibox/browser/autocomplete_controller.h"
#include "components/omnibox/browser/autocomplete_enums.h"
#include "components/omnibox/browser/autocomplete_result.h"
#include "components/omnibox/browser/fake_autocomplete_controller.h"
#include "components/omnibox/browser/fake_autocomplete_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::ElementsAre;
using testing::ElementsAreArray;

namespace {
// A fake provider that will:
//   a) Consume 1ms to run the sync pass.
//   b) Can be configured whether to be done or not after the sync pass.
class FakeAutocompleteProviderDelayed : public FakeAutocompleteProvider {
 public:
  FakeAutocompleteProviderDelayed(
      Type type,
      raw_ptr<base::test::SingleThreadTaskEnvironment> task_environment,
      bool done_after_sync_pass = false)
      : FakeAutocompleteProvider(type),
        task_environment_(task_environment),
        done_after_sync_pass_(done_after_sync_pass) {}

  void Start(const AutocompleteInput& input, bool minimal_changes) override {
    done_ = done_after_sync_pass_;
    task_environment_->FastForwardBy(base::Milliseconds(1));
    FakeAutocompleteProvider::Start(input, minimal_changes);
  }

  // Used to simulate the sync pass consuming 1ms.
  raw_ptr<base::test::SingleThreadTaskEnvironment> task_environment_ = nullptr;

  // Whether the provider should be done after the sync pass.
  bool done_after_sync_pass_;

 protected:
  ~FakeAutocompleteProviderDelayed() override = default;
};
}  // namespace

class AutocompleteControllerMetricsTest : public testing::Test {
 public:
  AutocompleteControllerMetricsTest()
      : controller_(&task_environment_),
        histogram_tester_(std::make_unique<base::HistogramTester>()) {
    controller_.providers_ = {
        base::MakeRefCounted<FakeAutocompleteProviderDelayed>(
            AutocompleteProvider::Type::TYPE_BOOKMARK, &task_environment_)};

    // Allow tests to simulate an initial update with no changes. Since the
    // 0-matches cases is special handled, tests can't simply do
    // `Simulate(true|false, {})` to simulate an initial update with no changes;
    // this allows tests to instead do `Simulate(true|false, {CreateMatch(0)})`.
    SimulateStart(true, {CreateMatch(0)});
    ResetHistogramTester();
  }

  AutocompleteMatch CreateMatch(int i) {
    const std::string name = base::NumberToString(i);
    AutocompleteMatch match{nullptr, 1000, false,
                            AutocompleteMatchType::HISTORY_URL};
    match.destination_url = GURL{"https://google.com/" + name};
    return match;
  }

  // By default, the controller wants async matches. `SetInputSync()` will
  // explicitly set this behavior.
  void SetInputSync(bool sync) {
    controller_.input_.set_omit_asynchronous_matches(sync);
  }

  // Mimics `AutocompleteController::Start()`'s behavior. `done` determines
  // whether the controller should be done after the sync pass. `old_result` and
  // `new_result` are passed to
  // `AutocompleteControllerMetrics::OnUpdateResult()` to determine which
  // matches changed.
  void SimulateStart(bool done, std::vector<AutocompleteMatch> matches) {
    controller_.GetFakeProvider().matches_ = matches;
    controller_.GetFakeProvider().done_ = true;
    controller_.GetFakeProvider<FakeAutocompleteProviderDelayed>()
        .done_after_sync_pass_ = done;
    controller_.Start(controller_.input_);
  }

  // Mimics `AutocompleteController::NotifyChanged()`'s behavior. `done`
  // determines weather the controller should be done after this async update.
  // `old_result` and `new_result` are passed to
  // `AutocompleteControllerMetrics::OnUpdateResult()` to determine which
  // matches changed.
  void SimulateAsyncUpdate(bool done, std::vector<AutocompleteMatch> matches) {
    controller_.GetFakeProvider().matches_ = matches;
    controller_.GetFakeProvider().done_ = done;
    task_environment_.FastForwardBy(base::Milliseconds(1));
    controller_.OnProviderUpdate(true, &controller_.GetFakeProvider());
  }

  // Simulates `AutocompleteController::Stop()`.
  void SimulateOnStop() {
    task_environment_.FastForwardBy(base::Milliseconds(1));
    controller_.Stop(AutocompleteStopReason::kInteraction);
  }

  // Convenience function to be called before/after EXPECT'ing histograms to
  // avoid old logs converting new logs.
  void ResetHistogramTester() {
    histogram_tester_ = std::make_unique<base::HistogramTester>();
  }

  // Convenience function to call AutocompleteControllerMetrics::OnStop()` and
  // expects it to not log any suggestion finalization metrics. Should be used
  // to simulate the controller completing in which case metrics should have
  // already been logged earlier during `::OnUpdateResult()`. Should not be used
  // when the controller is interrupted in which case metrics are expected to be
  // logged. Does not check provider or cross stability metrics.
  void StopAndExpectNoSuggestionFinalizationMetrics() {
    controller_.Stop(AutocompleteStopReason::kClobbered);
    ExpectNoSuggestionFinalizationMetrics();
  }

  // Convenience method to check the buckets of a single metric.
  void ExpectMetrics(const std::string metric_name,
                     std::optional<int> expected_time_ms) {
    SCOPED_TRACE(metric_name);
    const std::string full_name =
        "Omnibox.AsyncAutocompletionTime2." + metric_name;
    histogram_tester_->ExpectTotalCount(full_name,
                                        expected_time_ms.has_value());
    if (expected_time_ms) {
      // Total sum is exact, whereas buckets aren't. E.g. we can't verify
      // whether a metric recorded 300 or 301 if the containing bucket is [290,
      // 310].
      EXPECT_EQ(histogram_tester_->GetTotalSum(full_name), *expected_time_ms);
    }
  }

  // Convenience method to check the buckets of 3 metrics:
  // - '...<metric>' will be expected to have `buckets`.
  // - '...<metric>.[Completed]' will be expected to either have
  //   `buckets` or be empty, depending on `completed`.
  void ExpectSlicedMetrics(const std::string& metric,
                           std::optional<int> expected_time_ms,
                           bool completed) {
    ExpectMetrics(metric, expected_time_ms);
    ExpectMetrics(metric + ".Completed",
                  completed ? expected_time_ms : std::nullopt);
    ExpectMetrics(metric + ".Interrupted",
                  !completed ? expected_time_ms : std::nullopt);
  }

  // Convenience method to check all 9 suggestion finalization metrics are
  // empty. '...[Done|LastChange|LastDefaultChange][|.Completed]'
  void ExpectNoSuggestionFinalizationMetrics() {
    ExpectSlicedMetrics("Done", {}, false);
    ExpectSlicedMetrics("LastChange", {}, false);
    ExpectSlicedMetrics("LastDefaultChange", {}, false);
  }

  // Convince method to check all 9 suggestion finalization metrics have been
  // logged exactly once with the correct bucket. This should be used if
  // there's been a single controller request logged since the last
  // `ResetHistogramTester()`. If there have been none,
  // `ExpectNoSuggestionFinalizationMetrics()` should be used.
  void ExpectSingleCountSuggestionFinalizationMetrics(
      int done_bucket_min,
      int last_change_bucket_min,
      int last_default_change_bucket_min,
      bool completed) {
    ExpectSlicedMetrics("Done", done_bucket_min, completed);
    ExpectSlicedMetrics("LastChange", last_change_bucket_min, completed);
    ExpectSlicedMetrics("LastDefaultChange", last_default_change_bucket_min,
                        completed);
    ResetHistogramTester();
  }

  // Convenience method to check the 3 metrics for a specific provider are
  // empty. '...Provider.<Provider>[|.Completed]'
  void ExpectNoProviderMetrics(const std::string& provider) {
    ExpectSlicedMetrics("Provider." + provider, {}, false);
  }

  // Convenience method to check the 3 metrics for a specific provider have
  // been logged exactly once with the correct bucket. This should be used if
  // there's been a single log for the specified provider since the last
  // `ResetHistogramTester()`. If there have been none,
  // `ExpectNoProviderMetrics()` should be used.
  void ExpectProviderMetrics(const std::string& provider,
                             int bucket_min,
                             bool completed) {
    ExpectSlicedMetrics("Provider." + provider, bucket_min, completed);
  }

  // Used to control time passed between calls. Many metrics tested are timing
  // metrics.
  base::test::SingleThreadTaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
  FakeAutocompleteController controller_;
  std::unique_ptr<base::HistogramTester> histogram_tester_;
};

TEST_F(AutocompleteControllerMetricsTest, SuggestionFinalization_SyncInput) {
  // Sync inputs should not log metrics.
  SetInputSync(true);
  SimulateStart(true, {CreateMatch(1)});
  SimulateOnStop();
  ExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest,
       SuggestionFinalization_OnlySyncUpdate) {
  // Sync updates should log metrics.
  SimulateStart(true, {CreateMatch(1)});
  ExpectSingleCountSuggestionFinalizationMetrics(1, 1, 1, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest,
       SuggestionFinalization_OnlySyncUpdateWithNoChanges) {
  // Sync updates without changes should log metrics.
  SimulateStart(true, {CreateMatch(0)});
  ExpectSingleCountSuggestionFinalizationMetrics(1, 0, 0, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest,
       SuggestionFinalization_SyncAnd3AsyncUpdate) {
  // This is the typical flow: 1 sync update followed by multiple async updates.
  SimulateStart(false, {CreateMatch(1)});
  // 1st async update.
  SimulateAsyncUpdate(false, {CreateMatch(2)});
  // 2nd async update.
  SimulateAsyncUpdate(false, {CreateMatch(3)});
  // Last async update.
  SimulateAsyncUpdate(true, {CreateMatch(4)});
  ExpectSingleCountSuggestionFinalizationMetrics(4, 4, 4, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest,
       SuggestionFinalization_SyncAnd3AsyncUpdateWithNoChanges) {
  // 1 sync and 3 async updates, none of the 4 has a change.
  SimulateStart(false, {CreateMatch(0)});
  // 1st async update.
  SimulateAsyncUpdate(false, {CreateMatch(0)});
  // 2nd async update.
  SimulateAsyncUpdate(false, {CreateMatch(0)});
  // Last async update.
  SimulateAsyncUpdate(true, {CreateMatch(0)});
  ExpectSingleCountSuggestionFinalizationMetrics(4, 0, 0, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(
    AutocompleteControllerMetricsTest,
    SuggestionFinalization_UnchangedSyncAnd2UnchangedAnd1ChangedAsyncUpdates) {
  // 1 sync and 3 async updates, only the last of the 4 has a change.
  SimulateStart(false, {CreateMatch(0)});
  // 1st async update.
  SimulateAsyncUpdate(false, {CreateMatch(0)});
  // 2nd async update.
  SimulateAsyncUpdate(false, {CreateMatch(0)});
  // Last async update.
  SimulateAsyncUpdate(true, {CreateMatch(1)});
  ExpectSingleCountSuggestionFinalizationMetrics(4, 4, 4, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(
    AutocompleteControllerMetricsTest,
    SuggestionFinalization_UnchangedSyncAnd1ChangedAnd2UnchangedAsyncUpdates) {
  // 1 sync and 3 async updates, only the 2nd of the 4 has a change. Because of
  // debouncing, the change doesn't apply until the 4th update.
  SimulateStart(false, {CreateMatch(0)});
  // 1st async update.
  SimulateAsyncUpdate(false, {CreateMatch(1)});
  // 2nd async update.
  SimulateAsyncUpdate(false, {CreateMatch(1)});
  // Last async update.
  SimulateAsyncUpdate(true, {CreateMatch(1)});
  ExpectSingleCountSuggestionFinalizationMetrics(4, 4, 4, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(
    AutocompleteControllerMetricsTest,
    SuggestionFinalization_UnchangedSyncAnd1ChangedAnd2UnchangedAsyncUpdates_ChangeAppliedBeforeDone) {
  // 1 sync and 3 async updates, only the 2nd of the 4 has a change. The
  // debounced change applies before the last update.
  SimulateStart(false, {CreateMatch(0)});
  // 1st async update.
  SimulateAsyncUpdate(false, {CreateMatch(1)});
  task_environment_.FastForwardBy(base::Milliseconds(250));
  // 2nd async update.
  SimulateAsyncUpdate(false, {CreateMatch(1)});
  // Last async update.
  SimulateAsyncUpdate(true, {CreateMatch(1)});
  ExpectSingleCountSuggestionFinalizationMetrics(254, 202, 202, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest,
       SuggestionFinalization_ChangedSyncAnd3UnchangedAsyncUpdates) {
  // 1 sync and 3 async updates, only the 1st of the 4 has a change.
  SimulateStart(false, {CreateMatch(1)});
  // 1st async update.
  SimulateAsyncUpdate(false, {CreateMatch(1)});
  // 2nd async update.
  SimulateAsyncUpdate(false, {CreateMatch(1)});
  // Last async update.
  SimulateAsyncUpdate(true, {CreateMatch(1)});
  ExpectSingleCountSuggestionFinalizationMetrics(4, 1, 1, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest,
       SuggestionFinalization_StopTimerReached) {
  // Simulates the case where the async updates take longer than the 1.5s stop
  // timer. It's not possible for the sync update to take longer, as the stop
  // timer only starts after the sync update. The logged times should however
  // measure starting from before the sync update.
  SimulateStart(false, {CreateMatch(1)});
  // 1st async update.
  SimulateAsyncUpdate(false, {CreateMatch(1)});
  // Stop timer.
  SimulateOnStop();
  ExpectSingleCountSuggestionFinalizationMetrics(3, 1, 1, false);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest, SuggestionFinalization_Interrupted) {
  // Start 1st input.
  SimulateStart(false, {CreateMatch(1)});
  // 1 async update for 1st input.
  SimulateAsyncUpdate(false, {CreateMatch(2)});
  // Wait for the debouncer.
  task_environment_.FastForwardBy(base::Milliseconds(250));
  ExpectNoSuggestionFinalizationMetrics();
  // Interrupted by 2nd input. The log should include the time until
  // interruption.
  SimulateStart(false, {CreateMatch(3)});
  ExpectSingleCountSuggestionFinalizationMetrics(252, 202, 202, false);
  // Interrupted by 3rd input.
  task_environment_.FastForwardBy(base::Milliseconds(1));
  SimulateStart(false, {CreateMatch(4)});
  ExpectSingleCountSuggestionFinalizationMetrics(2, 1, 1, false);
  // 1st async update for 3rd input. Controller completes with the 2nd async
  // update.
  SimulateAsyncUpdate(false, {CreateMatch(5)});
  ExpectNoSuggestionFinalizationMetrics();
  // 2nd and last async update for 3rd input.
  SimulateAsyncUpdate(true, {CreateMatch(6)});
  ExpectSingleCountSuggestionFinalizationMetrics(3, 3, 3, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest, SuggestionFinalization_ExpireTimer) {
  SimulateStart(true, {CreateMatch(0), CreateMatch(1)});
  ResetHistogramTester();
  // A sync update without matches. The transferred match should remain.
  SimulateStart(false, {CreateMatch(2)});
  // Wait for the expire timer.
  task_environment_.FastForwardBy(base::Milliseconds(1000));
  // Last update with no matches.
  ExpectNoSuggestionFinalizationMetrics();
  SimulateAsyncUpdate(true, {CreateMatch(2)});
  // 500 expire timer + 200 debounce timer + 1 sync pass delay = 701.
  ExpectSingleCountSuggestionFinalizationMetrics(1002, 701, 1, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest,
       SuggestionFinalization_MatchDeletion) {
  SimulateStart(true, {CreateMatch(0), CreateMatch(1)});
  ResetHistogramTester();

  auto match = CreateMatch(0);
  match.provider = &controller_.GetFakeProvider();
  match.deletable = true;

  // Match deletion with changes. Since autocompletion is complete, finalization
  // metrics aren't logged. Stability metrics are logged, but that's just
  // arbitrary for consistency with historic behavior / code simplicity, and we
  // could stop logging them for deletions if we chose.
  controller_.DeleteMatch(match);
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition"),
              testing::ElementsAre(base::Bucket(1, 1)));
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest,
       SuggestionFinalization_DefaultUnchanged) {
  // Sync update with a default match change.
  SimulateStart(false, {CreateMatch(1)});
  // 1st async update with non-default match changed.
  SimulateAsyncUpdate(false, {CreateMatch(1), CreateMatch(2)});
  // Wait for the debounce timer.
  task_environment_.FastForwardBy(base::Milliseconds(200));
  // 2nd async update with no matches changed.
  SimulateAsyncUpdate(true, {CreateMatch(1), CreateMatch(2)});
  ExpectSingleCountSuggestionFinalizationMetrics(203, 202, 1, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
}

TEST_F(AutocompleteControllerMetricsTest, Provider_SyncAndAsyncCompletion) {
  controller_.providers_ = {
      base::MakeRefCounted<FakeAutocompleteProviderDelayed>(
          AutocompleteProvider::Type::TYPE_BOOKMARK, &task_environment_, true),
      base::MakeRefCounted<FakeAutocompleteProviderDelayed>(
          AutocompleteProvider::Type::TYPE_KEYWORD, &task_environment_),
      base::MakeRefCounted<FakeAutocompleteProviderDelayed>(
          AutocompleteProvider::Type::TYPE_BUILTIN, &task_environment_),
  };
  {
    SCOPED_TRACE("Sync update with 1st provider completing.");
    controller_.GetFakeProvider(0).matches_ = {CreateMatch(0)};
    controller_.Start(controller_.input_);
    ExpectNoProviderMetrics(controller_.GetFakeProvider(0).GetName());
    ExpectNoProviderMetrics(controller_.GetFakeProvider(1).GetName());
    ExpectNoProviderMetrics(controller_.GetFakeProvider(2).GetName());
    ExpectNoSuggestionFinalizationMetrics();
    ResetHistogramTester();
  }
  {
    SCOPED_TRACE("1st async update with 2nd provider completing.");
    task_environment_.FastForwardBy(base::Milliseconds(1));
    controller_.GetFakeProvider(1).done_ = true;
    controller_.OnProviderUpdate(true, &controller_.GetFakeProvider(1));
    ExpectNoProviderMetrics(controller_.GetFakeProvider(0).GetName());
    ExpectProviderMetrics(controller_.GetFakeProvider(1).GetName(), 4, true);
    ExpectNoProviderMetrics(controller_.GetFakeProvider(2).GetName());
    ExpectNoSuggestionFinalizationMetrics();
    ResetHistogramTester();
  }
  {
    SCOPED_TRACE("Last async update with 3rd provider completing.");
    task_environment_.FastForwardBy(base::Milliseconds(1));
    controller_.GetFakeProvider(2).done_ = true;
    controller_.OnProviderUpdate(true, &controller_.GetFakeProvider(2));
    ExpectNoProviderMetrics(controller_.GetFakeProvider(0).GetName());
    ExpectNoProviderMetrics(controller_.GetFakeProvider(1).GetName());
    ExpectProviderMetrics(controller_.GetFakeProvider(2).GetName(), 5, true);
    ExpectSingleCountSuggestionFinalizationMetrics(5, 0, 0, true);
    ResetHistogramTester();
  }
  {
    SCOPED_TRACE("Stop.");
    StopAndExpectNoSuggestionFinalizationMetrics();
    ExpectNoProviderMetrics(controller_.GetFakeProvider(0).GetName());
    ExpectNoProviderMetrics(controller_.GetFakeProvider(1).GetName());
    ExpectNoProviderMetrics(controller_.GetFakeProvider(2).GetName());
    ResetHistogramTester();
  }
}

TEST_F(AutocompleteControllerMetricsTest,
       Provider_1ProviderWithMultipleUpdates) {
  // Sync update without completion.
  controller_.GetFakeProvider().done_ = true;
  SimulateStart(false, {CreateMatch(0)});

  // 1st async update without completion.
  SimulateAsyncUpdate(false, {CreateMatch(0)});

  // Last async update with completion.
  SimulateAsyncUpdate(true, {CreateMatch(0)});
  ExpectProviderMetrics(controller_.GetFakeProvider().GetName(), 3, true);
  ExpectSingleCountSuggestionFinalizationMetrics(3, 0, 0, true);
  StopAndExpectNoSuggestionFinalizationMetrics();
  ExpectNoProviderMetrics(controller_.GetFakeProvider().GetName());
}

TEST_F(AutocompleteControllerMetricsTest, Provider_Interrupted) {
  controller_.providers_ = {
      base::MakeRefCounted<FakeAutocompleteProviderDelayed>(
          AutocompleteProvider::Type::TYPE_BOOKMARK, &task_environment_),
      base::MakeRefCounted<FakeAutocompleteProviderDelayed>(
          AutocompleteProvider::Type::TYPE_KEYWORD, &task_environment_),
  };

  // Sync update. 1st provider completes; 2nd provider does not.
  controller_.GetFakeProvider(0).done_ = true;
  controller_.GetFakeProvider(1).done_ = true;
  controller_.GetFakeProvider<FakeAutocompleteProviderDelayed>(0)
      .done_after_sync_pass_ = true;
  controller_.GetFakeProvider<FakeAutocompleteProviderDelayed>(1)
      .done_after_sync_pass_ = false;
  controller_.Start(controller_.input_);

  // 2nd provider is interrupted by stop.
  SimulateOnStop();

  ExpectNoProviderMetrics(controller_.GetFakeProvider(0).GetName());
  ExpectProviderMetrics(controller_.GetFakeProvider(1).GetName(), 3, false);
  ExpectSingleCountSuggestionFinalizationMetrics(3, 0, 0, false);
}

TEST_F(AutocompleteControllerMetricsTest, MatchStability) {
  auto create_result = [&](std::vector<int> ids) {
    std::vector<AutocompleteMatch> matches;
    std::ranges::transform(ids, std::back_inserter(matches), [&](int id) {
      auto match = CreateMatch(id);
      match.relevance -= id;
      return match;
    });
    return matches;
  };

  const auto first_result = create_result({10, 11, 12, 13, 14});
  // Same as `first_result`, but with these changes:
  //  - Last two matches removed.
  //  - Default match updated to a new URL.
  //  - Third match updated to a new URL.
  const auto second_result = create_result({0, 11, 20});
  // Same as `second_result`, but with these changes:
  //  - 2 new matches appended to the bottom.
  const auto third_result = create_result({0, 11, 20, 21, 22});

  // Verify logging to the CrossInput* histograms.
  SimulateStart(true, first_result);
  ResetHistogramTester();
  SimulateStart(true, second_result);
  // Expect the default match, third match, and last two matches to be logged
  // as changed, and nothing else.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex.CrossInput"),
              testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1),
                                   base::Bucket(3, 1), base::Bucket(4, 1)));
  // Expect that we log that at least one of the matches has changed.
  EXPECT_THAT(
      histogram_tester_->GetAllSamples(
          "Omnibox.MatchStability2.MatchChangeInAnyPosition.CrossInput"),
      testing::ElementsAre(base::Bucket(1, 1)));
  // Expect that we don't log sync updates to the Async histograms.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex.Async"),
              testing::ElementsAre());
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition.Async"),
              testing::ElementsAre());
  // Verify the unsliced histograms.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex"),
              testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1),
                                   base::Bucket(3, 1), base::Bucket(4, 1)));
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition"),
              testing::ElementsAre(base::Bucket(1, 1)));
  ResetHistogramTester();

  // Verify logging to the Async* histograms.
  SimulateStart(false, first_result);
  ResetHistogramTester();
  SimulateAsyncUpdate(true, second_result);
  // Expect the default match, third match, and last two matches to be logged
  // as changed, and nothing else.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex.Async"),
              testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1),
                                   base::Bucket(3, 1), base::Bucket(4, 1)));
  // Expect that we log that at least one of the matches has changed.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition.Async"),
              testing::ElementsAre(base::Bucket(1, 1)));
  // Expect that we don't log sync updates to the CrossInput histograms.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex.CrossInput"),
              testing::ElementsAre());
  EXPECT_THAT(
      histogram_tester_->GetAllSamples(
          "Omnibox.MatchStability2.MatchChangeInAnyPosition.CrossInput"),
      testing::ElementsAre());
  // Verify the unsliced histograms.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex"),
              testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1),
                                   base::Bucket(3, 1), base::Bucket(4, 1)));
  // Expect that we log that at least one of the matches has changed.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition"),
              testing::ElementsAre(base::Bucket(1, 1)));
  ResetHistogramTester();

  // Verify no logging when appending matches for sync updates.
  SimulateStart(true, second_result);
  ResetHistogramTester();
  SimulateStart(true, third_result);
  // Expect no changes logged; expect 1 false logged to each
  // MatchChangeInAnyPosition.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex.CrossInput"),
              testing::ElementsAre());
  EXPECT_THAT(
      histogram_tester_->GetAllSamples(
          "Omnibox.MatchStability2.MatchChangeInAnyPosition.CrossInput"),
      testing::ElementsAre(base::Bucket(0, 1)));
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex.Async"),
              testing::ElementsAre());
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition.Async"),
              testing::ElementsAre());
  // Verify the unsliced histograms.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex"),
              testing::ElementsAre());
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition"),
              testing::ElementsAre(base::Bucket(0, 1)));
  ResetHistogramTester();

  // Verify no logging when appending matches for async updates.
  SimulateStart(false, second_result);
  ResetHistogramTester();
  SimulateAsyncUpdate(true, third_result);
  // Expect no changes logged; expect 1 false logged to each
  // MatchChangeInAnyPosition.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex.CrossInput"),
              testing::ElementsAre());
  EXPECT_THAT(
      histogram_tester_->GetAllSamples(
          "Omnibox.MatchStability2.MatchChangeInAnyPosition.CrossInput"),
      testing::ElementsAre());
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex.Async"),
              testing::ElementsAre());
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition.Async"),
              testing::ElementsAre(base::Bucket(0, 1)));
  // Verify the unsliced histograms.
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeIndex"),
              testing::ElementsAre());
  EXPECT_THAT(histogram_tester_->GetAllSamples(
                  "Omnibox.MatchStability2.MatchChangeInAnyPosition"),
              testing::ElementsAre(base::Bucket(0, 1)));
  ResetHistogramTester();
}
