chromium/ios/chrome/browser/first_run/ui_bundled/omnibox_position/metrics_unittest.mm

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

#import "ios/chrome/browser/first_run/ui_bundled/omnibox_position/metrics.h"

#import <memory>
#import <string_view>

#import "base/test/metrics/histogram_tester.h"
#import "base/test/task_environment.h"
#import "components/prefs/pref_registry_simple.h"
#import "components/prefs/scoped_user_pref_update.h"
#import "components/prefs/testing_pref_service.h"
#import "components/segmentation_platform/embedder/default_model/device_switcher_model.h"
#import "components/segmentation_platform/embedder/default_model/device_switcher_result_dispatcher.h"
#import "components/segmentation_platform/public/field_trial_register.h"
#import "components/segmentation_platform/public/result.h"
#import "components/segmentation_platform/public/testing/mock_segmentation_platform_service.h"
#import "components/sync_device_info/fake_device_info_tracker.h"
#import "ios/chrome/browser/shared/model/utils/first_run_test_util.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/platform_test.h"

namespace {

/// Mock FieldTrialRegister for `DeviceSwitcherResultDispatcher`.
class MockFieldTrialRegister
    : public segmentation_platform::FieldTrialRegister {
 public:
  MOCK_METHOD(void,
              RegisterFieldTrial,
              (std::string_view trial_name, std::string_view group_name));

  MOCK_METHOD(void,
              RegisterSubsegmentFieldTrialIfNeeded,
              (std::string_view trial_name,
               segmentation_platform::proto::SegmentId segment_id,
               int subsegment_rank));
};

/// Fake `FakeDeviceSwitcherResultDispatcher` with public
/// `classification_result`.
class FakeDeviceSwitcherResultDispatcher
    : public segmentation_platform::DeviceSwitcherResultDispatcher {
 public:
  FakeDeviceSwitcherResultDispatcher(
      segmentation_platform::SegmentationPlatformService* segmentation_service,
      syncer::DeviceInfoTracker* device_info_tracker,
      PrefService* prefs,
      segmentation_platform::FieldTrialRegister* field_trial_register)
      : segmentation_platform::DeviceSwitcherResultDispatcher(
            segmentation_service,
            device_info_tracker,
            prefs,
            field_trial_register),
        classification_result(
            segmentation_platform::PredictionStatus::kNotReady) {}
  ~FakeDeviceSwitcherResultDispatcher() override = default;

  segmentation_platform::ClassificationResult GetCachedClassificationResult()
      override {
    return classification_result;
  }

  segmentation_platform::ClassificationResult classification_result;
};

class OmniboxPositionMetricsTest : public PlatformTest {
 public:
  OmniboxPositionMetricsTest() = default;
  ~OmniboxPositionMetricsTest() override = default;

  void SetUp() override {
    PlatformTest::SetUp();
    prefs_ = std::make_unique<TestingPrefServiceSimple>();
    segmentation_platform::DeviceSwitcherResultDispatcher::RegisterProfilePrefs(
        prefs_->registry());
    device_info_tracker_ = std::make_unique<syncer::FakeDeviceInfoTracker>();
    device_switcher_result_dispatcher_ =
        std::make_unique<FakeDeviceSwitcherResultDispatcher>(
            &segmentation_platform_service_, device_info_tracker_.get(),
            prefs_.get(), &field_trial_register_);
  }

  void TearDown() override {
    PlatformTest::TearDown();
    device_switcher_result_dispatcher_.reset();
    prefs_.reset();
    device_info_tracker_.reset();
  }

 protected:
  // base::test::TaskEnvironment task_environment_;
  std::unique_ptr<TestingPrefServiceSimple> prefs_;
  std::unique_ptr<syncer::FakeDeviceInfoTracker> device_info_tracker_;
  testing::NiceMock<segmentation_platform::MockSegmentationPlatformService>
      segmentation_platform_service_;
  testing::NiceMock<MockFieldTrialRegister> field_trial_register_;

  std::unique_ptr<FakeDeviceSwitcherResultDispatcher>
      device_switcher_result_dispatcher_;
};

// Tests selected position logging on startup without device switcher result.
TEST_F(OmniboxPositionMetricsTest,
       SelectedPositionStartupNoDeviceSwitcherResult) {
  base::HistogramTester histogram_tester;

  // Top default.
  RecordSelectedPosition(ToolbarType::kPrimary, /*is_default=*/YES, nullptr);
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup", /* Top default */ 0, 1);

  // Bottom default.
  RecordSelectedPosition(ToolbarType::kSecondary, /*is_default=*/YES, nullptr);
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup", /* Bottom default */ 1, 1);

  // Top not default.
  RecordSelectedPosition(ToolbarType::kPrimary, /*is_default=*/NO, nullptr);
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup", /* Top not default */ 2, 1);

  // Bottom not default.
  RecordSelectedPosition(ToolbarType::kSecondary, /*is_default=*/NO, nullptr);
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup", /* Bottom not default */ 3,
      1);

  // These histograms should not be logged when there is no device switcher
  // result.
  histogram_tester.ExpectTotalCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.NotNew", 0);
  histogram_tester.ExpectTotalCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.Unavailable", 0);
  histogram_tester.ExpectTotalCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.IsSwitcher", 0);
  histogram_tester.ExpectTotalCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.NotSwitcher", 0);
}

// Tests selected position logging on first run without device switcher result.
TEST_F(OmniboxPositionMetricsTest, SelectedPositionFRENoDeviceSwitcherResult) {
  base::HistogramTester histogram_tester;

  // Top default.
  RecordSelectedPosition(ToolbarType::kPrimary, /*is_default=*/YES, nullptr);
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup",
      /* Top default */ 0, 1);
  // Bottom default.
  RecordSelectedPosition(ToolbarType::kSecondary, /*is_default=*/YES, nullptr);
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup",
      /* Bottom default */ 1, 1);
  // Top not default.
  RecordSelectedPosition(ToolbarType::kPrimary, /*is_default=*/NO, nullptr);
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup",
      /* Top not default */ 2, 1);
  // Bottom not default.
  RecordSelectedPosition(ToolbarType::kSecondary, /*is_default=*/NO, nullptr);
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup",
      /* Bottom not default */ 3, 1);

  // These histograms should not be logged when there is no device switcher
  // result.
  histogram_tester.ExpectTotalCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.NotNew", 0);
  histogram_tester.ExpectTotalCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.Unavailable", 0);
  histogram_tester.ExpectTotalCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.IsSwitcher", 0);
  histogram_tester.ExpectTotalCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.NotSwitcher", 0);
}

// Tests selected position logging with device switcher results.
TEST_F(OmniboxPositionMetricsTest, SelectedPositionDeviceSwitcherResult) {
  base::HistogramTester histogram_tester;
  segmentation_platform::ClassificationResult& classification_result =
      device_switcher_result_dispatcher_->classification_result;
  (void)classification_result;

  // Reset to new user.
  ResetFirstRunSentinel();

  // New user without classification result.
  RecordSelectedPosition(ToolbarType::kPrimary, /*is_default=*/YES,
                         device_switcher_result_dispatcher_.get());
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.Unavailable",
      /* Top default */ 0, 1);

  // Set status to Succeeded for the following tests.
  classification_result.status =
      segmentation_platform::PredictionStatus::kSucceeded;

  // Safari switcher.
  classification_result.ordered_labels = std::vector<std::string>(
      {segmentation_platform::DeviceSwitcherModel::kDesktopLabel,
       segmentation_platform::DeviceSwitcherModel::kSyncedAndFirstDeviceLabel,
       segmentation_platform::DeviceSwitcherModel::kNotSyncedLabel});
  RecordSelectedPosition(ToolbarType::kPrimary, /*is_default=*/YES,
                         device_switcher_result_dispatcher_.get());
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.IsSwitcher",
      /* Top default */ 0, 1);

  // Not Safari switcher.
  classification_result.ordered_labels = std::vector<std::string>(
      {segmentation_platform::DeviceSwitcherModel::kAndroidPhoneLabel,
       segmentation_platform::DeviceSwitcherModel::kIosTabletLabel});
  RecordSelectedPosition(ToolbarType::kPrimary, /*is_default=*/YES,
                         device_switcher_result_dispatcher_.get());
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.NotSwitcher",
      /* Top default */ 0, 1);

  // Not a new user.
  // Force first run recency to a large value.
  ForceFirstRunRecency(100);
  RecordSelectedPosition(ToolbarType::kPrimary, /*is_default=*/YES,
                         device_switcher_result_dispatcher_.get());
  histogram_tester.ExpectBucketCount(
      "IOS.Omnibox.Promo.SelectedPosition.Startup.NotNew",
      /* Top default */ 0, 1);

  ResetFirstRunSentinel();
}

}  // namespace