blob: 672383141c41f57c885938efd035e4054511ac03 [file] [log] [blame]
// Copyright 2022 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 "typecd/usb_monitor.h"
#include <string>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "typecd/usb_device.h"
namespace {
constexpr char kBusnum[] = "1\n";
constexpr char kDevnum[] = "1\n";
constexpr char kDeviceClass[] = "00\n";
constexpr char kSpeed[] = "10000\n";
constexpr char kVersion[] = "3.00";
constexpr char kInvalidVersion[] = "4.01";
constexpr int kTypecPortNum = 1;
} // namespace
namespace typecd {
class UsbMonitorTest : public ::testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
temp_dir_ = scoped_temp_dir_.GetPath();
}
public:
base::FilePath temp_dir_;
base::ScopedTempDir scoped_temp_dir_;
};
// Test UsbDevice with correct information is created and removed in UsbMonitor.
TEST_F(UsbMonitorTest, TestDeviceAddAndRemove) {
// Set up fake sysfs directory.
auto usb_sysfs_path = temp_dir_.Append("1-1");
ASSERT_TRUE(base::CreateDirectory(usb_sysfs_path));
auto busnum_path = usb_sysfs_path.Append("busnum");
ASSERT_TRUE(base::WriteFile(busnum_path, kBusnum, strlen(kBusnum)));
auto devnum_path = usb_sysfs_path.Append("devnum");
ASSERT_TRUE(base::WriteFile(devnum_path, kDevnum, strlen(kDevnum)));
auto connector_dir_path = usb_sysfs_path.Append("port/connector");
base::CreateDirectory(connector_dir_path);
auto uevent_path = connector_dir_path.Append("uevent");
auto uevent_typec_port =
base::StringPrintf("TYPEC_PORT=port%d", kTypecPortNum);
ASSERT_TRUE(base::WriteFile(uevent_path, uevent_typec_port.c_str(),
uevent_typec_port.length()));
auto speed_path = usb_sysfs_path.Append("speed");
ASSERT_TRUE(base::WriteFile(speed_path, kSpeed, strlen(kSpeed)));
auto device_class_path = usb_sysfs_path.Append("bDeviceClass");
ASSERT_TRUE(
base::WriteFile(device_class_path, kDeviceClass, strlen(kDeviceClass)));
auto version_path = usb_sysfs_path.Append("version");
ASSERT_TRUE(base::WriteFile(version_path, kVersion, strlen(kVersion)));
auto usb_monitor = std::make_unique<UsbMonitor>();
// New USB device created and added to the map.
usb_monitor->OnDeviceAddedOrRemoved(usb_sysfs_path, true);
auto usb_device = usb_monitor->GetDevice(usb_sysfs_path.BaseName().value());
EXPECT_NE(nullptr, usb_device);
EXPECT_EQ(std::stoi(kBusnum), usb_device->GetBusnum());
EXPECT_EQ(std::stoi(kDevnum), usb_device->GetDevnum());
EXPECT_EQ(kTypecPortNum, usb_device->GetTypecPortNum());
EXPECT_EQ(UsbSpeed::k10000, usb_device->GetSpeed());
EXPECT_EQ(UsbDeviceClass::kNone, usb_device->GetDeviceClass());
EXPECT_EQ(UsbVersion::k3_0, usb_device->GetVersion());
// USB device removed from the map.
usb_monitor->OnDeviceAddedOrRemoved(usb_sysfs_path, false);
EXPECT_EQ(nullptr, usb_monitor->GetDevice(usb_sysfs_path.BaseName().value()));
}
// Test UsbDevice created with no Type C port when given invalid uevent path.
TEST_F(UsbMonitorTest, TestInvalidUeventPath) {
// Set up fake sysfs directory.
auto usb_sysfs_path = temp_dir_.Append("1-1");
ASSERT_TRUE(base::CreateDirectory(usb_sysfs_path));
auto busnum_path = usb_sysfs_path.Append("busnum");
ASSERT_TRUE(base::WriteFile(busnum_path, kBusnum, strlen(kBusnum)));
auto devnum_path = usb_sysfs_path.Append("devnum");
ASSERT_TRUE(base::WriteFile(devnum_path, kDevnum, strlen(kDevnum)));
auto connector_dir_path = usb_sysfs_path.Append("invalid/path");
base::CreateDirectory(connector_dir_path);
auto uevent_path = connector_dir_path.Append("uevent");
auto uevent_typec_port =
base::StringPrintf("TYPEC_PORT=port%d", kTypecPortNum);
ASSERT_TRUE(base::WriteFile(uevent_path, uevent_typec_port.c_str(),
uevent_typec_port.length()));
auto usb_monitor = std::make_unique<UsbMonitor>();
// New USB device created and added to the map.
usb_monitor->OnDeviceAddedOrRemoved(usb_sysfs_path, true);
auto usb_device = usb_monitor->GetDevice(usb_sysfs_path.BaseName().value());
EXPECT_NE(nullptr, usb_device);
EXPECT_EQ(std::stoi(kBusnum), usb_device->GetBusnum());
EXPECT_EQ(std::stoi(kDevnum), usb_device->GetDevnum());
// No Type C port set.
EXPECT_EQ(-1, usb_device->GetTypecPortNum());
}
// Test UsbDevice created with no Type C port.
TEST_F(UsbMonitorTest, TestNoTypecPort) {
// Set up fake sysfs directory.
auto usb_sysfs_path = temp_dir_.Append("1-1");
ASSERT_TRUE(base::CreateDirectory(usb_sysfs_path));
auto busnum_path = usb_sysfs_path.Append("busnum");
ASSERT_TRUE(base::WriteFile(busnum_path, kBusnum, strlen(kBusnum)));
auto devnum_path = usb_sysfs_path.Append("devnum");
ASSERT_TRUE(base::WriteFile(devnum_path, kDevnum, strlen(kDevnum)));
auto usb_monitor = std::make_unique<UsbMonitor>();
// New USB device created and added to the map.
usb_monitor->OnDeviceAddedOrRemoved(usb_sysfs_path, true);
auto usb_device = usb_monitor->GetDevice(usb_sysfs_path.BaseName().value());
EXPECT_NE(nullptr, usb_device);
EXPECT_EQ(std::stoi(kBusnum), usb_device->GetBusnum());
EXPECT_EQ(std::stoi(kDevnum), usb_device->GetDevnum());
// No Type C port set.
EXPECT_EQ(-1, usb_device->GetTypecPortNum());
}
// Test a tree of UsbDevice with Type C port only available to the parent.
TEST_F(UsbMonitorTest, TestDeviceTree) {
// Set up fake parent sysfs directory.
auto parent_usb_sysfs_path = temp_dir_.Append("3-1");
ASSERT_TRUE(base::CreateDirectory(parent_usb_sysfs_path));
auto parent_busnum_path = parent_usb_sysfs_path.Append("busnum");
ASSERT_TRUE(base::WriteFile(parent_busnum_path, "3", 1));
auto parent_devnum_path = parent_usb_sysfs_path.Append("devnum");
ASSERT_TRUE(base::WriteFile(parent_devnum_path, "2", 1));
// Type C port number set only for parent device.
auto parent_connector_dir_path =
parent_usb_sysfs_path.Append("port/connector");
base::CreateDirectory(parent_connector_dir_path);
auto parent_uevent_path = parent_connector_dir_path.Append("uevent");
auto uevent_typec_port =
base::StringPrintf("TYPEC_PORT=port%d", kTypecPortNum);
ASSERT_TRUE(base::WriteFile(parent_uevent_path, uevent_typec_port.c_str(),
uevent_typec_port.length()));
// Set up fake child sysfs directory.
auto child_usb_sysfs_path = temp_dir_.Append("3-1.1");
ASSERT_TRUE(base::CreateDirectory(child_usb_sysfs_path));
auto child_busnum_path = child_usb_sysfs_path.Append("busnum");
ASSERT_TRUE(base::WriteFile(child_busnum_path, "3", 1));
auto child_devnum_path = child_usb_sysfs_path.Append("devnum");
ASSERT_TRUE(base::WriteFile(child_devnum_path, "8", 1));
// Set up fake grandchild sysfs directory.
auto grandchild_usb_sysfs_path = temp_dir_.Append("3-1.1.4");
ASSERT_TRUE(base::CreateDirectory(grandchild_usb_sysfs_path));
auto grandchild_busnum_path = grandchild_usb_sysfs_path.Append("busnum");
ASSERT_TRUE(base::WriteFile(grandchild_busnum_path, "3", 1));
auto grandchild_devnum_path = grandchild_usb_sysfs_path.Append("devnum");
ASSERT_TRUE(base::WriteFile(grandchild_devnum_path, "18", 2));
auto usb_monitor = std::make_unique<UsbMonitor>();
// New USB devices created and added to the map.
usb_monitor->OnDeviceAddedOrRemoved(parent_usb_sysfs_path, true);
usb_monitor->OnDeviceAddedOrRemoved(child_usb_sysfs_path, true);
usb_monitor->OnDeviceAddedOrRemoved(grandchild_usb_sysfs_path, true);
auto child_usb_device =
usb_monitor->GetDevice(child_usb_sysfs_path.BaseName().value());
auto grandchild_usb_device =
usb_monitor->GetDevice(grandchild_usb_sysfs_path.BaseName().value());
EXPECT_NE(nullptr, child_usb_device);
EXPECT_NE(nullptr, grandchild_usb_device);
// Children have the same Type C port number with the one set for parent.
EXPECT_EQ(kTypecPortNum, child_usb_device->GetTypecPortNum());
EXPECT_EQ(kTypecPortNum, grandchild_usb_device->GetTypecPortNum());
}
// Test USB device with invalid version is created in UsbMonitor.
TEST_F(UsbMonitorTest, TestInvalidVersion) {
// Set up fake sysfs directory.
auto usb_sysfs_path = temp_dir_.Append("2-1");
ASSERT_TRUE(base::CreateDirectory(usb_sysfs_path));
auto busnum_path = usb_sysfs_path.Append("busnum");
ASSERT_TRUE(base::WriteFile(busnum_path, kBusnum, strlen(kBusnum)));
auto devnum_path = usb_sysfs_path.Append("devnum");
ASSERT_TRUE(base::WriteFile(devnum_path, kDevnum, strlen(kDevnum)));
auto version_path = usb_sysfs_path.Append("version");
ASSERT_TRUE(
base::WriteFile(version_path, kInvalidVersion, strlen(kInvalidVersion)));
auto usb_monitor = std::make_unique<UsbMonitor>();
// New USB device created and added to the map.
usb_monitor->OnDeviceAddedOrRemoved(usb_sysfs_path, true);
auto usb_device = usb_monitor->GetDevice(usb_sysfs_path.BaseName().value());
EXPECT_NE(nullptr, usb_device);
EXPECT_EQ(std::stoi(kBusnum), usb_device->GetBusnum());
EXPECT_EQ(std::stoi(kDevnum), usb_device->GetDevnum());
EXPECT_EQ(UsbVersion::kOther, usb_device->GetVersion());
}
} // namespace typecd