blob: 8010c57b9f4a539a6444ed835fe694a1fa6ac81c [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/device/usb/usb_device_handle_usbfs.h"
#include "base/files/scoped_file.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "services/device/usb/mock_usb_device.h"
#include "services/device/usb/usb_descriptors.h"
#include "services/device/usb/usb_device.h"
#include "services/device/usb/usb_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
#include "base/test/scoped_feature_list.h"
#include "services/device/public/cpp/device_features.h"
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
namespace device {
class MockBlockingTaskRunnerHelper
: public UsbDeviceHandleUsbfs::BlockingTaskRunnerHelper {
public:
MockBlockingTaskRunnerHelper() {
ON_CALL(*this, ClaimInterface).WillByDefault(testing::Return(true));
ON_CALL(*this, ReleaseInterface).WillByDefault(testing::Return(true));
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
ON_CALL(*this, DetachInterface).WillByDefault(testing::Return(true));
ON_CALL(*this, ReattachInterface).WillByDefault(testing::Return(true));
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
}
~MockBlockingTaskRunnerHelper() override = default;
MOCK_METHOD(void,
Initialize,
(base::ScopedFD,
base::ScopedFD,
base::WeakPtr<UsbDeviceHandleUsbfs>,
scoped_refptr<base::SequencedTaskRunner>),
(override));
MOCK_METHOD(bool, ClaimInterface, (int), (override));
MOCK_METHOD(bool, ReleaseInterface, (int), (override));
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
MOCK_METHOD(bool,
DetachInterface,
(int, const CombinedInterfaceInfo& interfaceInfo),
(override));
MOCK_METHOD(bool, ReattachInterface, (int), (override));
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
};
class UsbDeviceHandleUsbfsTest : public ::testing::Test {
protected:
UsbDeviceHandleUsbfsTest()
: usb_device_(base::MakeRefCounted<MockUsbDevice>(/*bus_number=*/0,
/*port_number=*/0)) {
mojom::UsbConfigurationInfoPtr config = BuildUsbConfigurationInfoPtr(
/*configuration_value=*/0, /*self_powered=*/false,
/*remote_wakeup=*/false, /*maximum_power=*/0);
config->interfaces.push_back(CreateInterface(0));
config->interfaces.push_back(CreateInterface(1));
config->interfaces.push_back(CreateInterface(2));
usb_device_->AddMockConfig(std::move(config));
handle1_ = CreateHandle(usb_device_);
handle2_ = CreateHandle(usb_device_);
}
~UsbDeviceHandleUsbfsTest() override {
handle1_->Close();
handle2_->Close();
}
static mojom::UsbInterfaceInfoPtr CreateInterface(int interface_number) {
return BuildUsbInterfaceInfoPtr(
interface_number, /*alternate_setting=*/0, /*interface_class=*/0,
/*interface_subclass=*/0, /*interface_protocol=*/0);
}
static scoped_refptr<UsbDeviceHandleUsbfs> CreateHandle(
scoped_refptr<UsbDevice> usb_device) {
scoped_refptr<UsbDeviceHandleUsbfs> handle =
base::MakeRefCounted<UsbDeviceHandleUsbfs>(
usb_device, /*fd=*/base::ScopedFD(),
/*lifeline_fd=*/base::ScopedFD(),
/*client_id=*/"", UsbService::CreateBlockingTaskRunner(),
std::make_unique<MockBlockingTaskRunnerHelper>());
usb_device->handles().push_back(handle.get());
return handle;
}
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
base::test::ScopedFeatureList scoped_feature_list_{
features::kAutomaticUsbDetach};
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
base::test::TaskEnvironment task_environment_;
scoped_refptr<MockUsbDevice> usb_device_;
scoped_refptr<UsbDeviceHandleUsbfs> handle1_;
scoped_refptr<UsbDeviceHandleUsbfs> handle2_;
};
namespace {
using ::base::test::TestFuture;
TEST_F(UsbDeviceHandleUsbfsTest, ClaimAndRelease) {
TestFuture<bool> claim_interface_future;
handle1_->ClaimInterface(1, claim_interface_future.GetCallback());
ASSERT_TRUE(claim_interface_future.Get());
TestFuture<bool> release_interface_future;
handle1_->ReleaseInterface(1, release_interface_future.GetCallback());
ASSERT_TRUE(release_interface_future.Get());
}
TEST_F(UsbDeviceHandleUsbfsTest, ReleaseWrongInterface) {
TestFuture<bool> claim_interface_future;
handle1_->ClaimInterface(1, claim_interface_future.GetCallback());
ASSERT_TRUE(claim_interface_future.Get());
TestFuture<bool> release_interface_future;
handle1_->ReleaseInterface(2, release_interface_future.GetCallback());
ASSERT_FALSE(release_interface_future.Get());
}
TEST_F(UsbDeviceHandleUsbfsTest, ClaimSameInterfaceConcurrently) {
TestFuture<bool> claim_interface_future1;
handle1_->ClaimInterface(1, claim_interface_future1.GetCallback());
ASSERT_TRUE(claim_interface_future1.Get());
TestFuture<bool> claim_interface_future2;
handle2_->ClaimInterface(1, claim_interface_future2.GetCallback());
ASSERT_FALSE(claim_interface_future2.Get());
TestFuture<bool> release_interface_future1;
handle2_->ReleaseInterface(1, release_interface_future1.GetCallback());
ASSERT_FALSE(release_interface_future1.Get());
TestFuture<bool> release_interface_future2;
handle1_->ReleaseInterface(1, release_interface_future2.GetCallback());
ASSERT_TRUE(release_interface_future2.Get());
TestFuture<bool> claim_interface_future3;
handle2_->ClaimInterface(1, claim_interface_future3.GetCallback());
ASSERT_TRUE(claim_interface_future3.Get());
TestFuture<bool> release_interface_future3;
handle2_->ReleaseInterface(1, release_interface_future3.GetCallback());
ASSERT_TRUE(release_interface_future3.Get());
}
TEST_F(UsbDeviceHandleUsbfsTest, ClaimDifferentInterfacesConcurrently) {
TestFuture<bool> claim_interface_future1;
handle1_->ClaimInterface(1, claim_interface_future1.GetCallback());
ASSERT_TRUE(claim_interface_future1.Get());
TestFuture<bool> claim_interface_future2;
handle2_->ClaimInterface(2, claim_interface_future2.GetCallback());
ASSERT_TRUE(claim_interface_future2.Get());
TestFuture<bool> release_interface_future1;
handle2_->ReleaseInterface(2, release_interface_future1.GetCallback());
ASSERT_TRUE(release_interface_future1.Get());
TestFuture<bool> release_interface_future2;
handle1_->ReleaseInterface(1, release_interface_future2.GetCallback());
ASSERT_TRUE(release_interface_future2.Get());
}
TEST_F(UsbDeviceHandleUsbfsTest, ClaimSameInterfaceSequentially) {
TestFuture<bool> claim_interface_future1;
handle1_->ClaimInterface(1, claim_interface_future1.GetCallback());
ASSERT_TRUE(claim_interface_future1.Get());
TestFuture<bool> release_interface_future1;
handle1_->ReleaseInterface(1, release_interface_future1.GetCallback());
ASSERT_TRUE(release_interface_future1.Get());
TestFuture<bool> claim_interface_future2;
handle2_->ClaimInterface(1, claim_interface_future2.GetCallback());
ASSERT_TRUE(claim_interface_future2.Get());
TestFuture<bool> release_interface_future2;
handle2_->ReleaseInterface(1, release_interface_future2.GetCallback());
ASSERT_TRUE(release_interface_future2.Get());
}
} // namespace
} // namespace device