diff --git a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm index 080f648b61772d..32d9c97be8b776 100644 --- a/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm +++ b/src/darwin/Framework/CHIP/CHIPAttributeCacheContainer.mm @@ -21,6 +21,7 @@ #import "CHIPDeviceControllerOverXPC+AttributeCache.h" #import "CHIPDevice_Internal.h" #import "CHIPError.h" +#import "CHIPError_Internal.h" #import "CHIPLogging.h" #include @@ -152,30 +153,20 @@ static CHIP_ERROR AppendAttibuteValueToArray( if (err == CHIP_NO_ERROR) { id obj = NSObjectFromCHIPTLV(&reader); if (obj) { - [array addObject:@{ - @"endpointId" : [NSNumber numberWithUnsignedShort:path.mEndpointId], - @"clusterId" : [NSNumber numberWithUnsignedLong:path.mClusterId], - @"attributeId" : [NSNumber numberWithUnsignedLong:path.mAttributeId], - @"status" : @0, - @"data" : obj - }]; + [array addObject:@ { kCHIPAttributePathKey : [[CHIPAttributePath alloc] initWithPath:path], kCHIPDataKey : obj }]; return CHIP_NO_ERROR; } CHIP_LOG_ERROR("Error: Cached value could not be converted to generic NSObject"); [array addObject:@ { - @"endpointId" : [NSNumber numberWithUnsignedShort:path.mEndpointId], - @"clusterId" : [NSNumber numberWithUnsignedLong:path.mClusterId], - @"attributeId" : [NSNumber numberWithUnsignedLong:path.mAttributeId], - @"status" : [NSNumber numberWithInteger:CHIP_ERROR_DECODE_FAILED.AsInteger()] + kCHIPAttributePathKey : [[CHIPAttributePath alloc] initWithPath:path], + kCHIPErrorKey : [CHIPError errorForCHIPErrorCode:CHIP_ERROR_DECODE_FAILED] }]; return CHIP_ERROR_DECODE_FAILED; } CHIP_LOG_ERROR("Error: Failed to read from attribute cache: %s", err.AsString()); [array addObject:@ { - @"endpointId" : [NSNumber numberWithUnsignedShort:path.mEndpointId], - @"clusterId" : [NSNumber numberWithUnsignedLong:path.mClusterId], - @"attributeId" : [NSNumber numberWithUnsignedLong:path.mAttributeId], - @"status" : [NSNumber numberWithInteger:err.AsInteger()] + kCHIPAttributePathKey : [[CHIPAttributePath alloc] initWithPath:path], + kCHIPErrorKey : [CHIPError errorForCHIPErrorCode:err] }]; return err; } diff --git a/src/darwin/Framework/CHIP/CHIPDevice.h b/src/darwin/Framework/CHIP/CHIPDevice.h index 2e28c868752799..74ba8bbf67bded 100644 --- a/src/darwin/Framework/CHIP/CHIPDevice.h +++ b/src/darwin/Framework/CHIP/CHIPDevice.h @@ -24,28 +24,72 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Handler for read attribute response, write attribute response, invoke command response and reports. + * + * Handler will receive either values or error. Either one of the parameters will be nil. + * + * @param values Received values are an NSArray object with response-value element as described below. + * + * A response-value is an NSDictionary object with the following key values: + * + * kCHIPAttributePathKey : CHIPAttributePath object. Included for attribute value. + * kCHIPCommandPathKey : CHIPCommandPath object. Included for command response. + * kCHIPErrorKey : NSError object. Included to indicate an error. + * kCHIPDataKey: Data-value NSDictionary object. + * Included when there is data and when there is no error. + * The data-value is described below. + * + * A data-value is an NSDictionary object with the following key values: + * + * kCHIPTypeKey : data type. kCHIPSignedIntegerValueType, kCHIPUnsignedIntegerValueType, kCHIPBooleanValueType, + * kCHIPUTF8StringValueType, kCHIPOctetStringValueType, kCHIPFloatValueType, kCHIPDoubleValueType, + * kCHIPNullValueType, kCHIPStructureValueType or kCHIPArrayValueType. + * + * kCHIPValueKey : data value. Per each data type, data value shall be the following object: + * + * kCHIPSignedIntegerValueType: NSNumber object. + * kCHIPUnsignedIntegerValueType: NSNumber object. + * kCHIPBooleanValueType: NSNumber object. + * kCHIPUTF8StringValueType: NSString object. + * kCHIPOctetStringValueType: NSData object. + * kCHIPFloatValueType: NSNumber object. + * kCHIPDoubleValueType: NSNumber object. + * kCHIPNullValueType: "value" key will not be included. + * kCHIPStructureValueType: structure-value NSArray object. + * See below for the definition of structure-value. + * kCHIPArrayValueType: Array-value NSArray object. See below for the definition of array-value. + * + * A structure-value is an NSArray object with NSDictionary objects as its elements. Each dictionary element will + * contain the following key values. + * + * kCHIPContextTagKey : NSNumber object as context tag. + * kCHIPDataKey : Data-value NSDictionary object. + * + * An array-value is an NSArray object with NSDictionary objects as its elements. Each dictionary element will + * contain the following key values. + * + * kCHIPDataKey : Data-value NSDictionary object. + */ typedef void (^CHIPDeviceResponseHandler)(NSArray *> * _Nullable values, NSError * _Nullable error); +extern NSString * const kCHIPAttributePathKey; +extern NSString * const kCHIPCommandPathKey; +extern NSString * const kCHIPDataKey; +extern NSString * const kCHIPErrorKey; extern NSString * const kCHIPTypeKey; extern NSString * const kCHIPValueKey; -extern NSString * const kCHIPTagKey; -extern NSString * const kCHIPSignedIntegerValueTypeKey; -extern NSString * const kCHIPUnsignedIntegerValueTypeKey; -extern NSString * const kCHIPBooleanValueTypeKey; -extern NSString * const kCHIPUTF8StringValueTypeKey; -extern NSString * const kCHIPOctetStringValueTypeKey; -extern NSString * const kCHIPFloatValueTypeKey; -extern NSString * const kCHIPDoubleValueTypeKey; -extern NSString * const kCHIPNullValueTypeKey; -extern NSString * const kCHIPStructureValueTypeKey; -extern NSString * const kCHIPArrayValueTypeKey; -extern NSString * const kCHIPListValueTypeKey; -extern NSString * const kCHIPEndpointIdKey; -extern NSString * const kCHIPClusterIdKey; -extern NSString * const kCHIPAttributeIdKey; -extern NSString * const kCHIPCommandIdKey; -extern NSString * const kCHIPDataKey; -extern NSString * const kCHIPStatusKey; +extern NSString * const kCHIPContextTagKey; +extern NSString * const kCHIPSignedIntegerValueType; +extern NSString * const kCHIPUnsignedIntegerValueType; +extern NSString * const kCHIPBooleanValueType; +extern NSString * const kCHIPUTF8StringValueType; +extern NSString * const kCHIPOctetStringValueType; +extern NSString * const kCHIPFloatValueType; +extern NSString * const kCHIPDoubleValueType; +extern NSString * const kCHIPNullValueType; +extern NSString * const kCHIPStructureValueType; +extern NSString * const kCHIPArrayValueType; @interface CHIPDevice : NSObject @@ -81,17 +125,6 @@ extern NSString * const kCHIPStatusKey; /** * Read attribute in a designated attribute path - * - * @param completion response handler will receive either value or error. value will be an NSArray object with NSDictionary - * elements. Each NSDictionary will have "endpointId", "clusterId", "attributeId", "status" and "data" keys. "endpointId", - * "clusterId", "attributeId" and "status" will be mapped to NSNumber objects. "status" with 0 value indicates success and non-zero - * value indicates failure. "data" key is present only when "status" value is 0. "data" key will be mapped to an NSDictionary - * object, representing attribute value of the path. NSDictionary representing attribute value will contain "type" and "value" keys. - * "type" will be mapped to "SignedInteger", "UnsignedInteger", "UTF8String", "OctetString", "Float", - * "Double", "Boolean", "Null", "Structure", "Array" or "List. "value" will be mapped to an NSNumber, NSString, nil or NSArray - * instance. When "type" is "OctetStriing", "value" will be an NSData object. When "type" is "Structure", "Array" or "List", "value" - * will be NSArray with NSDictionary elements. Each NSDictionary element will have "tag" and "value" keys. "tag" will be mapped to - * an NSNumber value. "value" will be mapped to an NSDictionary instance representing any attribute value recursively. */ - (void)readAttributeWithEndpointId:(NSUInteger)endpointId clusterId:(NSUInteger)clusterId @@ -102,10 +135,8 @@ extern NSString * const kCHIPStatusKey; /** * Write to attribute in a designated attribute path * - * @param completion response handler will receive either value or error. value will be an NSArray object with NSDictionary - * elements. Each NSDictionary will have "endpointId", "clusterId", "attributeId" and "status" keys. "endpointId", "clusterId", - * "attributeId" and "status" will be mapped to NSNumber objects. "status" with 0 value indicates success and non-zero value - * indicates failure. + * @param value A data-value NSDictionary object as described in + * CHIPDeviceResponseHandler. */ - (void)writeAttributeWithEndpointId:(NSUInteger)endpointId clusterId:(NSUInteger)clusterId @@ -117,16 +148,10 @@ extern NSString * const kCHIPStatusKey; /** * Invoke a command with a designated command path * - * @param commandFields command fields object. The object must be an NSDictionary object representing attribute value - * as described in the readAttributeWithEndpointId:clusterId:attributeId:clientQueue:responseHandler: method. - * The attribute must be a Structure, i.e., the NSDictionary "type" key must have the value "Structure". - * - * @param completion response handler will receive either value or error. value will be an NSArray object with NSDictionary - * elements. Each NSDictionary will have "endpointId", "clusterId", "commandId", "status" and "responseData" keys. "endpointId", - * "clusterId", "attributeId" and "status" will be mapped to NSNumber objects. "status" with 0 value indicates success and non-zero - * value indicates failure. "responseData" key will be included only when "status" key has 0 value and there is response data for - * the command. "responseData" key value will be an NSDictionary object representing attribute value as described in the - * readAttributeWithEndpointId:clusterId:attributeId:clientQueue:responseHandler: method. + * @param commandFields command fields object. The object must be a data-value NSDictionary object + * as described in the CHIPDeviceResponseHandler. + * The attribute must be a Structure, i.e., + * the NSDictionary kCHIPTypeKey key must have the value kCHIPStructureValueType. */ - (void)invokeCommandWithEndpointId:(NSUInteger)endpointId clusterId:(NSUInteger)clusterId @@ -137,12 +162,6 @@ extern NSString * const kCHIPStatusKey; /** * Subscribe an attribute in a designated attribute path - * - * @param reportHandler handler for the reports. Note that only the report handler by the last call to this method per the same - * attribute path will receive reports. Report handler will receive either value or error. value will be an NSDictionary object. The - * NSDictionary object will have "endpointId", "clusterId", "attributeId" and "value" keys. "endpointId", "clusterId" and - * "attributeId" will be mapped to NSNumber objects. "value" key value will be an NSDictionary object representing attribute value - * as described in the readAttributeWithEndpointId:clusterId:attributeId:clientQueue:responseHandler: method. */ - (void)subscribeAttributeWithEndpointId:(NSUInteger)endpointId clusterId:(NSUInteger)clusterId @@ -150,8 +169,7 @@ extern NSString * const kCHIPStatusKey; minInterval:(NSUInteger)minInterval maxInterval:(NSUInteger)maxInterval clientQueue:(dispatch_queue_t)clientQueue - reportHandler:(void (^)(NSDictionary * _Nullable value, - NSError * _Nullable error))reportHandler + reportHandler:(CHIPDeviceResponseHandler)reportHandler subscriptionEstablished:(nullable void (^)(void))subscriptionEstablishedHandler; /** @@ -170,6 +188,21 @@ extern NSString * const kCHIPStatusKey; @property (nonatomic, readonly, strong, nonnull) NSNumber * cluster; @property (nonatomic, readonly, strong, nonnull) NSNumber * attribute; ++ (instancetype)attributePathWithEndpointId:(NSNumber *)endpoint + clusterId:(NSNumber *)clusterId + attributeId:(NSNumber *)attributeId; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +@end + +@interface CHIPCommandPath : NSObject +@property (nonatomic, readonly, strong, nonnull) NSNumber * endpoint; +@property (nonatomic, readonly, strong, nonnull) NSNumber * cluster; +@property (nonatomic, readonly, strong, nonnull) NSNumber * command; + ++ (instancetype)commandPathWithEndpointId:(NSNumber *)endpoint clusterId:(NSNumber *)clusterId commandId:(NSNumber *)commandId; + - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; @end diff --git a/src/darwin/Framework/CHIP/CHIPDevice.mm b/src/darwin/Framework/CHIP/CHIPDevice.mm index b123dc62a9f952..614a0f3235e20f 100644 --- a/src/darwin/Framework/CHIP/CHIPDevice.mm +++ b/src/darwin/Framework/CHIP/CHIPDevice.mm @@ -20,7 +20,10 @@ #import "CHIPDevice_Internal.h" #import "CHIPError_Internal.h" #import "CHIPLogging.h" +#include "app/ConcreteAttributePath.h" +#include "app/ConcreteCommandPath.h" #include "lib/core/CHIPError.h" +#include "lib/core/DataModelTypes.h" #include #include @@ -38,28 +41,25 @@ using namespace chip::app; using namespace chip::Protocols::InteractionModel; +NSString * const kCHIPAttributePathKey = @"attributePath"; +NSString * const kCHIPCommandPathKey = @"commandPath"; +NSString * const kCHIPDataKey = @"data"; +NSString * const kCHIPErrorKey = @"error"; NSString * const kCHIPTypeKey = @"type"; NSString * const kCHIPValueKey = @"value"; -NSString * const kCHIPTagKey = @"tag"; -NSString * const kCHIPSignedIntegerValueTypeKey = @"SignedInteger"; -NSString * const kCHIPUnsignedIntegerValueTypeKey = @"UnsignedInteger"; -NSString * const kCHIPBooleanValueTypeKey = @"Boolean"; -NSString * const kCHIPUTF8StringValueTypeKey = @"UTF8String"; -NSString * const kCHIPOctetStringValueTypeKey = @"OctetString"; -NSString * const kCHIPFloatValueTypeKey = @"Float"; -NSString * const kCHIPDoubleValueTypeKey = @"Double"; -NSString * const kCHIPNullValueTypeKey = @"Null"; -NSString * const kCHIPStructureValueTypeKey = @"Structure"; -NSString * const kCHIPArrayValueTypeKey = @"Array"; -NSString * const kCHIPListValueTypeKey = @"List"; -NSString * const kCHIPEndpointIdKey = @"endpointId"; -NSString * const kCHIPClusterIdKey = @"clusterId"; -NSString * const kCHIPAttributeIdKey = @"attributeId"; -NSString * const kCHIPCommandIdKey = @"commandId"; -NSString * const kCHIPDataKey = @"data"; -NSString * const kCHIPStatusKey = @"status"; - -class NSObjectAttributeCallbackBridge; +NSString * const kCHIPContextTagKey = @"contextTag"; +NSString * const kCHIPSignedIntegerValueType = @"SignedInteger"; +NSString * const kCHIPUnsignedIntegerValueType = @"UnsignedInteger"; +NSString * const kCHIPBooleanValueType = @"Boolean"; +NSString * const kCHIPUTF8StringValueType = @"UTF8String"; +NSString * const kCHIPOctetStringValueType = @"OctetString"; +NSString * const kCHIPFloatValueType = @"Float"; +NSString * const kCHIPDoubleValueType = @"Double"; +NSString * const kCHIPNullValueType = @"Null"; +NSString * const kCHIPStructureValueType = @"Structure"; +NSString * const kCHIPArrayValueType = @"Array"; + +class NSObjectDataValueCallbackBridge; @interface CHIPDevice () @@ -69,10 +69,6 @@ @interface CHIPDevice () @end -@interface CHIPAttributePath () -- (instancetype)initWithPath:(const ConcreteDataAttributePath &)path; -@end - @interface CHIPAttributeReport () - (instancetype)initWithPath:(const ConcreteDataAttributePath &)path value:(nullable id)value error:(nullable NSError *)error; @end @@ -124,11 +120,11 @@ static void PurgeReadClientContainers(uint64_t deviceId, dispatch_queue_t queue, dispatch_async(DeviceLayer::PlatformMgrImpl().GetWorkQueue(), ^{ for (CHIPReadClientContainer * container in listToDelete) { if (container.readClientPtr) { - delete container.readClientPtr; + Platform::Delete(container.readClientPtr); container.readClientPtr = nullptr; } if (container.pathParams) { - delete container.pathParams; + Platform::Delete(container.pathParams); container.pathParams = nullptr; } } @@ -157,15 +153,43 @@ static void PurgeCompletedReadClientContainers(uint64_t deviceId) [readClientContainersLock unlock]; } +#ifdef DEBUG +// This function is for unit testing only. This function closes all read clients. +static void CauseReadClientFailure(uint64_t deviceId, dispatch_queue_t queue, void (^_Nullable completion)(void)) +{ + InitializeReadClientContainers(); + + NSMutableArray * listToFail; + NSNumber * key = [NSNumber numberWithUnsignedLongLong:deviceId]; + [readClientContainersLock lock]; + listToFail = readClientContainers[key]; + [readClientContainers removeObjectForKey:key]; + [readClientContainersLock unlock]; + + dispatch_async(DeviceLayer::PlatformMgrImpl().GetWorkQueue(), ^{ + for (CHIPReadClientContainer * container in listToFail) { + // Send auto resubscribe request again by read clients, which must fail. + chip::app::ReadPrepareParams readParams; + if (container.readClientPtr) { + container.readClientPtr->SendAutoResubscribeRequest(std::move(readParams)); + } + } + if (completion) { + dispatch_async(queue, completion); + } + }); +} +#endif + @implementation CHIPReadClientContainer - (void)onDone { if (_readClientPtr) { - delete _readClientPtr; + Platform::Delete(_readClientPtr); _readClientPtr = nullptr; } if (_pathParams) { - delete _pathParams; + Platform::Delete(_pathParams); _pathParams = nullptr; } PurgeCompletedReadClientContainers(_deviceId); @@ -174,11 +198,11 @@ - (void)onDone - (void)dealloc { if (_readClientPtr) { - delete _readClientPtr; + Platform::Delete(_readClientPtr); _readClientPtr = nullptr; } if (_pathParams) { - delete _pathParams; + Platform::Delete(_pathParams); _pathParams = nullptr; } } @@ -337,7 +361,7 @@ id _Nullable NSObjectFromCHIPTLV(chip::TLV::TLVReader * data) CHIP_LOG_ERROR("Error(%s): TLV signed integer decoding failed", chip::ErrorStr(err)); return nil; } - return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPSignedIntegerValueTypeKey, kCHIPTypeKey, + return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPSignedIntegerValueType, kCHIPTypeKey, [NSNumber numberWithLongLong:val], kCHIPValueKey, nil]; } case chip::TLV::kTLVType_UnsignedInteger: { @@ -347,7 +371,7 @@ id _Nullable NSObjectFromCHIPTLV(chip::TLV::TLVReader * data) CHIP_LOG_ERROR("Error(%s): TLV unsigned integer decoding failed", chip::ErrorStr(err)); return nil; } - return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPUnsignedIntegerValueTypeKey, kCHIPTypeKey, + return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPUnsignedIntegerValueType, kCHIPTypeKey, [NSNumber numberWithUnsignedLongLong:val], kCHIPValueKey, nil]; } case chip::TLV::kTLVType_Boolean: { @@ -358,27 +382,34 @@ id _Nullable NSObjectFromCHIPTLV(chip::TLV::TLVReader * data) return nil; } return [NSDictionary - dictionaryWithObjectsAndKeys:kCHIPBooleanValueTypeKey, kCHIPTypeKey, [NSNumber numberWithBool:val], kCHIPValueKey, nil]; + dictionaryWithObjectsAndKeys:kCHIPBooleanValueType, kCHIPTypeKey, [NSNumber numberWithBool:val], kCHIPValueKey, nil]; } case chip::TLV::kTLVType_FloatingPointNumber: { + // Try float first + float floatValue; + CHIP_ERROR err = data->Get(floatValue); + if (err == CHIP_NO_ERROR) { + return @ { kCHIPTypeKey : kCHIPFloatValueType, kCHIPValueKey : [NSNumber numberWithFloat:floatValue] }; + } double val; - CHIP_ERROR err = data->Get(val); + err = data->Get(val); if (err != CHIP_NO_ERROR) { CHIP_LOG_ERROR("Error(%s): TLV floating point decoding failed", chip::ErrorStr(err)); return nil; } - return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPDoubleValueTypeKey, kCHIPTypeKey, [NSNumber numberWithDouble:val], - kCHIPValueKey, nil]; + return [NSDictionary + dictionaryWithObjectsAndKeys:kCHIPDoubleValueType, kCHIPTypeKey, [NSNumber numberWithDouble:val], kCHIPValueKey, nil]; } case chip::TLV::kTLVType_UTF8String: { + uint32_t len = data->GetLength(); const uint8_t * ptr; CHIP_ERROR err = data->GetDataPtr(ptr); if (err != CHIP_NO_ERROR) { CHIP_LOG_ERROR("Error(%s): TLV UTF8String decoding failed", chip::ErrorStr(err)); return nil; } - return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPUTF8StringValueTypeKey, kCHIPTypeKey, - [NSString stringWithUTF8String:(const char *) ptr], kCHIPValueKey, nil]; + return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPUTF8StringValueType, kCHIPTypeKey, + [[NSString alloc] initWithBytes:ptr length:len encoding:NSUTF8StringEncoding], kCHIPValueKey, nil]; } case chip::TLV::kTLVType_ByteString: { uint32_t len = data->GetLength(); @@ -388,25 +419,21 @@ id _Nullable NSObjectFromCHIPTLV(chip::TLV::TLVReader * data) CHIP_LOG_ERROR("Error(%s): TLV ByteString decoding failed", chip::ErrorStr(err)); return nil; } - return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPOctetStringValueTypeKey, kCHIPTypeKey, + return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPOctetStringValueType, kCHIPTypeKey, [NSData dataWithBytes:ptr length:len], kCHIPValueKey, nil]; } case chip::TLV::kTLVType_Null: { - return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPNullValueTypeKey, kCHIPTypeKey, nil]; + return [NSDictionary dictionaryWithObjectsAndKeys:kCHIPNullValueType, kCHIPTypeKey, nil]; } case chip::TLV::kTLVType_Structure: - case chip::TLV::kTLVType_Array: - case chip::TLV::kTLVType_List: { + case chip::TLV::kTLVType_Array: { NSString * typeName; switch (dataTLVType) { case chip::TLV::kTLVType_Structure: - typeName = kCHIPStructureValueTypeKey; + typeName = kCHIPStructureValueType; break; case chip::TLV::kTLVType_Array: - typeName = kCHIPArrayValueTypeKey; - break; - case chip::TLV::kTLVType_List: - typeName = kCHIPListValueTypeKey; + typeName = kCHIPArrayValueType; break; default: typeName = @"Unsupported"; @@ -426,8 +453,12 @@ id _Nullable NSObjectFromCHIPTLV(chip::TLV::TLVReader * data) CHIP_LOG_ERROR("Error when decoding TLV container"); return nil; } - [array addObject:[NSDictionary dictionaryWithObjectsAndKeys:value, kCHIPValueKey, - [NSNumber numberWithUnsignedLongLong:(unsigned long long) tag.mVal], kCHIPTagKey, nil]]; + NSMutableDictionary * arrayElement = [NSMutableDictionary dictionary]; + [arrayElement setObject:value forKey:kCHIPDataKey]; + if (dataTLVType == chip::TLV::kTLVType_Structure) { + [arrayElement setObject:[NSNumber numberWithUnsignedLong:TagNumFromTag(tag)] forKey:kCHIPContextTagKey]; + } + [array addObject:arrayElement]; } if (err != CHIP_END_OF_TLV) { CHIP_LOG_ERROR("Error(%s): TLV container decoding failed", chip::ErrorStr(err)); @@ -458,59 +489,59 @@ static CHIP_ERROR EncodeTLVFromObject(id object, chip::TLV::TLVWriter & writer, CHIP_LOG_ERROR("Error: Object to encode is corrupt"); return CHIP_ERROR_INVALID_ARGUMENT; } - if ([typeName isEqualToString:kCHIPSignedIntegerValueTypeKey]) { + if ([typeName isEqualToString:kCHIPSignedIntegerValueType]) { if (![value isKindOfClass:[NSNumber class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt signed integer type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; } - return writer.Put(tag, [value intValue]); + return writer.Put(tag, [value longLongValue]); } - if ([typeName isEqualToString:kCHIPUnsignedIntegerValueTypeKey]) { + if ([typeName isEqualToString:kCHIPUnsignedIntegerValueType]) { if (![value isKindOfClass:[NSNumber class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt unsigned integer type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; } - return writer.Put(tag, [value unsignedIntValue]); + return writer.Put(tag, [value unsignedLongLongValue]); } - if ([typeName isEqualToString:kCHIPBooleanValueTypeKey]) { + if ([typeName isEqualToString:kCHIPBooleanValueType]) { if (![value isKindOfClass:[NSNumber class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt boolean type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; } - return writer.Put(tag, [value unsignedIntValue] ? true : false); + return writer.Put(tag, static_cast([value boolValue])); } - if ([typeName isEqualToString:kCHIPFloatValueTypeKey]) { + if ([typeName isEqualToString:kCHIPFloatValueType]) { if (![value isKindOfClass:[NSNumber class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt float type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; } return writer.Put(tag, [value floatValue]); } - if ([typeName isEqualToString:kCHIPDoubleValueTypeKey]) { + if ([typeName isEqualToString:kCHIPDoubleValueType]) { if (![value isKindOfClass:[NSNumber class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt double type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; } return writer.Put(tag, [value doubleValue]); } - if ([typeName isEqualToString:kCHIPNullValueTypeKey]) { + if ([typeName isEqualToString:kCHIPNullValueType]) { return writer.PutNull(tag); } - if ([typeName isEqualToString:kCHIPUTF8StringValueTypeKey]) { + if ([typeName isEqualToString:kCHIPUTF8StringValueType]) { if (![value isKindOfClass:[NSString class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt UTF8 string type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; } return writer.PutString(tag, [value cStringUsingEncoding:NSUTF8StringEncoding]); } - if ([typeName isEqualToString:kCHIPOctetStringValueTypeKey]) { + if ([typeName isEqualToString:kCHIPOctetStringValueType]) { if (![value isKindOfClass:[NSData class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt octet string type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; } return writer.Put(tag, chip::ByteSpan(static_cast([value bytes]), [value length])); } - if ([typeName isEqualToString:kCHIPStructureValueTypeKey]) { + if ([typeName isEqualToString:kCHIPStructureValueType]) { if (![value isKindOfClass:[NSArray class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt structure type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; @@ -522,8 +553,8 @@ static CHIP_ERROR EncodeTLVFromObject(id object, chip::TLV::TLVWriter & writer, CHIP_LOG_ERROR("Error: Structure element to encode has corrupt type: %@", [element class]); return CHIP_ERROR_INVALID_ARGUMENT; } - NSNumber * elementTag = element[kCHIPTagKey]; - id elementValue = element[kCHIPValueKey]; + NSNumber * elementTag = element[kCHIPContextTagKey]; + id elementValue = element[kCHIPDataKey]; if (!elementTag || !elementValue) { CHIP_LOG_ERROR("Error: Structure element to encode has corrupt value: %@", element); return CHIP_ERROR_INVALID_ARGUMENT; @@ -533,7 +564,7 @@ static CHIP_ERROR EncodeTLVFromObject(id object, chip::TLV::TLVWriter & writer, ReturnErrorOnFailure(writer.EndContainer(outer)); return CHIP_NO_ERROR; } - if ([typeName isEqualToString:kCHIPArrayValueTypeKey]) { + if ([typeName isEqualToString:kCHIPArrayValueType]) { if (![value isKindOfClass:[NSArray class]]) { CHIP_LOG_ERROR("Error: Object to encode has corrupt array type: %@", [value class]); return CHIP_ERROR_INVALID_ARGUMENT; @@ -545,7 +576,7 @@ static CHIP_ERROR EncodeTLVFromObject(id object, chip::TLV::TLVWriter & writer, CHIP_LOG_ERROR("Error: Array element to encode has corrupt type: %@", [element class]); return CHIP_ERROR_INVALID_ARGUMENT; } - id elementValue = element[kCHIPValueKey]; + id elementValue = element[kCHIPDataKey]; if (!elementValue) { CHIP_LOG_ERROR("Error: Array element to encode has corrupt value: %@", element); return CHIP_ERROR_INVALID_ARGUMENT; @@ -555,36 +586,12 @@ static CHIP_ERROR EncodeTLVFromObject(id object, chip::TLV::TLVWriter & writer, ReturnErrorOnFailure(writer.EndContainer(outer)); return CHIP_NO_ERROR; } - if ([typeName isEqualToString:kCHIPListValueTypeKey]) { - if (![value isKindOfClass:[NSArray class]]) { - CHIP_LOG_ERROR("Error: Object to encode has corrupt list type: %@", [value class]); - return CHIP_ERROR_INVALID_ARGUMENT; - } - TLV::TLVType outer; - ReturnErrorOnFailure(writer.StartContainer(tag, chip::TLV::kTLVType_List, outer)); - for (id element in value) { - if (![element isKindOfClass:[NSDictionary class]]) { - CHIP_LOG_ERROR("Error: List element to encode has corrupt type: %@", [element class]); - return CHIP_ERROR_INVALID_ARGUMENT; - } - NSNumber * elementTag = element[kCHIPTagKey]; - id elementValue = element[kCHIPValueKey]; - if (!elementValue) { - CHIP_LOG_ERROR("Error: Array element to encode has corrupt value: %@", element); - return CHIP_ERROR_INVALID_ARGUMENT; - } - ReturnErrorOnFailure(EncodeTLVFromObject(elementValue, writer, - elementTag ? chip::TLV::ContextTag((uint8_t)[elementTag unsignedCharValue]) : chip::TLV::AnonymousTag())); - } - ReturnErrorOnFailure(writer.EndContainer(outer)); - return CHIP_NO_ERROR; - } CHIP_LOG_ERROR("Error: Unsupported type to encode: %@", typeName); return CHIP_ERROR_INVALID_ARGUMENT; } -// Callback type to pass attribute value as an NSObject -typedef void (*NSObjectAttributeCallback)(void * context, id value); +// Callback type to pass data value as an NSObject +typedef void (*NSObjectDataValueCallback)(void * context, id value); typedef void (*CHIPErrorCallback)(void * context, CHIP_ERROR error); // Rename to be generic for decode and encode @@ -623,12 +630,12 @@ CHIP_ERROR Encode(chip::TLV::TLVWriter & writer, chip::TLV::Tag tag) const id _Nullable decodedObj; }; -// Callback bridge for NSObjectAttributeCallback -class NSObjectAttributeCallbackBridge : public CHIPCallbackBridge { +// Callback bridge for NSObjectDataValueCallback +class NSObjectDataValueCallbackBridge : public CHIPCallbackBridge { public: - NSObjectAttributeCallbackBridge( + NSObjectDataValueCallbackBridge( dispatch_queue_t queue, CHIPDeviceResponseHandler handler, CHIPActionBlock action, bool keepAlive = false) - : CHIPCallbackBridge(queue, handler, action, OnSuccessFn, keepAlive) {}; + : CHIPCallbackBridge(queue, handler, action, OnSuccessFn, keepAlive) {}; static void OnSuccessFn(void * context, id value) { DispatchSuccess(context, value); } }; @@ -698,15 +705,7 @@ void OnSubscriptionEstablished(uint64_t aSubscriptionId) override } } - void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override - { - VerifyOrDie( - aReadPrepareParams.mAttributePathParamsListSize == 1 && aReadPrepareParams.mpAttributePathParamsList != nullptr); - chip::Platform::Delete(aReadPrepareParams.mpAttributePathParamsList); - - VerifyOrDie(aReadPrepareParams.mDataVersionFilterListSize == 1 && aReadPrepareParams.mpDataVersionFilterList != nullptr); - chip::Platform::Delete(aReadPrepareParams.mpDataVersionFilterList); - } + void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override {} ClusterId mClusterId; AttributeId mAttributeId; @@ -724,9 +723,9 @@ - (void)readAttributeWithEndpointId:(NSUInteger)endpointId clientQueue:(dispatch_queue_t)clientQueue completion:(CHIPDeviceResponseHandler)completion { - new NSObjectAttributeCallbackBridge( + new NSObjectDataValueCallbackBridge( clientQueue, completion, ^(chip::Callback::Cancelable * success, chip::Callback::Cancelable * failure) { - auto successFn = chip::Callback::Callback::FromCancelable(success); + auto successFn = chip::Callback::Callback::FromCancelable(success); auto failureFn = chip::Callback::Callback::FromCancelable(failure); auto context = successFn->mContext; auto successCb = successFn->mCall; @@ -734,29 +733,25 @@ new NSObjectAttributeCallbackBridge( auto resultArray = [[NSMutableArray alloc] init]; auto resultSuccess = [[NSMutableArray alloc] init]; auto resultFailure = [[NSMutableArray alloc] init]; - auto onSuccessCb = [resultArray, resultSuccess]( - const app::ConcreteAttributePath & attribPath, const NSObjectData & aData) { - [resultArray - addObject:[[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:attribPath.mEndpointId], - kCHIPEndpointIdKey, [NSNumber numberWithUnsignedInt:attribPath.mClusterId], - kCHIPClusterIdKey, [NSNumber numberWithUnsignedInt:attribPath.mAttributeId], - kCHIPAttributeIdKey, aData.GetDecodedObject(), kCHIPDataKey, - [NSNumber numberWithUnsignedInt:0], kCHIPStatusKey, nil]]; - if ([resultSuccess count] == 0) { - [resultSuccess addObject:[NSNumber numberWithBool:YES]]; - } - }; + auto onSuccessCb + = [resultArray, resultSuccess](const app::ConcreteAttributePath & attribPath, const NSObjectData & aData) { + [resultArray addObject:@ { + kCHIPAttributePathKey : [[CHIPAttributePath alloc] initWithPath:attribPath], + kCHIPDataKey : aData.GetDecodedObject() + }]; + if ([resultSuccess count] == 0) { + [resultSuccess addObject:[NSNumber numberWithBool:YES]]; + } + }; auto onFailureCb = [resultArray, resultFailure](const app::ConcreteAttributePath * attribPath, CHIP_ERROR aError) { if (attribPath) { - [resultArray addObject:[[NSDictionary alloc] - initWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:attribPath->mEndpointId], - kCHIPEndpointIdKey, [NSNumber numberWithUnsignedInt:attribPath->mClusterId], - kCHIPClusterIdKey, [NSNumber numberWithUnsignedInt:attribPath->mAttributeId], - kCHIPAttributeIdKey, [NSNumber numberWithUnsignedInteger:aError.AsInteger()], - kCHIPStatusKey, nil]]; + [resultArray addObject:@ { + kCHIPAttributePathKey : [[CHIPAttributePath alloc] initWithPath:*attribPath], + kCHIPErrorKey : [CHIPError errorForCHIPErrorCode:aError] + }]; } else if ([resultFailure count] == 0) { - [resultFailure addObject:[NSNumber numberWithUnsignedInteger:aError.AsInteger()]]; + [resultFailure addObject:[CHIPError errorForCHIPErrorCode:aError]]; } }; @@ -778,12 +773,9 @@ new NSObjectAttributeCallbackBridge( // Failure if (failureCb) { if ([resultFailure count] > 0) { - failureCb( - context, CHIP_ERROR(static_cast([resultFailure[0] unsignedIntegerValue]))); + failureCb(context, [CHIPError errorToCHIPErrorCode:resultFailure[0]]); } else if ([resultArray count] > 0) { - failureCb(context, - CHIP_ERROR( - static_cast([resultArray[0][kCHIPStatusKey] unsignedIntegerValue]))); + failureCb(context, [CHIPError errorToCHIPErrorCode:resultArray[0][kCHIPErrorKey]]); } else { failureCb(context, CHIP_ERROR_READ_FAILED); } @@ -829,9 +821,9 @@ - (void)writeAttributeWithEndpointId:(NSUInteger)endpointId clientQueue:(dispatch_queue_t)clientQueue completion:(CHIPDeviceResponseHandler)completion { - new NSObjectAttributeCallbackBridge( + new NSObjectDataValueCallbackBridge( clientQueue, completion, ^(chip::Callback::Cancelable * success, chip::Callback::Cancelable * failure) { - auto successFn = chip::Callback::Callback::FromCancelable(success); + auto successFn = chip::Callback::Callback::FromCancelable(success); auto failureFn = chip::Callback::Callback::FromCancelable(failure); auto context = successFn->mContext; auto successCb = successFn->mCall; @@ -839,55 +831,46 @@ new NSObjectAttributeCallbackBridge( auto resultArray = [[NSMutableArray alloc] init]; auto resultSuccess = [[NSMutableArray alloc] init]; auto resultFailure = [[NSMutableArray alloc] init]; - auto onSuccessCb = [resultArray, resultSuccess](const app::ConcreteAttributePath & commandPath) { - [resultArray - addObject:[[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:commandPath.mEndpointId], - kCHIPEndpointIdKey, [NSNumber numberWithUnsignedInt:commandPath.mClusterId], - kCHIPClusterIdKey, [NSNumber numberWithUnsignedInt:commandPath.mAttributeId], - kCHIPAttributeIdKey, [NSNumber numberWithUnsignedInt:0], kCHIPStatusKey, nil]]; + auto onSuccessCb = [resultArray, resultSuccess](const app::ConcreteAttributePath & attribPath) { + [resultArray addObject:@ { kCHIPAttributePathKey : [[CHIPAttributePath alloc] initWithPath:attribPath] }]; if ([resultSuccess count] == 0) { [resultSuccess addObject:[NSNumber numberWithBool:YES]]; } }; - auto onFailureCb = [resultArray, resultFailure](const app::ConcreteAttributePath * commandPath, CHIP_ERROR aError) { - if (commandPath) { - [resultArray addObject:[[NSDictionary alloc] - initWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:commandPath->mEndpointId], - kCHIPEndpointIdKey, [NSNumber numberWithUnsignedInt:commandPath->mClusterId], - kCHIPClusterIdKey, [NSNumber numberWithUnsignedInt:commandPath->mAttributeId], - kCHIPAttributeIdKey, [NSNumber numberWithUnsignedInteger:aError.AsInteger()], - kCHIPStatusKey, nil]]; + auto onFailureCb = [resultArray, resultFailure](const app::ConcreteAttributePath * attribPath, CHIP_ERROR aError) { + if (attribPath) { + [resultArray addObject:@ { + kCHIPAttributePathKey : [[CHIPAttributePath alloc] initWithPath:*attribPath], + kCHIPErrorKey : [CHIPError errorForCHIPErrorCode:aError], + }]; } else { if ([resultFailure count] == 0) { - [resultFailure addObject:[NSNumber numberWithUnsignedInteger:aError.AsInteger()]]; + [resultFailure addObject:[CHIPError errorForCHIPErrorCode:aError]]; } } }; - auto onDoneCb = [context, successCb, failureCb, resultArray, resultSuccess, resultFailure]( - app::WriteClient * pWriteClient) { - if ([resultFailure count] > 0 || [resultSuccess count] == 0) { - // Failure - if (failureCb) { - if ([resultFailure count] > 0) { - failureCb( - context, CHIP_ERROR(static_cast([resultFailure[0] unsignedIntegerValue]))); - } else if ([resultArray count] > 0) { - failureCb(context, - CHIP_ERROR( - static_cast([resultArray[0][kCHIPStatusKey] unsignedIntegerValue]))); - } else { - failureCb(context, CHIP_ERROR_WRITE_FAILED); - } - } - } else { - // Success - if (successCb) { - successCb(context, resultArray); - } - } - }; + auto onDoneCb + = [context, successCb, failureCb, resultArray, resultSuccess, resultFailure](app::WriteClient * pWriteClient) { + if ([resultFailure count] > 0 || [resultSuccess count] == 0) { + // Failure + if (failureCb) { + if ([resultFailure count] > 0) { + failureCb(context, [CHIPError errorToCHIPErrorCode:resultFailure[0]]); + } else if ([resultArray count] > 0) { + failureCb(context, [CHIPError errorToCHIPErrorCode:resultArray[0][kCHIPErrorKey]]); + } else { + failureCb(context, CHIP_ERROR_WRITE_FAILED); + } + } + } else { + // Success + if (successCb) { + successCb(context, resultArray); + } + } + }; return chip::Controller::WriteAttribute([self internalDevice]->GetSecureSession().Value(), static_cast(endpointId), static_cast(clusterId), @@ -968,9 +951,9 @@ - (void)invokeCommandWithEndpointId:(NSUInteger)endpointId clientQueue:(dispatch_queue_t)clientQueue completion:(CHIPDeviceResponseHandler)completion { - new NSObjectAttributeCallbackBridge( + new NSObjectDataValueCallbackBridge( clientQueue, completion, ^(chip::Callback::Cancelable * success, chip::Callback::Cancelable * failure) { - auto successFn = chip::Callback::Callback::FromCancelable(success); + auto successFn = chip::Callback::Callback::FromCancelable(success); auto failureFn = chip::Callback::Callback::FromCancelable(failure); auto context = successFn->mContext; auto successCb = successFn->mCall; @@ -981,18 +964,12 @@ new NSObjectAttributeCallbackBridge( auto onSuccessCb = [resultArray, resultSuccess](const app::ConcreteCommandPath & commandPath, const app::StatusIB & status, const NSObjectData & responseData) { if (responseData.GetDecodedObject()) { - [resultArray addObject:[[NSDictionary alloc] - initWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:commandPath.mEndpointId], - kCHIPEndpointIdKey, [NSNumber numberWithUnsignedInt:commandPath.mClusterId], - kCHIPClusterIdKey, [NSNumber numberWithUnsignedInt:commandPath.mCommandId], - kCHIPCommandIdKey, [NSNumber numberWithUnsignedInt:0], kCHIPStatusKey, - responseData.GetDecodedObject(), kCHIPDataKey, nil]]; + [resultArray addObject:@ { + kCHIPCommandPathKey : [[CHIPCommandPath alloc] initWithPath:commandPath], + kCHIPDataKey : responseData.GetDecodedObject() + }]; } else { - [resultArray addObject:[[NSDictionary alloc] - initWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:commandPath.mEndpointId], - kCHIPEndpointIdKey, [NSNumber numberWithUnsignedInt:commandPath.mClusterId], - kCHIPClusterIdKey, [NSNumber numberWithUnsignedInt:commandPath.mCommandId], - kCHIPCommandIdKey, [NSNumber numberWithUnsignedInt:0], kCHIPStatusKey, nil]]; + [resultArray addObject:@ { kCHIPCommandPathKey : [[CHIPCommandPath alloc] initWithPath:commandPath] }]; } if ([resultSuccess count] == 0) { [resultSuccess addObject:[NSNumber numberWithBool:YES]]; @@ -1001,7 +978,7 @@ new NSObjectAttributeCallbackBridge( auto onFailureCb = [resultFailure](CHIP_ERROR aError) { if ([resultFailure count] == 0) { - [resultFailure addObject:[NSNumber numberWithUnsignedInteger:aError.AsInteger()]]; + [resultFailure addObject:[CHIPError errorForCHIPErrorCode:aError]]; } }; @@ -1019,8 +996,7 @@ new NSObjectAttributeCallbackBridge( // Failure if (failureCb) { if ([resultFailure count] > 0) { - failureCb( - context, CHIP_ERROR(static_cast([resultFailure[0] unsignedIntegerValue]))); + failureCb(context, [CHIPError errorToCHIPErrorCode:resultFailure[0]]); } else { failureCb(context, CHIP_ERROR_WRITE_FAILED); } @@ -1056,8 +1032,7 @@ - (void)subscribeAttributeWithEndpointId:(NSUInteger)endpointId minInterval:(NSUInteger)minInterval maxInterval:(NSUInteger)maxInterval clientQueue:(dispatch_queue_t)clientQueue - reportHandler:(void (^)(NSDictionary * _Nullable value, - NSError * _Nullable error))reportHandler + reportHandler:(CHIPDeviceResponseHandler)reportHandler subscriptionEstablished:(SubscriptionEstablishedHandler)subscriptionEstablishedHandler { dispatch_async(DeviceLayer::PlatformMgrImpl().GetWorkQueue(), ^{ @@ -1065,12 +1040,10 @@ - (void)subscribeAttributeWithEndpointId:(NSUInteger)endpointId id valueObject = data.GetDecodedObject(); app::ConcreteAttributePath pathCopy = attribPath; dispatch_async(clientQueue, ^{ - reportHandler(@ { - kCHIPEndpointIdKey : [NSNumber numberWithUnsignedShort:pathCopy.mEndpointId], - kCHIPClusterIdKey : [NSNumber numberWithUnsignedInt:pathCopy.mClusterId], - kCHIPAttributeIdKey : [NSNumber numberWithUnsignedInt:pathCopy.mAttributeId], - kCHIPValueKey : valueObject - }, + reportHandler( + @[ + @ { kCHIPAttributePathKey : [[CHIPAttributePath alloc] initWithPath:pathCopy], kCHIPDataKey : valueObject } + ], nil); }); }; @@ -1107,7 +1080,7 @@ - (void)subscribeAttributeWithEndpointId:(NSUInteger)endpointId CHIPReadClientContainer * container = [[CHIPReadClientContainer alloc] init]; container.deviceId = self.cppDevice->GetDeviceId(); - container.pathParams = new app::AttributePathParams(chipEndpointId, chipClusterId, chipAttributeId); + container.pathParams = Platform::New(chipEndpointId, chipClusterId, chipAttributeId); app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance(); CHIP_ERROR err = CHIP_NO_ERROR; @@ -1124,7 +1097,7 @@ - (void)subscribeAttributeWithEndpointId:(NSUInteger)endpointId auto callback = chip::Platform::MakeUnique>( chipClusterId, chipAttributeId, onReportCb, onFailureCb, onDone, onEstablishedCb); - auto readClient = new app::ReadClient(engine, [self internalDevice]->GetExchangeManager(), + auto readClient = Platform::New(engine, [self internalDevice]->GetExchangeManager(), callback -> GetBufferedCallback(), chip::app::ReadClient::InteractionType::Subscribe); err = readClient->SendAutoResubscribeRequest(std::move(readParams)); @@ -1135,7 +1108,7 @@ - (void)subscribeAttributeWithEndpointId:(NSUInteger)endpointId reportHandler(nil, [CHIPError errorForCHIPErrorCode:err]); }); } - delete readClient; + Platform::Delete(readClient); return; } @@ -1153,6 +1126,15 @@ - (void)deregisterReportHandlersWithClientQueue:(dispatch_queue_t)clientQueue co PurgeReadClientContainers(self.cppDevice->GetDeviceId(), clientQueue, completion); } +#ifdef DEBUG +// This method is for unit testing only +- (void)failSubscribers:(dispatch_queue_t)clientQueue completion:(void (^)(void))completion +{ + CHIP_LOG_DEBUG("Causing failure in subscribers on purpose"); + CauseReadClientFailure(self.cppDevice->GetDeviceId(), clientQueue, completion); +} +#endif + // The following method is for unit testing purpose only + (id)CHIPEncodeAndDecodeNSObject:(id)object { @@ -1205,6 +1187,35 @@ - (instancetype)initWithPath:(const ConcreteDataAttributePath &)path } return self; } + ++ (instancetype)attributePathWithEndpointId:(NSNumber *)endpoint clusterId:(NSNumber *)clusterId attributeId:(NSNumber *)attributeId +{ + ConcreteDataAttributePath path(static_cast([endpoint unsignedShortValue]), + static_cast([clusterId unsignedLongValue]), + static_cast([attributeId unsignedLongValue])); + + return [[CHIPAttributePath alloc] initWithPath:path]; +} +@end + +@implementation CHIPCommandPath +- (instancetype)initWithPath:(const ConcreteCommandPath &)path +{ + if (self = [super init]) { + _endpoint = @(path.mEndpointId); + _cluster = @(path.mClusterId); + _command = @(path.mCommandId); + } + return self; +} + ++ (instancetype)commandPathWithEndpointId:(NSNumber *)endpoint clusterId:(NSNumber *)clusterId commandId:(NSNumber *)commandId +{ + ConcreteCommandPath path(static_cast([endpoint unsignedShortValue]), + static_cast([clusterId unsignedLongValue]), static_cast([commandId unsignedLongValue])); + + return [[CHIPCommandPath alloc] initWithPath:path]; +} @end @implementation CHIPAttributeReport diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h b/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h index 77405ebfc04feb..58b2ff738da1e7 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h +++ b/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.h @@ -35,8 +35,19 @@ NS_ASSUME_NONNULL_BEGIN + (CHIPDeviceController *)sharedControllerWithId:(id _Nullable)controllerId xpcConnectBlock:(NSXPCConnection * (^)(void) )connectBlock; -@end +/** + * Returns an encoded values object to send over XPC for read, write and command interactions + */ ++ (NSArray *> * _Nullable)encodeXPCResponseValues: + (NSArray *> * _Nullable)values; +/** + * Returns a decoded values object from a values object received from XPC for read, write and command interactions + */ ++ (NSArray *> * _Nullable)decodeXPCResponseValues: + (NSArray *> * _Nullable)values; + +@end /** * Protocol that remote object must support over XPC */ @@ -126,7 +137,7 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)handleReportWithController:(id _Nullable)controller nodeId:(uint64_t)nodeId - value:(id _Nullable)value + values:(id _Nullable)values error:(NSError * _Nullable)error; @end diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.m b/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.m index 21ae2bd469e7c0..4c07d98846ded8 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.m +++ b/src/darwin/Framework/CHIP/CHIPDeviceController+XPC.m @@ -17,10 +17,43 @@ #import "CHIPDeviceController+XPC.h" +#import "CHIPDevice.h" #import "CHIPDeviceControllerOverXPC.h" NS_ASSUME_NONNULL_BEGIN +static NSArray * _Nullable serializeAttributePath(CHIPAttributePath * _Nullable path) +{ + if (!path) { + return nil; + } + return @[ path.endpoint, path.cluster, path.attribute ]; +} + +static NSArray * _Nullable serializeCommandPath(CHIPCommandPath * _Nullable path) +{ + if (!path) { + return nil; + } + return @[ path.endpoint, path.cluster, path.command ]; +} + +static CHIPAttributePath * _Nullable deserializeAttributePath(NSArray * _Nullable pathArray) +{ + if (pathArray == nil || [pathArray count] != 3) { + return nil; + } + return [CHIPAttributePath attributePathWithEndpointId:pathArray[0] clusterId:pathArray[1] attributeId:pathArray[2]]; +} + +static CHIPCommandPath * _Nullable deserializeCommandPath(NSArray * _Nullable pathArray) +{ + if (pathArray == nil || [pathArray count] != 3) { + return nil; + } + return [CHIPCommandPath commandPathWithEndpointId:pathArray[0] clusterId:pathArray[1] commandId:pathArray[2]]; +} + @implementation CHIPDeviceController (XPC) + (CHIPDeviceController *)sharedControllerWithId:(id _Nullable)controllerId @@ -29,6 +62,55 @@ + (CHIPDeviceController *)sharedControllerWithId:(id _Nullable)contro return [CHIPDeviceControllerOverXPC sharedControllerWithId:controllerId xpcConnectBlock:connectBlock]; } ++ (NSArray *> * _Nullable)encodeXPCResponseValues: + (NSArray *> * _Nullable)values +{ + if (!values) { + return values; + } + NSMutableArray * result = [NSMutableArray array]; + for (NSDictionary * value in values) { + if (!value || (value[kCHIPAttributePathKey] == nil && value[kCHIPCommandPathKey] == nil)) { + [result addObject:value]; + continue; + } + NSMutableDictionary * resultValue = [NSMutableDictionary dictionaryWithCapacity:[value count]]; + [resultValue addEntriesFromDictionary:value]; + if (value[kCHIPAttributePathKey]) { + resultValue[kCHIPAttributePathKey] = serializeAttributePath(value[kCHIPAttributePathKey]); + } + if (value[kCHIPCommandPathKey]) { + resultValue[kCHIPCommandPathKey] = serializeCommandPath(value[kCHIPCommandPathKey]); + } + [result addObject:resultValue]; + } + return result; +} + ++ (NSArray *> * _Nullable)decodeXPCResponseValues: + (NSArray *> * _Nullable)values +{ + if (!values) { + return values; + } + NSMutableArray * result = [NSMutableArray array]; + for (NSDictionary * value in values) { + if (!value || (value[kCHIPAttributePathKey] == nil && value[kCHIPCommandPathKey] == nil)) { + [result addObject:value]; + } + NSMutableDictionary * resultValue = [NSMutableDictionary dictionaryWithCapacity:[value count]]; + [resultValue addEntriesFromDictionary:value]; + if (value[kCHIPAttributePathKey]) { + resultValue[kCHIPAttributePathKey] = deserializeAttributePath(value[kCHIPAttributePathKey]); + } + if (value[kCHIPCommandPathKey]) { + resultValue[kCHIPCommandPathKey] = deserializeCommandPath(value[kCHIPCommandPathKey]); + } + [result addObject:resultValue]; + } + return result; +} + @end NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m b/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m index 744a6c8aa12b1a..34cab94731bb4f 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m +++ b/src/darwin/Framework/CHIP/CHIPDeviceControllerOverXPC+AttributeCache.m @@ -95,24 +95,24 @@ - (void)readAttributeCacheWithNodeId:(uint64_t)nodeId completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); return; } - [self.xpcConnection - getProxyHandleWithCompletion:^(dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) { - if (handle) { - [handle.proxy readAttributeCacheWithController:self.controllerId - nodeId:nodeId - endpointId:endpointId - clusterId:clusterId - attributeId:attributeId - completion:^(id _Nullable values, NSError * _Nullable error) { - completion(values, error); - __auto_type handleRetainer = handle; - (void) handleRetainer; - }]; - } else { - CHIP_LOG_ERROR("Attribute cache read failed due to XPC connection failure"); - completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); - } - }]; + [self.xpcConnection getProxyHandleWithCompletion:^( + dispatch_queue_t _Nonnull queue, CHIPDeviceControllerXPCProxyHandle * _Nullable handle) { + if (handle) { + [handle.proxy readAttributeCacheWithController:self.controllerId + nodeId:nodeId + endpointId:endpointId + clusterId:clusterId + attributeId:attributeId + completion:^(id _Nullable values, NSError * _Nullable error) { + completion([CHIPDeviceController decodeXPCResponseValues:values], error); + __auto_type handleRetainer = handle; + (void) handleRetainer; + }]; + } else { + CHIP_LOG_ERROR("Attribute cache read failed due to XPC connection failure"); + completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); + } + }]; }); } diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerXPCConnection.h b/src/darwin/Framework/CHIP/CHIPDeviceControllerXPCConnection.h index 9c7ff17b0806df..f1596a59e26070 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceControllerXPCConnection.h +++ b/src/darwin/Framework/CHIP/CHIPDeviceControllerXPCConnection.h @@ -47,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN CHIPDeviceControllerXPCProxyHandle * _Nullable container))completion; - (void)registerReportHandlerWithController:(id)controller nodeId:(NSUInteger)nodeId - handler:(void (^)(id _Nullable value, NSError * _Nullable error))handler; + handler:(void (^)(id _Nullable values, NSError * _Nullable error))handler; - (void)deregisterReportHandlersWithController:(id)controller nodeId:(NSUInteger)nodeId completion:(void (^)(void))completion; diff --git a/src/darwin/Framework/CHIP/CHIPDeviceControllerXPCConnection.m b/src/darwin/Framework/CHIP/CHIPDeviceControllerXPCConnection.m index ea34191e7081aa..0082cd82d614d3 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceControllerXPCConnection.m +++ b/src/darwin/Framework/CHIP/CHIPDeviceControllerXPCConnection.m @@ -121,7 +121,7 @@ - (void)getProxyHandleWithCompletion:(void (^)(dispatch_queue_t queue, - (void)registerReportHandlerWithController:(id)controller nodeId:(NSUInteger)nodeId - handler:(void (^)(id _Nullable value, NSError * _Nullable error))handler + handler:(void (^)(id _Nullable values, NSError * _Nullable error))handler { dispatch_async(_workQueue, ^{ BOOL shouldRetainProxyForReport = ([self.reportRegistry count] == 0); @@ -170,7 +170,7 @@ - (void)deregisterReportHandlersWithController:(id)controller - (void)handleReportWithController:(id)controller nodeId:(NSUInteger)nodeId - value:(id _Nullable)value + values:(id _Nullable)values error:(NSError * _Nullable)error { dispatch_async(_workQueue, ^{ @@ -183,8 +183,8 @@ - (void)handleReportWithController:(id)controller if (!nodeArray) { return; } - for (void (^handler)(id _Nullable value, NSError * _Nullable error) in nodeArray) { - handler(value, error); + for (void (^handler)(id _Nullable values, NSError * _Nullable error) in nodeArray) { + handler(values, error); } }); } diff --git a/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m b/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m index bfae60253098a0..909d943cbced68 100644 --- a/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m +++ b/src/darwin/Framework/CHIP/CHIPDeviceOverXPC.m @@ -22,6 +22,8 @@ #import "CHIPError.h" #import "CHIPLogging.h" +NS_ASSUME_NONNULL_BEGIN + @interface CHIPDeviceOverXPC () @property (nonatomic, strong, readonly) id controller; @@ -73,7 +75,7 @@ - (void)readAttributeWithEndpointId:(NSUInteger)endpointId completion:^(id _Nullable values, NSError * _Nullable error) { dispatch_async(clientQueue, ^{ CHIP_LOG_DEBUG("Attribute read"); - completion(values, error); + completion([CHIPDeviceController decodeXPCResponseValues:values], error); // The following captures the proxy handle in the closure so that the // handle won't be released prior to block call. __auto_type handleRetainer = handle; @@ -109,7 +111,7 @@ - (void)writeAttributeWithEndpointId:(NSUInteger)endpointId completion:^(id _Nullable values, NSError * _Nullable error) { dispatch_async(clientQueue, ^{ CHIP_LOG_DEBUG("Attribute written"); - completion(values, error); + completion([CHIPDeviceController decodeXPCResponseValues:values], error); // The following captures the proxy handle in the closure so that the // handle won't be released prior to block call. __auto_type handleRetainer = handle; @@ -145,7 +147,7 @@ - (void)invokeCommandWithEndpointId:(NSUInteger)endpointId completion:^(id _Nullable values, NSError * _Nullable error) { dispatch_async(clientQueue, ^{ CHIP_LOG_DEBUG("Command invoked"); - completion(values, error); + completion([CHIPDeviceController decodeXPCResponseValues:values], error); // The following captures the proxy handle in the closure so that the // handle won't be released prior to block call. __auto_type handleRetainer = handle; @@ -167,8 +169,7 @@ - (void)subscribeAttributeWithEndpointId:(NSUInteger)endpointId minInterval:(NSUInteger)minInterval maxInterval:(NSUInteger)maxInterval clientQueue:(dispatch_queue_t)clientQueue - reportHandler:(void (^)(NSDictionary * _Nullable value, - NSError * _Nullable error))reportHandler + reportHandler:(CHIPDeviceResponseHandler)reportHandler subscriptionEstablished:(void (^_Nullable)(void))subscriptionEstablishedHandler { CHIP_LOG_DEBUG("Subscribing attribute ..."); @@ -179,21 +180,36 @@ - (void)subscribeAttributeWithEndpointId:(NSUInteger)endpointId [self.xpcConnection registerReportHandlerWithController:self.controller nodeId:self.nodeId - handler:^(id _Nullable value, NSError * _Nullable error) { - if (value && ![value isKindOfClass:[NSDictionary class]]) { + handler:^(id _Nullable values, NSError * _Nullable error) { + if (values && ![values isKindOfClass:[NSArray class]]) { CHIP_LOG_ERROR("Unsupported report format"); return; } - NSUInteger receivedEndpointId = [value[kCHIPEndpointIdKey] unsignedIntegerValue]; - NSUInteger receivedClusterId = [value[kCHIPClusterIdKey] unsignedIntegerValue]; - NSUInteger receivedAttributeId = [value[kCHIPAttributeIdKey] unsignedIntegerValue]; - if (error - || ((receivedEndpointId == endpointId || endpointId == 0xffff) - && (receivedClusterId == clusterId || clusterId == 0xffffffff) - && (receivedAttributeId == attributeId || attributeId == 0xffffffff))) { + if (!values) { + CHIP_LOG_DEBUG("Error report received"); + dispatch_async(clientQueue, ^{ + reportHandler(values, error); + }); + return; + } + __auto_type decodedValues = [CHIPDeviceController decodeXPCResponseValues:values]; + NSMutableArray *> * filteredValues = + [NSMutableArray arrayWithCapacity:[decodedValues count]]; + for (NSDictionary * decodedValue in decodedValues) { + CHIPAttributePath * attributePath = decodedValue[kCHIPAttributePathKey]; + if (([attributePath.endpoint unsignedIntegerValue] == endpointId + || endpointId == 0xffff) + && ([attributePath.cluster unsignedIntegerValue] == clusterId + || clusterId == 0xffffffff) + && ([attributePath.attribute unsignedIntegerValue] == attributeId + || attributeId == 0xffffffff)) { + [filteredValues addObject:decodedValue]; + } + } + if ([filteredValues count] > 0) { CHIP_LOG_DEBUG("Report received"); dispatch_async(clientQueue, ^{ - reportHandler(value, error); + reportHandler(filteredValues, error); }); } }]; @@ -235,3 +251,5 @@ - (void)deregisterReportHandlersWithClientQueue:(dispatch_queue_t)clientQueue co } @end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/CHIPDevice_Internal.h b/src/darwin/Framework/CHIP/CHIPDevice_Internal.h index 2f6a9ed8bbd3ec..ffbd98b7a591f1 100644 --- a/src/darwin/Framework/CHIP/CHIPDevice_Internal.h +++ b/src/darwin/Framework/CHIP/CHIPDevice_Internal.h @@ -21,6 +21,8 @@ #import "CHIPDevice.h" #import +#include +#include #include NS_ASSUME_NONNULL_BEGIN @@ -32,6 +34,14 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface CHIPAttributePath () +- (instancetype)initWithPath:(const chip::app::ConcreteDataAttributePath &)path; +@end + +@interface CHIPCommandPath () +- (instancetype)initWithPath:(const chip::app::ConcreteCommandPath &)path; +@end + // Exported utility function id _Nullable NSObjectFromCHIPTLV(chip::TLV::TLVReader * data); diff --git a/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m b/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m index 4f47df6da4913c..0ddb2c8b99989d 100644 --- a/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPDeviceTests.m @@ -70,6 +70,12 @@ static void WaitForCommissionee(XCTestExpectation * expectation, dispatch_queue_ return mConnectedDevice; } +#ifdef DEBUG +@interface CHIPDevice (Test) +- (void)failSubscribers:(dispatch_queue_t)clientQueue completion:(void (^)(void))completion; +@end +#endif + @interface CHIPDeviceTestPairingDelegate : NSObject @property (nonatomic, strong) XCTestExpectation * expectation; @end @@ -214,9 +220,9 @@ - (void)test001_ReadAttribute XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 29); - XCTAssertEqual([result[@"attributeId"] unsignedIntegerValue], 0); - XCTAssertTrue([result[@"endpointId"] isKindOfClass:[NSNumber class]]); + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.cluster unsignedIntegerValue], 29); + XCTAssertEqual([path.attribute unsignedIntegerValue], 0); XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Array"]); } @@ -256,10 +262,11 @@ - (void)test002_WriteAttribute XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 8); - XCTAssertEqual([result[@"attributeId"] unsignedIntegerValue], 17); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 8); + XCTAssertEqual([path.attribute unsignedIntegerValue], 17); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -281,18 +288,13 @@ - (void)test003_InvokeCommand CHIPDevice * device = GetConnectedDevice(); dispatch_queue_t queue = dispatch_get_main_queue(); - NSDictionary * fields = [NSDictionary - dictionaryWithObjectsAndKeys:@"Structure", @"type", - [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:0], @"tag", - [NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", - [NSNumber numberWithUnsignedInteger:0], @"value", nil], - @"value", nil], - [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:1], @"tag", - [NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", - [NSNumber numberWithUnsignedInteger:10], @"value", nil], - @"value", nil], - nil], - @"value", nil]; + NSDictionary * fields = @{ + @"type" : @"Structure", + @"value" : @[ + @{ @"contextTag" : @0, @"data" : @ { @"type" : @"UnsignedInteger", @"value" : @0 } }, + @{ @"contextTag" : @1, @"data" : @ { @"type" : @"UnsignedInteger", @"value" : @10 } } + ] + }; [device invokeCommandWithEndpointId:1 clusterId:8 commandId:4 @@ -307,10 +309,11 @@ - (void)test003_InvokeCommand XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 8); - XCTAssertEqual([result[@"commandId"] unsignedIntegerValue], 4); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 8); + XCTAssertEqual([path.command unsignedIntegerValue], 4); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -397,14 +400,15 @@ - (void)test005_Subscribe XCTestExpectation * reportExpectation = [self expectationWithDescription:@"report received"]; globalReportHandler = ^(id _Nullable value, NSError * _Nullable error) { XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); - XCTAssertTrue([value isKindOfClass:[NSDictionary class]]); - NSDictionary * result = value; - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"attributeId"] unsignedIntegerValue], 0); - XCTAssertTrue([result[@"value"] isKindOfClass:[NSDictionary class]]); - XCTAssertTrue([result[@"value"][@"type"] isEqualToString:@"Boolean"]); - if ([result[@"value"][@"value"] boolValue] == YES) { + XCTAssertTrue([value isKindOfClass:[NSArray class]]); + NSDictionary * result = value[0]; + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.attribute unsignedIntegerValue], 0); + XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Boolean"]); + if ([result[@"data"][@"value"] boolValue] == YES) { [reportExpectation fulfill]; globalReportHandler = nil; } @@ -412,7 +416,7 @@ - (void)test005_Subscribe // Send commands to trigger attribute change XCTestExpectation * commandExpectation = [self expectationWithDescription:@"command responded"]; - NSDictionary * fields = [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type", [NSArray array], @"value", nil]; + NSDictionary * fields = @{ @"type" : @"Structure", @"value" : [NSArray array] }; [device invokeCommandWithEndpointId:1 clusterId:6 commandId:1 @@ -427,10 +431,11 @@ - (void)test005_Subscribe XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"commandId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.command unsignedIntegerValue], 1); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -445,14 +450,15 @@ - (void)test005_Subscribe reportExpectation = [self expectationWithDescription:@"receive OnOff attribute report"]; globalReportHandler = ^(id _Nullable value, NSError * _Nullable error) { XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); - XCTAssertTrue([value isKindOfClass:[NSDictionary class]]); - NSDictionary * result = value; - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"attributeId"] unsignedIntegerValue], 0); - XCTAssertTrue([result[@"value"] isKindOfClass:[NSDictionary class]]); - XCTAssertTrue([result[@"value"][@"type"] isEqualToString:@"Boolean"]); - if ([result[@"value"][@"value"] boolValue] == NO) { + XCTAssertTrue([value isKindOfClass:[NSArray class]]); + NSDictionary * result = value[0]; + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.attribute unsignedIntegerValue], 0); + XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Boolean"]); + if ([result[@"data"][@"value"] boolValue] == NO) { [reportExpectation fulfill]; globalReportHandler = nil; } @@ -474,10 +480,11 @@ - (void)test005_Subscribe XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"commandId"] unsignedIntegerValue], 0); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.command unsignedIntegerValue], 0); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -485,6 +492,13 @@ - (void)test005_Subscribe // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; + + expectation = [self expectationWithDescription:@"Report handler deregistered"]; + [device deregisterReportHandlersWithClientQueue:queue + completion:^{ + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:kTimeoutInSeconds]; } #endif @@ -664,8 +678,9 @@ - (void)test010_ReadAllAttribute XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 29); - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.cluster unsignedIntegerValue], 29); + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); } XCTAssertTrue([resultArray count] > 0); @@ -807,10 +822,11 @@ - (void)test011_ReadCachedAttribute NSLog(@"Read attribute cache value: %@, error %@", values, error); XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); XCTAssertEqual([values count], 1); - XCTAssertEqual([values[0][@"endpointId"] unsignedShortValue], 1); - XCTAssertEqual([values[0][@"clusterId"] unsignedLongValue], 6); - XCTAssertEqual([values[0][@"attributeId"] unsignedLongValue], 0); - XCTAssertEqual([values[0][@"status"] intValue], 0); + CHIPAttributePath * path = values[0][@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedShortValue], 1); + XCTAssertEqual([path.cluster unsignedLongValue], 6); + XCTAssertEqual([path.attribute unsignedLongValue], 0); + XCTAssertNil(values[0][@"error"]); XCTAssertTrue([values[0][@"data"][@"type"] isEqualToString:@"Boolean"]); XCTAssertEqual([values[0][@"data"][@"value"] boolValue], NO); [cacheExpectation fulfill]; @@ -830,9 +846,10 @@ - (void)test011_ReadCachedAttribute XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); XCTAssertTrue([values count] > 0); for (NSDictionary * value in values) { - XCTAssertEqual([value[@"clusterId"] unsignedLongValue], 6); - XCTAssertEqual([value[@"attributeId"] unsignedLongValue], 0); - XCTAssertEqual([value[@"status"] intValue], 0); + CHIPAttributePath * path = value[@"attributePath"]; + XCTAssertEqual([path.cluster unsignedLongValue], 6); + XCTAssertEqual([path.attribute unsignedLongValue], 0); + XCTAssertNil(value[@"error"]); } [cacheExpectation fulfill]; }]; @@ -851,8 +868,9 @@ - (void)test011_ReadCachedAttribute XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); XCTAssertTrue([values count] > 0); for (NSDictionary * value in values) { - XCTAssertEqual([value[@"endpointId"] unsignedShortValue], 1); - XCTAssertEqual([value[@"attributeId"] unsignedLongValue], 0); + CHIPAttributePath * path = value[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedShortValue], 1); + XCTAssertEqual([path.attribute unsignedLongValue], 0); } [cacheExpectation fulfill]; }]; @@ -871,9 +889,10 @@ - (void)test011_ReadCachedAttribute XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); XCTAssertTrue([values count] > 0); for (NSDictionary * value in values) { - XCTAssertEqual([value[@"endpointId"] unsignedShortValue], 1); - XCTAssertEqual([value[@"clusterId"] unsignedLongValue], 6); - XCTAssertEqual([value[@"status"] intValue], 0); + CHIPAttributePath * path = value[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedShortValue], 1); + XCTAssertEqual([path.cluster unsignedLongValue], 6); + XCTAssertNil(value[@"error"]); } [cacheExpectation fulfill]; }]; @@ -895,9 +914,114 @@ - (void)test011_ReadCachedAttribute [self waitForExpectations:@[ cacheExpectation ] timeout:kTimeoutInSeconds]; } -// Report behavior is erratic on the accessory side at the moment. -// Hence this test is enabled only for individual manual test. +#ifdef DEBUG +// Test an error to subscription +- (void)test012_SubscriptionError +{ #if MANUAL_INDIVIDUAL_TEST + [self initStack]; + [self waitForCommissionee]; +#endif + CHIPDevice * device = GetConnectedDevice(); + dispatch_queue_t queue = dispatch_get_main_queue(); + XCTestExpectation * deregisterExpectation = [self expectationWithDescription:@"Report handler deregistered"]; + [device deregisterReportHandlersWithClientQueue:queue + completion:^{ + [deregisterExpectation fulfill]; + }]; + [self waitForExpectations:@[ deregisterExpectation ] timeout:kTimeoutInSeconds]; + + // Subscribe + XCTestExpectation * expectation = [self expectationWithDescription:@"subscribe OnOff attribute"]; + [device subscribeAttributeWithEndpointId:1 + clusterId:6 + attributeId:0 + minInterval:1 + maxInterval:10 + clientQueue:queue + reportHandler:^(id _Nullable values, NSError * _Nullable error) { + NSLog(@"report attribute: OnOff values: %@, error: %@", values, error); + + if (globalReportHandler) { + __auto_type callback = globalReportHandler; + callback(values, error); + } + } + subscriptionEstablished:^{ + NSLog(@"subscribe attribute: OnOff established"); + [expectation fulfill]; + }]; + + // Wait till establishment + [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:kTimeoutInSeconds]; + + // Set up expectation for report + XCTestExpectation * reportExpectation = [self expectationWithDescription:@"report received"]; + globalReportHandler = ^(id _Nullable value, NSError * _Nullable error) { + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); + XCTAssertTrue([value isKindOfClass:[NSArray class]]); + NSDictionary * result = value[0]; + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.attribute unsignedIntegerValue], 0); + XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Boolean"]); + if ([result[@"data"][@"value"] boolValue] == YES) { + [reportExpectation fulfill]; + globalReportHandler = nil; + } + }; + + // Send commands to trigger attribute change + XCTestExpectation * commandExpectation = [self expectationWithDescription:@"command responded"]; + NSDictionary * fields = @{ @"type" : @"Structure", @"value" : [NSArray array] }; + [device invokeCommandWithEndpointId:1 + clusterId:6 + commandId:1 + commandFields:fields + clientQueue:queue + completion:^(id _Nullable values, NSError * _Nullable error) { + NSLog(@"invoke command: On values: %@, error: %@", values, error); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); + + { + XCTAssertTrue([values isKindOfClass:[NSArray class]]); + NSArray * resultArray = values; + for (NSDictionary * result in resultArray) { + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.command unsignedIntegerValue], 1); + XCTAssertNil(result[@"error"]); + } + XCTAssertEqual([resultArray count], 1); + } + [commandExpectation fulfill]; + }]; + [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds]; + + // Wait for report + [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; + + // Trigger reader failure + XCTestExpectation * failureExpectation = [self expectationWithDescription:@"failed on purpose"]; + [device failSubscribers:queue + completion:^{ + [failureExpectation fulfill]; + }]; + [self waitForExpectations:@[ failureExpectation ] timeout:kTimeoutInSeconds]; + + deregisterExpectation = [self expectationWithDescription:@"Report handler deregistered"]; + [device deregisterReportHandlersWithClientQueue:queue + completion:^{ + [deregisterExpectation fulfill]; + }]; + [self waitForExpectations:@[ deregisterExpectation ] timeout:kTimeoutInSeconds]; +} +#endif + - (void)test900_SubscribeAllAttributes { #if MANUAL_INDIVIDUAL_TEST @@ -906,6 +1030,14 @@ - (void)test900_SubscribeAllAttributes #endif CHIPDevice * device = GetConnectedDevice(); dispatch_queue_t queue = dispatch_get_main_queue(); + XCTestExpectation * cleanSubscriptionExpectation = [self expectationWithDescription:@"Previous subscriptions cleaned"]; + NSLog(@"Deregistering report handlers..."); + [device deregisterReportHandlersWithClientQueue:queue + completion:^{ + NSLog(@"Report handlers deregistered"); + [cleanSubscriptionExpectation fulfill]; + }]; + [self waitForExpectations:@[ cleanSubscriptionExpectation ] timeout:kTimeoutInSeconds]; XCTestExpectation * expectation = [self expectationWithDescription:@"subscribe OnOff attribute"]; __block void (^reportHandler)(id _Nullable values, NSError * _Nullable error) = nil; @@ -935,23 +1067,53 @@ - (void)test900_SubscribeAllAttributes // Set up expectation for report __auto_type reportExpectation = [self expectationWithDescription:@"receive OnOff attribute report"]; - reportHandler = ^(id _Nullable value, NSError * _Nullable error) { + reportHandler = ^(id _Nullable values, NSError * _Nullable error) { XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); - XCTAssertTrue([value isKindOfClass:[NSDictionary class]]); - NSDictionary * result = value; - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertTrue([result[@"value"] isKindOfClass:[NSDictionary class]]); - - if ([result[@"attributeId"] unsignedIntegerValue] == 0 && [result[@"value"][@"value"] boolValue] == YES) { + XCTAssertTrue([values isKindOfClass:[NSArray class]]); + NSDictionary * result = values[0]; + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); + + if ([path.attribute unsignedIntegerValue] == 0 && [result[@"data"][@"value"] boolValue] == YES) { [reportExpectation fulfill]; reportHandler = nil; } }; - // Send commands to trigger attribute change + // Send commands to set attribute state to a known state XCTestExpectation * commandExpectation = [self expectationWithDescription:@"command responded"]; - NSDictionary * fields = [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type", [NSArray array], @"value", nil]; + NSDictionary * fields = @{ @"type" : @"Structure", @"value" : @[] }; + [device invokeCommandWithEndpointId:1 + clusterId:6 + commandId:0 + commandFields:fields + clientQueue:queue + completion:^(id _Nullable values, NSError * _Nullable error) { + NSLog(@"invoke command: On values: %@, error: %@", values, error); + + XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); + + { + XCTAssertTrue([values isKindOfClass:[NSArray class]]); + NSArray * resultArray = values; + for (NSDictionary * result in resultArray) { + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.command unsignedIntegerValue], 0); + XCTAssertNil(result[@"error"]); + } + XCTAssertEqual([resultArray count], 1); + } + [commandExpectation fulfill]; + }]; + [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds]; + + // Send commands to trigger attribute change + commandExpectation = [self expectationWithDescription:@"command responded"]; + fields = @{ @"type" : @"Structure", @"value" : @[] }; [device invokeCommandWithEndpointId:1 clusterId:6 commandId:1 @@ -966,10 +1128,11 @@ - (void)test900_SubscribeAllAttributes XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"commandId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.command unsignedIntegerValue], 1); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -982,14 +1145,15 @@ - (void)test900_SubscribeAllAttributes // Set up expectation for 2nd report reportExpectation = [self expectationWithDescription:@"receive OnOff attribute report"]; - reportHandler = ^(id _Nullable value, NSError * _Nullable error) { + reportHandler = ^(id _Nullable values, NSError * _Nullable error) { XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); - XCTAssertTrue([value isKindOfClass:[NSDictionary class]]); - NSDictionary * result = value; - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertTrue([result[@"value"] isKindOfClass:[NSDictionary class]]); - if ([result[@"attributeId"] unsignedIntegerValue] == 0 && [result[@"value"][@"value"] boolValue] == NO) { + XCTAssertTrue([values isKindOfClass:[NSArray class]]); + NSDictionary * result = values[0]; + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); + if ([path.attribute unsignedIntegerValue] == 0 && [result[@"data"][@"value"] boolValue] == NO) { [reportExpectation fulfill]; reportHandler = nil; } @@ -997,7 +1161,7 @@ - (void)test900_SubscribeAllAttributes // Send command to trigger attribute change commandExpectation = [self expectationWithDescription:@"command responded"]; - fields = [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type", [NSArray array], @"value", nil]; + fields = @{ @"type" : @"Structure", @"value" : @[] }; [device invokeCommandWithEndpointId:1 clusterId:6 commandId:0 @@ -1012,10 +1176,11 @@ - (void)test900_SubscribeAllAttributes XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"commandId"] unsignedIntegerValue], 0); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.command unsignedIntegerValue], 0); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -1026,7 +1191,6 @@ - (void)test900_SubscribeAllAttributes // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; } -#endif #if !MANUAL_INDIVIDUAL_TEST - (void)test999_TearDown @@ -1058,6 +1222,17 @@ - (void)testSignedInteger XCTAssertTrue([output isEqualTo:input]); } +- (void)testSignedInteger64Bits +{ + NSDictionary * input = [NSDictionary + dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:-0x7000111122223333ll], @"value", nil]; + id output = [CHIPDevice CHIPEncodeAndDecodeNSObject:input]; + NSLog(@"Conversion input: %@\nOutput: %@", input, output); + XCTAssertNotNil(output); + XCTAssertTrue([output isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([output isEqualTo:input]); +} + - (void)testUnsignedInteger { NSDictionary * input = @@ -1069,6 +1244,17 @@ - (void)testUnsignedInteger XCTAssertTrue([output isEqualTo:input]); } +- (void)testUnsignedInteger64Bits +{ + NSDictionary * input = [NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", + [NSNumber numberWithUnsignedLongLong:0xCCCCDDDDEEEEFFFFull], @"value", nil]; + id output = [CHIPDevice CHIPEncodeAndDecodeNSObject:input]; + NSLog(@"Conversion input: %@\nOutput: %@", input, output); + XCTAssertNotNil(output); + XCTAssertTrue([output isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([output isEqualTo:input]); +} + - (void)testBoolean { NSDictionary * input = @@ -1110,9 +1296,8 @@ - (void)testFloat NSLog(@"Conversion input: %@\nOutput: %@", input, output); XCTAssertNotNil(output); XCTAssertTrue([output isKindOfClass:[NSDictionary class]]); - // Note that conversion doesn't guarantee back to Float. - XCTAssertTrue([output[@"type"] isEqualToString:@"Float"] || [output[@"type"] isEqualToString:@"Double"]); - XCTAssertTrue(([output[@"value"] doubleValue] - [input[@"value"] doubleValue]) < 0.0001); + XCTAssertTrue([output[@"type"] isEqualToString:@"Float"]); + XCTAssertTrue(([output[@"value"] floatValue] - [input[@"value"] floatValue]) < 0.0001); } - (void)testDouble @@ -1123,8 +1308,7 @@ - (void)testDouble NSLog(@"Conversion input: %@\nOutput: %@", input, output); XCTAssertNotNil(output); XCTAssertTrue([output isKindOfClass:[NSDictionary class]]); - // Note that conversion doesn't guarantee back to Double. - XCTAssertTrue([output[@"type"] isEqualToString:@"Float"] || [output[@"type"] isEqualToString:@"Double"]); + XCTAssertTrue([output[@"type"] isEqualToString:@"Double"]); XCTAssertTrue(([output[@"value"] doubleValue] - [input[@"value"] doubleValue]) < 0.0001); } @@ -1140,98 +1324,38 @@ - (void)testNull - (void)testStructure { - NSArray * inputFields = [NSArray - arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:1], @"tag", - [NSDictionary dictionaryWithObjectsAndKeys:@"Boolean", @"type", [NSNumber numberWithBool:NO], - @"value", nil], - @"value", nil], - [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:2], @"tag", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:5], @"value", nil], - @"value", nil], - nil]; - NSDictionary * inputValue = [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type", inputFields, @"value", nil]; - - // Output will have context tags and hence build object with context tags for comparison - NSMutableArray * contextTaggedInputFields = [NSMutableArray arrayWithCapacity:[inputFields count]]; - for (NSDictionary * field in inputFields) { - [contextTaggedInputFields - addObject:[NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:([field[@"tag"] unsignedIntegerValue] - | 0xffffffff00000000ull)], - @"tag", field[@"value"], @"value", nil]]; - } + NSArray * inputFields = @[ + @{ + @"contextTag" : @1, + @"data" : @ { @"type" : @"Boolean", @"value" : @NO }, + }, + @{ + @"contextTag" : @2, + @"data" : @ { @"type" : @"SignedInteger", @"value" : @5 }, + } + ]; + NSDictionary * inputValue = @{ @"type" : @"Structure", @"value" : inputFields }; id output = [CHIPDevice CHIPEncodeAndDecodeNSObject:inputValue]; NSLog(@"Conversion input: %@\nOutput: %@", inputValue, output); XCTAssertNotNil(output); XCTAssertTrue([output isKindOfClass:[NSDictionary class]]); - NSDictionary * compare = - [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type", contextTaggedInputFields, @"value", nil]; - XCTAssertTrue([output isEqualTo:compare]); + XCTAssertTrue([output isEqualTo:inputValue]); } - (void)testArray { - NSArray * inputFields = [NSArray - arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:[NSDictionary dictionaryWithObjectsAndKeys:@"Boolean", @"type", - [NSNumber numberWithBool:NO], @"value", nil], - @"value", nil], - [NSDictionary dictionaryWithObjectsAndKeys:[NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:5], @"value", nil], - @"value", nil], - nil]; - NSDictionary * inputValue = [NSDictionary dictionaryWithObjectsAndKeys:@"Array", @"type", inputFields, @"value", nil]; - - // Output will have anonymous tags and hence build object with context tags for comparison - NSMutableArray * contextTaggedInputFields = [NSMutableArray arrayWithCapacity:[inputFields count]]; - for (NSDictionary * field in inputFields) { - [contextTaggedInputFields - addObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:0xffffffffffffffffull], @"tag", - field[@"value"], @"value", nil]]; - } + NSArray * inputFields = @[ + @{ @"data" : @ { @"type" : @"Boolean", @"value" : @NO } }, @{ @"data" : @ { @"type" : @"SignedInteger", @"value" : @5 } } + ]; + NSDictionary * inputValue = @{ @"type" : @"Array", @"value" : inputFields }; id output = [CHIPDevice CHIPEncodeAndDecodeNSObject:inputValue]; NSLog(@"Conversion input: %@\nOutput: %@", inputValue, output); XCTAssertNotNil(output); XCTAssertTrue([output isKindOfClass:[NSDictionary class]]); - - NSDictionary * compare = [NSDictionary dictionaryWithObjectsAndKeys:@"Array", @"type", contextTaggedInputFields, @"value", nil]; - XCTAssertTrue([output isEqualTo:compare]); -} - -- (void)testList -{ - NSArray * inputFields = [NSArray - arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:1], @"tag", - [NSDictionary dictionaryWithObjectsAndKeys:@"Boolean", @"type", [NSNumber numberWithBool:NO], - @"value", nil], - @"value", nil], - [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:2], @"tag", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:5], @"value", nil], - @"value", nil], - nil]; - NSDictionary * inputValue = [NSDictionary dictionaryWithObjectsAndKeys:@"List", @"type", inputFields, @"value", nil]; - - // Output will have context tags and hence build object with context tags for comparison - NSMutableArray * contextTaggedInputFields = [NSMutableArray arrayWithCapacity:[inputFields count]]; - for (NSDictionary * field in inputFields) { - [contextTaggedInputFields - addObject:[NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:([field[@"tag"] unsignedIntegerValue] - | 0xffffffff00000000ull)], - @"tag", field[@"value"], @"value", nil]]; - } - - id output = [CHIPDevice CHIPEncodeAndDecodeNSObject:inputValue]; - NSLog(@"Conversion input: %@\nOutput: %@", inputValue, output); - XCTAssertNotNil(output); - XCTAssertTrue([output isKindOfClass:[NSDictionary class]]); - - NSDictionary * compare = [NSDictionary dictionaryWithObjectsAndKeys:@"List", @"type", contextTaggedInputFields, @"value", nil]; - XCTAssertTrue([output isEqualTo:compare]); + XCTAssertTrue([output isEqualTo:inputValue]); } @end diff --git a/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m b/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m index d62a4eee562962..c4fbd2a5b99b4d 100644 --- a/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPXPCListenerSampleTests.m @@ -162,20 +162,24 @@ - (void)readAttributeWithController:(id)controller (void) controller; __auto_type sharedController = [CHIPDeviceController sharedController]; if (sharedController) { - [sharedController getConnectedDevice:nodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - completion(nil, error); - } else { - [device readAttributeWithEndpointId:endpointId - clusterId:clusterId - attributeId:attributeId - clientQueue:dispatch_get_main_queue() - completion:completion]; - } - }]; + [sharedController + getConnectedDevice:nodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + if (error) { + NSLog(@"Failed to get connected device"); + completion(nil, error); + } else { + [device readAttributeWithEndpointId:endpointId + clusterId:clusterId + attributeId:attributeId + clientQueue:dispatch_get_main_queue() + completion:^(NSArray *> * _Nullable values, + NSError * _Nullable error) { + completion([CHIPDeviceController encodeXPCResponseValues:values], error); + }]; + } + }]; } else { NSLog(@"Failed to get shared controller"); completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); @@ -193,21 +197,25 @@ - (void)writeAttributeWithController:(id)controller (void) controller; __auto_type sharedController = [CHIPDeviceController sharedController]; if (sharedController) { - [sharedController getConnectedDevice:nodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - completion(nil, error); - } else { - [device writeAttributeWithEndpointId:endpointId - clusterId:clusterId - attributeId:attributeId - value:value - clientQueue:dispatch_get_main_queue() - completion:completion]; - } - }]; + [sharedController + getConnectedDevice:nodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + if (error) { + NSLog(@"Failed to get connected device"); + completion(nil, error); + } else { + [device writeAttributeWithEndpointId:endpointId + clusterId:clusterId + attributeId:attributeId + value:value + clientQueue:dispatch_get_main_queue() + completion:^(NSArray *> * _Nullable values, + NSError * _Nullable error) { + completion([CHIPDeviceController encodeXPCResponseValues:values], error); + }]; + } + }]; } else { NSLog(@"Failed to get shared controller"); completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); @@ -225,21 +233,25 @@ - (void)invokeCommandWithController:(id)controller (void) controller; __auto_type sharedController = [CHIPDeviceController sharedController]; if (sharedController) { - [sharedController getConnectedDevice:nodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - completion(nil, error); - } else { - [device invokeCommandWithEndpointId:endpointId - clusterId:clusterId - commandId:commandId - commandFields:fields - clientQueue:dispatch_get_main_queue() - completion:completion]; - } - }]; + [sharedController + getConnectedDevice:nodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + if (error) { + NSLog(@"Failed to get connected device"); + completion(nil, error); + } else { + [device invokeCommandWithEndpointId:endpointId + clusterId:clusterId + commandId:commandId + commandFields:fields + clientQueue:dispatch_get_main_queue() + completion:^(NSArray *> * _Nullable values, + NSError * _Nullable error) { + completion([CHIPDeviceController encodeXPCResponseValues:values], error); + }]; + } + }]; } else { NSLog(@"Failed to get shared controller"); completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); @@ -257,43 +269,45 @@ - (void)subscribeAttributeWithController:(id)controller { __auto_type sharedController = [CHIPDeviceController sharedController]; if (sharedController) { - [sharedController getConnectedDevice:nodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - if (error) { - NSLog(@"Failed to get connected device"); - establishedHandler(); - // Send an error report so that the client knows of the failure - [self.clientProxy handleReportWithController:controller - nodeId:nodeId - value:nil - error:[NSError errorWithDomain:CHIPErrorDomain - code:CHIPErrorCodeGeneralError - userInfo:nil]]; - } else { - [device subscribeAttributeWithEndpointId:endpointId - clusterId:clusterId - attributeId:attributeId - minInterval:minInterval - maxInterval:maxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, - NSError * _Nullable error) { - [self.clientProxy handleReportWithController:controller - nodeId:nodeId - value:value - error:error]; - } - subscriptionEstablished:establishedHandler]; - } - }]; + [sharedController + getConnectedDevice:nodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + if (error) { + NSLog(@"Failed to get connected device"); + establishedHandler(); + // Send an error report so that the client knows of the failure + [self.clientProxy handleReportWithController:controller + nodeId:nodeId + values:nil + error:[NSError errorWithDomain:CHIPErrorDomain + code:CHIPErrorCodeGeneralError + userInfo:nil]]; + } else { + [device subscribeAttributeWithEndpointId:endpointId + clusterId:clusterId + attributeId:attributeId + minInterval:minInterval + maxInterval:maxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, + NSError * _Nullable error) { + [self.clientProxy handleReportWithController:controller + nodeId:nodeId + values:[CHIPDeviceController + encodeXPCResponseValues:values] + error:error]; + } + subscriptionEstablished:establishedHandler]; + } + }]; } else { NSLog(@"Failed to get shared controller"); establishedHandler(); // Send an error report so that the client knows of the failure [self.clientProxy handleReportWithController:controller nodeId:nodeId - value:nil + values:nil error:[NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]]; @@ -337,11 +351,14 @@ - (void)readAttributeCacheWithController:(id _Nullable)controller { CHIPAttributeCacheContainer * attributeCacheContainer = _attributeCacheDictionary[[NSNumber numberWithUnsignedLongLong:nodeId]]; if (attributeCacheContainer) { - [attributeCacheContainer readAttributeWithEndpointId:endpointId - clusterId:clusterId - attributeId:attributeId - clientQueue:dispatch_get_main_queue() - completion:completion]; + [attributeCacheContainer + readAttributeWithEndpointId:endpointId + clusterId:clusterId + attributeId:attributeId + clientQueue:dispatch_get_main_queue() + completion:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + completion([CHIPDeviceController encodeXPCResponseValues:values], error); + }]; } else { NSLog(@"Attribute cache for node ID %llu was not setup", nodeId); completion(nil, [NSError errorWithDomain:CHIPErrorDomain code:CHIPErrorCodeGeneralError userInfo:nil]); @@ -547,9 +564,9 @@ - (void)test001_ReadAttribute XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 29); - XCTAssertEqual([result[@"attributeId"] unsignedIntegerValue], 0); - XCTAssertTrue([result[@"endpointId"] isKindOfClass:[NSNumber class]]); + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.cluster unsignedIntegerValue], 29); + XCTAssertEqual([path.attribute unsignedIntegerValue], 0); XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Array"]); } @@ -589,10 +606,11 @@ - (void)test002_WriteAttribute XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 8); - XCTAssertEqual([result[@"attributeId"] unsignedIntegerValue], 17); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 8); + XCTAssertEqual([path.attribute unsignedIntegerValue], 17); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -614,18 +632,13 @@ - (void)test003_InvokeCommand CHIPDevice * device = GetConnectedDevice(); dispatch_queue_t queue = dispatch_get_main_queue(); - NSDictionary * fields = [NSDictionary - dictionaryWithObjectsAndKeys:@"Structure", @"type", - [NSArray arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:0], @"tag", - [NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", - [NSNumber numberWithUnsignedInteger:0], @"value", nil], - @"value", nil], - [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:1], @"tag", - [NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", - [NSNumber numberWithUnsignedInteger:10], @"value", nil], - @"value", nil], - nil], - @"value", nil]; + NSDictionary * fields = @{ + @"type" : @"Structure", + @"value" : @[ + @{ @"contextTag" : @0, @"data" : @ { @"type" : @"UnsignedInteger", @"value" : @0 } }, + @{ @"contextTag" : @1, @"data" : @ { @"type" : @"UnsignedInteger", @"value" : @10 } } + ] + }; [device invokeCommandWithEndpointId:1 clusterId:8 commandId:4 @@ -640,10 +653,11 @@ - (void)test003_InvokeCommand XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 8); - XCTAssertEqual([result[@"commandId"] unsignedIntegerValue], 4); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 8); + XCTAssertEqual([path.command unsignedIntegerValue], 4); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -692,18 +706,18 @@ - (void)test004_Subscribe // Set up expectation for report expectation = [self expectationWithDescription:@"receive OnOff attribute report"]; - globalReportHandler = ^(id _Nullable value, NSError * _Nullable error) { + globalReportHandler = ^(id _Nullable values, NSError * _Nullable error) { XCTAssertEqual([CHIPErrorTestUtils errorToZCLErrorCode:error], 0); - - { - XCTAssertTrue([value isKindOfClass:[NSDictionary class]]); - NSDictionary * result = value; - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"attributeId"] unsignedIntegerValue], 0); - XCTAssertTrue([result[@"value"] isKindOfClass:[NSDictionary class]]); - XCTAssertTrue([result[@"value"][@"type"] isEqualToString:@"Boolean"]); - XCTAssertEqual([result[@"value"][@"value"] boolValue], YES); + XCTAssertTrue([values isKindOfClass:[NSArray class]]); + + for (NSDictionary * result in values) { + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.attribute unsignedIntegerValue], 0); + XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); + XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Boolean"]); + XCTAssertEqual([result[@"data"][@"value"] boolValue], YES); } [expectation fulfill]; }; @@ -724,10 +738,11 @@ - (void)test004_Subscribe XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"commandId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.command unsignedIntegerValue], 1); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -851,8 +866,8 @@ - (void)test008_SubscribeFailure // Set up expectation for report XCTestExpectation * errorReportExpectation = [self expectationWithDescription:@"receive OnOff attribute report"]; - globalReportHandler = ^(id _Nullable value, NSError * _Nullable error) { - XCTAssertNil(value); + globalReportHandler = ^(id _Nullable values, NSError * _Nullable error) { + XCTAssertNil(values); // Error is copied over XPC and hence cannot use CHIPErrorTestUtils utility which checks against a local domain string // object. XCTAssertTrue([error.domain isEqualToString:MatterInteractionErrorDomain]); @@ -934,10 +949,11 @@ - (void)test009_SubscribeAttributeCache XCTAssertTrue([values isKindOfClass:[NSArray class]]); NSArray * resultArray = values; for (NSDictionary * result in resultArray) { - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"commandId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"status"] unsignedIntegerValue], 0); + CHIPCommandPath * path = result[@"commandPath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.command unsignedIntegerValue], 1); + XCTAssertNil(result[@"error"]); } XCTAssertEqual([resultArray count], 1); } @@ -960,9 +976,10 @@ - (void)test009_SubscribeAttributeCache for (NSDictionary * value in values) { XCTAssertTrue([value isKindOfClass:[NSDictionary class]]); NSDictionary * result = value; - XCTAssertEqual([result[@"endpointId"] unsignedIntegerValue], 1); - XCTAssertEqual([result[@"clusterId"] unsignedIntegerValue], 6); - XCTAssertEqual([result[@"attributeId"] unsignedIntegerValue], 0); + CHIPAttributePath * path = result[@"attributePath"]; + XCTAssertEqual([path.endpoint unsignedIntegerValue], 1); + XCTAssertEqual([path.cluster unsignedIntegerValue], 6); + XCTAssertEqual([path.attribute unsignedIntegerValue], 0); XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]); XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Boolean"]); XCTAssertEqual([result[@"data"][@"value"] boolValue], YES); diff --git a/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m b/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m index fea62527b4eecd..85356cb9e68a3c 100644 --- a/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m +++ b/src/darwin/Framework/CHIPTests/CHIPXPCProtocolTests.m @@ -34,6 +34,48 @@ // Inverted expectation timeout static const uint16_t kNegativeTimeoutInSeconds = 1; +@interface CHIPAttributePath (Test) +- (BOOL)isEqual:(id)object; +@end + +@implementation CHIPAttributePath (Test) +- (BOOL)isEqual:(id)object +{ + if ([object isKindOfClass:[CHIPAttributePath class]]) { + CHIPAttributePath * other = object; + return [self.endpoint isEqualToNumber:other.endpoint] && [self.cluster isEqualToNumber:other.cluster] && + [self.attribute isEqualToNumber:other.attribute]; + } + return NO; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"CHIPAttributePath(%@,%@,%@)", self.endpoint, self.cluster, self.attribute]; +} +@end + +@interface CHIPCommandPath (Test) +- (BOOL)isEqual:(id)object; +@end + +@implementation CHIPCommandPath (Test) +- (BOOL)isEqual:(id)object +{ + if ([object isKindOfClass:[CHIPCommandPath class]]) { + CHIPCommandPath * other = object; + return [self.endpoint isEqualToNumber:other.endpoint] && [self.cluster isEqualToNumber:other.cluster] && + [self.command isEqualToNumber:other.command]; + } + return NO; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"CHIPCommandPath(%@,%@,%@)", self.endpoint, self.cluster, self.command]; +} +@end + @interface CHIPXPCProtocolTests : XCTestCase @property (nonatomic, readwrite, strong) NSXPCListener * xpcListener; @@ -185,6 +227,8 @@ - (void)readAttributeCacheWithController:(id _Nullable)controller - (void)setUp { + [self setContinueAfterFailure:NO]; + _xpcListener = [NSXPCListener anonymousListener]; [_xpcListener setDelegate:(id) self]; _serviceInterface = [NSXPCInterface interfaceWithProtocol:@protocol(CHIPDeviceControllerServerProtocol)]; @@ -212,13 +256,13 @@ - (void)testReadAttributeSuccess NSUInteger myEndpointId = 100; NSUInteger myClusterId = 200; NSUInteger myAttributeId = 300; - NSArray * myValues = - [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], - @"endpointId", [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", - [NSNumber numberWithUnsignedInteger:myAttributeId], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]]; + NSArray * myValues = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; + XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"]; @@ -231,7 +275,7 @@ - (void)testReadAttributeSuccess XCTAssertEqual(clusterId, myClusterId); XCTAssertEqual(attributeId, myAttributeId); [callExpectation fulfill]; - completion(myValues, nil); + completion([CHIPDeviceController encodeXPCResponseValues:myValues], nil); }; [_remoteDeviceController getConnectedDevice:myNodeId @@ -319,11 +363,12 @@ - (void)testWriteAttributeSuccess NSUInteger myAttributeId = 300; NSDictionary * myValue = [NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", [NSNumber numberWithInteger:654321], @"value", nil]; - NSArray * myResults = - [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], - @"endpointId", [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", - [NSNumber numberWithUnsignedInteger:myAttributeId], @"attributeId", - [NSNumber numberWithInteger:0], @"status", nil]]; + NSArray * myResults = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]] + } ]; + XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"]; @@ -337,7 +382,7 @@ - (void)testWriteAttributeSuccess XCTAssertEqual(attributeId, myAttributeId); XCTAssertTrue([value isEqualTo:myValue]); [callExpectation fulfill]; - completion(myResults, nil); + completion([CHIPDeviceController encodeXPCResponseValues:myResults], nil); }; [_remoteDeviceController getConnectedDevice:myNodeId @@ -432,11 +477,11 @@ - (void)testInvokeCommandSuccess [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Float", @"Type", [NSNumber numberWithFloat:1.0], @"value", nil]], @"value", nil]; - NSArray * myResults = - [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], - @"endpointId", [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", - [NSNumber numberWithUnsignedInteger:myCommandId], @"commandId", - [NSNumber numberWithInteger:0], @"status", nil]]; + NSArray * myResults = @[ @{ + @"commandPath" : [CHIPCommandPath commandPathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + commandId:[NSNumber numberWithUnsignedInteger:myCommandId]] + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"]; @@ -450,7 +495,7 @@ - (void)testInvokeCommandSuccess XCTAssertEqual(commandId, myCommandId); XCTAssertTrue([commandFields isEqualTo:myFields]); [callExpectation fulfill]; - completion(myResults, nil); + completion([CHIPDeviceController encodeXPCResponseValues:myResults], nil); }; [_remoteDeviceController getConnectedDevice:myNodeId @@ -545,12 +590,12 @@ - (void)testSubscribeAttributeSuccess NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - __block NSDictionary * myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]; + __block NSArray * myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Report sent"]; @@ -571,48 +616,55 @@ - (void)testSubscribeAttributeSuccess _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:myEndpointId - clusterId:myClusterId - attributeId:myAttributeId - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:myEndpointId + clusterId:myClusterId + attributeId:myAttributeId + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"2nd report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -641,14 +693,14 @@ - (void)testBadlyFormattedReport NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - // Incorrect report value. Report must be a single NSDictionary - __block id myReport = - [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], - @"endpointId", [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", - [NSNumber numberWithUnsignedInteger:myAttributeId], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]]; + // Incorrect serialized report value. Report should have ben a single NSDictionary + __block id myReport = @{ + @"attributePath" : @[ + [NSNumber numberWithUnsignedInteger:myEndpointId], [NSNumber numberWithUnsignedInteger:myClusterId], + [NSNumber numberWithUnsignedInteger:myAttributeId] + ], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + }; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Unexpected report sent"]; @@ -670,48 +722,52 @@ - (void)testBadlyFormattedReport _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:myEndpointId - clusterId:myClusterId - attributeId:myAttributeId - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:myEndpointId + clusterId:myClusterId + attributeId:myAttributeId + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject badly formatted report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + [clientObject handleReportWithController:uuid nodeId:myNodeId values:myReport error:nil]; // Wait for report, which isn't expected. [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"Report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -741,12 +797,12 @@ - (void)testReportWithUnrelatedEndpointId NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - __block NSDictionary * myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId + 1], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]; + __block NSArray * myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId + 1] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Unexpected report sent"]; @@ -768,48 +824,55 @@ - (void)testReportWithUnrelatedEndpointId _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:myEndpointId - clusterId:myClusterId - attributeId:myAttributeId - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:myEndpointId + clusterId:myClusterId + attributeId:myAttributeId + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report which isn't expected [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"2nd report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -838,12 +901,12 @@ - (void)testReportWithUnrelatedClusterId NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - __block NSDictionary * myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId + 1], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]; + __block NSArray * myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId + 1] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Unexpected report sent"]; @@ -865,48 +928,55 @@ - (void)testReportWithUnrelatedClusterId _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:myEndpointId - clusterId:myClusterId - attributeId:myAttributeId - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:myEndpointId + clusterId:myClusterId + attributeId:myAttributeId + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report not to come [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"2nd report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -935,12 +1005,12 @@ - (void)testReportWithUnrelatedAttributeId NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - __block NSDictionary * myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId + 1], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]; + __block NSArray * myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId + 1]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Unexpected report sent"]; @@ -962,48 +1032,55 @@ - (void)testReportWithUnrelatedAttributeId _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:myEndpointId - clusterId:myClusterId - attributeId:myAttributeId - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:myEndpointId + clusterId:myClusterId + attributeId:myAttributeId + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report not to come [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"2nd report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -1032,12 +1109,12 @@ - (void)testReportWithUnrelatedNode NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - __block NSDictionary * myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]; + __block NSArray * myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Unexpected report sent"]; @@ -1059,48 +1136,55 @@ - (void)testReportWithUnrelatedNode _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:myEndpointId - clusterId:myClusterId - attributeId:myAttributeId - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:myEndpointId + clusterId:myClusterId + attributeId:myAttributeId + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId + 1 value:myReport error:nil]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + 1 + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report not to come [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"2nd report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -1129,12 +1213,12 @@ - (void)testSubscribeMultiEndpoints NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - __block NSDictionary * myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]; + __block NSArray * myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Report sent"]; @@ -1155,48 +1239,55 @@ - (void)testSubscribeMultiEndpoints _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:0xffff - clusterId:myClusterId - attributeId:myAttributeId - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:0xffff + clusterId:myClusterId + attributeId:myAttributeId + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"2nd report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -1225,12 +1316,12 @@ - (void)testSubscribeMultiClusters NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - __block NSDictionary * myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]; + __block NSArray * myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Report sent"]; @@ -1251,48 +1342,55 @@ - (void)testSubscribeMultiClusters _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:myEndpointId - clusterId:0xffffffff - attributeId:myAttributeId - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:myEndpointId + clusterId:0xffffffff + attributeId:myAttributeId + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"2nd report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -1321,12 +1419,12 @@ - (void)testSubscribeMultiAttributes NSUInteger myAttributeId = 300; NSUInteger myMinInterval = 5; NSUInteger myMaxInterval = 60; - __block NSDictionary * myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]; + __block NSArray * myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * establishExpectation = [self expectationWithDescription:@"Established called"]; __block XCTestExpectation * reportExpectation = [self expectationWithDescription:@"Report sent"]; @@ -1347,48 +1445,55 @@ - (void)testSubscribeMultiAttributes _xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"]; - [_remoteDeviceController getConnectedDevice:myNodeId - queue:dispatch_get_main_queue() - completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { - XCTAssertNotNil(device); - XCTAssertNil(error); - NSLog(@"Device acquired. Subscribing..."); - [device subscribeAttributeWithEndpointId:myEndpointId - clusterId:myClusterId - attributeId:0xffffffff - minInterval:myMinInterval - maxInterval:myMaxInterval - clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Report value: %@", value); - XCTAssertNotNil(value); - XCTAssertNil(error); - XCTAssertTrue([myReport isEqualTo:value]); - [reportExpectation fulfill]; - } - subscriptionEstablished:^{ - [establishExpectation fulfill]; - }]; - }]; + [_remoteDeviceController + getConnectedDevice:myNodeId + queue:dispatch_get_main_queue() + completionHandler:^(CHIPDevice * _Nullable device, NSError * _Nullable error) { + XCTAssertNotNil(device); + XCTAssertNil(error); + NSLog(@"Device acquired. Subscribing..."); + [device subscribeAttributeWithEndpointId:myEndpointId + clusterId:myClusterId + attributeId:0xffffffff + minInterval:myMinInterval + maxInterval:myMaxInterval + clientQueue:dispatch_get_main_queue() + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Report value: %@", values); + XCTAssertNotNil(values); + XCTAssertNil(error); + XCTAssertTrue([myReport isEqualTo:values]); + [reportExpectation fulfill]; + } + subscriptionEstablished:^{ + [establishExpectation fulfill]; + }]; + }]; [self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds]; // Inject report id clientObject = _xpcConnection.remoteObjectProxy; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; // Inject another report reportExpectation = [self expectationWithDescription:@"2nd report sent"]; - myReport = [NSDictionary - dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], @"endpointId", - [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", [NSNumber numberWithUnsignedInteger:myAttributeId], - @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:771234], @"value", nil], - @"data", nil]; - [clientObject handleReportWithController:uuid nodeId:myNodeId value:myReport error:nil]; + myReport = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 } + } ]; + [clientObject handleReportWithController:uuid + nodeId:myNodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReport] + error:nil]; // Wait for report [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds]; @@ -1423,7 +1528,7 @@ - (void)testMutiSubscriptions __block NSUInteger myAttributeId = attributeIds[0]; __block NSUInteger myMinInterval = minIntervals[0]; __block NSUInteger myMaxInterval = maxIntervals[0]; - __block NSArray * myReports; + __block NSArray * myReports; __block XCTestExpectation * callExpectation; __block XCTestExpectation * establishExpectation; __block NSArray * reportExpectations; @@ -1467,11 +1572,11 @@ - (void)testMutiSubscriptions minInterval:myMinInterval maxInterval:myMaxInterval clientQueue:dispatch_get_main_queue() - reportHandler:^(NSDictionary * _Nullable value, NSError * _Nullable error) { - NSLog(@"Subscriber [%d] report value: %@", i, value); - XCTAssertNotNil(value); + reportHandler:^(NSArray *> * _Nullable values, NSError * _Nullable error) { + NSLog(@"Subscriber [%d] report value: %@", i, values); + XCTAssertNotNil(values); XCTAssertNil(error); - XCTAssertTrue([myReports[i] isEqualTo:value]); + XCTAssertTrue([myReports[i] isEqualTo:values]); [reportExpectations[i] fulfill]; } subscriptionEstablished:^{ @@ -1490,24 +1595,29 @@ - (void)testMutiSubscriptions arrayWithObjects:[self expectationWithDescription:[NSString stringWithFormat:@"Report(%d) for first subscriber sent", count]], [self expectationWithDescription:[NSString stringWithFormat:@"Report(%d) for second subscriber sent", count]], nil]; - myReports = [NSArray - arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:endpointIds[0]], - @"endpointId", [NSNumber numberWithUnsignedInteger:clusterIds[0]], @"clusterId", - [NSNumber numberWithUnsignedInteger:attributeIds[0]], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:123456 + count * 100], @"value", nil], - @"data", nil], - [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:endpointIds[1]], @"endpointId", - [NSNumber numberWithUnsignedInteger:clusterIds[1]], @"clusterId", - [NSNumber numberWithUnsignedInteger:attributeIds[1]], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:123457 + count * 100], @"value", nil], - @"data", nil], - nil]; + myReports = @[ + @[ @{ + @"attributePath" : + [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:endpointIds[0]] + clusterId:[NSNumber numberWithUnsignedInteger:clusterIds[0]] + attributeId:[NSNumber numberWithUnsignedInteger:attributeIds[0]]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:123456 + count * 100] } + } ], + @[ @{ + @"attributePath" : + [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:endpointIds[1]] + clusterId:[NSNumber numberWithUnsignedInteger:clusterIds[1]] + attributeId:[NSNumber numberWithUnsignedInteger:attributeIds[1]]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:123457 + count * 100] } + } ] + ]; for (int i = 0; i < 2; i++) { NSUInteger nodeId = nodeIds[i]; dispatch_async(dispatch_get_main_queue(), ^{ - [clientObject handleReportWithController:uuid nodeId:nodeId value:myReports[i] error:nil]; + [clientObject handleReportWithController:uuid + nodeId:nodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReports[i]] + error:nil]; }); } [self waitForExpectations:reportExpectations timeout:kTimeoutInSeconds]; @@ -1535,24 +1645,29 @@ - (void)testMutiSubscriptions stringWithFormat:@"Report(%d) for first subscriber sent", count]], [self expectationWithDescription:[NSString stringWithFormat:@"Report(%d) for second subscriber sent", count]], nil]; reportExpectations[0].inverted = YES; - myReports = [NSArray - arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:endpointIds[0]], - @"endpointId", [NSNumber numberWithUnsignedInteger:clusterIds[0]], @"clusterId", - [NSNumber numberWithUnsignedInteger:attributeIds[0]], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:223456 + count * 100], @"value", nil], - @"data", nil], - [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:endpointIds[1]], @"endpointId", - [NSNumber numberWithUnsignedInteger:clusterIds[1]], @"clusterId", - [NSNumber numberWithUnsignedInteger:attributeIds[1]], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:223457 + count * 100], @"value", nil], - @"data", nil], - nil]; + myReports = @[ + @[ @{ + @"attributePath" : + [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:endpointIds[0]] + clusterId:[NSNumber numberWithUnsignedInteger:clusterIds[0]] + attributeId:[NSNumber numberWithUnsignedInteger:attributeIds[0]]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:223456 + count * 100] } + } ], + @[ @{ + @"attributePath" : + [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:endpointIds[1]] + clusterId:[NSNumber numberWithUnsignedInteger:clusterIds[1]] + attributeId:[NSNumber numberWithUnsignedInteger:attributeIds[1]]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:223457 + count * 100] } + } ] + ]; for (int i = 0; i < 2; i++) { NSUInteger nodeId = nodeIds[i]; dispatch_async(dispatch_get_main_queue(), ^{ - [clientObject handleReportWithController:uuid nodeId:nodeId value:myReports[i] error:nil]; + [clientObject handleReportWithController:uuid + nodeId:nodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReports[i]] + error:nil]; }); } [self waitForExpectations:reportExpectations timeout:kTimeoutInSeconds]; @@ -1584,24 +1699,29 @@ - (void)testMutiSubscriptions [self expectationWithDescription:[NSString stringWithFormat:@"Report(%d) for second subscriber sent", count]], nil]; reportExpectations[0].inverted = YES; reportExpectations[1].inverted = YES; - myReports = [NSArray - arrayWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:endpointIds[0]], - @"endpointId", [NSNumber numberWithUnsignedInteger:clusterIds[0]], @"clusterId", - [NSNumber numberWithUnsignedInteger:attributeIds[0]], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:223456 + count * 100], @"value", nil], - @"data", nil], - [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:endpointIds[1]], @"endpointId", - [NSNumber numberWithUnsignedInteger:clusterIds[1]], @"clusterId", - [NSNumber numberWithUnsignedInteger:attributeIds[1]], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:223457 + count * 100], @"value", nil], - @"data", nil], - nil]; + myReports = @[ + @[ @{ + @"attributePath" : + [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:endpointIds[0]] + clusterId:[NSNumber numberWithUnsignedInteger:clusterIds[0]] + attributeId:[NSNumber numberWithUnsignedInteger:attributeIds[0]]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:223456 + count * 100] } + } ], + @[ @{ + @"attributePath" : + [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:endpointIds[1]] + clusterId:[NSNumber numberWithUnsignedInteger:clusterIds[1]] + attributeId:[NSNumber numberWithUnsignedInteger:attributeIds[1]]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:223457 + count * 100] } + } ] + ]; for (int i = 0; i < 2; i++) { NSUInteger nodeId = nodeIds[i]; dispatch_async(dispatch_get_main_queue(), ^{ - [clientObject handleReportWithController:uuid nodeId:nodeId value:myReports[i] error:nil]; + [clientObject handleReportWithController:uuid + nodeId:nodeId + values:[CHIPDeviceController encodeXPCResponseValues:myReports[i]] + error:nil]; }); } [self waitForExpectations:reportExpectations timeout:kNegativeTimeoutInSeconds]; @@ -1706,13 +1826,13 @@ - (void)testReadAttributeCacheSuccess NSUInteger myEndpointId = 100; NSUInteger myClusterId = 200; NSUInteger myAttributeId = 300; - NSArray * myValues = - [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInteger:myEndpointId], - @"endpointId", [NSNumber numberWithUnsignedInteger:myClusterId], @"clusterId", - [NSNumber numberWithUnsignedInteger:myAttributeId], @"attributeId", - [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", - [NSNumber numberWithInteger:123456], @"value", nil], - @"data", nil]]; + NSArray * myValues = @[ @{ + @"attributePath" : [CHIPAttributePath attributePathWithEndpointId:[NSNumber numberWithUnsignedInteger:myEndpointId] + clusterId:[NSNumber numberWithUnsignedInteger:myClusterId] + attributeId:[NSNumber numberWithUnsignedInteger:myAttributeId]], + @"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 } + } ]; + XCTestExpectation * subscribeExpectation = [self expectationWithDescription:@"Cache subscription complete"]; XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"]; XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"]; @@ -1735,7 +1855,7 @@ - (void)testReadAttributeCacheSuccess XCTAssertEqual(clusterId, myClusterId); XCTAssertEqual(attributeId, myAttributeId); [callExpectation fulfill]; - completion(myValues, nil); + completion([CHIPDeviceController encodeXPCResponseValues:myValues], nil); }; [attributeCacheContainer subscribeWithDeviceController:_remoteDeviceController