blob: b20dfc50424bc6e3226e57727732ce591adf9750 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include <utility>
#include <vector>
#include <base/check_op.h>
#include <base/strings/stringprintf.h>
#include "diagnostics/common/file_test_utils.h"
#include "diagnostics/common/mojo_type_utils.h"
#include "diagnostics/cros_healthd/fetchers/bus_fetcher.h"
#include "diagnostics/cros_healthd/fetchers/bus_fetcher_constants.h"
#include "diagnostics/cros_healthd/system/mock_context.h"
namespace diagnostics {
namespace {
constexpr char kFakePathPciDevices[] = "sys/devices/pci0000:00";
constexpr char kLinkPciDevices[] = "../../../devices/pci0000:00";
constexpr char kFakePathUsbDevices[] =
"sys/devices/pci0000:00/0000:00:14.0/usb1";
constexpr char kLinkUsbDevices[] =
"../../../devices/pci0000:00/0000:00:14.0/usb1";
constexpr char kLinkPciDriver[] = "../../../bus/pci/drivers";
constexpr char kLinkUsbDriver[] = "../../../../../../bus/usb/drivers";
constexpr char kFakePciVendorName[] = "Vendor:12AB";
constexpr char kFakePciProductName[] = "Device:34CD";
constexpr char kFakeUsbVendorName[] = "usb:v12ABp34CD";
constexpr auto kFakeUsbProductName = kFakeUsbVendorName;
constexpr auto kFakeUsbFallbackVendorName = "Fallback Vendor Name";
constexpr auto kFakeUsbFallbackProductName = "Fallback Product Name";
constexpr uint8_t kFakeClass = 0x0a;
constexpr uint8_t kFakeSubclass = 0x1b;
constexpr uint8_t kFakeProg = 0x2c;
constexpr uint8_t kFakeProtocol = kFakeProg;
constexpr uint16_t kFakeVendor = 0x12ab;
constexpr uint16_t kFakeDevice = 0x34cd;
constexpr char kFakeDriver[] = "driver";
namespace mojo_ipc = ::chromeos::cros_healthd::mojom;
std::string ToFixHexStr(uint8_t val) {
return base::StringPrintf("%02x", val);
}
std::string ToFixHexStr(uint16_t val) {
return base::StringPrintf("%04x", val);
}
class BusFetcherTest : public BaseFileTest {
public:
BusFetcherTest() = default;
BusFetcherTest(const BusFetcherTest&) = delete;
BusFetcherTest& operator=(const BusFetcherTest&) = delete;
void SetUp() override {
SetTestRoot(mock_context_.root_dir());
}
mojo_ipc::BusDevicePtr& AddExpectedPciDevice() {
auto device = mojo_ipc::BusDevice::New();
auto pci_info = mojo_ipc::PciBusInfo::New();
device->vendor_name = kFakePciVendorName;
device->product_name = kFakePciProductName;
device->device_class = mojo_ipc::BusDeviceClass::kOthers;
pci_info->class_id = kFakeClass;
pci_info->subclass_id = kFakeSubclass;
pci_info->prog_if_id = kFakeProg;
pci_info->vendor_id = kFakeVendor;
pci_info->device_id = kFakeDevice;
pci_info->driver = kFakeDriver;
device->bus_info = mojo_ipc::BusInfo::NewPciBusInfo(std::move(pci_info));
expected_bus_devices_.push_back(std::move(device));
return expected_bus_devices_.back();
}
mojo_ipc::BusDevicePtr& AddExpectedUsbDevice(size_t interface_count) {
CHECK_GE(interface_count, 1);
auto device = mojo_ipc::BusDevice::New();
auto usb_info = mojo_ipc::UsbBusInfo::New();
device->vendor_name = kFakeUsbVendorName;
device->product_name = kFakeUsbProductName;
device->device_class = mojo_ipc::BusDeviceClass::kOthers;
usb_info->class_id = kFakeClass;
usb_info->subclass_id = kFakeSubclass;
usb_info->protocol_id = kFakeProtocol;
usb_info->vendor_id = kFakeVendor;
usb_info->product_id = kFakeDevice;
for (size_t i = 0; i < interface_count; ++i) {
auto usb_if_info = mojo_ipc::UsbBusInterfaceInfo::New();
usb_if_info->interface_number = static_cast<uint8_t>(i);
usb_if_info->class_id = kFakeClass;
usb_if_info->subclass_id = kFakeSubclass;
usb_if_info->protocol_id = kFakeProtocol;
usb_if_info->driver = kFakeDriver;
usb_info->interfaces.push_back(std::move(usb_if_info));
}
device->bus_info = mojo_ipc::BusInfo::NewUsbBusInfo(std::move(usb_info));
expected_bus_devices_.push_back(std::move(device));
return expected_bus_devices_.back();
}
void SetExpectedBusDevices() {
for (size_t i = 0; i < expected_bus_devices_.size(); ++i) {
const auto& bus_info = expected_bus_devices_[i]->bus_info;
switch (bus_info->which()) {
case mojo_ipc::BusInfo::Tag::PCI_BUS_INFO:
SetPciBusInfo(bus_info->get_pci_bus_info(), i);
break;
case mojo_ipc::BusInfo::Tag::USB_BUS_INFO:
SetUsbBusInfo(bus_info->get_usb_bus_info(), i);
break;
}
}
}
void SetPciBusInfo(const mojo_ipc::PciBusInfoPtr& pci_info, size_t id) {
const auto dir = kFakePathPciDevices;
const auto dev = base::StringPrintf("0000:00:%02zx.0", id);
SetSymbolicLink({kLinkPciDevices, dev}, {kPathSysPci, dev});
auto class_str =
base::StringPrintf("%#02x%02x%02x", pci_info->class_id,
pci_info->subclass_id, pci_info->prog_if_id);
SetFile({dir, dev, kFilePciClass}, class_str);
SetFile({dir, dev, kFilePciVendor},
"0x" + ToFixHexStr(pci_info->vendor_id));
SetFile({dir, dev, kFilePciDevice},
"0x" + ToFixHexStr(pci_info->device_id));
if (pci_info->driver) {
SetSymbolicLink({kLinkPciDriver, pci_info->driver.value()},
{dir, dev, kFileDriver});
}
}
void SetUsbBusInfo(const mojo_ipc::UsbBusInfoPtr& usb_info, size_t id) {
const auto dir = kFakePathUsbDevices;
const auto dev = base::StringPrintf("1-%zu", id);
SetSymbolicLink({kLinkUsbDevices, dev}, {kPathSysUsb, dev});
SetFile({dir, dev, kFileUsbDevClass}, ToFixHexStr(usb_info->class_id));
SetFile({dir, dev, kFileUsbDevSubclass},
ToFixHexStr(usb_info->subclass_id));
SetFile({dir, dev, kFileUsbDevProtocol},
ToFixHexStr(usb_info->protocol_id));
SetFile({dir, dev, kFileUsbVendor}, ToFixHexStr(usb_info->vendor_id));
SetFile({dir, dev, kFileUsbProduct}, ToFixHexStr(usb_info->product_id));
SetFile({dir, dev, kFileUsbManufacturerName}, kFakeUsbFallbackVendorName);
SetFile({dir, dev, kFileUsbProductName}, kFakeUsbFallbackProductName);
for (size_t i = 0; i < usb_info->interfaces.size(); ++i) {
const auto dev_if = base::StringPrintf("1-%zu:1.%zu", id, i);
const mojo_ipc::UsbBusInterfaceInfoPtr& usb_if_info =
usb_info->interfaces[i];
ASSERT_EQ(usb_if_info->interface_number, static_cast<uint8_t>(i));
SetFile({dir, dev, dev_if, kFileUsbIFNumber},
ToFixHexStr(usb_if_info->interface_number));
SetFile({dir, dev, dev_if, kFileUsbIFClass},
ToFixHexStr(usb_if_info->class_id));
SetFile({dir, dev, dev_if, kFileUsbIFSubclass},
ToFixHexStr(usb_if_info->subclass_id));
SetFile({dir, dev, dev_if, kFileUsbIFProtocol},
ToFixHexStr(usb_if_info->protocol_id));
if (usb_if_info->driver) {
SetSymbolicLink({kLinkUsbDriver, usb_if_info->driver.value()},
{dir, dev, dev_if, kFileDriver});
}
}
}
void FetchBusDevices() {
auto res = bus_fetcher_.FetchBusDevices();
ASSERT_TRUE(res->is_bus_devices());
const auto& bus_devices = res->get_bus_devices();
const auto got = Sorted(bus_devices);
const auto expected = Sorted(expected_bus_devices_);
EXPECT_EQ(got, expected) << GetDiffString(got, expected);
}
protected:
std::vector<mojo_ipc::BusDevicePtr> expected_bus_devices_;
MockContext mock_context_;
BusFetcher bus_fetcher_{&mock_context_};
};
TEST_F(BusFetcherTest, TestFetchPci) {
AddExpectedPciDevice();
SetExpectedBusDevices();
FetchBusDevices();
}
TEST_F(BusFetcherTest, TestFetchUsbBusInfo) {
AddExpectedUsbDevice(1);
SetExpectedBusDevices();
FetchBusDevices();
}
TEST_F(BusFetcherTest, TestFetchMultiple) {
AddExpectedPciDevice();
AddExpectedPciDevice();
AddExpectedPciDevice();
AddExpectedUsbDevice(1);
AddExpectedUsbDevice(2);
AddExpectedUsbDevice(3);
SetExpectedBusDevices();
FetchBusDevices();
}
TEST_F(BusFetcherTest, TestUsbFallback) {
auto& bus_info = AddExpectedUsbDevice(1);
bus_info->vendor_name = kFakeUsbFallbackVendorName;
bus_info->product_name = kFakeUsbFallbackProductName;
mock_context_.fake_udev()->fake_udev_hwdb()->SetReturnEmptyProperties(true);
SetExpectedBusDevices();
FetchBusDevices();
}
} // namespace
} // namespace diagnostics