blob: 753adc6bddfe81daf7bd0750016dce11b6d02b22 [file] [log] [blame]
//
// MTRRemoteDeviceTests.m
// MTRTests
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// module headers
#import <Matter/Matter.h>
#import "MTRErrorTestUtils.h"
#import <app/util/af-enums.h>
#import <math.h> // For INFINITY
// system dependencies
#import <XCTest/XCTest.h>
static const uint16_t kTimeoutInSeconds = 3;
// Inverted expectation timeout
static const uint16_t kNegativeTimeoutInSeconds = 1;
@interface MTRAttributePath (Test)
- (BOOL)isEqual:(id)object;
@end
@implementation MTRAttributePath (Test)
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:[MTRAttributePath class]]) {
MTRAttributePath * 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:@"MTRAttributePath(%@,%@,%@)", self.endpoint, self.cluster, self.attribute];
}
@end
@interface MTRCommandPath (Test)
- (BOOL)isEqual:(id)object;
@end
@implementation MTRCommandPath (Test)
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:[MTRCommandPath class]]) {
MTRCommandPath * 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:@"MTRCommandPath(%@,%@,%@)", self.endpoint, self.cluster, self.command];
}
@end
@interface MTRClusterStateCacheContainer (Test)
// Obsolete method is moved to this test suite to keep tests compatible
- (void)subscribeWithDeviceController:(MTRDeviceController *)deviceController
deviceID:(NSNumber *)deviceID
params:(MTRSubscribeParams * _Nullable)params
queue:(dispatch_queue_t)queue
completion:(void (^)(NSError * _Nullable error))completion;
@end
@implementation MTRClusterStateCacheContainer (Test)
- (void)subscribeWithDeviceController:(MTRDeviceController *)deviceController
deviceID:(NSNumber *)deviceID
params:(MTRSubscribeParams * _Nullable)params
queue:queue
completion:(void (^)(NSError * _Nullable error))completion
{
__auto_type completionHandler = ^(NSError * _Nullable error) {
dispatch_async(queue, ^{
completion(error);
});
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:deviceID controller:deviceController];
__auto_type * subscriptionParams
= (params == nil) ? [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(43200)] : params;
__auto_type established = [NSMutableArray arrayWithCapacity:1];
[established addObject:@NO];
[device subscribeWithQueue:queue
params:subscriptionParams
clusterStateCacheContainer:self
attributeReportHandler:^(NSArray * value) {
NSLog(@"Report received for attribute cache: %@", value);
}
eventReportHandler:nil
errorHandler:^(NSError * error) {
NSLog(@"Report error received for attribute cache: %@", error);
if (![established[0] boolValue]) {
established[0] = @YES;
completionHandler(error);
}
}
subscriptionEstablished:^() {
NSLog(@"Attribute cache subscription succeeded for device %llu", [deviceID unsignedLongLongValue]);
if (![established[0] boolValue]) {
established[0] = @YES;
completionHandler(nil);
}
}
resubscriptionScheduled:nil];
}
@end
@interface MTRXPCProtocolTests<NSXPCListenerDelegate, CHIPRemoteDeviceProtocol> : XCTestCase
@property (nonatomic, readwrite, strong) NSXPCListener * xpcListener;
@property (nonatomic, readwrite, strong) NSXPCInterface * serviceInterface;
@property (nonatomic, readwrite, strong) NSXPCInterface * clientInterface;
@property (readwrite, strong) NSXPCConnection * xpcConnection;
@property (nonatomic, readwrite, strong) MTRDeviceController * remoteDeviceController;
@property (nonatomic, readwrite, strong) NSString * controllerUUID;
@property (readwrite, strong) XCTestExpectation * xpcDisconnectExpectation;
@property (readwrite, strong) void (^handleGetAnySharedRemoteControllerWithFabricId)
(NSNumber * fabricId, void (^completion)(id _Nullable controller, NSError * _Nullable error));
@property (readwrite, strong) void (^handleGetAnySharedRemoteController)
(void (^completion)(id _Nullable controller, NSError * _Nullable error));
@property (readwrite, strong) void (^handleReadAttribute)(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId,
NSNumber * _Nullable clusterId, NSNumber * _Nullable attributeId, MTRReadParams * _Nullable params,
void (^completion)(id _Nullable values, NSError * _Nullable error));
@property (readwrite, strong) void (^handleWriteAttribute)
(id controller, NSNumber * nodeId, NSNumber * endpointId, NSNumber * clusterId, NSNumber * attributeId, id value,
NSNumber * _Nullable timedWriteTimeout, void (^completion)(id _Nullable values, NSError * _Nullable error));
@property (readwrite, strong) void (^handleInvokeCommand)
(id controller, NSNumber * nodeId, NSNumber * endpointId, NSNumber * clusterId, NSNumber * commandId, id fields,
NSNumber * _Nullable timedInvokeTimeout, void (^completion)(id _Nullable values, NSError * _Nullable error));
@property (readwrite, strong) void (^handleSubscribeAttribute)
(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nonnull params, void (^establishedHandler)(void));
@property (readwrite, strong) void (^handleStopReports)(id controller, NSNumber * nodeId, void (^completion)(void));
@property (readwrite, strong) void (^handleSubscribeAll)(id controller, NSNumber * nodeId, MTRSubscribeParams * _Nonnull params,
BOOL shouldCache, void (^completion)(NSError * _Nullable error));
@property (readwrite, strong) void (^handleReadClusterStateCache)
(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error));
@end
@implementation MTRXPCProtocolTests
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{
XCTAssertNil(_xpcConnection);
XCTAssertNotNil(newConnection);
NSLog(@"XPC listener accepting connection");
newConnection.exportedInterface = _serviceInterface;
newConnection.remoteObjectInterface = _clientInterface;
newConnection.exportedObject = self;
newConnection.invalidationHandler = ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"XPC connection disconnected");
self.xpcConnection = nil;
if (self.xpcDisconnectExpectation) {
[self.xpcDisconnectExpectation fulfill];
self.xpcDisconnectExpectation = nil;
}
});
};
dispatch_async(dispatch_get_main_queue(), ^{
self.xpcConnection = newConnection;
[newConnection resume];
});
return YES;
}
- (void)getDeviceControllerWithFabricId:(uint64_t)fabricId
completion:(void (^)(id _Nullable controller, NSError * _Nullable error))completion
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleGetAnySharedRemoteControllerWithFabricId);
self.handleGetAnySharedRemoteControllerWithFabricId(@(fabricId), completion);
});
}
- (void)getAnyDeviceControllerWithCompletion:(void (^)(id _Nullable controller, NSError * _Nullable error))completion
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleGetAnySharedRemoteController);
self.handleGetAnySharedRemoteController(completion);
});
}
- (void)readAttributeWithController:(id)controller
nodeId:(uint64_t)nodeId
endpointId:(NSNumber * _Nullable)endpointId
clusterId:(NSNumber * _Nullable)clusterId
attributeId:(NSNumber * _Nullable)attributeId
params:(NSDictionary<NSString *, id> * _Nullable)params
completion:(MTRValuesHandler)completion
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleReadAttribute);
self.handleReadAttribute(controller, @(nodeId), endpointId, clusterId, attributeId,
[MTRDeviceController decodeXPCReadParams:params], completion);
});
}
- (void)writeAttributeWithController:(id)controller
nodeId:(uint64_t)nodeId
endpointId:(NSNumber *)endpointId
clusterId:(NSNumber *)clusterId
attributeId:(NSNumber *)attributeId
value:(id)value
timedWriteTimeout:(NSNumber * _Nullable)timeoutMs
completion:(MTRValuesHandler)completion
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleWriteAttribute);
self.handleWriteAttribute(controller, @(nodeId), endpointId, clusterId, attributeId, value, timeoutMs, completion);
});
}
- (void)invokeCommandWithController:(id)controller
nodeId:(uint64_t)nodeId
endpointId:(NSNumber *)endpointId
clusterId:(NSNumber *)clusterId
commandId:(NSNumber *)commandId
fields:(id)fields
timedInvokeTimeout:(NSNumber * _Nullable)timeoutMs
completion:(MTRValuesHandler)completion
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleInvokeCommand);
self.handleInvokeCommand(controller, @(nodeId), endpointId, clusterId, commandId, fields, timeoutMs, completion);
});
}
- (void)subscribeAttributeWithController:(id)controller
nodeId:(uint64_t)nodeId
endpointId:(NSNumber * _Nullable)endpointId
clusterId:(NSNumber * _Nullable)clusterId
attributeId:(NSNumber * _Nullable)attributeId
minInterval:(NSNumber *)minInterval
maxInterval:(NSNumber *)maxInterval
params:(NSDictionary<NSString *, id> * _Nullable)params
establishedHandler:(dispatch_block_t)establishedHandler
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleSubscribeAttribute);
__auto_type * subscriptionParams = [MTRDeviceController decodeXPCSubscribeParams:params];
if (subscriptionParams == nil) {
subscriptionParams = [[MTRSubscribeParams alloc] initWithMinInterval:minInterval maxInterval:maxInterval];
} else {
subscriptionParams.minInterval = minInterval;
subscriptionParams.maxInterval = maxInterval;
}
self.handleSubscribeAttribute(
controller, @(nodeId), endpointId, clusterId, attributeId, subscriptionParams, establishedHandler);
});
}
- (void)stopReportsWithController:(id)controller nodeId:(uint64_t)nodeId completion:(dispatch_block_t)completion
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleStopReports);
self.handleStopReports(controller, @(nodeId), completion);
});
}
- (void)subscribeWithController:(id _Nullable)controller
nodeId:(uint64_t)nodeId
minInterval:(NSNumber *)minInterval
maxInterval:(NSNumber *)maxInterval
params:(NSDictionary<NSString *, id> * _Nullable)params
shouldCache:(BOOL)shouldCache
completion:(MTRStatusCompletion)completion
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleSubscribeAll);
MTRSubscribeParams * subscribeParams = [MTRDeviceController decodeXPCSubscribeParams:params];
if (params == nil) {
subscribeParams = [[MTRSubscribeParams alloc] initWithMinInterval:minInterval maxInterval:maxInterval];
} else {
subscribeParams.minInterval = minInterval;
subscribeParams.maxInterval = maxInterval;
}
self.handleSubscribeAll(controller, @(nodeId), subscribeParams, shouldCache, completion);
});
}
- (void)readAttributeCacheWithController:(id _Nullable)controller
nodeId:(uint64_t)nodeId
endpointId:(NSNumber * _Nullable)endpointId
clusterId:(NSNumber * _Nullable)clusterId
attributeId:(NSNumber * _Nullable)attributeId
completion:(MTRValuesHandler)completion
{
dispatch_async(dispatch_get_main_queue(), ^{
XCTAssertNotNil(self.handleReadClusterStateCache);
self.handleReadClusterStateCache(controller, @(nodeId), endpointId, clusterId, attributeId, completion);
});
}
- (void)setUp
{
[self setContinueAfterFailure:NO];
_xpcListener = [NSXPCListener anonymousListener];
[_xpcListener setDelegate:(id<NSXPCListenerDelegate>) self];
_serviceInterface = [MTRDeviceController xpcInterfaceForServerProtocol];
_clientInterface = [MTRDeviceController xpcInterfaceForClientProtocol];
[_xpcListener resume];
_controllerUUID = [[NSUUID UUID] UUIDString];
_remoteDeviceController =
[MTRDeviceController sharedControllerWithID:_controllerUUID
xpcConnectBlock:^NSXPCConnection * {
return [[NSXPCConnection alloc] initWithListenerEndpoint:self.xpcListener.endpoint];
}];
}
- (void)tearDown
{
_remoteDeviceController = nil;
[_xpcListener suspend];
_xpcListener = nil;
_xpcDisconnectExpectation = nil;
}
- (void)testReadAttributeSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSArray * myValues = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 }
} ];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleReadAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRReadParams * _Nullable params,
void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertNil(params);
[callExpectation fulfill];
completion([MTRDeviceController encodeXPCResponseValues:myValues], nil);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Reading...");
[device readAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:nil
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Read value: %@", value);
XCTAssertNotNil(value);
XCTAssertNil(error);
XCTAssertTrue([myValues isEqual:value]);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When read is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testReadAttributeWithParamsSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSArray * myValues = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 }
} ];
MTRReadParams * myParams = [[MTRReadParams alloc] init];
myParams.filterByFabric = NO;
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleReadAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRReadParams * _Nullable params,
void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertNotNil(params);
XCTAssertEqual(params.filterByFabric, myParams.filterByFabric);
[callExpectation fulfill];
completion([MTRDeviceController encodeXPCResponseValues:myValues], nil);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Reading...");
[device readAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:myParams
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Read value: %@", value);
XCTAssertNotNil(value);
XCTAssertNil(error);
XCTAssertTrue([myValues isEqual:value]);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When read is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testReadAttributeFailure
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSError * myError = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleReadAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRReadParams * _Nullable params,
void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertNil(params);
[callExpectation fulfill];
completion(nil, myError);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Reading...");
[device readAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:nil
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Read value: %@", value);
XCTAssertNil(value);
XCTAssertNotNil(error);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When read is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testWriteAttributeSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSDictionary * myValue =
[NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", [NSNumber numberWithInteger:654321], @"value", nil];
NSArray * myResults = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId]
} ];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleWriteAttribute = ^(id controller, NSNumber * nodeId, NSNumber * endpointId, NSNumber * clusterId, NSNumber * attributeId,
id value, NSNumber * _Nullable timedWriteTimeout, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertTrue([value isEqual:myValue]);
XCTAssertNil(timedWriteTimeout);
[callExpectation fulfill];
completion([MTRDeviceController encodeXPCResponseValues:myResults], nil);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Writing...");
[device writeAttributeWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
value:myValue
timedWriteTimeout:nil
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Write response: %@", value);
XCTAssertNotNil(value);
XCTAssertNil(error);
XCTAssertTrue([myResults isEqual:value]);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When write is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testTimedWriteAttributeSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSDictionary * myValue =
[NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", [NSNumber numberWithInteger:654321], @"value", nil];
NSNumber * myTimedWriteTimeout = @1234;
NSArray * myResults = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId]
} ];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleWriteAttribute = ^(id controller, NSNumber * nodeId, NSNumber * endpointId, NSNumber * clusterId, NSNumber * attributeId,
id value, NSNumber * _Nullable timedWriteTimeout, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertTrue([value isEqual:myValue]);
XCTAssertNotNil(timedWriteTimeout);
XCTAssertEqual([timedWriteTimeout unsignedShortValue], [myTimedWriteTimeout unsignedShortValue]);
[callExpectation fulfill];
completion([MTRDeviceController encodeXPCResponseValues:myResults], nil);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Writing...");
[device writeAttributeWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
value:myValue
timedWriteTimeout:myTimedWriteTimeout
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Write response: %@", value);
XCTAssertNotNil(value);
XCTAssertNil(error);
XCTAssertTrue([myResults isEqual:value]);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When write is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testWriteAttributeFailure
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSDictionary * myValue =
[NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", [NSNumber numberWithInteger:654321], @"value", nil];
NSError * myError = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleWriteAttribute = ^(id controller, NSNumber * nodeId, NSNumber * endpointId, NSNumber * clusterId, NSNumber * attributeId,
id value, NSNumber * _Nullable timedWriteTimeout, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertTrue([value isEqual:myValue]);
XCTAssertNil(timedWriteTimeout);
[callExpectation fulfill];
completion(nil, myError);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Writing...");
[device writeAttributeWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
value:myValue
timedWriteTimeout:nil
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Write response: %@", value);
XCTAssertNil(value);
XCTAssertNotNil(error);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When write is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testInvokeCommandSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myCommandId = @300;
NSDictionary * myFields = [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type",
[NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Float", @"Type",
[NSNumber numberWithFloat:1.0], @"value", nil]],
@"value", nil];
NSArray * myResults = @[
@{ @"commandPath" : [MTRCommandPath commandPathWithEndpointID:myEndpointId clusterID:myClusterId commandID:myCommandId] }
];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleInvokeCommand
= ^(id controller, NSNumber * nodeId, NSNumber * endpointId, NSNumber * clusterId, NSNumber * commandId, id commandFields,
NSNumber * _Nullable timedInvokeTimeout, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([commandId unsignedLongValue], [myCommandId unsignedLongValue]);
XCTAssertTrue([commandFields isEqual:myFields]);
XCTAssertNil(timedInvokeTimeout);
[callExpectation fulfill];
completion([MTRDeviceController encodeXPCResponseValues:myResults], nil);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Invoking command...");
[device invokeCommandWithEndpointID:myEndpointId
clusterID:myClusterId
commandID:myCommandId
commandFields:myFields
timedInvokeTimeout:nil
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Command response: %@", value);
XCTAssertNotNil(value);
XCTAssertNil(error);
XCTAssertTrue([myResults isEqual:value]);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When command is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testTimedInvokeCommandSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myCommandId = @300;
NSNumber * myTimedInvokeTimeout = @5678;
NSDictionary * myFields = [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type",
[NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Float", @"Type",
[NSNumber numberWithFloat:1.0], @"value", nil]],
@"value", nil];
NSArray * myResults = @[
@{ @"commandPath" : [MTRCommandPath commandPathWithEndpointID:myEndpointId clusterID:myClusterId commandID:myCommandId] }
];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleInvokeCommand
= ^(id controller, NSNumber * nodeId, NSNumber * endpointId, NSNumber * clusterId, NSNumber * commandId, id commandFields,
NSNumber * _Nullable timedInvokeTimeout, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([commandId unsignedLongValue], [myCommandId unsignedLongValue]);
XCTAssertTrue([commandFields isEqual:myFields]);
XCTAssertNotNil(timedInvokeTimeout);
XCTAssertEqual([timedInvokeTimeout unsignedShortValue], [myTimedInvokeTimeout unsignedShortValue]);
[callExpectation fulfill];
completion([MTRDeviceController encodeXPCResponseValues:myResults], nil);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Invoking command...");
[device invokeCommandWithEndpointID:myEndpointId
clusterID:myClusterId
commandID:myCommandId
commandFields:myFields
timedInvokeTimeout:myTimedInvokeTimeout
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Command response: %@", value);
XCTAssertNotNil(value);
XCTAssertNil(error);
XCTAssertTrue([myResults isEqual:value]);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When command is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testInvokeCommandFailure
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myCommandId = @300;
NSDictionary * myFields = [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type",
[NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Float", @"Type",
[NSNumber numberWithFloat:1.0], @"value", nil]],
@"value", nil];
NSError * myError = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
_handleInvokeCommand
= ^(id controller, NSNumber * nodeId, NSNumber * endpointId, NSNumber * clusterId, NSNumber * commandId, id commandFields,
NSNumber * _Nullable timedInvokeTimeout, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([commandId unsignedLongValue], [myCommandId unsignedLongValue]);
XCTAssertTrue([commandFields isEqual:myFields]);
XCTAssertNil(timedInvokeTimeout);
[callExpectation fulfill];
completion(nil, myError);
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Invoking command...");
[device invokeCommandWithEndpointID:myEndpointId
clusterID:myClusterId
commandID:myCommandId
commandFields:myFields
timedInvokeTimeout:nil
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Command response: %@", value);
XCTAssertNil(value);
XCTAssertNotNil(error);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, responseExpectation, nil] timeout:kTimeoutInSeconds];
// When command is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testSubscribeAttributeSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID: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"];
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nonnull params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Inject another report
reportExpectation = [self expectationWithDescription:@"2nd report sent"];
myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testSubscribeAttributeWithParamsSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
MTRSubscribeParams * myParams = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
myParams.filterByFabric = NO;
myParams.replaceExistingSubscriptions = YES;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID: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"];
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nonnull params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
XCTAssertEqual(params.filterByFabric, myParams.filterByFabric);
XCTAssertEqual(params.replaceExistingSubscriptions, myParams.replaceExistingSubscriptions);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:myParams
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Inject another report
reportExpectation = [self expectationWithDescription:@"2nd report sent"];
myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testBadlyFormattedReport
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
// Incorrect serialized report value. Report should have ben a single NSDictionary
__block id myReport = @{
@"attributePath" : @[ myEndpointId, myClusterId, 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"];
reportExpectation.inverted = YES;
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nonnull params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject badly formatted report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[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 = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
_xpcDisconnectExpectation.inverted = NO;
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testReportWithUnrelatedEndpointId
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:@([myEndpointId unsignedShortValue] + 1)
clusterID:myClusterId
attributeID: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"];
reportExpectation.inverted = YES;
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nullable params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController 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 = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testReportWithUnrelatedClusterId
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:@([myClusterId unsignedLongValue] + 1)
attributeID: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"];
reportExpectation.inverted = YES;
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nonnull params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController 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 = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testReportWithUnrelatedAttributeId
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:@([myAttributeId unsignedLongValue] + 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"];
reportExpectation.inverted = YES;
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nonnull params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController 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 = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testReportWithUnrelatedNode
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID: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"];
reportExpectation.inverted = YES;
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nullable params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId + 1
values:[MTRDeviceController 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 = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testSubscribeMultiEndpoints
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID: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"];
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nullable params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertNil(endpointId);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:nil
clusterID:myClusterId
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds];
// Inject another report
reportExpectation = [self expectationWithDescription:@"2nd report sent"];
myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testSubscribeMultiClusters
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID: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"];
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nullable params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertNil(clusterId);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:nil
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kNegativeTimeoutInSeconds];
// Inject another report
reportExpectation = [self expectationWithDescription:@"2nd report sent"];
myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testSubscribeMultiAttributes
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSNumber * myMinInterval = @5;
NSNumber * myMaxInterval = @60;
__block NSArray * myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID: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"];
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nullable params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertNil(attributeId);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:nil
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Report value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReport isEqual:values]);
[reportExpectation fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
// Inject report
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Inject another report
reportExpectation = [self expectationWithDescription:@"2nd report sent"];
myReport = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @771234 }
} ];
[clientObject handleReportWithController:uuid
nodeId:myNodeId
values:[MTRDeviceController encodeXPCResponseValues:myReport]
error:nil];
// Wait for report
[self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion();
[stopExpectation fulfill];
};
// Deregister report handler
device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
}];
// Wait for disconnection
[self waitForExpectations:@[ _xpcDisconnectExpectation, stopExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testMutiSubscriptions
{
uint64_t nodeIds[] = { 9876543210, 9876543211 };
NSNumber * endpointIds[] = { @100, @150 };
NSNumber * clusterIds[] = { @200, @250 };
NSNumber * attributeIds[] = { @300, @350 };
NSNumber * minIntervals[] = { @5, @7 };
NSNumber * maxIntervals[] = { @60, @68 };
__block uint64_t myNodeId = nodeIds[0];
__block NSNumber * myEndpointId = endpointIds[0];
__block NSNumber * myClusterId = clusterIds[0];
__block NSNumber * myAttributeId = attributeIds[0];
__block NSNumber * myMinInterval = minIntervals[0];
__block NSNumber * myMaxInterval = maxIntervals[0];
__block NSArray<NSArray *> * myReports;
__block XCTestExpectation * callExpectation;
__block XCTestExpectation * establishExpectation;
__block NSArray<XCTestExpectation *> * reportExpectations;
__auto_type uuid = self.controllerUUID;
_handleSubscribeAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRSubscribeParams * _Nullable params, void (^establishedHandler)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertEqual([params.minInterval unsignedShortValue], [myMinInterval unsignedShortValue]);
XCTAssertEqual([params.maxInterval unsignedShortValue], [myMaxInterval unsignedShortValue]);
[callExpectation fulfill];
establishedHandler();
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
// Multi-subscriptions
for (unsigned int i = 0; i < 2; i++) {
myNodeId = nodeIds[i];
myEndpointId = endpointIds[i];
myClusterId = clusterIds[i];
myAttributeId = attributeIds[i];
myMinInterval = minIntervals[i];
myMaxInterval = maxIntervals[i];
callExpectation = [self expectationWithDescription:[NSString stringWithFormat:@"XPC call (%u) received", i]];
establishExpectation = [self expectationWithDescription:[NSString stringWithFormat:@"Established (%u) called", i]];
__auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:myMinInterval maxInterval:myMaxInterval];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:_remoteDeviceController];
NSLog(@"Device acquired. Subscribing...");
[device subscribeToAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:params
queue:dispatch_get_main_queue()
reportHandler:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Subscriber [%d] report value: %@", i, values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myReports[i] isEqual:values]);
[reportExpectations[i] fulfill];
}
subscriptionEstablished:^{
[establishExpectation fulfill];
}];
[self waitForExpectations:[NSArray arrayWithObjects:callExpectation, establishExpectation, nil] timeout:kTimeoutInSeconds];
}
id<MTRDeviceControllerClientProtocol> clientObject = _xpcConnection.remoteObjectProxy;
// Inject reports
for (int count = 0; count < 2; count++) {
reportExpectations = [NSArray
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 = @[
@[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:endpointIds[0]
clusterID:clusterIds[0]
attributeID:attributeIds[0]],
@"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:123456 + count * 100] }
} ],
@[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:endpointIds[1]
clusterID:clusterIds[1]
attributeID:attributeIds[1]],
@"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:123457 + count * 100] }
} ]
];
for (unsigned int i = 0; i < 2; i++) {
NSUInteger nodeId = nodeIds[i];
dispatch_async(dispatch_get_main_queue(), ^{
[clientObject handleReportWithController:uuid
nodeId:nodeId
values:[MTRDeviceController encodeXPCResponseValues:myReports[i]]
error:nil];
});
}
[self waitForExpectations:reportExpectations timeout:kTimeoutInSeconds];
}
// Setup stop report handler
XCTestExpectation * stopExpectation = [self expectationWithDescription:@"Reports stopped"];
__auto_type nodeToStop = nodeIds[0];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], nodeToStop);
completion();
[stopExpectation fulfill];
};
// Deregister report handler for first subscriber
__auto_type deregisterExpectation = [self expectationWithDescription:@"First subscriber deregistered"];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(nodeToStop) controller:_remoteDeviceController];
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
[deregisterExpectation fulfill];
}];
[self waitForExpectations:@[ stopExpectation, deregisterExpectation ] timeout:kTimeoutInSeconds];
// Inject reports
for (int count = 0; count < 1; count++) {
reportExpectations = [NSArray
arrayWithObjects:[self expectationWithDescription:[NSString
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 = @[
@[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:endpointIds[0]
clusterID:clusterIds[0]
attributeID:attributeIds[0]],
@"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:223456 + count * 100] }
} ],
@[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:endpointIds[1]
clusterID:clusterIds[1]
attributeID:attributeIds[1]],
@"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:223457 + count * 100] }
} ]
];
for (unsigned int i = 0; i < 2; i++) {
NSUInteger nodeId = nodeIds[i];
dispatch_async(dispatch_get_main_queue(), ^{
[clientObject handleReportWithController:uuid
nodeId:nodeId
values:[MTRDeviceController encodeXPCResponseValues:myReports[i]]
error:nil];
});
}
[self waitForExpectations:reportExpectations timeout:kTimeoutInSeconds];
}
// Setup stop report handler
stopExpectation = [self expectationWithDescription:@"Reports stopped"];
nodeToStop = nodeIds[1];
_handleStopReports = ^(id _Nullable controller, NSNumber * nodeId, void (^completion)(void)) {
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], nodeToStop);
completion();
[stopExpectation fulfill];
};
// Deregister report handler for second subscriber
__auto_type secondDeregisterExpectation = [self expectationWithDescription:@"Second subscriber deregistered"];
device = [MTRBaseDevice deviceWithNodeID:@(nodeToStop) controller:_remoteDeviceController];
NSLog(@"Device acquired. Deregistering...");
[device deregisterReportHandlersWithQueue:dispatch_get_main_queue()
completion:^{
NSLog(@"Deregistered");
[secondDeregisterExpectation fulfill];
}];
// Wait for deregistration and disconnection
[self waitForExpectations:@[ secondDeregisterExpectation, _xpcDisconnectExpectation, stopExpectation ]
timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
// Inject reports
for (int count = 0; count < 1; count++) {
reportExpectations = [NSArray
arrayWithObjects:[self expectationWithDescription:[NSString
stringWithFormat:@"Report(%d) for first subscriber sent", count]],
[self expectationWithDescription:[NSString stringWithFormat:@"Report(%d) for second subscriber sent", count]], nil];
reportExpectations[0].inverted = YES;
reportExpectations[1].inverted = YES;
myReports = @[
@[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:endpointIds[0]
clusterID:clusterIds[0]
attributeID:attributeIds[0]],
@"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:223456 + count * 100] }
} ],
@[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:endpointIds[1]
clusterID:clusterIds[1]
attributeID:attributeIds[1]],
@"data" : @ { @"type" : @"SignedInteger", @"value" : [NSNumber numberWithInteger:223457 + count * 100] }
} ]
];
for (unsigned int i = 0; i < 2; i++) {
NSUInteger nodeId = nodeIds[i];
dispatch_async(dispatch_get_main_queue(), ^{
[clientObject handleReportWithController:uuid
nodeId:nodeId
values:[MTRDeviceController encodeXPCResponseValues:myReports[i]]
error:nil];
});
}
[self waitForExpectations:reportExpectations timeout:kNegativeTimeoutInSeconds];
}
}
- (void)testAnySharedRemoteController
{
NSString * myUUID = [[NSUUID UUID] UUIDString];
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSArray * myValues = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId],
@"data" : @ { @"type" : @"SignedInteger", @"value" : @123456 }
} ];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
_handleReadAttribute = ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, MTRReadParams * _Nullable params,
void (^completion)(id _Nullable values, NSError * _Nullable error)) {
XCTAssertTrue([controller isEqualToString:myUUID]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
XCTAssertNil(params);
[callExpectation fulfill];
completion([MTRDeviceController encodeXPCResponseValues:myValues], nil);
};
__auto_type unspecifiedRemoteDeviceController =
[MTRDeviceController sharedControllerWithID:nil
xpcConnectBlock:^NSXPCConnection * {
return [[NSXPCConnection alloc] initWithListenerEndpoint:self.xpcListener.endpoint];
}];
__auto_type anySharedRemoteControllerCallExpectation =
[self expectationWithDescription:@"getAnySharedRemoteController was called"];
_handleGetAnySharedRemoteController = ^(void (^completion)(id _Nullable controller, NSError * _Nullable error)) {
completion(myUUID, nil);
[anySharedRemoteControllerCallExpectation fulfill];
};
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:unspecifiedRemoteDeviceController];
// Do a read to exercise the device.
NSLog(@"Device acquired. Reading...");
[device readAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:nil
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Read value: %@", value);
XCTAssertNotNil(value);
XCTAssertNil(error);
XCTAssertTrue([myValues isEqual:value]);
[responseExpectation fulfill];
self.xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
}];
[self waitForExpectations:[NSArray arrayWithObjects:anySharedRemoteControllerCallExpectation, callExpectation,
responseExpectation, nil]
timeout:kTimeoutInSeconds];
// When read is done, connection should have been released
[self waitForExpectations:[NSArray arrayWithObject:_xpcDisconnectExpectation] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testSubscribeClusterStateCacheSuccess
{
uint64_t myNodeId = 9876543210;
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
__auto_type clusterStateCacheContainer = [[MTRClusterStateCacheContainer alloc] init];
_handleSubscribeAll = ^(id controller, NSNumber * nodeId, MTRSubscribeParams * _Nonnull params, BOOL shouldCache,
void (^completion)(NSError * _Nullable error)) {
NSLog(@"Subscribe called");
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
[callExpectation fulfill];
completion(nil);
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
[clusterStateCacheContainer subscribeWithDeviceController:_remoteDeviceController
deviceID:@(myNodeId)
params:nil
queue:dispatch_get_main_queue()
completion:^(NSError * _Nullable error) {
NSLog(@"Subscribe completion called with error: %@", error);
XCTAssertNil(error);
[responseExpectation fulfill];
}];
[self waitForExpectations:@[ callExpectation, responseExpectation, self.xpcDisconnectExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testSubscribeClusterStateCacheWithParamsSuccess
{
uint64_t myNodeId = 9876543210;
MTRSubscribeParams * myParams = [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(43200)];
myParams.filterByFabric = YES;
myParams.replaceExistingSubscriptions = NO;
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
__auto_type clusterStateCacheContainer = [[MTRClusterStateCacheContainer alloc] init];
_handleSubscribeAll = ^(id controller, NSNumber * nodeId, MTRSubscribeParams * _Nonnull params, BOOL shouldCache,
void (^completion)(NSError * _Nullable error)) {
NSLog(@"Subscribe attribute cache called");
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual(params.filterByFabric, myParams.filterByFabric);
XCTAssertEqual(params.replaceExistingSubscriptions, myParams.replaceExistingSubscriptions);
[callExpectation fulfill];
completion(nil);
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
[clusterStateCacheContainer subscribeWithDeviceController:_remoteDeviceController
deviceID:@(myNodeId)
params:myParams
queue:dispatch_get_main_queue()
completion:^(NSError * _Nullable error) {
NSLog(@"Subscribe completion called with error: %@", error);
XCTAssertNil(error);
[responseExpectation fulfill];
}];
[self waitForExpectations:@[ callExpectation, responseExpectation, self.xpcDisconnectExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testSubscribeClusterStateCacheFailure
{
uint64_t myNodeId = 9876543210;
NSError * myError = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
__auto_type clusterStateCacheContainer = [[MTRClusterStateCacheContainer alloc] init];
_handleSubscribeAll = ^(id controller, NSNumber * nodeId, MTRSubscribeParams * _Nonnull params, BOOL shouldCache,
void (^completion)(NSError * _Nullable error)) {
NSLog(@"Subscribe attribute cache called");
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
[callExpectation fulfill];
completion(myError);
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
[clusterStateCacheContainer subscribeWithDeviceController:_remoteDeviceController
deviceID:@(myNodeId)
params:nil
queue:dispatch_get_main_queue()
completion:^(NSError * _Nullable error) {
NSLog(@"Subscribe completion called with error: %@", error);
XCTAssertNotNil(error);
[responseExpectation fulfill];
}];
[self waitForExpectations:@[ callExpectation, responseExpectation, _xpcDisconnectExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testReadClusterStateCacheSuccess
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSArray * myValues = @[ @{
@"attributePath" : [MTRAttributePath attributePathWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID: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"];
__auto_type uuid = self.controllerUUID;
__auto_type clusterStateCacheContainer = [[MTRClusterStateCacheContainer alloc] init];
_handleSubscribeAll = ^(id controller, NSNumber * nodeId, MTRSubscribeParams * _Nonnull params, BOOL shouldCache,
void (^completion)(NSError * _Nullable error)) {
NSLog(@"Subscribe attribute cache called");
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion(nil);
};
_handleReadClusterStateCache
= ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
NSLog(@"Read attribute cache called");
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
[callExpectation fulfill];
completion([MTRDeviceController encodeXPCResponseValues:myValues], nil);
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
[clusterStateCacheContainer subscribeWithDeviceController:_remoteDeviceController
deviceID:@(myNodeId)
params:nil
queue:dispatch_get_main_queue()
completion:^(NSError * _Nullable error) {
NSLog(@"Subscribe completion called with error: %@", error);
XCTAssertNil(error);
[subscribeExpectation fulfill];
}];
[self waitForExpectations:@[ subscribeExpectation, _xpcDisconnectExpectation ] timeout:kTimeoutInSeconds];
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
[clusterStateCacheContainer
readAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
queue:dispatch_get_main_queue()
completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Read cached value: %@", values);
XCTAssertNotNil(values);
XCTAssertNil(error);
XCTAssertTrue([myValues isEqual:values]);
[responseExpectation fulfill];
}];
[self waitForExpectations:@[ callExpectation, responseExpectation, _xpcDisconnectExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testReadClusterStateCacheFailure
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
NSError * myError = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeGeneralError userInfo:nil];
XCTestExpectation * subscribeExpectation = [self expectationWithDescription:@"Cache subscription complete"];
XCTestExpectation * callExpectation = [self expectationWithDescription:@"XPC call received"];
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"XPC response received"];
__auto_type uuid = self.controllerUUID;
__auto_type clusterStateCacheContainer = [[MTRClusterStateCacheContainer alloc] init];
_handleSubscribeAll = ^(id controller, NSNumber * nodeId, MTRSubscribeParams * _Nonnull params, BOOL shouldCache,
void (^completion)(NSError * _Nullable error)) {
NSLog(@"Subscribe attribute cache called");
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
completion(nil);
};
_handleReadClusterStateCache
= ^(id controller, NSNumber * nodeId, NSNumber * _Nullable endpointId, NSNumber * _Nullable clusterId,
NSNumber * _Nullable attributeId, void (^completion)(id _Nullable values, NSError * _Nullable error)) {
NSLog(@"Read attribute cache called");
XCTAssertTrue([controller isEqualToString:uuid]);
XCTAssertEqual([nodeId unsignedLongLongValue], myNodeId);
XCTAssertEqual([endpointId unsignedShortValue], [myEndpointId unsignedShortValue]);
XCTAssertEqual([clusterId unsignedLongValue], [myClusterId unsignedLongValue]);
XCTAssertEqual([attributeId unsignedLongValue], [myAttributeId unsignedLongValue]);
[callExpectation fulfill];
completion(nil, myError);
};
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
[clusterStateCacheContainer subscribeWithDeviceController:_remoteDeviceController
deviceID:@(myNodeId)
params:nil
queue:dispatch_get_main_queue()
completion:^(NSError * _Nullable error) {
NSLog(@"Subscribe completion called with error: %@", error);
XCTAssertNil(error);
[subscribeExpectation fulfill];
}];
[self waitForExpectations:@[ subscribeExpectation, _xpcDisconnectExpectation ] timeout:kTimeoutInSeconds];
_xpcDisconnectExpectation = [self expectationWithDescription:@"XPC Disconnected"];
[clusterStateCacheContainer
readAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
queue:dispatch_get_main_queue()
completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
NSLog(@"Read cached value: %@", values);
XCTAssertNil(values);
XCTAssertNotNil(error);
[responseExpectation fulfill];
}];
[self waitForExpectations:@[ callExpectation, responseExpectation, _xpcDisconnectExpectation ] timeout:kTimeoutInSeconds];
XCTAssertNil(_xpcConnection);
}
- (void)testXPCConnectionFailure
{
uint64_t myNodeId = 9876543210;
NSNumber * myEndpointId = @100;
NSNumber * myClusterId = @200;
NSNumber * myAttributeId = @300;
XCTestExpectation * responseExpectation = [self expectationWithDescription:@"Read response received"];
// Test with a device controller which wouldn't connect to XPC listener successfully
__auto_type failingDeviceController = [MTRDeviceController sharedControllerWithID:_controllerUUID
xpcConnectBlock:^NSXPCConnection * {
return nil;
}];
__auto_type * device = [MTRBaseDevice deviceWithNodeID:@(myNodeId) controller:failingDeviceController];
NSLog(@"Device acquired. Reading...");
[device readAttributesWithEndpointID:myEndpointId
clusterID:myClusterId
attributeID:myAttributeId
params:nil
queue:dispatch_get_main_queue()
completion:^(id _Nullable value, NSError * _Nullable error) {
NSLog(@"Read value: %@", value);
XCTAssertNil(value);
XCTAssertNotNil(error);
[responseExpectation fulfill];
}];
[self waitForExpectations:@[ responseExpectation ] timeout:kTimeoutInSeconds];
}
@end