TestHIDEventDriver.m   [plain text]


//
//  TestHIDEventDriver.m
//  IOHIDFamily
//
//  Created by yg on 2/6/17.
//
//

#import <XCTest/XCTest.h>
#include "IOHIDUnitTestUtility.h"
#include "IOHIDEventSystemTestController.h"
#include "IOHIDUserDeviceTestController.h"
#include "IOHIDUnitTestDescriptors.h"
#include <IOKit/hid/IOHIDEventSystemPrivate.h>



@interface TestHIDEventDriver : XCTestCase

@property dispatch_queue_t                  eventControllerQueue;
@property dispatch_queue_t                  rootQueue;
@property IOHIDEventSystemTestController *  eventController;
@property IOHIDUserDeviceTestController *   sourceController;

@end

@implementation TestHIDEventDriver

- (void)setUp {
    [super setUp];

    self.rootQueue = IOHIDUnitTestCreateRootQueue(31, 2);
    
    self.eventControllerQueue = dispatch_queue_create_with_target ("IOHIDEventSystemTestController", DISPATCH_QUEUE_SERIAL, self.rootQueue);
    HIDXCTAssertAndThrowTrue(self.eventControllerQueue != nil);

}

- (void)tearDown {
    if (self.sourceController) {
        [self.sourceController invalidate];
    }

    @autoreleasepool {
        self.sourceController = nil;
    }
    
    if (self.eventController) {
        
        // Check if event system reset occur
        XCTAssert(self.eventController.eventSystemResetCount == 0, "IOHIDEventSystem resetCount:%d", (int)self.eventController.eventSystemResetCount);
       
        [self.eventController invalidate];
    }

    @autoreleasepool {
        self.eventController = nil;
    }

    [super tearDown];
}

-(void)setupTestSystem : (NSData *) descriptorData {
    
    if (self.sourceController) {
        [self.sourceController invalidate];
    }
    @autoreleasepool {
        self.sourceController = nil;
    }
    
    if (self.eventController) {
        // Check if event system reset occur
        XCTAssert(self.eventController.eventSystemResetCount == 0, "IOHIDEventSystem resetCount:%d", (int)self.eventController.eventSystemResetCount);
        [self.eventController invalidate];
    }
    
    @autoreleasepool {
        self.eventController = nil;
    }
    
    NSString * uniqueID = [[[NSUUID alloc] init] UUIDString];
    
    self.sourceController = [[IOHIDUserDeviceTestController alloc] initWithDescriptor:descriptorData DeviceUniqueID:uniqueID andQueue:nil];
    HIDXCTAssertAndThrowTrue(self.sourceController != nil);
    
    self.eventController = [[IOHIDEventSystemTestController alloc] initWithDeviceUniqueID:uniqueID :self.eventControllerQueue :kIOHIDEventSystemClientTypeAdmin];
    HIDXCTAssertAndThrowTrue(self.eventController != nil);
}


- (void)MAC_OS_ONLY_TEST_CASE(testGameController) {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDGameControllerDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDGameControllerDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    for (int index = 0; index < 10; index++) {
        report.BTN_CountedBufferGamePadButton2 = 20 + index;
        report.BTN_CountedBufferGamePadButton3 = 20 + index;
        report.GD_CountedBufferGamePadPointerX = 20 + index;
        report.GD_CountedBufferGamePadPointerY = 20 + index;
        status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
        XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);

    }
   
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];

    HIDTestEventLatency(stats);
    
    XCTAssert(stats.totalCount == 9,
                  "events count:%lu expected:%d events:%@ ", (unsigned long)stats.totalCount , 9, events);

    XCTAssert (stats.counts[kIOHIDEventTypeGameController] == 9, "kIOHIDEventTypeGameController count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeGameController], events);
}


- (void)testHeadsetTelephony {
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDHeadsetTelephonyDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDHeadsetTelephonyDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    for (int index = 0; index < 10; index++) {
        report.CD_ConsumerControlVolumeIncrement = 1;
        report.CD_ConsumerControlVolumeDecrement = 0;
        status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
        XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
        report.CD_ConsumerControlVolumeIncrement = 0;
        report.CD_ConsumerControlVolumeDecrement = 1;
        status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
        XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    }

    report.CD_ConsumerControlVolumeIncrement = 0;
    report.CD_ConsumerControlVolumeDecrement = 0;
    
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);

    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    XCTAssert(stats.totalCount == 40,
              "events count:%lu expected:%d events:%@\n", (unsigned long)stats.totalCount , 40, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeKeyboard] == 40, "kIOHIDEventTypeKeyboard count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeKeyboard], events);
}

- (void)testMediaPlayback {
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDMediaPlaybackDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDMediaPlaybackDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    report.CD_ConsumerControlScanNextTrack = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.CD_ConsumerControlScanNextTrack = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    report.CD_ConsumerControlMute = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.CD_ConsumerControlMute = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
  
    report.CD_ConsumerControlRandomPlay = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.CD_ConsumerControlRandomPlay = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    report.CD_ConsumerControlRepeat = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.CD_ConsumerControlRepeat = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);

    report.CD_ConsumerControlAcPromote = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.CD_ConsumerControlAcPromote = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
   
    report.CD_ConsumerControlAcDemote = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.CD_ConsumerControlAcDemote = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);

    report.CD_ConsumerControlAcAddToCart = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.CD_ConsumerControlAcAddToCart = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);

    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);

    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    XCTAssert(stats.totalCount == 14,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 14, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeKeyboard] == 14, "kIOHIDEventTypeKeyboard count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeKeyboard], events);
}
 
    
- (void)testMediaPlaybackAndTelephony {
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDTelephonyAndMediaPlaybackDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDTelephonyAndMediaPlaybackDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    report.TEL_ConsumerControlFlash = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.TEL_ConsumerControlFlash = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);

    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    XCTAssert(stats.totalCount == 2,
              "events count:%lu expected:%d \n%@\n", (unsigned long)stats.totalCount , 2, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeKeyboard] == 2, "kIOHIDEventTypeKeyboard count:%lu", (unsigned long)stats.counts[kIOHIDEventTypeNULL]);
}
    

- (void)EMBEDDED_OS_ONLY_TEST_CASE(testSingleTouch) {

    IOReturn status;
    
    static uint8_t descriptor [] = {HIDSingleTouchDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDSingleTouchDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    report.DIG_TouchScreenFingerTouch = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.DIG_TouchScreenFingerTouch = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);

    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    XCTAssert(stats.totalCount == 2,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 2, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeDigitizer] == 2, "kIOHIDEventTypeDigitizer count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeDigitizer], events);
}

- (void)EMBEDDED_OS_ONLY_TEST_CASE(testBasicGestureRecognition) {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDBasicGestureRecognitionDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDBasicGestureRecognitionDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    
    report.DIG_TouchPadFingerTouch = 0x1;
    
    report.DIG_TouchPad0063 = 0x1;
    report.DIG_TouchPad0062 = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.DIG_TouchPad0063 = 0x202;
    report.DIG_TouchPad0062 = 2;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.DIG_TouchPad0063 = 0x030303;
    report.DIG_TouchPad0062 = 3;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    report.DIG_TouchPad0063 = 0x04040404;
    report.DIG_TouchPad0062 = 4;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);
    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    //
    // If service dispatch collection Digitizer->collectionDispatch  then  for each report with touch on we will dispatch digitizer event
    // regardless of whatever any other params have changed. @todo we might be smart in kernel to handle this case but for now fix test
    //
    NSDictionary * digitizerProp = (NSDictionary *)CFBridgingRelease(IOHIDServiceClientCopyProperty (self.eventController.eventService, CFSTR("Digitizer")));
    
    uint32_t numOfDigitizerEvents = 1;
    
    if (digitizerProp && digitizerProp [@"collectionDispatch"] && ((NSNumber *)digitizerProp [@"collectionDispatch"]).boolValue == YES) {
        numOfDigitizerEvents = 4;
    }
    
    XCTAssert(stats.totalCount == numOfDigitizerEvents + 4,
              "events count:%d expected:%d events:%@", stats.totalCount , numOfDigitizerEvents + 4, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeUnicode] == 4 && stats.counts[kIOHIDEventTypeDigitizer] == numOfDigitizerEvents, "kIOHIDEventTypeUnicode:%lu kIOHIDEventTypeDigitizer:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeUnicode], (unsigned long)stats.counts[kIOHIDEventTypeDigitizer] , events);
}


- (void)EMBEDDED_OS_ONLY_TEST_CASE(testGestureRecognitionAndAltInterpretation) {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDGestureRecognitionAndAltInterpretationDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDGestureRecognitionAndAltInterpretationDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    
    report.DIG_TouchPadFingerTouch = 0x1;
    report.DIG_TouchPad0063 = 0x1;
    report.DIG_TouchPad0062 = 1;
    report.DIG_TouchPad0061 = 80;
    report.DIG_VendorDefined0063 = 0x202;
    report.DIG_VendorDefined0062 = 2;
    report.DIG_VendorDefined0061 = 90;

    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);

    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    XCTAssert(stats.totalCount == 2,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 2, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeUnicode] == 1 && stats.counts[kIOHIDEventTypeDigitizer] == 1 , "kIOHIDEventTypeUnicode count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeUnicode], events);
}

- (void)testMultiAxisController {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDMultiAxisControllerDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDMultiAxisControllerDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    
    report.GD_MultiaxisControllerX = 80;
    report.GD_MultiaxisControllerY = 80;
    report.BTN_MultiaxisControllerButton1 = 01;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    report.GD_MultiaxisControllerX = 00;
    report.GD_MultiaxisControllerY = 00;
    report.BTN_MultiaxisControllerButton1 = 00;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);

    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    XCTAssert(stats.totalCount == 2,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 2, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypePointer] == 2, "kIOHIDEventTypePointer count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypePointer], events);
}

- (void) testMediaButtons {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDSimpleMediaButtonsDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDSimpleMediaButtonsDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    
    report.CD_ConsumerControlScanNextTrack = 1;
    report.CD_ConsumerControlScanPreviousTrack = 1;
    report.CD_ConsumerControlPlayPause = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    report.CD_ConsumerControlScanNextTrack = 0;
    report.CD_ConsumerControlScanPreviousTrack = 0;
    report.CD_ConsumerControlPlayPause = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);

    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    XCTAssert(stats.totalCount == 6,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 6, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeKeyboard] == 6, "kIOHIDEventTypeKeyboard count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeKeyboard], events);
}

- (void) testTelephonyButtons {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDTelephonyButtonsFlashNumpadDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDTelephonyButtonsFlashNumpadDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    
    report.TEL_PhoneFlash = 1;
    report.TEL_PhonePhoneMute = 1;
    report.TEL_PhonePhoneKey0 = 1;
    report.TEL_PhonePhoneKey9 = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    report.TEL_PhoneFlash = 0;
    report.TEL_PhonePhoneMute = 0;
    report.TEL_PhonePhoneKey0 = 0;
    report.TEL_PhonePhoneKey9 = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);

    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);

    XCTAssert(stats.totalCount == 8,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 8, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeKeyboard] == 8, "kIOHIDEventTypeKeyboard count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeKeyboard], events);
    
}

- (void) testDigitizerTouchCancel {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDDigitizerWithTouchCancel};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    
    [self setupTestSystem :  descriptorData];
    
    HIDDigitizerWithTouchCancelInputReport report;
    memset (&report, 0 , sizeof(report));
    
    
    report.DIG_TouchPadFingerTransducerIndex = 1;
    report.DIG_TouchPadFingerTouch = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);

    report.DIG_TouchPadFingerUntouch = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);

    report.DIG_TouchPadFingerTouch = 0;
    report.DIG_TouchPadFingerUntouch = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);
    
    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);
    
    XCTAssert(stats.totalCount == 3,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 3, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeDigitizer] == 3, "kIOHIDEventTypeDigitizer count:%lu events:%@", (unsigned long)stats.counts[kIOHIDEventTypeDigitizer], events);
    
    IOHIDEventRef event;
    CFIndex eventMask;

    event = (__bridge IOHIDEventRef) events[0];
    eventMask = IOHIDEventGetIntegerValue (event, kIOHIDEventFieldDigitizerEventMask);
    XCTAssert ((eventMask & kIOHIDDigitizerEventCancel) == 0);

    event = (__bridge IOHIDEventRef) events[1];
    eventMask = IOHIDEventGetIntegerValue (event, kIOHIDEventFieldDigitizerEventMask);
    XCTAssert ((eventMask & kIOHIDDigitizerEventCancel) == kIOHIDDigitizerEventCancel);

    event = (__bridge IOHIDEventRef) events[2];
    eventMask = IOHIDEventGetIntegerValue (event, kIOHIDEventFieldDigitizerEventMask);
    XCTAssert ((eventMask & kIOHIDDigitizerEventCancel) == 0);

}

- (void)MAC_OS_ONLY_TEST_CASE(testMouse) {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDMouseDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    [self setupTestSystem :  descriptorData];

    HIDMouseDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    report.GD_MousePointerX = 1;
    report.GD_MousePointerY = 1;
 
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);

    report.GD_MousePointerX = 0;
    report.GD_MousePointerY = 0;
    report.GD_MousePointerWheel = 1;
    
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);

    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);
    
    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);
    
    XCTAssert(stats.totalCount == 2,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 2, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypePointer] == 1 && stats.counts[kIOHIDEventTypeScroll] == 1, "Events:%@", events);

    
}

- (void)MAC_OS_ONLY_TEST_CASE(testAbsolutePointer) {
    
    IOReturn status;
    
    static uint8_t descriptor [] = {HIDPointerAbsoluteDescriptor};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
    [self setupTestSystem :  descriptorData];
    
    HIDPointerAbsoluteDescriptorInputReport report;
    memset (&report, 0 , sizeof(report));
    
    report.GD_MousePointerX = 1;
    report.GD_MousePointerY = 1;
    
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    report.GD_MousePointerX = 1000;
    report.GD_MousePointerY = 1000;
    
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];
    XCTAssert(status == kIOReturnSuccess, "handleReport:%x", status);
    
    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);
    
    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    HIDTestEventLatency(stats);
    
    XCTAssert(stats.totalCount == 2,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 2, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypePointer] == 2, "Events:%@", events);

    XCTAssert (IOHIDEventIsAbsolute((IOHIDEventRef)events[0]), "Events:%@", events);
    XCTAssert (IOHIDEventIsAbsolute((IOHIDEventRef)events[1]), "Events:%@", events);

}

- (void)testBiometricHumanPresenceAndProximity {

    IOReturn status;
    
    static uint8_t descriptor [] = {HIDBiometricHumanPresenceAndProximity};
    
    NSData * descriptorData = [[NSData alloc] initWithBytes:descriptor length:sizeof(descriptor)];
  
    [self setupTestSystem :  descriptorData];
    
    HIDBiometricHumanPresenceAndProximityInputReport report;
    memset (&report, 0 , sizeof(report));
    
    report.SNS_BiometricHumanPresenceDataFieldHumanPresence = 1;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];

    report.SNS_BiometricHumanPresenceDataFieldHumanPresence = 0;
    status = [self.sourceController handleReport:(uint8_t*)&report Length:sizeof(report) andInterval:2000];

    // Allow event to be dispatched
    usleep(kDefaultReportDispatchCompletionTime);
    
    
    NSArray *events = nil;
    @synchronized (self.eventController.events) {
        events = [self.eventController.events copy];
    }
    
    EVENTS_STATS stats =  [IOHIDEventSystemTestController getEventsStats:events];
    
    XCTAssert(stats.totalCount == 2,
              "events count:%lu expected:%d events:%@", (unsigned long)stats.totalCount , 2, events);
    
    XCTAssert (stats.counts[kIOHIDEventTypeBiometric] == 2, "Events:%@", events);

}

@end