/* ptrnt - print labels on linux Copyright (C) 2025 Moritz Martinius This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "libusb.h" #include "libusbwrap/LibUsbTypes.hpp" #include "libusbwrap/UsbDevice.hpp" #include "mocks/MockLibUsbWrapper.hpp" using ::testing::_; using ::testing::DoAll; using ::testing::NiceMock; using ::testing::Return; using ::testing::SetArgPointee; namespace libusbwrap { // Test fixture for UsbDevice tests class UsbDeviceTest : public ::testing::Test { protected: void SetUp() override { mockLibUsb = std::make_shared>(); // Create mock device pointer mockDevice = reinterpret_cast(0x1000); // Create mock device handle mockHandle = reinterpret_cast(0x2000); // Setup device descriptor desc.idVendor = 0x04f9; // Brother vendor ID desc.idProduct = 0x2042; // P700 product ID // Default behaviors ON_CALL(*mockLibUsb, open(_, _)).WillByDefault(DoAll(SetArgPointee<1>(mockHandle), Return(0))); ON_CALL(*mockLibUsb, getSpeed(_)).WillByDefault(Return(LIBUSB_SPEED_FULL)); ON_CALL(*mockLibUsb, getBusNumber(_)).WillByDefault(Return(1)); ON_CALL(*mockLibUsb, getPortNumber(_)).WillByDefault(Return(2)); ON_CALL(*mockLibUsb, errorName(_)).WillByDefault(Return("LIBUSB_SUCCESS")); } std::shared_ptr mockLibUsb; libusb_device* mockDevice; libusb_device_handle* mockHandle; libusb_device_descriptor desc{}; }; // Constructor tests TEST_F(UsbDeviceTest, ConstructorWithValidDevice) { EXPECT_NO_THROW({ auto device = std::make_unique(mockDevice, desc, mockLibUsb); EXPECT_NE(device, nullptr); }); } TEST_F(UsbDeviceTest, ConstructorWithNullptrThrows) { EXPECT_THROW({ auto device = std::make_unique(nullptr, desc, mockLibUsb); }, std::invalid_argument); } // Open/Close tests TEST_F(UsbDeviceTest, OpenSuccess) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); EXPECT_CALL(*mockLibUsb, open(mockDevice, _)).WillOnce(DoAll(SetArgPointee<1>(mockHandle), Return(0))); EXPECT_TRUE(device->open()); } TEST_F(UsbDeviceTest, OpenFailure) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); EXPECT_CALL(*mockLibUsb, open(mockDevice, _)).WillOnce(Return(LIBUSB_ERROR_ACCESS)); EXPECT_FALSE(device->open()); EXPECT_EQ(device->getLastError(), Error::ACCESS); } TEST_F(UsbDeviceTest, CloseWithOpenDevice) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); EXPECT_CALL(*mockLibUsb, close(mockHandle)).Times(1); device->close(); } TEST_F(UsbDeviceTest, CloseWithoutOpenDevice) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); // Should not call close if device was never opened EXPECT_CALL(*mockLibUsb, close(_)).Times(0); device->close(); } TEST_F(UsbDeviceTest, DestructorClosesOpenDevice) { EXPECT_CALL(*mockLibUsb, close(mockHandle)).Times(1); { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); // Device goes out of scope, destructor should call close } } // Kernel driver tests TEST_F(UsbDeviceTest, DetachKernelDriverWhenActive) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); EXPECT_CALL(*mockLibUsb, kernelDriverActive(mockHandle, 0)).WillOnce(Return(1)); // Active EXPECT_CALL(*mockLibUsb, detachKernelDriver(mockHandle, 0)).WillOnce(Return(0)); // Success EXPECT_TRUE(device->detachKernelDriver(0)); } TEST_F(UsbDeviceTest, DetachKernelDriverWhenNotActive) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); EXPECT_CALL(*mockLibUsb, kernelDriverActive(mockHandle, 0)).WillOnce(Return(0)); // Not active EXPECT_CALL(*mockLibUsb, detachKernelDriver(_, _)).Times(0); // Should not call detach EXPECT_TRUE(device->detachKernelDriver(0)); } TEST_F(UsbDeviceTest, DetachKernelDriverFailure) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); EXPECT_CALL(*mockLibUsb, kernelDriverActive(mockHandle, 0)).WillOnce(Return(1)); // Active EXPECT_CALL(*mockLibUsb, detachKernelDriver(mockHandle, 0)).WillOnce(Return(LIBUSB_ERROR_NOT_FOUND)); EXPECT_FALSE(device->detachKernelDriver(0)); EXPECT_EQ(device->getLastError(), Error::NOT_FOUND); } // Interface tests TEST_F(UsbDeviceTest, ClaimInterfaceSuccess) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); EXPECT_CALL(*mockLibUsb, claimInterface(mockHandle, 0)).WillOnce(Return(0)); EXPECT_TRUE(device->claimInterface(0)); } TEST_F(UsbDeviceTest, ClaimInterfaceFailure) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); EXPECT_CALL(*mockLibUsb, claimInterface(mockHandle, 0)).WillOnce(Return(LIBUSB_ERROR_BUSY)); EXPECT_FALSE(device->claimInterface(0)); EXPECT_EQ(device->getLastError(), Error::BUSY); } TEST_F(UsbDeviceTest, ReleaseInterfaceSuccess) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); EXPECT_CALL(*mockLibUsb, releaseInterface(mockHandle, 0)).WillOnce(Return(0)); EXPECT_TRUE(device->releaseInterface(0)); } TEST_F(UsbDeviceTest, ReleaseInterfaceFailure) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); EXPECT_CALL(*mockLibUsb, releaseInterface(mockHandle, 0)).WillOnce(Return(LIBUSB_ERROR_NOT_FOUND)); EXPECT_FALSE(device->releaseInterface(0)); EXPECT_EQ(device->getLastError(), Error::NOT_FOUND); } // Bulk transfer tests TEST_F(UsbDeviceTest, BulkTransferSuccess) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); std::vector data = {0x01, 0x02, 0x03}; int transferred = 0; EXPECT_CALL(*mockLibUsb, bulkTransfer(mockHandle, 0x02, _, 3, _, 1000)) .WillOnce(DoAll(SetArgPointee<4>(3), Return(0))); EXPECT_TRUE(device->bulkTransfer(0x02, data, &transferred, 1000)); } TEST_F(UsbDeviceTest, BulkTransferFailure) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); device->open(); std::vector data = {0x01, 0x02, 0x03}; int transferred = 0; EXPECT_CALL(*mockLibUsb, bulkTransfer(mockHandle, 0x02, _, 3, _, 1000)).WillOnce(Return(LIBUSB_ERROR_TIMEOUT)); EXPECT_FALSE(device->bulkTransfer(0x02, data, &transferred, 1000)); EXPECT_EQ(device->getLastError(), Error::TIMEOUT); } // Getter tests TEST_F(UsbDeviceTest, GetUsbId) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); auto usbId = device->getUsbId(); EXPECT_EQ(usbId.first, 0x04f9); // vid EXPECT_EQ(usbId.second, 0x2042); // pid } TEST_F(UsbDeviceTest, GetSpeed) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); EXPECT_CALL(*mockLibUsb, getSpeed(mockDevice)).WillOnce(Return(LIBUSB_SPEED_HIGH)); EXPECT_EQ(device->getSpeed(), device::Speed::HIGH); } TEST_F(UsbDeviceTest, GetBusNumber) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); EXPECT_CALL(*mockLibUsb, getBusNumber(mockDevice)).WillOnce(Return(5)); EXPECT_EQ(device->getBusNumber(), 5); } TEST_F(UsbDeviceTest, GetPortNumber) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); EXPECT_CALL(*mockLibUsb, getPortNumber(mockDevice)).WillOnce(Return(3)); EXPECT_EQ(device->getPortNumber(), 3); } TEST_F(UsbDeviceTest, GetLastError) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); // Initially no error EXPECT_EQ(device->getLastError(), Error::SUCCESS); // After a failed operation EXPECT_CALL(*mockLibUsb, open(_, _)).WillOnce(Return(LIBUSB_ERROR_NO_DEVICE)); device->open(); EXPECT_EQ(device->getLastError(), Error::NO_DEVICE); } TEST_F(UsbDeviceTest, GetLastErrorString) { auto device = std::make_unique(mockDevice, desc, mockLibUsb); EXPECT_CALL(*mockLibUsb, errorName(static_cast(Error::SUCCESS))).WillOnce(Return("LIBUSB_SUCCESS")); EXPECT_EQ(device->getLastErrorString(), "LIBUSB_SUCCESS"); } } // namespace libusbwrap