TestIOHIDEventSerialization.mm   [plain text]


//
//  TestEventTree.m
//  IOHIDFamilyUnitTests
//
//  Created by AB on 2/27/18.
//

#import <XCTest/XCTest.h>
#include <IOKit/hid/IOHIDEvent.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include "IOHIDUnitTestUtility.h"

#include "TestIOHIDEventSerializationWrapper.h"

void OSDefineMetaClassAndStructors();
#define OSDeclareAbstractStructors( IOHIDEvent ) IOHIDEvent() : _data(NULL), _children(NULL), _parent(NULL), _capacity(0), _eventCount(0) {}
#define OSDefineMetaClassAndStructors(IOHIDEvent, OSObject) void OSDefineMetaClassAndStructors() {}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfour-char-constants"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
//dummy reference
#define GET_EVENT_DATA(event, field, value, options ) { if(field && options){ value = 0;}}
#define GET_EVENT_VALUE(event, field, value, options, typeToken) { if(field && options){ value = 0;}}
#define SET_EVENT_VALUE(event, field, value, options, typeToken)  { if(field && options){ value = 0;}}
#include "IOHIDEvent.h"
#include "IOHIDEvent.cpp"
#undef super
#pragma clang diagnostic pop

namespace base {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#pragma clang diagnostic ignored "-Wmacro-redefined"
#include "IOHIDEventDataBase.h"
#pragma clang diagnostic pop
}


@interface TestIOHIDEventSerialization : XCTestCase

@end

@implementation TestIOHIDEventSerialization

- (void)setUp {
    [super setUp];
    
}

- (void)tearDown {
    
    [super tearDown];
}

- (void)testEventTreeVendorChildMiddle {
    
    uint8_t payload [256] = {0xaa, 0x55, 0xaa, 0x55} ;
    //create keyboard event
    IOHIDEvent *keyboardEvent = IOHIDEvent::keyboardEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 1);
    HIDXCTAssertAndThrowTrue (keyboardEvent != NULL);
    size_t keyboardEventLength = keyboardEvent->getLength();
    
    //create vendor event
    IOHIDEvent *vendorEvent = IOHIDEvent::vendorDefinedEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 0, payload, sizeof(payload));
    HIDXCTAssertAndThrowTrue (vendorEvent != NULL);
    size_t vendorEventLength = (IOByteCount)vendorEvent->getLength();
    
    //create scroll event
    IOHIDEvent *scrollEvent = IOHIDEvent::scrollEvent(mach_absolute_time(), 2, 3, 1);
    HIDXCTAssertAndThrowTrue (scrollEvent != NULL);
    size_t scrollEventLength = scrollEvent->getLength();
    
    //create ambient light sensor event
    IOHIDEvent *ambientLightSensorEvent = IOHIDEvent::ambientLightSensorEvent(mach_absolute_time(), 1);
    HIDXCTAssertAndThrowTrue (ambientLightSensorEvent != NULL);
    size_t ambientLightSensorEventLength = ambientLightSensorEvent->getLength();
    
    //construct tree
    keyboardEvent->appendChild(scrollEvent);
    keyboardEvent->appendChild(vendorEvent);
    keyboardEvent->appendChild(ambientLightSensorEvent);
    
    //get total length
    size_t totalLength = keyboardEvent->getLength();
    
    
    XCTAssert((keyboardEventLength + vendorEventLength + scrollEventLength + ambientLightSensorEventLength - 3*sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    
    uint8_t *eventBytes = (uint8_t*)malloc(totalLength);
    size_t readBytes = keyboardEvent->readBytes(eventBytes, (IOByteCount)totalLength);
    
    XCTAssert((readBytes + sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    IOHIDEventRef parentEvent =  IOHIDEventCreateWithBytes(kCFAllocatorDefault, eventBytes, (CFIndex)totalLength);
    HIDXCTAssertAndThrowTrue (parentEvent != NULL);
    
    //verify user event
    CFArrayRef children =  IOHIDEventGetChildren(parentEvent);
    IOHIDEventRef childEvent = NULL;
    CFIndex value;
    
    XCTAssert(CFArrayGetCount(children) == 3);
    
    //verify parent event
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardUsagePage);
    XCTAssert (value == kHIDUsage_GD_Keyboard);
    
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardUsage);
    XCTAssert (value == kHIDUsage_KeyboardC);
    
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardDown);
    XCTAssert (value == 1);
    
    
    //verify left child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 0);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollX);
    XCTAssert (value == 2);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollY);
    XCTAssert (value == 3);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollZ);
    XCTAssert (value == 1);
    
    
    //verify middle child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 1);
    
    uint8_t *vendorData = IOHIDEventGetDataValue(childEvent, kIOHIDEventFieldVendorDefinedData);
    
    XCTAssert(memcmp(&payload, vendorData, sizeof(payload)) == 0);
    
    //verify right child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 2);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldAmbientLightSensorLevel);
    XCTAssert (value == 1);
    
    //reconstruct back kernel event
    IOHIDEvent *eventCpy = IOHIDEvent::withBytes(eventBytes, (IOByteCount)totalLength);
    
    HIDXCTAssertAndThrowTrue (eventCpy != NULL);
    
    OSArray *eventCpyChildren = eventCpy->getChildren();
    
    XCTAssert(eventCpyChildren != NULL);
    
    XCTAssert(eventCpyChildren->getCount() == 3);
    
    eventCpy->release();
    
    for (unsigned int index = 0; index < eventCpyChildren->getCount(); index++) {
        IOHIDEvent *eventCpyChild = (IOHIDEvent*)eventCpyChildren->getObject(index);
        XCTAssert(eventCpyChild != NULL);
        eventCpyChild->release();
    }
    
    
    free(eventBytes);
    vendorEvent->release();
    scrollEvent->release();
    ambientLightSensorEvent->release();
    keyboardEvent->release();
    CFRelease(parentEvent);
}

- (void)testEventTreeVendorChildLeft {
    
    uint8_t payload [256] = {0xaa, 0x55, 0xaa, 0x55} ;
    
    //create keyboard event
    IOHIDEvent *keyboardEvent = IOHIDEvent::keyboardEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 1);
    HIDXCTAssertAndThrowTrue (keyboardEvent != NULL);
    size_t keyboardEventLength = keyboardEvent->getLength();
    
    //create vendor event
    IOHIDEvent *vendorEvent = IOHIDEvent::vendorDefinedEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 0, payload, sizeof(payload));
    HIDXCTAssertAndThrowTrue (vendorEvent != NULL);
    size_t vendorEventLength = (IOByteCount)vendorEvent->getLength();
    
    //create scroll event
    IOHIDEvent *scrollEvent = IOHIDEvent::scrollEvent(mach_absolute_time(), 2, 3, 1);
    HIDXCTAssertAndThrowTrue (scrollEvent != NULL);
    size_t scrollEventLength = scrollEvent->getLength();
    
    //create ambient light sensor event
    IOHIDEvent *ambientLightSensorEvent = IOHIDEvent::ambientLightSensorEvent(mach_absolute_time(), 1);
    HIDXCTAssertAndThrowTrue (ambientLightSensorEvent != NULL);
    size_t ambientLightSensorEventLength = ambientLightSensorEvent->getLength();
    
    
    //construct tree
    keyboardEvent->appendChild(vendorEvent);
    keyboardEvent->appendChild(scrollEvent);
    keyboardEvent->appendChild(ambientLightSensorEvent);
    
    //get total length
    size_t totalLength = keyboardEvent->getLength();
    
    
    XCTAssert((keyboardEventLength + vendorEventLength + scrollEventLength + ambientLightSensorEventLength - 3*sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    
    uint8_t *eventBytes = (uint8_t*)malloc(totalLength);
    size_t readBytes = keyboardEvent->readBytes(eventBytes, (IOByteCount)totalLength);
    
    XCTAssert((readBytes + sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    IOHIDEventRef parentEvent =  IOHIDEventCreateWithBytes(kCFAllocatorDefault, eventBytes, (CFIndex)totalLength);
    HIDXCTAssertAndThrowTrue (parentEvent != NULL);
    
    //verify user event
    CFArrayRef children =  IOHIDEventGetChildren(parentEvent);
    IOHIDEventRef childEvent = NULL;
    CFIndex value;
    
    XCTAssert(CFArrayGetCount(children) == 3);
    
    //verify parent event
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardUsagePage);
    XCTAssert (value == kHIDUsage_GD_Keyboard);
    
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardUsage);
    XCTAssert (value == kHIDUsage_KeyboardC);
    
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardDown);
    XCTAssert (value == 1);
    
    
    //verify middle child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 1);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollX);
    XCTAssert (value == 2);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollY);
    XCTAssert (value == 3);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollZ);
    XCTAssert (value == 1);
    
    
    //verify left child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 0);
    
    uint8_t *vendorData = IOHIDEventGetDataValue(childEvent, kIOHIDEventFieldVendorDefinedData);
    
    XCTAssert(memcmp(&payload, vendorData, sizeof(payload)) == 0);
    
    //verify right child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 2);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldAmbientLightSensorLevel);
    XCTAssert (value == 1);
    
    //reconstruct back kernel event
    IOHIDEvent *eventCpy = IOHIDEvent::withBytes(eventBytes, (IOByteCount)totalLength);
    
    HIDXCTAssertAndThrowTrue (eventCpy != NULL);
    
    OSArray *eventCpyChildren = eventCpy->getChildren();
    
    XCTAssert(eventCpyChildren != NULL);
    
    XCTAssert(eventCpyChildren->getCount() == 3);
    
    eventCpy->release();
    
    for (unsigned int index = 0; index < eventCpyChildren->getCount(); index++) {
        IOHIDEvent *eventCpyChild = (IOHIDEvent*)eventCpyChildren->getObject(index);
        XCTAssert(eventCpyChild != NULL);
        eventCpyChild->release();
    }
    
    free(eventBytes);
    vendorEvent->release();
    scrollEvent->release();
    ambientLightSensorEvent->release();
    keyboardEvent->release();
    CFRelease(parentEvent);
    
}
- (void)testEventTreeVendorChildRight {
    
    uint8_t payload [256] = {0xaa, 0x55, 0xaa, 0x55} ;
    
    //create keyboard event
    IOHIDEvent *keyboardEvent = IOHIDEvent::keyboardEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 1);
    HIDXCTAssertAndThrowTrue (keyboardEvent != NULL);
    size_t keyboardEventLength = keyboardEvent->getLength();
    
    //create vendor event
    IOHIDEvent *vendorEvent = IOHIDEvent::vendorDefinedEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 0, payload, sizeof(payload));
    HIDXCTAssertAndThrowTrue (vendorEvent != NULL);
    size_t vendorEventLength = (IOByteCount)vendorEvent->getLength();
    
    //create scroll event
    IOHIDEvent *scrollEvent = IOHIDEvent::scrollEvent(mach_absolute_time(), 2, 3, 1);
    HIDXCTAssertAndThrowTrue (scrollEvent != NULL);
    size_t scrollEventLength = scrollEvent->getLength();
    
    //create ambient light sensor event
    IOHIDEvent *ambientLightSensorEvent = IOHIDEvent::ambientLightSensorEvent(mach_absolute_time(), 1);
    HIDXCTAssertAndThrowTrue (ambientLightSensorEvent != NULL);
    size_t ambientLightSensorEventLength = ambientLightSensorEvent->getLength();
    
    
    //construct tree
    keyboardEvent->appendChild(scrollEvent);
    keyboardEvent->appendChild(ambientLightSensorEvent);
    keyboardEvent->appendChild(vendorEvent);
    
    //get total length
    size_t totalLength = keyboardEvent->getLength();
    
    
    XCTAssert((keyboardEventLength + vendorEventLength + scrollEventLength + ambientLightSensorEventLength - 3*sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    
    uint8_t *eventBytes = (uint8_t*)malloc(totalLength);
    size_t readBytes = keyboardEvent->readBytes(eventBytes, (IOByteCount)totalLength);
    
    XCTAssert((readBytes + sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    IOHIDEventRef parentEvent =  IOHIDEventCreateWithBytes(kCFAllocatorDefault, eventBytes, (CFIndex)totalLength);
    HIDXCTAssertAndThrowTrue (parentEvent != NULL);
    
    //verify user event
    CFArrayRef children =  IOHIDEventGetChildren(parentEvent);
    IOHIDEventRef childEvent = NULL;
    CFIndex value;
    
    XCTAssert(CFArrayGetCount(children) == 3);
    
    //verify parent event
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardUsagePage);
    XCTAssert (value == kHIDUsage_GD_Keyboard);
    
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardUsage);
    XCTAssert (value == kHIDUsage_KeyboardC);
    
    value = IOHIDEventGetIntegerValue (parentEvent, kIOHIDEventFieldKeyboardDown);
    XCTAssert (value == 1);
    
    
    //verify left child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 0);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollX);
    XCTAssert (value == 2);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollY);
    XCTAssert (value == 3);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollZ);
    XCTAssert (value == 1);
    
    
    //verify right child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 2);
    
    uint8_t *vendorData = IOHIDEventGetDataValue(childEvent, kIOHIDEventFieldVendorDefinedData);
    
    XCTAssert(memcmp(&payload, vendorData, sizeof(payload)) == 0);
    
    //verify middle child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 1);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldAmbientLightSensorLevel);
    XCTAssert (value == 1);
    
    //reconstruct back kernel event
    IOHIDEvent *eventCpy = IOHIDEvent::withBytes(eventBytes, (IOByteCount)totalLength);
    
    HIDXCTAssertAndThrowTrue (eventCpy != NULL);
    
    OSArray *eventCpyChildren = eventCpy->getChildren();
    
    XCTAssert(eventCpyChildren != NULL);
    
    XCTAssert(eventCpyChildren->getCount() == 3);
    
    eventCpy->release();
    
    for (unsigned int index = 0; index < eventCpyChildren->getCount(); index++) {
        IOHIDEvent *eventCpyChild = (IOHIDEvent*)eventCpyChildren->getObject(index);
        XCTAssert(eventCpyChild != NULL);
        eventCpyChild->release();
    }
    
    free(eventBytes);
    vendorEvent->release();
    scrollEvent->release();
    ambientLightSensorEvent->release();
    keyboardEvent->release();
    CFRelease(parentEvent);
}
- (void)testEventTreeVendorParent {
    
    uint8_t payload [256] = {0xaa, 0x55, 0xaa, 0x55} ;
    //create keyboard event
    IOHIDEvent *keyboardEvent = IOHIDEvent::keyboardEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 1);
    HIDXCTAssertAndThrowTrue (keyboardEvent != NULL);
    size_t keyboardEventLength = keyboardEvent->getLength();
    
    //create vendor event
    IOHIDEvent *vendorEvent = IOHIDEvent::vendorDefinedEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 0, payload, sizeof(payload));
    HIDXCTAssertAndThrowTrue (vendorEvent != NULL);
    size_t vendorEventLength = (IOByteCount)vendorEvent->getLength();
    
    //create scroll event
    IOHIDEvent *scrollEvent = IOHIDEvent::scrollEvent(mach_absolute_time(), 2, 3, 1);
    HIDXCTAssertAndThrowTrue (scrollEvent != NULL);
    size_t scrollEventLength = scrollEvent->getLength();
    
    //create ambient light sensor event
    IOHIDEvent *ambientLightSensorEvent = IOHIDEvent::ambientLightSensorEvent(mach_absolute_time(), 1);
    HIDXCTAssertAndThrowTrue (ambientLightSensorEvent != NULL);
    size_t ambientLightSensorEventLength = ambientLightSensorEvent->getLength();
    
    //construct tree
    vendorEvent->appendChild(keyboardEvent);
    vendorEvent->appendChild(scrollEvent);
    vendorEvent->appendChild(ambientLightSensorEvent);
    
    //get total length
    size_t totalLength = vendorEvent->getLength();
    
    
    XCTAssert((keyboardEventLength + vendorEventLength + scrollEventLength + ambientLightSensorEventLength - 3*sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    
    uint8_t *eventBytes = (uint8_t*)malloc(totalLength);
    size_t readBytes = vendorEvent->readBytes(eventBytes, (IOByteCount)totalLength);
    
    XCTAssert((readBytes + sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    IOHIDEventRef parentEvent =  IOHIDEventCreateWithBytes(kCFAllocatorDefault, eventBytes, (CFIndex)totalLength);
    HIDXCTAssertAndThrowTrue (parentEvent != NULL);
    
    //verify user event
    CFArrayRef children =  IOHIDEventGetChildren(parentEvent);
    IOHIDEventRef childEvent = NULL;
    CFIndex value;
    
    XCTAssert(CFArrayGetCount(children) == 3);
    
    //verify parent event
    uint8_t *vendorData = IOHIDEventGetDataValue(parentEvent, kIOHIDEventFieldVendorDefinedData);
    
    XCTAssert(memcmp(&payload, vendorData, sizeof(payload)) == 0);
    
    //verify left child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 0);
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldKeyboardUsagePage);
    XCTAssert (value == kHIDUsage_GD_Keyboard);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldKeyboardUsage);
    XCTAssert (value == kHIDUsage_KeyboardC);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldKeyboardDown);
    XCTAssert (value == 1);
    
    
    //verify middle child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 1);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollX);
    XCTAssert (value == 2);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollY);
    XCTAssert (value == 3);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldScrollZ);
    XCTAssert (value == 1);
    
    
    //verify right child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 2);
    
    value = IOHIDEventGetIntegerValue (childEvent, kIOHIDEventFieldAmbientLightSensorLevel);
    XCTAssert (value == 1);
    
    //reconstruct back kernel event
    IOHIDEvent *eventCpy = IOHIDEvent::withBytes(eventBytes, (IOByteCount)totalLength);
    
    HIDXCTAssertAndThrowTrue (eventCpy != NULL);
    
    OSArray *eventCpyChildren = eventCpy->getChildren();
    
    XCTAssert(eventCpyChildren != NULL);
    
    XCTAssert(eventCpyChildren->getCount() == 3);
    
    eventCpy->release();
    
    for (unsigned int index = 0; index < eventCpyChildren->getCount(); index++) {
        IOHIDEvent *eventCpyChild = (IOHIDEvent*)eventCpyChildren->getObject(index);
        XCTAssert(eventCpyChild != NULL);
        eventCpyChild->release();
    }
    
    free(eventBytes);
    vendorEvent->release();
    scrollEvent->release();
    ambientLightSensorEvent->release();
    keyboardEvent->release();
    CFRelease(parentEvent);
}
- (void)testEventTreeAllVendor {
    
    uint8_t payloadVendorParent [256] = {0xaa, 0x55, 0xaa, 0x55} ;
    uint8_t payloadVendorLeftChild [256] = {0x11, 0x55, 0x11, 0x55} ;
    uint8_t payloadVendorMiddleChild [256] = {0xbb, 0x45, 0xbb, 0x45} ;
    uint8_t payloadVendorRightChild [256] = {0xcc, 0x52, 0xcc, 0x52} ;
    
    
    //create vendor event parent
    IOHIDEvent *vendorEventParent = IOHIDEvent::vendorDefinedEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 0, payloadVendorParent, sizeof(payloadVendorParent));
    HIDXCTAssertAndThrowTrue (vendorEventParent != NULL);
    size_t vendorEventParentLength = (IOByteCount)vendorEventParent->getLength();
    
    //create vendor event left child
    IOHIDEvent *vendorEventLeftChild = IOHIDEvent::vendorDefinedEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 0, payloadVendorLeftChild, sizeof(payloadVendorLeftChild));
    HIDXCTAssertAndThrowTrue (vendorEventLeftChild != NULL);
    size_t vendorEventLeftChildLength = (IOByteCount)vendorEventLeftChild->getLength();
    
    //create vendor event middle child
    IOHIDEvent *vendorEventMiddleChild = IOHIDEvent::vendorDefinedEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 0, payloadVendorMiddleChild, sizeof(payloadVendorMiddleChild));
    HIDXCTAssertAndThrowTrue (vendorEventMiddleChild != NULL);
    size_t vendorEventMiddleChildLength = (IOByteCount)vendorEventMiddleChild->getLength();
    
    //create vendor event right child
    IOHIDEvent *vendorEventRightChild = IOHIDEvent::vendorDefinedEvent(mach_absolute_time(), kHIDUsage_GD_Keyboard, kHIDUsage_KeyboardC, 0, payloadVendorRightChild, sizeof(payloadVendorRightChild));
    HIDXCTAssertAndThrowTrue (vendorEventRightChild != NULL);
    size_t vendorEventRightChildLength = (IOByteCount)vendorEventRightChild->getLength();
    
    //construct tree
    vendorEventParent->appendChild(vendorEventLeftChild);
    vendorEventParent->appendChild(vendorEventMiddleChild);
    vendorEventParent->appendChild(vendorEventRightChild);
    
    //get total length
    size_t totalLength = vendorEventParent->getLength();
    
    
    XCTAssert((vendorEventParentLength + vendorEventLeftChildLength + vendorEventMiddleChildLength + vendorEventRightChildLength - 3*sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    
    uint8_t *eventBytes = (uint8_t*)malloc(totalLength);
    size_t readBytes = vendorEventParent->readBytes(eventBytes, (IOByteCount)totalLength);
    
    XCTAssert((readBytes + sizeof(base::IOHIDSystemQueueElement)) == totalLength);
    
    IOHIDEventRef parentEvent =  IOHIDEventCreateWithBytes(kCFAllocatorDefault, eventBytes, (CFIndex)totalLength);
    HIDXCTAssertAndThrowTrue (parentEvent != NULL);
    
    //verify user event
    CFArrayRef children =  IOHIDEventGetChildren(parentEvent);
    IOHIDEventRef childEvent = NULL;
    
    XCTAssert(CFArrayGetCount(children) == 3);
    
    //verify parent event
    uint8_t *vendorDataParent = IOHIDEventGetDataValue(parentEvent, kIOHIDEventFieldVendorDefinedData);
    
    XCTAssert(memcmp(&payloadVendorParent, vendorDataParent, sizeof(payloadVendorParent)) == 0);
    
    //verify left child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 0);
    
    uint8_t *vendorDataLeftChild = IOHIDEventGetDataValue(childEvent, kIOHIDEventFieldVendorDefinedData);
    
    XCTAssert(memcmp(&payloadVendorLeftChild, vendorDataLeftChild, sizeof(payloadVendorLeftChild)) == 0);
    
    //verify middle child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 1);
    
    uint8_t *vendorDataMiddleChild = IOHIDEventGetDataValue(childEvent, kIOHIDEventFieldVendorDefinedData);
    
    XCTAssert(memcmp(&payloadVendorMiddleChild, vendorDataMiddleChild, sizeof(payloadVendorMiddleChild)) == 0);
    
    //verify right child
    childEvent = (IOHIDEventRef)CFArrayGetValueAtIndex(children, 2);
    
    uint8_t *vendorDataRightChild = IOHIDEventGetDataValue(childEvent, kIOHIDEventFieldVendorDefinedData);
    
    XCTAssert(memcmp(&payloadVendorRightChild, vendorDataRightChild, sizeof(payloadVendorRightChild)) == 0);
    
    //reconstruct back kernel event
    IOHIDEvent *eventCpy = IOHIDEvent::withBytes(eventBytes, (IOByteCount)totalLength);
    
    HIDXCTAssertAndThrowTrue (eventCpy != NULL);
    
    OSArray *eventCpyChildren = eventCpy->getChildren();
    
    XCTAssert(eventCpyChildren != NULL);
    
    XCTAssert(eventCpyChildren->getCount() == 3);
    
    eventCpy->release();
    
    for (unsigned int index = 0; index < eventCpyChildren->getCount(); index++) {
        IOHIDEvent *eventCpyChild = (IOHIDEvent*)eventCpyChildren->getObject(index);
        XCTAssert(eventCpyChild != NULL);
        eventCpyChild->release();
    }
    
    free(eventBytes);
    vendorEventParent->release();
    vendorEventLeftChild->release();
    vendorEventMiddleChild->release();
    vendorEventRightChild->release();
    CFRelease(parentEvent);
    
}
@end