TestVirtualService.m [plain text]
//
// TestVirtualService.m
// IOHIDFamily
//
// Created by yg on 1/23/17.
//
//
#import <XCTest/XCTest.h>
#include <IOKit/hid/IOHIDEventSystemClient.h>
#include <IOKit/hid/IOHIDEvent.h>
#include <mach/mach_time.h>
#include "IOHIDUnitTestUtility.h"
#include "IOHIDPrivateKeys.h"
#import "IOHIDEventSystemTestController.h"
#import "IOHIDUserDeviceTestController.h"
#import "IOHIDDeviceTestController.h"
#import "IOHIDUnitTestDescriptors.h"
static void __IOHIDVirtualServiceNotifyCallback ( void * target, void * context, IOHIDServiceClientRef service, uint32_t type, CFDictionaryRef property);
static bool __IOHIDVirtualServiceSetPropertyCallback (void * _Nullable target, void * _Nullable context, IOHIDServiceClientRef service, CFStringRef key, CFTypeRef value);
static CFTypeRef __IOHIDVirtualServiceCopyPropertyCallback (void * _Nullable target, void * _Nullable context, IOHIDServiceClientRef service, CFStringRef key);
static IOHIDEventRef __IOHIDVirtualServiceCopyEvent (void * _Nullable target, void * _Nullable context, IOHIDServiceClientRef service, IOHIDEventType type, IOHIDEventRef matching, IOOptionBits options);
static IOReturn __IOHIDVirtualServiceSetOutputEvent (void * _Nullable target, void * _Nullable context, IOHIDServiceClientRef service, IOHIDEventRef event);
@interface TestVirtualService : XCTestCase
@property IOHIDEventSystemClientRef virtualServiceSystemClient;
@property dispatch_queue_t virtualServiceQueue;
@property dispatch_queue_t rootQueue;
@property IOHIDServiceClientRef virtualService;
@property NSMutableDictionary *virtualServicePropertyCache;
@property dispatch_queue_t eventControllerQueue;
@property IOHIDEventSystemTestController *eventController;
@property NSInteger virtualServiceOutputEventCount;
@end
@implementation TestVirtualService
- (void)setUp {
self.virtualServiceSystemClient = IOHIDEventSystemClientCreateWithType (kCFAllocatorDefault, kIOHIDEventSystemClientTypeSimple, NULL);
XCTAssert (self.virtualServiceSystemClient != NULL);
self.rootQueue = IOHIDUnitTestCreateRootQueue(63, 2);
XCTAssert(self.rootQueue != NULL) ;
self.virtualServiceQueue = dispatch_queue_create_with_target ("IOHIDVirtualService", DISPATCH_QUEUE_SERIAL, self.rootQueue);
XCTAssert(self.virtualServiceQueue != NULL);
self.virtualServicePropertyCache = [[NSMutableDictionary alloc] init];
self.virtualServicePropertyCache[@kIOHIDPrimaryUsagePageKey] = @(kHIDPage_GenericDesktop);
self.virtualServicePropertyCache[@kIOHIDPrimaryUsageKey] = @(kHIDUsage_GD_Mouse);
self.virtualServicePropertyCache[@kIOHIDVendorIDKey] = @(0xff00);
self.virtualServicePropertyCache[@kIOHIDProductIDKey] = @(0xff00);
self.virtualServicePropertyCache[@kIOHIDDeviceUsagePairsKey] = @[
@{
@kIOHIDDeviceUsagePageKey : @(kHIDPage_GenericDesktop),
@kIOHIDDeviceUsageKey: @(kHIDUsage_GD_Pointer)
},
@{
@kIOHIDDeviceUsagePageKey : @(kHIDPage_AppleVendor),
@kIOHIDDeviceUsageKey: @(kHIDPage_AppleVendor)
}
];
self.virtualServicePropertyCache[@kIOHIDTransportKey] = @"Virtual";
NSString * uniqueID = [[[NSUUID alloc] init] UUIDString];
self.virtualServicePropertyCache[@kIOHIDPhysicalDeviceUniqueIDKey] = uniqueID;
IOHIDEventSystemClientScheduleWithDispatchQueue(self.virtualServiceSystemClient, self.virtualServiceQueue);
dispatch_sync (self.virtualServiceQueue, ^() {
IOHIDVirtualServiceClientCallbacks callbacks = {
__IOHIDVirtualServiceNotifyCallback,
__IOHIDVirtualServiceSetPropertyCallback,
__IOHIDVirtualServiceCopyPropertyCallback,
__IOHIDVirtualServiceCopyEvent,
__IOHIDVirtualServiceSetOutputEvent
};
self.virtualService = IOHIDVirtualServiceClientCreate (self.virtualServiceSystemClient, NULL, callbacks, (__bridge void * _Nullable)(self), NULL);
});
HIDXCTAssertAndThrowTrue (self.virtualService != NULL);
sleep (2);
self.eventControllerQueue = dispatch_queue_create_with_target ("IOHIDEventSystemTestController", DISPATCH_QUEUE_SERIAL, self.rootQueue);
HIDXCTAssertAndThrowTrue(self.eventControllerQueue != nil);
self.eventController = [[IOHIDEventSystemTestController alloc] initWithDeviceUniqueID:uniqueID AndQueue:self.eventControllerQueue];
HIDXCTAssertAndThrowTrue(self.eventController != nil);
}
- (void)tearDown {
@autoreleasepool {
self.eventController = nil;
}
dispatch_sync (self.virtualServiceQueue, ^() {
if (self.virtualService) {
IOHIDVirtualServiceClientRemove (self.virtualService);
CFRelease(self.virtualService);
}
if (self.virtualServiceSystemClient) {
IOHIDEventSystemClientUnscheduleFromDispatchQueue (self.virtualServiceSystemClient, self.virtualServiceQueue);
CFRelease (self.virtualServiceSystemClient);
}
});
[super tearDown];
}
- (void)testVirtualServiceDispatchEvent {
uint8_t buffer[] = {1,2,3};
IOHIDEventRef event = IOHIDEventCreateVendorDefinedEvent (kCFAllocatorDefault, mach_absolute_time(), 0,0,0, buffer, sizeof(buffer), 0);
XCTAssert (event != NULL);
for (NSInteger index = 0 ; index < 10; index++) {
XCTAssert(IOHIDVirtualServiceClientDispatchEvent (self.virtualService, event) == TRUE);
}
// Allow event to be dispatched
usleep (kDefaultReportDispatchCompletionTime);
// Make copy
NSArray *events = nil;
@synchronized (self.eventController.events) {
events = [self.eventController.events copy];
}
EVENTS_STATS stats = [IOHIDEventSystemTestController getEventsStats:events];
HIDTestEventLatency(stats);
XCTAssert(stats.counts[kIOHIDEventTypeVendorDefined] == 10);
}
- (void)testVirtualServiceCopyEvent {
IOHIDEventRef event = IOHIDServiceClientCopyEvent (self.eventController.eventService, 0, NULL, 0);
XCTAssert(event != NULL);
}
- (void)testVirtualServiceSetOutputEvent {
IOReturn status;
status = IOHIDServiceClientSetElementValue(self.eventController.eventService, kHIDPage_LEDs, kHIDUsage_LED_CapsLock, 1);
XCTAssert (status == kIOReturnSuccess, "IOHIDServiceClientSetElementValue: sleep (1);
XCTAssert (self.virtualServiceOutputEventCount == 1);
status = IOHIDServiceClientSetElementValue(self.eventController.eventService, kHIDPage_LEDs, kHIDUsage_LED_CapsLock, 0);
XCTAssert (status == kIOReturnSuccess, "IOHIDServiceClientSetElementValue: sleep (1);
XCTAssert (self.virtualServiceOutputEventCount == 2);
}
- (id) serviceCopyProperty: (IOHIDServiceClientRef) __unused service : (NSString *) key {
return self.virtualServicePropertyCache[key];
}
- (Boolean) serviceSetProperty: (IOHIDServiceClientRef) __unused service : (NSString *) key : (id) value {
self.virtualServicePropertyCache[key] = value;
return true;
}
- (IOHIDEventRef) serviceCopyEvent: (IOHIDServiceClientRef) __unused service : (IOHIDEventType) __unused type : (IOHIDEventRef) __unused matching : (IOOptionBits) __unused options {
uint8_t buffer[] = {1,2,3};
IOHIDEventRef event = IOHIDEventCreateVendorDefinedEvent (kCFAllocatorDefault, mach_absolute_time(), 0,0,0, buffer, sizeof(buffer), 0);
XCTAssert (event != NULL);
return event;
}
@end
void __IOHIDVirtualServiceNotifyCallback ( void * __unused target, void * __unused context, IOHIDServiceClientRef __unused service, uint32_t type, CFDictionaryRef property) {
NSLog(@"__IOHIDVirtualServiceNotifyCallback: type:
}
bool __IOHIDVirtualServiceSetPropertyCallback (void * target, void * __unused context, IOHIDServiceClientRef service, CFStringRef key, CFTypeRef value) {
TestVirtualService *self = (__bridge TestVirtualService *)target;
NSLog(@"__IOHIDVirtualServiceSetPropertyCallback Key:
return [self serviceSetProperty: service : (__bridge NSString *)(key) : (__bridge id)(value)];
}
CFTypeRef __IOHIDVirtualServiceCopyPropertyCallback (void * target, void * __unused context, IOHIDServiceClientRef service, CFStringRef key) {
TestVirtualService *self = (__bridge TestVirtualService *)target;
CFTypeRef value = (__bridge CFTypeRef)([self serviceCopyProperty: service : (__bridge NSString *)(key)]);
NSLog(@"__IOHIDVirtualServiceCopyPropertyCallback Key:
return value ? CFRetain(value) : NULL;
}
IOHIDEventRef __IOHIDVirtualServiceCopyEvent (void * target, void * __unused context, IOHIDServiceClientRef service, IOHIDEventType type, IOHIDEventRef matching, IOOptionBits options) {
TestVirtualService *self = (__bridge TestVirtualService *)target;
NSLog(@"__IOHIDVirtualServiceCopyEvent");
return [self serviceCopyEvent:service :type :matching :options];;
}
static IOReturn __IOHIDVirtualServiceSetOutputEvent (void * target, void * __unused context, IOHIDServiceClientRef __unused service, IOHIDEventRef __unused event) {
TestVirtualService *self = (__bridge TestVirtualService *)target;
NSLog(@"__IOHIDVirtualServiceSetOutputEvent");
self.virtualServiceOutputEventCount += 1;
return kIOReturnSuccess;
}