blob: 1b5e64dd7728dd509f131efe8e2045e76bb7e67d [file] [log] [blame]
kpark-apple153a5642022-02-22 07:19:20 -08001//
Justin Woodf5c12c92022-07-02 00:43:40 -07002// MTRDeviceTests.m
3// MTRDeviceTests
kpark-apple153a5642022-02-22 07:19:20 -08004/*
5 *
6 * Copyright (c) 2022 Project CHIP Authors
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21// module headers
Jeff Tungae2913f2022-07-06 19:02:55 -070022#import <Matter/MTRBaseClusters.h>
23#import <Matter/MTRBaseDevice.h>
Boris Zbarsky3edba4a2022-11-17 09:59:53 -050024#import <Matter/MTRClusterStateCacheContainer.h>
Justin Woodf5c12c92022-07-02 00:43:40 -070025#import <Matter/Matter.h>
kpark-apple153a5642022-02-22 07:19:20 -080026
Justin Woodf5c12c92022-07-02 00:43:40 -070027#import "MTRErrorTestUtils.h"
28#import "MTRTestKeys.h"
29#import "MTRTestStorage.h"
kpark-apple153a5642022-02-22 07:19:20 -080030
31#import <app/util/af-enums.h>
32
33#import <math.h> // For INFINITY
34
35// system dependencies
36#import <XCTest/XCTest.h>
37
38// Set the following to 1 in order to run individual test case manually.
39#define MANUAL_INDIVIDUAL_TEST 0
40
41static const uint16_t kPairingTimeoutInSeconds = 10;
kpark-apple153a5642022-02-22 07:19:20 -080042static const uint16_t kTimeoutInSeconds = 3;
43static const uint64_t kDeviceId = 0x12344321;
Boris Zbarskybd5956d2022-10-13 12:12:49 -040044static NSString * kOnboardingPayload = @"MT:-24J0AFN00KA0648G00";
kpark-apple153a5642022-02-22 07:19:20 -080045static const uint16_t kLocalPort = 5541;
Boris Zbarskybd5956d2022-10-13 12:12:49 -040046static const uint16_t kTestVendorId = 0xFFF1u;
kpark-apple153a5642022-02-22 07:19:20 -080047
48// This test suite reuses a device object to speed up the test process for CI.
49// The following global variable holds the reference to the device object.
Jeff Tungae2913f2022-07-06 19:02:55 -070050static MTRBaseDevice * mConnectedDevice;
kpark-apple153a5642022-02-22 07:19:20 -080051
Boris Zbarskydfd71fe2022-04-14 23:29:44 -040052// Singleton controller we use.
Justin Woodf5c12c92022-07-02 00:43:40 -070053static MTRDeviceController * sController = nil;
Boris Zbarskydfd71fe2022-04-14 23:29:44 -040054
Boris Zbarskya7475d32022-10-11 10:22:56 -040055// Keys we can use to restart the controller.
56static MTRTestKeys * sTestKeys = nil;
57
Boris Zbarsky57570682022-09-14 14:56:13 -040058static void WaitForCommissionee(XCTestExpectation * expectation)
kpark-apple153a5642022-02-22 07:19:20 -080059{
Justin Woodf5c12c92022-07-02 00:43:40 -070060 MTRDeviceController * controller = sController;
kpark-apple153a5642022-02-22 07:19:20 -080061 XCTAssertNotNil(controller);
62
Boris Zbarskyfb865cc2022-11-09 08:09:37 -050063 // For now keep the async dispatch, but could we just
64 // synchronously fulfill the expectation here?
65 dispatch_async(dispatch_get_main_queue(), ^{
66 [expectation fulfill];
67 mConnectedDevice = [MTRBaseDevice deviceWithNodeID:@(kDeviceId) controller:controller];
68 });
kpark-apple153a5642022-02-22 07:19:20 -080069}
70
Jeff Tungae2913f2022-07-06 19:02:55 -070071static MTRBaseDevice * GetConnectedDevice(void)
kpark-apple153a5642022-02-22 07:19:20 -080072{
73 XCTAssertNotNil(mConnectedDevice);
74 return mConnectedDevice;
75}
76
Kundok Parkeac433a2022-03-15 10:05:54 -070077#ifdef DEBUG
Jeff Tungae2913f2022-07-06 19:02:55 -070078@interface MTRBaseDevice (Test)
Boris Zbarsky35187602022-11-02 18:19:46 -040079- (void)failSubscribers:(dispatch_queue_t)queue completion:(void (^)(void))completion;
Kundok Parkeac433a2022-03-15 10:05:54 -070080@end
81#endif
82
Boris Zbarskyf67c1b72022-11-16 11:03:02 -050083@interface MTRDeviceTestDeviceControllerDelegate : NSObject <MTRDeviceControllerDelegate>
kpark-apple153a5642022-02-22 07:19:20 -080084@property (nonatomic, strong) XCTestExpectation * expectation;
85@end
86
Boris Zbarskyf67c1b72022-11-16 11:03:02 -050087@implementation MTRDeviceTestDeviceControllerDelegate
kpark-apple153a5642022-02-22 07:19:20 -080088- (id)initWithExpectation:(XCTestExpectation *)expectation
89{
90 self = [super init];
91 if (self) {
92 _expectation = expectation;
93 }
94 return self;
95}
96
Boris Zbarsky3198f012022-11-18 00:04:13 -050097- (void)controller:(MTRDeviceController *)controller commissioningSessionEstablishmentDone:(NSError *)error
kpark-apple153a5642022-02-22 07:19:20 -080098{
99 XCTAssertEqual(error.code, 0);
Boris Zbarsky68518ce2022-05-06 10:46:29 -0400100
101 NSError * commissionError = nil;
Boris Zbarskyfb865cc2022-11-09 08:09:37 -0500102 [sController commissionNodeWithID:@(kDeviceId)
103 commissioningParams:[[MTRCommissioningParameters alloc] init]
104 error:&commissionError];
Boris Zbarsky68518ce2022-05-06 10:46:29 -0400105 XCTAssertNil(commissionError);
106
Boris Zbarsky3198f012022-11-18 00:04:13 -0500107 // Keep waiting for controller:MTRXPCListenerSampleTests.mcommissioningComplete
kpark-apple153a5642022-02-22 07:19:20 -0800108}
109
Boris Zbarsky3198f012022-11-18 00:04:13 -0500110- (void)controller:(MTRDeviceController *)controller commissioningComplete:(NSError *)error
kpark-apple153a5642022-02-22 07:19:20 -0800111{
112 XCTAssertEqual(error.code, 0);
113 [_expectation fulfill];
114 _expectation = nil;
115}
116
kpark-apple153a5642022-02-22 07:19:20 -0800117@end
118
Boris Zbarskya92b9522022-11-01 20:57:35 -0400119@interface MTRDeviceTestDelegate : NSObject <MTRDeviceDelegate>
120@property (nonatomic) dispatch_block_t onSubscriptionEstablished;
121@end
122
123@implementation MTRDeviceTestDelegate
124- (void)device:(MTRDevice *)device stateChanged:(MTRDeviceState)state
125{
126 if (state == MTRDeviceStateReachable) {
127 self.onSubscriptionEstablished();
128 }
129}
130
131- (void)device:(MTRDevice *)device receivedAttributeReport:(NSArray<NSDictionary<NSString *, id> *> *)attributeReport
132{
133}
134
135- (void)device:(MTRDevice *)device receivedEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventReport
136{
137}
138
139@end
140
Justin Woodf5c12c92022-07-02 00:43:40 -0700141@interface MTRDeviceTests : XCTestCase
kpark-apple153a5642022-02-22 07:19:20 -0800142@end
143
Justin Woodf5c12c92022-07-02 00:43:40 -0700144@implementation MTRDeviceTests
kpark-apple153a5642022-02-22 07:19:20 -0800145
146- (void)setUp
147{
148 [super setUp];
149 [self setContinueAfterFailure:NO];
150}
151
152- (void)tearDown
153{
154#if MANUAL_INDIVIDUAL_TEST
155 [self shutdownStack];
156#endif
157 [super tearDown];
158}
159
160- (void)initStack
161{
162 XCTestExpectation * expectation = [self expectationWithDescription:@"Pairing Complete"];
163
Boris Zbarskyc4042a32022-11-07 16:37:58 -0500164 __auto_type * factory = [MTRDeviceControllerFactory sharedInstance];
Boris Zbarskydfd71fe2022-04-14 23:29:44 -0400165 XCTAssertNotNil(factory);
166
Justin Woodf5c12c92022-07-02 00:43:40 -0700167 __auto_type * storage = [[MTRTestStorage alloc] init];
Boris Zbarskyc4042a32022-11-07 16:37:58 -0500168 __auto_type * factoryParams = [[MTRDeviceControllerFactoryParams alloc] initWithStorage:storage];
Boris Zbarskydfd71fe2022-04-14 23:29:44 -0400169 factoryParams.port = @(kLocalPort);
170
Boris Zbarskyc4042a32022-11-07 16:37:58 -0500171 BOOL ok = [factory startControllerFactory:factoryParams error:nil];
Boris Zbarskydfd71fe2022-04-14 23:29:44 -0400172 XCTAssertTrue(ok);
173
Justin Woodf5c12c92022-07-02 00:43:40 -0700174 __auto_type * testKeys = [[MTRTestKeys alloc] init];
Boris Zbarskyc880e282022-05-09 13:54:37 -0400175 XCTAssertNotNil(testKeys);
176
Boris Zbarskya7475d32022-10-11 10:22:56 -0400177 sTestKeys = testKeys;
178
Boris Zbarsky12802fb2022-10-12 21:45:38 -0400179 // Needs to match what startControllerOnExistingFabric calls elsewhere in
Boris Zbarskya7475d32022-10-11 10:22:56 -0400180 // this file do.
Boris Zbarskyc4042a32022-11-07 16:37:58 -0500181 __auto_type * params = [[MTRDeviceControllerStartupParams alloc] initWithIPK:testKeys.ipk fabricID:@(1) nocSigner:testKeys];
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400182 params.vendorID = @(kTestVendorId);
Boris Zbarskydfd71fe2022-04-14 23:29:44 -0400183
Boris Zbarskyc4042a32022-11-07 16:37:58 -0500184 MTRDeviceController * controller = [factory createControllerOnNewFabric:params error:nil];
kpark-apple153a5642022-02-22 07:19:20 -0800185 XCTAssertNotNil(controller);
186
Boris Zbarskydfd71fe2022-04-14 23:29:44 -0400187 sController = controller;
188
Boris Zbarskyf67c1b72022-11-16 11:03:02 -0500189 MTRDeviceTestDeviceControllerDelegate * deviceControllerDelegate =
190 [[MTRDeviceTestDeviceControllerDelegate alloc] initWithExpectation:expectation];
191 dispatch_queue_t callbackQueue = dispatch_queue_create("com.chip.device_controller_delegate", DISPATCH_QUEUE_SERIAL);
kpark-apple153a5642022-02-22 07:19:20 -0800192
Boris Zbarskyf67c1b72022-11-16 11:03:02 -0500193 [controller setDeviceControllerDelegate:deviceControllerDelegate queue:callbackQueue];
kpark-apple153a5642022-02-22 07:19:20 -0800194
kpark-apple153a5642022-02-22 07:19:20 -0800195 NSError * error;
Boris Zbarskybd5956d2022-10-13 12:12:49 -0400196 __auto_type * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:kOnboardingPayload error:&error];
197 XCTAssertNotNil(payload);
198 XCTAssertNil(error);
199
200 [controller setupCommissioningSessionWithPayload:payload newNodeID:@(kDeviceId) error:&error];
201 XCTAssertNil(error);
kpark-apple153a5642022-02-22 07:19:20 -0800202
203 [self waitForExpectationsWithTimeout:kPairingTimeoutInSeconds handler:nil];
kpark-apple153a5642022-02-22 07:19:20 -0800204}
205
206- (void)shutdownStack
207{
Justin Woodf5c12c92022-07-02 00:43:40 -0700208 MTRDeviceController * controller = sController;
kpark-apple153a5642022-02-22 07:19:20 -0800209 XCTAssertNotNil(controller);
210
Boris Zbarskydfd71fe2022-04-14 23:29:44 -0400211 [controller shutdown];
212 XCTAssertFalse([controller isRunning]);
213
Boris Zbarskyc4042a32022-11-07 16:37:58 -0500214 [[MTRDeviceControllerFactory sharedInstance] stopControllerFactory];
kpark-apple153a5642022-02-22 07:19:20 -0800215}
216
217- (void)waitForCommissionee
218{
219 XCTestExpectation * expectation = [self expectationWithDescription:@"Wait for the commissioned device to be retrieved"];
220
Boris Zbarsky57570682022-09-14 14:56:13 -0400221 WaitForCommissionee(expectation);
kpark-apple153a5642022-02-22 07:19:20 -0800222 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
223}
224
225#if !MANUAL_INDIVIDUAL_TEST
226- (void)test000_SetUp
227{
228 [self initStack];
229 [self waitForCommissionee];
230}
231#endif
232
233- (void)test001_ReadAttribute
234{
235#if MANUAL_INDIVIDUAL_TEST
236 [self initStack];
237 [self waitForCommissionee];
238#endif
239 XCTestExpectation * expectation =
240 [self expectationWithDescription:@"read DeviceDescriptor DeviceType attribute for all endpoints"];
241
Jeff Tungae2913f2022-07-06 19:02:55 -0700242 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800243 dispatch_queue_t queue = dispatch_get_main_queue();
244
Boris Zbarskyd0664632022-11-04 02:18:28 -0400245 [device readAttributesWithEndpointID:nil
246 clusterID:@29
247 attributeID:@0
248 params:nil
249 queue:queue
250 completion:^(id _Nullable values, NSError * _Nullable error) {
251 NSLog(@"read attribute: DeviceType values: %@, error: %@", values, error);
kpark-apple153a5642022-02-22 07:19:20 -0800252
Boris Zbarskyd0664632022-11-04 02:18:28 -0400253 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
kpark-apple153a5642022-02-22 07:19:20 -0800254
Boris Zbarskyd0664632022-11-04 02:18:28 -0400255 {
256 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
257 NSArray * resultArray = values;
258 for (NSDictionary * result in resultArray) {
259 MTRAttributePath * path = result[@"attributePath"];
260 XCTAssertEqual([path.cluster unsignedIntegerValue], 29);
261 XCTAssertEqual([path.attribute unsignedIntegerValue], 0);
262 XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]);
263 XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Array"]);
264 }
265 XCTAssertTrue([resultArray count] > 0);
266 }
kpark-apple153a5642022-02-22 07:19:20 -0800267
Boris Zbarskyd0664632022-11-04 02:18:28 -0400268 [expectation fulfill];
269 }];
kpark-apple153a5642022-02-22 07:19:20 -0800270
271 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
272}
273
274- (void)test002_WriteAttribute
275{
276#if MANUAL_INDIVIDUAL_TEST
277 [self initStack];
278 [self waitForCommissionee];
279#endif
280 XCTestExpectation * expectation = [self expectationWithDescription:@"write LevelControl Brightness attribute"];
281
Jeff Tungae2913f2022-07-06 19:02:55 -0700282 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800283 dispatch_queue_t queue = dispatch_get_main_queue();
284
285 NSDictionary * writeValue = [NSDictionary
286 dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", [NSNumber numberWithUnsignedInteger:200], @"value", nil];
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400287 [device writeAttributeWithEndpointID:@1
288 clusterID:@8
289 attributeID:@17
kpark-apple153a5642022-02-22 07:19:20 -0800290 value:writeValue
Kundok Parka72ab9b2022-03-21 12:12:47 -0700291 timedWriteTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -0400292 queue:queue
kpark-apple153a5642022-02-22 07:19:20 -0800293 completion:^(id _Nullable values, NSError * _Nullable error) {
294 NSLog(@"write attribute: Brightness values: %@, error: %@", values, error);
295
Justin Woodf5c12c92022-07-02 00:43:40 -0700296 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
kpark-apple153a5642022-02-22 07:19:20 -0800297
298 {
299 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
300 NSArray * resultArray = values;
301 for (NSDictionary * result in resultArray) {
Justin Woodf5c12c92022-07-02 00:43:40 -0700302 MTRAttributePath * path = result[@"attributePath"];
Kundok Parkeac433a2022-03-15 10:05:54 -0700303 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
304 XCTAssertEqual([path.cluster unsignedIntegerValue], 8);
305 XCTAssertEqual([path.attribute unsignedIntegerValue], 17);
306 XCTAssertNil(result[@"error"]);
kpark-apple153a5642022-02-22 07:19:20 -0800307 }
308 XCTAssertEqual([resultArray count], 1);
309 }
310
311 [expectation fulfill];
312 }];
313
314 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
315}
316
317- (void)test003_InvokeCommand
318{
319#if MANUAL_INDIVIDUAL_TEST
320 [self initStack];
321 [self waitForCommissionee];
322#endif
323 XCTestExpectation * expectation = [self expectationWithDescription:@"invoke MoveToLevelWithOnOff command"];
324
Jeff Tungae2913f2022-07-06 19:02:55 -0700325 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800326 dispatch_queue_t queue = dispatch_get_main_queue();
327
Kundok Parkeac433a2022-03-15 10:05:54 -0700328 NSDictionary * fields = @{
329 @"type" : @"Structure",
330 @"value" : @[
331 @{ @"contextTag" : @0, @"data" : @ { @"type" : @"UnsignedInteger", @"value" : @0 } },
332 @{ @"contextTag" : @1, @"data" : @ { @"type" : @"UnsignedInteger", @"value" : @10 } }
333 ]
334 };
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400335 [device invokeCommandWithEndpointID:@1
336 clusterID:@8
337 commandID:@4
kpark-apple153a5642022-02-22 07:19:20 -0800338 commandFields:fields
Kundok Parka72ab9b2022-03-21 12:12:47 -0700339 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -0400340 queue:queue
kpark-apple153a5642022-02-22 07:19:20 -0800341 completion:^(id _Nullable values, NSError * _Nullable error) {
342 NSLog(@"invoke command: MoveToLevelWithOnOff values: %@, error: %@", values, error);
343
Justin Woodf5c12c92022-07-02 00:43:40 -0700344 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
kpark-apple153a5642022-02-22 07:19:20 -0800345
346 {
347 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
348 NSArray * resultArray = values;
349 for (NSDictionary * result in resultArray) {
Justin Woodf5c12c92022-07-02 00:43:40 -0700350 MTRCommandPath * path = result[@"commandPath"];
Kundok Parkeac433a2022-03-15 10:05:54 -0700351 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
352 XCTAssertEqual([path.cluster unsignedIntegerValue], 8);
353 XCTAssertEqual([path.command unsignedIntegerValue], 4);
354 XCTAssertNil(result[@"error"]);
kpark-apple153a5642022-02-22 07:19:20 -0800355 }
356 XCTAssertEqual([resultArray count], 1);
357 }
358
359 [expectation fulfill];
360 }];
361
362 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
363}
364
Boris Zbarskyb5ca1252022-08-11 11:01:13 -0400365- (void)test004_InvokeTimedCommand
366{
367#if MANUAL_INDIVIDUAL_TEST
368 [self initStack];
369 [self waitForCommissionee];
370#endif
371 XCTestExpectation * expectation = [self expectationWithDescription:@"invoke Off command"];
372
373 MTRBaseDevice * device = GetConnectedDevice();
374 dispatch_queue_t queue = dispatch_get_main_queue();
375
376 NSDictionary * fields = @{
377 @"type" : @"Structure",
378 @"value" : @[],
379 };
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400380 [device invokeCommandWithEndpointID:@1
381 clusterID:@6
382 commandID:@0
Boris Zbarskyb5ca1252022-08-11 11:01:13 -0400383 commandFields:fields
384 timedInvokeTimeout:@10000
Boris Zbarsky35187602022-11-02 18:19:46 -0400385 queue:queue
Boris Zbarskyb5ca1252022-08-11 11:01:13 -0400386 completion:^(id _Nullable values, NSError * _Nullable error) {
387 NSLog(@"invoke command: Off values: %@, error: %@", values, error);
388
389 XCTAssertNil(error);
390
391 {
392 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
393 NSArray * resultArray = values;
394 for (NSDictionary * result in resultArray) {
395 MTRCommandPath * path = result[@"commandPath"];
396 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
397 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
398 XCTAssertEqual([path.command unsignedIntegerValue], 0);
399 XCTAssertNil(result[@"error"]);
400 }
401 XCTAssertEqual([resultArray count], 1);
402 }
403
404 [expectation fulfill];
405 }];
406
407 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
408}
409
kpark-apple153a5642022-02-22 07:19:20 -0800410static void (^globalReportHandler)(id _Nullable values, NSError * _Nullable error) = nil;
411
kpark-apple153a5642022-02-22 07:19:20 -0800412- (void)test005_Subscribe
413{
414#if MANUAL_INDIVIDUAL_TEST
415 [self initStack];
416 [self waitForCommissionee];
417#endif
Jeff Tungae2913f2022-07-06 19:02:55 -0700418 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800419 dispatch_queue_t queue = dispatch_get_main_queue();
420
kpark-apple153a5642022-02-22 07:19:20 -0800421 // Subscribe
422 XCTestExpectation * expectation = [self expectationWithDescription:@"subscribe OnOff attribute"];
Boris Zbarskyc530f632022-11-04 17:03:57 -0400423 __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(10)];
Boris Zbarskyd0664632022-11-04 02:18:28 -0400424 [device subscribeToAttributesWithEndpointID:@1
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400425 clusterID:@6
426 attributeID:@0
Boris Zbarskyc530f632022-11-04 17:03:57 -0400427 params:params
Boris Zbarsky35187602022-11-02 18:19:46 -0400428 queue:queue
kpark-apple153a5642022-02-22 07:19:20 -0800429 reportHandler:^(id _Nullable values, NSError * _Nullable error) {
430 NSLog(@"report attribute: OnOff values: %@, error: %@", values, error);
431
432 if (globalReportHandler) {
433 __auto_type callback = globalReportHandler;
434 callback(values, error);
435 }
436 }
437 subscriptionEstablished:^{
438 NSLog(@"subscribe attribute: OnOff established");
439 [expectation fulfill];
440 }];
441
442 // Wait till establishment
443 [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:kTimeoutInSeconds];
kpark-apple153a5642022-02-22 07:19:20 -0800444
445 // Set up expectation for report
446 XCTestExpectation * reportExpectation = [self expectationWithDescription:@"report received"];
Kundok Parka72ab9b2022-03-21 12:12:47 -0700447 globalReportHandler = ^(id _Nullable values, NSError * _Nullable error) {
Justin Woodf5c12c92022-07-02 00:43:40 -0700448 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
Kundok Parka72ab9b2022-03-21 12:12:47 -0700449 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
450 NSDictionary * result = values[0];
Justin Woodf5c12c92022-07-02 00:43:40 -0700451 MTRAttributePath * path = result[@"attributePath"];
Kundok Parkeac433a2022-03-15 10:05:54 -0700452 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
453 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
454 XCTAssertEqual([path.attribute unsignedIntegerValue], 0);
455 XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]);
456 XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Boolean"]);
457 if ([result[@"data"][@"value"] boolValue] == YES) {
kpark-apple153a5642022-02-22 07:19:20 -0800458 [reportExpectation fulfill];
459 globalReportHandler = nil;
460 }
461 };
462
463 // Send commands to trigger attribute change
464 XCTestExpectation * commandExpectation = [self expectationWithDescription:@"command responded"];
Kundok Parkeac433a2022-03-15 10:05:54 -0700465 NSDictionary * fields = @{ @"type" : @"Structure", @"value" : [NSArray array] };
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400466 [device invokeCommandWithEndpointID:@1
467 clusterID:@6
468 commandID:@1
kpark-apple153a5642022-02-22 07:19:20 -0800469 commandFields:fields
Kundok Parka72ab9b2022-03-21 12:12:47 -0700470 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -0400471 queue:queue
kpark-apple153a5642022-02-22 07:19:20 -0800472 completion:^(id _Nullable values, NSError * _Nullable error) {
473 NSLog(@"invoke command: On values: %@, error: %@", values, error);
474
Justin Woodf5c12c92022-07-02 00:43:40 -0700475 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
kpark-apple153a5642022-02-22 07:19:20 -0800476
477 {
478 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
479 NSArray * resultArray = values;
480 for (NSDictionary * result in resultArray) {
Justin Woodf5c12c92022-07-02 00:43:40 -0700481 MTRCommandPath * path = result[@"commandPath"];
Kundok Parkeac433a2022-03-15 10:05:54 -0700482 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
483 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
484 XCTAssertEqual([path.command unsignedIntegerValue], 1);
485 XCTAssertNil(result[@"error"]);
kpark-apple153a5642022-02-22 07:19:20 -0800486 }
487 XCTAssertEqual([resultArray count], 1);
488 }
489 [commandExpectation fulfill];
490 }];
491 [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds];
492
493 // Wait for report
494 [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
495
496 // Set up expectation for 2nd report
497 reportExpectation = [self expectationWithDescription:@"receive OnOff attribute report"];
Kundok Parka72ab9b2022-03-21 12:12:47 -0700498 globalReportHandler = ^(id _Nullable values, NSError * _Nullable error) {
Justin Woodf5c12c92022-07-02 00:43:40 -0700499 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
Kundok Parka72ab9b2022-03-21 12:12:47 -0700500 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
501 NSDictionary * result = values[0];
Justin Woodf5c12c92022-07-02 00:43:40 -0700502 MTRAttributePath * path = result[@"attributePath"];
Kundok Parkeac433a2022-03-15 10:05:54 -0700503 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
504 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
505 XCTAssertEqual([path.attribute unsignedIntegerValue], 0);
506 XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]);
507 XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Boolean"]);
508 if ([result[@"data"][@"value"] boolValue] == NO) {
kpark-apple153a5642022-02-22 07:19:20 -0800509 [reportExpectation fulfill];
510 globalReportHandler = nil;
511 }
512 };
513
514 // Send command to trigger attribute change
515 fields = [NSDictionary dictionaryWithObjectsAndKeys:@"Structure", @"type", [NSArray array], @"value", nil];
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400516 [device invokeCommandWithEndpointID:@1
517 clusterID:@6
518 commandID:@0
kpark-apple153a5642022-02-22 07:19:20 -0800519 commandFields:fields
Kundok Parka72ab9b2022-03-21 12:12:47 -0700520 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -0400521 queue:queue
kpark-apple153a5642022-02-22 07:19:20 -0800522 completion:^(id _Nullable values, NSError * _Nullable error) {
523 NSLog(@"invoke command: On values: %@, error: %@", values, error);
524
Justin Woodf5c12c92022-07-02 00:43:40 -0700525 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
kpark-apple153a5642022-02-22 07:19:20 -0800526
527 {
528 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
529 NSArray * resultArray = values;
530 for (NSDictionary * result in resultArray) {
Justin Woodf5c12c92022-07-02 00:43:40 -0700531 MTRCommandPath * path = result[@"commandPath"];
Kundok Parkeac433a2022-03-15 10:05:54 -0700532 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
533 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
534 XCTAssertEqual([path.command unsignedIntegerValue], 0);
535 XCTAssertNil(result[@"error"]);
kpark-apple153a5642022-02-22 07:19:20 -0800536 }
537 XCTAssertEqual([resultArray count], 1);
538 }
539 }];
540
541 // Wait for report
542 [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
Kundok Parkeac433a2022-03-15 10:05:54 -0700543
544 expectation = [self expectationWithDescription:@"Report handler deregistered"];
Boris Zbarsky35187602022-11-02 18:19:46 -0400545 [device deregisterReportHandlersWithQueue:queue
546 completion:^{
547 [expectation fulfill];
548 }];
Kundok Parkeac433a2022-03-15 10:05:54 -0700549 [self waitForExpectations:@[ expectation ] timeout:kTimeoutInSeconds];
kpark-apple153a5642022-02-22 07:19:20 -0800550}
kpark-apple153a5642022-02-22 07:19:20 -0800551
552- (void)test006_ReadAttributeFailure
553{
554#if MANUAL_INDIVIDUAL_TEST
555 [self initStack];
556 [self waitForCommissionee];
557#endif
558 XCTestExpectation * expectation = [self expectationWithDescription:@"read failed"];
559
Jeff Tungae2913f2022-07-06 19:02:55 -0700560 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800561 dispatch_queue_t queue = dispatch_get_main_queue();
562
Boris Zbarsky12802fb2022-10-12 21:45:38 -0400563 [device
Boris Zbarskyd0664632022-11-04 02:18:28 -0400564 readAttributesWithEndpointID:@0
565 clusterID:@10000
566 attributeID:@0
567 params:nil
568 queue:queue
569 completion:^(id _Nullable values, NSError * _Nullable error) {
570 NSLog(@"read attribute: DeviceType values: %@, error: %@", values, error);
kpark-apple153a5642022-02-22 07:19:20 -0800571
Boris Zbarskyd0664632022-11-04 02:18:28 -0400572 XCTAssertNil(values);
573 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], EMBER_ZCL_STATUS_UNSUPPORTED_CLUSTER);
kpark-apple153a5642022-02-22 07:19:20 -0800574
Boris Zbarskyd0664632022-11-04 02:18:28 -0400575 [expectation fulfill];
576 }];
kpark-apple153a5642022-02-22 07:19:20 -0800577
578 [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:kTimeoutInSeconds];
579}
580
581- (void)test007_WriteAttributeFailure
582{
583#if MANUAL_INDIVIDUAL_TEST
584 [self initStack];
585 [self waitForCommissionee];
586#endif
587 XCTestExpectation * expectation = [self expectationWithDescription:@"write failed"];
588
Jeff Tungae2913f2022-07-06 19:02:55 -0700589 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800590 dispatch_queue_t queue = dispatch_get_main_queue();
591
592 NSDictionary * writeValue = [NSDictionary
593 dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", [NSNumber numberWithUnsignedInteger:200], @"value", nil];
Justin Woodf5c12c92022-07-02 00:43:40 -0700594 [device
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400595 writeAttributeWithEndpointID:@1
596 clusterID:@8
597 attributeID:@10000
Justin Woodf5c12c92022-07-02 00:43:40 -0700598 value:writeValue
599 timedWriteTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -0400600 queue:queue
Justin Woodf5c12c92022-07-02 00:43:40 -0700601 completion:^(id _Nullable values, NSError * _Nullable error) {
602 NSLog(@"write attribute: Brightness values: %@, error: %@", values, error);
kpark-apple153a5642022-02-22 07:19:20 -0800603
Justin Woodf5c12c92022-07-02 00:43:40 -0700604 XCTAssertNil(values);
605 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE);
kpark-apple153a5642022-02-22 07:19:20 -0800606
Justin Woodf5c12c92022-07-02 00:43:40 -0700607 [expectation fulfill];
608 }];
kpark-apple153a5642022-02-22 07:19:20 -0800609
610 [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:kTimeoutInSeconds];
611}
612
kpark-apple153a5642022-02-22 07:19:20 -0800613- (void)test008_InvokeCommandFailure
614{
615#if MANUAL_INDIVIDUAL_TEST
616 [self initStack];
617 [self waitForCommissionee];
618#endif
619 XCTestExpectation * expectation = [self expectationWithDescription:@"invoke MoveToLevelWithOnOff command"];
620
Jeff Tungae2913f2022-07-06 19:02:55 -0700621 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800622 dispatch_queue_t queue = dispatch_get_main_queue();
623
Boris Zbarskyb9e1e7a2022-08-11 17:48:33 -0400624 NSDictionary * fields = @{
625 @"type" : @"Structure",
626 @"value" : @[
627 @{ @"contextTag" : @0, @"data" : @ { @"type" : @"UnsignedInteger", @"value" : @0 } },
628 @{ @"contextTag" : @1, @"data" : @ { @"type" : @"UnsignedInteger", @"value" : @10 } }
Kundok Parka72ab9b2022-03-21 12:12:47 -0700629 ]
630 };
631 [device
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400632 invokeCommandWithEndpointID:@1
633 clusterID:@8
634 commandID:@40000
Boris Zbarskyb9e1e7a2022-08-11 17:48:33 -0400635 commandFields:fields
636 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -0400637 queue:queue
Boris Zbarskyb9e1e7a2022-08-11 17:48:33 -0400638 completion:^(id _Nullable values, NSError * _Nullable error) {
639 NSLog(@"invoke command: MoveToLevelWithOnOff values: %@, error: %@", values, error);
kpark-apple153a5642022-02-22 07:19:20 -0800640
Boris Zbarskyb9e1e7a2022-08-11 17:48:33 -0400641 XCTAssertNil(values);
642 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], EMBER_ZCL_STATUS_UNSUPPORTED_COMMAND);
kpark-apple153a5642022-02-22 07:19:20 -0800643
Boris Zbarskyb9e1e7a2022-08-11 17:48:33 -0400644 [expectation fulfill];
645 }];
kpark-apple153a5642022-02-22 07:19:20 -0800646
647 [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:kTimeoutInSeconds];
648}
kpark-apple153a5642022-02-22 07:19:20 -0800649
650- (void)test009_SubscribeFailure
651{
652#if MANUAL_INDIVIDUAL_TEST
653 [self initStack];
654 [self waitForCommissionee];
655#endif
656 XCTestExpectation * expectation = [self expectationWithDescription:@"subscribe OnOff attribute"];
657 __block void (^reportHandler)(id _Nullable values, NSError * _Nullable error) = nil;
658
659 // Set up expectation for report
yunhanw-googlee74bb942022-09-08 12:24:57 -0700660 XCTestExpectation * errorReportExpectation = [self expectationWithDescription:@"receive subscription error"];
kpark-apple153a5642022-02-22 07:19:20 -0800661 reportHandler = ^(id _Nullable value, NSError * _Nullable error) {
yunhanw-googlee74bb942022-09-08 12:24:57 -0700662 // Because our subscription has no existent paths, it gets an
663 // InvalidAction response, which is EMBER_ZCL_STATUS_MALFORMED_COMMAND.
kpark-apple153a5642022-02-22 07:19:20 -0800664 XCTAssertNil(value);
yunhanw-googlee74bb942022-09-08 12:24:57 -0700665 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], EMBER_ZCL_STATUS_MALFORMED_COMMAND);
kpark-apple153a5642022-02-22 07:19:20 -0800666 [errorReportExpectation fulfill];
667 };
668
Jeff Tungae2913f2022-07-06 19:02:55 -0700669 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800670 dispatch_queue_t queue = dispatch_get_main_queue();
671
Kundok Park0048ee22022-03-04 17:45:14 -0800672 XCTestExpectation * cleanSubscriptionExpectation = [self expectationWithDescription:@"Previous subscriptions cleaned"];
673 NSLog(@"Deregistering report handlers...");
Boris Zbarsky35187602022-11-02 18:19:46 -0400674 [device deregisterReportHandlersWithQueue:queue
675 completion:^{
676 NSLog(@"Report handlers deregistered");
677 [cleanSubscriptionExpectation fulfill];
678 }];
Kundok Park0048ee22022-03-04 17:45:14 -0800679 [self waitForExpectations:@[ cleanSubscriptionExpectation ] timeout:kTimeoutInSeconds];
680
Boris Zbarskyc530f632022-11-04 17:03:57 -0400681 __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(2) maxInterval:@(10)];
682 params.resubscribeIfLost = NO;
Boris Zbarskyd0664632022-11-04 02:18:28 -0400683 [device subscribeToAttributesWithEndpointID:@10000
Boris Zbarskyf1ebea82022-10-29 04:46:26 -0400684 clusterID:@6
685 attributeID:@0
yunhanw-googlee74bb942022-09-08 12:24:57 -0700686 params:params
Boris Zbarsky35187602022-11-02 18:19:46 -0400687 queue:queue
kpark-apple153a5642022-02-22 07:19:20 -0800688 reportHandler:^(id _Nullable values, NSError * _Nullable error) {
689 NSLog(@"report attribute: OnOff values: %@, error: %@", values, error);
690
691 if (reportHandler) {
692 __auto_type callback = reportHandler;
693 reportHandler = nil;
694 callback(values, error);
695 }
696 }
697 subscriptionEstablished:^{
698 NSLog(@"subscribe attribute: OnOff established");
699 [expectation fulfill];
700 }];
701
702 // Wait till establishment and error report
703 [self waitForExpectations:[NSArray arrayWithObjects:expectation, errorReportExpectation, nil] timeout:kTimeoutInSeconds];
704}
705
706- (void)test010_ReadAllAttribute
707{
708#if MANUAL_INDIVIDUAL_TEST
709 [self initStack];
710 [self waitForCommissionee];
711#endif
712 XCTestExpectation * expectation =
713 [self expectationWithDescription:@"read DeviceDescriptor DeviceType attribute for all endpoints"];
714
Jeff Tungae2913f2022-07-06 19:02:55 -0700715 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -0800716 dispatch_queue_t queue = dispatch_get_main_queue();
717
Boris Zbarskyd0664632022-11-04 02:18:28 -0400718 [device readAttributesWithEndpointID:@1
719 clusterID:@29
720 attributeID:nil
721 params:nil
722 queue:queue
723 completion:^(id _Nullable values, NSError * _Nullable error) {
724 NSLog(@"read attribute: DeviceType values: %@, error: %@", values, error);
kpark-apple153a5642022-02-22 07:19:20 -0800725
Boris Zbarskyd0664632022-11-04 02:18:28 -0400726 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
kpark-apple153a5642022-02-22 07:19:20 -0800727
Boris Zbarskyd0664632022-11-04 02:18:28 -0400728 {
729 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
730 NSArray * resultArray = values;
731 for (NSDictionary * result in resultArray) {
732 MTRAttributePath * path = result[@"attributePath"];
733 XCTAssertEqual([path.cluster unsignedIntegerValue], 29);
734 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
735 XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]);
736 }
737 XCTAssertTrue([resultArray count] > 0);
738 }
kpark-apple153a5642022-02-22 07:19:20 -0800739
Boris Zbarskyd0664632022-11-04 02:18:28 -0400740 [expectation fulfill];
741 }];
kpark-apple153a5642022-02-22 07:19:20 -0800742
743 [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:kTimeoutInSeconds];
744}
745
Kundok Park0048ee22022-03-04 17:45:14 -0800746- (void)test011_ReadCachedAttribute
747{
748#if MANUAL_INDIVIDUAL_TEST
749 [self initStack];
750 [self waitForCommissionee];
751#endif
752
Jeff Tungae2913f2022-07-06 19:02:55 -0700753 MTRBaseDevice * device = GetConnectedDevice();
Kundok Park0048ee22022-03-04 17:45:14 -0800754 dispatch_queue_t queue = dispatch_get_main_queue();
755 XCTestExpectation * cleanSubscriptionExpectation = [self expectationWithDescription:@"Previous subscriptions cleaned"];
756 NSLog(@"Deregistering report handlers...");
Boris Zbarsky35187602022-11-02 18:19:46 -0400757 [device deregisterReportHandlersWithQueue:queue
758 completion:^{
759 NSLog(@"Report handlers deregistered");
760 [cleanSubscriptionExpectation fulfill];
761 }];
Kundok Park0048ee22022-03-04 17:45:14 -0800762 [self waitForExpectations:@[ cleanSubscriptionExpectation ] timeout:kTimeoutInSeconds];
763
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500764 __auto_type clusterStateCacheContainer = [[MTRClusterStateCacheContainer alloc] init];
Justin Woodf5c12c92022-07-02 00:43:40 -0700765 MTRDeviceController * controller = sController;
Kundok Park0048ee22022-03-04 17:45:14 -0800766 XCTAssertNotNil(controller);
767 XCTestExpectation * subscribeExpectation = [self expectationWithDescription:@"Subscription complete"];
768
769 NSLog(@"Subscribing...");
Jerry-ESPb810f772022-10-12 22:56:46 +0800770 // reportHandler returns TRUE if it got the things it was looking for or if there's an error.
771 __block BOOL (^reportHandler)(NSArray * _Nullable value, NSError * _Nullable error);
Boris Zbarskyc530f632022-11-04 17:03:57 -0400772 __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(2) maxInterval:@(60)];
Boris Zbarskyc6272c62022-11-17 11:33:08 -0500773 params.resubscribeIfLost = NO;
Kundok Parkf34d2332022-03-24 11:52:49 -0700774 [device subscribeWithQueue:queue
Boris Zbarskyc530f632022-11-04 17:03:57 -0400775 params:params
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500776 clusterStateCacheContainer:clusterStateCacheContainer
Justin Woodf5c12c92022-07-02 00:43:40 -0700777 attributeReportHandler:^(NSArray * value) {
778 NSLog(@"Received report: %@", value);
Kundok Parkf34d2332022-03-24 11:52:49 -0700779 if (reportHandler) {
780 __auto_type handler = reportHandler;
781 reportHandler = nil;
Jerry-ESPb810f772022-10-12 22:56:46 +0800782 BOOL done = handler(value, nil);
783 if (done == NO) {
784 // Keep waiting.
785 reportHandler = handler;
786 }
Kundok Parkf34d2332022-03-24 11:52:49 -0700787 }
788 }
Justin Woodf5c12c92022-07-02 00:43:40 -0700789 eventReportHandler:nil
790 errorHandler:^(NSError * error) {
791 NSLog(@"Received report error: %@", error);
792 if (reportHandler) {
793 __auto_type handler = reportHandler;
794 reportHandler = nil;
795 handler(nil, error);
796 }
797 }
798 subscriptionEstablished:^() {
Kundok Parkf34d2332022-03-24 11:52:49 -0700799 [subscribeExpectation fulfill];
Boris Zbarsky4fe81942022-09-02 20:10:37 -0400800 }
801 resubscriptionScheduled:nil];
Kundok Parkf34d2332022-03-24 11:52:49 -0700802 [self waitForExpectations:@[ subscribeExpectation ] timeout:60];
Kundok Park0048ee22022-03-04 17:45:14 -0800803
804 // Invoke command to set the attribute to a known state
805 XCTestExpectation * commandExpectation = [self expectationWithDescription:@"Command invoked"];
Boris Zbarsky09acc292022-10-28 14:05:35 -0400806 MTRBaseClusterOnOff * cluster = [[MTRBaseClusterOnOff alloc] initWithDevice:device endpointID:@(1) queue:queue];
Kundok Park0048ee22022-03-04 17:45:14 -0800807 XCTAssertNotNil(cluster);
808
809 NSLog(@"Invoking command...");
Boris Zbarsky35187602022-11-02 18:19:46 -0400810 [cluster onWithCompletion:^(NSError * _Nullable err) {
Kundok Park0048ee22022-03-04 17:45:14 -0800811 NSLog(@"Invoked command with error: %@", err);
Justin Woodf5c12c92022-07-02 00:43:40 -0700812 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:err], 0);
Kundok Park0048ee22022-03-04 17:45:14 -0800813 [commandExpectation fulfill];
814 }];
815 [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds];
816
Kundok Parkf34d2332022-03-24 11:52:49 -0700817 // Wait till reports arrive from accessory.
Kundok Park0048ee22022-03-04 17:45:14 -0800818 NSLog(@"Waiting for reports from accessory...");
Kundok Parkf34d2332022-03-24 11:52:49 -0700819 sleep(5);
Kundok Park0048ee22022-03-04 17:45:14 -0800820
821 // Read cache
822 NSLog(@"Reading from cache...");
823 XCTestExpectation * cacheExpectation = [self expectationWithDescription:@"Attribute cache read"];
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500824 [MTRBaseClusterOnOff readAttributeOnOffWithClusterStateCache:clusterStateCacheContainer
825 endpoint:@1
826 queue:queue
827 completion:^(NSNumber * _Nullable value, NSError * _Nullable err) {
828 NSLog(@"Read attribute cache value: %@, error: %@", value, err);
829 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:err], 0);
830 XCTAssertTrue([value isEqualToNumber:[NSNumber numberWithBool:YES]]);
831 [cacheExpectation fulfill];
832 }];
Kundok Park0048ee22022-03-04 17:45:14 -0800833 [self waitForExpectations:[NSArray arrayWithObject:cacheExpectation] timeout:kTimeoutInSeconds];
834
835 // Add another subscriber of the attribute to verify that attribute cache still works when there are other subscribers.
836 NSLog(@"New subscription...");
837 XCTestExpectation * newSubscriptionEstablished = [self expectationWithDescription:@"New subscription established"];
Boris Zbarskyc530f632022-11-04 17:03:57 -0400838 MTRSubscribeParams * newParams = [[MTRSubscribeParams alloc] initWithMinInterval:@(2) maxInterval:@(60)];
839 newParams.replaceExistingSubscriptions = NO;
Boris Zbarskyc6272c62022-11-17 11:33:08 -0500840 newParams.resubscribeIfLost = NO;
Boris Zbarskyc530f632022-11-04 17:03:57 -0400841 [cluster subscribeAttributeOnOffWithParams:newParams
Kundok Park0048ee22022-03-04 17:45:14 -0800842 subscriptionEstablished:^{
843 NSLog(@"New subscription was established");
844 [newSubscriptionEstablished fulfill];
845 }
846 reportHandler:^(NSNumber * _Nullable value, NSError * _Nullable error) {
847 NSLog(@"New subscriber received a report: %@, error: %@", value, error);
848 }];
849 [self waitForExpectations:[NSArray arrayWithObject:newSubscriptionEstablished] timeout:kTimeoutInSeconds];
850
Kundok Parkf34d2332022-03-24 11:52:49 -0700851 __auto_type reportExpectation = [self expectationWithDescription:@"Report handler called"];
852 reportHandler = ^(NSArray * _Nullable value, NSError * _Nullable error) {
Jerry-ESPb810f772022-10-12 22:56:46 +0800853 if (error != nil) {
854 return YES;
855 }
Kundok Parkf34d2332022-03-24 11:52:49 -0700856 NSLog(@"Report received: %@, error: %@", value, error);
Justin Woodf5c12c92022-07-02 00:43:40 -0700857 for (MTRAttributeReport * report in value) {
Kundok Parkf34d2332022-03-24 11:52:49 -0700858 if ([report.path.endpoint isEqualToNumber:@1] && [report.path.cluster isEqualToNumber:@6] &&
859 [report.path.attribute isEqualToNumber:@0]) {
860 NSLog(@"Report value for OnOff: %@", report.value);
861 XCTAssertNotNil(report.value);
862 XCTAssertTrue([report.value isKindOfClass:[NSNumber class]]);
863 XCTAssertEqual([report.value boolValue], NO);
864 [reportExpectation fulfill];
Jerry-ESPb810f772022-10-12 22:56:46 +0800865 return YES;
Kundok Parkf34d2332022-03-24 11:52:49 -0700866 }
867 }
Jerry-ESPb810f772022-10-12 22:56:46 +0800868
869 return NO;
Kundok Parkf34d2332022-03-24 11:52:49 -0700870 };
871
Kundok Park0048ee22022-03-04 17:45:14 -0800872 NSLog(@"Invoking another command...");
873 commandExpectation = [self expectationWithDescription:@"Command invoked"];
Boris Zbarsky35187602022-11-02 18:19:46 -0400874 [cluster offWithCompletion:^(NSError * _Nullable err) {
Kundok Park0048ee22022-03-04 17:45:14 -0800875 NSLog(@"Invoked command with error: %@", err);
Justin Woodf5c12c92022-07-02 00:43:40 -0700876 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:err], 0);
Kundok Park0048ee22022-03-04 17:45:14 -0800877 [commandExpectation fulfill];
878 }];
879 [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds];
880
881 // Wait till reports arrive from accessory.
882 NSLog(@"Waiting for reports from accessory...");
Kundok Parkf34d2332022-03-24 11:52:49 -0700883 [self waitForExpectations:@[ reportExpectation ] timeout:kTimeoutInSeconds];
Kundok Park0048ee22022-03-04 17:45:14 -0800884
885 NSLog(@"Disconnect accessory to test cache...");
Kundok Parkf34d2332022-03-24 11:52:49 -0700886 __auto_type idleExpectation = [self expectationWithDescription:@"Must not break out of idle"];
Kundok Park0048ee22022-03-04 17:45:14 -0800887 idleExpectation.inverted = YES;
888 [self waitForExpectations:[NSArray arrayWithObject:idleExpectation] timeout:10];
889
890 // Read cache
891 NSLog(@"Reading from cache...");
892 cacheExpectation = [self expectationWithDescription:@"Attribute cache read"];
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500893 [MTRBaseClusterOnOff readAttributeOnOffWithClusterStateCache:clusterStateCacheContainer
894 endpoint:@1
895 queue:queue
896 completion:^(NSNumber * _Nullable value, NSError * _Nullable err) {
897 NSLog(@"Read attribute cache value: %@, error: %@", value, err);
898 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:err], 0);
899 XCTAssertTrue([value isEqualToNumber:[NSNumber numberWithBool:NO]]);
900 [cacheExpectation fulfill];
901 }];
Kundok Park0048ee22022-03-04 17:45:14 -0800902 [self waitForExpectations:[NSArray arrayWithObject:cacheExpectation] timeout:kTimeoutInSeconds];
903
904 // Read from cache using generic path
905 NSLog(@"Reading from cache using generic path...");
906 cacheExpectation = [self expectationWithDescription:@"Attribute cache read"];
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500907 [clusterStateCacheContainer
908 readAttributesWithEndpointID:@1
909 clusterID:@6
910 attributeID:@0
911 queue:queue
912 completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
913 NSLog(@"Read attribute cache value: %@, error %@", values, error);
914 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
915 XCTAssertEqual([values count], 1);
916 MTRAttributePath * path = values[0][@"attributePath"];
917 XCTAssertEqual([path.endpoint unsignedShortValue], 1);
918 XCTAssertEqual([path.cluster unsignedLongValue], 6);
919 XCTAssertEqual([path.attribute unsignedLongValue], 0);
920 XCTAssertNil(values[0][@"error"]);
921 XCTAssertTrue([values[0][@"data"][@"type"] isEqualToString:@"Boolean"]);
922 XCTAssertEqual([values[0][@"data"][@"value"] boolValue], NO);
923 [cacheExpectation fulfill];
924 }];
Kundok Park0048ee22022-03-04 17:45:14 -0800925 [self waitForExpectations:@[ cacheExpectation ] timeout:kTimeoutInSeconds];
926
927 // Read from cache with wildcard path
928 NSLog(@"Reading from cache using wildcard endpoint...");
929 cacheExpectation = [self expectationWithDescription:@"Attribute cache read"];
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500930 [clusterStateCacheContainer
931 readAttributesWithEndpointID:nil
932 clusterID:@6
933 attributeID:@0
934 queue:queue
935 completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
936 NSLog(@"Read attribute cache value: %@, error %@", values, error);
937 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
938 XCTAssertTrue([values count] > 0);
939 for (NSDictionary<NSString *, id> * value in values) {
940 MTRAttributePath * path = value[@"attributePath"];
941 XCTAssertEqual([path.cluster unsignedLongValue], 6);
942 XCTAssertEqual([path.attribute unsignedLongValue], 0);
943 XCTAssertNil(value[@"error"]);
944 }
945 [cacheExpectation fulfill];
946 }];
Kundok Park0048ee22022-03-04 17:45:14 -0800947 [self waitForExpectations:@[ cacheExpectation ] timeout:kTimeoutInSeconds];
948
949 // Read from cache with wildcard path
950 NSLog(@"Reading from cache using wildcard cluster ID...");
951 cacheExpectation = [self expectationWithDescription:@"Attribute cache read"];
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500952 [clusterStateCacheContainer
953 readAttributesWithEndpointID:@1
954 clusterID:nil
955 attributeID:@0
956 queue:queue
957 completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
958 NSLog(@"Read attribute cache value: %@, error %@", values, error);
959 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
960 XCTAssertTrue([values count] > 0);
961 for (NSDictionary<NSString *, id> * value in values) {
962 MTRAttributePath * path = value[@"attributePath"];
963 XCTAssertEqual([path.endpoint unsignedShortValue], 1);
964 XCTAssertEqual([path.attribute unsignedLongValue], 0);
965 }
966 [cacheExpectation fulfill];
967 }];
Kundok Park0048ee22022-03-04 17:45:14 -0800968 [self waitForExpectations:@[ cacheExpectation ] timeout:kTimeoutInSeconds];
969
970 // Read from cache with wildcard path
971 NSLog(@"Reading from cache using wildcard attribute ID...");
972 cacheExpectation = [self expectationWithDescription:@"Attribute cache read"];
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500973 [clusterStateCacheContainer
974 readAttributesWithEndpointID:@1
975 clusterID:@6
976 attributeID:nil
977 queue:queue
978 completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
979 NSLog(@"Read attribute cache value: %@, error %@", values, error);
980 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
981 XCTAssertTrue([values count] > 0);
982 for (NSDictionary<NSString *, id> * value in values) {
983 MTRAttributePath * path = value[@"attributePath"];
984 XCTAssertEqual([path.endpoint unsignedShortValue], 1);
985 XCTAssertEqual([path.cluster unsignedLongValue], 6);
986 XCTAssertNil(value[@"error"]);
987 }
988 [cacheExpectation fulfill];
989 }];
Kundok Park0048ee22022-03-04 17:45:14 -0800990 [self waitForExpectations:@[ cacheExpectation ] timeout:kTimeoutInSeconds];
991
992 // Read from cache with wildcard path
993 NSLog(@"Reading from cache using wildcard endpoint ID and cluster ID...");
994 cacheExpectation = [self expectationWithDescription:@"Attribute cache read"];
Boris Zbarsky3edba4a2022-11-17 09:59:53 -0500995 [clusterStateCacheContainer
996 readAttributesWithEndpointID:nil
997 clusterID:nil
998 attributeID:@0
999 queue:queue
1000 completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
1001 NSLog(@"Read attribute cache value: %@, error %@", values, error);
1002 XCTAssertNotNil(error);
1003 [cacheExpectation fulfill];
1004 }];
Kundok Park0048ee22022-03-04 17:45:14 -08001005 [self waitForExpectations:@[ cacheExpectation ] timeout:kTimeoutInSeconds];
1006}
1007
Kundok Parkeac433a2022-03-15 10:05:54 -07001008#ifdef DEBUG
1009// Test an error to subscription
1010- (void)test012_SubscriptionError
1011{
kpark-apple153a5642022-02-22 07:19:20 -08001012#if MANUAL_INDIVIDUAL_TEST
Kundok Parkeac433a2022-03-15 10:05:54 -07001013 [self initStack];
1014 [self waitForCommissionee];
1015#endif
Jeff Tungae2913f2022-07-06 19:02:55 -07001016 MTRBaseDevice * device = GetConnectedDevice();
Kundok Parkeac433a2022-03-15 10:05:54 -07001017 dispatch_queue_t queue = dispatch_get_main_queue();
1018 XCTestExpectation * deregisterExpectation = [self expectationWithDescription:@"Report handler deregistered"];
Boris Zbarsky35187602022-11-02 18:19:46 -04001019 [device deregisterReportHandlersWithQueue:queue
1020 completion:^{
1021 [deregisterExpectation fulfill];
1022 }];
Kundok Parkeac433a2022-03-15 10:05:54 -07001023 [self waitForExpectations:@[ deregisterExpectation ] timeout:kTimeoutInSeconds];
1024
1025 // Subscribe
1026 XCTestExpectation * expectation = [self expectationWithDescription:@"subscribe OnOff attribute"];
Boris Zbarskyc530f632022-11-04 17:03:57 -04001027 MTRSubscribeParams * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(10)];
Boris Zbarskyc6272c62022-11-17 11:33:08 -05001028 params.resubscribeIfLost = NO;
Boris Zbarskyd0664632022-11-04 02:18:28 -04001029 [device subscribeToAttributesWithEndpointID:@1
Boris Zbarskyf1ebea82022-10-29 04:46:26 -04001030 clusterID:@6
1031 attributeID:@0
Boris Zbarskyc530f632022-11-04 17:03:57 -04001032 params:params
Boris Zbarsky35187602022-11-02 18:19:46 -04001033 queue:queue
Kundok Parkeac433a2022-03-15 10:05:54 -07001034 reportHandler:^(id _Nullable values, NSError * _Nullable error) {
1035 NSLog(@"report attribute: OnOff values: %@, error: %@", values, error);
1036
1037 if (globalReportHandler) {
1038 __auto_type callback = globalReportHandler;
1039 callback(values, error);
1040 }
1041 }
1042 subscriptionEstablished:^{
1043 NSLog(@"subscribe attribute: OnOff established");
1044 [expectation fulfill];
1045 }];
1046
1047 // Wait till establishment
1048 [self waitForExpectations:[NSArray arrayWithObject:expectation] timeout:kTimeoutInSeconds];
1049
1050 // Set up expectation for report
1051 XCTestExpectation * reportExpectation = [self expectationWithDescription:@"report received"];
1052 globalReportHandler = ^(id _Nullable value, NSError * _Nullable error) {
Justin Woodf5c12c92022-07-02 00:43:40 -07001053 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
Kundok Parkeac433a2022-03-15 10:05:54 -07001054 XCTAssertTrue([value isKindOfClass:[NSArray class]]);
1055 NSDictionary * result = value[0];
Justin Woodf5c12c92022-07-02 00:43:40 -07001056 MTRAttributePath * path = result[@"attributePath"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001057 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
1058 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
1059 XCTAssertEqual([path.attribute unsignedIntegerValue], 0);
1060 XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]);
1061 XCTAssertTrue([result[@"data"][@"type"] isEqualToString:@"Boolean"]);
1062 if ([result[@"data"][@"value"] boolValue] == YES) {
1063 [reportExpectation fulfill];
1064 globalReportHandler = nil;
1065 }
1066 };
1067
1068 // Send commands to trigger attribute change
1069 XCTestExpectation * commandExpectation = [self expectationWithDescription:@"command responded"];
1070 NSDictionary * fields = @{ @"type" : @"Structure", @"value" : [NSArray array] };
Boris Zbarskyf1ebea82022-10-29 04:46:26 -04001071 [device invokeCommandWithEndpointID:@1
1072 clusterID:@6
1073 commandID:@1
Kundok Parkeac433a2022-03-15 10:05:54 -07001074 commandFields:fields
Kundok Parka72ab9b2022-03-21 12:12:47 -07001075 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -04001076 queue:queue
Kundok Parkeac433a2022-03-15 10:05:54 -07001077 completion:^(id _Nullable values, NSError * _Nullable error) {
1078 NSLog(@"invoke command: On values: %@, error: %@", values, error);
1079
Justin Woodf5c12c92022-07-02 00:43:40 -07001080 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
Kundok Parkeac433a2022-03-15 10:05:54 -07001081
1082 {
1083 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
1084 NSArray * resultArray = values;
1085 for (NSDictionary * result in resultArray) {
Justin Woodf5c12c92022-07-02 00:43:40 -07001086 MTRCommandPath * path = result[@"commandPath"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001087 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
1088 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
1089 XCTAssertEqual([path.command unsignedIntegerValue], 1);
1090 XCTAssertNil(result[@"error"]);
1091 }
1092 XCTAssertEqual([resultArray count], 1);
1093 }
1094 [commandExpectation fulfill];
1095 }];
1096 [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds];
1097
1098 // Wait for report
1099 [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
1100
1101 // Trigger reader failure
1102 XCTestExpectation * failureExpectation = [self expectationWithDescription:@"failed on purpose"];
1103 [device failSubscribers:queue
1104 completion:^{
1105 [failureExpectation fulfill];
1106 }];
1107 [self waitForExpectations:@[ failureExpectation ] timeout:kTimeoutInSeconds];
1108
1109 deregisterExpectation = [self expectationWithDescription:@"Report handler deregistered"];
Boris Zbarsky35187602022-11-02 18:19:46 -04001110 [device deregisterReportHandlersWithQueue:queue
1111 completion:^{
1112 [deregisterExpectation fulfill];
1113 }];
Kundok Parkeac433a2022-03-15 10:05:54 -07001114 [self waitForExpectations:@[ deregisterExpectation ] timeout:kTimeoutInSeconds];
1115}
1116#endif
1117
Boris Zbarskyb5ca1252022-08-11 11:01:13 -04001118- (void)test013_ReuseChipClusterObject
1119{
1120#if MANUAL_INDIVIDUAL_TEST
1121 [self initStack];
1122 [self waitForCommissionee];
1123#endif
1124
1125 MTRDeviceController * controller = sController;
1126 XCTAssertNotNil(controller);
1127
Boris Zbarskyfb865cc2022-11-09 08:09:37 -05001128 MTRBaseDevice * device = [MTRBaseDevice deviceWithNodeID:@(kDeviceId) controller:controller];
Boris Zbarskyb5ca1252022-08-11 11:01:13 -04001129
1130 XCTestExpectation * expectation = [self expectationWithDescription:@"ReuseMTRClusterObjectFirstCall"];
1131
1132 dispatch_queue_t queue = dispatch_get_main_queue();
Andrei Litvincf323302022-11-15 15:00:44 +01001133 MTRBaseClusterUnitTesting * cluster = [[MTRBaseClusterUnitTesting alloc] initWithDevice:device endpointID:@(1) queue:queue];
Boris Zbarskyb5ca1252022-08-11 11:01:13 -04001134 XCTAssertNotNil(cluster);
1135
Boris Zbarsky35187602022-11-02 18:19:46 -04001136 [cluster testWithCompletion:^(NSError * err) {
Boris Zbarskyb5ca1252022-08-11 11:01:13 -04001137 NSLog(@"ReuseMTRClusterObject test Error: %@", err);
1138 XCTAssertEqual(err.code, 0);
1139 [expectation fulfill];
1140 }];
1141
1142 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
1143
1144 expectation = [self expectationWithDescription:@"ReuseMTRClusterObjectSecondCall"];
1145
1146 // Reuse the MTRCluster Object for multiple times.
1147
Boris Zbarsky35187602022-11-02 18:19:46 -04001148 [cluster testWithCompletion:^(NSError * err) {
Boris Zbarskyb5ca1252022-08-11 11:01:13 -04001149 NSLog(@"ReuseMTRClusterObject test Error: %@", err);
1150 XCTAssertEqual(err.code, 0);
1151 [expectation fulfill];
1152 }];
1153
1154 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
1155}
1156
Boris Zbarskyb9e1e7a2022-08-11 17:48:33 -04001157- (void)test014_InvokeCommandWithDifferentIdResponse
1158{
1159#if MANUAL_INDIVIDUAL_TEST
1160 [self initStack];
1161 [self waitForCommissionee];
1162#endif
1163 XCTestExpectation * expectation = [self expectationWithDescription:@"invoke Off command"];
1164
1165 MTRBaseDevice * device = GetConnectedDevice();
1166 dispatch_queue_t queue = dispatch_get_main_queue();
1167
1168 NSDictionary * fields = @{
1169 @"type" : @"Structure",
1170 @"value" : @[],
1171 };
1172 // KeySetReadAllIndices in the Group Key Management has id 4 and a data response with id 5
1173 [device
Boris Zbarskyf1ebea82022-10-29 04:46:26 -04001174 invokeCommandWithEndpointID:@0
1175 clusterID:@(0x003F)
1176 commandID:@4
Boris Zbarskyb9e1e7a2022-08-11 17:48:33 -04001177 commandFields:fields
1178 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -04001179 queue:queue
Boris Zbarskyb9e1e7a2022-08-11 17:48:33 -04001180 completion:^(id _Nullable values, NSError * _Nullable error) {
1181 NSLog(@"invoke command: KeySetReadAllIndices values: %@, error: %@", values, error);
1182
1183 XCTAssertNil(error);
1184
1185 {
1186 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
1187 NSArray * resultArray = values;
1188 for (NSDictionary * result in resultArray) {
1189 MTRCommandPath * path = result[MTRCommandPathKey];
1190 XCTAssertEqual([path.endpoint unsignedIntegerValue], 0);
1191 XCTAssertEqual([path.cluster unsignedIntegerValue], 0x003F);
1192 XCTAssertEqual([path.command unsignedIntegerValue], 5);
1193 // We expect a KeySetReadAllIndicesResponse struct,
1194 // which has context tag 0 pointing to a list with one
1195 // item: 0 (the IPK's keyset id).
1196 NSDictionary * expectedResult = @{
1197 MTRTypeKey : MTRStructureValueType,
1198 MTRValueKey : @[ @{
1199 MTRContextTagKey : @0,
1200 MTRDataKey : @ {
1201 MTRTypeKey : MTRArrayValueType,
1202 MTRValueKey : @[ @{
1203 MTRDataKey : @ { MTRTypeKey : MTRUnsignedIntegerValueType, MTRValueKey : @0 }
1204 } ]
1205 }
1206 } ],
1207 };
1208 XCTAssertEqualObjects(result[MTRDataKey], expectedResult);
1209 XCTAssertNil(result[MTRErrorKey]);
1210 }
1211 XCTAssertEqual([resultArray count], 1);
1212 }
1213
1214 [expectation fulfill];
1215 }];
1216
1217 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
1218}
1219
Boris Zbarskya7475d32022-10-11 10:22:56 -04001220- (void)test015_FailedSubscribeWithQueueAcrossShutdown
1221{
1222#if MANUAL_INDIVIDUAL_TEST
1223 [self initStack];
1224 [self waitForCommissionee];
1225#endif
1226
1227 MTRBaseDevice * device = GetConnectedDevice();
1228 dispatch_queue_t queue = dispatch_get_main_queue();
1229
1230 MTRDeviceController * controller = sController;
1231 XCTAssertNotNil(controller);
1232 XCTestExpectation * firstSubscribeExpectation = [self expectationWithDescription:@"First subscription complete"];
1233 XCTestExpectation * errorExpectation = [self expectationWithDescription:@"First subscription errored out"];
1234
1235 // Create first subscription. It needs to be using subscribeWithQueue and
1236 // must have a clusterStateCacheContainer to exercise the onDone case.
1237 NSLog(@"Subscribing...");
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001238 __auto_type clusterStateCacheContainer = [[MTRAttributeCacheContainer alloc] init];
1239 __auto_type * params = [[MTRSubscribeParams alloc] init];
Boris Zbarskyc530f632022-11-04 17:03:57 -04001240 params.resubscribeIfLost = NO;
Boris Zbarsky1a8cf552022-11-09 09:11:21 -05001241 params.replaceExistingSubscriptions = NO; // Not strictly needed, but checking that doing this does not
1242 // affect this subscription erroring out correctly.
Boris Zbarskya7475d32022-10-11 10:22:56 -04001243 [device subscribeWithQueue:queue
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001244 minInterval:1
1245 maxInterval:2
Boris Zbarskya7475d32022-10-11 10:22:56 -04001246 params:params
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001247 cacheContainer:clusterStateCacheContainer
Boris Zbarskya7475d32022-10-11 10:22:56 -04001248 attributeReportHandler:nil
1249 eventReportHandler:nil
1250 errorHandler:^(NSError * error) {
1251 NSLog(@"Received report error: %@", error);
1252
1253 // Restart the controller here, to exercise our various event queue bits.
1254 [controller shutdown];
1255
1256 // Wait a bit before restart, to allow whatever async things are going on after this is called to try to happen.
1257 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), queue, ^{
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001258 __auto_type * factory = [MTRControllerFactory sharedInstance];
Boris Zbarskya7475d32022-10-11 10:22:56 -04001259 XCTAssertNotNil(factory);
1260
1261 // Needs to match what initStack does.
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001262 __auto_type * params = [[MTRDeviceControllerStartupParams alloc] initWithSigningKeypair:sTestKeys
1263 fabricId:1
1264 ipk:sTestKeys.ipk];
1265 __auto_type * newController = [factory startControllerOnExistingFabric:params];
Boris Zbarskya7475d32022-10-11 10:22:56 -04001266 XCTAssertNotNil(newController);
1267
1268 sController = newController;
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001269
1270 WaitForCommissionee(errorExpectation);
Boris Zbarskya7475d32022-10-11 10:22:56 -04001271 });
1272 }
1273 subscriptionEstablished:^() {
1274 [firstSubscribeExpectation fulfill];
1275 }
1276 resubscriptionScheduled:nil];
1277 [self waitForExpectations:@[ firstSubscribeExpectation ] timeout:60];
1278
1279 // Create second subscription which will cancel the first subscription. We
1280 // can use a non-existent path here to cut down on the work that gets done.
Boris Zbarsky1a8cf552022-11-09 09:11:21 -05001281 params.replaceExistingSubscriptions = YES;
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001282 [device subscribeAttributeWithEndpointId:@10000
1283 clusterId:@6
1284 attributeId:@0
1285 minInterval:@(1)
1286 maxInterval:@(2)
1287 params:params
1288 clientQueue:queue
1289 reportHandler:^(id _Nullable values, NSError * _Nullable error) {
1290 }
1291 subscriptionEstablished:^() {
1292 }];
Boris Zbarskya7475d32022-10-11 10:22:56 -04001293 [self waitForExpectations:@[ errorExpectation ] timeout:60];
1294}
1295
1296- (void)test016_FailedSubscribeWithCacheReadDuringFailure
1297{
1298#if MANUAL_INDIVIDUAL_TEST
1299 [self initStack];
1300 [self waitForCommissionee];
1301#endif
1302
1303 MTRBaseDevice * device = GetConnectedDevice();
1304 dispatch_queue_t queue = dispatch_get_main_queue();
1305
1306 MTRDeviceController * controller = sController;
1307 XCTAssertNotNil(controller);
1308 XCTestExpectation * firstSubscribeExpectation = [self expectationWithDescription:@"First subscription complete"];
1309 XCTestExpectation * errorExpectation = [self expectationWithDescription:@"First subscription errored out"];
1310
1311 // Create first subscription. It needs to be using subscribeWithQueue and
1312 // must have a clusterStateCacheContainer to exercise the onDone case.
1313 NSLog(@"Subscribing...");
Boris Zbarsky3edba4a2022-11-17 09:59:53 -05001314 __auto_type clusterStateCacheContainer = [[MTRClusterStateCacheContainer alloc] init];
Boris Zbarskyc530f632022-11-04 17:03:57 -04001315 __auto_type * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(2)];
1316 params.resubscribeIfLost = NO;
Boris Zbarskya7475d32022-10-11 10:22:56 -04001317 [device subscribeWithQueue:queue
1318 params:params
Boris Zbarsky3edba4a2022-11-17 09:59:53 -05001319 clusterStateCacheContainer:clusterStateCacheContainer
Boris Zbarskya7475d32022-10-11 10:22:56 -04001320 attributeReportHandler:nil
1321 eventReportHandler:nil
1322 errorHandler:^(NSError * error) {
1323 NSLog(@"Received report error: %@", error);
1324
Boris Zbarsky3edba4a2022-11-17 09:59:53 -05001325 [MTRBaseClusterOnOff readAttributeOnOffWithClusterStateCache:clusterStateCacheContainer
1326 endpoint:@1
1327 queue:queue
1328 completion:^(NSNumber * _Nullable value, NSError * _Nullable error) {
1329 [errorExpectation fulfill];
1330 }];
Boris Zbarskya7475d32022-10-11 10:22:56 -04001331 }
1332 subscriptionEstablished:^() {
1333 [firstSubscribeExpectation fulfill];
1334 }
1335 resubscriptionScheduled:nil];
1336 [self waitForExpectations:@[ firstSubscribeExpectation ] timeout:60];
1337
1338 // Create second subscription which will cancel the first subscription. We
1339 // can use a non-existent path here to cut down on the work that gets done.
Boris Zbarsky1a8cf552022-11-09 09:11:21 -05001340 params.replaceExistingSubscriptions = YES;
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001341 [device subscribeAttributeWithEndpointId:@10000
1342 clusterId:@6
1343 attributeId:@0
1344 minInterval:@(1)
1345 maxInterval:@(2)
1346 params:params
1347 clientQueue:queue
1348 reportHandler:^(id _Nullable values, NSError * _Nullable error) {
1349 }
1350 subscriptionEstablished:^() {
1351 }];
Boris Zbarskya7475d32022-10-11 10:22:56 -04001352 [self waitForExpectations:@[ errorExpectation ] timeout:60];
1353}
1354
Boris Zbarskya92b9522022-11-01 20:57:35 -04001355- (void)test017_TestMTRDeviceBasics
1356{
1357#if MANUAL_INDIVIDUAL_TEST
1358 [self initStack];
1359 [self waitForCommissionee];
1360#endif
1361
1362 __auto_type * device = [MTRDevice deviceWithNodeID:kDeviceId deviceController:sController];
1363 dispatch_queue_t queue = dispatch_get_main_queue();
1364
1365 XCTestExpectation * subscriptionExpectation = [self expectationWithDescription:@"Subscription has been set up"];
1366
1367 __auto_type * delegate = [[MTRDeviceTestDelegate alloc] init];
1368 delegate.onSubscriptionEstablished = ^() {
1369 [subscriptionExpectation fulfill];
1370 };
1371
1372 [device setDelegate:delegate queue:queue];
1373
1374 [self waitForExpectations:@[ subscriptionExpectation ] timeout:60];
1375}
1376
Boris Zbarsky1a8cf552022-11-09 09:11:21 -05001377- (void)test018_SubscriptionErrorWhenNotResubscribing
1378{
1379#if MANUAL_INDIVIDUAL_TEST
1380 [self initStack];
1381 [self waitForCommissionee];
1382#endif
1383 MTRBaseDevice * device = GetConnectedDevice();
1384 dispatch_queue_t queue = dispatch_get_main_queue();
1385
1386 XCTestExpectation * firstSubscribeExpectation = [self expectationWithDescription:@"First subscription complete"];
1387 XCTestExpectation * errorExpectation = [self expectationWithDescription:@"First subscription errored out"];
1388
1389 // Subscribe
1390 MTRSubscribeParams * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(10)];
1391 params.resubscribeIfLost = NO;
1392 params.replaceExistingSubscriptions = NO; // Not strictly needed, but checking that doing this does not
1393 // affect this subscription erroring out correctly.
1394 __block BOOL subscriptionEstablished = NO;
1395 [device subscribeToAttributesWithEndpointID:@1
1396 clusterID:@6
1397 attributeID:@0
1398 params:params
1399 queue:queue
1400 reportHandler:^(id _Nullable values, NSError * _Nullable error) {
1401 if (subscriptionEstablished) {
1402 // We should only get an error here.
1403 XCTAssertNil(values);
1404 XCTAssertNotNil(error);
1405 [errorExpectation fulfill];
1406 } else {
1407 XCTAssertNotNil(values);
1408 XCTAssertNil(error);
1409 }
1410 }
1411 subscriptionEstablished:^{
1412 NSLog(@"subscribe attribute: OnOff established");
1413 XCTAssertFalse(subscriptionEstablished);
1414 subscriptionEstablished = YES;
1415 [firstSubscribeExpectation fulfill];
1416 }];
1417
1418 // Wait till establishment
1419 [self waitForExpectations:@[ firstSubscribeExpectation ] timeout:kTimeoutInSeconds];
1420
1421 // Create second subscription which will cancel the first subscription. We
1422 // can use a non-existent path here to cut down on the work that gets done.
1423 params.replaceExistingSubscriptions = YES;
1424 [device subscribeAttributeWithEndpointId:@10000
1425 clusterId:@6
1426 attributeId:@0
1427 minInterval:@(1)
1428 maxInterval:@(2)
1429 params:params
1430 clientQueue:queue
1431 reportHandler:^(id _Nullable values, NSError * _Nullable error) {
1432 }
1433 subscriptionEstablished:^() {
1434 }];
1435 [self waitForExpectations:@[ errorExpectation ] timeout:60];
1436}
1437
Boris Zbarskyc6272c62022-11-17 11:33:08 -05001438- (void)test019_MTRDeviceMultipleCommands
1439{
1440#if MANUAL_INDIVIDUAL_TEST
1441 [self initStack];
1442 [self waitForCommissionee];
1443#endif
1444
1445 __auto_type * device = [MTRDevice deviceWithNodeID:kDeviceId deviceController:sController];
1446 dispatch_queue_t queue = dispatch_get_main_queue();
1447
1448 __auto_type * opcredsCluster = [[MTRClusterOperationalCredentials alloc] initWithDevice:device endpointID:@(0) queue:queue];
1449 __auto_type * onOffCluster = [[MTRClusterOnOff alloc] initWithDevice:device endpointID:@(1) queue:queue];
1450 __auto_type * badOnOffCluster = [[MTRClusterOnOff alloc] initWithDevice:device endpointID:@(0) queue:queue];
1451
1452 XCTestExpectation * onExpectation = [self expectationWithDescription:@"On command executed"];
1453 [onOffCluster onWithParams:nil
1454 expectedValues:nil
1455 expectedValueInterval:nil
1456 completion:^(NSError * _Nullable error) {
1457 XCTAssertNil(error);
1458 [onExpectation fulfill];
1459 }];
1460
1461 XCTestExpectation * offFailedExpectation = [self expectationWithDescription:@"Off command failed"];
1462 [badOnOffCluster offWithParams:nil
1463 expectedValues:nil
1464 expectedValueInterval:nil
1465 completion:^(NSError * _Nullable error) {
1466 XCTAssertNotNil(error);
1467 [offFailedExpectation fulfill];
1468 }];
1469
1470 XCTestExpectation * updateLabelExpectation = [self expectationWithDescription:@"Fabric label updated"];
1471 __auto_type * params = [[MTROperationalCredentialsClusterUpdateFabricLabelParams alloc] init];
1472 params.label = @("Test");
1473 [opcredsCluster updateFabricLabelWithParams:params
1474 expectedValues:nil
1475 expectedValueInterval:nil
1476 completion:^(MTROperationalCredentialsClusterNOCResponseParams * _Nullable data,
1477 NSError * _Nullable error) {
1478 XCTAssertNil(error);
1479 XCTAssertNotNil(data);
1480 XCTAssertEqualObjects(data.statusCode, @(0));
1481 XCTAssertNotNil(data.fabricIndex);
1482 [updateLabelExpectation fulfill];
1483 }];
1484
1485 XCTestExpectation * offExpectation = [self expectationWithDescription:@"Off command executed"];
1486 [onOffCluster offWithParams:nil
1487 expectedValues:nil
1488 expectedValueInterval:nil
1489 completion:^(NSError * _Nullable error) {
1490 XCTAssertNil(error);
1491 [offExpectation fulfill];
1492 }];
1493
1494 XCTestExpectation * onFailedExpectation = [self expectationWithDescription:@"On command failed"];
1495 [badOnOffCluster onWithParams:nil
1496 expectedValues:nil
1497 expectedValueInterval:nil
1498 completion:^(NSError * _Nullable error) {
1499 XCTAssertNotNil(error);
1500 [onFailedExpectation fulfill];
1501 }];
1502
1503 XCTestExpectation * updateLabelFailedExpectation = [self expectationWithDescription:@"Fabric label update failed"];
1504 params.label = @("12345678901234567890123445678901234567890"); // Too long
1505 [opcredsCluster updateFabricLabelWithParams:params
1506 expectedValues:nil
1507 expectedValueInterval:nil
1508 completion:^(MTROperationalCredentialsClusterNOCResponseParams * _Nullable data,
1509 NSError * _Nullable error) {
1510 XCTAssertNotNil(error);
1511 XCTAssertNil(data);
1512 [updateLabelFailedExpectation fulfill];
1513 }];
1514
1515 [self waitForExpectations:@[
1516 onExpectation, offFailedExpectation, updateLabelExpectation, offExpectation, onFailedExpectation,
1517 updateLabelFailedExpectation
1518 ]
1519 timeout:60
1520 enforceOrder:YES];
1521}
1522
kpark-apple153a5642022-02-22 07:19:20 -08001523- (void)test900_SubscribeAllAttributes
1524{
1525#if MANUAL_INDIVIDUAL_TEST
1526 [self initStack];
1527 [self waitForCommissionee];
1528#endif
Jeff Tungae2913f2022-07-06 19:02:55 -07001529 MTRBaseDevice * device = GetConnectedDevice();
kpark-apple153a5642022-02-22 07:19:20 -08001530 dispatch_queue_t queue = dispatch_get_main_queue();
Kundok Parkeac433a2022-03-15 10:05:54 -07001531 XCTestExpectation * cleanSubscriptionExpectation = [self expectationWithDescription:@"Previous subscriptions cleaned"];
1532 NSLog(@"Deregistering report handlers...");
Boris Zbarsky35187602022-11-02 18:19:46 -04001533 [device deregisterReportHandlersWithQueue:queue
1534 completion:^{
1535 NSLog(@"Report handlers deregistered");
1536 [cleanSubscriptionExpectation fulfill];
1537 }];
Kundok Parkeac433a2022-03-15 10:05:54 -07001538 [self waitForExpectations:@[ cleanSubscriptionExpectation ] timeout:kTimeoutInSeconds];
kpark-apple153a5642022-02-22 07:19:20 -08001539
1540 XCTestExpectation * expectation = [self expectationWithDescription:@"subscribe OnOff attribute"];
1541 __block void (^reportHandler)(id _Nullable values, NSError * _Nullable error) = nil;
1542
Boris Zbarskyc530f632022-11-04 17:03:57 -04001543 MTRSubscribeParams * params = [[MTRSubscribeParams alloc] initWithMinInterval:@(2) maxInterval:@(10)];
Boris Zbarskyc6272c62022-11-17 11:33:08 -05001544 params.resubscribeIfLost = NO;
Boris Zbarskyd0664632022-11-04 02:18:28 -04001545 [device subscribeToAttributesWithEndpointID:@1
Boris Zbarskyf1ebea82022-10-29 04:46:26 -04001546 clusterID:@6
1547 attributeID:@0xffffffff
Boris Zbarskyc530f632022-11-04 17:03:57 -04001548 params:params
Boris Zbarsky35187602022-11-02 18:19:46 -04001549 queue:queue
kpark-apple153a5642022-02-22 07:19:20 -08001550 reportHandler:^(id _Nullable values, NSError * _Nullable error) {
1551 NSLog(@"Subscribe all - report attribute values: %@, error: %@, report handler: %d", values, error,
1552 (reportHandler != nil));
1553
1554 if (reportHandler) {
1555 __auto_type callback = reportHandler;
1556 callback(values, error);
1557 }
1558 }
1559 subscriptionEstablished:^{
1560 NSLog(@"subscribe attribute: OnOff established");
1561 [expectation fulfill];
1562 }];
1563
1564 // Wait till establishment
1565 [self waitForExpectationsWithTimeout:kTimeoutInSeconds handler:nil];
1566
1567 // Set up expectation for report
1568 __auto_type reportExpectation = [self expectationWithDescription:@"receive OnOff attribute report"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001569 reportHandler = ^(id _Nullable values, NSError * _Nullable error) {
Justin Woodf5c12c92022-07-02 00:43:40 -07001570 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
Kundok Parkeac433a2022-03-15 10:05:54 -07001571 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
1572 NSDictionary * result = values[0];
Justin Woodf5c12c92022-07-02 00:43:40 -07001573 MTRAttributePath * path = result[@"attributePath"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001574 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
1575 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
1576 XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]);
kpark-apple153a5642022-02-22 07:19:20 -08001577
Kundok Parkeac433a2022-03-15 10:05:54 -07001578 if ([path.attribute unsignedIntegerValue] == 0 && [result[@"data"][@"value"] boolValue] == YES) {
kpark-apple153a5642022-02-22 07:19:20 -08001579 [reportExpectation fulfill];
1580 reportHandler = nil;
1581 }
1582 };
1583
Kundok Parkeac433a2022-03-15 10:05:54 -07001584 // Send commands to set attribute state to a known state
kpark-apple153a5642022-02-22 07:19:20 -08001585 XCTestExpectation * commandExpectation = [self expectationWithDescription:@"command responded"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001586 NSDictionary * fields = @{ @"type" : @"Structure", @"value" : @[] };
Boris Zbarskyf1ebea82022-10-29 04:46:26 -04001587 [device invokeCommandWithEndpointID:@1
1588 clusterID:@6
1589 commandID:@0
kpark-apple153a5642022-02-22 07:19:20 -08001590 commandFields:fields
Kundok Parka72ab9b2022-03-21 12:12:47 -07001591 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -04001592 queue:queue
kpark-apple153a5642022-02-22 07:19:20 -08001593 completion:^(id _Nullable values, NSError * _Nullable error) {
1594 NSLog(@"invoke command: On values: %@, error: %@", values, error);
1595
Justin Woodf5c12c92022-07-02 00:43:40 -07001596 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
kpark-apple153a5642022-02-22 07:19:20 -08001597
1598 {
1599 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
1600 NSArray * resultArray = values;
1601 for (NSDictionary * result in resultArray) {
Justin Woodf5c12c92022-07-02 00:43:40 -07001602 MTRCommandPath * path = result[@"commandPath"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001603 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
1604 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
1605 XCTAssertEqual([path.command unsignedIntegerValue], 0);
1606 XCTAssertNil(result[@"error"]);
1607 }
1608 XCTAssertEqual([resultArray count], 1);
1609 }
1610 [commandExpectation fulfill];
1611 }];
1612 [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds];
1613
1614 // Send commands to trigger attribute change
1615 commandExpectation = [self expectationWithDescription:@"command responded"];
1616 fields = @{ @"type" : @"Structure", @"value" : @[] };
Boris Zbarskyf1ebea82022-10-29 04:46:26 -04001617 [device invokeCommandWithEndpointID:@1
1618 clusterID:@6
1619 commandID:@1
Kundok Parkeac433a2022-03-15 10:05:54 -07001620 commandFields:fields
Kundok Parka72ab9b2022-03-21 12:12:47 -07001621 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -04001622 queue:queue
Kundok Parkeac433a2022-03-15 10:05:54 -07001623 completion:^(id _Nullable values, NSError * _Nullable error) {
1624 NSLog(@"invoke command: On values: %@, error: %@", values, error);
1625
Justin Woodf5c12c92022-07-02 00:43:40 -07001626 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
Kundok Parkeac433a2022-03-15 10:05:54 -07001627
1628 {
1629 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
1630 NSArray * resultArray = values;
1631 for (NSDictionary * result in resultArray) {
Justin Woodf5c12c92022-07-02 00:43:40 -07001632 MTRCommandPath * path = result[@"commandPath"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001633 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
1634 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
1635 XCTAssertEqual([path.command unsignedIntegerValue], 1);
1636 XCTAssertNil(result[@"error"]);
1637 }
1638 XCTAssertEqual([resultArray count], 1);
1639 }
1640 [commandExpectation fulfill];
1641 }];
1642 [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds];
1643
1644 // Wait for report
1645 [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
1646
1647 // Set up expectation for 2nd report
1648 reportExpectation = [self expectationWithDescription:@"receive OnOff attribute report"];
1649 reportHandler = ^(id _Nullable values, NSError * _Nullable error) {
Justin Woodf5c12c92022-07-02 00:43:40 -07001650 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
Kundok Parkeac433a2022-03-15 10:05:54 -07001651 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
1652 NSDictionary * result = values[0];
Justin Woodf5c12c92022-07-02 00:43:40 -07001653 MTRAttributePath * path = result[@"attributePath"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001654 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
1655 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
1656 XCTAssertTrue([result[@"data"] isKindOfClass:[NSDictionary class]]);
1657 if ([path.attribute unsignedIntegerValue] == 0 && [result[@"data"][@"value"] boolValue] == NO) {
1658 [reportExpectation fulfill];
1659 reportHandler = nil;
1660 }
1661 };
1662
1663 // Send command to trigger attribute change
1664 commandExpectation = [self expectationWithDescription:@"command responded"];
1665 fields = @{ @"type" : @"Structure", @"value" : @[] };
Boris Zbarskyf1ebea82022-10-29 04:46:26 -04001666 [device invokeCommandWithEndpointID:@1
1667 clusterID:@6
1668 commandID:@0
Kundok Parkeac433a2022-03-15 10:05:54 -07001669 commandFields:fields
Kundok Parka72ab9b2022-03-21 12:12:47 -07001670 timedInvokeTimeout:nil
Boris Zbarsky35187602022-11-02 18:19:46 -04001671 queue:queue
Kundok Parkeac433a2022-03-15 10:05:54 -07001672 completion:^(id _Nullable values, NSError * _Nullable error) {
1673 NSLog(@"invoke command: On values: %@, error: %@", values, error);
1674
Justin Woodf5c12c92022-07-02 00:43:40 -07001675 XCTAssertEqual([MTRErrorTestUtils errorToZCLErrorCode:error], 0);
Kundok Parkeac433a2022-03-15 10:05:54 -07001676
1677 {
1678 XCTAssertTrue([values isKindOfClass:[NSArray class]]);
1679 NSArray * resultArray = values;
1680 for (NSDictionary * result in resultArray) {
Justin Woodf5c12c92022-07-02 00:43:40 -07001681 MTRCommandPath * path = result[@"commandPath"];
Kundok Parkeac433a2022-03-15 10:05:54 -07001682 XCTAssertEqual([path.endpoint unsignedIntegerValue], 1);
1683 XCTAssertEqual([path.cluster unsignedIntegerValue], 6);
1684 XCTAssertEqual([path.command unsignedIntegerValue], 0);
1685 XCTAssertNil(result[@"error"]);
kpark-apple153a5642022-02-22 07:19:20 -08001686 }
1687 XCTAssertEqual([resultArray count], 1);
1688 }
1689 [commandExpectation fulfill];
1690 }];
1691 [self waitForExpectations:[NSArray arrayWithObject:commandExpectation] timeout:kTimeoutInSeconds];
1692
1693 // Wait for report
1694 [self waitForExpectations:[NSArray arrayWithObject:reportExpectation] timeout:kTimeoutInSeconds];
1695}
kpark-apple153a5642022-02-22 07:19:20 -08001696
1697#if !MANUAL_INDIVIDUAL_TEST
1698- (void)test999_TearDown
1699{
Boris Zbarsky20601052022-10-03 15:02:36 -04001700 // Put the device back in the state we found it: open commissioning window, no fabrics commissioned.
1701 MTRBaseDevice * device = GetConnectedDevice();
1702 dispatch_queue_t queue = dispatch_get_main_queue();
1703
1704 // Get our current fabric index, for later deletion.
1705 XCTestExpectation * readFabricIndexExpectation = [self expectationWithDescription:@"Fabric index read"];
1706
1707 __block NSNumber * fabricIndex;
Boris Zbarsky09acc292022-10-28 14:05:35 -04001708 __auto_type * opCredsCluster = [[MTRBaseClusterOperationalCredentials alloc] initWithDevice:device endpointID:@(0) queue:queue];
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001709 [opCredsCluster
1710 readAttributeCurrentFabricIndexWithCompletionHandler:^(NSNumber * _Nullable value, NSError * _Nullable readError) {
1711 XCTAssertNil(readError);
1712 XCTAssertNotNil(value);
1713 fabricIndex = value;
1714 [readFabricIndexExpectation fulfill];
1715 }];
Boris Zbarsky20601052022-10-03 15:02:36 -04001716
1717 [self waitForExpectations:@[ readFabricIndexExpectation ] timeout:kTimeoutInSeconds];
1718
1719 // Open a commissioning window.
1720 XCTestExpectation * openCommissioningWindowExpectation = [self expectationWithDescription:@"Commissioning window opened"];
1721
1722 __auto_type * adminCommissioningCluster = [[MTRBaseClusterAdministratorCommissioning alloc] initWithDevice:device
Boris Zbarsky09acc292022-10-28 14:05:35 -04001723 endpointID:@(0)
Boris Zbarsky20601052022-10-03 15:02:36 -04001724 queue:queue];
1725 __auto_type * openWindowParams = [[MTRAdministratorCommissioningClusterOpenBasicCommissioningWindowParams alloc] init];
1726 openWindowParams.commissioningTimeout = @(900);
1727 openWindowParams.timedInvokeTimeoutMs = @(50000);
1728 [adminCommissioningCluster openBasicCommissioningWindowWithParams:openWindowParams
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001729 completionHandler:^(NSError * _Nullable error) {
1730 XCTAssertNil(error);
1731 [openCommissioningWindowExpectation fulfill];
1732 }];
Boris Zbarsky20601052022-10-03 15:02:36 -04001733
1734 [self waitForExpectations:@[ openCommissioningWindowExpectation ] timeout:kTimeoutInSeconds];
1735
1736 // Remove our fabric from the device.
1737 XCTestExpectation * removeFabricExpectation = [self expectationWithDescription:@"Fabric removed"];
1738
1739 __auto_type * removeParams = [[MTROperationalCredentialsClusterRemoveFabricParams alloc] init];
1740 removeParams.fabricIndex = fabricIndex;
1741
1742 [opCredsCluster removeFabricWithParams:removeParams
Boris Zbarsky12802fb2022-10-12 21:45:38 -04001743 completionHandler:^(
1744 MTROperationalCredentialsClusterNOCResponseParams * _Nullable data, NSError * _Nullable removeError) {
1745 XCTAssertNil(removeError);
1746 XCTAssertNotNil(data);
1747 XCTAssertEqualObjects(data.statusCode, @(0));
1748 [removeFabricExpectation fulfill];
1749 }];
Boris Zbarsky20601052022-10-03 15:02:36 -04001750
1751 [self waitForExpectations:@[ removeFabricExpectation ] timeout:kTimeoutInSeconds];
1752
kpark-apple153a5642022-02-22 07:19:20 -08001753 [self shutdownStack];
1754}
1755#endif
1756
1757@end
1758
Jeff Tungae2913f2022-07-06 19:02:55 -07001759@interface MTRBaseDevice (Test)
kpark-apple153a5642022-02-22 07:19:20 -08001760// Test function for whitebox testing
1761+ (id)CHIPEncodeAndDecodeNSObject:(id)object;
1762@end
1763
Justin Woodf5c12c92022-07-02 00:43:40 -07001764@interface MTRDeviceEncoderTests : XCTestCase
kpark-apple153a5642022-02-22 07:19:20 -08001765@end
1766
Justin Woodf5c12c92022-07-02 00:43:40 -07001767@implementation MTRDeviceEncoderTests
kpark-apple153a5642022-02-22 07:19:20 -08001768
1769- (void)testSignedInteger
1770{
1771 NSDictionary * input =
1772 [NSDictionary dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:-713], @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001773 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
kpark-apple153a5642022-02-22 07:19:20 -08001774 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1775 XCTAssertNotNil(output);
1776 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001777 XCTAssertTrue([output isEqual:input]);
kpark-apple153a5642022-02-22 07:19:20 -08001778}
1779
Kundok Parkeac433a2022-03-15 10:05:54 -07001780- (void)testSignedInteger64Bits
1781{
1782 NSDictionary * input = [NSDictionary
1783 dictionaryWithObjectsAndKeys:@"SignedInteger", @"type", [NSNumber numberWithInteger:-0x7000111122223333ll], @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001784 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
Kundok Parkeac433a2022-03-15 10:05:54 -07001785 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1786 XCTAssertNotNil(output);
1787 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001788 XCTAssertTrue([output isEqual:input]);
Kundok Parkeac433a2022-03-15 10:05:54 -07001789}
1790
kpark-apple153a5642022-02-22 07:19:20 -08001791- (void)testUnsignedInteger
1792{
1793 NSDictionary * input =
1794 [NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type", [NSNumber numberWithInteger:1025], @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001795 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
kpark-apple153a5642022-02-22 07:19:20 -08001796 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1797 XCTAssertNotNil(output);
1798 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001799 XCTAssertTrue([output isEqual:input]);
kpark-apple153a5642022-02-22 07:19:20 -08001800}
1801
Kundok Parkeac433a2022-03-15 10:05:54 -07001802- (void)testUnsignedInteger64Bits
1803{
1804 NSDictionary * input = [NSDictionary dictionaryWithObjectsAndKeys:@"UnsignedInteger", @"type",
1805 [NSNumber numberWithUnsignedLongLong:0xCCCCDDDDEEEEFFFFull], @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001806 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
Kundok Parkeac433a2022-03-15 10:05:54 -07001807 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1808 XCTAssertNotNil(output);
1809 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001810 XCTAssertTrue([output isEqual:input]);
Kundok Parkeac433a2022-03-15 10:05:54 -07001811}
1812
kpark-apple153a5642022-02-22 07:19:20 -08001813- (void)testBoolean
1814{
1815 NSDictionary * input =
1816 [NSDictionary dictionaryWithObjectsAndKeys:@"Boolean", @"type", [NSNumber numberWithBool:YES], @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001817 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
kpark-apple153a5642022-02-22 07:19:20 -08001818 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1819 XCTAssertNotNil(output);
1820 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001821 XCTAssertTrue([output isEqual:input]);
kpark-apple153a5642022-02-22 07:19:20 -08001822}
1823
1824- (void)testUTF8String
1825{
1826 NSDictionary * input = [NSDictionary dictionaryWithObjectsAndKeys:@"UTF8String", @"type", @"Hello World", @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001827 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
kpark-apple153a5642022-02-22 07:19:20 -08001828 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1829 XCTAssertNotNil(output);
1830 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001831 XCTAssertTrue([output isEqual:input]);
kpark-apple153a5642022-02-22 07:19:20 -08001832}
1833
1834- (void)testOctetString
1835{
1836 const uint8_t data[] = { 0x00, 0xF2, 0x63 };
1837 NSDictionary * input = [NSDictionary
1838 dictionaryWithObjectsAndKeys:@"OctetString", @"type", [NSData dataWithBytes:data length:sizeof(data)], @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001839 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
kpark-apple153a5642022-02-22 07:19:20 -08001840 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1841 XCTAssertNotNil(output);
1842 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001843 XCTAssertTrue([output isEqual:input]);
kpark-apple153a5642022-02-22 07:19:20 -08001844}
1845
1846- (void)testFloat
1847{
1848 NSDictionary * input =
Boris Zbarsky46cc4a72022-07-19 16:58:17 -04001849 [NSDictionary dictionaryWithObjectsAndKeys:@"Float", @"type", [NSNumber numberWithFloat:0.1245f], @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001850 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
kpark-apple153a5642022-02-22 07:19:20 -08001851 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1852 XCTAssertNotNil(output);
1853 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Kundok Parkeac433a2022-03-15 10:05:54 -07001854 XCTAssertTrue([output[@"type"] isEqualToString:@"Float"]);
1855 XCTAssertTrue(([output[@"value"] floatValue] - [input[@"value"] floatValue]) < 0.0001);
kpark-apple153a5642022-02-22 07:19:20 -08001856}
1857
1858- (void)testDouble
1859{
1860 NSDictionary * input =
Boris Zbarsky46cc4a72022-07-19 16:58:17 -04001861 [NSDictionary dictionaryWithObjectsAndKeys:@"Double", @"type", [NSNumber numberWithDouble:0.1245], @"value", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001862 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
kpark-apple153a5642022-02-22 07:19:20 -08001863 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1864 XCTAssertNotNil(output);
1865 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Kundok Parkeac433a2022-03-15 10:05:54 -07001866 XCTAssertTrue([output[@"type"] isEqualToString:@"Double"]);
kpark-apple153a5642022-02-22 07:19:20 -08001867 XCTAssertTrue(([output[@"value"] doubleValue] - [input[@"value"] doubleValue]) < 0.0001);
1868}
1869
1870- (void)testNull
1871{
1872 NSDictionary * input = [NSDictionary dictionaryWithObjectsAndKeys:@"Null", @"type", nil];
Jeff Tungae2913f2022-07-06 19:02:55 -07001873 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:input];
kpark-apple153a5642022-02-22 07:19:20 -08001874 NSLog(@"Conversion input: %@\nOutput: %@", input, output);
1875 XCTAssertNotNil(output);
1876 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001877 XCTAssertTrue([output isEqual:input]);
kpark-apple153a5642022-02-22 07:19:20 -08001878}
1879
1880- (void)testStructure
1881{
Kundok Parkeac433a2022-03-15 10:05:54 -07001882 NSArray * inputFields = @[
1883 @{
1884 @"contextTag" : @1,
1885 @"data" : @ { @"type" : @"Boolean", @"value" : @NO },
1886 },
1887 @{
1888 @"contextTag" : @2,
1889 @"data" : @ { @"type" : @"SignedInteger", @"value" : @5 },
1890 }
1891 ];
1892 NSDictionary * inputValue = @{ @"type" : @"Structure", @"value" : inputFields };
kpark-apple153a5642022-02-22 07:19:20 -08001893
Jeff Tungae2913f2022-07-06 19:02:55 -07001894 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:inputValue];
kpark-apple153a5642022-02-22 07:19:20 -08001895 NSLog(@"Conversion input: %@\nOutput: %@", inputValue, output);
1896 XCTAssertNotNil(output);
1897 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
1898
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001899 XCTAssertTrue([output isEqual:inputValue]);
kpark-apple153a5642022-02-22 07:19:20 -08001900}
1901
1902- (void)testArray
1903{
Kundok Parkeac433a2022-03-15 10:05:54 -07001904 NSArray * inputFields = @[
1905 @{ @"data" : @ { @"type" : @"Boolean", @"value" : @NO } }, @{ @"data" : @ { @"type" : @"SignedInteger", @"value" : @5 } }
1906 ];
1907 NSDictionary * inputValue = @{ @"type" : @"Array", @"value" : inputFields };
kpark-apple153a5642022-02-22 07:19:20 -08001908
Jeff Tungae2913f2022-07-06 19:02:55 -07001909 id output = [MTRBaseDevice CHIPEncodeAndDecodeNSObject:inputValue];
kpark-apple153a5642022-02-22 07:19:20 -08001910 NSLog(@"Conversion input: %@\nOutput: %@", inputValue, output);
1911 XCTAssertNotNil(output);
1912 XCTAssertTrue([output isKindOfClass:[NSDictionary class]]);
Jeff Tung83fb7fd2022-08-02 19:29:07 -07001913 XCTAssertTrue([output isEqual:inputValue]);
kpark-apple153a5642022-02-22 07:19:20 -08001914}
1915
1916@end