//
// TestHIDManager.m
// IOHIDFamily
//
// Created by YG on 04/06/17.
//
//
#import <XCTest/XCTest.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <IOKit/hid/IOHIDManager.h>
#include <IOKit/hid/IOHIDDevice.h>
#include "IOHIDUnitTestUtility.h"
#include "IOHIDUnitTestDescriptors.h"
#import "IOHIDEventSystemTestController.h"
#import "IOHIDUserDeviceTestController.h"
#import "IOHIDDeviceTestController.h"
static void HIDManagerValueCallback (
void * _Nullable context,
IOReturn result,
void * _Nullable sender,
IOHIDValueRef value);
static void HIDManagerReportCallback (
void * _Nullable context,
IOReturn result,
void * _Nullable sender,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength);
static uint8_t descriptorForVendor[] = {
HIDVendorMessage32BitDescriptor
};
static uint8_t descriptorForKeyboard[] = {
HIDKeyboardDescriptor
};
static uint8_t descriptorForDigitizer[] = {
HIDDigitizerWithTouchCancel
};
@interface TestHIDManager : XCTestCase
@property IOHIDManagerRef deviceManager;
@property NSString * keyboardID;
@property NSString * vendorID;
@property NSString * digitizerID;
@property NSMutableDictionary * deviceDict;
@property dispatch_queue_t deviceControllerQueue;
@property dispatch_queue_t rootQueue;
@property CFRunLoopRef runLoop;
@property NSInteger valueCount;
@property NSInteger reportCount;
@end
@implementation TestHIDManager
- (void)setUp {
[super setUp];
TestLog("TestHIDManager setup");
IOHIDUserDeviceTestController * device;
NSData * descriptorData;
self.rootQueue = IOHIDUnitTestCreateRootQueue(31, 2);
self.deviceControllerQueue = dispatch_queue_create_with_target ("IOHIDDeviceTestController", DISPATCH_QUEUE_SERIAL, self.rootQueue);
HIDXCTAssertAndThrowTrue(self.deviceControllerQueue != nil);
self.deviceDict = [[NSMutableDictionary alloc] init];
HIDXCTAssertAndThrowTrue ( self.deviceDict != nil);
self.deviceManager = IOHIDManagerCreate (kCFAllocatorDefault, kIOHIDOptionsTypeNone);
IOHIDManagerSetDeviceMatching(self.deviceManager, NULL);
IOHIDManagerRegisterInputValueCallback (self.deviceManager, HIDManagerValueCallback, (__bridge void * _Nullable)(self));
IOHIDManagerRegisterInputReportCallback (self.deviceManager, HIDManagerReportCallback, (__bridge void * _Nullable)(self));
IOHIDManagerScheduleWithRunLoop(self.deviceManager, CFRunLoopGetCurrent() , kCFRunLoopDefaultMode);
IOHIDManagerOpen(self.deviceManager, kIOHIDOptionsTypeNone);
self.keyboardID = [[[NSUUID alloc] init] UUIDString];
descriptorData = [[NSData alloc] initWithBytes:descriptorForKeyboard length:sizeof(descriptorForKeyboard)];
device = [[IOHIDUserDeviceTestController alloc] initWithDescriptor:descriptorData DeviceUniqueID:self.keyboardID andQueue:nil];
HIDXCTAssertAndThrowTrue(device != nil);
self.deviceDict [self.keyboardID] = device;
self.vendorID = [[[NSUUID alloc] init] UUIDString];
descriptorData = [[NSData alloc] initWithBytes:descriptorForVendor length:sizeof(descriptorForVendor)];
device = [[IOHIDUserDeviceTestController alloc] initWithDescriptor:descriptorData DeviceUniqueID:self.vendorID andQueue:nil];
HIDXCTAssertAndThrowTrue(device != nil);
self.deviceDict [self.vendorID] = device;
self.digitizerID = [[[NSUUID alloc] init] UUIDString];
descriptorData = [[NSData alloc] initWithBytes:descriptorForDigitizer length:sizeof(descriptorForDigitizer)];
device = [[IOHIDUserDeviceTestController alloc] initWithDescriptor:descriptorData DeviceUniqueID:self.digitizerID andQueue:nil];
HIDXCTAssertAndThrowTrue(device != nil);
self.deviceDict [self.digitizerID] = device;
}
- (void)tearDown {
TestLog("TestHIDManager tearDown");
for(id key in self.deviceDict) {
id value = [self.deviceDict objectForKey:key];
[value invalidate];
}
self.deviceDict = nil;
IOHIDManagerUnscheduleFromRunLoop (self.deviceManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDManagerClose(self.deviceManager, 0);
CFRelease(self.deviceManager);
[super tearDown];
}
- (void)testMatching {
NSSet * devices;
NSDictionary *matching;
NSMutableArray *matchingMultiple = [[NSMutableArray alloc ] initWithCapacity:3];
HIDXCTAssertAndThrowTrue (matchingMultiple != nil);
matching = @{@kIOHIDPhysicalDeviceUniqueIDKey:self.keyboardID};
IOHIDManagerSetDeviceMatching(self.deviceManager, (CFDictionaryRef)matching);
devices = CFBridgingRelease(IOHIDManagerCopyDevices (self.deviceManager));
XCTAssert(devices != nil && devices.count == 1, "devices:
[matchingMultiple addObject: matching];
matching = @{@kIOHIDPhysicalDeviceUniqueIDKey:self.vendorID};
[matchingMultiple addObject: matching];
IOHIDManagerSetDeviceMatchingMultiple(self.deviceManager, (CFArrayRef)matchingMultiple);
devices = CFBridgingRelease(IOHIDManagerCopyDevices (self.deviceManager));
XCTAssert(devices != nil && devices.count == 2, "devices:
matching = @{@kIOHIDPhysicalDeviceUniqueIDKey:self.digitizerID};
[matchingMultiple addObject: matching];
IOHIDManagerSetDeviceMatchingMultiple(self.deviceManager, (CFArrayRef)matchingMultiple);
devices = CFBridgingRelease(IOHIDManagerCopyDevices (self.deviceManager));
XCTAssert(devices != nil && devices.count == 3, "devices:
matching = @{@kIOHIDPhysicalDeviceUniqueIDKey:self.keyboardID};
IOHIDManagerSetDeviceMatching(self.deviceManager, (CFDictionaryRef)matching);
devices = CFBridgingRelease(IOHIDManagerCopyDevices (self.deviceManager));
XCTAssert(devices != nil && devices.count == 1, "devices: }
- (void)testProperty {
NSSet * devices;
NSArray * matchingMultiple = @[
@{@kIOHIDPhysicalDeviceUniqueIDKey:self.digitizerID} ,
@{@kIOHIDPhysicalDeviceUniqueIDKey:self.vendorID} ,
@{@kIOHIDPhysicalDeviceUniqueIDKey:self.keyboardID}
];
IOHIDManagerSetDeviceMatchingMultiple(self.deviceManager, (CFArrayRef)matchingMultiple);
devices = CFBridgingRelease(IOHIDManagerCopyDevices (self.deviceManager));
HIDXCTAssertAndThrowTrue(devices != nil && devices.count == 3, "Devices:
IOHIDManagerSetProperty(self.deviceManager, CFSTR("TestKey"), CFSTR("TestValue"));
for (id device in devices) {
CFTypeRef value = IOHIDDeviceGetProperty ((IOHIDDeviceRef)device, CFSTR("TestKey"));
XCTAssert (CFEqual (value, CFSTR("TestValue")) == true);
}
}
- (void)MAC_OS_ONLY_TEST_CASE(testReports) {
__block IOReturn status;
self.valueCount = 0;
self.reportCount = 0;
NSArray * elementMatching = @[
@{
@kIOHIDElementUsagePageKey:@(kHIDPage_AppleVendor),
@kIOHIDElementUsageKey:@(kHIDUsage_AppleVendor_Message) ,
},
@{
@kIOHIDElementUsagePageKey:@(kHIDPage_Digitizer),
@kIOHIDElementUsageKey:@(kHIDUsage_Dig_Touch)
}
];
IOHIDManagerSetInputValueMatchingMultiple (self.deviceManager, (CFArrayRef)elementMatching);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), self.deviceControllerQueue, ^{
HIDDigitizerWithTouchCancelInputReport digitizerReport;
memset (&digitizerReport, 00, sizeof(digitizerReport));
digitizerReport.DIG_TouchPadFingerTouch = 1;
status |= [self.deviceDict[self.digitizerID] handleReport:(uint8_t*)&digitizerReport Length:sizeof(digitizerReport) andInterval:2000];
digitizerReport.DIG_TouchPadFingerTouch = 0;
status |= [self.deviceDict[self.digitizerID] handleReport:(uint8_t*)&digitizerReport Length:sizeof(digitizerReport) andInterval:2000];
digitizerReport.DIG_TouchPadFingerUntouch = 1;
status |= [self.deviceDict[self.digitizerID] handleReport:(uint8_t*)&digitizerReport Length:sizeof(digitizerReport) andInterval:2000];
digitizerReport.DIG_TouchPadFingerUntouch = 0;
status |= [self.deviceDict[self.digitizerID] handleReport:(uint8_t*)&digitizerReport Length:sizeof(digitizerReport) andInterval:2000];
HIDVendorMessage32BitDescriptorInputReport vendorReport;
vendorReport.VEN_VendorDefined0023 = 1;
status |= [self.deviceDict[self.vendorID] handleReport:(uint8_t*)&vendorReport Length:sizeof(vendorReport) andInterval:2000];
vendorReport.VEN_VendorDefined0023 = 2;
status |= [self.deviceDict[self.vendorID] handleReport:(uint8_t*)&vendorReport Length:sizeof(vendorReport) andInterval:2000];
});
XCTAssert(status == kIOReturnSuccess);
CFRunLoopRunInMode (kCFRunLoopDefaultMode, 5.0, false);
XCTAssert(self.reportCount == 6 && self.valueCount == 4, "reportCount:
}
-(void) managerValueCallback: (IOHIDDeviceRef) __unused sender :(IOHIDValueRef) __unused value :(IOReturn) result {
++self.valueCount;
XCTAssert (result == kIOReturnSuccess, "managerValueCallback result: }
-(void) managerReportCallback: (IOHIDDeviceRef) __unused sender :(IOHIDReportType) __unused type :(uint32_t) __unused reportID : (uint8_t*) __unused report : (CFIndex) __unused reportLength : (IOReturn) __unused result {
++self.reportCount;
}
@end
void HIDManagerValueCallback (void * _Nullable context, IOReturn result, void * _Nullable sender __unused, IOHIDValueRef value) {
TestHIDManager *self = (__bridge TestHIDManager *)context;
TestLog("HIDManagerValueCallback sender: [self managerValueCallback: (IOHIDDeviceRef) sender :value :result];
}
void HIDManagerReportCallback (
void * _Nullable context,
IOReturn result,
void * _Nullable sender,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength) {
TestHIDManager *self = (__bridge TestHIDManager *)context;
TestLog("HIDManagerReportCallback sender: [self managerReportCallback: (IOHIDDeviceRef) sender :type :reportID :report :reportLength :result];
}