/*
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 "libusbwrap/UsbDevice.hpp"
#include
#include
#include
#include "libusb.h"
#include "libusbwrap/LibUsbTypes.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