Skip to content

Commit 9153b59

Browse files
committed
Add HIDP tests
1 parent d0b8da4 commit 9153b59

1 file changed

Lines changed: 167 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//
2+
// HIDPTests.swift
3+
// BluetoothLinuxTests
4+
//
5+
6+
#if ENABLE_MOCKING
7+
import Foundation
8+
import XCTest
9+
import Bluetooth
10+
import SystemPackage
11+
import Socket
12+
@testable import BluetoothLinux
13+
#if canImport(Glibc)
14+
import Glibc
15+
#elseif canImport(Darwin)
16+
import Darwin
17+
#endif
18+
19+
final class HIDPTests: XCTestCase {
20+
21+
/// A fake file descriptor; mocked syscalls never reach the kernel.
22+
private let fileDescriptor = SocketDescriptor(rawValue: 3)
23+
24+
func testIORawValues() {
25+
// read-direction requests are sign-extended, matching the C `ioctl` request argument
26+
XCTAssertEqual(HIDPIO.addConnection.rawValue, 0x400448C8)
27+
XCTAssertEqual(HIDPIO.removeConnection.rawValue, 0x400448C9)
28+
XCTAssertEqual(HIDPIO.getConnectionList.rawValue, UInt(bitPattern: Int(Int32(bitPattern: 0x800448D2))))
29+
XCTAssertEqual(HIDPIO.getConnectionInfo.rawValue, UInt(bitPattern: Int(Int32(bitPattern: 0x800448D3))))
30+
for value in HIDPIO.allCases {
31+
XCTAssertEqual(HIDPIO(rawValue: value.rawValue), value)
32+
}
33+
}
34+
35+
func testRequestLayout() {
36+
// must match the kernel's C struct layouts
37+
XCTAssertEqual(MemoryLayout<CInterop.HIDPConnectionAddRequest>.size, 168)
38+
XCTAssertEqual(MemoryLayout<CInterop.HIDPConnectionAddRequest>.stride, 168)
39+
XCTAssertEqual(MemoryLayout<CInterop.HIDPConnectionDeleteRequest>.size, 12)
40+
XCTAssertEqual(MemoryLayout<CInterop.HIDPConnectionInformation>.size, 148)
41+
XCTAssertEqual(MemoryLayout<CInterop.HIDPConnectionListRequest>.size, 16)
42+
}
43+
44+
func testDeviceName() {
45+
let name = "Wireless Keyboard".hidpDeviceName
46+
XCTAssertEqual(String(hidpDeviceName: name), "Wireless Keyboard")
47+
// truncated to 127 bytes plus null terminator
48+
let long = String(repeating: "a", count: 200).hidpDeviceName
49+
XCTAssertEqual(String(hidpDeviceName: long).count, 127)
50+
}
51+
52+
func testAddConnectionFakedKernelReply() throws {
53+
try MockingDriver.withMockingEnabled { driver in
54+
let descriptor = Data([0x05, 0x01, 0x09, 0x06]) // partial keyboard descriptor
55+
driver.ioctlHandler = { fd, request, pointer in
56+
XCTAssertEqual(request, HIDPIO.addConnection.rawValue)
57+
guard let pointer else {
58+
XCTFail("Expected a request buffer for HIDPCONNADD")
59+
errno = EINVAL
60+
return -1
61+
}
62+
let request = pointer.assumingMemoryBound(to: CInterop.HIDPConnectionAddRequest.self)
63+
XCTAssertEqual(request.pointee.controlSocket, 7)
64+
XCTAssertEqual(request.pointee.interruptSocket, 8)
65+
XCTAssertEqual(request.pointee.parser, 0x0100)
66+
XCTAssertEqual(request.pointee.vendor, 0x05AC)
67+
XCTAssertEqual(request.pointee.reportDescriptorSize, 4)
68+
guard let reportDescriptor = request.pointee.reportDescriptor else {
69+
XCTFail("Expected a report descriptor buffer")
70+
errno = EINVAL
71+
return -1
72+
}
73+
XCTAssertEqual(Data(bytes: reportDescriptor, count: 4), descriptor)
74+
XCTAssertEqual(String(hidpDeviceName: request.pointee.name), "Test Keyboard")
75+
return 0
76+
}
77+
try fileDescriptor.hidpAddConnection(
78+
controlSocket: SocketDescriptor(rawValue: 7),
79+
interruptSocket: SocketDescriptor(rawValue: 8),
80+
vendor: 0x05AC,
81+
name: "Test Keyboard",
82+
reportDescriptor: descriptor
83+
)
84+
}
85+
}
86+
87+
func testRemoveConnectionIsTraced() throws {
88+
try MockingDriver.withMockingEnabled { driver in
89+
try fileDescriptor.hidpRemoveConnection(
90+
destination: BluetoothAddress(bytes: (1, 2, 3, 4, 5, 6))
91+
)
92+
XCTAssertEqual(
93+
driver.trace.dequeue(),
94+
Trace.Entry(name: "ioctl", [
95+
fileDescriptor.rawValue,
96+
HIDPIO.removeConnection.rawValue
97+
])
98+
)
99+
XCTAssertTrue(driver.trace.isEmpty)
100+
}
101+
}
102+
103+
func testConnectionListFakedKernelReply() throws {
104+
try MockingDriver.withMockingEnabled { driver in
105+
let expected = CInterop.HIDPConnectionInformation(
106+
address: BluetoothAddress(bytes: (6, 5, 4, 3, 2, 1)),
107+
flags: HIDPConnectionFlag.bootProtocolMode.rawValue,
108+
state: HIDPConnectionState.connected.rawValue,
109+
vendor: 0x05AC,
110+
product: 0x022C,
111+
version: 0x011B,
112+
name: "Test Keyboard".hidpDeviceName
113+
)
114+
driver.ioctlHandler = { fd, request, pointer in
115+
XCTAssertEqual(request, HIDPIO.getConnectionList.rawValue)
116+
guard let pointer else {
117+
XCTFail("Expected a request buffer for HIDPGETCONNLIST")
118+
errno = EINVAL
119+
return -1
120+
}
121+
let request = pointer.assumingMemoryBound(to: CInterop.HIDPConnectionListRequest.self)
122+
guard let connections = request.pointee.connections else {
123+
XCTFail("Expected a connection buffer")
124+
errno = EINVAL
125+
return -1
126+
}
127+
XCTAssertGreaterThanOrEqual(request.pointee.count, 1)
128+
connections[0] = expected
129+
request.pointee.count = 1
130+
return 0
131+
}
132+
let connections = try fileDescriptor.hidpConnectionList(limit: 8)
133+
XCTAssertEqual(connections.count, 1)
134+
XCTAssertEqual(connections[0].name, "Test Keyboard")
135+
XCTAssertEqual(connections[0].flags, [.bootProtocolMode])
136+
XCTAssertEqual(connections[0].state, .connected)
137+
XCTAssertEqual(connections[0].vendor, 0x05AC)
138+
XCTAssertEqual(connections[0].product, 0x022C)
139+
XCTAssertEqual(connections[0].address, BluetoothAddress(littleEndian: BluetoothAddress(bytes: (6, 5, 4, 3, 2, 1))))
140+
}
141+
}
142+
143+
func testConnectionInformationFakedKernelReply() throws {
144+
try MockingDriver.withMockingEnabled { driver in
145+
driver.ioctlHandler = { fd, request, pointer in
146+
XCTAssertEqual(request, HIDPIO.getConnectionInfo.rawValue)
147+
guard let pointer else {
148+
XCTFail("Expected a request buffer for HIDPGETCONNINFO")
149+
errno = EINVAL
150+
return -1
151+
}
152+
let request = pointer.assumingMemoryBound(to: CInterop.HIDPConnectionInformation.self)
153+
request.pointee.state = HIDPConnectionState.connected.rawValue
154+
request.pointee.vendor = 0x05AC
155+
request.pointee.name = "Test Mouse".hidpDeviceName
156+
return 0
157+
}
158+
let connection = try fileDescriptor.hidpConnectionInformation(
159+
for: BluetoothAddress(bytes: (1, 2, 3, 4, 5, 6))
160+
)
161+
XCTAssertEqual(connection.name, "Test Mouse")
162+
XCTAssertEqual(connection.state, .connected)
163+
XCTAssertEqual(connection.vendor, 0x05AC)
164+
}
165+
}
166+
}
167+
#endif

0 commit comments

Comments
 (0)