chromium/ui/events/ash/keyboard_info_metrics_unittest.cc

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

#include "ui/events/ash/keyboard_info_metrics.h"

#include "base/test/metrics/histogram_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/ash/keyboard_capability.h"

namespace ash {

namespace {

using DeviceType = ui::KeyboardCapability::DeviceType;
using KeyboardTopRowLayout = ui::KeyboardCapability::KeyboardTopRowLayout;
using KeyboardInfo = ui::KeyboardCapability::KeyboardInfo;

}  // namespace

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

  void SetUp() override {
    histogram_tester_ = std::make_unique<base::HistogramTester>();
  }

 protected:
  std::unique_ptr<base::HistogramTester> histogram_tester_;
};

TEST_F(KeyboardInfoMetricsTest, Layout1) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayout1;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType",
      ui::KeyboardTopRowLayoutForMetric::kLayout1, 1);
}

TEST_F(KeyboardInfoMetricsTest, Layout2) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayout2;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType",
      ui::KeyboardTopRowLayoutForMetric::kLayout2, 1);
}

TEST_F(KeyboardInfoMetricsTest, Layout2WithAssistant) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayout2;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/true,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType", 1);
  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType",
      ui::KeyboardTopRowLayoutForMetric::kLayout2WithAssistant, 1);
}

TEST_F(KeyboardInfoMetricsTest, Layout3) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayoutWilco;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType",
      ui::KeyboardTopRowLayoutForMetric::kLayout3, 1);
}

TEST_F(KeyboardInfoMetricsTest, Layout4) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayoutDrallion;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType",
      ui::KeyboardTopRowLayoutForMetric::kLayout4, 1);
}

TEST_F(KeyboardInfoMetricsTest, LayoutCustom1) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayoutCustom;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType",
      ui::KeyboardTopRowLayoutForMetric::kLayoutCustom1, 1);
}

TEST_F(KeyboardInfoMetricsTest, LayoutCustom2) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayoutCustom;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/true);

  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType", 1);
  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType",
      ui::KeyboardTopRowLayoutForMetric::kLayoutCustom2, 1);
}

class LayoutsBesidesLayout2WithAssistantKey
    : public KeyboardInfoMetricsTest,
      public testing::WithParamInterface<KeyboardTopRowLayout> {};

// Test that non-Layout2 keyboards don't emit the wrong type when
// has_assistant_key is true. Note that this case shouldn't come up in reality.
INSTANTIATE_TEST_SUITE_P(
    All,
    LayoutsBesidesLayout2WithAssistantKey,
    testing::ValuesIn(std::vector<KeyboardTopRowLayout>{
        KeyboardTopRowLayout::kKbdTopRowLayout1,
        /* Purposefully excluding Layout2 (see comment above)*/
        KeyboardTopRowLayout::kKbdTopRowLayoutWilco,
        KeyboardTopRowLayout::kKbdTopRowLayoutDrallion,
        KeyboardTopRowLayout::kKbdTopRowLayoutCustom,
    }));

TEST_P(LayoutsBesidesLayout2WithAssistantKey, Layout2WithAssistantNotEmitted) {
  KeyboardTopRowLayout keyboard_top_row_layout = GetParam();

  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout = keyboard_top_row_layout;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/true,
                                /*has_right_alt_key=*/false);

  // When has_assistant_key is true, kLayout2WithAssistant should not be
  // recorded unless the keyboard layout is Layout2.
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType",
      ui::KeyboardTopRowLayoutForMetric::kLayout2WithAssistant, 0);
}

class NonInternalTopRowLayoutTest
    : public KeyboardInfoMetricsTest,
      public testing::WithParamInterface<DeviceType> {};

// Test that non-kDeviceInternalKeyboards do not emit metrics related to the
// internal keyboard top-row layout.
INSTANTIATE_TEST_SUITE_P(
    All,
    NonInternalTopRowLayoutTest,
    testing::ValuesIn(std::vector<DeviceType>{
        DeviceType::kDeviceUnknown,
        DeviceType::kDeviceInternalRevenKeyboard,
        DeviceType::kDeviceExternalAppleKeyboard,
        DeviceType::kDeviceExternalChromeOsKeyboard,
        DeviceType::kDeviceExternalNullTopRowChromeOsKeyboard,
        DeviceType::kDeviceExternalGenericKeyboard,
        DeviceType::kDeviceExternalUnknown,
        DeviceType::kDeviceHotrodRemote,
        DeviceType::kDeviceVirtualCoreKeyboard,
    }));

TEST_P(NonInternalTopRowLayoutTest,
       NonInternalKeyboardDoesNotEmitTopRowLayout) {
  DeviceType device_type = GetParam();

  KeyboardInfo external_keyboard_info;
  external_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayoutCustom;
  external_keyboard_info.device_type = device_type;
  ui::RecordKeyboardInfoMetrics(external_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowLayoutType", 0);
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.NumberOfTopRowKeys",
      0);
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      0);
}

class NonCustomLayoutTopRowKeysTest
    : public KeyboardInfoMetricsTest,
      public testing::WithParamInterface<KeyboardTopRowLayout> {};

// Test that kDeviceInternalKeyboards do not emit metrics related to the top-row
// keys if the keyboard layout is not Custom.
INSTANTIATE_TEST_SUITE_P(All,
                         NonCustomLayoutTopRowKeysTest,
                         testing::ValuesIn(std::vector<KeyboardTopRowLayout>{
                             KeyboardTopRowLayout::kKbdTopRowLayout1,
                             KeyboardTopRowLayout::kKbdTopRowLayout2,
                             KeyboardTopRowLayout::kKbdTopRowLayoutWilco,
                             KeyboardTopRowLayout::kKbdTopRowLayoutDrallion,
                         }));

TEST_P(NonCustomLayoutTopRowKeysTest,
       NonCustomLayoutDoesNotEmitTopRowKeysMetrics) {
  KeyboardTopRowLayout keyboard_top_row_layout = GetParam();

  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.device_type =
      ui::KeyboardCapability::DeviceType::kDeviceInternalKeyboard;
  internal_keyboard_info.top_row_layout = keyboard_top_row_layout;
  internal_keyboard_info.top_row_action_keys = {
      ui::TopRowActionKey::kBack,
      ui::TopRowActionKey::kForward,
      ui::TopRowActionKey::kRefresh,
      ui::TopRowActionKey::kFullscreen,
      ui::TopRowActionKey::kAllApplications,
      ui::TopRowActionKey::kScreenBrightnessDown,
      ui::TopRowActionKey::kScreenBrightnessUp,
      ui::TopRowActionKey::kVolumeMute,
      ui::TopRowActionKey::kVolumeDown,
      ui::TopRowActionKey::kVolumeUp,
  };
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.NumberOfTopRowKeys",
      0);
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      0);
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.NumberOfTopRowKeys", 1);
  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.NumberOfTopRowKeys", 10, 1);
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent", 10);
}

TEST_F(KeyboardInfoMetricsTest, CustomLayout_NumberOfTopRowKeys) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayoutCustom;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  internal_keyboard_info.top_row_action_keys = {
      ui::TopRowActionKey::kBack,
      ui::TopRowActionKey::kForward,
      ui::TopRowActionKey::kRefresh,
      ui::TopRowActionKey::kFullscreen,
      ui::TopRowActionKey::kAllApplications,
      ui::TopRowActionKey::kScreenBrightnessDown,
      ui::TopRowActionKey::kScreenBrightnessUp,
      ui::TopRowActionKey::kVolumeMute,
      ui::TopRowActionKey::kVolumeDown,
      ui::TopRowActionKey::kVolumeUp,
  };
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.NumberOfTopRowKeys",
      10, 1);
}

TEST_F(KeyboardInfoMetricsTest, CustomLayout_NoKeys) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayoutCustom;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  // This shouldn't happen, but the metric should still technically emit.
  internal_keyboard_info.top_row_action_keys = {};
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectUniqueSample(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.NumberOfTopRowKeys",
      0, 1);
}

TEST_F(KeyboardInfoMetricsTest, CustomLayout_SpecificTopRowKeys) {
  KeyboardInfo internal_keyboard_info;
  internal_keyboard_info.top_row_layout =
      KeyboardTopRowLayout::kKbdTopRowLayoutCustom;
  internal_keyboard_info.device_type = DeviceType::kDeviceInternalKeyboard;
  internal_keyboard_info.top_row_action_keys = {
      ui::TopRowActionKey::kBack,
      ui::TopRowActionKey::kForward,
      ui::TopRowActionKey::kRefresh,
      ui::TopRowActionKey::kFullscreen,
      ui::TopRowActionKey::kAllApplications,
      ui::TopRowActionKey::kScreenBrightnessDown,
      ui::TopRowActionKey::kScreenBrightnessUp,
      ui::TopRowActionKey::kVolumeMute,
      ui::TopRowActionKey::kVolumeDown,
      ui::TopRowActionKey::kVolumeUp,
  };
  ui::RecordKeyboardInfoMetrics(internal_keyboard_info,
                                /*has_assistant_key=*/false,
                                /*has_right_alt_key=*/false);

  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      10);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kBack, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kForward, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kRefresh, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kFullscreen, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kAllApplications, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kScreenBrightnessDown, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kScreenBrightnessUp, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kVolumeMute, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kVolumeDown, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.CustomTopRowLayout.TopRowKeysPresent",
      ui::TopRowActionKey::kVolumeUp, 1);
  histogram_tester_->ExpectTotalCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent", 10);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kBack, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kForward, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kRefresh, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kFullscreen, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kAllApplications, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kScreenBrightnessDown, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kScreenBrightnessUp, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kVolumeMute, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kVolumeDown, 1);
  histogram_tester_->ExpectBucketCount(
      "ChromeOS.Inputs.InternalKeyboard.TopRowKeysPresent",
      ui::TopRowActionKey::kVolumeUp, 1);
}

}  // namespace ash