TestNonStandardDescriptor.m   [plain text]


//
//  TestNonStandardDescriptor.m
//  IOHIDFamilyUnitTests
//
//  Created by Paul Doerr on 10/15/18.
//

#import <XCTest/XCTest.h>
#import <IOKit/hid/IOHIDUserDevice.h>
#import <IOKit/hid/IOHIDDevice.h>
#import <IOKit/hid/IOHIDManager.h>
#import "IOHIDUnitTestUtility.h"
#import "IOHIDXCTestExpectation.h"


static uint8_t constantLogicalDescriptor[] = {
    0x06, 0x00, 0xFF,                         // Usage Page (65280)
    0x09, 0x80,                               // Usage 128 (0x80)
    0xA1, 0x02,                               // Collection (Logical)
    0x85, 0x02,                               //   ReportID................ (2)
    0x75, 0x08,                               //   Report Size............. (8)
    0x95, 0x3F,                               //   Report Count............ (63)
    0x15, 0x00,                               //   Logical Minimum......... (0)
    0x26, 0x00, 0x10,                         //   Logical Maximum......... (4096)
    0x81, 0x01,                               //   Input...................(Constant)
    0x85, 0x03,                               //   ReportID................ (3)
    0x75, 0x08,                               //   Report Size............. (8)
    0x95, 0x3F,                               //   Report Count............ (63)
    0x81, 0x01,                               //   Input...................(Constant)
    0xC0,                                     // End Collection
};

static void HIDManagerDeviceAddedCallback ( void * _Nullable context, IOReturn  result, void * _Nullable  sender, IOHIDDeviceRef device);

@interface TestNonStandardDescriptor :  XCTestCase

@property NSString *                        uniqueID;
@property IOHIDManagerRef                   deviceManager;
@property IOHIDXCTestExpectation *          deviceMatchedExpection;
@property IOHIDUserDeviceRef                userDevice;

@end

@implementation TestNonStandardDescriptor

- (void)setUp {
    [super setUp];

    self.deviceMatchedExpection = [[IOHIDXCTestExpectation alloc] initWithDescription:@"Expectation: Test HID Device"];
    HIDXCTAssertAndThrowTrue(self.deviceMatchedExpection != nil);

    self.uniqueID = [[[NSUUID alloc] init] UUIDString];

    self.deviceManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    HIDXCTAssertAndThrowTrue(self.deviceManager != nil);

    NSDictionary * matching = @{@kIOHIDPhysicalDeviceUniqueIDKey:self.uniqueID};
    IOHIDManagerSetDeviceMatching(self.deviceManager, (CFDictionaryRef)matching);
    IOHIDManagerRegisterDeviceMatchingCallback(self.deviceManager, HIDManagerDeviceAddedCallback, (__bridge void *)(self));
    IOHIDManagerScheduleWithRunLoop(self.deviceManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    IOHIDManagerOpen(self.deviceManager, kIOHIDOptionsTypeNone);
}

- (void)tearDown {
    IOHIDManagerUnscheduleFromRunLoop(self.deviceManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    CFRelease(self.deviceManager);
    if (self.userDevice) {
        CFRelease(self.userDevice);
    }

    [super tearDown];
}

- (void)testConstantLogicalDescriptor {
    NSData * descriptorData = [[NSData alloc] initWithBytes:constantLogicalDescriptor length:sizeof(constantLogicalDescriptor)];

    NSDictionary * configDict = @{@kIOHIDPhysicalDeviceUniqueIDKey  : self.uniqueID,
                                  @kIOHIDReportDescriptorKey        : descriptorData};
    self.userDevice = IOHIDUserDeviceCreate(kCFAllocatorDefault, (CFDictionaryRef)configDict);
    HIDXCTAssertAndThrowTrue(self.userDevice != nil);

    XCTWaiterResult result = [XCTWaiter waitForExpectations:@[self.deviceMatchedExpection] timeout:10];
    HIDXCTAssertWithParameters (RETURN_FROM_TEST | COLLECT_HIDUTIL | COLLECT_IOREG,
                                result == XCTWaiterResultCompleted,
                                "%@",
                                self.deviceMatchedExpection);
}

@end

void HIDManagerDeviceAddedCallback(void * _Nullable context, IOReturn  result __unused, void * _Nullable  sender __unused, IOHIDDeviceRef device __unused) {
    TestNonStandardDescriptor *self = (__bridge TestNonStandardDescriptor *)context;

    [self.deviceMatchedExpection fulfill];
}