TestKeyboardServicePlugin.m [plain text]
//
// TestKeyRepeats.m
// IOHIDFamily
//
// Created by YG on 10/31/16.
//
//
#import <XCTest/XCTest.h>
#include "IOHIDUnitTestUtility.h"
#import "IOHIDEventSystemTestController.h"
#import "IOHIDUserDeviceTestController.h"
#import "IOHIDDeviceTestController.h"
#import "IOHIDUnitTestDescriptors.h"
typedef struct {
NSUInteger initialDelay;
NSUInteger repeatDelay;
NSUInteger repeatTime;
} KEY_REPEAT_INFO;
static uint8_t descriptor[] = {
HIDKeyboardDescriptor
};
@interface TestKeyboardServicePlugin : XCTestCase
@property IOHIDEventSystemClientRef eventSystem;
@property IOHIDServiceClientRef eventService;
@property IOHIDUserDeviceRef userDevice;
@property XCTestExpectation * testServiceExpectation;
@property XCTestExpectation * testKbcDownEventExpectation;
@property XCTestExpectation * testKbcUpEventExpectation;
@property NSMutableArray * events;
@end
@implementation TestKeyboardServicePlugin
- (void)setUp {
[super setUp];
self.events = [[NSMutableArray alloc] init];
NSString * uniqueID = [[[NSUUID alloc] init] UUIDString];
self.eventSystem = IOHIDEventSystemClientCreateWithType (kCFAllocatorDefault, kIOHIDEventSystemClientTypeMonitor, NULL);
HIDXCTAssertAndThrowTrue(self.eventSystem != NULL);
NSDictionary *matching = @{@kIOHIDPhysicalDeviceUniqueIDKey : uniqueID};
IOHIDEventSystemClientSetMatching(self.eventSystem , (__bridge CFDictionaryRef)matching);
self.testServiceExpectation = [[XCTestExpectation alloc] initWithDescription:@"Expectation: Test HID service"];
IOHIDServiceClientBlock handler = ^(void * _Nullable target __unused, void * _Nullable refcon __unused, IOHIDServiceClientRef _Nonnull service __unused) {
self.eventService = service;
[self.testServiceExpectation fulfill];
};
IOHIDEventSystemClientRegisterDeviceMatchingBlock(self.eventSystem , handler, NULL, NULL);
IOHIDEventSystemClientRegisterEventBlock(self.eventSystem, ^(void * _Nullable target __unused, void * _Nullable refcon __unused, void * _Nullable sender __unused, IOHIDEventRef _Nonnull event) {
NSLog(@"Event: if (IOHIDEventGetType(event) == kIOHIDEventTypeKeyboard &&
IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardUsage) == kHIDUsage_KeypadEqualSignAS400) {
if (!IOHIDEventGetIntegerValue(event, kIOHIDEventFieldKeyboardDown)) {
[self.testKbcUpEventExpectation fulfill];
}
}
[self.events addObject: (__bridge id _Nonnull)event];
}, NULL, NULL);
IOHIDEventSystemClientScheduleWithDispatchQueue(self.eventSystem, dispatch_get_main_queue());
NSDictionary * description = @{
@kIOHIDPhysicalDeviceUniqueIDKey : uniqueID,
@kIOHIDReportDescriptorKey : [NSData dataWithBytes:descriptor length:sizeof(descriptor)],
@kIOHIDVendorIDKey : @(555),
@kIOHIDProductIDKey : @(555),
};
self.userDevice = IOHIDUserDeviceCreate(kCFAllocatorDefault, (CFDictionaryRef)description);
HIDXCTAssertAndThrowTrue(self.userDevice != NULL, "HID User Device config:}
- (void)tearDown {
if (self.userDevice) {
CFRelease(self.userDevice);
}
if (self.eventSystem) {
CFRelease(self.eventSystem);
}
[super tearDown];
}
- (void)checkRepeats: (NSUInteger) initialDelayMS :(NSUInteger) repeatDelayMS :(NSUInteger) repeatTime
{
XCTWaiterResult result;
IOReturn status;
NSNumber *valueInitialRepeat = @(MS_TO_NS(initialDelayMS));
NSNumber *valueRepeatDelay = @(MS_TO_NS(repeatDelayMS));
self.testKbcUpEventExpectation = [[XCTestExpectation alloc] initWithDescription:@"Expectation: Keyboard event Up"];
IOHIDServiceClientSetProperty(self.eventService,
CFSTR(kIOHIDServiceInitialKeyRepeatDelayKey),
(__bridge CFTypeRef _Nonnull)(valueInitialRepeat));
IOHIDServiceClientSetProperty(self.eventService,
CFSTR(kIOHIDServiceKeyRepeatDelayKey),
(__bridge CFTypeRef _Nonnull)(valueRepeatDelay));
TestLog("initialDelayMS:
HIDKeyboardDescriptorInputReport report;
memset(&report, 0, sizeof(report));
report.KB_Keyboard[0] = kHIDUsage_KeypadEqualSignAS400;
status = IOHIDUserDeviceHandleReport(self.userDevice, (uint8_t *)&report, sizeof(report));
XCTAssert(status == kIOReturnSuccess, "IOHIDUserDeviceHandleReport:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(repeatTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
IOReturn ret;
HIDKeyboardDescriptorInputReport ureport;
memset(&ureport, 0, sizeof(report));
ret = IOHIDUserDeviceHandleReport(self.userDevice, (uint8_t *)&ureport, sizeof(ureport));
XCTAssert(ret == kIOReturnSuccess, "IOHIDUserDeviceHandleReport:
});
result = [XCTWaiter waitForExpectations:@[self.testKbcUpEventExpectation] timeout:10];
HIDXCTAssertWithParameters (RETURN_FROM_TEST | COLLECT_HIDUTIL,
result == XCTWaiterResultCompleted,
" self.testKbcUpEventExpectation);
NSInteger expectedCount = (repeatTime * 1000 - initialDelayMS) / repeatDelayMS;
NSLog (@"expected count
HIDXCTAssertWithParameters(RETURN_FROM_TEST | COLLECT_TAILSPIN | COLLECT_HIDUTIL,
VALUE_IN_RANGE(expectedCount, VALUE_PST(self.events.count,-20) , VALUE_PST(self.events.count,+20)),
"expected count (int)expectedCount,
(int)self.events.count
);
uint64_t delta = NS_TO_MS(IOHIDInitTestAbsoluteTimeToNanosecond (IOHIDEventGetTimeStamp((IOHIDEventRef)self.events[1]) - IOHIDEventGetTimeStamp((IOHIDEventRef)self.events[0]))) ;
HIDXCTAssertWithParameters(RETURN_FROM_TEST | COLLECT_TAILSPIN,
VALUE_IN_RANGE(delta , VALUE_PST(initialDelayMS,-20) , VALUE_PST(initialDelayMS,+20)),
"initial delay: (int)delta,
(int)VALUE_PST(initialDelayMS,-20),
(int)VALUE_PST(initialDelayMS,+20),
self.events[0],
self.events[0]
);
for (size_t index = 2; index < (self.events.count - 1); index++) {
uint64_t prev = IOHIDEventGetTimeStamp((IOHIDEventRef)self.events[index - 1]);
uint64_t next = IOHIDEventGetTimeStamp((IOHIDEventRef)self.events[index]);
delta = NS_TO_MS(IOHIDInitTestAbsoluteTimeToNanosecond (next - prev));
NSLog (@"repeat:
HIDXCTAssertWithParameters (RETURN_FROM_TEST | COLLECT_TAILSPIN,
VALUE_IN_RANGE(delta , VALUE_PST(repeatDelayMS,-20) , VALUE_PST(repeatDelayMS,+20)),
"repeat: (int)delta ,
(int)VALUE_PST(repeatDelayMS,-20),
(int)VALUE_PST(repeatDelayMS,+20),
self.events[index - 1],
self.events[index]
);
}
}
- (void) MAC_OS_ONLY_TEST_CASE(testKeyRepeat) {
XCTWaiterResult result;
KEY_REPEAT_INFO info [] = {
{250 , 33, 2},
{500 , 66, 2}
};
result = [XCTWaiter waitForExpectations:@[self.testServiceExpectation] timeout:20];
HIDXCTAssertWithParameters ( RETURN_FROM_TEST | COLLECT_TAILSPIN | COLLECT_IOREG,
result == XCTWaiterResultCompleted,
" self.testServiceExpectation);
for (size_t index = 0; index < (sizeof (info) / sizeof(info[0]));index++) {
[self checkRepeats:info[index].initialDelay :info[index].repeatDelay : info[index].repeatTime];
[self.events removeAllObjects];
}
}
@end