chromium/chromeos/ash/components/peripheral_notification/peripheral_notification_manager_unittest.cc

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

#include "chromeos/ash/components/peripheral_notification/peripheral_notification_manager.h"

#include <memory>

#include "ash/constants/ash_features.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/dbus/pciguard/fake_pciguard_client.h"
#include "chromeos/ash/components/dbus/pciguard/pciguard_client.h"
#include "chromeos/ash/components/dbus/typecd/fake_typecd_client.h"
#include "chromeos/ash/components/dbus/typecd/typecd_client.h"
#include "services/device/public/cpp/test/fake_usb_device_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/typecd/dbus-constants.h"

namespace ash {

namespace {

const int kUsbConfigWithInterfaces = 1;
const int kBillboardDeviceClassCode = 17;
const int kNonBillboardDeviceClassCode = 16;
constexpr char thunderbolt_path_for_testing[] =
    "/tmp/tbt/sys/bus/thunderbolt/devices/0-0";
constexpr char root_prefix_for_testing[] = "/tmp/tbt";

}  // namespace

class FakeObserver : public PeripheralNotificationManager::Observer {
 public:
  FakeObserver() = default;
  ~FakeObserver() override = default;

  size_t num_limited_performance_notification_calls() const {
    return num_limited_performance_notification_calls_;
  }

  size_t num_guest_notification_calls() const {
    return num_guest_notification_calls_;
  }

  size_t num_peripheral_blocked_notification_calls() const {
    return num_peripheral_blocked_notification_calls_;
  }

  size_t num_billboard_notification_calls() const {
    return num_billboard_notification_calls_;
  }

  size_t num_invalid_dp_cable_notification_calls() const {
    return num_invalid_dp_cable_notification_calls_;
  }

  size_t num_invalid_usb4_valid_tbt_cable_notification_calls() const {
    return num_invalid_usb4_valid_tbt_cable_notification_calls_;
  }

  size_t num_invalid_usb4_cable_notification_calls() const {
    return num_invalid_usb4_cable_notification_calls_;
  }

  size_t num_invalid_tbt_cable_notification_calls() const {
    return num_invalid_tbt_cable_notification_calls_;
  }

  size_t num_speed_limiting_cable_notification_calls() const {
    return num_speed_limiting_cable_notification_calls_;
  }

  bool is_current_guest_device_tbt_only() const {
    return is_current_guest_device_tbt_only_;
  }

  // PeripheralNotificationManager::Observer:
  void OnLimitedPerformancePeripheralReceived() override {
    ++num_limited_performance_notification_calls_;
  }

  void OnGuestModeNotificationReceived(bool is_thunderbolt_only) override {
    is_current_guest_device_tbt_only_ = is_thunderbolt_only;
    ++num_guest_notification_calls_;
  }

  void OnPeripheralBlockedReceived() override {
    ++num_peripheral_blocked_notification_calls_;
  }

  void OnBillboardDeviceConnected() override {
    ++num_billboard_notification_calls_;
  }

  void OnInvalidDpCableWarning() override {
    ++num_invalid_dp_cable_notification_calls_;
  }

  void OnInvalidUSB4ValidTBTCableWarning() override {
    ++num_invalid_usb4_valid_tbt_cable_notification_calls_;
  }

  void OnInvalidUSB4CableWarning() override {
    ++num_invalid_usb4_cable_notification_calls_;
  }

  void OnInvalidTBTCableWarning() override {
    ++num_invalid_tbt_cable_notification_calls_;
  }

  void OnSpeedLimitingCableWarning() override {
    ++num_speed_limiting_cable_notification_calls_;
  }

 private:
  size_t num_limited_performance_notification_calls_ = 0u;
  size_t num_guest_notification_calls_ = 0u;
  size_t num_peripheral_blocked_notification_calls_ = 0u;
  size_t num_billboard_notification_calls_ = 0u;
  size_t num_invalid_dp_cable_notification_calls_ = 0u;
  size_t num_invalid_usb4_valid_tbt_cable_notification_calls_ = 0u;
  size_t num_invalid_usb4_cable_notification_calls_ = 0u;
  size_t num_invalid_tbt_cable_notification_calls_ = 0u;
  size_t num_speed_limiting_cable_notification_calls_ = 0u;
  bool is_current_guest_device_tbt_only_ = false;
};

class PeripheralNotificationManagerTest : public testing::Test {
 protected:
  PeripheralNotificationManagerTest() = default;
  PeripheralNotificationManagerTest(const PeripheralNotificationManagerTest&) =
      delete;
  PeripheralNotificationManagerTest& operator=(
      const PeripheralNotificationManagerTest&) = delete;
  ~PeripheralNotificationManagerTest() override = default;

  // testing::Test:
  void SetUp() override {
    TypecdClient::InitializeFake();
    fake_typecd_client_ = static_cast<FakeTypecdClient*>(TypecdClient::Get());

    PciguardClient::InitializeFake();
    fake_pciguard_client_ =
        static_cast<FakePciguardClient*>(PciguardClient::Get());

    base::DeletePathRecursively(base::FilePath(thunderbolt_path_for_testing));
  }

  void InitializeManager(bool is_guest_session,
                         bool is_pcie_tunneling_allowed) {
    PeripheralNotificationManager::Initialize(is_guest_session,
                                              is_pcie_tunneling_allowed);
    manager_ = PeripheralNotificationManager::Get();

    manager_->AddObserver(&fake_observer_);
    manager_->SetRootPrefixForTesting(root_prefix_for_testing);
  }

  void TearDown() override {
    manager_->RemoveObserver(&fake_observer_);
    PeripheralNotificationManager::Shutdown();
    TypecdClient::Shutdown();
    PciguardClient::Shutdown();
    base::DeletePathRecursively(base::FilePath(thunderbolt_path_for_testing));
  }

  FakeTypecdClient* fake_typecd_client() { return fake_typecd_client_; }

  FakePciguardClient* fake_pciguard_client() { return fake_pciguard_client_; }

  size_t GetNumLimitedPerformanceObserverCalls() {
    return fake_observer_.num_limited_performance_notification_calls();
  }

  size_t GetNumGuestModeNotificationObserverCalls() {
    return fake_observer_.num_guest_notification_calls();
  }

  size_t GetNumPeripheralBlockedNotificationObserverCalls() {
    return fake_observer_.num_peripheral_blocked_notification_calls();
  }

  size_t GetNumBillboardNotificationObserverCalls() {
    return fake_observer_.num_billboard_notification_calls();
  }

  size_t GetInvalidDpCableNotificationObserverCalls() {
    return fake_observer_.num_invalid_dp_cable_notification_calls();
  }

  size_t GetInvalidUSB4ValidTBTCableNotificationObserverCalls() {
    return fake_observer_.num_invalid_usb4_valid_tbt_cable_notification_calls();
  }

  size_t GetInvalidUSB4CableNotificationObserverCalls() {
    return fake_observer_.num_invalid_usb4_cable_notification_calls();
  }

  size_t GetInvalidTBTCableNotificationObserverCalls() {
    return fake_observer_.num_invalid_tbt_cable_notification_calls();
  }

  size_t GetSpeedLimitingCableNotificationObserverCalls() {
    return fake_observer_.num_speed_limiting_cable_notification_calls();
  }

  bool GetIsCurrentGuestDeviceTbtOnly() {
    return fake_observer_.is_current_guest_device_tbt_only();
  }

  base::HistogramTester histogram_tester_;

  base::test::TaskEnvironment* task_environment() { return &task_environment_; }

 private:
  base::test::TaskEnvironment task_environment_;
  raw_ptr<FakeTypecdClient, DanglingUntriaged> fake_typecd_client_;
  raw_ptr<FakePciguardClient, DanglingUntriaged> fake_pciguard_client_;
  raw_ptr<PeripheralNotificationManager, DanglingUntriaged> manager_ = nullptr;
  FakeObserver fake_observer_;
};

scoped_refptr<device::FakeUsbDeviceInfo> CreateTestDeviceOfClass(
    uint8_t device_class) {
  auto config = device::mojom::UsbConfigurationInfo::New();
  config->configuration_value = kUsbConfigWithInterfaces;

  auto alternate = device::mojom::UsbAlternateInterfaceInfo::New();
  alternate->alternate_setting = 0;
  alternate->class_code = device_class;
  alternate->subclass_code = 0xff;
  alternate->protocol_code = 0xff;

  auto interface = device::mojom::UsbInterfaceInfo::New();
  interface->interface_number = 0;
  interface->alternates.push_back(std::move(alternate));

  config->interfaces.push_back(std::move(interface));

  std::vector<device::mojom::UsbConfigurationInfoPtr> configs;
  configs.push_back(std::move(config));

  scoped_refptr<device::FakeUsbDeviceInfo> device =
      base::MakeRefCounted<device::FakeUsbDeviceInfo>(
          /*vendor_id=*/0, /*product_id=*/1, device_class, std::move(configs));
  device->SetActiveConfig(kUsbConfigWithInterfaces);
  return device;
}

TEST_F(PeripheralNotificationManagerTest, InitialTest) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/false);
  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_EQ(0u, GetNumBillboardNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
}

TEST_F(PeripheralNotificationManagerTest, LimitedPerformanceNotification) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/false);

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kAltModeFallbackDueToPciguard,
      0);

  // Simulate emitting D-Bus signal.
  fake_typecd_client()->EmitThunderboltDeviceConnectedSignal(
      /*is_thunderbolt_only=*/false);
  // pcie tunneling is not allowed and a alt-mode device has been plugged in.
  // Expect the notification observer to be called.
  EXPECT_EQ(1u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kAltModeFallbackDueToPciguard,
      1);
}

TEST_F(PeripheralNotificationManagerTest, NoNotificationShown) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/true);

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kTBTSupportedAndAllowed,
      0);

  // Simulate emitting D-Bus signal.
  fake_typecd_client()->EmitThunderboltDeviceConnectedSignal(
      /*is_thunderbolt_only=*/false);
  // Pcie tunneling allowed, we do not show any notifications for this case.
  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kTBTSupportedAndAllowed,
      1);

  // Simulate emitting a new D-Bus signal, this time with |is_thunderbolt_only|
  // set to true.
  fake_typecd_client()->EmitThunderboltDeviceConnectedSignal(
      /*is_thunderbolt_only=*/true);
  // Pcie tunneling allowed, we do not show any notifications for this case.
  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  // No observer was called, therefore don't expect this to be updated.
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kTBTSupportedAndAllowed,
      2);
}

TEST_F(PeripheralNotificationManagerTest, TBTOnlyAndBlockedByPciguard) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/false);

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kTBTOnlyAndBlockedByPciguard,
      0);

  // Simulate emitting D-Bus signal.
  fake_typecd_client()->EmitThunderboltDeviceConnectedSignal(
      /*is_thunderbolt_only=*/true);
  // Pcie tunneling allowed, we do not show any notifications for this case.
  EXPECT_EQ(1u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kTBTOnlyAndBlockedByPciguard,
      1);
}

TEST_F(PeripheralNotificationManagerTest, GuestNotificationLimitedPerformance) {
  InitializeManager(/*is_guest_profile=*/true,
                    /*is_pcie_tunneling_allowed=*/false);

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kAltModeFallbackInGuestSession,
      0);

  // Simulate emitting D-Bus signal.
  fake_typecd_client()->EmitThunderboltDeviceConnectedSignal(
      /*is_thunderbolt_only=*/false);
  // Pcie tunneling not allowed and user is in guest session. The device
  // supports an alt-mode, expect the notification observer to be called.
  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(1u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kAltModeFallbackInGuestSession,
      1);
}

TEST_F(PeripheralNotificationManagerTest, GuestNotificationRestricted) {
  InitializeManager(/*is_guest_profile=*/true,
                    /*is_pcie_tunneling_allowed=*/false);

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_FALSE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kTBTOnlyAndBlockedInGuestSession,
      0);

  // Simulate emitting D-Bus signal.
  fake_typecd_client()->EmitThunderboltDeviceConnectedSignal(
      /*is_thunderbolt_only=*/true);
  // Pcie tunneling not allowed and user is in guest session. The device
  // does not support alt-mode, expect the notification observer to be called.
  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(1u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_TRUE(GetIsCurrentGuestDeviceTbtOnly());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kTBTOnlyAndBlockedInGuestSession,
      1);
}

TEST_F(PeripheralNotificationManagerTest, BlockedDeviceReceived) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/true);

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_EQ(0u, GetNumPeripheralBlockedNotificationObserverCalls());

  // Simulate emitting D-Bus signal for a blocked device received.
  fake_pciguard_client()->EmitDeviceBlockedSignal(/*device_name=*/"test");

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_EQ(1u, GetNumPeripheralBlockedNotificationObserverCalls());

  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kPeripheralBlocked,
      1);
}

TEST_F(PeripheralNotificationManagerTest, BillboardDevice) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(features::kPcieBillboardNotification);

  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/true);

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_EQ(0u, GetNumPeripheralBlockedNotificationObserverCalls());
  EXPECT_EQ(0u, GetNumBillboardNotificationObserverCalls());

  // Simulate connecting a billboard device.
  const auto fake_device = CreateTestDeviceOfClass(kBillboardDeviceClassCode);
  const auto device = fake_device->GetDeviceInfo().Clone();
  PeripheralNotificationManager::Get()->OnDeviceConnected(device.get());

  task_environment()->RunUntilIdle();

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_EQ(0u, GetNumPeripheralBlockedNotificationObserverCalls());
  EXPECT_EQ(1u, GetNumBillboardNotificationObserverCalls());

  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kBillboardDevice,
      1);

  // Connect a non-billboard device. There should be no notification.
  const auto fake_device_1 =
      CreateTestDeviceOfClass(kNonBillboardDeviceClassCode);
  const auto device_1 = fake_device_1->GetDeviceInfo().Clone();
  PeripheralNotificationManager::Get()->OnDeviceConnected(device_1.get());

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_EQ(0u, GetNumPeripheralBlockedNotificationObserverCalls());
  EXPECT_EQ(1u, GetNumBillboardNotificationObserverCalls());

  // Fake a board that supports Thunderbolt.
  auto thunderbolt_directory = std::make_unique<base::ScopedTempDir>();
  EXPECT_TRUE(thunderbolt_directory->CreateUniqueTempDirUnderPath(
      base::FilePath(thunderbolt_path_for_testing)));

  // Connect a billboard device. There should be no notification.
  const auto fake_device_2 = CreateTestDeviceOfClass(kBillboardDeviceClassCode);
  const auto device_2 = fake_device_2->GetDeviceInfo().Clone();
  PeripheralNotificationManager::Get()->OnDeviceConnected(device_2.get());

  EXPECT_EQ(0u, GetNumLimitedPerformanceObserverCalls());
  EXPECT_EQ(0u, GetNumGuestModeNotificationObserverCalls());
  EXPECT_EQ(0u, GetNumPeripheralBlockedNotificationObserverCalls());
  EXPECT_EQ(1u, GetNumBillboardNotificationObserverCalls());

  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kBillboardDevice,
      1);
}

TEST_F(PeripheralNotificationManagerTest, InvalidDpCableWarning) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/false);

  EXPECT_EQ(0u, GetInvalidDpCableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kInvalidDpCable,
      0);

  // Simulate emitting D-Bus signal for an invalid dp cable.
  typecd::CableWarningType cable_warning_type =
      typecd::CableWarningType::kInvalidDpCable;
  fake_typecd_client()->EmitCableWarningSignal(cable_warning_type);

  EXPECT_EQ(1u, GetInvalidDpCableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kInvalidDpCable,
      1);
}

TEST_F(PeripheralNotificationManagerTest, InvalidUSB4ValidTBTCableWarning) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/false);
  EXPECT_EQ(0u, GetInvalidUSB4ValidTBTCableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kInvalidUSB4ValidTBTCable,
      0);

  typecd::CableWarningType cable_warning_type =
      typecd::CableWarningType::kInvalidUSB4ValidTBTCable;
  fake_typecd_client()->EmitCableWarningSignal(cable_warning_type);
  EXPECT_EQ(1u, GetInvalidUSB4ValidTBTCableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kInvalidUSB4ValidTBTCable,
      1);
}

TEST_F(PeripheralNotificationManagerTest, InvalidUSB4CableWarning) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/false);
  EXPECT_EQ(0u, GetInvalidUSB4CableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kInvalidUSB4Cable,
      0);

  typecd::CableWarningType cable_warning_type =
      typecd::CableWarningType::kInvalidUSB4Cable;
  fake_typecd_client()->EmitCableWarningSignal(cable_warning_type);
  EXPECT_EQ(1u, GetInvalidUSB4CableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kInvalidUSB4Cable,
      1);
}

TEST_F(PeripheralNotificationManagerTest, InvalidTBTCableWarning) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/false);
  EXPECT_EQ(0u, GetInvalidTBTCableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kInvalidTBTCable,
      0);

  typecd::CableWarningType cable_warning_type =
      typecd::CableWarningType::kInvalidTBTCable;
  fake_typecd_client()->EmitCableWarningSignal(cable_warning_type);
  EXPECT_EQ(1u, GetInvalidTBTCableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kInvalidTBTCable,
      1);
}

TEST_F(PeripheralNotificationManagerTest, SpeedLimitingCableWarning) {
  InitializeManager(/*is_guest_profile=*/false,
                    /*is_pcie_tunneling_allowed=*/false);
  EXPECT_EQ(0u, GetSpeedLimitingCableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kSpeedLimitingCable,
      0);

  typecd::CableWarningType cable_warning_type =
      typecd::CableWarningType::kSpeedLimitingCable;
  fake_typecd_client()->EmitCableWarningSignal(cable_warning_type);
  EXPECT_EQ(1u, GetSpeedLimitingCableNotificationObserverCalls());
  histogram_tester_.ExpectBucketCount(
      "Ash.Peripheral.ConnectivityResults",
      PeripheralNotificationManager::PeripheralConnectivityResults::
          kSpeedLimitingCable,
      1);
}

}  // namespace ash