TestHIDEventSystemClient.m [plain text]
//
// TestFilterClient.m
// IOHIDFamily
//
// Created by YG on 10/25/16.
//
//
#import <XCTest/XCTest.h>
#include "IOHIDUnitTestUtility.h"
#import "IOHIDEventSystemTestController.h"
#import "IOHIDUserDeviceTestController.h"
#import "IOHIDDeviceTestController.h"
#import "IOHIDUnitTestDescriptors.h"
#include <IOKit/hid/IOHIDEventSystemKeys.h>
static boolean_t EventSystemEventFilterCallback (void * _Nullable target, void * _Nullable refcon __unused, void * _Nullable sender, IOHIDEventRef event);
static void EventSystemResetCallback(void * _Nullable target, void * _Nullable context __unused);
//static void EventSystemServiceRemovedCallback (void *target, void *refcon __unused, IOHIDServiceClientRef service);
//static void EventSystemServiceAddedCallback (void *target, void *refcon __unused, IOHIDServiceClientRef service);
static void EventSystemEventCallback (void * _Nullable target, void * _Nullable refcon __unused, void * _Nullable sender, IOHIDEventRef event);
static void EventSystemPropertyChangedCallback (void * _Nullable target, void * _Nullable context, CFStringRef property, CFTypeRef value);
static uint8_t descriptor[] = {
HIDVendorMessage32BitDescriptor
};
@interface TestEventSystemClient : XCTestCase
@property IOHIDUserDeviceTestController * sourceController;
@property dispatch_queue_t rootQueue;
@property dispatch_queue_t clientQueue;
@property IOHIDEventSystemClientRef eventSystemClient;
@property IOHIDEventSystemClientRef testEventSystemClient;
@property NSInteger eventCount;
@property NSInteger filterCount;
@property NSInteger resetCount;
@property NSInteger serviceAddedCount;
@property NSInteger serviceRemovedCount;
@property NSInteger propertyCount;
@property NSString* uniqueID;
@end
@implementation TestEventSystemClient
- (void)setUp {
[super setUp];
self.rootQueue = IOHIDUnitTestCreateRootQueue(37, 2);
self.clientQueue = dispatch_queue_create_with_target ("IOHIDEventSystemClientQueue", DISPATCH_QUEUE_SERIAL, self.rootQueue);
HIDXCTAssertAndThrowTrue(self.clientQueue != nil);
NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
self.uniqueID = [[[NSUUID alloc] init] UUIDString];
self.sourceController = [[IOHIDUserDeviceTestController alloc] initWithDescriptor:descriptorData DeviceUniqueID:self.uniqueID andQueue:nil];
HIDXCTAssertAndThrowTrue(EVAL(self.sourceController != nil));
self.eventSystemClient = IOHIDEventSystemClientCreateWithType (kCFAllocatorDefault, kIOHIDEventSystemClientTypeMonitor, NULL);
HIDXCTAssertAndThrowTrue (self.eventSystemClient != NULL);
self.testEventSystemClient = IOHIDEventSystemClientCreateWithType (kCFAllocatorDefault, kIOHIDEventSystemClientTypeMonitor, NULL);
HIDXCTAssertAndThrowTrue (self.testEventSystemClient != NULL);
XCTAssert (IOHIDEventSystemClientGetTypeID() == CFGetTypeID(self.testEventSystemClient));
IOHIDEventSystemClientRegisterEventCallback (self.eventSystemClient, EventSystemEventCallback, (__bridge void * _Nullable)(self), NULL);
NSDictionary * matching = @{@kIOHIDPhysicalDeviceUniqueIDKey : self.uniqueID};
IOHIDEventSystemClientSetMatching (self.eventSystemClient, (CFDictionaryRef)matching);
IOHIDEventSystemClientRegisterResetCallback(self.eventSystemClient, EventSystemResetCallback, (__bridge void * _Nullable)(self), NULL);
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientScheduleWithDispatchQueue(self.eventSystemClient, self.clientQueue);
});
}
- (void)tearDown {
if (self.clientQueue) {
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientUnscheduleFromDispatchQueue(self.eventSystemClient, self.clientQueue);
});
self.clientQueue = NULL;
}
if (self.eventSystemClient) {
CFRelease(self.eventSystemClient);
self.eventSystemClient = NULL;
}
if (self.testEventSystemClient) {
CFRelease(self.testEventSystemClient);
self.testEventSystemClient = NULL;
}
[super tearDown];
}
- (void)testServiceClient {
usleep(kDefaultReportDispatchCompletionTime);
NSArray * services = CFBridgingRelease(IOHIDEventSystemClientCopyServices(self.eventSystemClient));
XCTAssert (services != nil && services.count == 1, "Copy services:
XCTAssert (IOHIDServiceClientGetTypeID() == CFGetTypeID((IOHIDServiceClientRef)services[0]));
NSNumber * registryID = IOHIDServiceClientGetRegistryID ((IOHIDServiceClientRef)services[0]);
XCTAssert (registryID != nil);
IOHIDServiceClientRef serviceClient = IOHIDEventSystemClientCopyServiceForRegistryID (self.eventSystemClient, registryID.unsignedLongLongValue);
XCTAssert ((IOHIDServiceClientRef)CFBridgingRetain(services[0]) == serviceClient);
XCTAssert(IOHIDServiceClientConformsTo (serviceClient, kHIDPage_AppleVendor, kHIDUsage_AppleVendor_Message));
XCTAssert (IOHIDEventSystemClientRegistryIDConformsTo (self.eventSystemClient, registryID.unsignedLongLongValue, kHIDPage_AppleVendor, kHIDUsage_AppleVendor_Message) == true);
CFStringRef testPropertyKey = CFSTR ("ClientTestProperty");
IOHIDServiceClientSetProperty(serviceClient, testPropertyKey, testPropertyKey);
CFTypeRef value = IOHIDServiceClientCopyProperty (serviceClient, testPropertyKey);
XCTAssert (CFEqual(testPropertyKey, value));
if (value) {
CFRelease(value);
}
if (serviceClient) {
CFRelease (serviceClient);
}
@autoreleasepool {
self.sourceController = nil;
services = nil;
registryID = nil;
}
usleep(kDefaultReportDispatchCompletionTime);
services = CFBridgingRelease(IOHIDEventSystemClientCopyServices(self.eventSystemClient));
XCTAssert (services == nil || (services && services.count == 0), "Copy service:
}
- (void)testSystemProperty {
CFStringRef testPropertyKey = CFSTR ("TestProperty");
IOHIDEventSystemClientSetProperty(self.testEventSystemClient, testPropertyKey, testPropertyKey);
CFTypeRef value = IOHIDEventSystemClientCopyProperty (self.testEventSystemClient, testPropertyKey);
XCTAssert (CFEqual(testPropertyKey, value));
if (value) {
CFRelease(value);
}
}
- (void)testEventDispatch {
IOReturn status;
HIDVendorMessage32BitDescriptorInputReport report;
IOHIDEventBlock handler = ^(void * __unused _Nullable target, void * __unused _Nullable refcon, void * __unused _Nullable sender, IOHIDEventRef __unused event) {
++self.eventCount;
};
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientRegisterEventBlock (self.eventSystemClient, handler, (__bridge void * _Nullable)(self), NULL);
});
sleep (kServiceMatchingTimeout);
self.eventCount = 0;
report.VEN_VendorDefined0023 = 1;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 2;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 3;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport:
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue(self.eventCount == 3, "eventCount:
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientUnregisterEventBlock (self.eventSystemClient, handler, (__bridge void * _Nullable)(self), NULL);
});
self.eventCount = 0;
report.VEN_VendorDefined0023 = 1;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 2;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 3;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport:
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue(self.eventCount == 0, "eventCount:}
- (void)testRunloopEventDispatch {
IOReturn status;
HIDVendorMessage32BitDescriptorInputReport report;
IOHIDEventBlock handler = ^(void * __unused _Nullable target, void * __unused _Nullable refcon, void * __unused _Nullable sender, __unused IOHIDEventRef event) {
++self.eventCount;
};
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientUnscheduleFromDispatchQueue(self.eventSystemClient, self.clientQueue);
});
CFRunLoopRef runLoop = IOHIDUnitTestCreateRunLoop (31);
HIDXCTAssertAndThrowTrue(runLoop != NULL);
CFRunLoopPerformBlock(runLoop, kCFRunLoopDefaultMode, ^{
IOHIDEventSystemClientUnregisterEventBlock (self.eventSystemClient, handler, (__bridge void * _Nullable)(self), NULL);
IOHIDEventSystemClientRegisterEventBlock (self.eventSystemClient, handler, (__bridge void * _Nullable)(self), NULL);
IOHIDEventSystemClientScheduleWithRunLoop (self.eventSystemClient, runLoop, kCFRunLoopDefaultMode);
});
CFRunLoopWakeUp(runLoop);
sleep (kServiceMatchingTimeout);
self.eventCount = 0;
report.VEN_VendorDefined0023 = 1;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 2;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 3;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport:
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue(self.eventCount == 3, "eventCount:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
CFRunLoopPerformBlock(runLoop, kCFRunLoopDefaultMode, ^{
IOHIDEventSystemClientUnregisterEventBlock (self.eventSystemClient, handler, (__bridge void * _Nullable)(self), NULL);
IOHIDEventSystemClientUnscheduleWithRunLoop (self.eventSystemClient, runLoop, kCFRunLoopDefaultMode);
dispatch_semaphore_signal(sema);
});
CFRunLoopWakeUp(runLoop);
XCTAssert(dispatch_semaphore_wait (sema, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC)) == 0);
IOHIDUnitTestDestroyRunLoop (runLoop);
}
- (void)testEventEventConnectionFilter {
IOReturn status;
HIDVendorMessage32BitDescriptorInputReport report;
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientRegisterEventCallback (self.eventSystemClient, EventSystemEventCallback, (__bridge void * _Nullable)(self), NULL);
});
sleep (kServiceMatchingTimeout);
self.eventCount = 0;
report.VEN_VendorDefined0023 = 1;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport:
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue(self.eventCount == 1, "eventCount:
dispatch_sync (self.clientQueue, ^{
NSArray * eventFilterMask = @[];
IOHIDEventSystemClientSetProperty (self.eventSystemClient, CFSTR(kIOHIDClientEventFilterKey),(CFArrayRef)eventFilterMask);
});
report.VEN_VendorDefined0023 = 2;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport:
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue(self.eventCount == 1, "eventCount:
dispatch_sync (self.clientQueue, ^{
NSArray * eventFilterMask = @[@(kIOHIDEventTypeVendorDefined)];
IOHIDEventSystemClientSetProperty (self.eventSystemClient, CFSTR(kIOHIDClientEventFilterKey),(CFArrayRef)eventFilterMask);
});
report.VEN_VendorDefined0023 = 3;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport:
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue(self.eventCount == 2, "eventCount:
}
- (void)testEventFilterClient {
IOReturn status;
HIDVendorMessage32BitDescriptorInputReport report;
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientRegisterEventCallback (self.eventSystemClient, EventSystemEventCallback, (__bridge void * _Nullable)(self), NULL);
IOHIDEventSystemClientRegisterEventFilterCallback (self.eventSystemClient, EventSystemEventFilterCallback, (__bridge void * _Nullable)(self), NULL);
});
sleep (kServiceMatchingTimeout);
self.filterCount = 0;
self.eventCount = 0;
report.VEN_VendorDefined0023 = 1;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 2;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 3;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport:
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue(self.filterCount == 1 && self.eventCount == 2, "filterCount:
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientUnregisterEventCallback (self.eventSystemClient, EventSystemEventCallback, (__bridge void * _Nullable)(self), NULL);
IOHIDEventSystemClientUnregisterEventFilterCallback (self.eventSystemClient, EventSystemEventFilterCallback, (__bridge void * _Nullable)(self), NULL);
});
self.filterCount = 0;
self.eventCount = 0;
report.VEN_VendorDefined0023 = 1;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 2;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport: report.VEN_VendorDefined0023 = 3;
status = [self.sourceController handleReport: (uint8_t*)&report Length:1 andInterval:2000];
XCTAssert(status == kIOReturnSuccess, "handleReport:
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssert(self.filterCount == 0 && self.eventCount == 0);
XCTAssert(self.resetCount == 0);
}
- (void)testPropertyNotification {
self.propertyCount = 0;
CFStringRef testPropertyKey = CFSTR ("TestPropertyCallback");
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientRegisterPropertyChangedCallback (self.eventSystemClient, testPropertyKey , EventSystemPropertyChangedCallback, (__bridge void * _Nullable)(self), NULL);
});
usleep(kDefaultReportDispatchCompletionTime);
for (int i = 0; i < 10; i++) {
NSNumber * value = @(i);
Boolean status = IOHIDEventSystemClientSetProperty(self.testEventSystemClient, testPropertyKey, (__bridge CFTypeRef _Nonnull)(value));
XCTAssert(status);
}
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue (self.propertyCount == 10, "propertyCount:
dispatch_sync (self.clientQueue, ^{
IOHIDEventSystemClientUnregisterPropertyChangedCallback (self.eventSystemClient, testPropertyKey , EventSystemPropertyChangedCallback, (__bridge void * _Nullable)(self), NULL);
});
self.propertyCount = 0;
for (int i = 0; i < 10; i++) {
Boolean status = IOHIDEventSystemClientSetProperty(self.testEventSystemClient, testPropertyKey, testPropertyKey);
XCTAssert(status);
}
usleep(kDefaultReportDispatchCompletionTime);
XCTAssertTrue (self.propertyCount == 0, "propertyCount:}
- (void)testClientEventDispatch {
self.eventCount = 0;
UInt32 value = 0x55555555;
IOHIDEventRef event = IOHIDEventCreateVendorDefinedEvent(
kCFAllocatorDefault,
mach_absolute_time(),
kHIDPage_AppleVendor,
kHIDUsage_AppleVendor_Message,
0,
(uint8_t*)&value,
sizeof(value),
0);
IOHIDEventSystemClientDispatchEvent(self.testEventSystemClient, event);
// Allow event to be dispatched
usleep(kDefaultReportDispatchCompletionTime);
XCTAssert(self.eventCount == 1);
}
- (void)testEventSystemDebugDump {
CFArrayRef value;
value = IOHIDEventSystemClientCopyProperty(self.testEventSystemClient, CFSTR(kIOHIDClientRecordsKey));
XCTAssert(value != NULL && CFGetTypeID(value) == CFArrayGetTypeID() && CFArrayGetCount(value) != 0, "kIOHIDClientRecordsKey:
value = IOHIDEventSystemClientCopyProperty(self.testEventSystemClient, CFSTR(kIOHIDServiceRecordsKey));
XCTAssert(value != NULL && CFGetTypeID(value) == CFArrayGetTypeID() && CFArrayGetCount(value) != 0, "kIOHIDServiceRecordsKey:
value = IOHIDEventSystemClientCopyProperty(self.testEventSystemClient, CFSTR(kIOHIDSessionFilterDebugKey));
XCTAssert(value != NULL && CFGetTypeID(value) == CFArrayGetTypeID() && CFArrayGetCount(value) != 0, "kIOHIDSessionFilterDebugKey:
}
-(void) PropertyChangeCallback: (nonnull __unused CFStringRef) property And: (nullable __unused CFTypeRef) value {
++self.propertyCount;
}
-(void) EventCallback: (nonnull __unused IOHIDEventRef) event For: (nullable __unused IOHIDServiceClientRef) service {
++self.eventCount;
}
-(BOOL) FilterCallback: (nonnull __unused IOHIDEventRef) event For: (nullable __unused IOHIDServiceClientRef) service {
if (IOHIDEventConformsTo(event, kIOHIDEventTypeVendorDefined)) {
uint8_t *payload = NULL;
CFIndex lenght = 0;
IOHIDEventGetVendorDefinedData (event, &payload, &lenght);
if (*payload == 2) {
++self.filterCount;
return YES;
}
}
return NO;
}
-(void) ServiceAddedCallback: (__unused IOHIDServiceClientRef) service {
++self.serviceAddedCount;
}
-(void) ServiceRemovedCallback: (__unused IOHIDServiceClientRef) service {
++self.serviceRemovedCount;
}
-(void) ResetCallback {
++self.resetCount;
}
@end
boolean_t EventSystemEventFilterCallback (void * _Nullable target, void * _Nullable refcon __unused, void * _Nullable sender, IOHIDEventRef event) {
TestEventSystemClient *self = (__bridge TestEventSystemClient *)target;
return [self FilterCallback : event For:(IOHIDServiceClientRef)sender];
}
void EventSystemResetCallback(void * _Nullable target, void * _Nullable context __unused) {
TestEventSystemClient *self = (__bridge TestEventSystemClient *)target;
[self ResetCallback];
}
//void EventSystemServiceRemovedCallback (void *target, void *refcon __unused, IOHIDServiceClientRef service) {
// TestEventSystemClient *self = (__bridge TestEventSystemClient *)target;
// [self ServiceRemovedCallback: service];
//}
//void EventSystemServiceAddedCallback (void *target, void *refcon __unused, IOHIDServiceClientRef service) {
// TestEventSystemClient *self = (__bridge TestEventSystemClient *)target;
// [self ServiceAddedCallback: service];
//}
void EventSystemEventCallback (void * _Nullable target, void * _Nullable refcon __unused, void * _Nullable sender, IOHIDEventRef event) {
TestEventSystemClient *self = (__bridge TestEventSystemClient *)target;
[self EventCallback : event For:(IOHIDServiceClientRef)sender];
}
void EventSystemPropertyChangedCallback (void * _Nullable target, void * _Nullable __unused context, CFStringRef property, CFTypeRef value) {
TestEventSystemClient *self = (__bridge TestEventSystemClient *)target;
[self PropertyChangeCallback : property And:value];
}